Add masking, color space, and variable bit depth support (Issue #90)

This commit is contained in:
Michael R Sweet 2025-02-11 22:07:02 -05:00
parent 7f5fc456bc
commit 990342f2a5
No known key found for this signature in database
GPG Key ID: BE67C75EC81F3244

View File

@ -60,7 +60,7 @@ typedef pdfio_obj_t *(*_pdfio_image_func_t)(pdfio_dict_t *dict, int fd);
static pdfio_obj_t *copy_jpeg(pdfio_dict_t *dict, int fd); static pdfio_obj_t *copy_jpeg(pdfio_dict_t *dict, int fd);
static pdfio_obj_t *copy_png(pdfio_dict_t *dict, int fd); static pdfio_obj_t *copy_png(pdfio_dict_t *dict, int fd);
static bool create_cp1252(pdfio_file_t *pdf); static bool create_cp1252(pdfio_file_t *pdf);
static pdfio_obj_t *create_image(pdfio_dict_t *dict, const unsigned char *data, size_t width, size_t height, size_t num_colors, bool alpha); static pdfio_obj_t *create_image(pdfio_dict_t *dict, const unsigned char *data, size_t width, size_t height, size_t depth, size_t num_colors, bool alpha);
#ifdef HAVE_LIBPNG #ifdef HAVE_LIBPNG
static void png_error_func(png_structp pp, png_const_charp message); static void png_error_func(png_structp pp, png_const_charp message);
static void png_read_func(png_structp png_ptr, png_bytep data, size_t length); static void png_read_func(png_structp png_ptr, png_bytep data, size_t length);
@ -2027,7 +2027,7 @@ pdfioFileCreateImageObjFromData(
pdfioDictSetName(dict, "ColorSpace", defcolors[num_colors]); pdfioDictSetName(dict, "ColorSpace", defcolors[num_colors]);
// Create the image object(s)... // Create the image object(s)...
return (create_image(dict, data, width, height, num_colors, alpha)); return (create_image(dict, data, width, height, 8, num_colors, alpha));
} }
@ -2484,6 +2484,11 @@ copy_png(pdfio_dict_t *dict, // I - Dictionary
int fd) // I - File descriptor int fd) // I - File descriptor
{ {
pdfio_obj_t *obj = NULL; // Object pdfio_obj_t *obj = NULL; // Object
double gamma = 2.2, // Gamma value
wx = 0.0, wy = 0.0, // White point chromacity
rx = 0.0, ry = 0.0, // Red chromacity
gx = 0.0, gy = 0.0, // Green chromacity
bx = 0.0, by = 0.0; // Blue chromacity
#ifdef HAVE_LIBPNG #ifdef HAVE_LIBPNG
png_structp pp = NULL; // PNG read pointer png_structp pp = NULL; // PNG read pointer
png_infop info = NULL; // PNG info pointers png_infop info = NULL; // PNG info pointers
@ -2493,9 +2498,14 @@ copy_png(pdfio_dict_t *dict, // I - Dictionary
color_type, // PNG color mode color_type, // PNG color mode
width, // Width in columns width, // Width in columns
height, // Height in lines height, // Height in lines
depth, // Bit depth
num_colors = 0, // Number of colors num_colors = 0, // Number of colors
bpp; // Bytes per pixel linesize; // Bytes per line
bool alpha; // Alpha transparency? bool alpha; // Alpha transparency?
png_color *palette; // Color palette information
int num_palette; // Number of colors
int num_trans; // Number of transparent colors
png_color_16 *trans; // Transparent colors
PDFIO_DEBUG("copy_png(dict=%p, fd=%d)\n", (void *)dict, fd); PDFIO_DEBUG("copy_png(dict=%p, fd=%d)\n", (void *)dict, fd);
@ -2539,51 +2549,28 @@ copy_png(pdfio_dict_t *dict, // I - Dictionary
width = png_get_image_width(pp, info); width = png_get_image_width(pp, info);
height = png_get_image_height(pp, info); height = png_get_image_height(pp, info);
depth = png_get_bit_depth(pp, info);
color_type = png_get_color_type(pp, info); color_type = png_get_color_type(pp, info);
if (color_type & PNG_COLOR_MASK_COLOR) if (color_type & PNG_COLOR_MASK_PALETTE)
num_colors = 1;
else if (color_type & PNG_COLOR_MASK_COLOR)
num_colors = 3; num_colors = 3;
else else
num_colors = 1; num_colors = 1;
PDFIO_DEBUG("copy_png: width=%u, height=%u, color_type=%u, num_colors=%d\n", width, height, color_type, num_colors); PDFIO_DEBUG("copy_png: width=%u, height=%u, depth=%u, color_type=%u, num_colors=%d\n", width, height, depth, color_type, num_colors);
// Set decoding options... // Set decoding options...
if (png_get_valid(pp, info, PNG_INFO_tRNS)) alpha = (color_type & PNG_COLOR_MASK_ALPHA) != 0;
{ linesize = (width * num_colors * depth + 7) / 8;
// Map transparency to alpha if (alpha)
png_set_tRNS_to_alpha(pp); linesize += width;
color_type |= PNG_COLOR_MASK_ALPHA;
}
if (png_get_bit_depth(pp, info) > 8) PDFIO_DEBUG("copy_png: alpha=%s, linesize=%u\n", alpha ? "true" : "false", (unsigned)linesize);
{
// Strip the bottom bits of 16-bit values
png_set_strip_16(pp);
}
else if (png_get_bit_depth(pp, info) < 8)
{
// Expand 1, 2, and 4-bit values to 8 bits
if (num_colors == 1)
png_set_expand_gray_1_2_4_to_8(pp);
else
png_set_packing(pp);
}
#if 1
if (color_type & PNG_COLOR_MASK_PALETTE)
{
// Convert indexed images to RGB...
png_set_palette_to_rgb(pp);
num_colors = 3;
}
#endif // 0
alpha = (color_type & PNG_COLOR_MASK_ALPHA) != 0;
bpp = num_colors + (alpha ? 1 : 0);
// Allocate memory for the image... // Allocate memory for the image...
if ((pixels = (unsigned char *)calloc(height, width * bpp)) == NULL) if ((pixels = (unsigned char *)calloc(height, linesize)) == NULL)
{ {
_pdfioFileError(dict->pdf, "Unable to allocate memory for PNG image: %s", strerror(errno)); _pdfioFileError(dict->pdf, "Unable to allocate memory for PNG image: %s", strerror(errno));
goto finish_png; goto finish_png;
@ -2596,7 +2583,7 @@ copy_png(pdfio_dict_t *dict, // I - Dictionary
} }
for (i = 0; i < height; i ++) for (i = 0; i < height; i ++)
rows[i] = pixels + i * width * bpp; rows[i] = pixels + i * linesize;
// Read the image... // Read the image...
for (i = png_set_interlace_handling(pp); i > 0; i --) for (i = png_set_interlace_handling(pp); i > 0; i --)
@ -2605,13 +2592,67 @@ copy_png(pdfio_dict_t *dict, // I - Dictionary
// Add image dictionary information... // Add image dictionary information...
pdfioDictSetNumber(dict, "Width", width); pdfioDictSetNumber(dict, "Width", width);
pdfioDictSetNumber(dict, "Height", height); pdfioDictSetNumber(dict, "Height", height);
pdfioDictSetNumber(dict, "BitsPerComponent", 8); pdfioDictSetNumber(dict, "BitsPerComponent", depth);
// Grab any color space/palette information... // Grab any color space/palette information...
pdfioDictSetArray(dict, "ColorSpace", pdfioArrayCreateColorFromStandard(dict->pdf, num_colors, PDFIO_CS_SRGB)); if (png_get_PLTE(pp, info, &palette, &num_palette))
{
pdfioDictSetArray(dict, "ColorSpace", pdfioArrayCreateColorFromPalette(dict->pdf, num_palette, (unsigned char *)palette));
}
else if (png_get_cHRM(pp, info, &wx, &wy, &rx, &ry, &gx, &gy, &bx, &by) && png_get_gAMA(pp, info, &gamma))
{
pdfioDictSetArray(dict, "ColorSpace", pdfioArrayCreateColorFromPrimaries(dict->pdf, num_colors, gamma, wx, wy, rx, ry, gx, gy, bx, by));
}
else
{
// Default to sRGB...
pdfioDictSetArray(dict, "ColorSpace", pdfioArrayCreateColorFromStandard(dict->pdf, num_colors, PDFIO_CS_SRGB));
}
if (png_get_tRNS(pp, info, /*trans_alpha*/NULL, &num_trans, &trans))
{
int m; // Looping var
pdfio_array_t *mask; // Mask array
mask = pdfioArrayCreate(dict->pdf);
if (color_type & PNG_COLOR_MASK_PALETTE)
{
// List color indices that are transparent...
for (m = 0; m < num_trans; m ++)
{
pdfioArrayAppendNumber(mask, trans[m].index);
pdfioArrayAppendNumber(mask, trans[m].index);
}
}
else if (num_colors == 1)
{
// List grayscale values that are transparent...
for (m = 0; m < num_trans; m ++)
{
pdfioArrayAppendNumber(mask, trans[m].gray >> (16 - depth));
pdfioArrayAppendNumber(mask, trans[m].gray >> (16 - depth));
}
}
else
{
// List RGB color values that are transparent...
for (m = 0; m < num_trans; m ++)
{
pdfioArrayAppendNumber(mask, trans[m].red >> (16 - depth));
pdfioArrayAppendNumber(mask, trans[m].green >> (16 - depth));
pdfioArrayAppendNumber(mask, trans[m].blue >> (16 - depth));
pdfioArrayAppendNumber(mask, trans[m].red >> (16 - depth));
pdfioArrayAppendNumber(mask, trans[m].green >> (16 - depth));
pdfioArrayAppendNumber(mask, trans[m].blue >> (16 - depth));
}
}
pdfioDictSetArray(dict, "Mask", mask);
}
// Create the image object... // Create the image object...
obj = create_image(dict, pixels, width, height, num_colors, alpha); obj = create_image(dict, pixels, width, height, depth, num_colors, alpha);
finish_png: finish_png:
@ -2648,11 +2689,6 @@ copy_png(pdfio_dict_t *dict, // I - Dictionary
unsigned char bit_depth = 0, // Bit depth unsigned char bit_depth = 0, // Bit depth
color_type = 0, // Color type color_type = 0, // Color type
interlace = 0; // Interlace type interlace = 0; // Interlace type
double gamma = 2.2, // Gamma value
wx = 0.0, wy = 0.0, // White point chromacity
rx = 0.0, ry = 0.0, // Red chromacity
gx = 0.0, gy = 0.0, // Green chromacity
bx = 0.0, by = 0.0; // Blue chromacity
pdfio_array_t *mask = NULL; // Color masking array pdfio_array_t *mask = NULL; // Color masking array
@ -2803,7 +2839,7 @@ copy_png(pdfio_dict_t *dict, // I - Dictionary
return (NULL); return (NULL);
} }
num_colors = (color_type == _PDFIO_PNG_TYPE_RGB || color_type == _PDFIO_PNG_TYPE_RGBA) ? 3 : 1; num_colors = color_type == _PDFIO_PNG_TYPE_RGB ? 3 : 1;
pdfioDictSetNumber(dict, "Width", width); pdfioDictSetNumber(dict, "Width", width);
pdfioDictSetNumber(dict, "Height", height); pdfioDictSetNumber(dict, "Height", height);
@ -3304,6 +3340,7 @@ create_image(
const unsigned char *data, // I - Image data const unsigned char *data, // I - Image data
size_t width, // I - Width in columns size_t width, // I - Width in columns
size_t height, // I - Height in lines size_t height, // I - Height in lines
size_t depth, // I - Bit depth
size_t num_colors, // I - Number of colors size_t num_colors, // I - Number of colors
bool alpha) // I - `true` if there is transparency bool alpha) // I - `true` if there is transparency
{ {
@ -3316,7 +3353,9 @@ create_image(
// Mask image object, if any // Mask image object, if any
pdfio_stream_t *st; // Image stream pdfio_stream_t *st; // Image stream
size_t x, y, // X and Y position in image size_t x, y, // X and Y position in image
bpp, // Bytes per pixel bpc = depth / 8,// Bytes per component
bpp = num_colors * bpc,
// Bytes per pixel (less alpha)
linelen; // Line length linelen; // Line length
const unsigned char *dataptr; // Pointer into image data const unsigned char *dataptr; // Pointer into image data
unsigned char *line = NULL, // Current line unsigned char *line = NULL, // Current line
@ -3324,8 +3363,7 @@ create_image(
// Allocate memory for one line of data... // Allocate memory for one line of data...
bpp = alpha ? num_colors + 1 : num_colors; linelen = (width * num_colors * depth + 7) / 8;
linelen = num_colors * width;
if ((line = malloc(linelen)) == NULL) if ((line = malloc(linelen)) == NULL)
return (NULL); return (NULL);
@ -3353,7 +3391,7 @@ create_image(
return (NULL); return (NULL);
} }
pdfioDictSetNumber(decode, "BitsPerComponent", 8); pdfioDictSetNumber(decode, "BitsPerComponent", depth);
pdfioDictSetNumber(decode, "Colors", 1); pdfioDictSetNumber(decode, "Colors", 1);
pdfioDictSetNumber(decode, "Columns", width); pdfioDictSetNumber(decode, "Columns", width);
pdfioDictSetNumber(decode, "Predictor", _PDFIO_PREDICTOR_PNG_AUTO); pdfioDictSetNumber(decode, "Predictor", _PDFIO_PREDICTOR_PNG_AUTO);
@ -3373,12 +3411,23 @@ create_image(
return (NULL); return (NULL);
} }
for (y = height, dataptr = data + num_colors; y > 0; y --) for (y = height, dataptr = data + bpp; y > 0; y --)
{ {
for (x = width, lineptr = line; x > 0; x --, dataptr += bpp) if (bpc == 1)
*lineptr++ = *dataptr; {
for (x = width, lineptr = line; x > 0; x --, dataptr += bpp)
*lineptr++ = *dataptr++;
}
else
{
for (x = width, lineptr = line; x > 0; x --, dataptr += bpp)
{
*lineptr++ = *dataptr++;
*lineptr++ = *dataptr++;
}
}
pdfioStreamWrite(st, line, width); pdfioStreamWrite(st, line, width * bpc);
} }
pdfioStreamClose(st); pdfioStreamClose(st);
@ -3394,7 +3443,7 @@ create_image(
return (NULL); return (NULL);
} }
pdfioDictSetNumber(decode, "BitsPerComponent", 8); pdfioDictSetNumber(decode, "BitsPerComponent", depth);
pdfioDictSetNumber(decode, "Colors", num_colors); pdfioDictSetNumber(decode, "Colors", num_colors);
pdfioDictSetNumber(decode, "Columns", width); pdfioDictSetNumber(decode, "Columns", width);
pdfioDictSetNumber(decode, "Predictor", _PDFIO_PREDICTOR_PNG_AUTO); pdfioDictSetNumber(decode, "Predictor", _PDFIO_PREDICTOR_PNG_AUTO);
@ -3418,29 +3467,15 @@ create_image(
{ {
if (alpha) if (alpha)
{ {
switch (num_colors) if (bpp == 1)
{ {
case 1 : for (x = width, lineptr = line; x > 0; x --, dataptr += bpc)
for (x = width, lineptr = line; x > 0; x --, dataptr += bpp) *lineptr++ = *dataptr++;
*lineptr++ = *dataptr; }
break; else
case 3 : {
for (x = width, lineptr = line; x > 0; x --, dataptr += bpp) for (x = width, lineptr = line; x > 0; x --, dataptr += bpp + bpc, lineptr += bpp)
{ memcpy(lineptr, dataptr, bpp);
*lineptr++ = dataptr[0];
*lineptr++ = dataptr[1];
*lineptr++ = dataptr[2];
}
break;
case 4 :
for (x = width, lineptr = line; x > 0; x --, dataptr += bpp)
{
*lineptr++ = dataptr[0];
*lineptr++ = dataptr[1];
*lineptr++ = dataptr[2];
*lineptr++ = dataptr[3];
}
break;
} }
pdfioStreamWrite(st, line, linelen); pdfioStreamWrite(st, line, linelen);