libjpeg.net
libjpeg.net copied to clipboard
Cannot change jpeg_width and jpeg_height without reflection
I was working on a tool that let you losslessly merge two JPEGs together by putting one on top of the other. This can be done if they were saved with the same compression settings.
I did manage to get it working correctly, but I encountered one problem with the library when I had to save the JPEG file.
var decompress1 = new jpeg_decompress_struct(); //Create 'decompress' object
decompress1.jpeg_stdio_src(inputFileStream1); //Load a JPEG file
decompress1.jpeg_read_header(true); //Load the header
var compress = new jpeg_compress_struct(); //Create 'compress' object
compress.jpeg_stdio_dest(outputStream); //Assign the output stream
decompress1.jpeg_copy_critical_parameters(compress); //Copy the critical parameters
compress.Image_width = outputWidth; //Set output width
compress.Image_height = outputHeight; //Set output height
compress.jpeg_write_coefficients(outputBlocks); //Set Output DCT blocks
compress.jpeg_finish_compress(); //Output the JPEG file
The problem happens when you call jpeg_finish_compress
, it ignores the public Image_width
and Image_height
fields. Instead, it uses the private fields jpeg_width
and jpeg_height
to determine what dimensions to use for the output file.
In order to get the program to work, I needed to use reflection to write to those private members.
Please take a look at source code of jpeg_copy_critical_parameters
. This should give you an idea how to work around the issue.
The jpeg_width
and jpeg_height
fields are internal because the plan is to calculate them in jpeg_calc_jpeg_dimensions
or copy in jpeg_copy_critical_parameters
. Other use cases are not supported at the moment.
Just peeked at the code for jpeg_write_coefficients
.
The code path that could possibly reach jpeg_calc_jpeg_dimensions
is disabled.
jpeg_write_coefficients
-> transencode_master_selection
-> jinit_c_master_control(transcode_only: true)
-> initial_setup(transcode_only: true)
initial_setup
is the function that could call jpeg_calc_jpeg_dimensions
, but because transcode_only = true
, it does not call it. So Image_width
and Image_height
never get re-read, and the private members jpeg_width
and jpeg_height
remain at their previous values.
It just so happens that poking the private jpeg_width
and jpeg_height
values before calling jpeg_write_coefficients
will make everything work correctly, but it only works because jpeg_write_coefficients
is calling initial_setup
.
Another point of consideration is that for jpeg_write_coefficients
, the dimensions of the blocks is provided in the array itself. Luma plane is always in 8x8 blocks and is never subsampled. So the padded image dimensions could be determined by the luma plane, and the non-padded image dimensions can only be be up to 7 pixels smaller.
Image_width
and Image_height
could be checked at this time, and if they are not valid, could be reset to the block dimensions.
Then transcode_only = true
could be removed as well, then jpeg_write_coefficients
would have all the information it needs to set the Image_width, Image_height, jpeg_width
, and jpeg_height
variables.
I am not sure I follow. Could you please clarify what you suggest to do?