Creating Alpha Channel JPEGs Using SVG

November 7th, 2015 by zoltan · 2 Comments

The image of the sun above is not a PNG on top of the animated stars, it is a plain-old JPEG with an alpha channel. This was created by embedding an SVG and alpha channel inside of an SVG and compressing it to form an SVGZ file. The types of SVG files are much smaller than their lossless PNG equivalents and are a great fallback/alternative to JPEG-XR, JPEG 2000 and WEBP, which support both lossy compression and alpha channeling as well (see my previous blog post on the subject for more details). It even works in Firefox, where the other image formats don’t:

WEBPJPEG-2000JPEG-XRJPEG embedded in SVG
Chrome
Safari
IE9+
Firefox

Since it looks like Firefox won’t support any of the other formats any time soon, this is a really good cross-browser solution to optimize your alpha-channeled images.

Dirk Weber details how he did this with Adobe Photoshop by using an SVG and two JPEGs. This was a great start, but making one asset into three is not optimal since it adds a bit of HTTP overhead. I then saw Mario Klingemann‘s web application Zorro SVG, which uses base64 encoding to combine the files into the SVG code. It then compresses the SVG into an SVGZ file using gzip. I took the ideas from both posts and incorporated them into my html5ImageConverter so I can create these images quickly on the command line (which is great for mass batch conversions).

How To Create Them

Using ZorroSVG

The quickest way to play around with these images without installing any software is by using the Zorro SVG website. Just upload your image via the form and download the resultant SVG file. This is great, quick way to start off playing with these files but if you have lots of files to convert, this can take quite a bit of time.

Using The Command Line html5ImageConverter

In order to speed up the batch conversion of multiple files, I created a tool, createAlphaJPEG.sh, as part of my html5ImageConverter toolset. Download the html5ImageConverter and type in the following command:

createAlphaJPEG.sh --q=50 --compress-svg dice.png

The --q=50 is used to control the JPEG compression quality and --compress-svg ensures we create a compressed SVGZ version of the file. If you want to convert more than one PNG file, use the following:

createAlphaJPEG.sh --q=50 --compress-svg file1.png file2.png file3.png

How does createAlphaJPEG.sh create alpha channeled JPEGs? How do you make your own command-line tools to generate them? Read on.

Using The Command Line without html5ImageConverter

If you want to “roll your own” PNG to SVGZ converter, let me walk you through the steps that the html5ImageConverter uses to generate these files:

  1. Let’s start with a PNG file, like this:
    PNG rendition of dice with alpha channel
  2. Next, we use ImageMagick to extract the alpha channel from the PNG and convert it to grayscale with this command:
    convert dice.png -alpha extract -colorspace Gray dice_alpha.png
    

    dice-320_alpha

  3. We then convert the original image and the extracted alpha channel image to JPEG format:
    convert dice.png -define quality=80 dice.jpg
    convert dice_alpha.png -define quality=80 dice_alpha.jpg
    

    Here is dice.jpg:
    dice-320
    and here is dice_alpha.jpg:
    dice-320
    (Note: I am using 80% quality here. You can compress the image even more if you want to).

  4. We then create an SVG that will contain the images.
    <svg xmlns:xlink="http://www.w3.org/1999/xlink" 
      viewBox="0 0 320 240" width="320" height="240" 
      xmlns="http://www.w3.org/2000/svg">
      <defs>
        <mask id="a" maskUnits="userSpaceOnUse" 
            maskContentUnits="userSpaceOnUse">
          <image width="320px" height="240px" 
            xlink:href="dice_alpha.jpg"></image>
        </mask>
      </defs>
      <image style="mask: url(#a);" 
        xlink:href="dice.jpg"></image>
    </svg>
    
  5. The problem with the above SVG is that, as said earlier in this post, we will have three assets to download (Mario Klingemann mentions on the Zorro SVG website that relative URLs are also not handled consistently, so there is an issue with SVG). In order to fix this, let’s base64 the JPGs using the base64 command:
    cat dice_alpha.png | base64 | pbcopy
    

    (Note: pbcopy takes the output of the cat command and puts it into the OSX clipboard so you can paste it into the editor that contains your SVG. If you want to do the same thing for Windows or Linux, use the xclip and clip command respectively).

    Using the base64 command on the dice_alpha.jpg and dice.jpg above, we get the following SVG code:

    <svg xmlns:xlink="http://www.w3.org/1999/xlink" 
      viewBox="0 0 320 240" width="320" height="240" 
      xmlns="http://www.w3.org/2000/svg">
      <defs>
        <mask id="a" maskUnits="userSpaceOnUse" 
            maskContentUnits="userSpaceOnUse">
          <image width="320px" height="240px" 
            xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgA..."></image>
        </mask>
      </defs>
      <image style="mask: url(#a);" 
        xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQA..."></image>
    </svg>
    

    (Another Note: We only put the first few characters of the base64 code above since it is quite huge).

  6. We save the final SVG file and compress it with gzip. If the resultant SVG is saved in the file dice.svg, then we compress the SVG file with this command:

    gzip -9 $stub.svg
    mv $stub.svg.gz $stub.svgz
    
  7. Load the image in your webpage with the <picture> element:

    <picture title='alternative image format' id='alternate-image'>
          <!--[if IE 9]><video style='display: none;'><![endif]-->
          <source srcset='dice.svgz' type='image/svg+xml'>
          <!--[if IE 9]></video><![endif]-->
          <img srcset='dice-320.png'  />
    </picture>
    

    (the IE conditional-commented <video> tags are there to prevent bugs in IE9 and down). If you are using picturefill, this code will load the SVG in all browser that support it. If you aren’t, then it will load the SVG in browsers that support it as long as they can natively use the <picture> tag. For more information about how to use the <picture> tag, read the “Use <picture> For Still Images section of my blog postUsing WEBP/JPEG 2000/JPEG-XR/APNG Now With Picturefill and Modernizr. Sara Soueidan excellent in-depth article Better SVG Fallback and Art Direction With The <picture> Element is a thorough examination of the <picture> tag.

  8. The result should be this:




  9. If you have troubles loading the SVG into your browser, you may want to make sure your webserver is configured correctly. You can either read the kaioa.com article on How to Configure Apache to Serve SVG/SVGZ the Right Way, or you can put this code into a .htaccess file inside the directory that will serve the SVGZ files:

    AddType image/svg+xml svg svgz
    AddEncoding gzip svgz
    

