bool loadPngFile( IMAGE_T* image, FILE *file) { png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { return false; } png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr, 0, 0); return false; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, 0); return false; } //--------------------------------------------------------------------- png_init_io(png_ptr, file); png_read_info(png_ptr, info_ptr); //--------------------------------------------------------------------- png_byte colour_type = png_get_color_type(png_ptr, info_ptr); png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr); VC_IMAGE_TYPE_T type = VC_IMAGE_RGB888; if (colour_type & PNG_COLOR_MASK_ALPHA) { type = VC_IMAGE_RGBA32; } initImage(image, type, png_get_image_width(png_ptr, info_ptr), png_get_image_height(png_ptr, info_ptr), false); //--------------------------------------------------------------------- double gamma = 0.0; if (png_get_gAMA(png_ptr, info_ptr, &gamma)) { png_set_gamma(png_ptr, 2.2, gamma); } //--------------------------------------------------------------------- if (colour_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); } if ((colour_type == PNG_COLOR_TYPE_GRAY) && (bit_depth < 8)) { png_set_expand_gray_1_2_4_to_8(png_ptr); } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); } if (bit_depth == 16) { #ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED png_set_scale_16(png_ptr); #else png_set_strip_16(png_ptr); #endif } if (colour_type == PNG_COLOR_TYPE_GRAY || colour_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); } //--------------------------------------------------------------------- png_read_update_info(png_ptr, info_ptr); //--------------------------------------------------------------------- png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep)); png_uint_32 j = 0; for (j = 0 ; j < image->height ; ++j) { row_pointers[j] = image->buffer + (j * image->pitch); } //--------------------------------------------------------------------- png_read_image(png_ptr, row_pointers); //--------------------------------------------------------------------- free(row_pointers); png_destroy_read_struct(&png_ptr, &info_ptr, 0); return true; }
int png_include_image (pdf_ximage *ximage, FILE *png_file) { pdf_obj *stream; pdf_obj *stream_dict; pdf_obj *colorspace, *mask, *intent; png_bytep stream_data_ptr; int trans_type; ximage_info info; /* Libpng stuff */ png_structp png_ptr; png_infop png_info_ptr; png_byte bpc, color_type; png_uint_32 width, height, rowbytes; pdf_ximage_init_image_info(&info); stream = NULL; stream_dict = NULL; colorspace = mask = intent = NULL; rewind (png_file); png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, warn); if (png_ptr == NULL || (png_info_ptr = png_create_info_struct (png_ptr)) == NULL) { WARN("%s: Creating Libpng read/info struct failed.", PNG_DEBUG_STR); if (png_ptr) png_destroy_read_struct(&png_ptr, NULL, NULL); return -1; } #if PNG_LIBPNG_VER >= 10603 /* ignore possibly incorrect CMF bytes */ png_set_option(png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON); #endif /* Inititializing file IO. */ png_init_io (png_ptr, png_file); /* Read PNG info-header and get some info. */ png_read_info(png_ptr, png_info_ptr); color_type = png_get_color_type (png_ptr, png_info_ptr); width = png_get_image_width (png_ptr, png_info_ptr); height = png_get_image_height(png_ptr, png_info_ptr); bpc = png_get_bit_depth (png_ptr, png_info_ptr); /* Ask libpng to convert down to 8-bpc. */ if (bpc > 8) { if (pdf_get_version() < 5) { WARN("%s: 16-bpc PNG requires PDF version 1.5.", PNG_DEBUG_STR); png_set_strip_16(png_ptr); bpc = 8; } } /* Ask libpng to gamma-correct. * It is wrong to assume screen gamma value 2.2 but... * We do gamma correction here only when uncalibrated color space is used. */ if (!png_get_valid(png_ptr, png_info_ptr, PNG_INFO_iCCP) && !png_get_valid(png_ptr, png_info_ptr, PNG_INFO_sRGB) && !png_get_valid(png_ptr, png_info_ptr, PNG_INFO_cHRM) && png_get_valid(png_ptr, png_info_ptr, PNG_INFO_gAMA)) { double G = 1.0; png_get_gAMA (png_ptr, png_info_ptr, &G); png_set_gamma(png_ptr, 2.2, G); } trans_type = check_transparency(png_ptr, png_info_ptr); /* check_transparency() does not do updata_info() */ png_read_update_info(png_ptr, png_info_ptr); rowbytes = png_get_rowbytes(png_ptr, png_info_ptr); /* Values listed below will not be modified in the remaining process. */ info.width = width; info.height = height; info.bits_per_component = bpc; if (compat_mode) info.xdensity = info.ydensity = 72.0 / 100.0; else { png_uint_32 xppm = png_get_x_pixels_per_meter(png_ptr, png_info_ptr); png_uint_32 yppm = png_get_y_pixels_per_meter(png_ptr, png_info_ptr); if (xppm > 0) info.xdensity = 72.0 / 0.0254 / xppm; if (yppm > 0) info.ydensity = 72.0 / 0.0254 / yppm; } stream = pdf_new_stream (STREAM_COMPRESS); stream_dict = pdf_stream_dict(stream); stream_data_ptr = (png_bytep) NEW(rowbytes*height, png_byte); read_image_data(png_ptr, stream_data_ptr, height, rowbytes); /* Non-NULL intent means there is valid sRGB chunk. */ intent = get_rendering_intent(png_ptr, png_info_ptr); if (intent) pdf_add_dict(stream_dict, pdf_new_name("Intent"), intent); switch (color_type) { case PNG_COLOR_TYPE_PALETTE: colorspace = create_cspace_Indexed(png_ptr, png_info_ptr); switch (trans_type) { case PDF_TRANS_TYPE_BINARY: /* Color-key masking */ mask = create_ckey_mask(png_ptr, png_info_ptr); break; case PDF_TRANS_TYPE_ALPHA: /* Soft mask */ mask = create_soft_mask(png_ptr, png_info_ptr, stream_data_ptr, width, height); break; default: /* Nothing to be done here. * No tRNS chunk or image already composited with background color. */ break; } info.num_components = 1; break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_iCCP)) colorspace = create_cspace_ICCBased(png_ptr, png_info_ptr); else if (intent) { colorspace = create_cspace_sRGB(png_ptr, png_info_ptr); } else { colorspace = create_cspace_CalRGB(png_ptr, png_info_ptr); } if (!colorspace) colorspace = pdf_new_name("DeviceRGB"); switch (trans_type) { case PDF_TRANS_TYPE_BINARY: mask = create_ckey_mask(png_ptr, png_info_ptr); break; /* rowbytes changes 4 to 3 at here */ case PDF_TRANS_TYPE_ALPHA: mask = strip_soft_mask(png_ptr, png_info_ptr, stream_data_ptr, &rowbytes, width, height); break; default: mask = NULL; } info.num_components = 3; break; case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_iCCP)) colorspace = create_cspace_ICCBased(png_ptr, png_info_ptr); else if (intent) { colorspace = create_cspace_sRGB(png_ptr, png_info_ptr); } else { colorspace = create_cspace_CalGray(png_ptr, png_info_ptr); } if (!colorspace) colorspace = pdf_new_name("DeviceGray"); switch (trans_type) { case PDF_TRANS_TYPE_BINARY: mask = create_ckey_mask(png_ptr, png_info_ptr); break; case PDF_TRANS_TYPE_ALPHA: mask = strip_soft_mask(png_ptr, png_info_ptr, stream_data_ptr, &rowbytes, width, height); break; default: mask = NULL; } info.num_components = 1; break; default: WARN("%s: Unknown PNG colortype %d.", PNG_DEBUG_STR, color_type); } pdf_add_dict(stream_dict, pdf_new_name("ColorSpace"), colorspace); pdf_add_stream(stream, stream_data_ptr, rowbytes*height); RELEASE(stream_data_ptr); if (mask) { if (trans_type == PDF_TRANS_TYPE_BINARY) pdf_add_dict(stream_dict, pdf_new_name("Mask"), mask); else if (trans_type == PDF_TRANS_TYPE_ALPHA) { if (info.bits_per_component >= 8 && info.width > 64) { pdf_stream_set_predictor(mask, 2, info.width, info.bits_per_component, 1); } pdf_add_dict(stream_dict, pdf_new_name("SMask"), pdf_ref_obj(mask)); pdf_release_obj(mask); } else { WARN("%s: Unknown transparency type...???", PNG_DEBUG_STR); pdf_release_obj(mask); } } /* Finally read XMP Metadata * See, XMP Specification Part 3, Storage in Files * http://www.adobe.com/jp/devnet/xmp.html * * We require libpng version >= 1.6.14 since prior versions * of libpng had a bug that incorrectly treat the compression * flag of iTxt chunks. */ #if PNG_LIBPNG_VER >= 10614 if (pdf_get_version() >= 4) { png_textp text_ptr; pdf_obj *XMP_stream, *XMP_stream_dict; int i, num_text; int have_XMP = 0; num_text = png_get_text(png_ptr, png_info_ptr, &text_ptr, NULL); for (i = 0; i < num_text; i++) { if (!memcmp(text_ptr[i].key, "XML:com.adobe.xmp", 17)) { /* XMP found */ if (text_ptr[i].compression != PNG_ITXT_COMPRESSION_NONE || text_ptr[i].itxt_length == 0) WARN("%s: Invalid value(s) in iTXt chunk for XMP Metadata.", PNG_DEBUG_STR); else if (have_XMP) WARN("%s: Multiple XMP Metadata. Don't know how to treat it.", PNG_DEBUG_STR); else { /* We compress XMP metadata for included images here. * It is not recommended to compress XMP metadata for PDF documents but * we compress XMP metadata for included images here to avoid confusing * application programs that only want PDF document global XMP metadata * and scan for that. */ XMP_stream = pdf_new_stream(STREAM_COMPRESS); XMP_stream_dict = pdf_stream_dict(XMP_stream); pdf_add_dict(XMP_stream_dict, pdf_new_name("Type"), pdf_new_name("Metadata")); pdf_add_dict(XMP_stream_dict, pdf_new_name("Subtype"), pdf_new_name("XML")); pdf_add_stream(XMP_stream, text_ptr[i].text, text_ptr[i].itxt_length); pdf_add_dict(stream_dict, pdf_new_name("Metadata"), pdf_ref_obj(XMP_stream)); pdf_release_obj(XMP_stream); have_XMP = 1; } } } } #endif /* PNG_LIBPNG_VER */ png_read_end(png_ptr, NULL); /* Cleanup */ if (png_info_ptr) png_destroy_info_struct(png_ptr, &png_info_ptr); if (png_ptr) png_destroy_read_struct(&png_ptr, NULL, NULL); if (color_type != PNG_COLOR_TYPE_PALETTE && info.bits_per_component >= 8 && info.height > 64) { pdf_stream_set_predictor(stream, 15, info.width, info.bits_per_component, info.num_components); } pdf_ximage_set_image(ximage, &info, stream); return 0; }
int SplashDecodePng(Splash * splash, png_rw_ptr read_func, void *io_ptr) { int stride; ImageFormat srcFormat; png_uint_32 i, rowbytes; png_bytepp row_pointers = NULL; png_bytep image_data = NULL; int success = 0; double gamma; png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_uint_32 width, height; int bit_depth, color_type; ImageRect srcRect, dstRect; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { goto done; } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { goto done; } if (setjmp(png_ptr->jmpbuf)) { goto done; } png_ptr->io_ptr = io_ptr; png_ptr->read_data_fn = read_func; png_set_sig_bytes(png_ptr, SIG_BYTES); /* we already read the 8 signature bytes */ png_read_info(png_ptr, info_ptr); /* read all PNG info up to image data */ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits, * transparency chunks to full alpha channel; strip 16-bit-per-sample * images to 8 bits per sample; and convert grayscale to RGB[A] * this may be sub-optimal but this simplifies implementation */ png_set_expand(png_ptr); png_set_tRNS_to_alpha(png_ptr); png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); png_set_strip_16(png_ptr); png_set_gray_to_rgb(png_ptr); if (png_get_gAMA(png_ptr, info_ptr, &gamma)) png_set_gamma(png_ptr, 2.2, gamma); png_read_update_info(png_ptr, info_ptr); rowbytes = png_get_rowbytes(png_ptr, info_ptr); if (!SAFE_TO_ALLOC(rowbytes, height)) { goto done; } if ((image_data = (unsigned char *) malloc(rowbytes * height)) == NULL) { goto done; } if (!SAFE_TO_ALLOC(height, sizeof(png_bytep))) { goto done; } if ((row_pointers = (png_bytepp) malloc(height * sizeof(png_bytep))) == NULL) { goto done; } for (i = 0; i < height; ++i) row_pointers[i] = image_data + i * rowbytes; png_read_image(png_ptr, row_pointers); SplashCleanup(splash); splash->width = width; splash->height = height; if (!SAFE_TO_ALLOC(splash->width, splash->imageFormat.depthBytes)) { goto done; } stride = splash->width * splash->imageFormat.depthBytes; if (!SAFE_TO_ALLOC(splash->height, stride)) { goto done; } splash->frameCount = 1; splash->frames = (SplashImage *) malloc(sizeof(SplashImage) * splash->frameCount); if (splash->frames == NULL) { goto done; } splash->loopCount = 1; splash->frames[0].bitmapBits = malloc(stride * splash->height); if (splash->frames[0].bitmapBits == NULL) { free(splash->frames); goto done; } splash->frames[0].delay = 0; /* FIXME: sort out the real format */ initFormat(&srcFormat, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); srcFormat.byteOrder = BYTE_ORDER_MSBFIRST; initRect(&srcRect, 0, 0, width, height, 1, rowbytes, image_data, &srcFormat); initRect(&dstRect, 0, 0, width, height, 1, stride, splash->frames[0].bitmapBits, &splash->imageFormat); convertRect(&srcRect, &dstRect, CVT_COPY); SplashInitFrameShape(splash, 0); png_read_end(png_ptr, NULL); success = 1; done: free(row_pointers); free(image_data); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return success; }
RImage *RLoadPNG(RContext *context, const char *file) { char *tmp; RImage *image = NULL; FILE *f; png_structp png; png_infop pinfo, einfo; png_color_16p bkcolor; int alpha; int x, y, i; double gamma, sgamma; png_uint_32 width, height; int depth, junk, color_type; png_bytep *png_rows; unsigned char *ptr; f = fopen(file, "rb"); if (!f) { RErrorCode = RERR_OPEN; return NULL; } png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, (png_error_ptr) NULL, (png_error_ptr) NULL); if (!png) { RErrorCode = RERR_NOMEMORY; fclose(f); return NULL; } pinfo = png_create_info_struct(png); if (!pinfo) { RErrorCode = RERR_NOMEMORY; fclose(f); png_destroy_read_struct(&png, NULL, NULL); return NULL; } einfo = png_create_info_struct(png); if (!einfo) { RErrorCode = RERR_NOMEMORY; fclose(f); png_destroy_read_struct(&png, &pinfo, NULL); return NULL; } RErrorCode = RERR_INTERNAL; #if PNG_LIBPNG_VER - 0 < 10400 if (setjmp(png->jmpbuf)) { #else if (setjmp(png_jmpbuf(png))) { #endif fclose(f); png_destroy_read_struct(&png, &pinfo, &einfo); if (image) RReleaseImage(image); return NULL; } png_init_io(png, f); png_read_info(png, pinfo); png_get_IHDR(png, pinfo, &width, &height, &depth, &color_type, &junk, &junk, &junk); /* sanity check */ if (width < 1 || height < 1) { fclose(f); png_destroy_read_struct(&png, &pinfo, &einfo); RErrorCode = RERR_BADIMAGEFILE; return NULL; } /* check for an alpha channel */ if (png_get_valid(png, pinfo, PNG_INFO_tRNS)) alpha = True; else alpha = (color_type & PNG_COLOR_MASK_ALPHA); /* allocate RImage */ image = RCreateImage(width, height, alpha); if (!image) { fclose(f); png_destroy_read_struct(&png, &pinfo, &einfo); return NULL; } /* normalize to 8bpp with alpha channel */ if (color_type == PNG_COLOR_TYPE_PALETTE && depth <= 8) png_set_expand(png); if (color_type == PNG_COLOR_TYPE_GRAY && depth <= 8) png_set_expand(png); if (png_get_valid(png, pinfo, PNG_INFO_tRNS)) png_set_expand(png); if (depth == 16) png_set_strip_16(png); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png); /* set gamma correction */ if ((context->attribs->flags & RC_GammaCorrection) && context->depth != 8) { sgamma = (context->attribs->rgamma + context->attribs->ggamma + context->attribs->bgamma) / 3; } else if ((tmp = getenv("DISPLAY_GAMMA")) != NULL) { sgamma = atof(tmp); if (sgamma < 1.0E-3) sgamma = 1; } else { /* blah */ sgamma = 2.2; } if (png_get_gAMA(png, pinfo, &gamma)) png_set_gamma(png, sgamma, gamma); else png_set_gamma(png, sgamma, 0.45); /* do the transforms */ png_read_update_info(png, pinfo); /* set background color */ if (png_get_bKGD(png, pinfo, &bkcolor)) { image->background.red = bkcolor->red >> 8; image->background.green = bkcolor->green >> 8; image->background.blue = bkcolor->blue >> 8; } png_rows = calloc(height, sizeof(char *)); if (!png_rows) { RErrorCode = RERR_NOMEMORY; fclose(f); RReleaseImage(image); png_destroy_read_struct(&png, &pinfo, &einfo); return NULL; } for (y = 0; y < height; y++) { png_rows[y] = malloc(png_get_rowbytes(png, pinfo)); if (!png_rows[y]) { RErrorCode = RERR_NOMEMORY; fclose(f); RReleaseImage(image); png_destroy_read_struct(&png, &pinfo, &einfo); while (y-- > 0) if (png_rows[y]) free(png_rows[y]); free(png_rows); return NULL; } } /* read data */ png_read_image(png, png_rows); png_read_end(png, einfo); png_destroy_read_struct(&png, &pinfo, &einfo); fclose(f); ptr = image->data; /* convert to RImage */ if (alpha) { for (y = 0; y < height; y++) { for (x = 0, i = width * 4; x < i; x++, ptr++) { *ptr = *(png_rows[y] + x); } } } else { for (y = 0; y < height; y++) { for (x = 0, i = width * 3; x < i; x++, ptr++) { *ptr = *(png_rows[y] + x); } } } for (y = 0; y < height; y++) if (png_rows[y]) free(png_rows[y]); free(png_rows); return image; }
BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file, BOOL raw, BOOL alpha) { png_struct *png_ptr = NULL; png_info *info_ptr = NULL; png_byte buf[8]; png_byte *png_pixels = NULL; png_byte **row_pointers = NULL; png_byte *pix_ptr = NULL; png_uint_32 row_bytes; png_uint_32 width; png_uint_32 height; int bit_depth; int channels; int color_type; int alpha_present; int row, col; int ret; int i; long dep_16; /* read and check signature in PNG file */ ret = fread (buf, 1, 8, png_file); if (ret != 8) return FALSE; ret = !png_sig_cmp (buf, 0, 8); if (!ret) return FALSE; /* create png and info structures */ png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) return FALSE; /* out of memory */ info_ptr = png_create_info_struct (png_ptr); if (!info_ptr) { png_destroy_read_struct (&png_ptr, NULL, NULL); return FALSE; /* out of memory */ } if (setjmp (png_jmpbuf(png_ptr))) { png_destroy_read_struct (&png_ptr, &info_ptr, NULL); return FALSE; } /* set up the input control for C streams */ png_init_io (png_ptr, png_file); png_set_sig_bytes (png_ptr, 8); /* we already read the 8 signature bytes */ /* read the file information */ png_read_info (png_ptr, info_ptr); /* get size and bit-depth of the PNG-image */ png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); /* set-up the transformations */ /* transform paletted images into full-color rgb */ if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand (png_ptr); /* expand images to bit-depth 8 (only applicable for grayscale images) */ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand (png_ptr); /* transform transparency maps into full alpha-channel */ if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand (png_ptr); #ifdef NJET /* downgrade 16-bit images to 8 bit */ if (bit_depth == 16) png_set_strip_16 (png_ptr); /* transform grayscale images into full-color */ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb (png_ptr); /* only if file has a file gamma, we do a correction */ if (png_get_gAMA (png_ptr, info_ptr, &file_gamma)) png_set_gamma (png_ptr, (double) 2.2, file_gamma); #endif /* all transformations have been registered; now update info_ptr data, * get rowbytes and channels, and allocate image memory */ png_read_update_info (png_ptr, info_ptr); /* get the new color-type and bit-depth (after expansion/stripping) */ png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); /* check for 16-bit files */ if (bit_depth == 16) { raw = FALSE; #ifdef __TURBOC__ pnm_file->flags &= ~((unsigned) _F_BIN); #endif } /* calculate new number of channels and store alpha-presence */ if (color_type == PNG_COLOR_TYPE_GRAY) channels = 1; else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) channels = 2; else if (color_type == PNG_COLOR_TYPE_RGB) channels = 3; else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) channels = 4; else channels = 0; /* should never happen */ alpha_present = (channels - 1) % 2; /* check if alpha is expected to be present in file */ if (alpha && !alpha_present) { fprintf (stderr, "PNG2PNM\n"); fprintf (stderr, "Error: PNG-file doesn't contain alpha channel\n"); exit (1); } /* row_bytes is the width x number of channels x (bit-depth / 8) */ row_bytes = png_get_rowbytes (png_ptr, info_ptr); if ((png_pixels = (png_byte *) malloc (row_bytes * height * sizeof (png_byte))) == NULL) { png_destroy_read_struct (&png_ptr, &info_ptr, NULL); return FALSE; } if ((row_pointers = (png_byte **) malloc (height * sizeof (png_bytep))) == NULL) { png_destroy_read_struct (&png_ptr, &info_ptr, NULL); free (png_pixels); png_pixels = NULL; return FALSE; } /* set the individual row_pointers to point at the correct offsets */ for (i = 0; i < (height); i++) row_pointers[i] = png_pixels + i * row_bytes; /* now we can go ahead and just read the whole image */ png_read_image (png_ptr, row_pointers); /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ png_read_end (png_ptr, info_ptr); /* clean up after the read, and free any memory allocated - REQUIRED */ png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp) NULL); /* write header of PNM file */ if ((color_type == PNG_COLOR_TYPE_GRAY) || (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) { fprintf (pnm_file, "%s\n", (raw) ? "P5" : "P2"); fprintf (pnm_file, "%d %d\n", (int) width, (int) height); fprintf (pnm_file, "%ld\n", ((1L << (int) bit_depth) - 1L)); } else if ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGB_ALPHA)) { fprintf (pnm_file, "%s\n", (raw) ? "P6" : "P3"); fprintf (pnm_file, "%d %d\n", (int) width, (int) height); fprintf (pnm_file, "%ld\n", ((1L << (int) bit_depth) - 1L)); } /* write header of PGM file with alpha channel */ if ((alpha) && ((color_type == PNG_COLOR_TYPE_GRAY_ALPHA) || (color_type == PNG_COLOR_TYPE_RGB_ALPHA))) { fprintf (alpha_file, "%s\n", (raw) ? "P5" : "P2"); fprintf (alpha_file, "%d %d\n", (int) width, (int) height); fprintf (alpha_file, "%ld\n", ((1L << (int) bit_depth) - 1L)); } /* write data to PNM file */ pix_ptr = png_pixels; for (row = 0; row < height; row++) { for (col = 0; col < width; col++) { for (i = 0; i < (channels - alpha_present); i++) { if (raw) fputc ((int) *pix_ptr++ , pnm_file); else if (bit_depth == 16){ dep_16 = (long) *pix_ptr++; fprintf (pnm_file, "%ld ", (dep_16 << 8) + ((long) *pix_ptr++)); } else fprintf (pnm_file, "%ld ", (long) *pix_ptr++); } if (alpha_present) { if (!alpha) { pix_ptr++; /* alpha */ if (bit_depth == 16) pix_ptr++; } else /* output alpha-channel as pgm file */ { if (raw) fputc ((int) *pix_ptr++ , alpha_file); else if (bit_depth == 16){ dep_16 = (long) *pix_ptr++; fprintf (alpha_file, "%ld ", (dep_16 << 8) + (long) *pix_ptr++); } else fprintf (alpha_file, "%ld ", (long) *pix_ptr++); } } /* if alpha_present */ if (!raw) if (col % 4 == 3) fprintf (pnm_file, "\n"); } /* end for col */ if (!raw) if (col % 4 != 0) fprintf (pnm_file, "\n"); } /* end for row */ if (row_pointers != (unsigned char**) NULL) free (row_pointers); if (png_pixels != (unsigned char*) NULL) free (png_pixels); return TRUE; } /* end of source */
unsigned char * ReadPNG(FILE *infile,int *width, int *height, XColor *colrs) { unsigned char *pixmap; unsigned char *p; png_byte *q; png_struct *png_ptr; png_info *info_ptr; png_uint_32 raw_width, raw_height, rowbytes; int bit_depth, color_type, interlace_type, compression_type, filter_type; png_uint_32 have_palette; png_colorp palette; int num_palette; png_uint_16p hist = NULL; double gamma, screen_gamma; png_byte *png_pixels=NULL, **row_pointers=NULL; int i, j; unsigned int packets; png_color std_color_cube[216]; /* first check to see if its a valid PNG file. If not, return. */ /* we assume that infile is a valid filepointer */ { int ret; png_byte buf[8]; ret = fread(buf, 1, 8, infile); if(ret != 8) return 0; ret = png_sig_cmp(buf, 0, 8); if(ret) return(0); } /* OK, it is a valid PNG file, so let's rewind it, and start decoding it */ rewind(infile); /* allocate the structures */ /*png_ptr = (png_struct *)malloc(sizeof(png_struct));*/ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(!png_ptr) return 0; /* initialize the structures */ info_ptr = png_create_info_struct(png_ptr); if(!info_ptr) { /*free(png_ptr);*/ return 0; } /* Establish the setjmp return context for png_error to use. */ if (setjmp(png_jmpbuf(png_ptr))) { #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr, "\n!!!libpng read error!!!\n"); } #endif /*png_read_destroy(png_ptr, info_ptr, (png_info *)0); */ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); if(png_pixels != NULL) free((char *)png_pixels); if(row_pointers != NULL) free((png_byte **)row_pointers); /*free((char *)png_ptr);*/ free((char *)info_ptr); return 0; } #ifdef SAM_NO /* SWP -- Hopefully to fix cores on bad PNG files */ png_set_message_fn(png_ptr,png_get_msg_ptr(png_ptr),NULL,NULL); #endif /*png_read_init(png_ptr);*/ /* set up the input control */ png_init_io(png_ptr, infile); /* read the file information */ png_read_info(png_ptr, info_ptr); /* setup other stuff using the fields of png_info. */ png_get_IHDR(png_ptr, info_ptr, &raw_width, &raw_height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type); rowbytes = png_get_rowbytes(png_ptr, info_ptr); have_palette = png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); *width = (int)raw_width; *height = (int)raw_height; #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"\n\nBEFORE\nwidth = %d\n", *width); fprintf(stderr,"height = %d\n", *height); fprintf(stderr,"bit depth = %d\n", bit_depth); fprintf(stderr,"color type = %d\n", color_type); fprintf(stderr,"compression type = %d\n", compression_type); fprintf(stderr,"filter type = %d\n", filter_type); fprintf(stderr,"interlace type = %d\n", interlace_type); fprintf(stderr,"num colors = %d\n", num_palette); fprintf(stderr,"rowbytes = %d\n", rowbytes); } #endif #if 0 /* This handles alpha and transparency by replacing it with a background value. */ /* its #if'ed out for now cause I don't have anything to test it with */ { png_color_16 my_background; if (info_ptr->valid & PNG_INFO_bKGD) png_set_background(png_ptr, &(info_ptr->background), PNG_GAMMA_FILE, 1, 1.0); else png_set_background(png_ptr, &my_background, PNG_GAMMA_SCREEN, 0, 1.0); } #endif /* strip pixels in 16-bit images down to 8 bits */ if (bit_depth == 16) png_set_strip_16(png_ptr); /* If it is a color image then check if it has a palette. If not then dither the image to 256 colors, and make up a palette */ if (color_type==PNG_COLOR_TYPE_RGB || color_type==PNG_COLOR_TYPE_RGB_ALPHA) { if(!have_palette) { #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"dithering (RGB->palette)...\n"); } #endif /* if there is is no valid palette, then we need to make one up */ for(i=0;i<216;i++) { /* 255.0/5 = 51 */ std_color_cube[i].red=(i%6)*51; std_color_cube[i].green=((i/6)%6)*51; std_color_cube[i].blue=(i/36)*51; } /* this should probably be dithering to Rdata.colors_per_inlined_image colors */ png_set_quantize(png_ptr, std_color_cube, 216, 216, NULL, 1); } else { #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"dithering (RGB->file supplied palette)...\n"); } #endif png_get_hIST(png_ptr, info_ptr, &hist); png_set_quantize(png_ptr, palette, num_palette, get_pref_int(eCOLORS_PER_INLINED_IMAGE), hist, 1); } } /* PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as they can. This expands pixels to 1 pixel per byte, and if a transparency value is supplied, an alpha channel is built.*/ if (bit_depth < 8) png_set_packing(png_ptr); /* have libpng handle the gamma conversion */ if (get_pref_boolean(eUSE_SCREEN_GAMMA)) { /*SWP*/ if (bit_depth != 16) { /* temporary .. glennrp */ screen_gamma=(double)(get_pref_float(eSCREEN_GAMMA)); #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"screen gamma=%f\n",screen_gamma); } #endif if (png_get_gAMA(png_ptr, info_ptr, &gamma)) { #ifndef DISABLE_TRACE if (srcTrace) { printf("setting gamma=%f\n", gamma); } #endif png_set_gamma(png_ptr, screen_gamma, gamma); } else { #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"setting gamma=%f\n",0.45); } #endif png_set_gamma(png_ptr, screen_gamma, (double)0.45); } } } if (interlace_type) png_set_interlace_handling(png_ptr); png_read_update_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &raw_width, &raw_height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type); rowbytes = png_get_rowbytes(png_ptr, info_ptr); have_palette = png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"\n\nAFTER\nwidth = %d\n", *width); fprintf(stderr,"height = %d\n", *height); fprintf(stderr,"bit depth = %d\n", bit_depth); fprintf(stderr,"color type = %d\n", color_type); fprintf(stderr,"compression type = %d\n", compression_type); fprintf(stderr,"filter type = %d\n", filter_type); fprintf(stderr,"interlace type = %d\n", interlace_type); fprintf(stderr,"num colors = %d\n",num_palette); fprintf(stderr,"rowbytes = %d\n", rowbytes); } #endif /* allocate the pixel grid which we will need to send to png_read_image(). */ png_pixels = (png_byte *)malloc(rowbytes * (*height) * sizeof(png_byte)); row_pointers = (png_byte **) malloc((*height) * sizeof(png_byte *)); for (i=0; i < *height; i++) row_pointers[i]=png_pixels+(rowbytes*i); /* FINALLY - read the darn thing. */ png_read_image(png_ptr, row_pointers); /* now that we have the (transformed to 8-bit RGB) image, we have to copy the resulting palette to our colormap. */ if (color_type & PNG_COLOR_MASK_COLOR) { if (have_palette) { for (i=0; i < num_palette; i++) { colrs[i].red = palette[i].red << 8; colrs[i].green = palette[i].green << 8; colrs[i].blue = palette[i].blue << 8; colrs[i].pixel = i; colrs[i].flags = DoRed|DoGreen|DoBlue; } } else { for (i=0; i < 216; i++) { colrs[i].red = std_color_cube[i].red << 8; colrs[i].green = std_color_cube[i].green << 8; colrs[i].blue = std_color_cube[i].blue << 8; colrs[i].pixel = i; colrs[i].flags = DoRed|DoGreen|DoBlue; } } } else { /* grayscale image */ for(i=0; i < 256; i++ ) { colrs[i].red = i << 8; colrs[i].green = i << 8; colrs[i].blue = i << 8; colrs[i].pixel = i; colrs[i].flags = DoRed|DoGreen|DoBlue; } } /* Now copy the pixel data from png_pixels to pixmap */ pixmap = (png_byte *)malloc((*width) * (*height) * sizeof(png_byte)); p = pixmap; q = png_pixels; /* if there is an alpha channel, we have to get rid of it in the pixmap, since I don't do anything with it yet */ if (color_type & PNG_COLOR_MASK_ALPHA) { #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"Getting rid of alpha channel\n"); } #endif for(i=0; i<*height; i++) { q = row_pointers[i]; for(j=0; j<*width; j++) { *p++ = *q++; /*palette index*/ q++; /* skip the alpha pixel */ } } free((char *)png_pixels); } else { #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"No alpha channel\n"); } #endif for(i=0; i<*height; i++) { q = row_pointers[i]; for(j=0; j<*width; j++) { *p++ = *q++; /*palette index*/ } } free((char *)png_pixels); } free((png_byte **)row_pointers); /* clean up after the read, and free any memory allocated */ /*png_read_destroy(png_ptr, info_ptr, (png_info *)0);*/ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); /* free the structures */ /*free((char *)png_ptr);*/ free((char *)info_ptr); return pixmap; }
static void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, float screen_gamma=0.0) { if (screen_gamma != 0.0 && png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) { double file_gamma; png_get_gAMA(png_ptr, info_ptr, &file_gamma); png_set_gamma(png_ptr, screen_gamma, file_gamma); } png_uint_32 width; png_uint_32 height; int bit_depth; int color_type; png_bytep trans_alpha = 0; png_color_16p trans_color_p = 0; int num_trans; png_colorp palette = 0; int num_palette; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); png_set_interlace_handling(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY) { // Black & White or 8-bit grayscale if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) { png_set_invert_mono(png_ptr); png_read_update_info(png_ptr, info_ptr); if (image.size() != QSize(width, height) || image.format() != QImage::Format_Mono) { image = QImage(width, height, QImage::Format_Mono); if (image.isNull()) return; } image.setColorCount(2); image.setColor(1, qRgb(0,0,0)); image.setColor(0, qRgb(255,255,255)); } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_expand(png_ptr); png_set_strip_16(png_ptr); png_set_gray_to_rgb(png_ptr); if (image.size() != QSize(width, height) || image.format() != QImage::Format_ARGB32) { image = QImage(width, height, QImage::Format_ARGB32); if (image.isNull()) return; } if (QSysInfo::ByteOrder == QSysInfo::BigEndian) png_set_swap_alpha(png_ptr); png_read_update_info(png_ptr, info_ptr); } else { if (bit_depth == 16) png_set_strip_16(png_ptr); else if (bit_depth < 8) png_set_packing(png_ptr); int ncols = bit_depth < 8 ? 1 << bit_depth : 256; png_read_update_info(png_ptr, info_ptr); if (image.size() != QSize(width, height) || image.format() != QImage::Format_Indexed8) { image = QImage(width, height, QImage::Format_Indexed8); if (image.isNull()) return; } image.setColorCount(ncols); for (int i=0; i<ncols; i++) { int c = i*255/(ncols-1); image.setColor(i, qRgba(c,c,c,0xff)); } if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_color_p) { const int g = trans_color_p->gray; if (g < ncols) { image.setColor(g, 0); } } } } else if (color_type == PNG_COLOR_TYPE_PALETTE && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette) && num_palette <= 256) { // 1-bit and 8-bit color if (bit_depth != 1) png_set_packing(png_ptr); png_read_update_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); QImage::Format format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8; if (image.size() != QSize(width, height) || image.format() != format) { image = QImage(width, height, format); if (image.isNull()) return; } png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); image.setColorCount(num_palette); int i = 0; if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_alpha) { while (i < num_trans) { image.setColor(i, qRgba( palette[i].red, palette[i].green, palette[i].blue, trans_alpha[i] ) ); i++; } } while (i < num_palette) { image.setColor(i, qRgba( palette[i].red, palette[i].green, palette[i].blue, 0xff ) ); i++; } } else { // 32-bit if (bit_depth == 16) png_set_strip_16(png_ptr); png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); QImage::Format format = QImage::Format_ARGB32; // Only add filler if no alpha, or we can get 5 channel data. if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_filler(png_ptr, 0xff, QSysInfo::ByteOrder == QSysInfo::BigEndian ? PNG_FILLER_BEFORE : PNG_FILLER_AFTER); // We want 4 bytes, but it isn't an alpha channel format = QImage::Format_RGB32; } if (image.size() != QSize(width, height) || image.format() != format) { image = QImage(width, height, format); if (image.isNull()) return; } if (QSysInfo::ByteOrder == QSysInfo::BigEndian) png_set_swap_alpha(png_ptr); png_read_update_info(png_ptr, info_ptr); } // Qt==ARGB==Big(ARGB)==Little(BGRA) if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { png_set_bgr(png_ptr); } }
CDibSection* InformApp::GetImage(const char* path) { // Check if it's a PNG file CStdioFile imageFile; if (!imageFile.Open(path,CFile::modeRead|CFile::typeBinary)) return 0; png_byte fileHeader[8]; imageFile.Read(fileHeader,8); bool isPNG = (png_sig_cmp(fileHeader,0,8) == 0); if (isPNG) { // Prepare to read the PNG file png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,(png_voidp)NULL,NULL,NULL); if (png_ptr == NULL) return 0; png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr,(png_infopp)NULL,(png_infopp)NULL); return 0; } png_infop end_info = png_create_info_struct(png_ptr); if (end_info == NULL) { png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)NULL); return 0; } // Set up the point to return to in case of error png_bytep* rowPointers = NULL; if (setjmp(png_jmpbuf(png_ptr))) { if (rowPointers) delete[] rowPointers; png_destroy_read_struct(&png_ptr,&info_ptr,&end_info); return 0; } png_init_io(png_ptr,imageFile.m_pStream); png_set_sig_bytes(png_ptr,8); png_read_info(png_ptr,info_ptr); // Get details of the image png_uint_32 width = png_get_image_width(png_ptr,info_ptr); png_uint_32 height = png_get_image_height(png_ptr,info_ptr); int bit_depth = png_get_bit_depth(png_ptr,info_ptr); int color_type = png_get_color_type(png_ptr,info_ptr); // Set up transforms to the required format if (color_type == PNG_COLOR_TYPE_PALETTE && bit_depth <= 8) png_set_palette_to_rgb(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); double gamma; if (png_get_gAMA(png_ptr,info_ptr,&gamma)) png_set_gamma(png_ptr,2.2,gamma); if (bit_depth == 16) png_set_strip_16(png_ptr); if (bit_depth < 8) png_set_packing(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); png_set_bgr(png_ptr); png_set_filler(png_ptr,0xFF,PNG_FILLER_AFTER); // Create a bitmap HDC dc = ::GetDC(NULL); CDibSection* dib = new CDibSection(); BOOL created = dib->CreateBitmap(dc,width,height); ::ReleaseDC(NULL,dc); if (!created) { png_destroy_read_struct(&png_ptr,&info_ptr,&end_info); return NULL; } // Read in the image rowPointers = new png_bytep[height]; for (int i = 0; i < (int)height; i++) rowPointers[i] = ((png_bytep)dib->GetBits())+(width*i*4); png_read_image(png_ptr,rowPointers); png_read_end(png_ptr,end_info); delete[] rowPointers; png_destroy_read_struct(&png_ptr,&info_ptr,&end_info); return dib; } else { imageFile.SeekToBegin(); struct jpeg_decompress_struct info; struct JPEGErrorInfo error; // Initialize the error handling info.err = jpeg_std_error(&(error.base)); error.base.error_exit = errorJPEGExit; error.base.output_message = outputJPEGMessage; if (setjmp(error.errorJump)) { jpeg_destroy_decompress(&info); return NULL; } // Set up the decompression jpeg_create_decompress(&info); jpeg_stdio_src(&info,imageFile.m_pStream); // Read the image attributes jpeg_read_header(&info,TRUE); jpeg_calc_output_dimensions(&info); int width = info.output_width; int height = info.output_height; // Force RGB output info.out_color_space = JCS_RGB; // Create a bitmap HDC dc = ::GetDC(NULL); CDibSection* dib = new CDibSection(); BOOL created = dib->CreateBitmap(dc,width,height); ::ReleaseDC(NULL,dc); if (!created) { jpeg_destroy_decompress(&info); return NULL; } // Get an output buffer JSAMPARRAY buffer = (*info.mem->alloc_sarray) ((j_common_ptr)&info,JPOOL_IMAGE,width*3,1); // Read in the image jpeg_start_decompress(&info); while ((int)info.output_scanline < height) { jpeg_read_scanlines(&info,buffer,1); BYTE* pixelRow = ((BYTE*)dib->GetBits())+(width*(info.output_scanline-1)*4); for (int i = 0; i < width; i++) { pixelRow[(i*4)+0] = (*buffer)[(i*3)+2]; pixelRow[(i*4)+1] = (*buffer)[(i*3)+1]; pixelRow[(i*4)+2] = (*buffer)[(i*3)+0]; pixelRow[(i*4)+3] = 0xFF; } } jpeg_finish_decompress(&info); jpeg_destroy_decompress(&info); return dib; } }
void PNGImageDecoder::headerAvailable() { png_structp png = m_reader->pngPtr(); png_infop info = m_reader->infoPtr(); png_uint_32 width = png_get_image_width(png, info); png_uint_32 height = png_get_image_height(png, info); // Protect against large PNGs. See http://bugzil.la/251381 for more details. const unsigned long maxPNGSize = 1000000UL; if (width > maxPNGSize || height > maxPNGSize) { longjmp(JMPBUF(png), 1); return; } // Set the image size now that the image header is available. if (!setSize(width, height)) { longjmp(JMPBUF(png), 1); return; } int bitDepth, colorType, interlaceType, compressionType, filterType, channels; png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, &compressionType, &filterType); // The options we set here match what Mozilla does. // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) png_set_expand(png); png_bytep trns = 0; int trnsCount = 0; if (png_get_valid(png, info, PNG_INFO_tRNS)) { png_get_tRNS(png, info, &trns, &trnsCount, 0); png_set_expand(png); } if (bitDepth == 16) png_set_strip_16(png); if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png); #if USE(QCMSLIB) if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreGammaAndColorProfile) { // We only support color profiles for color PALETTE and RGB[A] PNG. Supporting // color profiles for gray-scale images is slightly tricky, at least using the // CoreGraphics ICC library, because we expand gray-scale images to RGB but we // do not similarly transform the color profile. We'd either need to transform // the color profile or we'd need to decode into a gray-scale image buffer and // hand that to CoreGraphics. bool sRGB = false; ColorProfile colorProfile; getColorProfile(png, info, colorProfile, sRGB); bool imageHasAlpha = (colorType & PNG_COLOR_MASK_ALPHA) || trnsCount; m_reader->createColorTransform(colorProfile, imageHasAlpha, sRGB); m_hasColorProfile = !!m_reader->colorTransform(); } #endif if (!m_hasColorProfile) { // Deal with gamma and keep it under our control. const double inverseGamma = 0.45455; const double defaultGamma = 2.2; double gamma; if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) { const double maxGamma = 21474.83; if ((gamma <= 0.0) || (gamma > maxGamma)) { gamma = inverseGamma; png_set_gAMA(png, info, gamma); } png_set_gamma(png, defaultGamma, gamma); } else { png_set_gamma(png, defaultGamma, inverseGamma); } } // Tell libpng to send us rows for interlaced pngs. if (interlaceType == PNG_INTERLACE_ADAM7) png_set_interlace_handling(png); // Update our info now. png_read_update_info(png, info); channels = png_get_channels(png, info); ASSERT(channels == 3 || channels == 4); m_reader->setHasAlpha(channels == 4); if (m_reader->decodingSizeOnly()) { // If we only needed the size, halt the reader. #if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5) // '0' argument to png_process_data_pause means: Do not cache unprocessed data. m_reader->setReadOffset(m_reader->currentBufferSize() - png_process_data_pause(png, 0)); #else m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size); png->buffer_size = 0; #endif } }
static void setup_qt( QImage& image, png_structp png_ptr, png_infop info_ptr ) { if ( png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) ) { double file_gamma; png_get_gAMA(png_ptr, info_ptr, &file_gamma); #if defined(_OS_MAC_) // a good guess for Mac systems png_set_gamma( png_ptr, 1.7, file_gamma ); #else // a good guess for PC monitors in a bright office or a dim room png_set_gamma( png_ptr, 2.2, file_gamma ); #endif } png_uint_32 width; png_uint_32 height; int bit_depth; int color_type; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); if ( color_type == PNG_COLOR_TYPE_GRAY ) { // Black & White or 8-bit greyscale if ( bit_depth == 1 && info_ptr->channels == 1 ) { png_set_invert_mono( png_ptr ); png_read_update_info( png_ptr, info_ptr ); image.create( width, height, 1, 2, QImage::BigEndian ); image.setColor( 1, qRgb(0,0,0) ); image.setColor( 0, qRgb(255,255,255) ); } else { if ( bit_depth == 16 ) png_set_strip_16(png_ptr); else if ( bit_depth < 8 ) png_set_packing(png_ptr); int ncols = bit_depth < 8 ? 1 << bit_depth : 256; png_read_update_info(png_ptr, info_ptr); image.create(width, height, 8, ncols); for (int i=0; i<ncols; i++) { int c = i*255/(ncols-1); image.setColor( i, qRgba(c,c,c,0xff) ); } if ( png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ) { int g = info_ptr->trans_values.gray; if ( bit_depth > 8 ) { // transparency support disabled for now } else { image.setAlphaBuffer( TRUE ); image.setColor(g, RGB_MASK & image.color(g)); } } } } else if ( color_type == PNG_COLOR_TYPE_PALETTE && png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE) && info_ptr->num_palette <= 256 ) { // 1-bit and 8-bit color if ( bit_depth != 1 ) png_set_packing( png_ptr ); png_read_update_info( png_ptr, info_ptr ); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); image.create(width, height, bit_depth, info_ptr->num_palette, QImage::BigEndian); int i = 0; if ( png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ) { image.setAlphaBuffer( TRUE ); while ( i < info_ptr->num_trans ) { image.setColor(i, qRgba( info_ptr->palette[i].red, info_ptr->palette[i].green, info_ptr->palette[i].blue, info_ptr->trans[i] ) ); i++; } } while ( i < info_ptr->num_palette ) { image.setColor(i, qRgba( info_ptr->palette[i].red, info_ptr->palette[i].green, info_ptr->palette[i].blue, 0xff ) ); i++; } } else { // 32-bit if ( bit_depth == 16 ) png_set_strip_16(png_ptr); png_set_expand(png_ptr); if ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA ) png_set_gray_to_rgb(png_ptr); image.create(width, height, 32); // Only add filler if no alpha, or we can get 5 channel data. if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_filler(png_ptr, 0xff, QImage::systemByteOrder() == QImage::BigEndian ? PNG_FILLER_BEFORE : PNG_FILLER_AFTER); // We want 4 bytes, but it isn't an alpha channel } else { image.setAlphaBuffer(TRUE); } if ( QImage::systemByteOrder() == QImage::BigEndian ) { png_set_swap_alpha(png_ptr); } png_read_update_info(png_ptr, info_ptr); } // Qt==ARGB==Big(ARGB)==Little(BGRA) if ( QImage::systemByteOrder() == QImage::LittleEndian ) { png_set_bgr(png_ptr); } }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { png_structp png_ptr = NULL; png_infop info_ptr; png_uint_32 width, height; png_colorp png_palette = NULL; int color_type, palette_entries = 0; int bit_depth, pixel_depth; // pixel_depth = bit_depth * channels FIBITMAP *dib = NULL; RGBQUAD *palette = NULL; // pointer to dib palette png_bytepp row_pointers = NULL; int i; fi_ioStructure fio; fio.s_handle = handle; fio.s_io = io; if (handle) { BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; try { // check to see if the file is in fact a PNG file BYTE png_check[PNG_BYTES_TO_CHECK]; io->read_proc(png_check, PNG_BYTES_TO_CHECK, 1, handle); if (png_sig_cmp(png_check, (png_size_t)0, PNG_BYTES_TO_CHECK) != 0) { return NULL; // Bad signature } // create the chunk manage structure png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler); if (!png_ptr) { return NULL; } // create the info structure info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return NULL; } // init the IO png_set_read_fn(png_ptr, &fio, _ReadProc); if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return NULL; } // because we have already read the signature... png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); // read the IHDR chunk png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); pixel_depth = png_get_bit_depth(png_ptr, info_ptr) * png_get_channels(png_ptr, info_ptr); // get image data type (assume standard image type) FREE_IMAGE_TYPE image_type = FIT_BITMAP; if (bit_depth == 16) { if ((pixel_depth == 16) && (color_type == PNG_COLOR_TYPE_GRAY)) { image_type = FIT_UINT16; } else if ((pixel_depth == 48) && (color_type == PNG_COLOR_TYPE_RGB)) { image_type = FIT_RGB16; } else if ((pixel_depth == 64) && (color_type == PNG_COLOR_TYPE_RGB_ALPHA)) { image_type = FIT_RGBA16; } else { // tell libpng to strip 16 bit/color files down to 8 bits/color png_set_strip_16(png_ptr); bit_depth = 8; } } #ifndef FREEIMAGE_BIGENDIAN if((image_type == FIT_UINT16) || (image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) { // turn on 16 bit byte swapping png_set_swap(png_ptr); } #endif // set some additional flags switch(color_type) { case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip the RGB pixels to BGR (or RGBA to BGRA) if(image_type == FIT_BITMAP) { png_set_bgr(png_ptr); } #endif break; case PNG_COLOR_TYPE_PALETTE: // expand palette images to the full 8 bits from 2 bits/pixel if (pixel_depth == 2) { png_set_packing(png_ptr); pixel_depth = 8; } break; case PNG_COLOR_TYPE_GRAY: // expand grayscale images to the full 8 bits from 2 bits/pixel // but *do not* expand fully transparent palette entries to a full alpha channel if (pixel_depth == 2) { png_set_expand_gray_1_2_4_to_8(png_ptr); pixel_depth = 8; } break; case PNG_COLOR_TYPE_GRAY_ALPHA: // expand 8-bit greyscale + 8-bit alpha to 32-bit png_set_gray_to_rgb(png_ptr); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip the RGBA pixels to BGRA png_set_bgr(png_ptr); #endif pixel_depth = 32; break; default: throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } // unlike the example in the libpng documentation, we have *no* idea where // this file may have come from--so if it doesn't have a file gamma, don't // do any correction ("do no harm") if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) { double gamma = 0; double screen_gamma = 2.2; if (png_get_gAMA(png_ptr, info_ptr, &gamma) && ( flags & PNG_IGNOREGAMMA ) != PNG_IGNOREGAMMA) { png_set_gamma(png_ptr, screen_gamma, gamma); } } // all transformations have been registered; now update info_ptr data png_read_update_info(png_ptr, info_ptr); // color type may have changed, due to our transformations color_type = png_get_color_type(png_ptr,info_ptr); // create a DIB and write the bitmap header // set up the DIB palette, if needed switch (color_type) { case PNG_COLOR_TYPE_RGB: png_set_invert_alpha(png_ptr); if(image_type == FIT_BITMAP) { dib = FreeImage_AllocateHeader(header_only, width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); } break; case PNG_COLOR_TYPE_RGB_ALPHA: if(image_type == FIT_BITMAP) { dib = FreeImage_AllocateHeader(header_only, width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); } break; case PNG_COLOR_TYPE_PALETTE: dib = FreeImage_AllocateHeader(header_only, width, height, pixel_depth); png_get_PLTE(png_ptr,info_ptr, &png_palette, &palette_entries); palette_entries = MIN((unsigned)palette_entries, FreeImage_GetColorsUsed(dib)); palette = FreeImage_GetPalette(dib); // store the palette for (i = 0; i < palette_entries; i++) { palette[i].rgbRed = png_palette[i].red; palette[i].rgbGreen = png_palette[i].green; palette[i].rgbBlue = png_palette[i].blue; } break; case PNG_COLOR_TYPE_GRAY: dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); if(pixel_depth <= 8) { palette = FreeImage_GetPalette(dib); palette_entries = 1 << pixel_depth; for (i = 0; i < palette_entries; i++) { palette[i].rgbRed = palette[i].rgbGreen = palette[i].rgbBlue = (BYTE)((i * 255) / (palette_entries - 1)); } } break; default: throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } // store the transparency table if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { // array of alpha (transparency) entries for palette png_bytep trans_alpha = NULL; // number of transparent entries int num_trans = 0; // graylevel or color sample values of the single transparent color for non-paletted images png_color_16p trans_color = NULL; png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color); if((color_type == PNG_COLOR_TYPE_GRAY) && trans_color) { // single transparent color if (trans_color->gray < palette_entries) { BYTE table[256]; memset(table, 0xFF, palette_entries); table[trans_color->gray] = 0; FreeImage_SetTransparencyTable(dib, table, palette_entries); } } else if((color_type == PNG_COLOR_TYPE_PALETTE) && trans_alpha) { // transparency table FreeImage_SetTransparencyTable(dib, (BYTE *)trans_alpha, num_trans); } } // store the background color if (png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) { // Get the background color to draw transparent and alpha images over. // Note that even if the PNG file supplies a background, you are not required to // use it - you should use the (solid) application background if it has one. png_color_16p image_background = NULL; RGBQUAD rgbBkColor; if (png_get_bKGD(png_ptr, info_ptr, &image_background)) { rgbBkColor.rgbRed = (BYTE)image_background->red; rgbBkColor.rgbGreen = (BYTE)image_background->green; rgbBkColor.rgbBlue = (BYTE)image_background->blue; rgbBkColor.rgbReserved = 0; FreeImage_SetBackgroundColor(dib, &rgbBkColor); } } // get physical resolution if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) { png_uint_32 res_x, res_y; // we'll overload this var and use 0 to mean no phys data, // since if it's not in meters we can't use it anyway int res_unit_type = PNG_RESOLUTION_UNKNOWN; png_get_pHYs(png_ptr,info_ptr, &res_x, &res_y, &res_unit_type); if (res_unit_type == PNG_RESOLUTION_METER) { FreeImage_SetDotsPerMeterX(dib, res_x); FreeImage_SetDotsPerMeterY(dib, res_y); } } // get possible ICC profile if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) { png_charp profile_name = NULL; png_bytep profile_data = NULL; png_uint_32 profile_length = 0; int compression_type; png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &profile_length); // copy ICC profile data (must be done after FreeImage_AllocateHeader) FreeImage_CreateICCProfile(dib, profile_data, profile_length); } // --- header only mode => clean-up and return if (header_only) { // get possible metadata (it can be located both before and after the image data) ReadMetadata(png_ptr, info_ptr, dib); if (png_ptr) { // clean up after the read, and free any memory allocated - REQUIRED png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } return dib; } // set the individual row_pointers to point at the correct offsets row_pointers = (png_bytepp)malloc(height * sizeof(png_bytep)); if (!row_pointers) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); FreeImage_Unload(dib); return NULL; } // read in the bitmap bits via the pointer table // allow loading of PNG with minor errors (such as images with several IDAT chunks) for (png_uint_32 k = 0; k < height; k++) { row_pointers[height - 1 - k] = FreeImage_GetScanLine(dib, k); } png_set_benign_errors(png_ptr, 1); png_read_image(png_ptr, row_pointers); // check if the bitmap contains transparency, if so enable it in the header if (FreeImage_GetBPP(dib) == 32) { if (FreeImage_GetColorType(dib) == FIC_RGBALPHA) { FreeImage_SetTransparent(dib, TRUE); } else { FreeImage_SetTransparent(dib, FALSE); } } // cleanup if (row_pointers) { free(row_pointers); row_pointers = NULL; } // read the rest of the file, getting any additional chunks in info_ptr png_read_end(png_ptr, info_ptr); // get possible metadata (it can be located both before and after the image data) ReadMetadata(png_ptr, info_ptr, dib); if (png_ptr) { // clean up after the read, and free any memory allocated - REQUIRED png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } return dib; } catch (const char *text) { if (png_ptr) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } if (row_pointers) { free(row_pointers); } if (dib) { FreeImage_Unload(dib); } FreeImage_OutputMessageProc(s_format_id, text); return NULL; } } return NULL; }
// load in the image data Picture PictureLoaderPng::load( io::NFile file ) const { if(!file.isOpen()) { Logger::warning( "LOAD PNG: can't open file %s", file.getFileName().toString().c_str() ); return Picture::getInvalid(); } png_byte buffer[8]; // Read the first few bytes of the PNG file if( file.read(buffer, 8) != 8 ) { Logger::warning( "LOAD PNG: can't read file %s", file.getFileName().toString().c_str() ); return Picture::getInvalid(); } // Check if it really is a PNG file if( png_sig_cmp(buffer, 0, 8) ) { Logger::warning( "LOAD PNG: not really a png %s", file.getFileName().toString().c_str() ); return Picture::getInvalid(); } // Allocate the png read struct png_structp png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, (png_error_ptr)png_cpexcept_error, (png_error_ptr)png_cpexcept_warn); if( !png_ptr ) { Logger::warning( "LOAD PNG: Internal PNG create read struct failure %s", file.getFileName().toString().c_str() ); return Picture::getInvalid(); } // Allocate the png info struct png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { Logger::warning( "LOAD PNG: Internal PNG create info struct failure 5s", file.getFileName().toString().c_str() ); png_destroy_read_struct(&png_ptr, NULL, NULL); return Picture::getInvalid(); } // for proper error handling if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); /* if( RowPointers ) delete [] RowPointers; */ return Picture::getInvalid(); } // changed by zola so we don't need to have public FILE pointers png_set_read_fn(png_ptr, &file, user_read_data_fcn); png_set_sig_bytes(png_ptr, 8); // Tell png that we read the signature png_read_info(png_ptr, info_ptr); // Read the info section of the png file unsigned int Width; unsigned int Height; int BitDepth; int ColorType; { // Use temporary variables to avoid passing casted pointers png_uint_32 w,h; // Extract info png_get_IHDR(png_ptr, info_ptr, &w, &h, &BitDepth, &ColorType, NULL, NULL, NULL); Width=w; Height=h; } // Convert palette color to true color if (ColorType==PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); // Convert low bit colors to 8 bit colors if (BitDepth < 8) { if (ColorType==PNG_COLOR_TYPE_GRAY || ColorType==PNG_COLOR_TYPE_GRAY_ALPHA) png_set_expand_gray_1_2_4_to_8(png_ptr); else png_set_packing(png_ptr); } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); // Convert high bit colors to 8 bit colors if (BitDepth == 16) png_set_strip_16(png_ptr); // Convert gray color to true color if (ColorType==PNG_COLOR_TYPE_GRAY || ColorType==PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); int intent; const double screen_gamma = 2.2; if (png_get_sRGB(png_ptr, info_ptr, &intent)) png_set_gamma(png_ptr, screen_gamma, 0.45455); else { double image_gamma; if (png_get_gAMA(png_ptr, info_ptr, &image_gamma)) png_set_gamma(png_ptr, screen_gamma, image_gamma); else png_set_gamma(png_ptr, screen_gamma, 0.45455); } // Update the changes in between, as we need to get the new color type // for proper processing of the RGBA type png_read_update_info(png_ptr, info_ptr); { // Use temporary variables to avoid passing casted pointers png_uint_32 w,h; // Extract info png_get_IHDR(png_ptr, info_ptr, &w, &h, &BitDepth, &ColorType, NULL, NULL, NULL); Width=w; Height=h; } // Convert RGBA to BGRA if (ColorType==PNG_COLOR_TYPE_RGB_ALPHA) { png_set_bgr(png_ptr); } // Create the image structure to be filled by png data Picture* pic = GfxEngine::instance().createPicture( Size( Width, Height ) ); GfxEngine::instance().loadPicture( *pic ); if( pic->getSize().getArea() == 0 ) { Logger::warning( "LOAD PNG: Internal PNG create image struct failure %s", file.getFileName().toString().c_str() ); png_destroy_read_struct(&png_ptr, NULL, NULL); return Picture::getInvalid(); } if( !Height ) { Logger::warning( "LOAD PNG: Internal PNG create row pointers failure %s", file.getFileName().toString().c_str() ); png_destroy_read_struct(&png_ptr, NULL, NULL); return Picture::getInvalid(); } // Create array of pointers to rows in image data ScopedPtr<unsigned char*> RowPointers( (unsigned char**)new png_bytep[ Height ] ); // Fill array of pointers to rows in image data SDL_LockSurface( pic->getSurface() ); unsigned char* data = (unsigned char*)pic->getSurface()->pixels; for(unsigned int i=0; i<Height; ++i) { RowPointers.data()[i] = data; data += pic->getSurface()->pitch; } // for proper error handling if( setjmp( png_jmpbuf( png_ptr ) ) ) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); GfxEngine::instance().deletePicture( pic ); return Picture::getInvalid(); } // Read data using the library function that handles all transformations including interlacing png_read_image( png_ptr, RowPointers.data() ); png_read_end( png_ptr, NULL ); png_destroy_read_struct( &png_ptr, &info_ptr, 0 ); // Clean up memory SDL_UnlockSurface(pic->getSurface()); return *pic; }
int ReadPngStream(FILE *file, std::vector<unsigned char> * ptr, int * w, int * h, int * depth) { // first check the eight byte PNG signature png_byte pbSig[8]; size_t readcnt = fread(pbSig, 1, 8, file); (void) readcnt; if (png_sig_cmp(pbSig, 0, 8)) { return 0; } // create the two png(-info) structures png_structp png_ptr = nullptr; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, (png_error_ptr)nullptr, (png_error_ptr)nullptr); if (!png_ptr) { return 0; } png_infop info_ptr = nullptr; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, nullptr, nullptr); return 0; } // initialize the png structure png_init_io(png_ptr, file); png_set_sig_bytes(png_ptr, 8); // read all PNG info up to image data png_read_info(png_ptr, info_ptr); // get width, height, bit-depth and color-type png_uint_32 wPNG, hPNG; int iBitDepth; int iColorType; png_get_IHDR(png_ptr, info_ptr, &wPNG, &hPNG, &iBitDepth, &iColorType, nullptr, nullptr, nullptr); // expand images of all color-type to 8-bit if (iColorType == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); if (iBitDepth < 8) png_set_expand(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); if (iBitDepth == 16) // convert 16-bit to 8-bit on the fly png_set_strip_16(png_ptr); double dGamma; // if required set gamma conversion if (png_get_gAMA(png_ptr, info_ptr, &dGamma)) png_set_gamma(png_ptr, (double) 2.2, dGamma); // after the transformations are registered, update info_ptr data png_read_update_info(png_ptr, info_ptr); // get again width, height and the new bit-depth and color-type png_get_IHDR(png_ptr, info_ptr, &wPNG, &hPNG, &iBitDepth, &iColorType, nullptr, nullptr, nullptr); // Get number of byte along a tow png_uint_32 ulRowBytes; ulRowBytes = png_get_rowbytes(png_ptr, info_ptr); // and allocate memory for an array of row-pointers png_byte **ppbRowPointers = nullptr; if ((ppbRowPointers = (png_bytepp) malloc(hPNG * sizeof(png_bytep))) == nullptr) { std::cerr << "PNG: out of memory" << std::endl; return 0; } *w = wPNG; *h = hPNG; *depth = png_get_channels(png_ptr, info_ptr); // now we can allocate memory to store the image ptr->resize((*h)*(*w)*(*depth)); // set the individual row-pointers to point at the correct offsets for (png_uint_32 i = 0; i < hPNG; i++) ppbRowPointers[i] = &((*ptr)[0]) + i * ulRowBytes; // now we can go ahead and just read the whole image png_read_image(png_ptr, ppbRowPointers); // read the additional chunks in the PNG file (not really needed) png_read_end(png_ptr, nullptr); free (ppbRowPointers); png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); return 1; }
ImageData PNGImageCodec::Load(InputStreamPtr& file, const ImageParams& params, ImageCodecMetaInfo& metaInfo) { InputStream* readr = file.GetPtr(); // check if png_ptr if (!CheckIfPng(readr)) { Warn(String("Image file format is not png: ") + params.name); } png_structp png_ptr; png_infop info_ptr; png_uint_32 width, height; int bit_depth, color_type; bool alpha; Color32 keycolor; int keycolor_index = -1; bool hascolorkey = false; ImageType imagetype; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if (png_ptr == NULL) { Error("Could not create read struct"); NEX_THROW_GracefulError(EXCEPT_INVALID_CALL); } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr, NULL, NULL); Error("Could not create info struct"); NEX_THROW_GracefulError(EXCEPT_INVALID_CALL); } png_set_error_fn(png_ptr, (void *) (readr), PngWarn, PngWarn); png_set_read_fn(png_ptr, (void *) (readr), PngReadFile); png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, NULL, NULL); // todo Unstrip png_set_strip_16(png_ptr); if (bit_depth != 8 && bit_depth != 16) { Error("Unsupported bit depth"); NEX_THROW_GracefulError(EXCEPT_INVALID_CALL); } switch (color_type) { case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: if (color_type & PNG_COLOR_MASK_ALPHA) imagetype = GrayAlpha; else { imagetype = Palleted; png_set_strip_alpha(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_color_16p trans_values; png_get_tRNS(png_ptr, info_ptr, 0, 0, &trans_values); hascolorkey = true; keycolor.red = uint8(trans_values->gray) & 0xff; keycolor.green = uint8(trans_values->gray) & 0xff; keycolor.blue = uint8(trans_values->gray) & 0xff; keycolor.alpha = uint8(trans_values->gray) & 0xff; } } break; case PNG_COLOR_TYPE_PALETTE: imagetype = Palleted; alpha = true; if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { // tRNS chunk. Every palette entry gets its own alpha value. png_bytep trans; int num_trans; png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, 0); // see if there is a single entry w/ alpha==0 and all other 255. // if yes use keycolor transparency. bool only_binary_trans = true; for (int32 i = 0; (i < num_trans) && only_binary_trans; i++) { if (trans[i] != 0xff) { only_binary_trans = only_binary_trans && (trans[i] == 0) && (keycolor_index == -1); keycolor_index = i; } } if (!only_binary_trans) { keycolor_index = -1; png_set_palette_to_rgb(png_ptr); png_set_tRNS_to_alpha(png_ptr); imagetype = RGBImage; } else alpha = false; } else alpha = false; break; case PNG_COLOR_TYPE_RGBA: alpha = true; case PNG_COLOR_TYPE_RGB: imagetype = RGBImage; if (!(color_type & PNG_COLOR_MASK_ALPHA)) { alpha = false; if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_color_16p trans_values; png_get_tRNS(png_ptr, info_ptr, 0, 0, &trans_values); hascolorkey = true; keycolor.red = uint8((trans_values->red) & 0xff); keycolor.green = uint8((trans_values->green) & 0xff); keycolor.blue = uint8((trans_values->blue) & 0xff); } } break; } // setup gamma //@ TODO: add code to retrieve the gamma here double screen_gamma = 0; //X_SharedPtr(iSystem)->getUserGamma(); // and comment this screen_gamma = 2.2; int intent; if (png_get_sRGB(png_ptr, info_ptr, &intent)) png_set_gamma(png_ptr, screen_gamma, 0.45455); else { double image_gamma; if (png_get_gAMA(png_ptr, info_ptr, &image_gamma)) png_set_gamma(png_ptr, screen_gamma, image_gamma); else png_set_gamma(png_ptr, screen_gamma, 0.45455); } /* todo Unstrip this */ if (bit_depth > 8) { // tell libpng to strip 16 bit/color files down to 8 bits/color png_set_strip_16(png_ptr); bit_depth = 8; } else if (bit_depth < 8) // Expand pictures with less than 8bpp to 8bpp png_set_packing(png_ptr); // update png_read_update_info(png_ptr, info_ptr); png_uint_32 bytes_per_row = (png_uint_32)png_get_rowbytes(png_ptr, info_ptr); //png_read_image PixelFormat fmt; uint32 bpp = 1; // we dont support anything else // other than RGBImage right now // so! if (imagetype == RGBImage) { fmt = PixelFormat::RGBA8; bpp = 4; } else if (imagetype == GrayAlpha && bytes_per_row == 1) fmt = PixelFormat::R8; else { // pallete // cant handle this Warn(String("PixelFormat not supported.") + params.name); NEX_THROW_GracefulError(EXCEPT_INVALID_CALL); } int numPass = png_set_interlace_handling(png_ptr); ImageData img; uint8* imgDest = (uint8*) NEX_ALLOC(bpp * height * width, MEMCAT_GENERAL); img.data = imgDest; img.format = fmt; metaInfo.metaInfoInited = true; metaInfo.mipLevelsToRead = 0; metaInfo.metaInfo.maxDepth = img.depth = 1; metaInfo.metaInfo.maxHeight = img.height = (uint16) height; metaInfo.metaInfo.maxWidth = img.width = (uint16) width; metaInfo.metaInfo.maxMipMapCount = img.numMipMaps = 1; img.numFaces = 1; uint8* imgRows = 0; if (numPass > 1) { imgRows = (uint8*) NEX_ALLOC(bytes_per_row * height, MEMCAT_GENERAL); } else imgRows = (uint8*) NEX_ALLOC(bytes_per_row, MEMCAT_GENERAL); uint32 srcBpp = bytes_per_row / width; for (uint32 i = 0; (int32) i < numPass; ++i) { for (uint32 y = 0; y < height; y++) { uint8* rowPtr = numPass > 1 ? imgRows + bytes_per_row * y : imgRows; png_read_row(png_ptr, rowPtr, NULL); // write only once if (i == numPass - 1) { for (uint32 x = 0; x < width; x++) { // bgra switch (fmt) { case PixelFormat::RGBA8: imgDest[0] = rowPtr[0]; imgDest[1] = rowPtr[1]; imgDest[2] = rowPtr[2]; if (alpha) imgDest[3] = rowPtr[3]; else imgDest[3] = 0xff; break; case PixelFormat::R8: imgDest[0] = rowPtr[0]; break; } imgDest += bpp; rowPtr += srcBpp; } } } } png_read_end(png_ptr, (png_infop) 0); png_destroy_read_struct(&png_ptr, NULL, NULL); NEX_FREE(imgRows, MEMCAT_GENERAL); if (hascolorkey) { imgDest = (uint8*) img.data; for (uint32 y = 0; y < height; y++) { for (uint32 x = 0; x < width; x++) { switch (fmt) { case PixelFormat::RGBA8: if (imgDest[0] == keycolor.red && imgDest[1] == keycolor.green && imgDest[2] == keycolor.blue) imgDest[3] = 0; break; case PixelFormat::R8: if (imgDest[0] == keycolor.alpha) imgDest[0] = 0; break; } imgDest += bpp; } } } return img; }
void PNGImageDecoder::headerAvailable() { png_structp png = m_reader->pngPtr(); png_infop info = m_reader->infoPtr(); png_uint_32 width = png->width; png_uint_32 height = png->height; // Protect against large images. if (png->width > cMaxPNGSize || png->height > cMaxPNGSize) { longjmp(JMPBUF(png), 1); return; } // We can fill in the size now that the header is available. Avoid memory // corruption issues by neutering setFailed() during this call; if we don't // do this, failures will cause |m_reader| to be deleted, and our jmpbuf // will cease to exist. Note that we'll still properly set the failure flag // in this case as soon as we longjmp(). m_doNothingOnFailure = true; bool result = setSize(width, height); m_doNothingOnFailure = false; if (!result) { longjmp(JMPBUF(png), 1); return; } int bitDepth, colorType, interlaceType, compressionType, filterType, channels; png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, &compressionType, &filterType); // The options we set here match what Mozilla does. // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) png_set_expand(png); png_bytep trns = 0; int trnsCount = 0; if (png_get_valid(png, info, PNG_INFO_tRNS)) { png_get_tRNS(png, info, &trns, &trnsCount, 0); png_set_expand(png); } if (bitDepth == 16) png_set_strip_16(png); if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png); // Deal with gamma and keep it under our control. double gamma; if (png_get_gAMA(png, info, &gamma)) { if ((gamma <= 0.0) || (gamma > cMaxGamma)) { gamma = cInverseGamma; png_set_gAMA(png, info, gamma); } png_set_gamma(png, cDefaultGamma, gamma); } else png_set_gamma(png, cDefaultGamma, cInverseGamma); // Tell libpng to send us rows for interlaced pngs. if (interlaceType == PNG_INTERLACE_ADAM7) png_set_interlace_handling(png); // Update our info now. png_read_update_info(png, info); channels = png_get_channels(png, info); ASSERT(channels == 3 || channels == 4); m_reader->setHasAlpha(channels == 4); if (m_reader->decodingSizeOnly()) { // If we only needed the size, halt the reader. m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size); png->buffer_size = 0; } }
void readPng(GImage* pImage, const unsigned char* pData, size_t nDataSize) { // Check for the PNG signature if(nDataSize < 8 || png_sig_cmp((png_bytep)pData, 0, 8) != 0) throw Ex("not a png file"); // Read all PNG data up until the image data chunk. GPNGReader reader(pData); png_set_read_fn(reader.m_pReadStruct, (png_voidp)&reader, (png_rw_ptr)readFunc); png_read_info(reader.m_pReadStruct, reader.m_pInfoStruct); // Get the image data int depth, color; png_uint_32 width, height; png_get_IHDR(reader.m_pReadStruct, reader.m_pInfoStruct, &width, &height, &depth, &color, NULL, NULL, NULL); if(depth != 8) throw Ex("unexpected depth"); pImage->resize(width, height); // Set gamma correction double dGamma; if (png_get_gAMA(reader.m_pReadStruct, reader.m_pInfoStruct, &dGamma)) png_set_gamma(reader.m_pReadStruct, 2.2, dGamma); else png_set_gamma(reader.m_pReadStruct, 2.2, 1.0 / 2.2); // 1.0 = viewing gamma, 2.2 = screen gamma // Update the 'info' struct with the gamma information png_read_update_info(reader.m_pReadStruct, reader.m_pInfoStruct); // Tell it to expand palettes to full channels png_set_expand(reader.m_pReadStruct); png_set_gray_to_rgb(reader.m_pReadStruct); // Allocate the row pointers unsigned long rowbytes = png_get_rowbytes(reader.m_pReadStruct, reader.m_pInfoStruct); unsigned long channels = rowbytes / width; png_bytep pRawData = (png_bytep)new unsigned char[rowbytes * height]; unsigned int i; { png_bytep* pRows = (png_bytep*)new unsigned char[sizeof(png_bytep) * height]; for(i = 0; i < height; i++) pRows[i] = pRawData + i * rowbytes; png_read_image(reader.m_pReadStruct, pRows); delete[] pRows; } // Copy to the GImage unsigned long nPixels = width * height; unsigned int* pRGBQuads = pImage->m_pPixels; unsigned char *pBytes = pRawData; if(channels > 3) { if(channels != 4) throw Ex("Unexpected number of channels"); for(i = 0; i < nPixels; i++) { *pRGBQuads = gARGB(pBytes[3], pBytes[0], pBytes[1], pBytes[2]); pBytes += channels; pRGBQuads++; } } else if(channels == 3) { for(i = 0; i < nPixels; i++) { *pRGBQuads = gARGB(0xff, pBytes[0], pBytes[1], pBytes[2]); pBytes += channels; pRGBQuads++; } } else { delete[] pRawData; throw Ex("Sorry, loading ", to_str(channels), "-channel png files is not yet supported"); } delete[] pRawData; // Check for additional tags png_read_end(reader.m_pReadStruct, reader.m_pEndInfoStruct); }
/* really_load_png: * Worker routine, used by load_png and load_memory_png. */ static BITMAP *really_load_png(png_structp png_ptr, png_infop info_ptr, RGB *pal) { BITMAP *bmp; PALETTE tmppal; png_uint_32 width, height, rowbytes; int bit_depth, color_type, interlace_type; double image_gamma, screen_gamma; int intent; int bpp, dest_bpp; int number_passes, pass; ASSERT(png_ptr && info_ptr); /* The call to png_read_info() gives us all of the information from the * PNG file before the first IDAT (image data chunk). */ png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single * byte into separate bytes (useful for paletted and grayscale images). */ png_set_packing(png_ptr); /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */ if ((color_type == PNG_COLOR_TYPE_GRAY) && (bit_depth < 8)) png_set_expand(png_ptr); /* Adds a full alpha channel if there is transparency information * in a tRNS chunk. */ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); } /* Convert 16-bits per colour component to 8-bits per colour component. */ if (bit_depth == 16) png_set_strip_16(png_ptr); /* Convert grayscale to RGB triplets */ if ((color_type == PNG_COLOR_TYPE_GRAY) || (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) png_set_gray_to_rgb(png_ptr); /* Optionally, tell libpng to handle the gamma correction for us. */ if (_png_screen_gamma != 0.0) { screen_gamma = get_gamma(); if (png_get_sRGB(png_ptr, info_ptr, &intent)) png_set_gamma(png_ptr, screen_gamma, 0.45455); else { if (png_get_gAMA(png_ptr, info_ptr, &image_gamma)) png_set_gamma(png_ptr, screen_gamma, image_gamma); else png_set_gamma(png_ptr, screen_gamma, 0.45455); } } /* Turn on interlace handling. */ number_passes = png_set_interlace_handling(png_ptr); /* Call to gamma correct and add the background to the palette * and update info structure. */ png_read_update_info(png_ptr, info_ptr); /* Even if the user doesn't supply space for a palette, we want * one for the load process. */ if (!pal) pal = tmppal; /* Palettes. */ if (color_type & PNG_COLOR_MASK_PALETTE) { int num_palette, i; png_colorp palette; if (png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)) { /* We don't actually dither, we just copy the palette. */ for (i = 0; ((i < num_palette) && (i < 256)); i++) { pal[i].r = palette[i].red >> 2; /* 256 -> 64 */ pal[i].g = palette[i].green >> 2; pal[i].b = palette[i].blue >> 2; } for (; i < 256; i++) pal[i].r = pal[i].g = pal[i].b = 0; } }
// This is the interesting function here. It sets up a PNG image as a texture. void loadPngTex(char * fname , struct tImagePNG * pImagePNG , int *hasAlpha ) { // The header of the file will be saved in here char buf[PNG_BYTES_TO_CHECK]; // images infos int bit_depth; int cType; double gamma; FILE *fp; // Open the file and check correct opening /* Open the prospective PNG file. */ if ((fp = fopen(fname, "rb")) == NULL) { printf( "Error: Could not open the texture file [%s]!" , fname ); throw 336; } // Read the PNG header, which is 8 bytes long. /* Read in some of the signature bytes */ if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK) { printf( "Error: Incorrect PNG texture file [%s]!" , fname ); throw 336; } // Check whether the file is a PNG file // png_sig_cmp() checks the given PNG header and returns 0 if it could // be the start of a PNG file. We can use 8 bytes at max for // this comparison. if(png_sig_cmp((png_byte*)buf, (png_size_t)0, PNG_BYTES_TO_CHECK) != 0) { fclose( fp ); printf( "Error: Not a PNG file [%s]!" , fname ); throw 337; } // Create / initialize the png_struct // The png_struct isn't directly accessed by the user (us). // We will later create a png_info from this to get access to the // PNG's infos. // The three 0's in the arg list could be pointers to user defined error // handling functions. 0 means we don't want to specify them, but use // libPNG's default error handling instead. png_infop info_ptr; png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL , NULL , NULL ); if(!png_ptr) { fclose( fp ); printf( "Error: Couldn't create PNG read structure [%s]!" , fname ); throw 338; } // Create / initialize the png_info // The png_info grants the user access to the PNG infos. info_ptr = png_create_info_struct(png_ptr); if(!info_ptr) { // We need to destroy the read_struct png_destroy_read_struct(&png_ptr, NULL, NULL); fclose( fp ); printf( "Error: Couldn't create PNG info structure [%s]!" , fname ); throw 339; } // Setup error handler // This sets the point libPNG jumps back to is an error occurs. if (setjmp(png_jmpbuf(png_ptr))) { /* Free all of the memory associated with the png_ptr and info_ptr */ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); fclose(fp); /* If we get here, we had a problem reading the file */ printf( "Error: Couldn't setup PNG error handler [%s]!" , fname ); throw 340; } /* Set up the input control if you are using standard C streams */ png_init_io(png_ptr, fp); // This tells libPNG that we have already read 8 bytes from the start // of the file (for the header check above). png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); // This reads the PNG file into the read and info structs /* * If you have enough memory to read in the entire image at once, * and you need to specify only transforms that can be controlled * with one of the PNG_TRANSFORM_* bits (this presently excludes * dithering, filling, setting background, and doing gamma * adjustment), then you can read the entire image (including * pixels) into the info structure with this call: */ png_read_info(png_ptr, info_ptr); // Get some basic infos about the image from png_info structure // width & height in px, bit depth // interlace_method, compression_method, & filter_method are ignored png_get_IHDR(png_ptr, info_ptr, &(pImagePNG->sizeX), &(pImagePNG->sizeY), &bit_depth, &cType, 0, 0, 0); // COLOR TYPE read and possible corrections - then reread // Color type: we handle RGB and RGB_ALPHA (with Alpha) // GRAY (luminance) and GRAY_ALPHA (luminance with Alpha) cType = png_get_color_type(png_ptr, info_ptr); // strip the pixels of a PNG stream with 16 bits per channel to 8 bits per channel if (bit_depth == 16) { png_set_strip_16(png_ptr); } // set transformation in png_ptr such that paletted images are expanded to RGB, // grayscale images of bit-depth less than 8 are expanded to 8-bit images // tRNS chunks are expanded to alpha channels if (cType == PNG_COLOR_TYPE_PALETTE) { png_set_expand(png_ptr); } if (bit_depth < 8) { png_set_expand(png_ptr); } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_expand(png_ptr); } // if required set gamma conversion if (png_get_gAMA(png_ptr, info_ptr, &gamma)) { png_set_gamma(png_ptr, (double) 2.2, gamma); } // After the image transformations have been registered update info_ptr data png_read_update_info(png_ptr, info_ptr); // Gets again width, height and the new bit-depth and color-type png_get_IHDR(png_ptr, info_ptr, &(pImagePNG->sizeX), &(pImagePNG->sizeY), &bit_depth, &cType, 0, 0, 0); // We now calculate the *bytes* per pixel from the color type and the // bits per pixel. if((cType == PNG_COLOR_TYPE_RGB) && (bit_depth == 8)) { pImagePNG->bytesPerPixel = 3; *hasAlpha = false; } else if((cType == PNG_COLOR_TYPE_RGB_ALPHA) && (bit_depth == 8)) { pImagePNG->bytesPerPixel = 4; *hasAlpha = true; } else if((cType == PNG_COLOR_TYPE_GRAY) && (bit_depth == 8)) { pImagePNG->bytesPerPixel = 1; *hasAlpha = false; } else if((cType == PNG_COLOR_TYPE_GRAY_ALPHA) && (bit_depth == 8)) { pImagePNG->bytesPerPixel = 2; *hasAlpha = true; } else { /* clean up after the read, and free any memory allocated - REQUIRED */ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); fclose( fp ); printf( "Error: PNG image [%s] type (%d) bit depth (%d) is unsupported!" , fname , cType , bit_depth ); throw 336; } // rowbytes is the width x number of channels. channels is not used currently unsigned int rowbytes = png_get_rowbytes(png_ptr, info_ptr); // unsigned int channels = png_get_channels(png_ptr, info_ptr); // Allocates memory to store the image pImagePNG->data = new GLubyte[pImagePNG->sizeY * rowbytes]; // printf( "PNG image %ldx%ld (%d bitsperpixel/%d bytesPerPixel) size row %d total %ld\n" , // pImagePNG->sizeX , // pImagePNG->sizeY , bit_depth , pImagePNG->bytesPerPixel, // rowbytes , pImagePNG->sizeY * rowbytes); //// Alternate solution without explicit row pointers //// and image read through png_get_rows // Returns a pointer to the array of array of png_bytes that holds the // image data. // png_byte **imageData = png_get_rows(png_ptr, info_ptr); //// must read row after row // for( unsigned int i = 0 ; i < pImagePNG->sizeY ; i++ ) { // printf( "Ind %d\n" , i ); // memcpy(&pImagePNG->data[(pImagePNG->sizeY - i - 1) * rowbytes], // row[i], rowbytes); // } //// Alternate solution with explicit row pointers //// and image read through png_read_image // Allocates memory for an array of row-pointers png_byte ** row = new GLubyte * [pImagePNG->sizeY]; // Sets the row-pointers to point at the correct offsets for (unsigned int i = 0; i < pImagePNG->sizeY; i++) { row[i] = pImagePNG->data + (pImagePNG->sizeY - i - 1) * rowbytes; } // Reads the whole image png_read_image(png_ptr, row); // deallocate the now unuseful row pointers delete [] row; // Free the memory we used - we don't need it anymore /* clean up after the read, and free any memory allocated - REQUIRED */ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); fclose( fp ); }
void png_reader<T>::read(unsigned x0, unsigned y0,image_rgba8& image) { stream_.clear(); stream_.seekg(0, std::ios_base::beg); png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,0,0,0); if (!png_ptr) { throw image_reader_exception("failed to allocate png_ptr"); } // catch errors in a custom way to avoid the need for setjmp png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), user_error_fn, user_warning_fn); png_infop info_ptr; png_struct_guard sguard(&png_ptr,&info_ptr); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) throw image_reader_exception("failed to create info_ptr"); png_set_read_fn(png_ptr, (png_voidp)&stream_, png_read_data); png_read_info(png_ptr, info_ptr); if (color_type_ == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); if (color_type_ == PNG_COLOR_TYPE_GRAY && bit_depth_ < 8) png_set_expand(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); if (bit_depth_ == 16) png_set_strip_16(png_ptr); if (color_type_ == PNG_COLOR_TYPE_GRAY || color_type_ == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); // quick hack -- only work in >=libpng 1.2.7 png_set_add_alpha(png_ptr,0xff,PNG_FILLER_AFTER); //rgba double gamma; if (png_get_gAMA(png_ptr, info_ptr, &gamma)) png_set_gamma(png_ptr, 2.2, gamma); if (x0 == 0 && y0 == 0 && image.width() >= width_ && image.height() >= height_) { if (png_get_interlace_type(png_ptr,info_ptr) == PNG_INTERLACE_ADAM7) { png_set_interlace_handling(png_ptr); // FIXME: libpng bug? // according to docs png_read_image // "..automatically handles interlacing, // so you don't need to call png_set_interlace_handling()" } png_read_update_info(png_ptr, info_ptr); // we can read whole image at once // alloc row pointers const std::unique_ptr<png_bytep[]> rows(new png_bytep[height_]); for (unsigned i=0; i<height_; ++i) rows[i] = (png_bytep)image.get_row(i); png_read_image(png_ptr, rows.get()); } else { png_read_update_info(png_ptr, info_ptr); unsigned w=std::min(unsigned(image.width()),width_ - x0); unsigned h=std::min(unsigned(image.height()),height_ - y0); unsigned rowbytes=png_get_rowbytes(png_ptr, info_ptr); const std::unique_ptr<png_byte[]> row(new png_byte[rowbytes]); //START read image rows for (unsigned i = 0;i < height_; ++i) { png_read_row(png_ptr,row.get(),0); if (i >= y0 && i < (y0 + h)) { image.set_row(i-y0,reinterpret_cast<unsigned*>(&row[x0 * 4]),w); } } //END } png_read_end(png_ptr,0); }
// PNG image handler functions int LoadImagePNG (char* filename, char** image, int *width, int *height, int *channels) { FILE *file; png_structp png = NULL; png_infop info = NULL; png_bytepp row = NULL; png_byte sig[8]; png_uint_32 rowsize; int depth; int type; double gamma; int i; // open the PNG input file if (!filename) { *image = NULL; return 1; } if (!strcmp(filename, "-")) { file = stdin; _setmode(_fileno(file), _O_BINARY); } else if (!(file = fopen(filename, "rb"))) { *image = NULL; return 1; } // first check the eight byte PNG signature fread(sig, 1, 8, file); if (!png_check_sig(sig, 8)) { *image = NULL; return 1; } /* create png struct with the error handlers above */ png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, (png_error_ptr)_ErrorHandlerPNG, (png_error_ptr)NULL); if (!png) { *image = NULL; return 1; } /* create image info struct */ info = png_create_info_struct(png); if (!info) { png_destroy_read_struct(&png, NULL, NULL); *image = NULL; return 1; } /* set up png style error handling */ if (setjmp(png->jmpbuf)) { /* the error is reported inside the handler, but we still need to clean up and return */ png_destroy_read_struct(&png, &info, NULL); if (*image) free(*image); if (row) free(row); if (file != stdin) fclose(file); *image = NULL; return 1; } /* initialize png I/O */ #ifdef PNG_NO_STDIO png_set_read_fn(png, (png_voidp)file, _ReadDataPNG); #else png_init_io(png, file); #endif /* if we are here, we have already read 8 bytes from the file */ png_set_sig_bytes(png, 8); /* png_read_info() returns all information from the file before the first data chunk */ png_read_info(png, info); /* query required image info */ png_get_IHDR(png, info, width, height, &depth, &type, NULL, NULL, NULL); /* expand images of all color-type and bit-depth to 3x8 bit RGB images let the library process things like alpha, transparency, background */ if (depth == 16) png_set_strip_16(png); /* expand paletted colors into rgb triplets */ if (type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png); /* expand grayscale images to 8 bits from 1, 2, or 4 bits */ if (type == PNG_COLOR_TYPE_GRAY && depth < 8) png_set_gray_1_2_4_to_8(png); /* expand paletted or rgb images with transparency to full alpha channels so the data will be available as rgba quartets */ if (png_get_valid(png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png); /* convert grayscale images to rgb */ if (type == PNG_COLOR_TYPE_GRAY || type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png); /* set gamma conversion */ if (png_get_gAMA(png, info, &gamma)) png_set_gamma(png, (double)2.2, gamma); /* after the transformations have been registered update info */ png_read_update_info(png, info); /* get width, height again and the new bit-depth and color-type */ png_get_IHDR(png, info, width, height, &depth, &type, NULL, NULL, NULL); *channels = png_get_channels(png, info); /* row_bytes is the width x number of channels */ rowsize = png_get_rowbytes(png, info); /* now we can allocate memory to store the image */ if (*image) { free(*image); *image = NULL; } *image = (png_byte*)malloc(rowsize*(*height)*sizeof(png_byte)); if (*image == NULL) png_error(png, "out of memory"); /* set up row pointers */ row = (png_bytepp)malloc((*height)*sizeof(png_bytep)); if (row == NULL) png_error(png, "out of memory"); for (i=0; i<(*height); i++) row[i] = &((png_bytep)(*image))[i*rowsize]; /* read the whole image */ png_read_image(png, row); /* finish reading */ png_read_end(png, NULL); /* clean up */ png_destroy_read_struct(&png, &info, NULL); free(row); row = NULL; if (file != stdin) fclose(file); /* return image data */ return 0; }
void PNGImageDecoder::headerAvailable() { png_structp png = m_reader->pngPtr(); png_infop info = m_reader->infoPtr(); png_uint_32 width = png_get_image_width(png, info); png_uint_32 height = png_get_image_height(png, info); // Protect against large images. if (width > cMaxPNGSize || height > cMaxPNGSize) { longjmp(JMPBUF(png), 1); return; } // We can fill in the size now that the header is available. Avoid memory // corruption issues by neutering setFailed() during this call; if we don't // do this, failures will cause |m_reader| to be deleted, and our jmpbuf // will cease to exist. Note that we'll still properly set the failure flag // in this case as soon as we longjmp(). m_doNothingOnFailure = true; bool result = setSize(width, height); m_doNothingOnFailure = false; if (!result) { longjmp(JMPBUF(png), 1); return; } int bitDepth, colorType, interlaceType, compressionType, filterType, channels; png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, &compressionType, &filterType); // The options we set here match what Mozilla does. // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) png_set_expand(png); png_bytep trns = 0; int trnsCount = 0; if (png_get_valid(png, info, PNG_INFO_tRNS)) { png_get_tRNS(png, info, &trns, &trnsCount, 0); png_set_expand(png); } if (bitDepth == 16) png_set_strip_16(png); if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png); if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreGammaAndColorProfile) { // We only support color profiles for color PALETTE and RGB[A] PNG. Supporting // color profiles for gray-scale images is slightly tricky, at least using the // CoreGraphics ICC library, because we expand gray-scale images to RGB but we // do not similarly transform the color profile. We'd either need to transform // the color profile or we'd need to decode into a gray-scale image buffer and // hand that to CoreGraphics. readColorProfile(png, info, m_colorProfile); #if USE(QCMSLIB) bool decodedImageHasAlpha = (colorType & PNG_COLOR_MASK_ALPHA) || trnsCount; m_reader->createColorTransform(m_colorProfile, decodedImageHasAlpha); m_colorProfile.clear(); #endif } // Deal with gamma and keep it under our control. double gamma; if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) { if ((gamma <= 0.0) || (gamma > cMaxGamma)) { gamma = cInverseGamma; png_set_gAMA(png, info, gamma); } png_set_gamma(png, cDefaultGamma, gamma); } else png_set_gamma(png, cDefaultGamma, cInverseGamma); // Tell libpng to send us rows for interlaced pngs. if (interlaceType == PNG_INTERLACE_ADAM7) png_set_interlace_handling(png); // Update our info now. png_read_update_info(png, info); channels = png_get_channels(png, info); ASSERT(channels == 3 || channels == 4); m_reader->setHasAlpha(channels == 4); if (m_reader->decodingSizeOnly()) { // If we only needed the size, halt the reader. #if defined(PNG_LIBPNG_VER_MAJOR) && defined(PNG_LIBPNG_VER_MINOR) && (PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)) // '0' argument to png_process_data_pause means: Do not cache unprocessed data. m_reader->setReadOffset(m_reader->currentBufferSize() - png_process_data_pause(png, 0)); #else m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size); png->buffer_size = 0; #endif } }
int read_PNG(char *file_name) { /* * Took this code from doc/fblib * and adjusted it to the likings * of this program. * ?i * It is really awesome that a NOOB * like me, Noisegate can actually * make useable software on an open * system like this Linux... * * openSOFtWARE rocKS */ png_structp png_ptr; png_infop info_ptr; unsigned int sig_read = 0; png_uint_32 _width, _height, row; int bit_depth, color_type, interlace_type; FILE *fp; float screen_gamma; if ((fp = fopen(file_name, "rb")) == NULL) return -1; /* Create and initialize the png_struct with the desired error handler * functions. If you want to use the default stderr and longjump method, * you can supply NULL for the last three parameters. We also supply the * the compiler header file version, so that we know if the application * was compiled with a compatible version of the library. REQUIRED */ //png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, png_voidp user_error_ptr, user_error_fn, user_warning_fn); png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL); if (png_ptr == NULL){ fclose(fp); return -1; } /* Allocate/initialize the memory for image information. REQUIRED. */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { fclose(fp); png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL); return -1; } /* Set error handling if you are using the setjmp/longjmp method (this is * the normal method of doing things with libpng). REQUIRED unless you * set up your own error handlers in the png_create_read_struct() earlier. */ if (setjmp(png_jmpbuf(png_ptr))) { /* Free all of the memory associated with the png_ptr and info_ptr */ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); fclose(fp); /* If we get here, we had a problem reading the file */ fprintf(stderr, "Error: Couldn't read the file."); return -1; } /* One of the following I/O initialization methods is REQUIRED */ /* Set up the input control if you are using standard C streams */ png_init_io(png_ptr, fp); /* If we have already read some of the signature */ png_set_sig_bytes(png_ptr, sig_read); /* OK, you're doing it the hard way, with the lower-level functions */ /* The call to png_read_info() gives us all of the information from the * PNG file before the first IDAT (image data chunk). REQUIRED */ png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &_width, &_height, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL); /* Set up the data transformations you want. Note that these are all * optional. Only call them if you want/need them. Many of the * transformations only work on specific types of images, and many * are mutually exclusive. */ /* Tell libpng to strip 16 bit/color files down to 8 bits/color */ png_set_strip_16(png_ptr); /* Strip alpha bytes from the input data without combining with the * background (not recommended). */ //png_set_strip_alpha(png_ptr); /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single * byte into separate bytes (useful for paletted and grayscale images). */ png_set_packing(png_ptr); /* Change the order of packed pixels to least significant bit first * (not useful if you are using png_set_packing). */ png_set_packswap(png_ptr); /* Expand paletted colors into true RGB triplets */ if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); /* Expand paletted or RGB images with transparency to full alpha channels * so the data will be available as RGBA quartets. */ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); /* Set the background color to draw transparent and alpha images over. * It is possible to set the red, green, and blue components directly * for paletted images instead of supplying a palette index. Note that * even if the PNG file supplies a background, you are not required to * use it - you should use the (solid) application background if it has one. */ png_color_16 my_background, *image_background; if (png_get_bKGD(png_ptr, info_ptr, &image_background)) png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); else png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); screen_gamma = 1.0; int intent; if (png_get_sRGB(png_ptr, info_ptr, &intent)) png_set_gamma(png_ptr, screen_gamma, 0.45455); else { double image_gamma; if (png_get_gAMA(png_ptr, info_ptr, &image_gamma)) png_set_gamma(png_ptr, screen_gamma, image_gamma); else png_set_gamma(png_ptr, screen_gamma, 0.45455); } /* Invert monochrome files to have 0 as white and 1 as black */ png_set_invert_mono(png_ptr); /* If you want to shift the pixel values from the range [0,255] or * [0,65535] to the original [0,7] or [0,31], or whatever range the * colors were originally in: */ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) { png_color_8p sig_bit_p; png_get_sBIT(png_ptr, info_ptr, &sig_bit_p); png_set_shift(png_ptr, sig_bit_p); } /* Flip the RGB pixels to BGR (or RGBA to BGRA) */ if (color_type & PNG_COLOR_MASK_COLOR) png_set_bgr(png_ptr); /* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ png_set_swap_alpha(png_ptr); /* Swap bytes of 16 bit files to least significant byte first */ png_set_swap(png_ptr); /* Add filler (or alpha) byte (before/after each RGB triplet) */ png_set_filler(png_ptr, 0x00, PNG_FILLER_AFTER); /* Turn on interlace handling. REQUIRED if you are not using * png_read_image(). To see how to handle interlacing passes, * see the png_read_row() method below: */ //number_passes = png_set_interlace_handling(png_ptr); /* Optional call to gamma correct and add the background to the palette * and update info structure. REQUIRED if you are expecting libpng to * update the palette for you (ie you selected such a transform above). */ png_read_update_info(png_ptr, info_ptr); /* Allocate the memory to hold the image using the fields of info_ptr. */ /* The easiest way to read the image: */ png_bytep row_pointers[_height]; /* Clear the pointer array */ for (row = 0; row < _height; row++) row_pointers[row] = NULL; for (row = 0; row < _height; row++) row_pointers[row] = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); /* Now it's time to read the image. One of these methods is REQUIRED */ png_read_image(png_ptr, row_pointers); /* Now mess this rowpointer thing into the tmp_buf * U dont know if width>_width and height>_height * so we trunc the guy */ int maxY = _height>height?height:_height; int maxX = _width>width?finfo.line_length:_width; int pix_offset=0; for (row=0; row<maxY;row++){ pix_offset = Ox*BPP + ((Oy+row)*finfo.line_length); memcpy(tmp_buf+pix_offset, row_pointers[row], maxX*BPP); } /* Read rest of file, and get additional chunks in info_ptr - REQUIRED */ png_read_end(png_ptr, info_ptr); /* At this point you have read the entire image */ /* Clean up after the read, and free any memory allocated - REQUIRED */ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); /* Close the file */ fclose(fp); //printf("png W x H = %i x %i\n", _width, _height); /* That's it */ return 0; }
static gint gegl_buffer_import_png (GeglBuffer *gegl_buffer, GInputStream *stream, gint dest_x, gint dest_y, gint *ret_width, gint *ret_height, const Babl *format, // can be NULL GError **err) { gint width; gint bit_depth; gint bpp; gint number_of_passes=1; png_uint_32 w; png_uint_32 h; png_structp load_png_ptr; png_infop load_info_ptr; guchar *pixels; /*png_bytep *rows;*/ unsigned int i; png_bytep *row_p = NULL; g_return_val_if_fail(stream, -1); if (!check_valid_png_header(stream, err)) { return -1; } load_png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, error_fn, NULL); if (!load_png_ptr) { return -1; } load_info_ptr = png_create_info_struct (load_png_ptr); if (!load_info_ptr) { png_destroy_read_struct (&load_png_ptr, &load_info_ptr, NULL); return -1; } if (setjmp (png_jmpbuf (load_png_ptr))) { png_destroy_read_struct (&load_png_ptr, &load_info_ptr, NULL); if (row_p) g_free (row_p); return -1; } png_set_read_fn(load_png_ptr, stream, read_fn); png_set_sig_bytes (load_png_ptr, 8); // we already read header png_read_info (load_png_ptr, load_info_ptr); { int color_type; int interlace_type; png_get_IHDR (load_png_ptr, load_info_ptr, &w, &h, &bit_depth, &color_type, &interlace_type, NULL, NULL); width = w; if (ret_width) *ret_width = w; if (ret_height) *ret_height = h; if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { png_set_expand (load_png_ptr); bit_depth = 8; } if (png_get_valid (load_png_ptr, load_info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha (load_png_ptr); color_type |= PNG_COLOR_MASK_ALPHA; } switch (color_type) { case PNG_COLOR_TYPE_GRAY: bpp = 1; break; case PNG_COLOR_TYPE_GRAY_ALPHA: bpp = 2; break; case PNG_COLOR_TYPE_RGB: bpp = 3; break; case PNG_COLOR_TYPE_RGB_ALPHA: bpp = 4; break; case (PNG_COLOR_TYPE_PALETTE | PNG_COLOR_MASK_ALPHA): bpp = 4; break; case PNG_COLOR_TYPE_PALETTE: bpp = 3; break; default: g_warning ("color type mismatch"); png_destroy_read_struct (&load_png_ptr, &load_info_ptr, NULL); return -1; } if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb (load_png_ptr); if (bit_depth == 16) bpp = bpp << 1; if (!format) format = get_babl_format(bit_depth, color_type); #if BYTE_ORDER == LITTLE_ENDIAN if (bit_depth == 16) png_set_swap (load_png_ptr); #endif if (interlace_type == PNG_INTERLACE_ADAM7) number_of_passes = png_set_interlace_handling (load_png_ptr); if (png_get_valid (load_png_ptr, load_info_ptr, PNG_INFO_gAMA)) { gdouble gamma; png_get_gAMA (load_png_ptr, load_info_ptr, &gamma); png_set_gamma (load_png_ptr, 2.2, gamma); } else { png_set_gamma (load_png_ptr, 2.2, 0.45455); } png_read_update_info (load_png_ptr, load_info_ptr); } pixels = g_malloc0 (width*bpp); { gint pass; GeglRectangle rect; for (pass=0; pass<number_of_passes; pass++) { for(i=0; i<h; i++) { gegl_rectangle_set (&rect, 0, i, width, 1); if (pass != 0) gegl_buffer_get (gegl_buffer, &rect, 1.0, format, pixels, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); png_read_rows (load_png_ptr, &pixels, NULL, 1); gegl_buffer_set (gegl_buffer, &rect, 0, format, pixels, GEGL_AUTO_ROWSTRIDE); } } } png_read_end (load_png_ptr, NULL); png_destroy_read_struct (&load_png_ptr, &load_info_ptr, NULL); g_free (pixels); return 0; }
bool vtImage::_ReadPNG(const char *filename) { FILE *fp = NULL; uchar header[8]; png_structp png; png_infop info; png_infop endinfo; png_bytep *row_p; png_uint_32 width, height; int depth, color; png_uint_32 i; png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png) { // We compiled against the headers of one version of libpng, but // linked against the libraries from another version. If you get // this, fix the paths in your development environment. return false; } info = png_create_info_struct(png); endinfo = png_create_info_struct(png); fp = vtFileOpen(filename, "rb"); if (fp && fread(header, 1, 8, fp) && png_check_sig(header, 8)) png_init_io(png, fp); else { png_destroy_read_struct(&png, &info, &endinfo); return false; } png_set_sig_bytes(png, 8); png_read_info(png, info); png_get_IHDR(png, info, &width, &height, &depth, &color, NULL, NULL, NULL); if (color == PNG_COLOR_TYPE_GRAY || color == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png); // never strip alpha // { // png_set_strip_alpha(png); // color &= ~PNG_COLOR_MASK_ALPHA; // } // Always expand paletted images if (color == PNG_COLOR_TYPE_PALETTE) png_set_expand(png); /*--GAMMA--*/ // checkForGammaEnv(); double screenGamma = 2.2 / 1.0; #if 0 // Getting the gamma from the PNG file is disabled here, since // PhotoShop writes bizarre gamma values like .227 (PhotoShop 5.0) // or .45 (newer versions) double fileGamma; if (png_get_gAMA(png, info, &fileGamma)) png_set_gamma(png, screenGamma, fileGamma); else #endif png_set_gamma(png, screenGamma, 1.0/2.2); png_read_update_info(png, info); m_pPngData = (png_bytep) malloc(png_get_rowbytes(png, info)*height); row_p = (png_bytep *) malloc(sizeof(png_bytep)*height); bool StandardOrientation = true; for (i = 0; i < height; i++) { if (StandardOrientation) row_p[height - 1 - i] = &m_pPngData[png_get_rowbytes(png, info)*i]; else row_p[i] = &m_pPngData[png_get_rowbytes(png, info)*i]; } png_read_image(png, row_p); free(row_p); int iBitCount; switch (color) { case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_PALETTE: iBitCount = 24; break; case PNG_COLOR_TYPE_GRAY_ALPHA: case PNG_COLOR_TYPE_RGB_ALPHA: iBitCount = 32; break; default: return false; } png_read_end(png, endinfo); png_destroy_read_struct(&png, &info, &endinfo); // Don't free the data, we're going to pass it to OSG // free(m_pPngData); if (fp) fclose(fp); int pixelFormat; uint internalFormat; if (iBitCount == 24) pixelFormat = GL_RGB; else if (iBitCount == 32) pixelFormat = GL_RGBA; if (m_internalformat == -1) internalFormat = pixelFormat; // use default else internalFormat = m_internalformat; // use specific setImage(width, height, 1, internalFormat, // int internalFormat, pixelFormat, // uint pixelFormat GL_UNSIGNED_BYTE, // uint dataType m_pPngData, osg::Image::USE_MALLOC_FREE); return true; }
bool Image::load (const std::string &s) { // // The following is based on the libpng manual. http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-3.1 // // // Check the file header // // Open the file std::ifstream infile; infile.open(s.c_str(), std::ifstream::in | std::ifstream::binary); // Read the header const unsigned int NUM_HEADER_BYTES_TO_READ = 8; char header[NUM_HEADER_BYTES_TO_READ]; infile.read(header, NUM_HEADER_BYTES_TO_READ); if (png_sig_cmp((png_bytep) header, 0, NUM_HEADER_BYTES_TO_READ)) { return false; } // // Read the image data // png_bytep *row_pointers = NULL; // Create and initialize the png_struct with the desired error handler // functions. If you want to use the default stderr and longjump method, // you can supply NULL for the last three parameters. We also supply the // the compiler header file version, so that we know if the application // was compiled with a compatible version of the library. REQUIRED png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { return false; } // Allocate/initialize the memory for image information png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return false; } // Set error handling if you are using the setjmp/longjmp method (this is // the normal method of doing things with libpng). REQUIRED unless you // set up your own error handlers in the png_create_read_struct() earlier. if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); delete row_pointers; // Gets allocated after here, but don't forget the jump! return false; } // Set custom read routine png_set_read_fn(png_ptr, &infile, &png_read_data); // If we have already read some of the signature png_set_sig_bytes(png_ptr, NUM_HEADER_BYTES_TO_READ); // // Read the entire image // // Read PNG header info png_read_info(png_ptr, info_ptr); int bit_depth, color_type, interlace_type; // Header info png_uint_32 width,height; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); // Strip 16 bit down to 8 bit png_set_strip_16(png_ptr); // Extract components to even bytes png_set_packing(png_ptr); // Expand grayscale images to 8 bit png_set_expand(png_ptr); // Add a filler byte when missing png_set_filler(png_ptr,0xFFFFFFFF,PNG_FILLER_AFTER); // Expand grayscale images to rgb png_set_gray_to_rgb(png_ptr); // Update the transformation info within libpng png_read_update_info(png_ptr, info_ptr); // Header info again png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); // Allocate target image allocate(width, height); // Handle Gamma png_set_gamma(png_ptr, 0.0, 0.0); // Setup row pointers row_pointers = new png_bytep[_data_height]; if (!row_pointers) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return false; } for (unsigned int y = 0; y < _data_height; ++y) { row_pointers[y] = reinterpret_cast<png_byte*>( &getPixel(0,y) ); } // Read the entire image png_read_image(png_ptr, row_pointers); // // Clean up // delete row_pointers; png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return true; }
void png_reader::read(unsigned x0, unsigned y0,image_data_32& image) { FILE *fp=fopen(fileName_.c_str(),"rb"); if (!fp) throw image_reader_exception("cannot open image file "+fileName_); png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,0,0,0); if (!png_ptr) { fclose(fp); throw image_reader_exception("failed to allocate png_ptr"); } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr,0,0); fclose(fp); throw image_reader_exception("failed to create info_ptr"); } png_set_read_fn(png_ptr, (png_voidp)fp, png_read_data); png_read_info(png_ptr, info_ptr); if (color_type_ == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); if (color_type_ == PNG_COLOR_TYPE_GRAY && bit_depth_ < 8) png_set_expand(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); if (bit_depth_ == 16) png_set_strip_16(png_ptr); if (color_type_ == PNG_COLOR_TYPE_GRAY || color_type_ == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); // quick hack -- only work in >=libpng 1.2.7 png_set_add_alpha(png_ptr,0xff,PNG_FILLER_AFTER); //rgba double gamma; if (png_get_gAMA(png_ptr, info_ptr, &gamma)) png_set_gamma(png_ptr, 2.2, gamma); png_read_update_info(png_ptr, info_ptr); //START read image rows unsigned w=std::min(unsigned(image.width()),width_); unsigned h=std::min(unsigned(image.height()),height_); unsigned rowbytes=png_get_rowbytes(png_ptr, info_ptr); boost::scoped_array<png_byte> row(new png_byte[rowbytes]); for (unsigned i=0;i<height_;++i) { png_read_row(png_ptr,row.get(),0); if (i>=y0 && i<h) { image.setRow(i-y0,reinterpret_cast<unsigned*>(&row[x0]),w); } } //END png_read_end(png_ptr,0); png_destroy_read_struct(&png_ptr, &info_ptr,0); fclose(fp); }
void PNGImageDecoder::headerAvailable() { png_structp png = reader()->pngPtr(); png_infop info = reader()->infoPtr(); png_uint_32 width = png->width; png_uint_32 height = png->height; // Protect against large images. if (png->width > cMaxPNGSize || png->height > cMaxPNGSize) { m_failed = true; longjmp(png->jmpbuf, 1); return; } // We can fill in the size now that the header is available. if (!m_sizeAvailable) { m_sizeAvailable = true; m_size = IntSize(width, height); } int bitDepth, colorType, interlaceType, compressionType, filterType, channels; png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, &compressionType, &filterType); // The options we set here match what Mozilla does. // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) png_set_expand(png); png_bytep trns = 0; int trnsCount = 0; if (png_get_valid(png, info, PNG_INFO_tRNS)) { png_get_tRNS(png, info, &trns, &trnsCount, 0); png_set_expand(png); } if (bitDepth == 16) png_set_strip_16(png); if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png); // Deal with gamma and keep it under our control. double gamma; if (png_get_gAMA(png, info, &gamma)) { if ((gamma <= 0.0) || (gamma > cMaxGamma)) { gamma = cInverseGamma; png_set_gAMA(png, info, gamma); } png_set_gamma(png, cDefaultGamma, gamma); } else png_set_gamma(png, cDefaultGamma, cInverseGamma); // Tell libpng to send us rows for interlaced pngs. if (interlaceType == PNG_INTERLACE_ADAM7) png_set_interlace_handling(png); // Update our info now png_read_update_info(png, info); channels = png_get_channels(png, info); assert(channels == 3 || channels == 4); reader()->setHasAlpha(channels == 4); if (reader()->decodingSizeOnly()) { // If we only needed the size, halt the reader. reader()->setReadOffset(m_data->size() - png->buffer_size); png->buffer_size = 0; } }
void PLPNGDecoder::Open(PLDataSource *pDataSrc) { png_uint_32 width, height; m_png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, user_error_fn, user_warning_fn); PLASSERT(m_png_ptr); m_info_ptr = png_create_info_struct(m_png_ptr); PLASSERT(m_info_ptr); png_set_read_fn(m_png_ptr, (void*)pDataSrc, my_read_data); /* The call to png_read_info() gives us all of the information from the * PNG file before the first IDAT (image data chunk). */ png_read_info(m_png_ptr, m_info_ptr); #ifdef DUMP_PNG_DATA debugLog("%s", toString(*m_png_ptr).cstr()); #endif png_get_IHDR(m_png_ptr, m_info_ptr, &width, &height, &m_bit_depth ,&m_color_type, NULL, NULL, NULL); if( (m_color_type != PNG_COLOR_TYPE_RGB_ALPHA) && (m_color_type != PNG_COLOR_TYPE_GRAY_ALPHA) && ((m_color_type != PNG_COLOR_TYPE_RGB) || (m_bit_depth < 16)) ) { #ifdef PNG_READ_16_TO_8_SUPPORTED if(m_bit_depth == 16) { #ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED png_set_scale_16(png_ptr); #else png_set_strip_16(png_ptr); #endif } #endif if(m_color_type == PNG_COLOR_TYPE_PALETTE) { png_set_expand(m_png_ptr); } if(m_bit_depth < 8) { png_set_expand(m_png_ptr); } if(png_get_valid(m_png_ptr, m_info_ptr, PNG_INFO_tRNS)) { png_set_expand(m_png_ptr); } if((m_color_type == PNG_COLOR_TYPE_GRAY) || (m_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) { png_set_gray_to_rgb(m_png_ptr); } /* set the background color to draw transparent and alpha images over */ png_color_16 *pBackground; png_color bkgColor = {127, 127, 127}; if(png_get_bKGD(m_png_ptr, m_info_ptr, &pBackground)) { png_set_background(m_png_ptr, pBackground, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); bkgColor.red = (png_byte)pBackground->red; bkgColor.green = (png_byte)pBackground->green; bkgColor.blue = (png_byte)pBackground->blue; #ifdef DUMP_PNG_DATA debugLog(" Background : (%d,%d,%d)\n", bkgColor.red, bkgColor.green, bkgColor.blue); #endif } /* if required set gamma conversion */ double dGamma; if(png_get_gAMA(m_png_ptr, m_info_ptr, &dGamma)) { png_set_gamma(m_png_ptr, (double)2.2, dGamma); } /* after the transformations are registered, update info_ptr data */ png_read_update_info(m_png_ptr, m_info_ptr); png_get_IHDR(m_png_ptr, m_info_ptr, &width, &height, &m_bit_depth ,&m_color_type, NULL, NULL, NULL); } PLPixelFormat pf; switch(m_color_type) { case PNG_COLOR_TYPE_RGB: pf = PLPixelFormat::R8G8B8; break; case PNG_COLOR_TYPE_RGB_ALPHA: pf = PLPixelFormat::A8R8G8B8; break; case PNG_COLOR_TYPE_GRAY: pf = PLPixelFormat::L8; break; case PNG_COLOR_TYPE_GRAY_ALPHA: png_set_gray_to_rgb(m_png_ptr); png_set_expand(m_png_ptr); pf = PLPixelFormat::A8R8G8B8; break; case PNG_COLOR_TYPE_PALETTE: if(m_bit_depth != 16) { pf = PLPixelFormat::I8; } else { // 16-bit palette image png_set_expand(m_png_ptr); pf = PLPixelFormat::R8G8B8; } break; } if((pf.GetBitsPerPixel() == 32) || (pf.GetBitsPerPixel() == 24)) { png_set_bgr(m_png_ptr); } SetBmpInfo(PLPoint(width, height), PLPoint(0,0), pf); png_uint_32 XRes, YRes; int UnitType; png_get_pHYs(m_png_ptr, m_info_ptr, &XRes, &YRes, &UnitType); if(UnitType == PNG_RESOLUTION_METER) { m_Resolution = PLPoint(int (XRes/39.37f+0.5), int (YRes/39.37f+0.5)); } }
static int pngreadstr(FILE *fp, int *w, int *h, int *nc, png_structp png_ptr, png_infop info_ptr, unsigned char **ppixels, unsigned char ***prows) { png_uint_32 width, height; int bit_depth, color_type, interlace_type; int bytes_per_row, row; int gray_palette; unsigned char *pixels, **rows; /* Set error handling if you are using the setjmp/longjmp method (this is * the normal method of doing things with libpng). REQUIRED unless you * set up your own error handlers in the png_create_read_struct() earlier. */ /* if (setjmp(png_ptr->jmpbuf)) */ if (setjmp(png_jmpbuf(png_ptr))) { /* If we get here, we had a problem reading the file */ return 0; } /* Set up the input control if you are using standard C streams */ png_init_io(png_ptr, fp); /* The call to png_read_info() gives us all of the information from the * PNG file before the first IDAT (image data chunk). REQUIRED */ png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); *nc = png_get_channels(png_ptr, info_ptr); /*printf("color_type %d, nc = %d ", color_type, *nc);*/ /**** Set up the data transformations you want. Note that these are all **** optional. Only call them if you want/need them. Many of the **** transformations only work on specific types of images, and many **** are mutually exclusive. ****/ /* tell libpng to strip 16 bit/color files down to 8 bits/color */ png_set_strip_16(png_ptr); /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single * byte into separate bytes (useful for paletted and grayscale images). */ png_set_packing(png_ptr); /* Expand paletted colors into true RGB triplets */ if (color_type == PNG_COLOR_TYPE_PALETTE) { /* Even gray paletted images will get expanded here */ png_set_expand(png_ptr); *nc = 3; } /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(png_ptr); /* Expand paletted or RGB images with transparency to full alpha channels * so the data will be available as RGBA quartets. */ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_expand(png_ptr); ++(*nc); } gray_palette = 0; if (color_type == PNG_COLOR_TYPE_PALETTE) { int n, num_palette; png_colorp palette; if (png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)) { gray_palette = 1; for (n = 0; n < num_palette; ++n) if (palette[n].red != palette[n].green || palette[n].blue != palette[n].green) { gray_palette = 0; break; } } } /* set gamma */ { double file_gamma, default_exponent = get_gamma_exp(); default_exponent = 1.7; if (png_get_gAMA(png_ptr, info_ptr, &file_gamma)) { /* fprintf(stderr,"default_exponent, FileGamma: %f,%f\n" ,default_exponent,file_gamma); */ png_set_gamma(png_ptr, default_exponent, file_gamma); /* png_set_gamma(png_ptr, default_exponent, 1.7); */ } else { png_set_gamma(png_ptr, default_exponent, 0.45455); /* png_set_gamma(png_ptr, default_exponent, 1.7); */ /* fprintf(stderr,"DefaultGamma: 1.7\n"); */ } } /* Get updated info */ png_read_update_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); /* Allocate the memory to hold the image using the fields of info_ptr. */ bytes_per_row = *nc * width; *ppixels = (unsigned char *)malloc(bytes_per_row * height); *prows = (unsigned char **)malloc(height * sizeof(char *)); if (*ppixels == 0 || *prows == 0) { /* Free all of the memory associated with the png_ptr and info_ptr */ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return 0; } pixels = *ppixels; rows = *prows; for (row = 0; row < height; ++row) rows[row] = &pixels[row * bytes_per_row]; /* Now it's time to read the image. One of these methods is REQUIRED */ /* Read the entire image in one go */ png_read_image(png_ptr, rows); /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ png_read_end(png_ptr, info_ptr); /* clean up after the read, and free any memory allocated - REQUIRED */ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); /* Reduce gray palette images down to intensity or intensity/alpha */ if (gray_palette) { int n, np = width * height; if (*nc == 3) { for (n = 1; n < np; ++n) pixels[n] = pixels[3 * n]; *nc = 1; } else if (*nc == 4) { for (n = 0; n < np; ++n) { pixels[2 * n] = pixels[4 * n]; pixels[2 * n + 1] = pixels[4 * n + 3]; } *nc = 2; } } *w = width; *h = height; return 1; }
void png_reader::read(unsigned x0, unsigned y0,image_data_32& image) { FILE *fp=fopen(fileName_.c_str(),"rb"); if (!fp) throw image_reader_exception("cannot open image file "+fileName_); png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,0,0,0); if (!png_ptr) { fclose(fp); throw image_reader_exception("failed to allocate png_ptr"); } // catch errors in a custom way to avoid the need for setjmp png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), user_error_fn, user_warning_fn); png_infop info_ptr; try { info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr,0,0); fclose(fp); throw image_reader_exception("failed to create info_ptr"); } } catch (std::exception const& ex) { png_destroy_read_struct(&png_ptr,0,0); fclose(fp); throw; } png_set_read_fn(png_ptr, (png_voidp)fp, png_read_data); png_read_info(png_ptr, info_ptr); if (color_type_ == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); if (color_type_ == PNG_COLOR_TYPE_GRAY && bit_depth_ < 8) png_set_expand(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); if (bit_depth_ == 16) png_set_strip_16(png_ptr); if (color_type_ == PNG_COLOR_TYPE_GRAY || color_type_ == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); // quick hack -- only work in >=libpng 1.2.7 png_set_add_alpha(png_ptr,0xff,PNG_FILLER_AFTER); //rgba double gamma; if (png_get_gAMA(png_ptr, info_ptr, &gamma)) png_set_gamma(png_ptr, 2.2, gamma); if (x0 == 0 && y0 == 0 && image.width() >= width_ && image.height() >= height_) { if (png_get_interlace_type(png_ptr,info_ptr) == PNG_INTERLACE_ADAM7) { png_set_interlace_handling(png_ptr); // FIXME: libpng bug? // according to docs png_read_image // "..automatically handles interlacing, // so you don't need to call png_set_interlace_handling()" } png_read_update_info(png_ptr, info_ptr); // we can read whole image at once // alloc row pointers boost::scoped_array<png_byte*> rows(new png_bytep[height_]); for (unsigned i=0; i<height_; ++i) rows[i] = (png_bytep)image.getRow(i); png_read_image(png_ptr, rows.get()); } else { png_read_update_info(png_ptr, info_ptr); unsigned w=std::min(unsigned(image.width()),width_); unsigned h=std::min(unsigned(image.height()),height_); unsigned rowbytes=png_get_rowbytes(png_ptr, info_ptr); boost::scoped_array<png_byte> row(new png_byte[rowbytes]); //START read image rows for (unsigned i=0;i<height_;++i) { png_read_row(png_ptr,row.get(),0); if (i>=y0 && i<h) { image.setRow(i-y0,reinterpret_cast<unsigned*>(&row[x0]),w); } } //END } png_read_end(png_ptr,0); png_destroy_read_struct(&png_ptr, &info_ptr,0); fclose(fp); }