Rework pdfioFileCreateImageObjFromData to have a separate alpha argument so

that CMYK images can be supported.

Add unit tests.
This commit is contained in:
Michael R Sweet 2021-07-18 09:23:39 -04:00
parent 014c5dccba
commit 3e0507ba6c
No known key found for this signature in database
GPG Key ID: BE67C75EC81F3244
3 changed files with 241 additions and 39 deletions

View File

@ -1641,8 +1641,9 @@ pdfioFileCreateImageObjFromData(
const unsigned char *data, // I - Pointer to image data const unsigned char *data, // I - Pointer to image data
size_t width, // I - Width of image size_t width, // I - Width of image
size_t height, // I - Height of image size_t height, // I - Height of image
int num_colors, // I - Number of colors size_t num_colors, // I - Number of colors
pdfio_array_t *color_data, // I - Colorspace data or `NULL` for default pdfio_array_t *color_data, // I - Colorspace data or `NULL` for default
bool alpha, // I - `true` if data contains an alpha channel
bool interpolate) // I - Interpolate image data? bool interpolate) // I - Interpolate image data?
{ {
pdfio_dict_t *dict, // Image dictionary pdfio_dict_t *dict, // Image dictionary
@ -1652,24 +1653,34 @@ pdfioFileCreateImageObjFromData(
// 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
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
*lineptr; // Pointer into line *lineptr; // Pointer into line
static const char *defcolors[] = // Default ColorSpace values
{
NULL,
"DeviceGray",
NULL,
"DeviceRGB",
"DeviceCMYK"
};
// Range check input... // Range check input...
if (!pdf || !data || !width || !height || num_colors < 1 || num_colors > 4) if (!pdf || !data || !width || !height || num_colors < 1 || num_colors == 2 || num_colors > 4)
return (NULL); return (NULL);
// Allocate memory for one line of data... // Allocate memory for one line of data...
linelen = (num_colors < 3 ? 1 : 3) * width; bpp = alpha ? num_colors + 1 : num_colors;
linelen = num_colors * width;
if ((line = malloc(linelen)) == NULL) if ((line = malloc(linelen)) == NULL)
return (NULL); return (NULL);
// Generate a mask image, as needed... // Generate a mask image, as needed...
if (!(num_colors & 1)) if (alpha)
{ {
// Create the image mask dictionary... // Create the image mask dictionary...
if ((dict = pdfioDictCreate(pdf)) == NULL) if ((dict = pdfioDictCreate(pdf)) == NULL)
@ -1711,9 +1722,9 @@ pdfioFileCreateImageObjFromData(
return (NULL); return (NULL);
} }
for (y = height, dataptr = data + num_colors - 1; y > 0; y --) for (y = height, dataptr = data + num_colors; y > 0; y --)
{ {
for (x = width, lineptr = line; x > 0; x --, dataptr += num_colors) for (x = width, lineptr = line; x > 0; x --, dataptr += bpp)
*lineptr++ = *dataptr; *lineptr++ = *dataptr;
pdfioStreamWrite(st, line, width); pdfioStreamWrite(st, line, width);
@ -1739,7 +1750,7 @@ pdfioFileCreateImageObjFromData(
if (color_data) if (color_data)
pdfioDictSetArray(dict, "ColorSpace", color_data); pdfioDictSetArray(dict, "ColorSpace", color_data);
else else
pdfioDictSetArray(dict, "ColorSpace", pdfioArrayCreateColorFromMatrix(pdf, num_colors < 3 ? 1 : 3, pdfioSRGBGamma, pdfioSRGBMatrix, pdfioSRGBWhitePoint)); pdfioDictSetName(dict, "ColorSpace", defcolors[num_colors]);
if (mask_obj) if (mask_obj)
pdfioDictSetObj(dict, "SMask", mask_obj); pdfioDictSetObj(dict, "SMask", mask_obj);
@ -1751,7 +1762,7 @@ pdfioFileCreateImageObjFromData(
} }
pdfioDictSetNumber(decode, "BitsPerComponent", 8); pdfioDictSetNumber(decode, "BitsPerComponent", 8);
pdfioDictSetNumber(decode, "Colors", num_colors < 3 ? 1 : 3); 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);
pdfioDictSetDict(dict, "DecodeParms", decode); pdfioDictSetDict(dict, "DecodeParms", decode);
@ -1771,30 +1782,39 @@ pdfioFileCreateImageObjFromData(
for (y = height, dataptr = data; y > 0; y --) for (y = height, dataptr = data; y > 0; y --)
{ {
switch (num_colors) if (alpha)
{ {
case 1 : switch (num_colors)
pdfioStreamWrite(st, dataptr, linelen); {
dataptr += linelen; case 1 :
break; for (x = width, lineptr = line; x > 0; x --, dataptr += bpp)
case 2 : *lineptr++ = *dataptr;
for (x = width, lineptr = line; x > 0; x --, dataptr += num_colors) break;
*lineptr++ = *dataptr; case 3 :
pdfioStreamWrite(st, line, linelen); for (x = width, lineptr = line; x > 0; x --, dataptr += bpp)
break; {
case 3 : *lineptr++ = dataptr[0];
pdfioStreamWrite(st, dataptr, linelen); *lineptr++ = dataptr[1];
dataptr += linelen; *lineptr++ = dataptr[2];
break; }
case 4 : break;
for (x = width, lineptr = line; x > 0; x --, dataptr += num_colors) case 4 :
{ for (x = width, lineptr = line; x > 0; x --, dataptr += bpp)
*lineptr++ = dataptr[0]; {
*lineptr++ = dataptr[1]; *lineptr++ = dataptr[0];
*lineptr++ = dataptr[2]; *lineptr++ = dataptr[1];
} *lineptr++ = dataptr[2];
pdfioStreamWrite(st, line, linelen); *lineptr++ = dataptr[3];
break; }
break;
}
pdfioStreamWrite(st, line, linelen);
}
else
{
pdfioStreamWrite(st, dataptr, linelen);
dataptr += linelen;
} }
} }

View File

@ -147,7 +147,7 @@ extern bool pdfioContentTextShowJustified(pdfio_stream_t *st, bool unicode, siz
extern pdfio_obj_t *pdfioFileCreateFontObjFromBase(pdfio_file_t *pdf, const char *name) PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateFontObjFromBase(pdfio_file_t *pdf, const char *name) PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateFontObjFromFile(pdfio_file_t *pdf, const char *filename, bool unicode) PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateFontObjFromFile(pdfio_file_t *pdf, const char *filename, bool unicode) PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateICCObjFromFile(pdfio_file_t *pdf, const char *filename, size_t num_colors) PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateICCObjFromFile(pdfio_file_t *pdf, const char *filename, size_t num_colors) PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateImageObjFromData(pdfio_file_t *pdf, const unsigned char *data, size_t width, size_t height, int num_colors, pdfio_array_t *color_data, bool interpolate) PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateImageObjFromData(pdfio_file_t *pdf, const unsigned char *data, size_t width, size_t height, size_t num_colors, pdfio_array_t *color_data, bool alpha, bool interpolate) PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateImageObjFromFile(pdfio_file_t *pdf, const char *filename, bool interpolate) PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateImageObjFromFile(pdfio_file_t *pdf, const char *filename, bool interpolate) PDFIO_PUBLIC;
// Image object helpers... // Image object helpers...

View File

@ -33,6 +33,7 @@ static bool error_cb(pdfio_file_t *pdf, const char *message, bool *error);
static ssize_t token_consume_cb(const char **s, size_t bytes); static ssize_t token_consume_cb(const char **s, size_t bytes);
static ssize_t token_peek_cb(const char **s, char *buffer, size_t bytes); static ssize_t token_peek_cb(const char **s, char *buffer, size_t bytes);
static int verify_image(pdfio_file_t *pdf, size_t number); static int verify_image(pdfio_file_t *pdf, size_t number);
static int write_alpha_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_color_patch(pdfio_stream_t *st, bool device); static int write_color_patch(pdfio_stream_t *st, bool device);
static int write_color_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font); static int write_color_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_font_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font, bool unicode); static int write_font_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font, bool unicode);
@ -417,15 +418,19 @@ do_unit_tests(void)
if (write_images_test(outpdf, 7, helvetica)) if (write_images_test(outpdf, 7, helvetica))
return (1); return (1);
// Test TrueType fonts... // Write a page width alpha (soft masks)...
if (write_font_test(outpdf, 8, helvetica, false)) if (write_alpha_test(outpdf, 8, helvetica))
return (1); return (1);
if (write_font_test(outpdf, 9, helvetica, true)) // Test TrueType fonts...
if (write_font_test(outpdf, 9, helvetica, false))
return (1);
if (write_font_test(outpdf, 10, helvetica, true))
return (1); return (1);
// Print this text file... // Print this text file...
if (write_text_test(outpdf, 10, helvetica, "README.md")) if (write_text_test(outpdf, 11, helvetica, "README.md"))
return (1); return (1);
// Close the test PDF file... // Close the test PDF file...
@ -723,6 +728,182 @@ verify_image(pdfio_file_t *pdf, // I - PDF file
} }
//
// 'write_alpha_test()' - Write a series of test images with alpha channels.
//
static int // O - 1 on failure, 0 on success
write_alpha_test(
pdfio_file_t *pdf, // I - PDF file
int number, // I - Page number
pdfio_obj_t *font) // I - Text font
{
pdfio_dict_t *dict; // Page dictionary
pdfio_stream_t *st; // Page stream
pdfio_obj_t *images[6]; // Images using PNG predictors
char iname[32]; // Image name
int i, // Image number
x, y; // Coordinates in image
unsigned char buffer[1280 * 256], // Buffer for image
*bufptr; // Pointer into buffer
// Create the images...
for (i = 0; i < 6; i ++)
{
size_t num_colors = 0; // Number of colors
// Generate test image data...
switch (i)
{
case 0 : // Grayscale
case 3 : // Grayscale + alpha
num_colors = 1;
for (y = 0, bufptr = buffer; y < 256; y ++)
{
for (x = 0; x < 256; x ++)
{
unsigned char r = (unsigned char)y;
unsigned char g = (unsigned char)(y + x);
unsigned char b = (unsigned char)(y - x);
*bufptr++ = (unsigned char)((r * 30 + g * 59 + b * 11) / 100);
if (i > 2)
{
// Add alpha channel
*bufptr++ = (unsigned char)((x + y) / 2);
}
}
}
break;
case 1 : // RGB
case 4 : // RGB + alpha
num_colors = 3;
for (y = 0, bufptr = buffer; y < 256; y ++)
{
for (x = 0; x < 256; x ++)
{
*bufptr++ = (unsigned char)y;
*bufptr++ = (unsigned char)(y + x);
*bufptr++ = (unsigned char)(y - x);
if (i > 2)
{
// Add alpha channel
*bufptr++ = (unsigned char)((x + y) / 2);
}
}
}
break;
case 2 : // CMYK
case 5 : // CMYK + alpha
num_colors = 4;
for (y = 0, bufptr = buffer; y < 256; y ++)
{
for (x = 0; x < 256; x ++)
{
unsigned char cc = (unsigned char)y;
unsigned char mm = (unsigned char)(y + x);
unsigned char yy = (unsigned char)(y - x);
unsigned char kk = cc < mm ? cc < yy ? cc : yy : mm < yy ? mm : yy;
*bufptr++ = (unsigned char)(cc - kk);
*bufptr++ = (unsigned char)(mm - kk);
*bufptr++ = (unsigned char)(yy - kk);
*bufptr++ = (unsigned char)(kk);
if (i > 2)
{
// Add alpha channel
*bufptr++ = (unsigned char)((x + y) / 2);
}
}
}
break;
}
// Write the image...
printf("pdfioFileCreateImageObjFromData(num_colors=%u, alpha=%s): ", (unsigned)num_colors, i > 2 ? "true" : "false");
if ((images[i] = pdfioFileCreateImageObjFromData(pdf, buffer, 256, 256, num_colors, NULL, i > 2, false)) != NULL)
{
puts("PASS");
}
else
{
puts("FAIL");
return (1);
}
}
// Create the page dictionary, object, and stream...
fputs("pdfioDictCreate: ", stdout);
if ((dict = pdfioDictCreate(pdf)) != NULL)
puts("PASS");
else
return (1);
for (i = 0; i < 6; i ++)
{
printf("pdfioPageDictAddImage(%d): ", i + 1);
snprintf(iname, sizeof(iname), "IM%d", i + 1);
if (pdfioPageDictAddImage(dict, pdfioStringCreate(pdf, iname), images[i]))
puts("PASS");
else
return (1);
}
fputs("pdfioPageDictAddFont(F1): ", stdout);
if (pdfioPageDictAddFont(dict, "F1", font))
puts("PASS");
else
return (1);
printf("pdfioFileCreatePage(%d): ", number);
if ((st = pdfioFileCreatePage(pdf, dict)) != NULL)
puts("PASS");
else
return (1);
if (write_header_footer(st, "Image Writing Test", number))
goto error;
// Draw images
for (i = 0; i < 6; i ++)
{
static const char *labels[] = {
"DeviceGray",
"DeviceRGB",
"DeviceCMYK",
"DeviceGray + Alpha",
"DeviceRGB + Alpha",
"DeviceCMYK + Alpha"
};
snprintf(iname, sizeof(iname), "IM%d", i + 1);
if (draw_image(st, iname, 36 + 180 * (i % 3), 306 - 216 * (i / 3), 144, 144, labels[i]))
goto error;
}
// Wrap up...
fputs("pdfioStreamClose: ", stdout);
if (pdfioStreamClose(st))
puts("PASS");
else
return (1);
return (0);
error:
pdfioStreamClose(st);
return (1);
}
// //
// 'write_color_patch()' - Write a color patch... // 'write_color_patch()' - Write a color patch...
// //
@ -1594,9 +1775,10 @@ write_image_object(
// //
static int // O - 1 on failure, 0 on success static int // O - 1 on failure, 0 on success
write_images_test(pdfio_file_t *pdf, // I - PDF file write_images_test(
int number, // I - Page number pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *font) // I - Text font int number, // I - Page number
pdfio_obj_t *font) // I - Text font
{ {
pdfio_dict_t *dict; // Page dictionary pdfio_dict_t *dict; // Page dictionary
pdfio_stream_t *st; // Page stream pdfio_stream_t *st; // Page stream