📜 ⬆️ ⬇️

JPEG image compression with alpha channel or SVG masks

Hi, Habr! A recent article about compression in png-8 with preservation of translucency reminded me of a technique that allows you to use images with an alpha channel on sites, while using a lossy compression algorithm, JPEG, which can significantly reduce their size.


Prehistory

Some time ago, when few people knew about HTML5, and flash was quite common, I had to learn ActionScript and make beautiful “image” sites. Usually such a site was rather saturated with pictures, and moreover, for some effects it was required to use photographs with a transparent background (for example, when a commodity with a shadow flies out beautifully from somewhere along a non-uniform background). But as you know, JPEG does not allow the alpha channel to be stored in itself, and PNG is a lossless compression format, which is why the size of such a photo was, to put it mildly, large. In flash, it was possible to save translucent images with JPEG compression, this was achieved by the fact that the alpha channel was kept separately from the image, receiving two images that were subsequently saved in JPEG.
Later, the verst one of the “regular” html sites, I also needed to save a large non-uniform translucent background, but neither saving in png-32 nor in png-8 gave an acceptable result - png-8 looked disgusting, and png-32 weighed too much lot. In search of a technology that would allow saving images with alpha channel with loss of quality, I came across the SVG Mask.

So what is it for?

Take a hypothetical example - we need just such a picture with a “hole” inside:
PNG-32 (PNG-32, 1870 kb)

Compressing into the 256-color palette PNG-8 "kills" the image:
PNG-8 (PNG-8, 305 KB)
')
Whereas, using flash, we get a modest-sized swf, while the eye is hardly distinguishable from the original:
flash (swf, 453 kb)

In principle, if it were not for the difficulties with the convenience of swf generation, and not full flash support on all devices, it would be possible to end there. But there is a more convenient HTML5 alternative!

The essence of the method

The method itself is not new and occasionally there is a description of it on some resources. But unfortunately, there is relatively little information about him, at least in the article’s article I didn’t find what I’m in a hurry to fix.
The SVG standard allows you to embed bitmaps in yourself, and also allows you to apply masks . So why don't we do everything in the same way as in flash?
Separate the alpha channel and save it to a separate file:
image mask (JPEG, 165/46 KB)

The total volume was even less than .swf - 211 KB!

Writing SVG:
out.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> 

Alternatively, you can embed files directly into svg (reduce the number of requests) using data: URI , although in this case the size will increase by an average of 33%, although this can be bypassed using gzip compression. An example .

How about cross-browser compatibility?

At one time, I left this method for a “bright future,” since then SVG support was not very good for browsers, and customers with requirements for ie6 still remained, but time is ticking, and now you can use this method in “battle conditions ".
About the introduction of SVG in HTML, there were already articles on Habré: one , two , so I will not dwell on this issue in any detail. In addition, you may have noticed that you registered -webkit-background-clip: text; , it is necessary for the transparency of the object being implemented in old safari and chrome (in modern versions this bug has already been closed).
But what about ie7, ie8? For this “dying” audience, we can offer flash, and if it is not there, then at worst, let them load a heavy PNG-32 (or some kind of stub).

Decision

Let's start with how to connect SVG?
To review the various methods of introducing SVG, I made a special demo page .



In addition to the standard method of connection via object / embed (links to articles above), you can connect SVG via <img> or CSS. But as practice has shown, through <img> SVG in firefox and chrome is shown only with files embedded via data: URI, moreover, some older browsers do not support SVG embedded in this way (for example, firefox 3.6), moreover, in this case unable to control SVG using javascript. But if we discard these facts, then this method is even more preferable for “static” pictures.
If there is no SVG support (ie6-8, android <3.0, etc.), then you can replace it with flash or png. Looking for a ready-made solution, I didn’t find anything, so I wrote my bike (using jQuery and SWFObject).

In HTML, we include the jQuery, swfobject and script-fix libraries. Then we introduce the svg-image by any method you like. To support cross-browser compatibility, we assign a CSS class for embedded svg elements, and specify alternative sources via the data-altflash and / or data-altpng attributes.
index.html
 <!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 (please do not criticize strongly for the quality of the code, it is written literally on the knee):
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); }); }); }; 

Total

Thus, we can significantly reduce the size of images. This method has been tested on ie7-ie9, firefox 13, chrome 19, opera 12, Safari 5 (win), android 2.3. Please test it in other browsers. Thanks for attention!

Links

Demo
Archive ( mirror )

Ps. It is not the first time that I come across the misunderstanding that a mask is an arbitrary image, I used the circular gradient as an example, in reality the mask is a JPEG image, which can be of any geometric complexity!

Source: https://habr.com/ru/post/146804/


All Articles