I've been dabbling with Libpng library to create PNG-based software. As I explore the library, it seems that when I read in certain paletted images, then check the 2D arrays that I produce to see if any indexes are out of range -- many actually are. And I can't seem to figure out why.
I've tried chatgpt, but it has not been particularly helpful.
Although, it did generate this debug code for me to use as a tester. I will supply the code and the file below. But first, here is the generated reading/writing code. The output_pixel_values function is what reads to me the value of each palette entry.
void read_and_write_png(const char* input_filename, const char* output_filename) {
FILE* input_file = fopen(input_filename, "rb");
if(!input_file) {
printf("Can't open file %s for reading\n", input_filename);
return;
}
// Initialize the reading structures.
png_structp read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop read_info_ptr = png_create_info_struct(read_ptr);
png_init_io(read_ptr, input_file);
png_read_info(read_ptr, read_info_ptr);
png_uint_32 height = png_get_image_height(read_ptr, read_info_ptr);
png_bytepp row_pointers = (png_bytepp)malloc(sizeof(png_bytep) * height);
for (png_uint_32 i = 0; i < height; i++) {
row_pointers[i] = (png_bytep)malloc(png_get_rowbytes(read_ptr, read_info_ptr));
}
png_read_image(read_ptr, row_pointers);
// Now that we've read the image, let's write it to another file.
FILE* output_file = fopen(output_filename, "wb");
if(!output_file) {
printf("Can't open file %s for writing\n", output_filename);
return;
}
// Initialize the writing structures.
png_structp write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop write_info_ptr = png_create_info_struct(write_ptr);
// Set up the output.
png_init_io(write_ptr, output_file);
// Copy the image data from the read structures to the write structures.
png_set_IHDR(write_ptr, write_info_ptr, png_get_image_width(read_ptr, read_info_ptr),
png_get_image_height(read_ptr, read_info_ptr), png_get_bit_depth(read_ptr, read_info_ptr),
png_get_color_type(read_ptr, read_info_ptr), png_get_interlace_type(read_ptr, read_info_ptr),
png_get_compression_type(read_ptr, read_info_ptr), png_get_filter_type(read_ptr, read_info_ptr));
png_colorp palette;
int num_palette;
if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette)) {
png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette);
}
png_bytep trans_alpha = NULL;
int num_trans = 0;
png_color_16p trans_color = NULL;
if (png_get_tRNS(read_ptr, read_info_ptr, &trans_alpha, &num_trans, &trans_color)) {
png_set_tRNS(write_ptr, write_info_ptr, trans_alpha, num_trans, trans_color);
}
printf("Reading image now. . \n");
sleep(5);
output_pixel_values(read_ptr, read_info_ptr, row_pointers);
printf("Done!!");
// Set up the data in the write structure.
png_set_rows(write_ptr, write_info_ptr, row_pointers);
// Finally, write the image to the file.
png_write_png(write_ptr, write_info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
// Clean up.
fclose(input_file);
fclose(output_file);
png_destroy_read_struct(&read_ptr, &read_info_ptr, NULL);
png_destroy_write_struct(&write_ptr, &write_info_ptr);
// Free the memory associated with row_pointers
for (png_uint_32 i = 0; i < height; i++) {
free(row_pointers[i]);
}
free(row_pointers);
}
void output_pixel_values(png_structp png_ptr, png_infop info_ptr, png_bytep *row_pointers) {
int width = png_get_image_width(png_ptr, info_ptr);
int height = png_get_image_height(png_ptr, info_ptr);
int color_type = png_get_color_type(png_ptr, info_ptr);
int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
png_colorp palette = NULL;
int num_palette = 0;
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
}
for (int y = 0; y < height; y++) {
png_bytep row = row_pointers[y];
for (int x = 0; x < width; x++) {
if (color_type == PNG_COLOR_TYPE_RGB) {
png_bytep px = &(row[x * 3]);
printf("Pixel at (%d, %d): R=%d, G=%d, B=%d\n", x, y, px[0], px[1], px[2]);
} else if (color_type == PNG_COLOR_TYPE_RGBA) {
png_bytep px = &(row[x * 4]);
printf("Pixel at (%d, %d): R=%d, G=%d, B=%d, A=%d\n", x, y, px[0], px[1], px[2], px[3]);
} else if (color_type == PNG_COLOR_TYPE_GRAY) {
int num_bytes = bit_depth == 8 ? 1 : 2;
png_bytep px = &(row[x * num_bytes]);
int gray_value = num_bytes == 1 ? px[0] : (px[0] << 8) + px[1];
printf("Pixel at (%d, %d): Gray=%d\n", x, y, gray_value);
} else if (color_type == PNG_COLOR_TYPE_PALETTE) {
int index = row[x];
if (index < num_palette) {
png_color palette_color = palette[index];
printf("Pixel at (%d, %d): Palette Index=%d, R=%d, G=%d, B=%d\n", x, y, index, palette_color.red, palette_color.green, palette_color.blue);
} else {
printf("Index = %d\n", index);
printf("Pixel at (%d, %d): Palette index out of range\n", x, y);
row[x] = 0;
}
}
else {
printf("Index = %d", row[x]);
printf("Pixel at (%d, %d): Color type not supported\n", x, y);
}
}
}
}
I was expecting NOT to receive the following:
Index = 96 Pixel at (319, 96): Palette index out of range
Please let me know if you all can see anything immediately wrong. Here is a link to the image I've used a tester.
I've tried chatgpt, but it has not been particularly helpful.
Although, it did generate this debug code for me to use as a tester.
The artificial intelligence wasn't intelligent enough to account for other than 8-bit/pixel paletted data. You could change int index = row[x];
to
int index, ppb = 8/bit_depth; // pixels per byte
switch (bit_depth)
{
default: printf("bit depth %d not implemented\n", bit_depth); exit(1);
case 1:
case 2:
case 4:
case 8: index = row[x/ppb]>>8-(x%ppb+1)*bit_depth&255>>8-bit_depth;
}
or use png_set_packing()
.