Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Catalog · How the catalog works
Anything · Lazarus or Delphi
Random projectRandom project

jpeg4pas

jpeg transcoder in pascal

View on GitLab

Member rating

No ratings yet.

Sign in to rate this project.

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:

pascal
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 supported

Even 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:

pascal
    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:

pascal
  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=25

Note 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.