pngminus: Improve and modernize the PNG processing

Improve and modernize png2pnm.c:

 * Remove the explicit reading of the input PNG file signature.
   Libpng is now able to read it, check it, and issue an appropriate
   error message in case of magic number mismatch or file corruption.
   (See the function `png_read_sig`.)

 * Remove the explicit allocation and dealocation of the image data.
   Libpng is now able to manage all the image data automatically.
   (See the function `png_read_png`.)

 * Specify the needed image transformations without a-priori checking
   the image type for applicability.

 * Use the `png_set_expand_gray_1_2_4_to_8` transformation.
   Since libpng version 1.2.9, this transformation (if needed) must
   be enabled separately from `png_set_expand`.

Improve and modernize pnm2png.c:

 * Modify the allocation of image data, in order to match libpng's
   internal allocation model.

 * Transfer the ownership of the image data from the `pnm2png` function
   to libpng, which will manage and dealocate it at the right time.
   (See the functions `png_set_image_rows` and `png_data_freer`.)

Refactor, clean up, etc.
This commit is contained in:
Cosmin Truta 2024-01-08 20:31:18 +02:00
parent abb8d4a71f
commit bdbbcaa457
3 changed files with 123 additions and 176 deletions

View File

@ -3,7 +3,7 @@ pnm2png / png2pnm --- conversion from PBM/PGM/PPM-file to PNG-file
copyright (C) 1999-2019 by Willem van Schaik <willem at schaik dot com>
version 1.0 - 1999.10.15 - First version.
1.1 - 2015.07.29 - Fixed leaks (Glenn Randers-Pehrson)
1.1 - 2015.07.29 - Fix memory leaks (Glenn Randers-Pehrson)
1.2 - 2017.04.22 - Add buffer-size check
1.3 - 2017.08.24 - Fix potential overflow in buffer-size check
(Glenn Randers-Pehrson)
@ -11,3 +11,4 @@ version 1.0 - 1999.10.15 - First version.
1.5 - 2018.08.05 - Fix buffer overflow in tokenizer (Cosmin Truta)
1.6 - 2018.08.05 - Improve portability and fix style (Cosmin Truta)
1.7 - 2019.01.22 - Change license to MIT (Willem van Schaik)
1.8 - 2024.01.08 - Fix, improve, modernize (Cosmin Truta)

View File

