Sunday, September 6, 2015

Transparent JPG

[Update 8] A new and improved version is out, located here:
TransparentJPG 2

[Update 7] TransparentJPG.Dll quality default is now 80 and smoothing default is now 20. Progressive mode for the JpegEncoder is enabled potentially resulting in smaller file sizes. TransparentJPG has an added parameter for subsampling detail level. Options are 0 low (Default), 1 (medium), 2 (High). 2TJPG.exe has the added parameter as well.

[Revert 1] Changed back to using the LibJpeg.Dll. Smoothing is available again. Updated source below as well as the TransparentJPG.zip download and Latest Builds. The source for the version that is not reliant on LibJpeg.Dll will still be provided for reference.

[UPDATE 6] Removed the dependency for the LibJpeg.Dll which means that smoothing feature which helped save some space is now gone :( . I did so because I was given a 6Kx4K image to convert to TJPG and the converter (2TJPG.exe) kept crashing. I debugged it and found that LibJpeg was the problem. I am guessing that lib doesn't support images with such large resolutions? Either way I had to address the issue so I went and resorted to using Microsoft's built in encoder. It is slightly faster now due to not implementing the smoothing feature but file sizes will be slightly larger. The TransparentJPG.zip below has also been updated and contains (2TJPG.exe, TJPGviewer.exe, TransparentJPG.Dll). So essentially encoding will be different but decoding stays the same. The converter has also been updated in that it no longer supports the "Smoothing" parameter.

[UPDATE 5] Changed the color of the background squares of TJPGviewer.exe to a lighter shade(Grey) as well as made them 3x3 instead of 4x4 making them look less obtrusive.

[UPDATE 4] Optimized the code for saving TJPGs as well as removed all dependencies to LockBitmap.Dll(Removed from package). Also removed the CType to Byte() since it seemed redundant to the Decode method. Also provided SRC to TransparentJPG.DLL below.

[UPDATE 3] Changed the default values for conversion tool to quality=75 and smoothing=25. Also added a Decode method in TransparentJPG.Dll to get the pixel data, width and height of TJPG file. Also cleaned up the LoadTJPG function so that it doesn't rely on the LockBitmap.Dll though other things still need it, I will get to them soon.

[UPDATE 2] Fixed an issue with the converter (2TJPG.exe) where it would error and crash when given file paths that are valid and saying they aren't. Fixed version is provided in the "Latest Builds" link below.

[UPDATE 1] Added averaging when applying the mask to make the other bits of the alpha "useful" as well as provide a bit of compensation for rounding errors that jpeg compression might have. This has no impact on the encoding and only the decoding so file sizes are not affected though there is technically an impact on the resulting image visually it isn't noticeable and so negligible.
Moved all newer builds to a new location so people can grab this independent of the package I provided below:
Latest Builds: https://www.mediafire.com/folder/o2r7is9fsb279/Latest_Build
TransparentJPG: https://www.mediafire.com/folder/kjqpmfazac24c/TransparentJPG

     While working on my game I needed a way to import images and in my game people can share these images among each other via P2P. These images are almost always going to be grouped with tens if not hundreds, if not thousands of other images so I needed to pick an image file that is small in size. My next requirement was that the images needed to have transparency as a capability. Jpeg while small in size isn't capable of transparency. PNG has transparency but is much larger in size due to it being a lossless format. Another requirement was it needed to be compatible, I needed to work with an already existing format that most computers already know how to use so it wouldn't add time and complexity over compatibility. There exists much better solutions today but they are all either proprietary or there are no libs for them in use for .net which I need.

     I first thought hey I'll just take the pixel data from an image and limit it to 6 bits per channel then compress the whole thing which gave rise to a format I called TZI. While TZI proved to be a more acceptable format to my game than PNG I thought why don't I try a lossy attempt.

     I looked at Jpeg and I already knew the complexity behind its compression so I wasn't going to mess with the inner workings of Jpeg. Instead I thought of a hack about way to add transparency as another level on top of Jpeg. I did this by simply creating a mask via the images Alpha channel. I then removed all transparency from the image. The mask was then appended under the original image (with the alpha turned off). What you get then is an image with no transparency on top and a greyscale image of the Alpha channel on the bottom. I couldn't stop there, people randomly running into this image and viewing it as a typical Jpeg would be confused and the image shown isn't even in its intended form. And so I had to change its header so that people don't mix it with Jpeg files (even though this is essentially what this is). To reconstruct the original intended image I made an application which views the TransparentJPG files.

     The image is loaded in memory. The alpha mask is applied and then the image is essentially cropped to half its height. There is actually more detailed aspects going on behind the scenes and the workings is a bit different from how I explained it but I figure just a gist of how it works is good enough and easier to understand.

Code (VB.Net):


Code-not LibJpeg.Dll reliant (VB.Net):


Converter and Viewer Tools: http://www.mediafire.com/download/2p3tkx4yty2d0ai/TransparentJPG.zip

Samples: https://www.mediafire.com/folder/n0p76oq8p1t0v/Samples



Now because of the added height as well as the transparency bit the images will never be smaller than their original non-transparent JPG version (with the same quality etc).

    Here is an exact representation of an image with its header unchanged after the mask has been generated:


    Intended Image:


    Here is another example of how the images are encoded without the header change:


    Intended Image: