mirror of
				https://github.com/michaelrsweet/pdfio.git
				synced 2025-10-31 02:15:48 +01:00 
			
		
		
		
	Rework pdfioFileCreateImageObjFromData to have a separate alpha argument so
that CMYK images can be supported. Add unit tests.
This commit is contained in:
		| @@ -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; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -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... | ||||
|   | ||||
							
								
								
									
										196
									
								
								testpdfio.c
									
									
									
									
									
								
							
							
						
						
									
										196
									
								
								testpdfio.c
									
									
									
									
									
								
							| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user