@ -20,11 +20,6 @@
#define FALSE ((BOOL) 0)
#endif
/* make png2pnm verbose so we can find problems (needs to be before png.h) */
#ifndef PNG_DEBUG
#define PNG_DEBUG 0
#endif
#include "png.h"
/* function prototypes */
@ -33,6 +28,9 @@ int main (int argc, char *argv[]);
void usage ();
BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file,
BOOL raw, BOOL alpha);
BOOL png2pnm_internal (png_struct *png_ptr, png_info *info_ptr,
FILE *pnm_file, FILE *alpha_file,
BOOL raw, BOOL alpha);
/*
* main
@ -163,35 +161,11 @@ void usage ()
BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file,
BOOL raw, BOOL alpha)
{
png_struct *png_ptr = NULL;
png_info *info_ptr = NULL;
png_byte buf[8];
png_byte *png_pixels = NULL;
png_byte **row_pointers = NULL;
png_byte *pix_ptr = NULL;
png_uint_32 row_bytes;
png_struct *png_ptr;
png_info *info_ptr;
BOOL ret;
png_uint_32 width;
png_uint_32 height;
int bit_depth;
int channels;
int color_type;
int alpha_present;
int row, col;
int ret;
int i;
long dep_16;
/* read and check signature in PNG file */
ret = fread (buf, 1, 8, png_file);
if (ret != 8)
return FALSE;
ret = png_sig_cmp (buf, 0, 8);
if (ret != 0)
return FALSE;
/* create png and info structures */
/* initialize the libpng structures for reading from png_file */
png_ptr = png_create_read_struct (png_get_libpng_ver(NULL),
NULL, NULL, NULL);
@ -208,33 +182,49 @@ BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file,
if (setjmp (png_jmpbuf (png_ptr)))
{
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
return FALSE;
return FALSE; /* generic libpng error */
}
/* set up the input control for C streams */
png_init_io (png_ptr, png_file);
png_set_sig_bytes (png_ptr, 8); /* we already read the 8 signature bytes */
/* read the file information */
png_read_info (png_ptr, info_ptr);
/* do the actual conversion */
ret = png2pnm_internal (png_ptr, info_ptr, pnm_file, alpha_file, raw, alpha);
/* get size and bit-depth of the PNG-image */
png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
NULL, NULL, NULL);
/* clean up the libpng structures and their internally-managed data */
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
/* set-up the transformations */
return ret;
}
/* transform paletted images into full-color rgb */
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_expand (png_ptr);
/* expand images to bit-depth 8 (only applicable for grayscale images) */
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand (png_ptr);
/* transform transparency maps into full alpha-channel */
if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_expand (png_ptr);
/*
* png2pnm_internal
*/
#ifdef NJET
BOOL png2pnm_internal (png_struct *png_ptr, png_info *info_ptr,
FILE *pnm_file, FILE *alpha_file,
BOOL raw, BOOL alpha)
{
png_byte **row_pointers;
png_byte *pix_ptr;
png_uint_32 width;
png_uint_32 height;
int bit_depth;
int channels;
int color_type;
int alpha_present;
png_uint_32 row, col, i;
long dep_16;
/* set up the image transformations that are necessary for the PNM format */
/* set up (if applicable) the expansion of paletted images to full-color rgb,
* and the expansion of transparency maps to full alpha-channel */
png_set_expand (png_ptr);
/* set up (if applicable) the expansion of grayscale images to bit-depth 8 */
png_set_expand_gray_1_2_4_to_8 (png_ptr);
#ifdef NJET /* FIXME */
/* downgrade 16-bit images to 8-bit */
if (bit_depth == 16)
png_set_strip_16 (png_ptr);
@ -247,16 +237,14 @@ BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file,
png_set_gamma (png_ptr, (double) 2.2, file_gamma);
#endif
/* all transformations have been registered; now update info_ptr data,
* get rowbytes and channels, and allocate image memory */
/* read the image file, with all of the above image transforms applied */
png_read_png (png_ptr, info_ptr, 0, NULL);
png_read_update_info (png_ptr, info_ptr);
/* get the new color-type and bit-depth (after expansion/stripping) */
/* get the image size, bit-depth and color-type */
png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
NULL, NULL, NULL);
/* calculate new number of channels and store alpha-presence */
/* calculate the number of channels and store alpha-presence */
if (color_type == PNG_COLOR_TYPE_GRAY)
channels = 1;
else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
@ -273,47 +261,12 @@ BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file,
if (alpha && !alpha_present)
{
fprintf (stderr, "PNG2PNM\n");
fprintf (stderr, "Error: PNG-file doesn't contain alpha channel\n");
exit (1);
}
/* row_bytes is the width x number of channels x (bit-depth / 8) */
row_bytes = png_get_rowbytes (png_ptr, info_ptr);
if ((row_bytes == 0) ||
((size_t) height > (size_t) (-1) / (size_t) row_bytes))
{
/* too big */
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
return FALSE;
}
if ((png_pixels = (png_byte *)
malloc ((size_t) row_bytes * (size_t) height)) == NULL)
{
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
fprintf (stderr, "Warning: no alpha channel in PNG file\n");
return FALSE;
}
if ((row_pointers = (png_byte **)
malloc ((size_t) height * sizeof (png_byte *))) == NULL)
{
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
free (png_pixels);
return FALSE;
}
/* set the individual row_pointers to point at the correct offsets */
for (i = 0; i < ((int) height); i++)
row_pointers[i] = png_pixels + i * row_bytes;
/* now we can go ahead and just read the whole image */
png_read_image (png_ptr, row_pointers);
/* read rest of file, and get additional chunks in info_ptr - REQUIRED */
png_read_end (png_ptr, info_ptr);
/* clean up after the read, and free any memory allocated - REQUIRED */
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
/* get address of internally-allocated image data */
row_pointers = png_get_rows (png_ptr, info_ptr);
/* write header of PNM file */
@ -344,13 +297,13 @@ BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file,
}
/* write data to PNM file */
pix_ptr = png_pixels;
for (row = 0; row < (int) height; row++)
for (row = 0; row < height; row++)
{
for (col = 0; col < (int) width; col++)
pix_ptr = row_pointers[row];
for (col = 0; col < width; col++)
{
for (i = 0; i < (channels - alpha_present); i++)
for (i = 0; i < (png_uint_32) (channels - alpha_present); i++)
{
if (raw)
{
@ -416,10 +369,5 @@ BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file,
fprintf (pnm_file, "\n");
} /* end for row */
if (row_pointers != NULL)
free (row_pointers);
if (png_pixels != NULL)
free (png_pixels);
return TRUE;
} /* end of source */

View File

@ -21,11 +21,6 @@
#define FALSE ((BOOL) 0)
#endif
/* make pnm2png verbose so we can find problems (needs to be before png.h) */
#ifndef PNG_DEBUG
#define PNG_DEBUG 0
#endif
#include "png.h"
/* function prototypes */
@ -34,6 +29,9 @@ int main (int argc, char *argv[]);
void usage ();
BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file,
BOOL interlace, BOOL alpha);
BOOL pnm2png_internal (png_struct *png_ptr, png_info *info_ptr,
FILE *pnm_file, FILE *alpha_file,
BOOL interlace, BOOL alpha);
int fscan_pnm_magic (FILE *pnm_file, char *magic_buf, size_t magic_buf_size);
int fscan_pnm_token (FILE *pnm_file, char *token_buf, size_t token_buf_size);
int fscan_pnm_uint_32 (FILE *pnm_file, png_uint_32 *num_ptr);
@ -166,11 +164,52 @@ void usage ()
BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file,
BOOL interlace, BOOL alpha)
{
png_struct *png_ptr = NULL;
png_info *info_ptr = NULL;
png_byte *png_pixels = NULL;
png_byte **row_pointers = NULL;
png_byte *pix_ptr = NULL;
png_struct *png_ptr;
png_info *info_ptr;
BOOL ret;
/* initialize the libpng structures for writing to png_file */
png_ptr = png_create_write_struct (png_get_libpng_ver(NULL),
NULL, NULL, NULL);
if (!png_ptr)
return FALSE; /* out of memory */
info_ptr = png_create_info_struct (png_ptr);
if (!info_ptr)
{
png_destroy_write_struct (&png_ptr, NULL);
return FALSE; /* out of memory */
}
if (setjmp (png_jmpbuf (png_ptr)))
{
png_destroy_write_struct (&png_ptr, &info_ptr);
return FALSE; /* generic libpng error */
}
png_init_io (png_ptr, png_file);
/* do the actual conversion */
ret = pnm2png_internal (png_ptr, info_ptr,
pnm_file, alpha_file, interlace, alpha);
/* clean up the libpng structures and their internally-managed data */
png_destroy_write_struct (&png_ptr, &info_ptr);
return ret;
}
/*
* pnm2png_internal
*/
BOOL pnm2png_internal (png_struct *png_ptr, png_info *info_ptr,
FILE *pnm_file, FILE *alpha_file,
BOOL interlace, BOOL alpha)
{
png_byte **row_pointers;
png_byte *pix_ptr;
int bit_depth;
int color_type;
int channels;
@ -336,18 +375,27 @@ BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file,
/* too big */
return FALSE;
}
if ((png_pixels = (png_byte *)
malloc ((size_t) row_bytes * (size_t) height)) == NULL)
/* allocate the rows using the same memory layout as libpng, and transfer
* their ownership to libpng, with the responsibility to clean everything up;
* please note the use of png_calloc instead of png_malloc */
row_pointers = (png_byte **)
png_calloc (png_ptr, height * sizeof (png_byte *));
png_set_rows (png_ptr, info_ptr, row_pointers);
png_data_freer (png_ptr, info_ptr, PNG_DESTROY_WILL_FREE_DATA, PNG_FREE_ALL);
for (row = 0; row < height; row++)
{
/* out of memory */
return FALSE;
/* the individual rows should only be allocated after all the previous
* steps completed successfully, because libpng must handle correctly
* any image allocation left incomplete after an out-of-memory error */
row_pointers[row] = (png_byte *) png_malloc (png_ptr, row_bytes);
}
/* read data from PNM file */
pix_ptr = png_pixels;
/* read the data from PNM file */
for (row = 0; row < height; row++)
{
pix_ptr = row_pointers[row];
#if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
if (packed_bitmap)
{
@ -413,21 +461,10 @@ BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file,
} /* end for col */
} /* end for row */
/* prepare the standard PNG structures */
png_ptr = png_create_write_struct (png_get_libpng_ver(NULL),
NULL, NULL, NULL);
if (!png_ptr)
{
free (png_pixels);
return FALSE;
}
info_ptr = png_create_info_struct (png_ptr);
if (!info_ptr)
{
png_destroy_write_struct (&png_ptr, NULL);
free (png_pixels);
return FALSE;
}
/* we're going to write more or less the same PNG as the input file */
png_set_IHDR (png_ptr, info_ptr, width, height, bit_depth, color_type,
(!interlace) ? PNG_INTERLACE_NONE : PNG_INTERLACE_ADAM7,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
#if defined(PNG_WRITE_INVERT_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
if (packed_bitmap == TRUE)
@ -437,54 +474,15 @@ BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file,
}
#endif
if (setjmp (png_jmpbuf (png_ptr)))
{
png_destroy_write_struct (&png_ptr, &info_ptr);
free (png_pixels);
return FALSE;
}
/* initialize the png structure */
png_init_io (png_ptr, png_file);
/* we're going to write more or less the same PNG as the input file */
png_set_IHDR (png_ptr, info_ptr, width, height, bit_depth, color_type,
(!interlace) ? PNG_INTERLACE_NONE : PNG_INTERLACE_ADAM7,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
/* write the file header information */
png_write_info (png_ptr, info_ptr);
/* if needed we will allocate memory for an new array of row-pointers */
if (row_pointers == NULL)
{
if ((row_pointers = (png_byte **)
malloc (height * sizeof (png_byte *))) == NULL)
{
png_destroy_write_struct (&png_ptr, &info_ptr);
free (png_pixels);
return FALSE;
}
}
/* set the individual row_pointers to point at the correct offsets */
for (i = 0; i < height; i++)
row_pointers[i] = png_pixels + i * row_bytes;
/* write out the entire image data in one call */
png_write_image (png_ptr, row_pointers);
/* write the additional chunks to the PNG file (not really needed) */
png_write_end (png_ptr, info_ptr);
/* clean up after the write, and free any memory allocated */
png_destroy_write_struct (&png_ptr, &info_ptr);
if (row_pointers != NULL)
free (row_pointers);
if (png_pixels != NULL)
free (png_pixels);
return TRUE;
} /* end of pnm2png */