This is a forked version of libjpeg-turbo with optimizations for low-memory decoding of progressive-mode JPEGs. For details of libjpeg-turbo and non-turbo libjpeg please refer to README-turbo.md and README.ijg.
In a previous life I worked on a media server product that ran on very low memory devices (usually in the range 64-128MB of RAM). One of the functions of the media server was JPEG transcoding, and this was implemented using libjpeg-6b. Usually the transcoder worked without hitch - it received an image file, decoded it, then re-encoded the image at a lower resolution and sent it out again. However we noted that certain images when decoded on certain devices caused the server to crash. It was somewhat of a mystery but eventually we were able to determine a pattern: the problem occurred when decoding progressive-mode JPEG images on devices that did not have any internal storage (or with very small internal storage). We found that the transcoder when writing to the backing store was exhausting all available memory. (Please check structures.txt and the source code for full details of what a backing store is.) On devices with no internal memory the backing store was being created on a RAM drive of about 10MB which soon ran out in most cases. To work around the issue we added a feature to disable transcoding for progressive-mode JPEG; later we added a pixel limit so that relatively small progressive-mode JPEG files could still be transcoded. However intuitively I was sure that there must be another way to decode these images without using hundreds of megabytes of RAM and so I came up with my low memory progressive-mode JPEG decode implementation.
Two passes are used. The first determines the locations of each of the scans; the second does the actual decoding.
First pass - reads through the file to determine the location of all the "start of scan" markers. Initializes a "scan context" for each scan. Second pass - restores the scan context of the first scan, reads one row of MCUs, saves the updated scan context for the first scan. Repeats for the second scan and all subsequent scans until a complete row of MCUs is decoded. Outputs the completed row of MCUs and repeats for the second row of MCUs etc. The implementation relies on the data source being "seekable" where as the original libjpeg does not. A "check_seekable" method in the jpeg_source_mgr checks for working standard C library seek/tell functions.
OK you say this optimizes memory use for decoding progressive-mode JPEG. How much memory are we actually talking about?
Memory (bytes) required to decode | Camera Model | Resolution | Without optimization | With optimization |
---|---|---|---|---|
Sony DSC-T30 | 6000x4500 | 108,226,801 | 723,820 | |
Olympus E-500 | 2448x3264 | 48,023,542 | 451,441 | |
Olympus C750UZ | 2288x1712 | 23,580,721 | 424,876 | |
Phase One A/S P25 | 9713x12943 | 755,173,340 | 1,672,945 |
OK these numbers look impressive. How can we verify these results?
In file jmorecfg.h change the line
#undef ENABLE_MEMORY_TEST
to
#define ENABLE_MEMORY_TEST
and rebuild. Then run
tjbench -memtest <JPEG-file-you-want-to-test>
To disable the low memory progressive-mode JPEG decoding optimizations, change the line
#define LOWMEM_PROGRESSIVE_DECODE
to
#undef LOWMEM_PROGRESSIVE_DECODE
and rebuild.
You want to decode all types of JPEG images without worrying about running out of memory You have a seekable data-source You don't require progressive rendering You don't require ABI (application binary interface) compatibility with other libjpeg libraries
If you find a bug in the low memory progressive-mode JPEG decoding optimizations please create an issue with the GitHub issue tracker.
Update: Feb 2022 - rebased onto upstream/main and changed the default branch to main