Note that some browsers (e.g. Firefox) need to have their cache cleared in order to see the image after these server tweaks are added.

Using Photoshop

As mentioned earlier, Dirk Weber details how he did this with Adobe Photoshop by using an SVG and two JPEGs. You can take the two JPEGs created in his tutorial and use steps 5-8 in the commandline tutorial above to put the data into one SVG file.

Can You Do Animations?

If you create a “sprite-sheet” containing all of the frames in one still SVG image, You can use CSS steps() function to animate through the frames using CSS animations. Since a previous blog post covered this animation technique with JPEG-XR and JPEG-2000 alpha channeled images, I won’t cover it again here. Instead, drag the animated JPEG/SVG sun below and see how it works in action.

View the clean-room version of the above example

How Do These Files Compare with Alpha-Channeled WEBP/JPEG-XR and JPEG 2000 Images?

That is a very good question. In a lot of cases, JPEG doesn’t compress as well as WEBP, JPEG 2000 or JPEG-XR, since they have better compression algorithms. The question of “which format is better than which” is a topic unto itself, which I will leave for a separate blog post.

Further Reading

Credits

2 responses so far ↓
  • 1 Kornel // Nov 9, 2015 at 10:33 am

    The SVG trick may be advantageous on large photographic images that pngquant can’t compress that well, but in this particular example, the SVG+JPG file is *larger* than a PNG file from pngquant (try pngquant.org http://pngmini.com or tinypng.com)

    dice-320.svgz is 16KB gzipped, but dice-320-fs8.png is 14KB.

  • 2 zoltan // Nov 9, 2015 at 11:32 am

    @Kornel: Agreed! The sun example at the top is the best example of where the SVGZ method works really well vs an optimized PNG. The dice example could possibly be optimized more with mozjpeg, but I have not done a full analysis on comparing png vs SVGZ on this case, since this was done quickly to show the process of creating the SVGZ file.

    I plan to write a blog post comparing the cases where each of the image formats (JPG/SVG, optimized PNG, JPEG 2000, JPEG-XR, WEBP) compress better, since it is not obvious and my findings so far are really interesting. If you want to collaborate on this, I would welcome it. I would love to get your feedback to ensure all aspects of the research are covered, given your experience hacking some of these image formats yourself.

Give Feedback

Don't be shy! Give feedback and join the discussion.

Please Note: If you are asking for help using the information on this page or if you are reporting a bug with the code featured here, please include a URL that shows the problem you are experiencing along with the browser/version number/operating system combination where the issue manifests itself. Without this information, I may not be able to respond.

An orange star denotes a required field.