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
size_t width, // I - Width 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
bool alpha, // I - `true` if data contains an alpha channel
bool interpolate) // I - Interpolate image data?
{
pdfio_dict_t *dict, // Image dictionary
@ -1652,24 +1653,34 @@ pdfioFileCreateImageObjFromData(
// Mask image object, if any
pdfio_stream_t *st; // Image stream
size_t x, y, // X and Y position in image
bpp, // Bytes per pixel
linelen; // Line length
const unsigned char *dataptr; // Pointer into image data
unsigned char *line = NULL, // Current line
*lineptr; // Pointer into line
static const char *defcolors[] = // Default ColorSpace values
{
NULL,
"DeviceGray",
NULL,
"DeviceRGB",
"DeviceCMYK"
};
// 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);
// 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)
return (NULL);
// Generate a mask image, as needed...
if (!(num_colors & 1))
if (alpha)
{
// Create the image mask dictionary...
if ((dict = pdfioDictCreate(pdf)) == NULL)
@ -1711,9 +1722,9 @@ pdfioFileCreateImageObjFromData(
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;
pdfioStreamWrite(st, line, width);
@ -1739,7 +1750,7 @@ pdfioFileCreateImageObjFromData(
if (color_data)
pdfioDictSetArray(dict, "ColorSpace", color_data);
else
pdfioDictSetArray(dict, "ColorSpace", pdfioArrayCreateColorFromMatrix(pdf, num_colors < 3 ? 1 : 3, pdfioSRGBGamma, pdfioSRGBMatrix, pdfioSRGBWhitePoint));
pdfioDictSetName(dict, "ColorSpace", defcolors[num_colors]);
if (mask_obj)
pdfioDictSetObj(dict, "SMask", mask_obj);
@ -1751,7 +1762,7 @@ pdfioFileCreateImageObjFromData(
}
pdfioDictSetNumber(decode, "BitsPerComponent", 8);
pdfioDictSetNumber(decode, "Colors", num_colors < 3 ? 1 : 3);
pdfioDictSetNumber(decode, "Colors", num_colors);
pdfioDictSetNumber(decode, "Columns", width);
pdfioDictSetNumber(decode, "Predictor", _PDFIO_PREDICTOR_PNG_AUTO);
pdfioDictSetDict(dict, "DecodeParms", decode);
@ -1771,30 +1782,39 @@ pdfioFileCreateImageObjFromData(
for (y = height, dataptr = data; y > 0; y --)
{
switch (num_colors)
if (alpha)
{
case 1 :
pdfioStreamWrite(st, dataptr, linelen);
dataptr += linelen;
break;
case 2 :
for (x = width, lineptr = line; x > 0; x --, dataptr += num_colors)
*lineptr++ = *dataptr;
pdfioStreamWrite(st, line, linelen);
break;
case 3 :
pdfioStreamWrite(st, dataptr, linelen);
dataptr += linelen;
break;
case 4 :
for (x = width, lineptr = line; x > 0; x --, dataptr += num_colors)
{
*lineptr++ = dataptr[0];
*lineptr++ = dataptr[1];
*lineptr++ = dataptr[2];
}
pdfioStreamWrite(st, line, linelen);
break;
switch (num_colors)
{
case 1 :
for (x = width, lineptr = line; x > 0; x --, dataptr += bpp)
*lineptr++ = *dataptr;
break;
case 3 :
for (x = width, lineptr = line; x > 0; x --, 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);
}
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 *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 *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;
// 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_peek_cb(const char **s, char *buffer, size_t bytes);
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_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);
@ -417,15 +418,19 @@ do_unit_tests(void)
if (write_images_test(outpdf, 7, helvetica))
return (1);
// Test TrueType fonts...
if (write_font_test(outpdf, 8, helvetica, false))
// Write a page width alpha (soft masks)...
if (write_alpha_test(outpdf, 8, helvetica))
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);
// Print this text file...
if (write_text_test(outpdf, 10, helvetica, "README.md"))
if (write_text_test(outpdf, 11, helvetica, "README.md"))
return (1);
// 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...
//
@ -1594,9 +1775,10 @@ write_image_object(
//
static int // O - 1 on failure, 0 on success
write_images_test(pdfio_file_t *pdf, // I - PDF file
int number, // I - Page number
pdfio_obj_t *font) // I - Text font
write_images_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