2 * << Haru Free PDF Library >> -- hpdf_image.c
4 * URL: http://libharu.org
6 * Copyright (c) 1999-2006 Takeshi Kanno <takeshi_kanno@est.hi-ho.ne.jp>
7 * Copyright (c) 2007-2009 Antony Dovgal <tony@daylessday.org>
9 * Permission to use, copy, modify, distribute and sell this software
10 * and its documentation for any purpose is hereby granted without fee,
11 * provided that the above copyright notice appear in all copies and
12 * that both that copyright notice and this permission notice appear
13 * in supporting documentation.
14 * It is provided "as is" without express or implied warranty.
18 #include "hpdf_conf.h"
19 #include "hpdf_utils.h"
20 #include "hpdf_image.h"
22 #ifndef LIBHPDF_HAVE_NOPNGLIB
26 PngErrorFunc (png_structp png_ptr,
31 PngReadFunc (png_structp png_ptr,
35 HPDF_UINT len = length;
36 HPDF_Stream stream = (HPDF_Stream)png_get_io_ptr (png_ptr);
38 HPDF_Stream_Read (stream, data, &len);
43 LoadPngData (HPDF_Dict image,
46 HPDF_BOOL delayed_loading);
50 PngErrorFunc (png_structp png_ptr,
55 ReadPngData_Interlaced (HPDF_Dict image,
61 ReadPngData (HPDF_Dict image,
67 CreatePallet (HPDF_Dict image,
73 PngBeforeWrite (HPDF_Dict obj);
77 PngAfterWrite (HPDF_Dict obj);
80 /*---------------------------------------------------------------------------*/
83 PngErrorFunc (png_structp png_ptr,
86 char error_number[16];
88 HPDF_STATUS detail_no;
91 /* pick out error-number from error message */
92 HPDF_MemSet (error_number, 0, 16);
94 for (i = 0; i < 15; i++) {
95 error_number[i] = *(msg + i);
96 if (*(msg + i + 1) == ' ')
100 error = (HPDF_Error)png_get_error_ptr (png_ptr);
101 detail_no = (HPDF_STATUS)HPDF_AToI (error_number);
102 HPDF_SetError (error, HPDF_LIBPNG_ERROR, detail_no);
107 ReadPngData_Interlaced (HPDF_Dict image,
111 png_uint_32 len = png_get_rowbytes(png_ptr, info_ptr);
112 png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
113 png_bytep* row_pointers = HPDF_GetMem (image->mmgr,
114 height * sizeof (png_bytep));
119 HPDF_MemSet (row_pointers, 0, height * sizeof (png_bytep));
120 for (i = 0; i < (HPDF_UINT)height; i++) {
121 row_pointers[i] = HPDF_GetMem (image->mmgr, len);
123 if (image->error->error_no != HPDF_OK)
127 if (image->error->error_no == HPDF_OK) {
128 png_read_image(png_ptr, row_pointers);
129 if (image->error->error_no == HPDF_OK) { /* add this line */
130 for (i = 0; i < (HPDF_UINT)height; i++) {
131 if (HPDF_Stream_Write (image->stream, row_pointers[i], len) !=
139 for (i = 0; i < (HPDF_UINT)height; i++) {
140 HPDF_FreeMem (image->mmgr, row_pointers[i]);
143 HPDF_FreeMem (image->mmgr, row_pointers);
146 return image->error->error_no;
150 ReadPngData (HPDF_Dict image,
154 png_uint_32 len = png_get_rowbytes(png_ptr, info_ptr);
155 png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
156 png_bytep buf_ptr = HPDF_GetMem (image->mmgr, len);
161 for (i = 0; i < (HPDF_UINT)height; i++) {
162 png_read_rows(png_ptr, (png_byte**)&buf_ptr, NULL, 1);
163 if (image->error->error_no != HPDF_OK)
166 if (HPDF_Stream_Write (image->stream, buf_ptr, len) != HPDF_OK)
170 HPDF_FreeMem (image->mmgr, buf_ptr);
173 return image->error->error_no;
177 ReadTransparentPaletteData (HPDF_Dict image,
180 png_bytep smask_data,
184 HPDF_STATUS ret = HPDF_OK;
187 png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
188 png_uint_32 width = png_get_image_width(png_ptr, info_ptr);
190 row_ptr = HPDF_GetMem (image->mmgr, height * sizeof(png_bytep));
192 return HPDF_FAILD_TO_ALLOC_MEM;
194 png_uint_32 len = png_get_rowbytes(png_ptr, info_ptr);
196 for (i = 0; i < (HPDF_UINT)height; i++) {
197 row_ptr[i] = HPDF_GetMem(image->mmgr, len);
200 HPDF_FreeMem (image->mmgr, row_ptr[i]);
202 HPDF_FreeMem (image->mmgr, row_ptr);
203 return HPDF_FAILD_TO_ALLOC_MEM;
208 png_read_image(png_ptr, row_ptr);
209 if (image->error->error_no != HPDF_OK) {
210 ret = HPDF_INVALID_PNG_IMAGE;
214 for (j = 0; j < height; j++) {
215 for (i = 0; i < width; i++) {
216 smask_data[width * j + i] = (row_ptr[j][i] < num_trans) ? trans[row_ptr[j][i]] : 0xFF;
219 if (HPDF_Stream_Write (image->stream, row_ptr[j], width) != HPDF_OK) {
220 ret = HPDF_FILE_IO_ERROR;
226 for (i = 0; i < (HPDF_UINT)height; i++) {
227 HPDF_FreeMem (image->mmgr, row_ptr[i]);
230 HPDF_FreeMem (image->mmgr, row_ptr);
235 ReadTransparentPngData (HPDF_Dict image,
238 png_bytep smask_data)
240 HPDF_STATUS ret = HPDF_OK;
243 png_bytep *row_ptr, row;
245 png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
246 png_uint_32 width = png_get_image_width(png_ptr, info_ptr);
248 color_type = png_get_color_type(png_ptr, info_ptr);
250 if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
251 return HPDF_INVALID_PNG_IMAGE;
254 row_ptr = HPDF_GetMem (image->mmgr, height * sizeof(png_bytep));
256 return HPDF_FAILD_TO_ALLOC_MEM;
258 png_uint_32 len = png_get_rowbytes(png_ptr, info_ptr);
260 for (i = 0; i < (HPDF_UINT)height; i++) {
261 row_ptr[i] = HPDF_GetMem(image->mmgr, len);
264 HPDF_FreeMem (image->mmgr, row_ptr[i]);
266 HPDF_FreeMem (image->mmgr, row_ptr);
267 return HPDF_FAILD_TO_ALLOC_MEM;
272 png_read_image(png_ptr, row_ptr);
273 if (image->error->error_no != HPDF_OK) {
274 ret = HPDF_INVALID_PNG_IMAGE;
278 switch (color_type) {
279 case PNG_COLOR_TYPE_RGB_ALPHA:
280 row_len = 3 * width * sizeof(png_byte);
281 for (j = 0; j < height; j++) {
282 for (i = 0; i < width; i++) {
284 memmove(row + (3 * i), row + (4*i), 3);
285 smask_data[width * j + i] = row[4 * i + 3];
288 if (HPDF_Stream_Write (image->stream, row, row_len) != HPDF_OK) {
289 ret = HPDF_FILE_IO_ERROR;
294 case PNG_COLOR_TYPE_GRAY_ALPHA:
295 row_len = width * sizeof(png_byte);
296 for (j = 0; j < height; j++) {
297 for (i = 0; i < width; i++) {
300 smask_data[width * j + i] = row[2 * i + 1];
303 if (HPDF_Stream_Write (image->stream, row, row_len) != HPDF_OK) {
304 ret = HPDF_FILE_IO_ERROR;
310 ret = HPDF_INVALID_PNG_IMAGE;
315 for (i = 0; i < (HPDF_UINT)height; i++) {
316 HPDF_FreeMem (image->mmgr, row_ptr[i]);
319 HPDF_FreeMem (image->mmgr, row_ptr);
324 CreatePallet (HPDF_Dict image,
329 png_color *src_pl = NULL;
335 /* png_get_PLTE does not call PngErrorFunc even if it failed.
336 * so we call HPDF_Set_Error to set error-code.
338 if (png_get_PLTE(png_ptr, info_ptr, (png_color**)&src_pl, &num_pl) !=
340 return HPDF_SetError (image->error, HPDF_LIBPNG_ERROR,
341 HPDF_CANNOT_GET_PALLET);
344 /* make a pallet array for indexed image. */
345 ppallet = HPDF_GetMem (image->mmgr, num_pl * 3);
347 return image->error->error_no;
350 for (i = 0; i < num_pl; i++, src_pl++) {
352 *p++ = src_pl->green;
356 array = HPDF_Array_New (image->mmgr);
360 HPDF_Dict_Add (image, "ColorSpace", array);
362 HPDF_Array_AddName (array, "Indexed");
363 HPDF_Array_AddName (array, "DeviceRGB");
364 HPDF_Array_AddNumber (array, num_pl - 1);
366 b = HPDF_Binary_New (image->mmgr, ppallet, num_pl * 3);
368 HPDF_Array_Add (array, b);
371 HPDF_FreeMem (image->mmgr, ppallet);
373 return image->error->error_no;
376 #define HPDF_PNG_BYTES_TO_CHECK 8
379 HPDF_Image_LoadPngImage (HPDF_MMgr mmgr,
380 HPDF_Stream png_data,
382 HPDF_BOOL delayed_loading)
386 png_byte header[HPDF_PNG_BYTES_TO_CHECK];
387 HPDF_UINT len = HPDF_PNG_BYTES_TO_CHECK;
389 HPDF_PTRACE ((" HPDF_Image_LoadPngImage\n"));
391 HPDF_MemSet (header, 0x00, HPDF_PNG_BYTES_TO_CHECK);
392 ret = HPDF_Stream_Read (png_data, header, &len);
393 if (ret != HPDF_OK ||
394 png_sig_cmp (header, (png_size_t)0, HPDF_PNG_BYTES_TO_CHECK)) {
395 HPDF_SetError (mmgr->error, HPDF_INVALID_PNG_IMAGE, 0);
399 image = HPDF_DictStream_New (mmgr, xref);
403 image->header.obj_class |= HPDF_OSUBCLASS_XOBJECT;
404 ret += HPDF_Dict_AddName (image, "Type", "XObject");
405 ret += HPDF_Dict_AddName (image, "Subtype", "Image");
409 if (LoadPngData (image, xref, png_data, delayed_loading) != HPDF_OK)
417 LoadPngData (HPDF_Dict image,
419 HPDF_Stream png_data,
420 HPDF_BOOL delayed_loading)
423 HPDF_STATUS ret = HPDF_OK;
424 png_uint_32 width, height;
425 int bit_depth, color_type;
426 png_structp png_ptr = NULL;
427 png_infop info_ptr = NULL;
429 HPDF_PTRACE ((" HPDF_Image_LoadPngImage\n"));
431 /* create read_struct. */
432 png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
433 image->error, PngErrorFunc, PngErrorFunc);
435 if (png_ptr == NULL) {
436 HPDF_SetError (image->error, HPDF_FAILD_TO_ALLOC_MEM, 0);
437 return HPDF_FAILD_TO_ALLOC_MEM;
440 /* create info-struct */
441 info_ptr = png_create_info_struct (png_ptr);
443 if (info_ptr == NULL) {
444 HPDF_SetError (image->error, HPDF_FAILD_TO_ALLOC_MEM, 0);
448 png_set_sig_bytes (png_ptr, HPDF_PNG_BYTES_TO_CHECK);
449 png_set_read_fn (png_ptr, (void *)png_data, (png_rw_ptr)&PngReadFunc);
451 /* reading info structure. */
452 png_read_info(png_ptr, info_ptr);
453 if (image->error->error_no != HPDF_OK) {
457 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
459 /* 16bit images are not supported. */
460 if (bit_depth == 16) {
461 png_set_strip_16(png_ptr);
464 png_read_update_info(png_ptr, info_ptr);
465 if (image->error->error_no != HPDF_OK) {
469 /* check palette-based images for transparent areas and load them immediately if found */
470 if (xref && PNG_COLOR_TYPE_PALETTE & color_type) {
474 png_bytep smask_data;
476 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ||
477 !png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL)) {
478 goto no_transparent_color_in_palette;
481 smask = HPDF_DictStream_New (image->mmgr, xref);
483 ret = HPDF_FAILD_TO_ALLOC_MEM;
487 smask->header.obj_class |= HPDF_OSUBCLASS_XOBJECT;
488 ret = HPDF_Dict_AddName (smask, "Type", "XObject");
489 ret += HPDF_Dict_AddName (smask, "Subtype", "Image");
490 ret += HPDF_Dict_AddNumber (smask, "Width", (HPDF_UINT)width);
491 ret += HPDF_Dict_AddNumber (smask, "Height", (HPDF_UINT)height);
492 ret += HPDF_Dict_AddName (smask, "ColorSpace", "DeviceGray");
493 ret += HPDF_Dict_AddNumber (smask, "BitsPerComponent", (HPDF_UINT)bit_depth);
495 if (ret != HPDF_OK) {
496 HPDF_Dict_Free(smask);
497 ret = HPDF_INVALID_PNG_IMAGE;
501 smask_data = HPDF_GetMem(image->mmgr, width * height);
503 HPDF_Dict_Free(smask);
504 ret = HPDF_FAILD_TO_ALLOC_MEM;
508 if (ReadTransparentPaletteData(image, png_ptr, info_ptr, smask_data, trans, num_trans) != HPDF_OK) {
509 HPDF_FreeMem(image->mmgr, smask_data);
510 HPDF_Dict_Free(smask);
511 ret = HPDF_INVALID_PNG_IMAGE;
515 if (HPDF_Stream_Write(smask->stream, smask_data, width * height) != HPDF_OK) {
516 HPDF_FreeMem(image->mmgr, smask_data);
517 HPDF_Dict_Free(smask);
518 ret = HPDF_FILE_IO_ERROR;
521 HPDF_FreeMem(image->mmgr, smask_data);
524 ret += CreatePallet(image, png_ptr, info_ptr);
525 ret += HPDF_Dict_AddNumber (image, "Width", (HPDF_UINT)width);
526 ret += HPDF_Dict_AddNumber (image, "Height", (HPDF_UINT)height);
527 ret += HPDF_Dict_AddNumber (image, "BitsPerComponent", (HPDF_UINT)bit_depth);
528 ret += HPDF_Dict_Add (image, "SMask", smask);
530 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
534 no_transparent_color_in_palette:
536 /* read images with alpha channel right away
537 we have to do this because image transparent mask must be added to the Xref */
538 if (xref && PNG_COLOR_MASK_ALPHA & color_type) {
540 png_bytep smask_data;
542 smask = HPDF_DictStream_New (image->mmgr, xref);
544 ret = HPDF_FAILD_TO_ALLOC_MEM;
548 smask->header.obj_class |= HPDF_OSUBCLASS_XOBJECT;
549 ret = HPDF_Dict_AddName (smask, "Type", "XObject");
550 ret += HPDF_Dict_AddName (smask, "Subtype", "Image");
551 ret += HPDF_Dict_AddNumber (smask, "Width", (HPDF_UINT)width);
552 ret += HPDF_Dict_AddNumber (smask, "Height", (HPDF_UINT)height);
553 ret += HPDF_Dict_AddName (smask, "ColorSpace", "DeviceGray");
554 ret += HPDF_Dict_AddNumber (smask, "BitsPerComponent", (HPDF_UINT)bit_depth);
556 if (ret != HPDF_OK) {
557 HPDF_Dict_Free(smask);
558 ret = HPDF_INVALID_PNG_IMAGE;
562 smask_data = HPDF_GetMem(image->mmgr, width * height);
564 HPDF_Dict_Free(smask);
565 ret = HPDF_FAILD_TO_ALLOC_MEM;
569 if (ReadTransparentPngData(image, png_ptr, info_ptr, smask_data) != HPDF_OK) {
570 HPDF_FreeMem(image->mmgr, smask_data);
571 HPDF_Dict_Free(smask);
572 ret = HPDF_INVALID_PNG_IMAGE;
576 if (HPDF_Stream_Write(smask->stream, smask_data, width * height) != HPDF_OK) {
577 HPDF_FreeMem(image->mmgr, smask_data);
578 HPDF_Dict_Free(smask);
579 ret = HPDF_FILE_IO_ERROR;
582 HPDF_FreeMem(image->mmgr, smask_data);
584 if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
585 ret += HPDF_Dict_AddName (image, "ColorSpace", "DeviceGray");
587 ret += HPDF_Dict_AddName (image, "ColorSpace", "DeviceRGB");
589 ret += HPDF_Dict_AddNumber (image, "Width", (HPDF_UINT)width);
590 ret += HPDF_Dict_AddNumber (image, "Height", (HPDF_UINT)height);
591 ret += HPDF_Dict_AddNumber (image, "BitsPerComponent", (HPDF_UINT)bit_depth);
592 ret += HPDF_Dict_Add (image, "SMask", smask);
594 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
598 /* if the image has color palette, copy the pallet of the image to
601 if (color_type == PNG_COLOR_TYPE_PALETTE)
602 ret = CreatePallet(image, png_ptr, info_ptr);
603 else if (color_type == PNG_COLOR_TYPE_GRAY)
604 ret = HPDF_Dict_AddName (image, "ColorSpace", "DeviceGray");
606 ret = HPDF_Dict_AddName (image, "ColorSpace", "DeviceRGB");
612 * if the image is interlaced, read whole image at once.
613 * if delayed_loading is HPDF_TRUE, the data does not load this phase.
615 if (delayed_loading) {
616 image->before_write_fn = PngBeforeWrite;
617 image->after_write_fn = PngAfterWrite;
619 if (png_get_interlace_type(png_ptr, info_ptr) != PNG_INTERLACE_NONE)
620 ret = ReadPngData_Interlaced(image, png_ptr, info_ptr);
622 ret = ReadPngData(image, png_ptr, info_ptr);
628 /* setting the info of the image. */
629 if (HPDF_Dict_AddNumber (image, "Width", (HPDF_UINT)width)
633 if (HPDF_Dict_AddNumber (image, "Height", (HPDF_UINT)height)
637 if (HPDF_Dict_AddNumber (image, "BitsPerComponent",
638 (HPDF_UINT)bit_depth) != HPDF_OK)
642 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
647 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
649 if (ret != HPDF_OK) {
652 return image->error->error_no;
657 PngBeforeWrite (HPDF_Dict obj)
660 png_byte header[HPDF_PNG_BYTES_TO_CHECK];
661 HPDF_UINT len = HPDF_PNG_BYTES_TO_CHECK;
662 HPDF_Stream png_data;
665 HPDF_PTRACE ((" PngBeforeWrite\n"));
667 HPDF_MemStream_FreeData(obj->stream);
669 s = HPDF_Dict_GetItem (obj, "_FILE_NAME", HPDF_OCLASS_STRING);
671 return HPDF_SetError (obj->error, HPDF_MISSING_FILE_NAME_ENTRY, 0);
673 png_data = HPDF_FileReader_New (obj->mmgr, (const char *)(s->value));
674 if (!HPDF_Stream_Validate (png_data))
675 return obj->error->error_no;
677 HPDF_MemSet (header, 0x00, HPDF_PNG_BYTES_TO_CHECK);
678 ret = HPDF_Stream_Read (png_data, header, &len);
679 if (ret != HPDF_OK ||
680 png_sig_cmp (header, (png_size_t)0, HPDF_PNG_BYTES_TO_CHECK)) {
681 HPDF_Stream_Free(png_data);
682 return HPDF_SetError (obj->error, HPDF_INVALID_PNG_IMAGE, 0);
685 if ((ret = LoadPngData (obj, NULL, png_data, HPDF_FALSE)) != HPDF_OK) {
686 HPDF_Stream_Free(png_data);
690 HPDF_Stream_Free(png_data);
697 PngAfterWrite (HPDF_Dict obj)
699 HPDF_PTRACE ((" PngAfterWrite\n"));
701 HPDF_MemStream_FreeData(obj->stream);
707 #endif /* LIBHPDF_HAVE_NOPNGLIB */