- PVSM.RU - https://www.pvsm.ru -
JPEG сжатие картинки с альфа-каналом или SVG masks
Привет! Недавняя статья [1] про сжатие в png-8 с сохранение полупрозрачности, напомнила мне об одной технике, которая позволяет применять на сайтах изображения с альфа-каналом, при этом используя алгоритм сжатия с потерями — JPEG, что позволяет существенно сократить их объём.
Некоторое время назад, когда об HTML5 мало кто знал, а flash был довольно распространён, мне пришлось изучить ActionScript и делать красивые «имиджевые» сайты. Обычно такой сайт был довольно насыщен картинками, и более того, для некоторых эффектов требовалось использовать фотографии с прозрачным фоном (например, когда товарная единица с тенью красиво вылетает откуда-нибудь по неоднородному фону). Но как известно JPEG не позволяет хранить в себе альфа-канал, а PNG — это формат сжатия без потерь, из-за чего размер такого фото получался мягко говоря большим. Во flash же возможно было сохранить полупрозрачные изображения с JPEG-компрессией, достигалось это тем, что альфа-канал сохранялся отдельно от изображения, получая два изображения, которые впоследствии сохранялись в JPEG.
Позже верстая один из «обычных» html-сайтов, мне также потребовалось сохранить большой неоднородный полупрозрачный фон, но ни сохранение в png-32, ни в png-8 не давали приемлемый результат — png-8 выглядел отвратно, а png-32 весил слишком много. В поисках технологии, которая бы позволила сохранять изображения с альфа-каналом с потерей качества, я наткнулся на SVG Mask.
Возьмём гипотетический пример — нужна нам вот-такая картинка с «дыркой» внутри:
[2] (PNG-32, 1870 Кбайт)
Сжатие в 256ти-цветную палитру PNG-8 «убивает» изображение:
[3] (PNG-8, 305 Кбайт)
Тогда как применяя flash мы получим swf скромного размера, при этом на глаз мало различимого от оригинала:
flash [4] (swf, 453 Кбайт)
В принципе, если бы не сложности с удобством генерации swf, и не полной поддержки flash на всех устройствах, можно было бы на этом и закончить. Но есть более удобная HTML5 альтернатива!
Сам метод не новый и изредка встречается его описание на некоторых ресурсах. Но к сожалению информации о нём относительно мало, по крайней мере на хабре статьи не нашёл, что спешу поправить.
Стандарт SVG [5], позволяет внедрять в себя растровые изображения [6], а также позволяет применять маски [7]. Так почему же нам не реализовать всё так же, как и во flash?
Отделяем альфа-канал и сохраняем его в отдельный файл:
[8]
[9] (JPEG, 165/46 Кбайт)
Суммарный объём получился даже меньше, чем .swf — 211 Кбайт!
Пишем SVG:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 240" width="300px" height="240px" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
<style>
svg { -webkit-background-clip: text; }
</style>
<defs>
<mask id="mymask">
<image xlink:href="mask.jpg" x="0" y="0" width="300px" height="240px" />
</mask>
</defs>
<image xlink:href="image.jpg" mask="url(#mymask)" x="0" y="0" width="300px" height="240px" />
</svg>
Как вариант можно внедрить файлы прямо в svg (уменьшаем кол-во запросов), с помощью data: URI [10], правда в этом случае размер возрастёт в среднем на 33%, хотя это можно обойти с помощью gzip-сжатия. Пример [11].
В своё время я этот метод оставил на «светлое будущее», т. к. тогда поддержка SVG браузерами была не очень, да и заказчики с требованиями к ie6 ещё оставались, но время идёт, и сейчас уже можно применять этот метод в «боевых условиях».
Про внедрение SVG в HTML уже были статьи на хабре: раз [12], два [13], поэтому особо подробно останавливаться на этом вопросе не буду. Кроме того вы возможно заметили, что прописал -webkit-background-clip: text;, это нужно для прозрачности внедряемого объекта в старых safari и chrome [14] (в современных версиях этот баг уже закрыли).
Но как же быть с ie7, ie8? Для этой «умирающей» аудитории мы можем предложить flash, а если и его нет, то, на худой конец, пусть грузят тяжёлый PNG-32 (или какую-нибудь заглушку).
Начнём с того как подключать SVG?
Для обзора различных методов внедрения SVG, я сделал специальную демо-страничку [15].
Кроме стандартного способа подключения через object/embed (ссылки на статьи выше), можно подключать SVG через <img> или CSS. Но как показала практика, через <img> SVG в firefox и chrome показывается только с внедрёнными через data: URI файлами, более того, некоторые устаревшие версии браузеров не поддерживают внедрённые таким способом SVG [16] (например, firefox 3.6), кроме того, в этом случае невозможно управлять SVG с помощью JavaScript. Но если отбросить эти факты, то этот способ даже более предпочтителен для «статичных» картинок.
Если поддержки SVG нет (ie6-8, android <3.0 и др.), то можно подменить его на flash или png. В поисках готового решения, я ничего не нашёл, поэтому написал свой велосипед (с использованием jQuery и SWFObject).
В HTML подключаем библиотеки jQuery, swfobject и скрипт-фикс. Затем внедряем любым понравившимся методом svg-изображение. Для поддержки кроссбраузерности выбираем назначаем CSS-класс для внедрённых svg-элементов, и указываем через атрибуты data-altflash и/или data-altpng URL альтернативных источников.
<!DOCTYPE html>
<html>
<head>
<title>SVG alpha</title>
<style>
body { margin: 0; padding: 0; font: 12px/1 Arial, sans-serif; text-align: center; background: #ccc url(bg.png) repeat fixed left top; }
h2 { margin-top: 2em; font-size: 120%; text-align: center; }
table { width: 800px; margin: 0 auto; }
table td { width: 300px; }
object,iframe,embed,img,.svg-bg { width: 300px; height: 240px; margin: 0; padding: 0; border: 0px none; }
object { -webkit-background-clip: text; }
.svg-bg { background: transparent none no-repeat scroll left top; }
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js" type="text/javascript"></script>
<script type="text/javascript" src="svg.js"></script>
<script type="text/javascript">
$(function(){ $('.svg').svg(); }); // init
</script>
</head>
<body>
<table>
<tr>
<th>через object</th>
<th>через img (data:URI)</th>
</tr>
<tr>
<td><object class="svg" data="out.svg" type="image/svg+xml" data-altflash="flash.swf" data-altpng="png-32.png"></object></td>
<td><img class="svg" src="in.svg" alt="" data-altpng="png-32.png"/></td>
</tr>
</table>
</body>
</html>
svg.js (прошу сильно не критиковать за качество кода, он написан буквально на коленке):
$.supportSvg = function()
{
return document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", "1.1");
};
$.svgReplaceImage = function()
{
var img = new Image();
img.src = $(this).data('altpng');
$(this).replaceWith(img);
};
$.fn.svg = function()
{
if($.supportSvg()) return;
this.filter('[data-altpng]:not([data-altflash])').each($.svgReplaceImage);
var i = 0;
this.filter('[data-altflash]').each(function()
{
var obj = this;
this.id = 'svg-alt-'+(++i);
swfobject.embedSWF($(this).data('altflash'), this.id, $(this).width(), $(this).height(), '10.0.0', 'expressInstall.swf', false, {wmode: 'transparent'}, false,
function(e){ if(!e.success) $.svgReplaceImage.apply(obj); });
});
};
Таким образом мы можем существенно сократить размер изображений. Данный метод протестирован на ie7-ie9, firefox 13, chrome 19, opera 12, Safari 5 (win), android 2.3. Просьба протестировать его в других браузерах. Спасибо за внимание!
Демо [15]
Архив [17] (зеркало [18])
Автор: PaulZi
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/png/10497
Ссылки в тексте:
[1] статья: http://habrahabr.ru/post/146704/
[2] Image: http://habrastorage.org/storage2/117/699/535/11769953559a1c9579660777f504e5a9.png
[3] Image: http://habrastorage.org/storage2/dfa/09e/4c9/dfa09e4c925e681dbc39261bb2e4ae33.png
[4] flash: http://paulzi.ru/habr/svg-alpha/flash.swf
[5] SVG: http://www.w3.org/TR/SVG/
[6] растровые изображения: http://www.w3.org/TR/SVG/struct.html#ImageElement
[7] маски: http://www.w3.org/TR/SVG/masking.html#Masking
[8] Image: http://habrastorage.org/storage2/8d1/f62/864/8d1f62864ef731d55eaafb309e9e3318.jpg
[9] Image: http://habrastorage.org/storage2/7b2/c2a/c86/7b2c2ac866acdeab0778c133d94a97a2.jpg
[10] data: URI: http://tools.ietf.org/html/rfc2397
[11] Пример: http://paulzi.ru/habr/svg-alpha/in.svg
[12] раз: http://habrahabr.ru/post/99143/
[13] два: http://habrahabr.ru/post/127994/
[14] для прозрачности внедряемого объекта в старых safari и chrome: http://habrahabr.ru/post/110367/
[15] демо-страничку: http://paulzi.ru/habr/svg-alpha/
[16] внедрённые таким способом SVG: http://caniuse.com/#feat=svg-img
[17] Архив: http://paulzi.ru/habr/svg-alpha/svg.rar
[18] зеркало: http://narod.ru/disk/54701768001.9e81837ecbadf6e8d25558f15f52549f/svg.rar.html
Нажмите здесь для печати.