jpeg4pas
jpeg transcoder in pascal
- Quiet · last change 7-1-2022
- ★ 2 on GitLab
README
Lightweight Pascal units for jpeg transcoding
This implementation supports almost any mode of operation as defined in ITU-T.81 (the JPEG specification).
It's very lightweight, in terms of source code complexity as well as generated code. Where things get to become configurable, don't expect to see much abstractions that wouldn't really make things easier to configure, but minimalistic code that implements what is needed to read or write almost any jpeg.
One thing that is not supported is patented JPEG arithmetic entropy coding (instead of huffman compression). Also, vendor-specifics and color managment are out of scope.
One more slight shortcoming is hierarchical mode of operation (some higher level modifications needed, not already done because of the lack of testing files).
Finally, the DNL-marker is unsupported (used when the image-height isn't known at the start of frame).
In a test with thousands of images downloaded from all over the web, only some few failed to load (and it turned out those were broken filen).
To become more specific, the supported profile reads like:
baseline process
interleaved and non-interleaved scans
many restriction mentioned in ITU-T.81 does not apply
restarts fully supported
extended DCT process
interleaved and non-interleaved scans
many restriction mentioned in ITU-T.81 does not apply
no restrictions on precision (not only 8 and 12 bit, tested 8Bits only)
restarts fully supported
progressive DCT process
interleaved and non-interleaved scans
spectral selection
successive approximation
restriction mentioned in ITU-T.81 does not apply
no restrictions on precision (not only 8 and 12 bit, tested 8Bits only)
restarts fully supported
lossless process (doesn't use DCT per spec)
interleaved and non-interleaved scans
no restrictions on precision (2..16 bit, tested 8,12,14,16 Bits)
many restriction mentioned in ITU-T.81 does not apply
restarts fully supported
hierarchical process
would need some higher level adjustments
restarts fully supportedEven though it sounds a little oldschool at first, some modern jpeg writers include restart markers, breaking the bitstream. The reason hasn't got to with resync, or something. It's simply that they theoretically allow parallelized bitstream decoding, even for the interleaved baseline process.
There are fDCT and iDCT routines included, but they operate single core, only. Feel free to stream to gpu diretcly.
image writing
The main interface to an application is the Tjpeg_imagecomponent.
This is where an application feeds it's image data, or gets it back. A single color channel, always 16 Bits. Depending on which processing steps the application wants to do on it's own, the Tjpeg_imagecomponent's memory oranization can hold simple greyscale scanline information, DCT transformed image data, zigzaged DCT image data or, in lossless mode, greyscale scanline image data with predictors applied. Transforms between these memory layouts are normally done in place.
After reading an image file, some Tjpegparser presents the application with an array of Tjpeg_imagecomponent, and he application then decides to use Tjpeg_imagecomponent's default postprocessing steps, or implement it's own. The last postprocessing step supported by Tjpeg_imagecomponent is the data reorganization from DCT-block layout to scanline greyscale. No scaling (ok, some examples might have subsampling-undo), no color space conversion.
To generate a jpeg, an application creates a set of Tjpeg_imagecomponent, fills in the data format of it's choice, and passes an array of these components to some jpeg writer class. To make sure a set of Tjpeg_imagecomponent can be encoded into a valid jpeg, it is necessary to know about how to deal with subsampled components (this is a little complicated per specification).
Tjpeg_imagecomponent have a method called
procedure recalc_area(x,y,H,V,mH,mV : longint; blocky : boolean; o : Timgdataorganization);
where x,y specify the reference, non-subsampled, overall dimensions of the image.
H,V,mH and mV are a little bit counterintuitive to understand. mH and mV specify what heaviest sub-scaling is used for some component in the collection, vertically and horizontally. H and V tell about the subsmpling to use for the actual component. But -this is the counterintuitive part- in units of resolution-multiples of the heaviest subsampled ones. Maybe a look in the method's implementation might help to understand this:
procedure Tjpeg_imagecomponent.recalc_area(x,y,H,V,mH,mV : longint; blocky : boolean; o : Timgdataorganization);
begin
scanH := H;
scanV := V;
xres := (x*scanH) div mH;
if (mH*xres<x*scanH) then inc(xres);
yres := (y*scanV) div mV;
if (mV*yres<y*scanV) then inc(yres);
...
end;To construct a complicated example, already not allowed with jpeg, but explaining this:
Assume an Image with dimensions 123x123 pixel consists of 3 components. One with full resolution, one subsampled by 3, and another one subsampled by 5. Then the calls to Tjpeg_imagecomponent.recalc_area() should look like:
compo1.recalc_area(123,123,15,15,15,15); // subsampled by 1 => xres=123
compo2.recalc_area(123,123, 5, 5,15,15); // subsampled by 3 => xres=41
compo3.recalc_area(123,123, 3, 3,15,15); // subsampled by 5 => xres=25Note that 15 is not the heaviest subsampling used, as I said, but the least common multiple. An interleaved jpeg scan would then contain 2x2 MCUs, each with 15 data elements for compo1, 5 data elements for comop2, and 3 data items for compo3. If the data-item to use is DCT-block, the jpeg-internal component-sizes would be 240x240 pixel (compo1), 80x80 pixel (compo2) and 48x48 pixel (compo3).
This is also the memory to be reserved, if blocky=true indicates that the component shall probably be used in a DCT process.
The actual memory allocation works, after a call to recalc_area with
procedure Tjpeg_imagecomponent.makeup_area(p : pointer; keepondestroy : boolean);
where p might be a pointer to application-reserved memory, or nil, in which case the memory shall be allocated by Tjpeg_imagecomponent. The Tjpeg_imagecomponent then has methods to get pixel data pointers, so the application knows where exactly to write the image data (normally on a scanline-basis).
compile
Compilation is to easy to talk about (just -Fu the src). IRC the makefile fits compile-instructions to fpc-packge format. Only tested on intel-style machines using fpc.
