void set_add_alpha(uint_32 filler, filler_type type) const { TRACE_IO_TRANSFORM("png_set_add_alpha: filler=%08x, type=%d\n", filler, type); png_set_add_alpha(m_png, filler, type); }
bool PNGImageReader::decode() { try { // From http://trac.mapnik.org/browser/trunk/src/png_reader.cpp if (color == PNG_COLOR_TYPE_PALETTE) png_set_expand(png); if (color == PNG_COLOR_TYPE_GRAY) png_set_expand(png); if (png_get_valid(png, info, PNG_INFO_tRNS)) png_set_expand(png); if (depth == 16) png_set_strip_16(png); if (depth < 8) png_set_packing(png); if (color == PNG_COLOR_TYPE_GRAY || color == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png); if (interlace == PNG_INTERLACE_ADAM7) png_set_interlace_handling(png); // Always add an alpha channel. if (!this->alpha) { png_set_add_alpha(png, 0xFF, PNG_FILLER_AFTER); } double gamma; if (png_get_gAMA(png, info, &gamma)) png_set_gamma(png, 2.2, gamma); png_read_update_info(png, info); unsigned int rowbytes = png_get_rowbytes(png, info); assert(width * 4 == rowbytes); surface = (unsigned int*)malloc(width * height * 4); assert(surface); png_bytep row_pointers[height]; for (unsigned i = 0; i < height; i++) { row_pointers[i] = (unsigned char *)surface + (i * rowbytes); } // Read image data png_read_image(png, row_pointers); png_read_end(png, NULL); return true; } catch (std::exception& e) { png_destroy_read_struct(&png, &info, NULL); width = 0; height = 0; if (surface) free(surface); surface = NULL; return false; } }
// // Load a texture from a png image // bool PngLoader::loadImageIntoBuffer() { DEBUG_ENTER_FUNC(); if (!basicImageLoad()) { png_destroy_read_struct(&_pngPtr, &_infoPtr, png_infopp_NULL); return false; } png_set_strip_16(_pngPtr); // Strip off 16 bit channels in case they occur if (_paletteSize) { // Copy the palette png_colorp srcPal = _infoPtr->palette; for (int i = 0; i < _infoPtr->num_palette; i++) { unsigned char alphaVal = (i < _infoPtr->num_trans) ? _infoPtr->trans[i] : 0xFF; // Load alpha if it's there _palette->setSingleColorRGBA(i, srcPal->red, srcPal->green, srcPal->blue, alphaVal); srcPal++; } } else { // Not a palettized image if (_colorType == PNG_COLOR_TYPE_GRAY && _bitDepth < 8) png_set_gray_1_2_4_to_8(_pngPtr); // Round up grayscale images if (png_get_valid(_pngPtr, _infoPtr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(_pngPtr); // Convert trans channel to alpha for 32 bits png_set_add_alpha(_pngPtr, 0xff, PNG_FILLER_AFTER); // Filler for alpha if none exists } uint32 rowBytes = png_get_rowbytes(_pngPtr, _infoPtr); // there seems to be a bug in libpng where it doesn't increase the rowbytes or the // channel even after we add the alpha channel if (_channels == 3 && (rowBytes / _width) == 3) { _channels = 4; rowBytes = _width * _channels; } PSP_DEBUG_PRINT("rowBytes[%d], channels[%d]\n", rowBytes, _channels); unsigned char *line = (unsigned char*) malloc(rowBytes); if (!line) { png_destroy_read_struct(&_pngPtr, png_infopp_NULL, png_infopp_NULL); PSP_ERROR("Couldn't allocate line\n"); return false; } for (size_t y = 0; y < _height; y++) { png_read_row(_pngPtr, line, png_bytep_NULL); _buffer->copyFromRect(line, rowBytes, 0, y, _width, 1); // Copy into buffer } free(line); png_read_end(_pngPtr, _infoPtr); png_destroy_read_struct(&_pngPtr, &_infoPtr, png_infopp_NULL); return true; }
/* * @brief process IHDR data * @note not called until the IDAT chunk starts processing */ void apng_ani::info_callback() { png_set_expand(_pngp); png_set_strip_16(_pngp); png_set_gray_to_rgb(_pngp); // what about 'non-colour' headanis? png_set_add_alpha(_pngp, 0xff, PNG_FILLER_AFTER); // doesn't affect images that already have alpha channel png_set_interlace_handling(_pngp); png_set_bgr(_pngp); png_read_update_info(_pngp, _infop); }
static PngInfo read_and_update_info(const png_structp png_ptr, const png_infop info_ptr) { png_uint_32 width, height; int bit_depth, color_type; png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); // Convert transparency to full alpha if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); } // Convert grayscale, if needed. if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { png_set_expand_gray_1_2_4_to_8(png_ptr); } // Convert paletted images, if needed. if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); } // Add alpha channel, if there is none (rationale: GL_RGBA is faster than GL_RGB on many GPUs) if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_RGB) { png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); } // Ensure 8-bit packing if (bit_depth < 8) { png_set_packing(png_ptr); } else if (bit_depth == 16) { png_set_scale_16(png_ptr); } png_read_update_info(png_ptr, info_ptr); // Read the new color type after updates have been made. color_type = png_get_color_type(png_ptr, info_ptr); return (PngInfo) {width, height, color_type}; }
void setformat_rgba8( png_structp png, png_infop info, int bitdepth, int colortype ) { double gamma; if( png_get_gAMA( png, info, &gamma ) ) { png_set_gamma( png, 2.2, gamma ); } else { png_set_gamma( png, 2.2, 0.45455 ); } if( colortype == PNG_COLOR_TYPE_PALETTE ) { png_set_palette_to_rgb( png ); } if( colortype == PNG_COLOR_TYPE_GRAY && bitdepth < 8 ) { png_set_expand_gray_1_2_4_to_8( png ); } if( png_get_valid( png, info, PNG_INFO_tRNS ) ) { png_set_tRNS_to_alpha( png ); } else { int channels = png_get_channels( png, info ); if( channels == 1 || channels == 3 ) { png_set_add_alpha( png, 255, PNG_FILLER_AFTER ); } } if( colortype == PNG_COLOR_TYPE_GRAY || colortype == PNG_COLOR_TYPE_GRAY_ALPHA ) { png_set_gray_to_rgb( png ); } if( bitdepth == 16 ) { png_set_scale_16( png ); } }
bool loadParallax (unsigned short v, unsigned short fracX, unsigned short fracY) { setResourceForFatal (v); if (! openFileFromNum (v)) return fatal ("Can't open parallax image"); parallaxLayer * nP = new parallaxLayer; if (! checkNew (nP)) return false; nP -> next = parallaxStuff; parallaxStuff = nP; if (nP -> next) { nP -> next -> prev = nP; } nP -> prev = NULL; int picWidth; int picHeight; long file_pointer = ftell (bigDataFile); png_structp png_ptr; png_infop info_ptr, end_info; int fileIsPNG = true; // Is this a PNG file? char tmp[10]; size_t bytes_read = fread(tmp, 1, 8, bigDataFile); if (bytes_read != 8 && ferror (bigDataFile)) { debugOut("Reading error in loadParallax.\n"); } if (png_sig_cmp((png_byte *) tmp, 0, 8)) { // No, it's old-school HSI fileIsPNG = false; fseek(bigDataFile, file_pointer, SEEK_SET); picWidth = nP -> width = get2bytes (bigDataFile); picHeight = nP -> height = get2bytes (bigDataFile); } else { // Read the PNG header png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { return false; } 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; } end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return false; } png_init_io(png_ptr, bigDataFile); // Tell libpng which file to read png_set_sig_bytes(png_ptr, 8); // 8 bytes already read png_read_info(png_ptr, info_ptr); png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_method; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); picWidth = nP -> width = width; picHeight = nP -> height = height; if (bit_depth < 8) png_set_packing(png_ptr); png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); if (bit_depth == 16) png_set_strip_16(png_ptr); png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER); png_read_update_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); //int rowbytes = png_get_rowbytes(png_ptr, info_ptr); } if (! NPOT_textures) { picWidth = getNextPOT(picWidth); picHeight = getNextPOT(picHeight); } nP -> fileNum = v; nP -> fractionX = fracX; nP -> fractionY = fracY; if (fracX == 65535) { nP -> wrapS = false; if (nP -> width < winWidth) { fatal ("For AUTOFIT parallax backgrounds, the image must be at least as wide as the game window/screen."); return false; } } else { nP -> wrapS = true; } if (fracY == 65535) { nP -> wrapT = false; if (nP -> height < winHeight) { fatal ("For AUTOFIT parallax backgrounds, the image must be at least as tall as the game window/screen."); return false; } } else { nP -> wrapT = true; } nP -> texture = new GLubyte [picHeight * picWidth * 4]; if (! checkNew (nP -> texture)) return false; if (fileIsPNG) { unsigned char * row_pointers[nP -> height]; for (int i = 0; i < nP -> height; i++) row_pointers[i] = nP -> texture + 4*i*picWidth; png_read_image(png_ptr, (png_byte **) row_pointers); png_read_end(png_ptr, NULL); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); } else { int t1, t2, n; unsigned short c; GLubyte * target; for (t2 = 0; t2 < nP -> height; t2 ++) { t1 = 0; while (t1 < nP -> width) { c = (unsigned short) get2bytes (bigDataFile); if (c & 32) { n = fgetc (bigDataFile) + 1; c -= 32; } else { n = 1; } while (n--) { target = nP -> texture + 4*picWidth*t2 + t1*4; if (c == 63519 || c == 2015) { target[0] = (GLubyte) 0; target[1] = (GLubyte) 0; target[2] = (GLubyte) 0; target[3] = (GLubyte) 0; } else { target[0] = (GLubyte) redValue(c); target[1] = (GLubyte) greenValue(c); target[2] = (GLubyte) blueValue(c); target[3] = (GLubyte) 255; } t1 ++; } } } } glGenTextures (1, &nP->textureName); glBindTexture (GL_TEXTURE_2D, nP->textureName); if (nP -> wrapS) glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); else glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); if (nP -> wrapT) glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); else glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); if (gameSettings.antiAlias < 0) { glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } else { glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } texImage2D (GL_TEXTURE_2D, 0, GL_RGBA, picWidth, picHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nP->texture, nP->textureName); finishAccess (); setResourceForFatal (-1); return true; }
bool loadLightMap (int v) { int newPicWidth, newPicHeight; setResourceForFatal (v); if (! openFileFromNum (v)) return fatal ("Can't open light map."); long file_pointer = ftell (bigDataFile); png_structp png_ptr; png_infop info_ptr, end_info; int fileIsPNG = true; // Is this a PNG file? char tmp[10]; size_t bytes_read = fread(tmp, 1, 8, bigDataFile); if (bytes_read != 8 && ferror (bigDataFile)) { debugOut("Reading error in loadLightMap.\n"); } if (png_sig_cmp((png_byte *) tmp, 0, 8)) { // No, it's old-school HSI fileIsPNG = false; fseek(bigDataFile, file_pointer, SEEK_SET); newPicWidth = lightMap.w = get2bytes (bigDataFile); newPicHeight = lightMap.h = get2bytes (bigDataFile); } else { // Read the PNG header png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { return false; } 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; } end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return false; } png_init_io(png_ptr, bigDataFile); // Tell libpng which file to read png_set_sig_bytes(png_ptr, 8); // 8 bytes already read png_read_info(png_ptr, info_ptr); png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_method; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); newPicWidth = lightMap.w = width; newPicHeight = lightMap.h = height; if (bit_depth < 8) png_set_packing(png_ptr); png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); if (bit_depth == 16) png_set_strip_16(png_ptr); png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER); png_read_update_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); //int rowbytes = png_get_rowbytes(png_ptr, info_ptr); } if (lightMapMode == LIGHTMAPMODE_HOTSPOT) { if (lightMap.w != sceneWidth || lightMap.h != sceneHeight) { return fatal ("Light map width and height don't match scene width and height. That is required for lightmaps in HOTSPOT mode."); } } if (! NPOT_textures) { newPicWidth = getNextPOT(lightMap.w); newPicHeight = getNextPOT(lightMap.h); lightMap.texW = (double) lightMap.w / newPicWidth; lightMap.texH = (double) lightMap.h / newPicHeight; } else { lightMap.texW = 1.0; lightMap.texH = 1.0; } killLightMap (); lightMapNumber = v; glPixelStorei (GL_UNPACK_ALIGNMENT, 1); if (lightMap.data) delete [] lightMap.data; lightMap.data = new GLubyte [newPicWidth*newPicHeight*4]; if (! lightMap.data) { return fatal ("Out of memory loading light map."); } int t1, t2, n; unsigned short c; GLubyte * target; if (fileIsPNG) { unsigned char * row_pointers[lightMap.h]; for (int i = 0; i<lightMap.h; i++) row_pointers[i] = lightMap.data + 4*i*newPicWidth; png_read_image(png_ptr, (png_byte **) row_pointers); png_read_end(png_ptr, NULL); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); } else { for (t2 = 0; t2 < lightMap.h; t2 ++) { t1 = 0; while (t1 < lightMap.w) { c = (unsigned short) get2bytes (bigDataFile); if (c & 32) { n = fgetc (bigDataFile) + 1; c -= 32; } else { n = 1; } while (n --) { target = lightMap.data + 4*newPicWidth*t2 + t1*4; target[0] = (GLubyte) redValue(c); target[1] = (GLubyte) greenValue(c); target[2] = (GLubyte) blueValue(c); target[3] = (GLubyte) 255; t1++; } } } } if (! lightMap.name) glGenTextures (1, &lightMap.name); glBindTexture(GL_TEXTURE_2D, lightMap.name); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); texImage2D (GL_TEXTURE_2D, 0, GL_RGBA, newPicWidth, newPicHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, lightMap.data, lightMap.name); finishAccess (); setResourceForFatal (-1); return true; }
bool MCImageDecodePNG(IO_handle p_stream, MCImageBitmap *&r_bitmap) { bool t_success = true; MCImageBitmap *t_bitmap = nil; png_structp t_png = nil; png_infop t_info = nil; png_infop t_end_info = nil; t_success = nil != (t_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)); if (t_success) t_success = nil != (t_info = png_create_info_struct(t_png)); if (t_success) t_success = nil != (t_end_info = png_create_info_struct(t_png)); if (setjmp(png_jmpbuf(t_png))) { t_success = false; } if (t_success) { png_set_read_fn(t_png, p_stream, stream_read); png_read_info(t_png, t_info); } png_uint_32 t_width, t_height; int t_bit_depth, t_color_type; int t_interlace_method, t_compression_method, t_filter_method; int t_interlace_passes; if (t_success) { png_get_IHDR(t_png, t_info, &t_width, &t_height, &t_bit_depth, &t_color_type, &t_interlace_method, &t_compression_method, &t_filter_method); } if (t_success) t_success = MCImageBitmapCreate(t_width, t_height, t_bitmap); if (t_success) { bool t_need_alpha = false; t_interlace_passes = png_set_interlace_handling(t_png); if (t_color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(t_png); if (t_color_type == PNG_COLOR_TYPE_GRAY || t_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(t_png); if (png_get_valid(t_png, t_info, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(t_png); t_need_alpha = true; /* OVERHAUL - REVISIT - assume image has transparent pixels if tRNS is present */ t_bitmap->has_transparency = true; } if (t_color_type & PNG_COLOR_MASK_ALPHA) { t_need_alpha = true; /* OVERHAUL - REVISIT - assume image has alpha if color type allows it */ t_bitmap->has_alpha = t_bitmap->has_transparency = true; } else if (!t_need_alpha) png_set_add_alpha(t_png, 0xFF, MCswapbytes ? PNG_FILLER_AFTER : PNG_FILLER_BEFORE); if (t_bit_depth == 16) png_set_strip_16(t_png); if (MCswapbytes) png_set_bgr(t_png); else png_set_swap_alpha(t_png); } // MW-2009-12-10: Support for color profiles MCColorTransformRef t_color_xform; t_color_xform = nil; // Try to get an embedded ICC profile... if (t_success && t_color_xform == nil && png_get_valid(t_png, t_info, PNG_INFO_iCCP)) { png_charp t_ccp_name; png_bytep t_ccp_profile; int t_ccp_compression_type; png_uint_32 t_ccp_profile_length; png_get_iCCP(t_png, t_info, &t_ccp_name, &t_ccp_compression_type, &t_ccp_profile, &t_ccp_profile_length); MCColorSpaceInfo t_csinfo; t_csinfo . type = kMCColorSpaceEmbedded; t_csinfo . embedded . data = t_ccp_profile; t_csinfo . embedded . data_size = t_ccp_profile_length; t_color_xform = MCscreen -> createcolortransform(t_csinfo); } // Next try an sRGB style profile... if (t_success && t_color_xform == nil && png_get_valid(t_png, t_info, PNG_INFO_sRGB)) { int t_intent; png_get_sRGB(t_png, t_info, &t_intent); MCColorSpaceInfo t_csinfo; t_csinfo . type = kMCColorSpaceStandardRGB; t_csinfo . standard . intent = (MCColorSpaceIntent)t_intent; t_color_xform = MCscreen -> createcolortransform(t_csinfo); } // Finally try for cHRM + gAMA... if (t_success && t_color_xform == nil && png_get_valid(t_png, t_info, PNG_INFO_cHRM) && png_get_valid(t_png, t_info, PNG_INFO_gAMA)) { MCColorSpaceInfo t_csinfo; t_csinfo . type = kMCColorSpaceCalibratedRGB; png_get_cHRM(t_png, t_info, &t_csinfo . calibrated . white_x, &t_csinfo . calibrated . white_y, &t_csinfo . calibrated . red_x, &t_csinfo . calibrated . red_y, &t_csinfo . calibrated . green_x, &t_csinfo . calibrated . green_y, &t_csinfo . calibrated . blue_x, &t_csinfo . calibrated . blue_y); png_get_gAMA(t_png, t_info, &t_csinfo . calibrated . gamma); t_color_xform = MCscreen -> createcolortransform(t_csinfo); } // Could not create any kind, so fallback to gamma transform. if (t_success && t_color_xform == nil) { double image_gamma; if (png_get_gAMA(t_png, t_info, &image_gamma)) png_set_gamma(t_png, MCgamma, image_gamma); else png_set_gamma(t_png, MCgamma, 0.45); } if (t_success) { for (uindex_t t_pass = 0; t_pass < t_interlace_passes; t_pass++) { png_bytep t_data_ptr = (png_bytep)t_bitmap->data; for (uindex_t i = 0; i < t_height; i++) { png_read_row(t_png, t_data_ptr, nil); t_data_ptr += t_bitmap->stride; } } } if (t_success) png_read_end(t_png, t_end_info); if (t_png != nil) png_destroy_read_struct(&t_png, &t_info, &t_end_info); // transform colours using extracted colour profile if (t_success && t_color_xform != nil) MCImageBitmapApplyColorTransform(t_bitmap, t_color_xform); if (t_color_xform != nil) MCscreen -> destroycolortransform(t_color_xform); if (t_success) r_bitmap = t_bitmap; else { if (t_bitmap != nil) MCImageFreeBitmap(t_bitmap); } return t_success; }
/* ================= image_png_load ================= */ GNUC_NONNULL static erbool image_png_load (const char *name, image_t *im) { fs_file_t f; int size, r, width, height, inc; png_byte *image = NULL, *p = NULL; png_structp pngst; png_infop info = NULL; png_byte depth, color_type; if (NULL == (f = fs_open(name, FS_RDONLY, &size, false))) return false; if (NULL == (pngst = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, &epng_error, &epng_warn))) { sys_printf("failed to create png read struct\n"); fs_close(f); return false; } if (NULL == (info = png_create_info_struct(pngst))) { sys_printf("failed to create png info struct\n"); goto error; } if (setjmp(png_jmpbuf(pngst))) goto error; png_set_user_limits(pngst, IMAGE_MAX_WIDTH, IMAGE_MAX_HEIGHT); png_set_read_fn(pngst, f, &epng_read); png_read_info(pngst, info); width = png_get_image_width(pngst, info); height = png_get_image_height(pngst, info); depth = png_get_bit_depth(pngst, info); if (16 == depth) { /* 16 -> 8 */ png_set_strip_16(pngst); depth = 8; } color_type = png_get_color_type(pngst, info); /* 1/2/4 gray -> 8 gray */ if (PNG_COLOR_TYPE_GRAY == color_type && depth < 8) png_set_expand_gray_1_2_4_to_8(pngst); /* gray/palette -> rgb */ if (PNG_COLOR_TYPE_GRAY == color_type || PNG_COLOR_TYPE_GRAY_ALPHA == color_type) png_set_gray_to_rgb(pngst); else if (PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(pngst); /* transparency -> alpha */ if (png_get_valid(pngst, info, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(pngst); } else { /* to rgba */ if (PNG_COLOR_TYPE_RGB_ALPHA != color_type && PNG_COLOR_TYPE_GRAY_ALPHA != color_type) png_set_add_alpha(pngst, 0xff, PNG_FILLER_AFTER); } /* deinterlace */ png_set_interlace_handling(pngst); /* read */ inc = width * 4; p = image = mem_alloc(image_mempool, height * inc); for (r = 0; r < height ;r++, p += inc) png_read_row(pngst, p, NULL); png_read_end(pngst, NULL); png_destroy_read_struct(&pngst, &info, NULL); fs_close(f); im->width = width; im->height = height; im->data = image; return true; error: if (NULL != f) fs_close(f); png_destroy_read_struct(&pngst, NULL != info ? &info : NULL, NULL); if (NULL != image) mem_free(image); return false; }
static int VS_CC read_png(img_hnd_t *ih, int n) { FILE *fp = imgr_fopen(ih->src[n].name); if (!fp) { return -1; } png_structp p_str = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!p_str) { fclose(fp); return -1; } png_infop p_info = png_create_info_struct(p_str); if (!p_info) { fclose(fp); png_destroy_read_struct(&p_str, NULL, NULL); return -1; } png_init_io(p_str, fp); png_read_info(p_str, p_info); png_uint_32 width, height; int color_type, bit_depth; png_get_IHDR(p_str, p_info, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); if (color_type & PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(p_str); } if (bit_depth < 8) { png_set_packing(p_str); } if (bit_depth > 8) { png_set_swap(p_str); } if (ih->enable_alpha == 0) { if (color_type & PNG_COLOR_MASK_ALPHA) { png_set_strip_alpha(p_str); } } else if ((color_type & PNG_COLOR_MASK_ALPHA) == 0) { png_set_add_alpha(p_str, 0x00, PNG_FILLER_AFTER); } png_read_update_info(p_str, p_info); png_read_image(p_str, ih->png_row_index); fclose(fp); png_destroy_read_struct(&p_str, &p_info, NULL); ih->misc = IMG_ORDER_RGB; ih->row_adjust = 1; switch ((ih->src[n].format->id << 1) | ih->enable_alpha) { case (pfRGB24 << 1 | 0): ih->write_frame = func_write_rgb24; break; case (pfRGB24 << 1 | 1): ih->write_frame = func_write_rgb32; break; case (pfRGB48 << 1 | 0): ih->write_frame = func_write_rgb48; break; case (pfRGB48 << 1 | 1): ih->write_frame = func_write_rgb64; break; case (pfGray8 << 1 | 0): case (pfGray16 << 1 | 0): ih->write_frame = func_write_planar; break; case (pfGray8 << 1 | 1): ih->write_frame = func_write_gray8_a; break; case (pfGray16 << 1 | 1): ih->write_frame = func_write_gray16_a; break; default: break; } return 0; }
void Image::LoadPNG(FILE *f, char *filename, Texture *tex) { png_structp png; png_infop pnginfo; byte ioBuffer[8192]; byte *raw; int filesize = SystemFileManager::FileLength(f); raw = (byte *) malloc(filesize + 1); if (raw == NULL) { Sys_Error("Out of memory to load PNG: %s\n", filename); return; } fread(raw, 1, filesize, f); fclose(f); if (!raw) { Con_Printf("Bad png file %s\n", filename); return; } if (png_sig_cmp(raw, 0, 4)) { free(raw); return; } png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if (!png) { free(raw); return; } pnginfo = png_create_info_struct(png); if (!pnginfo) { free(raw); png_destroy_read_struct(&png, &pnginfo, 0); return; } png_set_sig_bytes(png, 0/*sizeof( sig )*/); mypng_struct_create(); // creates the my_png struct my_png->tmpBuf = (char *) raw; //buf = whole file content my_png->tmpi = 0; png_set_read_fn(png, ioBuffer, fReadData); png_read_info(png, pnginfo); png_get_IHDR(png, pnginfo, &my_png->Width, &my_png->Height, &my_png->BitDepth, &my_png->ColorType, &my_png->Interlace, &my_png->Compression, &my_png->Filter); // ...removed bgColor code here... if (my_png->ColorType == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png); if (my_png->ColorType == PNG_COLOR_TYPE_GRAY && my_png->BitDepth < 8) png_set_expand_gray_1_2_4_to_8(png); // Add alpha channel if present if (png_get_valid(png, pnginfo, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png); // hax: expand 24bit to 32bit if (my_png->BitDepth == 8 && my_png->ColorType == PNG_COLOR_TYPE_RGB) png_set_filler(png, 255, PNG_FILLER_AFTER); if ((my_png->ColorType == PNG_COLOR_TYPE_GRAY) || (my_png->ColorType == PNG_COLOR_TYPE_GRAY_ALPHA)) { png_set_gray_to_rgb(png); png_set_add_alpha(png, 255, PNG_FILLER_AFTER); } if (my_png->BitDepth < 8) png_set_expand(png); // update the info structure png_read_update_info(png, pnginfo); my_png->FRowBytes = png_get_rowbytes(png, pnginfo); my_png->BytesPerPixel = png_get_channels(png, pnginfo); // DL Added 30/08/2000 InitializeDemData(); if ((my_png->Data) && (my_png->FRowPtrs)) png_read_image(png, (png_bytepp) my_png->FRowPtrs); png_read_end(png, pnginfo); // read last information chunks png_destroy_read_struct(&png, &pnginfo, 0); if (my_png->BitDepth == 8) { tex->width = my_png->Width; tex->height = my_png->Height; tex->bytesPerPixel = my_png->BytesPerPixel; tex->setData((byte *) my_png->Data); } else { Con_Printf("Bad png color depth: %s\n", filename); free(my_png->Data); } free(raw); mypng_struct_destroy(true); return; }
bool loadSpriteFromPNG (const char * file, struct spriteBank *sprites, int index) { if (sprites->type<2) { char * error = joinStrings(file, "\n\nPNG files currently not supported in palette mode. Change to 32-bit mode and try again."); errorBox ("Can't open PNG file", error); delete error; return false; } // Open the file FILE * fp = fopen (file, "rb"); if (fp == NULL) { char * error = joinStrings(file, "\n\nThe file can't be opened. I don't know why."); errorBox ("Can't open PNG file", error); delete error; return false; } char tmp[10]; size_t bytes_read = fread(tmp, 1, 8, fp); if (bytes_read != 8 && ferror (fp)) { fprintf(stderr, "Reading error in loadSpriteFromPNG.\n"); } if (png_sig_cmp((png_byte *) tmp, 0, 8)) { fclose (fp); char * error = joinStrings(file, "\n\nIt doesn't appear to be a valid PNG image."); errorBox ("Can't open PNG file", error); delete error; return false; } png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { fclose (fp); char * error = joinStrings(file, "\n\nError reading the file."); errorBox ("Can't open PNG file", error); delete error; return false; } 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); fclose (fp); char * error = joinStrings(file, "\n\nError reading the file."); errorBox ("Can't open PNG file", error); delete error; return false; } png_infop end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); fclose (fp); char * error = joinStrings(file, "\n\nError readin the file"); return errorBox ("Can't open PNG file", error); } png_init_io(png_ptr, fp); // Tell libpng which file to read png_set_sig_bytes(png_ptr, 8); // 8 bytes already read png_read_info(png_ptr, info_ptr); png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_method; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); if (bit_depth < 8) png_set_packing(png_ptr); png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); if (bit_depth == 16) png_set_strip_16(png_ptr); png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER); png_read_update_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); int rowbytes = png_get_rowbytes(png_ptr, info_ptr); unsigned char * row_pointers[height]; unsigned char * data = new unsigned char [rowbytes*height]; for (int i = 0; i<height; i++) row_pointers[i] = data + i*rowbytes; png_read_image(png_ptr, (png_byte **) row_pointers); png_read_end(png_ptr, NULL); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); fclose (fp); if (sprites->sprites[index].data) delete sprites->sprites[index].data; sprites->sprites[index].data = data; sprites->sprites[index].width = width; sprites->sprites[index].height = height; int n, r, g, b; for (int x=0; x<width;x++) { for (int y=0; y<height; y++) { if (! sprites->sprites[index].data[4*width*y + x*4 + 3]) { n = r = g = b = 0; if (x>0 && sprites->sprites[index].data[4*width*y + (x-1)*4 + 3]) { n++; r+= sprites->sprites[index].data[4*width*y + (x-1)*4]; g+= sprites->sprites[index].data[4*width*y + (x-1)*4+1]; b+= sprites->sprites[index].data[4*width*y + (x-1)*4+2]; } if (x<width-1 && sprites->sprites[index].data[4*width*y + (x+1)*4 + 3]) { n++; r+= sprites->sprites[index].data[4*width*y + (x+1)*4]; g+= sprites->sprites[index].data[4*width*y + (x+1)*4+1]; b+= sprites->sprites[index].data[4*width*y + (x+1)*4+2]; } if (y>0 && sprites->sprites[index].data[4*width*(y-1) + x*4 + 3]) { n++; r+= sprites->sprites[index].data[4*width*(y-1) + x*4]; g+= sprites->sprites[index].data[4*width*(y-1) + x*4+1]; b+= sprites->sprites[index].data[4*width*(y-1) + x*4+2]; } if (y<height-1 && sprites->sprites[index].data[4*width*(y+1) + x*4 + 3]) { n++; r+= sprites->sprites[index].data[4*width*(y+1) + x*4]; g+= sprites->sprites[index].data[4*width*(y+1) + x*4+1]; b+= sprites->sprites[index].data[4*width*(y+1) + x*4+2]; } if (n) { r /= n; g /= n; b /= n; sprites->sprites[index].data[4*width*y + x*4]=r; sprites->sprites[index].data[4*width*y + x*4+1]=g; sprites->sprites[index].data[4*width*y + x*4+2]=b; } } } } loadSpriteTexture (sprites, index); return true; }
static unsigned char* png_file_load (const char *file, int *width, int *height) { FILE *fd; unsigned char *data; unsigned char header[8]; int bit_depth, color_type; png_uint_32 png_width, png_height, i, rowbytes; png_structp png_ptr; png_infop info_ptr; png_bytep *row_pointers; if ((fd = fopen( file, "rb" )) == NULL) return NULL; fread( header, 1, 8, fd ); if ( ! png_check_sig( header, 8 ) ) { fclose(fd); return NULL; } png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if ( ! png_ptr ) { fclose(fd); return NULL; } info_ptr = png_create_info_struct(png_ptr); if ( ! info_ptr ) { png_destroy_read_struct( &png_ptr, (png_infopp)NULL, (png_infopp)NULL); fclose(fd); return NULL; } if (setjmp (png_jmpbuf (png_ptr))) { png_destroy_read_struct( &png_ptr, &info_ptr, NULL); fclose(fd); return NULL; } png_init_io( png_ptr, fd ); png_set_sig_bytes( png_ptr, 8); png_read_info( png_ptr, info_ptr); png_get_IHDR( png_ptr, info_ptr, &png_width, &png_height, &bit_depth, &color_type, NULL, NULL, NULL); *width = (int) png_width; *height = (int) png_height; 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); /* Add alpha */ if (( color_type == PNG_COLOR_TYPE_GRAY ) || ( color_type == PNG_COLOR_TYPE_RGB )) png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER); /* req 1.2.7 */ if (( color_type == PNG_COLOR_TYPE_PALETTE )|| ( png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS ))) png_set_expand(png_ptr); png_read_update_info( png_ptr, info_ptr); /* allocate space for data and row pointers */ rowbytes = png_get_rowbytes( png_ptr, info_ptr); data = (unsigned char *) malloc( (rowbytes*(*height + 1))); row_pointers = (png_bytep *) malloc( (*height)*sizeof(png_bytep)); if (( data == NULL )||( row_pointers == NULL )) { png_destroy_read_struct( &png_ptr, &info_ptr, NULL); free(data); free(row_pointers); return NULL; } for ( i = 0; i < *height; i++ ) row_pointers[i] = data + i*rowbytes; png_read_image( png_ptr, row_pointers ); png_read_end( png_ptr, NULL); free(row_pointers); png_destroy_read_struct( &png_ptr, &info_ptr, NULL); fclose(fd); return data; }
SDL_Surface* LoadPng(const char* Filename){ // Loads to a software image //Prepare the guards - prepare here so all are called by lngjmp FileGuard guardfin; PngGuard guardPng; SurfaceGuard guardRetVal; //Open the file FILE *fin; if(!GLOBAL_ZipFile.IsOpen()){ fin = fopen(Filename, "rb"); guardfin.GuardFile(fin); if (!fin) return 0; } //Verify the header { png_byte Header[8]; if(GLOBAL_ZipFile.IsOpen()){ if(GLOBAL_ZipFile.ReadSomeStart(Filename,Header,8) != 8) return 0; } else fread(Header,1,8,fin); if(png_sig_cmp(Header,0,8) != 0) return 0; // File closing handled by guard. } //Initialize structures png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); guardPng.GuardPngStruct(png_ptr); if (!png_ptr) return 0; // File closing handled by guard png_infop info_ptr = png_create_info_struct(png_ptr); guardPng.GuardStartInfo(info_ptr); if (!info_ptr) return 0; // File and png closing handled by guards png_infop end_info = png_create_info_struct(png_ptr); guardPng.GuardEndInfo(end_info); if (!end_info) return 0; // File and png closing handled by guards if (setjmp(png_jmpbuf(png_ptr))) return 0; // If there's an error, jump here. File and png closing still handled by guards // Read the png header if(GLOBAL_ZipFile.IsOpen()){ png_set_read_fn(png_ptr, NULL, PngZipRead); // Using global zipfile, so leave io ptr as null }else png_init_io(png_ptr,fin); png_set_sig_bytes(png_ptr,8); png_read_info(png_ptr,info_ptr); unsigned int width = png_get_image_width(png_ptr, info_ptr); unsigned int 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); // Modify so pixel data is RGBA if (color_type == PNG_COLOR_TYPE_PALETTE){ // palette -> rgb png_set_palette_to_rgb(png_ptr); #if SDL_BYTEORDER == SDL_LIL_ENDIAN png_set_add_alpha(png_ptr, 255, PNG_FILLER_BEFORE); #else png_set_add_alpha(png_ptr, 255, PNG_FILLER_AFTER); #endif } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) // grayscale 2,4 -> rgba png_set_tRNS_to_alpha(png_ptr); else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) #if SDL_BYTEORDER == SDL_LIL_ENDIAN png_set_add_alpha(png_ptr, 255, PNG_FILLER_BEFORE); #else png_set_add_alpha(png_ptr, 255, PNG_FILLER_AFTER); #endif else
void *png_read_file(const char *name, png_uint_32 *_w, png_uint_32 *_h, check_w_h_constraint_t constraint, rgba_surface_allocator_t allocator) { png_uint_32 i, w, h, stride; png_byte header[8]; png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_bytep * volatile row_ptrs = NULL; void * volatile s = NULL; void *pixels; int type; int bpp; FILE *f; f = fopen_unsafe(name, "rb"); if(!f) goto exit_out; if(fread(header, 1, 8, f) < 8) goto exit_close; if(png_sig_cmp(header, 0, 8)) goto exit_close; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(!png_ptr) goto exit_close; info_ptr = png_create_info_struct(png_ptr); if(!info_ptr) goto exit_free_close; if(setjmp(png_jmpbuf(png_ptr))) goto exit_free_close; png_init_io(png_ptr, f); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &w, &h, &bpp, &type, NULL, NULL, NULL); if(!constraint(w, h)) { warn("Requested image '%s' failed dimension checks.\n", name); goto exit_free_close; } if(type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); else if(type == PNG_COLOR_TYPE_GRAY_ALPHA || !(type & PNG_COLOR_MASK_COLOR)) png_set_gray_to_rgb(png_ptr); else if(!(type & PNG_COLOR_MASK_COLOR) && bpp < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); if(bpp == 16) png_set_strip_16(png_ptr); else if(bpp < 8) png_set_packing(png_ptr); if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); else if(!(type & PNG_COLOR_MASK_ALPHA)) png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER); // FIXME: Are these necessary? png_read_update_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &w, &h, &bpp, &type, NULL, NULL, NULL); row_ptrs = cmalloc(sizeof(png_bytep) * h); if(!row_ptrs) goto exit_free_close; s = allocator(w, h, &stride, &pixels); if(!s) goto exit_free_close; for(i = 0; i < h; i++) row_ptrs[i] = (png_bytep)(unsigned char *)pixels + i * stride; png_read_image(png_ptr, row_ptrs); if(_w) *_w = w; if(_h) *_h = h; exit_free_close: png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(row_ptrs); exit_close: fclose(f); exit_out: return s; }
int Read ( byte **data, int *width, int *height ) { // Setup the pointers *data = NULL; *width = 0; *height = 0; // Make sure we're actually reading PNG data. const int SIGNATURE_LEN = 8; byte ident[SIGNATURE_LEN]; memcpy (ident, buf, SIGNATURE_LEN); if ( !png_check_sig (ident, SIGNATURE_LEN) ) { ri.Printf (PRINT_ERROR, "PNG signature not found in given image."); return 0; } png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, png_print_error, png_print_warning); if ( png_ptr == NULL ) { ri.Printf (PRINT_ERROR, "Could not allocate enough memory to load the image."); return 0; } info_ptr = png_create_info_struct (png_ptr); if ( setjmp (png_jmpbuf (png_ptr)) ) { return 0; } // We've read the signature offset += SIGNATURE_LEN; // Setup reading information, and read header png_set_read_fn (png_ptr, (png_voidp)this, &user_read_data); #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED // This generic "ignore all, except required chunks" requires 1.6.0 or newer" png_set_keep_unknown_chunks (png_ptr, PNG_HANDLE_CHUNK_NEVER, NULL, -1); #endif png_set_sig_bytes (png_ptr, SIGNATURE_LEN); png_read_info (png_ptr, info_ptr); png_uint_32 width_; png_uint_32 height_; int depth; int colortype; png_get_IHDR (png_ptr, info_ptr, &width_, &height_, &depth, &colortype, NULL, NULL, NULL); // While modern OpenGL can handle non-PoT textures, it's faster to handle only PoT // so that the graphics driver doesn't have to fiddle about with the texture when uploading. if ( !IsPowerOfTwo (width_) || !IsPowerOfTwo (height_) ) { ri.Printf (PRINT_ERROR, "Width or height is not a power-of-two.\n"); return 0; } // This function is equivalent to using what used to be LoadPNG32. LoadPNG8 also existed, // but this only seemed to be used by the RMG system which does not work in JKA. If this // does need to be re-implemented, then colortype should be PNG_COLOR_TYPE_PALETTE or // PNG_COLOR_TYPE_GRAY. if ( colortype != PNG_COLOR_TYPE_RGB && colortype != PNG_COLOR_TYPE_RGBA ) { ri.Printf (PRINT_ERROR, "Image is not 24-bit or 32-bit."); return 0; } // Read the png data if ( colortype == PNG_COLOR_TYPE_RGB ) { // Expand RGB -> RGBA png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER); } png_read_update_info (png_ptr, info_ptr); // We always assume there are 4 channels. RGB channels are expanded to RGBA when read. byte *tempData = (byte *)ri.Z_Malloc (width_ * height_ * 4, TAG_TEMP_PNG, qfalse, 4); if ( !tempData ) { ri.Printf (PRINT_ERROR, "Could not allocate enough memory to load the image."); return 0; } // Dynamic array of row pointers, with 'height' elements, initialized to NULL. byte **row_pointers = (byte **)ri.Z_Malloc (sizeof (byte *) * height_, TAG_TEMP_PNG, qfalse, 4); if ( !row_pointers ) { ri.Printf (PRINT_ERROR, "Could not allocate enough memory to load the image."); ri.Z_Free (tempData); return 0; } // Re-set the jmp so that these new memory allocations can be reclaimed if ( setjmp (png_jmpbuf (png_ptr)) ) { ri.Z_Free (row_pointers); ri.Z_Free (tempData); return 0; } for ( unsigned int i = 0, j = 0; i < height_; i++, j += 4 ) { row_pointers[i] = tempData + j * width_; } png_read_image (png_ptr, row_pointers); // Finish reading png_read_end (png_ptr, NULL); ri.Z_Free (row_pointers); // Finally assign all the parameters *data = tempData; *width = width_; *height = height_; return 1; }
bool loadHSI (FILE * fp, int x, int y, bool reserve) { int t1, t2, n; unsigned short c; GLubyte * target; int32_t transCol = reserve ? -1 : 63519; int picWidth; int picHeight; int realPicWidth, realPicHeight; long file_pointer = ftell (fp); png_structp png_ptr; png_infop info_ptr, end_info; int fileIsPNG = true; // Is this a PNG file? char tmp[10]; size_t bytes_read = fread(tmp, 1, 8, fp); if (bytes_read != 8 && ferror (fp)) { debugOut("Reading error in loadHSI.\n"); } if (png_sig_cmp((png_byte *) tmp, 0, 8)) { // No, it's old-school HSI fileIsPNG = false; fseek(fp, file_pointer, SEEK_SET); picWidth = realPicWidth = get2bytes (fp); picHeight = realPicHeight = get2bytes (fp); } else { // Read the PNG header png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { return false; } 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; } end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return false; } png_init_io(png_ptr, fp); // Tell libpng which file to read png_set_sig_bytes(png_ptr, 8); // 8 bytes already read png_read_info(png_ptr, info_ptr); png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_method; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); picWidth = realPicWidth = width; picHeight = realPicHeight = height; if (bit_depth < 8) png_set_packing(png_ptr); png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); if (bit_depth == 16) png_set_strip_16(png_ptr); png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER); png_read_update_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); //int rowbytes = png_get_rowbytes(png_ptr, info_ptr); } GLfloat texCoordW = 1.0; GLfloat texCoordH = 1.0; if (! NPOT_textures) { picWidth = getNextPOT(picWidth); picHeight = getNextPOT(picHeight); texCoordW = ((double)realPicWidth) / picWidth; texCoordH = ((double)realPicHeight) / picHeight; } if (reserve) { if (! resizeBackdrop (realPicWidth, realPicHeight)) return false; } if (x == IN_THE_CENTRE) x = (sceneWidth - realPicWidth) >> 1; if (y == IN_THE_CENTRE) y = (sceneHeight - realPicHeight) >> 1; if (x < 0 || x + realPicWidth > sceneWidth || y < 0 || y + realPicHeight > sceneHeight) return false; if (fileIsPNG) { unsigned char * row_pointers[realPicHeight]; for (int i = 0; i<realPicHeight; i++) row_pointers[i] = backdropTexture + 4*i*picWidth; png_read_image(png_ptr, (png_byte **) row_pointers); png_read_end(png_ptr, NULL); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); } else { for (t2 = 0; t2 < realPicHeight; t2 ++) { t1 = 0; while (t1 < realPicWidth) { c = (unsigned short) get2bytes (fp); if (c & 32) { n = fgetc (fp) + 1; c -= 32; } else { n = 1; } while (n --) { target = backdropTexture + 4*picWidth*t2 + t1*4; if (c == transCol || c == 2015) { target[0] = (GLubyte) 0; target[1] = (GLubyte) 0; target[2] = (GLubyte) 0; target[3] = (GLubyte) 0; } else { target[0] = (GLubyte) redValue(c); target[1] = (GLubyte) greenValue(c); target[2] = (GLubyte) blueValue(c); target[3] = (GLubyte) 255; } t1++; } } } } GLuint tmpTex; glGenTextures (1, &tmpTex); glBindTexture(GL_TEXTURE_2D, tmpTex); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); if (gameSettings.antiAlias < 0) { glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } else { glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } texImage2D (GL_TEXTURE_2D, 0, GL_RGBA, picWidth, picHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, backdropTexture, tmpTex); //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); float btx1; float btx2; float bty1; float bty2; if (! NPOT_textures) { btx1 = backdropTexW * x / sceneWidth; btx2 = backdropTexW * (x+realPicWidth) / sceneWidth; bty1 = backdropTexH * y / sceneHeight; bty2 = backdropTexH * (y+realPicHeight) / sceneHeight; } else { btx1 = (float) x / sceneWidth; btx2 = (float) (x+realPicWidth) / sceneWidth; bty1 = (float) y / sceneHeight; bty2 = (float) (y+realPicHeight) / sceneHeight; } const GLfloat btexCoords[] = { btx1, bty1, btx2, bty1, btx1, bty2, btx2, bty2 }; setPixelCoords (true); int xoffset = 0; while (xoffset < realPicWidth) { int w = (realPicWidth-xoffset < viewportWidth) ? realPicWidth-xoffset : viewportWidth; int yoffset = 0; while (yoffset < realPicHeight) { int h = (realPicHeight-yoffset < viewportHeight) ? realPicHeight-yoffset : viewportHeight; glClear(GL_COLOR_BUFFER_BIT); // Clear The Screen const GLfloat vertices[] = { (GLfloat)-xoffset, (GLfloat)-yoffset, 0., (GLfloat)realPicWidth-xoffset, (GLfloat)-yoffset, 0., (GLfloat)-xoffset, (GLfloat)-yoffset+realPicHeight, 0., (GLfloat)realPicWidth-xoffset, (GLfloat)-yoffset+realPicHeight, 0. }; const GLfloat texCoords[] = { 0.0f, 0.0f, texCoordW, 0.0f, 0.0f, texCoordH, texCoordW, texCoordH }; if (backdropExists) { // Render the sprite to the backdrop // (using mulitexturing, so the old backdrop is seen where alpha < 1.0) glActiveTexture(GL_TEXTURE2); glBindTexture (GL_TEXTURE_2D, backdropTextureName); glActiveTexture(GL_TEXTURE0); glUseProgram(shader.paste); GLint uniform = glGetUniformLocation(shader.paste, "useLightTexture"); if (uniform >= 0) glUniform1i(uniform, 0); // No lighting setPMVMatrix(shader.paste); setPrimaryColor(1.0, 1.0, 1.0, 1.0); glBindTexture(GL_TEXTURE_2D, tmpTex); //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); drawQuad(shader.paste, vertices, 3, texCoords, NULL, btexCoords); glUseProgram(0); } else { // It's all new - nothing special to be done. glUseProgram(shader.texture); setPMVMatrix(shader.texture); glBindTexture(GL_TEXTURE_2D, tmpTex); setPrimaryColor(1.0, 0.0, 0.0, 0.0); drawQuad(shader.texture, vertices, 1, texCoords); glUseProgram(0); } // Copy Our ViewPort To The Texture copyTexSubImage2D(GL_TEXTURE_2D, 0, x+xoffset, y+yoffset, viewportOffsetX, viewportOffsetY, w, h, backdropTextureName); yoffset += viewportHeight; } xoffset += viewportWidth; } deleteTextures(1, &tmpTex); setPixelCoords (false); backdropExists = true; return true; }
static const char * VS_CC check_png(img_hnd_t *ih, int n, FILE *fp, vs_args_t *va) { uint8_t signature[PNG_SIG_LENGTH]; if (fread(signature, 1, PNG_SIG_LENGTH, fp) != PNG_SIG_LENGTH || png_sig_cmp(signature, 0, PNG_SIG_LENGTH)) { return "unsupported format"; } png_structp p_str = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!p_str) { return "failed to create png_read_struct"; } png_infop p_info = png_create_info_struct(p_str); if (!p_info) { png_destroy_read_struct(&p_str, NULL, NULL); return "failed to create png_info_struct"; } png_init_io(p_str, fp); png_set_sig_bytes(p_str, PNG_SIG_LENGTH); png_read_info(p_str, p_info); png_uint_32 width, height; int color_type, bit_depth; png_get_IHDR(p_str, p_info, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); if (color_type & PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(p_str); } if (bit_depth < 8) { png_set_packing(p_str); } if (ih->enable_alpha == 0) { if (color_type & PNG_COLOR_MASK_ALPHA) { png_set_strip_alpha(p_str); } } else if ((color_type & PNG_COLOR_MASK_ALPHA) == 0) { png_set_add_alpha(p_str, 0x00, PNG_FILLER_AFTER); } png_read_update_info(p_str, p_info); png_get_IHDR(p_str, p_info, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); uint32_t row_size = png_get_rowbytes(p_str, p_info); png_destroy_read_struct(&p_str, &p_info, NULL); ih->src[n].width = width; ih->src[n].height = height; VSPresetFormat pf = get_dst_format(color_type, bit_depth); if (pf == pfNone) { return "unsupported png color type"; } ih->src[n].format = va->vsapi->getFormatPreset(pf, va->core); ih->src[n].read = read_png; ih->src[n].flip = 0; if (row_size > va->max_row_size) { va->max_row_size = row_size; } return NULL; }
void png_reader<T>::read(unsigned x0, unsigned y0,image_data_32& 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.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_ - 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.setRow(i-y0,reinterpret_cast<unsigned*>(&row[x0 * 4]),w); } } //END } png_read_end(png_ptr,0); }
ImageReaderPNG::ImageType ImageReaderPNG::readData(const std::string& filePath, PNGInfra& infra, bool wantAlpha) { if (filePath.empty()) return eInvalid; infra.pFile = fopen(filePath.c_str(), "rb"); if (!infra.pFile) { fprintf(stderr, "Error opening file: %s\n", filePath.c_str()); return eInvalid; } unsigned char sig[8]; // check the signature fread(sig, 1, 8, infra.pFile); if (!png_check_sig(sig, 8)) { fprintf(stderr, "Cannot open file: %s - not a valid PNG file.\n", filePath.c_str()); fclose(infra.pFile); return eInvalid; } infra.pPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!infra.pPNG) { fclose(infra.pFile); return eInvalid; } infra.pInfo = png_create_info_struct(infra.pPNG); if (!infra.pInfo) { png_destroy_read_struct(&infra.pPNG, NULL, NULL); fclose(infra.pFile); return eInvalid; } if (setjmp(png_jmpbuf(infra.pPNG))) { png_destroy_read_struct(&infra.pPNG, &infra.pInfo, NULL); fclose(infra.pFile); return eInvalid; } png_init_io(infra.pPNG, infra.pFile); png_set_sig_bytes(infra.pPNG, 8); png_read_info(infra.pPNG, infra.pInfo); int colorType; int bitDepth, interlaceType, compressionType; png_get_IHDR(infra.pPNG, infra.pInfo, &infra.width, &infra.height, &bitDepth, &colorType, &interlaceType, &compressionType, NULL); if (colorType == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(infra.pPNG); if (png_get_valid(infra.pPNG, infra.pInfo, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(infra.pPNG); } if (bitDepth == 16) png_set_strip_16(infra.pPNG); ImageType type = eInvalid; if (!wantAlpha) { // add black Alpha if ((colorType & PNG_COLOR_MASK_ALPHA) == 0) png_set_add_alpha(infra.pPNG, 0xFF, PNG_FILLER_AFTER); if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(infra.pPNG); else if (colorType != PNG_COLOR_TYPE_RGB && colorType != PNG_COLOR_TYPE_RGB_ALPHA) return eInvalid; type = eRGBA; } else { if (colorType == PNG_COLOR_TYPE_RGB_ALPHA) type = eRGBA; else if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) type = eA; else return eInvalid; } png_read_update_info(infra.pPNG, infra.pInfo); infra.pRows = new png_bytep[infra.height * png_sizeof(png_bytep)]; png_set_rows(infra.pPNG, infra.pInfo, infra.pRows); for (unsigned int i = 0; i < infra.height; i++) { infra.pRows[i] = new png_byte[png_get_rowbytes(infra.pPNG, infra.pInfo)]; } png_read_image(infra.pPNG, infra.pRows); png_read_end(infra.pPNG, infra.pInfo); return type; }
bool isis::codecs::png::DecoderImpl::init() { // checking PNG signature png_byte pngsig[8]; int bytes_read = source_->read(pngsig, 8); if (8 != bytes_read) return false; bool is_png = 0 == png_sig_cmp(pngsig, 0, 8); if (!is_png) return false; // creating png read struct png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, report_error, report_warning); if (!png_) return false; // creating png info struct info_ = png_create_info_struct(png_); if (!info_) return false; // overriding read function png_set_read_fn(png_, reinterpret_cast<png_voidp>(source_.get()), read_png); // error handler if (setjmp(png_jmpbuf(png_))) { ISIS_LOG_COMMENT_LOCATION log_error("Reading error."); return false; } // reading info png_set_sig_bytes(png_, 8); png_read_info(png_, info_); // getting info // png_byte channels = png_get_channels( png, info ); png_uint_32 image_width = png_get_image_width(png_, info_); png_uint_32 image_height = png_get_image_height(png_, info_); png_byte bit_depth = png_get_bit_depth(png_, info_); png_byte colour_type = png_get_color_type(png_, info_); // png_byte filter_type = png_get_filter_type( png, info ); // png_byte interlace_type = png_get_interlace_type( png, info ); // png_byte compression_type = png_get_compression_type( png, info ); // converting any format to A8R8G8B8 switch (colour_type & ~PNG_COLOR_MASK_ALPHA) { case PNG_COLOR_TYPE_GRAY: if (bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_); png_set_gray_to_rgb(png_); break; case PNG_COLOR_TYPE_RGB: // do nothing break; case PNG_COLOR_TYPE_PALETTE: png_set_palette_to_rgb(png_); break; } // narrow 16bit channels if (bit_depth == 16) png_set_strip_16(png_); // add alpha channel if none if (!(colour_type & PNG_COLOR_MASK_ALPHA)) { // use tRNS if present, otherwise just add 0xFF alpha if (png_get_valid(png_, info_, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_); else png_set_add_alpha(png_, 0xFF, PNG_FILLER_AFTER); } // BGR -> RGB png_set_bgr(png_); size_ = {(int)image_width, (int)image_height}; bit_depth_ = 32; // all images are converted to A8R8G8B8 return true; }
void PngReader::read(unsigned x0, unsigned y0,ImageData32& image) { FILE *fp=fopen(fileName_.c_str(),"r"); if (!fp) throw ImageReaderException("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 ImageReaderException("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 ImageReaderException("failed to create info_ptr"); } png_init_io(png_ptr, fp); 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,1,1); 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); unsigned char* row= new unsigned char[rowbytes]; for (unsigned i=0;i<height_;++i) { png_read_row(png_ptr,row,0); if (i>=y0 && i<h) { image.setRow(i-y0,(unsigned*) &row[x0],w); } } //END delete [] row; png_read_end(png_ptr,0); png_destroy_read_struct(&png_ptr, &info_ptr,0); fclose(fp); }
int Read ( const char *filename, byte **data, int *width, int *height ) { // Setup the pointers *data = NULL; *width = 0; *height = 0; // Check and make sure the filename is valid (because you can never be too careful...) if( !filename || !filename[0] ) { ri->Printf( PRINT_ERROR, "PNG read called with invalid filename.\n" ); return 0; } // Copy the filename to the global variable Q_strncpyz( currentPNGFile, filename, sizeof(currentPNGFile) ); // Make sure we're actually reading PNG data. const int SIGNATURE_LEN = 8; byte ident[SIGNATURE_LEN]; memcpy (ident, buf, SIGNATURE_LEN); if ( !png_check_sig (ident, SIGNATURE_LEN) ) { ri->Printf (PRINT_ERROR, "PNG signature not found in %s.\n", currentPNGFile ); return 0; } png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, png_print_error, png_print_warning); if ( png_ptr == NULL ) { ri->Printf (PRINT_ERROR, "PNG: Could not allocate enough memory to load %s\n", currentPNGFile ); return 0; } info_ptr = png_create_info_struct (png_ptr); if ( setjmp (png_jmpbuf (png_ptr)) ) { return 0; } // We've read the signature offset += SIGNATURE_LEN; // Setup reading information, and read header png_set_read_fn (png_ptr, (png_voidp)this, &user_read_data); png_set_sig_bytes (png_ptr, SIGNATURE_LEN); png_read_info (png_ptr, info_ptr); png_uint_32 width_; png_uint_32 height_; int depth; int colortype; png_get_IHDR (png_ptr, info_ptr, &width_, &height_, &depth, &colortype, NULL, NULL, NULL); // While modern OpenGL can handle non-PoT textures, it's faster to handle only PoT // so that the graphics driver doesn't have to fiddle about with the texture when uploading. if ( !IsPowerOfTwo (width_) || !IsPowerOfTwo (height_) ) { ri->Printf (PRINT_ERROR, "PNG: Width or height of %s is not a power-of-two.\n", currentPNGFile ); return 0; } // This function is equivalent to using what used to be LoadPNG32. LoadPNG8 also existed, // but this only seemed to be used by the RMG system which does not work in JKA. If this // does need to be re-implemented, then colortype should be PNG_COLOR_TYPE_PALETTE or // PNG_COLOR_TYPE_GRAY. if ( colortype != PNG_COLOR_TYPE_RGB && colortype != PNG_COLOR_TYPE_RGBA ) { ri->Printf (PRINT_ERROR, "PNG: %s is not 24-bit or 32-bit.\n", currentPNGFile ); return 0; } // Read the png data if ( colortype == PNG_COLOR_TYPE_RGB ) { // Expand RGB -> RGBA png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER); } png_read_update_info (png_ptr, info_ptr); // We always assume there are 4 channels. RGB channels are expanded to RGBA when read. byte *tempData = (byte *)ri->Z_Malloc (width_ * height_ * 4, TAG_TEMP_PNG, qfalse, 4); if ( !tempData ) { ri->Printf (PRINT_ERROR, "Could not allocate enough memory to load %s.\n", currentPNGFile ); return 0; } // Dynamic array of row pointers, with 'height' elements, initialized to NULL. byte **row_pointers = (byte **)ri->Hunk_AllocateTempMemory (sizeof (byte *) * height_); if ( !row_pointers ) { ri->Printf (PRINT_ERROR, "Could not allocate enough memory to load %s.\n", currentPNGFile ); ri->Z_Free (tempData); return 0; } // Re-set the jmp so that these new memory allocations can be reclaimed if ( setjmp (png_jmpbuf (png_ptr)) ) { ri->Hunk_FreeTempMemory (row_pointers); ri->Z_Free (tempData); return 0; } for ( unsigned int i = 0, j = 0; i < height_; i++, j += 4 ) { row_pointers[i] = tempData + j * width_; } png_read_image (png_ptr, row_pointers); // Finish reading png_read_end (png_ptr, NULL); ri->Hunk_FreeTempMemory (row_pointers); // Finally assign all the parameters *data = tempData; *width = width_; *height = height_; return 1; }
s32 pngDecodeData(PPUThread& ppu, PHandle handle, PStream stream, vm::ptr<u8> data, PDataControlParam data_control_param, PDataOutInfo data_out_info, PCbControlDisp cb_control_disp = vm::null, PDispParam disp_param = vm::null) { if (cb_control_disp || disp_param) { throw EXCEPTION("Partial image decoding is not supported"); } // Indicate, that the PNG decoding is stopped/failed. This is incase, we return an error code in the middle of decoding data_out_info->status = CELL_PNGDEC_DEC_STATUS_STOP; // Possibilities for decoding in different sizes aren't tested, so if anyone finds any of these cases, we'll know about it. if (stream->info.imageWidth != stream->out_param.outputWidth) { throw EXCEPTION("Image width doesn't match output width! (%d/%d)", stream->out_param.outputWidth, stream->info.imageWidth); } if (stream->info.imageHeight != stream->out_param.outputHeight) { throw EXCEPTION("Image width doesn't match output height! (%d/%d)", stream->out_param.outputHeight, stream->info.imageHeight); } // Get the amount of output bytes per line const u32 bytes_per_line = data_control_param->outputBytesPerLine; // Whether to recaculate bytes per row bool recalculate_bytes_per_row = false; // Check if the game is expecting the number of bytes per line to be lower, than the actual bytes per line on the image. (Arkedo Pixel for example) // In such case we strip the bit depth to be lower. if ((bytes_per_line < stream->out_param.outputWidthByte) && stream->out_param.outputBitDepth != 8) { // Check if the packing is really 1 byte per 1 pixel if (stream->packing != CELL_PNGDEC_1BYTE_PER_1PIXEL) { throw EXCEPTION("Unexpected packing value! (%d)", stream->packing); } // Scale 16 bit depth down to 8 bit depth. PS3 uses png_set_strip_16, since png_set_scale_16 wasn't available back then. png_set_strip_16(stream->png_ptr); recalculate_bytes_per_row = true; } // Check if the outputWidthByte is smaller than the intended output length of a line. For example an image might be in RGB, but we need to output 4 components, so we need to perform alpha padding. else if (stream->out_param.outputWidthByte < (stream->out_param.outputWidth * stream->out_param.outputComponents)) { // If fixed alpha is not specified in such a case, the default value for the alpha is 0xFF (255) if (!stream->fixed_alpha) { stream->fixed_alpha_colour = 0xFF; } // We need to fill alpha (before or after, depending on the output colour format) using the fixed alpha value passed by the game. png_set_add_alpha(stream->png_ptr, stream->fixed_alpha_colour, stream->out_param.outputColorSpace == CELL_PNGDEC_RGBA ? PNG_FILLER_AFTER : PNG_FILLER_BEFORE); recalculate_bytes_per_row = true; } // We decode as RGBA, so we need to swap the alpha else if (stream->out_param.outputColorSpace == CELL_PNGDEC_ARGB) { // Swap the alpha channel for the ARGB output format, if the padding isn't needed png_set_swap_alpha(stream->png_ptr); } // Sometimes games pass in a RBG/RGBA image and want it as grayscale else if ((stream->out_param.outputColorSpace == CELL_PNGDEC_GRAYSCALE_ALPHA || stream->out_param.outputColorSpace == CELL_PNGDEC_GRAYSCALE) && (stream->info.colorSpace == CELL_PNGDEC_RGB || stream->info.colorSpace == CELL_PNGDEC_RGBA)) { // Tell libpng to convert it to grayscale png_set_rgb_to_gray(stream->png_ptr, PNG_ERROR_ACTION_NONE, PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT); recalculate_bytes_per_row = true; } if (recalculate_bytes_per_row) { // Update the info structure png_read_update_info(stream->png_ptr, stream->info_ptr); // Recalculate the bytes per row stream->out_param.outputWidthByte = png_get_rowbytes(stream->png_ptr, stream->info_ptr); } // Calculate the image size u32 image_size = stream->out_param.outputWidthByte * stream->out_param.outputHeight; // Buffer for storing the image std::vector<u8> png(image_size); // Make an unique pointer for the row pointers std::vector<u8*> row_pointers(stream->out_param.outputHeight); // Allocate memory for rows for (u32 y = 0; y < stream->out_param.outputHeight; y++) { row_pointers[y] = &png[y * stream->out_param.outputWidthByte]; } // Decode the image png_read_image(stream->png_ptr, row_pointers.data()); // Check if the image needs to be flipped const bool flip = stream->out_param.outputMode == CELL_PNGDEC_BOTTOM_TO_TOP; // Copy the result to the output buffer switch (stream->out_param.outputColorSpace) { case CELL_PNGDEC_RGB: case CELL_PNGDEC_RGBA: case CELL_PNGDEC_ARGB: case CELL_PNGDEC_GRAYSCALE_ALPHA: { // Check if we need to flip the image or need to leave empty bytes at the end of a line if ((bytes_per_line > stream->out_param.outputWidthByte) || flip) { // Get how many bytes per line we need to output - bytesPerLine is total amount of bytes per line, rest is unused and the game can do as it pleases. const u32 line_size = std::min(bytes_per_line, stream->out_param.outputWidth * 4); // If the game wants more bytes per line to be output, than the image has, then we simply copy what we have for each line, // and continue on the next line, thus leaving empty bytes at the end of the line. for (u32 i = 0; i < stream->out_param.outputHeight; i++) { const u32 dst = i * bytes_per_line; const u32 src = stream->out_param.outputWidth * 4 * (flip ? stream->out_param.outputHeight - i - 1 : i); memcpy(&data[dst], &png[src], line_size); } } else { // We can simply copy the output to the data pointer specified by the game, since we already do alpha channel transformations in libpng, if needed memcpy(data.get_ptr(), png.data(), image_size); } break; } default: throw EXCEPTION("Unsupported color space (%d)", stream->out_param.outputColorSpace); } // Get the number of iTXt, tEXt and zTXt chunks s32 text_chunks = 0; png_get_text(stream->png_ptr, stream->info_ptr, nullptr, &text_chunks); // Set the chunk information and the previously obtained number of text chunks data_out_info->numText = (u32)text_chunks; data_out_info->chunkInformation = pngDecGetChunkInformation(stream, true); data_out_info->numUnknownChunk = 0; // TODO: Get this somehow. Does anything even use or need this? // Indicate that the decoding succeeded data_out_info->status = CELL_PNGDEC_DEC_STATUS_FINISH; return CELL_OK; }
void FPngImageWrapper::UncompressPNGData( const ERGBFormat::Type InFormat, const int32 InBitDepth ) { // thread safety FScopeLock PNGLock(&GPNGSection); check( CompressedData.Num() ); check( Width > 0 ); check( Height > 0 ); // Note that PNGs on PC tend to be BGR check( InFormat == ERGBFormat::BGRA || InFormat == ERGBFormat::RGBA || InFormat == ERGBFormat::Gray ) // Other formats unsupported at present check( InBitDepth == 8 || InBitDepth == 16 ) // Other formats unsupported at present // Reset to the beginning of file so we can use png_read_png(), which expects to start at the beginning. ReadOffset = 0; png_structp png_ptr = png_create_read_struct_2( PNG_LIBPNG_VER_STRING, this, FPngImageWrapper::user_error_fn, FPngImageWrapper::user_warning_fn, NULL, FPngImageWrapper::user_malloc, FPngImageWrapper::user_free); check( png_ptr ); png_infop info_ptr = png_create_info_struct( png_ptr ); check( info_ptr ); PNGReadGuard PNGGuard( &png_ptr, &info_ptr ); { if (ColorType == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); } if ((ColorType & PNG_COLOR_MASK_COLOR) == 0 && BitDepth < 8) { png_set_expand_gray_1_2_4_to_8(png_ptr); } // Insert alpha channel with full opacity for RGB images without alpha if ((ColorType & PNG_COLOR_MASK_ALPHA) == 0 && (InFormat == ERGBFormat::BGRA || InFormat == ERGBFormat::RGBA)) { // png images don't set PNG_COLOR_MASK_ALPHA if they have alpha from a tRNS chunk, but png_set_add_alpha seems to be safe regardless if ((ColorType & PNG_COLOR_MASK_COLOR) == 0) { png_set_tRNS_to_alpha(png_ptr); } else if (ColorType == PNG_COLOR_TYPE_PALETTE) { png_set_tRNS_to_alpha(png_ptr); } if (InBitDepth == 8) { png_set_add_alpha(png_ptr, 0xff , PNG_FILLER_AFTER); } else if (InBitDepth == 16) { png_set_add_alpha(png_ptr, 0xffff , PNG_FILLER_AFTER); } } // Calculate Pixel Depth const uint32 PixelChannels = (InFormat == ERGBFormat::Gray) ? 1 : 4; const uint32 BytesPerPixel = (InBitDepth * PixelChannels) / 8; const uint32 BytesPerRow = BytesPerPixel * Width; RawData.Empty(Height * BytesPerRow); RawData.AddUninitialized(Height * BytesPerRow); png_set_read_fn( png_ptr, this, FPngImageWrapper::user_read_compressed ); png_bytep* row_pointers = (png_bytep*) png_malloc( png_ptr, Height*sizeof(png_bytep) ); PNGGuard.SetRowPointers(&row_pointers); for (int32 i = 0; i < Height; i++) { row_pointers[i]= &RawData[i * BytesPerRow]; } png_set_rows(png_ptr, info_ptr, row_pointers); uint32 Transform = (InFormat == ERGBFormat::BGRA) ? PNG_TRANSFORM_BGR : PNG_TRANSFORM_IDENTITY; // PNG files store 16-bit pixels in network byte order (big-endian, ie. most significant bits first). #if PLATFORM_LITTLE_ENDIAN // We're little endian so we need to swap if (BitDepth == 16) { Transform |= PNG_TRANSFORM_SWAP_ENDIAN; } #endif // Convert grayscale png to RGB if requested if ((ColorType & PNG_COLOR_MASK_COLOR) == 0 && (InFormat == ERGBFormat::RGBA || InFormat == ERGBFormat::BGRA)) { Transform |= PNG_TRANSFORM_GRAY_TO_RGB; } // Convert RGB png to grayscale if requested if ((ColorType & PNG_COLOR_MASK_COLOR) != 0 && InFormat == ERGBFormat::Gray) { png_set_rgb_to_gray_fixed(png_ptr, 2 /* warn if image is in color */, -1, -1); } // Strip alpha channel if requested output is grayscale if (InFormat == ERGBFormat::Gray) { // this is not necessarily the best option, instead perhaps: // png_color background = {0,0,0}; // png_set_background(png_ptr, &background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); Transform |= PNG_TRANSFORM_STRIP_ALPHA; } // Reduce 16-bit to 8-bit if requested if (BitDepth == 16 && InBitDepth == 8) { #if PNG_LIBPNG_VER >= 10504 check(0); // Needs testing Transform |= PNG_TRANSFORM_SCALE_16; #else Transform |= PNG_TRANSFORM_STRIP_16; #endif } // Increase 8-bit to 16-bit if requested if (BitDepth <= 8 && InBitDepth == 16) { #if PNG_LIBPNG_VER >= 10504 check(0); // Needs testing Transform |= PNG_TRANSFORM_EXPAND_16 #else // Expanding 8-bit images to 16-bit via transform needs a libpng update check(0); #endif } png_read_png(png_ptr, info_ptr, Transform, NULL); } RawFormat = InFormat; RawBitDepth = InBitDepth; }
static GstFlowReturn gst_pngdec_caps_create_and_set (GstPngDec * pngdec) { GstFlowReturn ret = GST_FLOW_OK; gint bpc = 0, color_type; png_uint_32 width, height; GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN; g_return_val_if_fail (GST_IS_PNGDEC (pngdec), GST_FLOW_ERROR); /* Get bits per channel */ bpc = png_get_bit_depth (pngdec->png, pngdec->info); /* Get Color type */ color_type = png_get_color_type (pngdec->png, pngdec->info); /* Add alpha channel if 16-bit depth, but not for GRAY images */ if ((bpc > 8) && (color_type != PNG_COLOR_TYPE_GRAY)) { png_set_add_alpha (pngdec->png, 0xffff, PNG_FILLER_BEFORE); png_set_swap (pngdec->png); } #if 0 /* We used to have this HACK to reverse the outgoing bytes, but the problem * that originally required the hack seems to have been in videoconvert's * RGBA descriptions. It doesn't seem needed now that's fixed, but might * still be needed on big-endian systems, I'm not sure. J.S. 6/7/2007 */ if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) png_set_bgr (pngdec->png); #endif /* Gray scale with alpha channel converted to RGB */ if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { GST_LOG_OBJECT (pngdec, "converting grayscale png with alpha channel to RGB"); png_set_gray_to_rgb (pngdec->png); } /* Gray scale converted to upscaled to 8 bits */ if ((color_type == PNG_COLOR_TYPE_GRAY_ALPHA) || (color_type == PNG_COLOR_TYPE_GRAY)) { if (bpc < 8) { /* Convert to 8 bits */ GST_LOG_OBJECT (pngdec, "converting grayscale image to 8 bits"); #if PNG_LIBPNG_VER < 10400 png_set_gray_1_2_4_to_8 (pngdec->png); #else png_set_expand_gray_1_2_4_to_8 (pngdec->png); #endif } } /* Palette converted to RGB */ if (color_type == PNG_COLOR_TYPE_PALETTE) { GST_LOG_OBJECT (pngdec, "converting palette png to RGB"); png_set_palette_to_rgb (pngdec->png); } png_set_interlace_handling (pngdec->png); /* Update the info structure */ png_read_update_info (pngdec->png, pngdec->info); /* Get IHDR header again after transformation settings */ png_get_IHDR (pngdec->png, pngdec->info, &width, &height, &bpc, &pngdec->color_type, NULL, NULL, NULL); GST_LOG_OBJECT (pngdec, "this is a %dx%d PNG image", (gint) width, (gint) height); switch (pngdec->color_type) { case PNG_COLOR_TYPE_RGB: GST_LOG_OBJECT (pngdec, "we have no alpha channel, depth is 24 bits"); if (bpc == 8) format = GST_VIDEO_FORMAT_RGB; break; case PNG_COLOR_TYPE_RGB_ALPHA: GST_LOG_OBJECT (pngdec, "we have an alpha channel, depth is 32 or 64 bits"); if (bpc == 8) format = GST_VIDEO_FORMAT_RGBA; else if (bpc == 16) format = GST_VIDEO_FORMAT_ARGB64; break; case PNG_COLOR_TYPE_GRAY: GST_LOG_OBJECT (pngdec, "We have an gray image, depth is 8 or 16 (be) bits"); if (bpc == 8) format = GST_VIDEO_FORMAT_GRAY8; else if (bpc == 16) format = GST_VIDEO_FORMAT_GRAY16_BE; break; default: break; } if (format == GST_VIDEO_FORMAT_UNKNOWN) { GST_ELEMENT_ERROR (pngdec, STREAM, NOT_IMPLEMENTED, (NULL), ("pngdec does not support this color type")); ret = GST_FLOW_NOT_SUPPORTED; goto beach; } /* Check if output state changed */ if (pngdec->output_state) { GstVideoInfo *info = &pngdec->output_state->info; if (width == GST_VIDEO_INFO_WIDTH (info) && height == GST_VIDEO_INFO_HEIGHT (info) && GST_VIDEO_INFO_FORMAT (info) == format) { goto beach; } gst_video_codec_state_unref (pngdec->output_state); } pngdec->output_state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (pngdec), format, width, height, pngdec->input_state); gst_video_decoder_negotiate (GST_VIDEO_DECODER (pngdec)); GST_DEBUG ("Final %d %d", GST_VIDEO_INFO_WIDTH (&pngdec->output_state->info), GST_VIDEO_INFO_HEIGHT (&pngdec->output_state->info)); beach: return ret; }
/* TODO: I wonder why this function ALWAYS returns 0 */ int loadPNG(ePtr<gPixmap> &result, const char *filename, int accel) { CFile fp(filename, "rb"); if (!fp) { eDebug("[ePNG] couldn't open %s", filename ); return 0; } { __u8 header[8]; if (!fread(header, 8, 1, fp)) { eDebug("[ePNG] failed to get png header"); return 0; } if (png_sig_cmp(header, 0, 8)) { eDebug("[ePNG] header size mismatch"); return 0; } } png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if (!png_ptr) { eDebug("[ePNG] failed to create read struct"); return 0; } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { eDebug("[ePNG] failed to create info struct"); png_destroy_read_struct(&png_ptr, (png_infopp)0, (png_infopp)0); return 0; } png_infop end_info = png_create_info_struct(png_ptr); if (!end_info) { eDebug("[ePNG] failed to create end info struct"); png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return 0; } if (setjmp(png_jmpbuf(png_ptr))) { eDebug("[ePNG] png setjump failed or activated"); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); result = 0; return 0; } png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); png_uint_32 width, height; int bit_depth; int color_type; int interlace_type; int channels; int trns; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, 0, 0); channels = png_get_channels(png_ptr, info_ptr); trns = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS); //eDebug("[ePNG] %s: before %dx%dx%dbpcx%dchan coltyp=%d", filename, (int)width, (int)height, bit_depth, channels, color_type); /* * gPixmaps use 8 bits per channel. rgb pixmaps are stored as abgr. * So convert 1,2 and 4 bpc to 8bpc images that enigma can blit * so add 'empty' alpha channel * Expand G+tRNS to GA, RGB+tRNS to RGBA */ 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 && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && trns) png_set_tRNS_to_alpha(png_ptr); if ((color_type == PNG_COLOR_TYPE_GRAY && trns) || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); png_set_bgr(png_ptr); } if (color_type == PNG_COLOR_TYPE_RGB) { if (trns) png_set_tRNS_to_alpha(png_ptr); else png_set_add_alpha(png_ptr, 255, PNG_FILLER_AFTER); } if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) png_set_bgr(png_ptr); // Update the info structures after the transformations take effect if (interlace_type != PNG_INTERLACE_NONE) png_set_interlace_handling(png_ptr); // needed before read_update_info() png_read_update_info (png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); channels = png_get_channels(png_ptr, info_ptr); result = new gPixmap(eSize(width, height), bit_depth * channels, accel); gUnmanagedSurface *surface = result->surface; png_bytep *rowptr = new png_bytep[height]; for (unsigned int i = 0; i < height; i++) rowptr[i] = ((png_byte*)(surface->data)) + i * surface->stride; png_read_image(png_ptr, rowptr); delete [] rowptr; int num_palette = -1, num_trans = -1; if (color_type == PNG_COLOR_TYPE_PALETTE) { if (png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) { png_color *palette; png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); if (num_palette) surface->clut.data = new gRGB[num_palette]; else surface->clut.data = 0; surface->clut.colors = num_palette; for (int i = 0; i < num_palette; i++) { surface->clut.data[i].a = 0; surface->clut.data[i].r = palette[i].red; surface->clut.data[i].g = palette[i].green; surface->clut.data[i].b = palette[i].blue; } if (trns) { png_byte *trans; png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, 0); for (int i = 0; i < num_trans; i++) surface->clut.data[i].a = 255 - trans[i]; for (int i = num_trans; i < num_palette; i++) surface->clut.data[i].a = 0; } } else { surface->clut.data = 0; surface->clut.colors = 0; } surface->clut.start = 0; } //eDebug("[ePNG] %s: after %dx%dx%dbpcx%dchan coltyp=%d cols=%d trans=%d", filename, (int)width, (int)height, bit_depth, channels, color_type, num_palette, num_trans); png_read_end(png_ptr, end_info); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return 0; }
// Pull a texture off the graphics card // Load a PNG file, and send it to upload_texture SPTexture* SPTexture_frompng(const char* path) { SPTexture* texture = NULL; spTexID texid = spTexID_Bad; FILE* pngf = fopen(path, "rb"); uint32_t width = 0; uint32_t height = 0; ubyte* pixmap = NULL; png_byte** row_heads = NULL; const size_t SIG_LENGTH = 8; png_structp png_state = NULL; png_infop png_info = NULL; if(pngf == NULL) { return texture; } // Ensure that the given path really is a PNG file { ubyte signature[8]; size_t bytes_read = fread(signature, sizeof(byte), SIG_LENGTH, pngf); if(bytes_read < 8) { fclose(pngf); printf("Error\n"); return texture; } if(png_sig_cmp(signature, 0, SIG_LENGTH)) { fclose(pngf); return texture; } } // Read the PNG file png_state = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_info = png_create_info_struct(png_state); png_init_io(png_state, pngf); png_set_sig_bytes(png_state, SIG_LENGTH); if(setjmp(png_jmpbuf(png_state))) { // Something went wrong while reading the PNG data png_destroy_read_struct(&png_state, &png_info, NULL); if(pixmap) { free(pixmap); } if(row_heads) { free(row_heads); } fclose(pngf); return spTexID_Bad; } // Prep work for reading the PNG { png_read_info(png_state, png_info); width = png_get_image_width(png_state, png_info); height = png_get_image_height(png_state, png_info); // We use signed integers to correspond with glTexImage2D, but PNG uses uint32_t if(width >= INT_MAX || height >= INT_MAX) { return spTexID_Bad; } png_byte bit_depth = png_get_bit_depth(png_state, png_info); png_byte color_type = png_get_color_type(png_state, png_info); png_size_t stride = 0; // Ensure RGBA color if(color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_state); } if(color_type == PNG_COLOR_TYPE_RGB) { png_set_add_alpha(png_state, 0xFF, PNG_FILLER_AFTER); } // Ensure exactly 8 bits of color if(bit_depth > 8) { png_set_strip_16(png_state); } else if(bit_depth < 8) { png_set_packing(png_state); } png_read_update_info(png_state, png_info); // Create blank, safe-ish row pointers that point inside a dense pixmap stride = png_get_rowbytes(png_state, png_info); pixmap = malloc(stride * height); row_heads = calloc(sizeof(byte*), height); for(uint32_t row=0; row<height; row++) { // I've read that OpenGL expects rows in the opposite order, but // this works fine row_heads[row] = pixmap + row*stride; // row_heads[row] = &(pixmap[(height-row-1)*stride]); } // Read in the PNG data png_read_image(png_state, row_heads); texid = upload_texture(spTextureFmt_RGBA, (GLsizei)width, (GLsizei)height, pixmap); // Goodness gracious, Spaceman! What a mess to clean! png_destroy_read_struct(&png_state, &png_info, NULL); free(row_heads); free(pixmap); } fclose(pngf); texture = SPTexture_new(width, height, texid); return texture; }
APNGDATA * loadPng(IPngReader *pSrc) { png_bytep dataFrame; png_uint_32 bytesPerRow; png_uint_32 bytesPerFrame; png_bytepp rowPointers; png_byte sig[8]; png_structp png_ptr_read; png_infop info_ptr_read; pSrc->read(sig,8); if(!png_check_sig(sig,8)) { return NULL; } png_ptr_read = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); info_ptr_read = png_create_info_struct(png_ptr_read); if (setjmp(png_jmpbuf(png_ptr_read))) { png_destroy_read_struct(&png_ptr_read, &info_ptr_read, NULL); return NULL; } png_set_read_fn(png_ptr_read,pSrc,mypng_read_data); png_set_sig_bytes(png_ptr_read, 8); if ((png_ptr_read->bit_depth < 8) || (png_ptr_read->color_type == PNG_COLOR_TYPE_PALETTE) || (info_ptr_read->valid & PNG_INFO_tRNS)) png_set_expand(png_ptr_read); png_set_add_alpha(png_ptr_read, 0xff, PNG_FILLER_AFTER); png_set_interlace_handling(png_ptr_read); png_set_gray_to_rgb(png_ptr_read); png_set_strip_16(png_ptr_read); png_read_info(png_ptr_read, info_ptr_read); png_read_update_info(png_ptr_read, info_ptr_read); bytesPerRow = png_ptr_read->width * 4; bytesPerFrame = bytesPerRow * png_ptr_read->height; APNGDATA * apng = (APNGDATA*) malloc(sizeof(APNGDATA)); memset(apng,0,sizeof(APNGDATA)); apng->nWid = png_ptr_read->width; apng->nHei = png_ptr_read->height; //图像帧数据 dataFrame = (png_bytep)malloc(bytesPerRow * apng->nHei); memset(dataFrame,0,bytesPerFrame); //获得扫描行指针 rowPointers = (png_bytepp)malloc(sizeof(png_bytep)* apng->nHei); for(int i=0;i<apng->nHei;i++) rowPointers[i] = dataFrame + bytesPerRow * i; if (!png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_acTL)) {//load png doesn't has this trunk. png_read_image(png_ptr_read,rowPointers); apng->pdata =dataFrame; apng->nFrames =1; }else {//load apng apng->nFrames = png_get_num_frames(png_ptr_read, info_ptr_read);//获取总帧数 png_bytep data = (png_bytep)malloc( bytesPerFrame * apng->nFrames);//为每一帧分配内存 png_bytep curFrame = (png_bytep)malloc(bytesPerFrame); memset(curFrame,0,bytesPerFrame); apng->nLoops = png_get_num_plays(png_ptr_read, info_ptr_read); apng->pDelay = (unsigned short*)malloc(sizeof(unsigned short)*apng->nFrames); for(int iFrame = 0;iFrame<apng->nFrames;iFrame++) { //读帧信息头 png_read_frame_head(png_ptr_read, info_ptr_read); //计算出帧延时信息 if (png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_fcTL)) { png_uint_16 delay_num = info_ptr_read->next_frame_delay_num, delay_den = info_ptr_read->next_frame_delay_den; if (delay_den==0 || delay_den==100) apng->pDelay[iFrame] = delay_num; else if (delay_den==10) apng->pDelay[iFrame] = delay_num*10; else if (delay_den==1000) apng->pDelay[iFrame] = delay_num/10; else apng->pDelay[iFrame] = delay_num*100/delay_den; }else { apng->pDelay[iFrame] = 0; } //读取PNG帧到dataFrame中,不含偏移数据 png_read_image(png_ptr_read, rowPointers); {//将当前帧数据绘制到当前显示帧中:1)获得绘制的背景;2)计算出绘制位置; 3)使用指定的绘制方式与背景混合 //1)计算出绘制位置 png_bytep lineDst=curFrame+info_ptr_read->next_frame_y_offset*bytesPerRow + 4 * info_ptr_read->next_frame_x_offset; png_bytep lineSour=dataFrame; //2)使用指定的绘制方式与背景混合 switch(info_ptr_read->next_frame_blend_op) { case PNG_BLEND_OP_OVER: { for(unsigned int y=0;y<info_ptr_read->next_frame_height;y++) { png_bytep lineDst1=lineDst; png_bytep lineSour1=lineSour; for(unsigned int x=0;x<info_ptr_read->next_frame_width;x++) { png_byte alpha = lineSour1[3]; *lineDst1++ = ((*lineDst1)*(255-alpha)+(*lineSour1++)*alpha)>>8; *lineDst1++ = ((*lineDst1)*(255-alpha)+(*lineSour1++)*alpha)>>8; *lineDst1++ = ((*lineDst1)*(255-alpha)+(*lineSour1++)*alpha)>>8; *lineDst1++ = ((*lineDst1)*(255-alpha)+(*lineSour1++)*alpha)>>8; } lineDst += bytesPerRow; lineSour+= bytesPerRow; } } break; case PNG_BLEND_OP_SOURCE: { for(unsigned int y=0;y<info_ptr_read->next_frame_height;y++) { memcpy(lineDst,lineSour,info_ptr_read->next_frame_width*4); lineDst += bytesPerRow; lineSour+= bytesPerRow; } } break; default: SASSERT(FALSE); break; } png_bytep targetFrame = data + bytesPerFrame * iFrame; memcpy(targetFrame,curFrame,bytesPerFrame); lineDst=curFrame+info_ptr_read->next_frame_y_offset*bytesPerRow + 4 * info_ptr_read->next_frame_x_offset; //3)处理当前帧绘制区域 switch(info_ptr_read->next_frame_dispose_op) { case PNG_DISPOSE_OP_BACKGROUND://clear background { for(unsigned int y=0;y<info_ptr_read->next_frame_height;y++) { memset(lineDst,0,info_ptr_read->next_frame_width*4); lineDst += bytesPerRow; } } break; case PNG_DISPOSE_OP_PREVIOUS://copy previous frame if(iFrame>0) { memcpy(curFrame,targetFrame-bytesPerFrame,bytesPerFrame); } break; case PNG_DISPOSE_OP_NONE://using current frame, doing nothing break; default: SASSERT(0); break; } } } free(curFrame); free(dataFrame); apng->pdata =data; } free(rowPointers); png_read_end(png_ptr_read,info_ptr_read); png_destroy_read_struct(&png_ptr_read, &info_ptr_read, NULL); return apng; }