Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read and Write png file without change file size in C (libpng)

I want to read the png file, see Image Data and write again without any change in file size. Based on libpng documents,the png is lossless and use deflate and lz77 for compression. There is an example project in libpng which has claimed to read and write image without loss, it's correct in pixel values, but change the structure of file (such as number of IDATs, optional chunks and etc) png file size.

My explicit question: How to extract encode parameters (such as deflate params or lz77 params) from compressed stream (Zstream in libpng) and use this parameters to encode raw image to create image file same as input file without any change?

This is my code. I try to hold params in info_ptr to write image,but not work. How to do it?

int main(int argc, char *argv[])
{
inname = argv[1];
outname = argv[2];

png_structrp read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info_ptr = png_create_info_struct(read_ptr);



if (!info_ptr)
{
    png_destroy_read_struct(&read_ptr, (png_infopp)NULL, (png_infopp)NULL);
}

png_FILE_p imageFile, imageFile2, imageFileW;
    imageFile=fopen(inname, "rb");  imageFileW = fopen(outname, "wb"); imageFile2 = fopen(inname, "rb");

int fileSize=fsize(imageFile2);
unsigned char* bufImWrite = malloc(sizeof(char)*fileSize);
fread(bufImWrite, 1, fileSize, imageFile2);

png_init_io(read_ptr, imageFile);

png_read_info(read_ptr, info_ptr);


png_uint_32 height;
height = info_ptr->height;


png_bytep * row_pointers;

row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
for (int y = 0; y < height; y++)
    row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(read_ptr, info_ptr));


png_read_image(read_ptr, row_pointers);


png_read_end(read_ptr, info_ptr);


png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);;
png_init_io(png_ptr, imageFileW);
png_write_info(png_ptr, info_ptr);
//png_set_compression_level(png_ptr, 9);
//png_set_compression_window_bits(png_ptr, 15);
//png_set_compression_strategy(png_ptr, 3);
//png_set_compression_mem_level(png_ptr, 8);

png_write_image(png_ptr, row_pointers);
png_write_end(png_ptr, info_ptr);


}
like image 702
Ali Mahdavi Avatar asked Dec 14 '22 22:12

Ali Mahdavi


2 Answers

A PNG file does not contain a record of the exact deflate/zlib parameters used for compression, so what you want is not really possible. If you need to preserve the original encoding, you should keep a copy of your original PNG file rather than destructively reading it.

If you want to change ancillary PNG chunks without changing the image data, use some application such as tweakpng to copy the IHDR, PLTE, IDAT, and IEND chunks from your original PNG file into the new file.

If, as the comments suggest, you want to add a watermark without changing the compressed datastream, that's simply impossible because the image data is different. If you really need a removable watermark, use some editable format such as SVG to keep the watermark separate, or a format such as APNG where the watermark can be stored in a separate part. The disadvantage of that is that anyone could remove the watermark from their copy.

like image 142
Glenn Randers-Pehrson Avatar answered Feb 19 '23 06:02

Glenn Randers-Pehrson


In addition to Glenn's correct response, even if you somehow had the parameters used for compression, the compression may have been done by code that is no longer available, or by code that is proprietary and not available to you. In either case there is no way to replicate the exact compressed data. Just keep the original compressed data if you're not going to mess with the image.

like image 45
Mark Adler Avatar answered Feb 19 '23 08:02

Mark Adler