/** \brief Constructor. * * @param fd File handle to adapt. * @param skip How many bytes of file have already been read */ PngReader(FILE *fd, unsigned skip) : m_fd(fd), m_png(NULL), m_info(NULL), m_end(NULL), m_block(NULL) { m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(!m_png) { std::stringstream sstr; sstr << "could not create a PNG read struct"; BOOST_THROW_EXCEPTION(std::runtime_error(sstr.str())); } m_info = png_create_info_struct(m_png); if(!m_info) { std::stringstream sstr; sstr << "could not create a PNG info struct"; BOOST_THROW_EXCEPTION(std::runtime_error(sstr.str())); } m_end = png_create_info_struct(m_png); if(!m_end) { std::stringstream sstr; sstr << "could not create a PNG end info struct"; BOOST_THROW_EXCEPTION(std::runtime_error(sstr.str())); } png_byte keep_chunks[] = { 0 }; png_set_keep_unknown_chunks(m_png, PNG_HANDLE_CHUNK_NEVER, keep_chunks, 0); png_init_io(m_png, m_fd); png_set_sig_bytes(m_png, static_cast<int>(skip)); }
bool PNGInstallChunkHandler(png_structp png, void* context, ChunkHandler handler, const char* chunkName) { if (setjmp(png_jmpbuf(png))) { return false; } png_set_read_user_chunk_fn(png, context, handler); png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_bytep) chunkName, 1); return true; }
/* * Extension to libpng's unknown chunk handler. */ static void opng_set_keep_unknown_chunk(png_structp png_ptr, int keep, png_bytep chunk_type) { png_byte chunk_name[5]; /* Call png_set_keep_unknown_chunks() once per each chunk type only. */ memcpy(chunk_name, chunk_type, 4); chunk_name[4] = 0; if (!png_handle_as_unknown(png_ptr, chunk_name)) png_set_keep_unknown_chunks(png_ptr, keep, chunk_name, 1); }
// Reads the header and initializes the output fields, if not NULL. // // @param stream Input data. Will be read to get enough information to properly // setup the codec. // @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL. // If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is // expected to continue to own it for the lifetime of the png_ptr. // @param outCodec Optional output variable. If non-NULL, will be set to a new // SkPngCodec on success. // @param png_ptrp Optional output variable. If non-NULL, will be set to a new // png_structp on success. // @param info_ptrp Optional output variable. If non-NULL, will be set to a new // png_infop on success; // @return true on success, in which case the caller is responsible for calling // png_destroy_read_struct(png_ptrp, info_ptrp). // If it returns false, the passed in fields (except stream) are unchanged. static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec** outCodec, png_structp* png_ptrp, png_infop* info_ptrp) { // The image is known to be a PNG. Decode enough to know the SkImageInfo. png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_fn, sk_warning_fn); if (!png_ptr) { return false; } AutoCleanPng autoClean(png_ptr, stream, chunkReader, outCodec); png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == nullptr) { return false; } autoClean.setInfoPtr(info_ptr); // FIXME: Could we use the return value of setjmp to specify the type of // error? if (setjmp(png_jmpbuf(png_ptr))) { return false; } #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED // Hookup our chunkReader so we can see any user-chunks the caller may be interested in. // This needs to be installed before we read the png header. Android may store ninepatch // chunks in the header. if (chunkReader) { png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0); png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_user_chunk); } #endif const bool decodedBounds = autoClean.decodeBounds(); if (!decodedBounds) { return false; } // On success, decodeBounds releases ownership of png_ptr and info_ptr. if (png_ptrp) { *png_ptrp = png_ptr; } if (info_ptrp) { *info_ptrp = info_ptr; } // decodeBounds takes care of setting outCodec if (outCodec) { SkASSERT(*outCodec); } return true; }
int main(int argc, char *argv[]) { FILE *fp; const char *filename; unsigned char header[8]; png_structp png_ptr = NULL; png_infop info_ptr = NULL; int result = 1; if (argc != 2) { fprintf(stderr, "Usage: %s <png>\n", argv[0]); return -1; } filename = argv[1]; fp = fopen(filename, "rb"); if (!fp) { perror(filename); return -1; } if (fread(header, 8, 1, fp) != 1) FAIL("%s: Couldn't read header.", filename); if (png_sig_cmp(header, 0, 8)) FAIL("%s: Not a PNG.", filename); png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) FAIL("Couldn't set up PNG reader."); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) FAIL("Couldn't set up PNG info reader."); if (setjmp(png_jmpbuf(png_ptr))) goto done; png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, 8); png_set_read_user_chunk_fn(png_ptr, NULL, &read_chunk); png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER, NULL, 0); png_read_info(png_ptr, info_ptr); puts(apng ? "APNG" : "PNG"); result = 0; done: if (png_ptr) png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fclose(fp); return result; }
/* * Imports an image from an PNG file stream. * The function returns 0 on success or -1 on error. */ int opng_decode_image(struct opng_codec_context *context, FILE *stream, const char *fname, bool force_no_palette) { context->libpng_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, opng_read_error, opng_read_warning); context->info_ptr = png_create_info_struct(context->libpng_ptr); if (!context->libpng_ptr || !context->info_ptr) { opng_error(0, "Out of memory"); png_destroy_read_struct(&context->libpng_ptr, &context->info_ptr, 0); exit(1); } opng_init_image(context->image); const char * volatile err_msg; /* volatile is required by cexcept */ struct opng_encoding_stats * stats = context->stats; opng_init_stats(stats); context->stream = stream; context->fname = fname; if (force_no_palette) { png_set_palette_to_rgb(context->libpng_ptr); } Try { png_set_keep_unknown_chunks(context->libpng_ptr, PNG_HANDLE_CHUNK_ALWAYS, 0, 0); png_set_read_fn(context->libpng_ptr, context, opng_read_data); png_read_png(context->libpng_ptr, context->info_ptr, 0, 0); } Catch (err_msg) { if (opng_validate_image(context->libpng_ptr, context->info_ptr)) { /* The critical image info has already been loaded. * Treat this error as a warning in order to allow data recovery. */ opng_warning(fname, err_msg); } else { opng_error(fname, err_msg); return -1; } } opng_load_image(context->image, context->libpng_ptr, context->info_ptr); return 0; }
/* * Encodes an image to a PNG file stream. */ int opng_encode_image(struct opng_codec_context *context, int filtered, FILE *stream, const char *fname, int level) { const char * volatile err_msg; /* volatile is required by cexcept */ context->libpng_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, opng_write_error, opng_write_warning); context->info_ptr = png_create_info_struct(context->libpng_ptr); if (!context->libpng_ptr || !context->info_ptr) { opng_error(0, "Out of memory"); png_destroy_write_struct(&context->libpng_ptr, &context->info_ptr); exit(1); } struct opng_encoding_stats * stats = context->stats; opng_init_stats(stats); context->stream = stream; context->fname = fname; Try { png_set_filter(context->libpng_ptr, PNG_FILTER_TYPE_BASE, filtered ? PNG_ALL_FILTERS : PNG_FILTER_NONE); if (level != 6) { png_set_compression_level(context->libpng_ptr, level); } png_set_compression_mem_level(context->libpng_ptr, 8); png_set_compression_window_bits(context->libpng_ptr, 15); png_set_compression_strategy(context->libpng_ptr, 0); png_set_keep_unknown_chunks(context->libpng_ptr, PNG_HANDLE_CHUNK_ALWAYS, 0, 0); opng_store_image(context->image, context->libpng_ptr, context->info_ptr); /* Write the PNG stream. */ png_set_write_fn(context->libpng_ptr, context, opng_write_data, 0); png_write_png(context->libpng_ptr, context->info_ptr, 0, 0); } Catch (err_msg) { stats->idat_size = OPTK_INT64_MAX; opng_error(fname, err_msg); return -1; } png_data_freer(context->libpng_ptr, context->info_ptr, PNG_USER_WILL_FREE_DATA, PNG_FREE_ALL); png_destroy_write_struct(&context->libpng_ptr, &context->info_ptr); return 0; }
void nsPNGDecoder::InitInternal() { mCMSMode = gfxPlatform::GetCMSMode(); if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0) mCMSMode = eCMSMode_Off; mDisablePremultipliedAlpha = (mDecodeFlags & DECODER_NO_PREMULTIPLY_ALPHA) != 0; #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED static png_byte color_chunks[]= { 99, 72, 82, 77, '\0', /* cHRM */ 105, 67, 67, 80, '\0'}; /* iCCP */ static png_byte unused_chunks[]= { 98, 75, 71, 68, '\0', /* bKGD */ 104, 73, 83, 84, '\0', /* hIST */ 105, 84, 88, 116, '\0', /* iTXt */ 111, 70, 70, 115, '\0', /* oFFs */ 112, 67, 65, 76, '\0', /* pCAL */ 115, 67, 65, 76, '\0', /* sCAL */ 112, 72, 89, 115, '\0', /* pHYs */ 115, 66, 73, 84, '\0', /* sBIT */ 115, 80, 76, 84, '\0', /* sPLT */ 116, 69, 88, 116, '\0', /* tEXt */ 116, 73, 77, 69, '\0', /* tIME */ 122, 84, 88, 116, '\0'}; /* zTXt */ #endif // For size decodes, we only need a small buffer if (IsSizeDecode()) { mHeaderBuf = (PRUint8 *)moz_xmalloc(BYTES_NEEDED_FOR_DIMENSIONS); return; } /* For full decodes, do png init stuff */ /* Initialize the container's source image header. */ /* Always decode to 24 bit pixdepth */ mPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, nsPNGDecoder::error_callback, nsPNGDecoder::warning_callback); if (!mPNG) { PostDecoderError(NS_ERROR_OUT_OF_MEMORY); return; } mInfo = png_create_info_struct(mPNG); if (!mInfo) { PostDecoderError(NS_ERROR_OUT_OF_MEMORY); png_destroy_read_struct(&mPNG, NULL, NULL); return; } #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED /* Ignore unused chunks */ if (mCMSMode == eCMSMode_Off) png_set_keep_unknown_chunks(mPNG, 1, color_chunks, 2); png_set_keep_unknown_chunks(mPNG, 1, unused_chunks, (int)sizeof(unused_chunks)/5); #endif #ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED if (mCMSMode != eCMSMode_Off) png_set_chunk_malloc_max(mPNG, 4000000L); #endif /* use this as libpng "progressive pointer" (retrieve in callbacks) */ png_set_progressive_read_fn(mPNG, static_cast<png_voidp>(this), nsPNGDecoder::info_callback, nsPNGDecoder::row_callback, nsPNGDecoder::end_callback); }
//--------------------------------------------------------------------------- void __fastcall TDeePNG::LoadFromStream(Classes::TStream * Stream) { // LoadFromStream png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_infop end_info = NULL; png_bytep *row_pointers = NULL; BYTE *image = NULL; png_uint_32 i; try { // create png_struct png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, (png_voidp)this, DeePNG_error, DeePNG_warning, (png_voidp)this, DeePNG_malloc, DeePNG_free); // set read_chunk_callback png_set_read_user_chunk_fn(png_ptr, reinterpret_cast<void*>(this), PNG_read_chunk_callback); png_set_keep_unknown_chunks(png_ptr, 2, NULL, 0); // keep only if safe-to-copy chunks, for all unknown chunks // create png_info info_ptr = png_create_info_struct(png_ptr); // create end_info end_info = png_create_info_struct(png_ptr); // set stream input functions png_set_read_fn(png_ptr, (voidp)Stream, DeePNG_read_data); // set read_row_callback png_set_read_status_fn(png_ptr, DeePNG_read_row_callback); // call png_read_info png_read_info(png_ptr, info_ptr); // retrieve IHDR png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_type; png_get_IHDR(png_ptr,info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type); // expand palletted image which has transparent color, to 32bpp if (png_get_valid(png_ptr, info_ptr,PNG_INFO_tRNS)) { png_set_expand(png_ptr); color_type=PNG_COLOR_TYPE_RGB_ALPHA; } // analyse IHDR ( color_type ) switch(color_type) { case PNG_COLOR_TYPE_GRAY_ALPHA: PixelFormat=pf32bit; break; case PNG_COLOR_TYPE_GRAY: // w/b SetGrayscalePalette(this,bit_depth); break; case PNG_COLOR_TYPE_PALETTE: SetColorDepth(this,bit_depth); break; case PNG_COLOR_TYPE_RGB_ALPHA: PixelFormat=pf32bit; break; case PNG_COLOR_TYPE_RGB: PixelFormat=pf24bit; break; default: throw EDeePNG("EDeePNG : Non-supported color type."); } // retrieve offset information png_int_32 offset_x, offset_y; int offset_unit_type; if(png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, &offset_unit_type)) { ofs_x = offset_x; ofs_y = offset_y; ofs_unit = offset_unit_type; ofs_set = true; } else { ofs_set = false; } // check size if(width>=65536 || height>=65536) { throw EDeePNG("EDeePNG : Too large image size."); } // retrieve palette if(color_type == PNG_COLOR_TYPE_PALETTE) { int num_palette; png_color *palette = NULL; png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); int i; #pragma pack(push, 1) struct { WORD palVersion; WORD palNumEntries; PALETTEENTRY entry[256]; } pal; #pragma pack(pop) pal.palVersion = 0x300; pal.palNumEntries = num_palette; for(i = 0; i < num_palette; i++) { pal.entry[i].peRed = palette[i].red; pal.entry[i].peGreen = palette[i].green; pal.entry[i].peBlue = palette[i].blue; pal.entry[i].peFlags = 0; } Palette = CreatePalette((const LOGPALETTE*)&pal); } // collapse 16bit precision data to 8bit if(bit_depth == 16) png_set_strip_16(png_ptr); // change color component order if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) png_set_bgr(png_ptr); // call png_read_update_info ... png_read_update_info(png_ptr, info_ptr); // set size Width=width, Height=height; // allocate memory for row_pointers row_pointers = new png_bytep[height]; png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr); image = new BYTE[rowbytes*height]; for(i = 0;i < height; i++) { row_pointers[i] = image + i*rowbytes; } // load image png_read_image(png_ptr, row_pointers); // finish loading image png_read_end(png_ptr, info_ptr); // set image to ScanLines BYTE *imageptr = image; if(color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { // IA IA IA .... for(i = 0; i < height; i++) { BYTE *scanptr = (BYTE*)ScanLine[i]; png_uint_32 j; for(j = 0; j < width; j++) { BYTE i = *(imageptr++); scanptr[0] = i; scanptr[1] = i; scanptr[2] = i; scanptr[3] = *(imageptr++); scanptr += 4; } } } else { // intact copy for(i = 0; i < height; i++) { BYTE *scanptr = (BYTE*)ScanLine[i]; memcpy(scanptr, imageptr, rowbytes); imageptr += rowbytes; } } } catch(...) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); if(row_pointers) delete [] row_pointers; if(image) delete [] image; throw; } png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); if(row_pointers) delete [] row_pointers; if(image) delete [] image; }
bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp, png_infop *info_ptrp) { /* 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. */ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn, NULL); // png_voidp user_error_ptr, user_error_fn, user_warning_fn); if (png_ptr == NULL) { return false; } *png_ptrp = png_ptr; /* Allocate/initialize the memory for image information. */ 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 false; } *info_ptrp = info_ptr; /* 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))) { return false; } /* If you are using replacement read functions, instead of calling * png_init_io() here you would call: */ png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn); png_set_seek_fn(png_ptr, sk_seek_fn); /* where user_io_ptr is a structure you want available to the callbacks */ /* If we have already read some of the signature */ // png_set_sig_bytes(png_ptr, 0 /* sig_read */ ); // hookup our peeker so we can see any user-chunks the caller may be interested in png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0); if (this->getPeeker()) { png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk); } /* 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_uint_32 origWidth, origHeight; int bit_depth, color_type, interlace_type; png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL); /* tell libpng to strip 16 bit/color files down to 8 bits/color */ if (bit_depth == 16) { 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). */ if (bit_depth < 8) { 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_gray_1_2_4_to_8(png_ptr); } /* Make a grayscale image into RGB. */ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); } return true; }
static int read_png(FILE *fp) { png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); png_infop info_ptr = NULL; png_bytep row = NULL, display = NULL; if (png_ptr == NULL) return 0; if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); if (row != NULL) free(row); if (display != NULL) free(display); return 0; } png_init_io(png_ptr, fp); info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) png_error(png_ptr, "OOM allocating info structure"); png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0); png_read_info(png_ptr, info_ptr); { png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr); row = malloc(rowbytes); display = malloc(rowbytes); if (row == NULL || display == NULL) png_error(png_ptr, "OOM allocating row buffers"); { png_uint_32 height = png_get_image_height(png_ptr, info_ptr); int passes = png_set_interlace_handling(png_ptr); int pass; png_start_read_image(png_ptr); for (pass = 0; pass < passes; ++pass) { png_uint_32 y = height; /* NOTE: this trashes the row each time; interlace handling won't * work, but this avoids memory thrashing for speed testing. */ while (y-- > 0) png_read_row(png_ptr, row, display); } } } /* Make sure to read to the end of the file: */ png_read_end(png_ptr, info_ptr); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(row); free(display); return 1; }
void nsPNGDecoder::InitInternal() { // For size decodes, we don't need to initialize the png decoder if (IsSizeDecode()) { return; } mCMSMode = gfxPlatform::GetCMSMode(); if (GetDecodeFlags() & imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION) { mCMSMode = eCMSMode_Off; } mDisablePremultipliedAlpha = GetDecodeFlags() & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA; #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED static png_byte color_chunks[]= { 99, 72, 82, 77, '\0', // cHRM 105, 67, 67, 80, '\0'}; // iCCP static png_byte unused_chunks[]= { 98, 75, 71, 68, '\0', // bKGD 104, 73, 83, 84, '\0', // hIST 105, 84, 88, 116, '\0', // iTXt 111, 70, 70, 115, '\0', // oFFs 112, 67, 65, 76, '\0', // pCAL 115, 67, 65, 76, '\0', // sCAL 112, 72, 89, 115, '\0', // pHYs 115, 66, 73, 84, '\0', // sBIT 115, 80, 76, 84, '\0', // sPLT 116, 69, 88, 116, '\0', // tEXt 116, 73, 77, 69, '\0', // tIME 122, 84, 88, 116, '\0'}; // zTXt #endif // For full decodes, do png init stuff // Initialize the container's source image header // Always decode to 24 bit pixdepth mPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nsPNGDecoder::error_callback, nsPNGDecoder::warning_callback); if (!mPNG) { PostDecoderError(NS_ERROR_OUT_OF_MEMORY); return; } mInfo = png_create_info_struct(mPNG); if (!mInfo) { PostDecoderError(NS_ERROR_OUT_OF_MEMORY); png_destroy_read_struct(&mPNG, nullptr, nullptr); return; } #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED // Ignore unused chunks if (mCMSMode == eCMSMode_Off) { png_set_keep_unknown_chunks(mPNG, 1, color_chunks, 2); } png_set_keep_unknown_chunks(mPNG, 1, unused_chunks, (int)sizeof(unused_chunks)/5); #endif #ifdef PNG_SET_USER_LIMITS_SUPPORTED if (mCMSMode != eCMSMode_Off) { png_set_chunk_malloc_max(mPNG, 4000000L); } #endif #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED // Disallow palette-index checking, for speed; we would ignore the warning // anyhow. This feature was added at libpng version 1.5.10 and is disabled // in the embedded libpng but enabled by default in the system libpng. This // call also disables it in the system libpng, for decoding speed. // Bug #745202. png_set_check_for_invalid_index(mPNG, 0); #endif #if defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_sRGB_PROFILE_CHECKS) && \ PNG_sRGB_PROFILE_CHECKS >= 0 // Skip checking of sRGB ICC profiles png_set_option(mPNG, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON); #endif // use this as libpng "progressive pointer" (retrieve in callbacks) png_set_progressive_read_fn(mPNG, static_cast<png_voidp>(this), nsPNGDecoder::info_callback, nsPNGDecoder::row_callback, nsPNGDecoder::end_callback); }
/* Test one file */ int test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) { static FILE *fpin, *fpout; /* "static" prevents setjmp corruption */ png_structp read_ptr, write_ptr; png_infop read_info_ptr, write_info_ptr, end_info_ptr, write_end_info_ptr; png_bytep row_buf; png_uint_32 y; png_uint_32 width, height; int num_pass, pass; int bit_depth, color_type; #ifdef PNG_SETJMP_SUPPORTED #ifdef USE_FAR_KEYWORD jmp_buf jmp_env; #endif #endif char inbuf[256], outbuf[256]; row_buf = (png_bytep)NULL; if ((fpin = fopen(inname, "rb")) == NULL) { fprintf(STDERR, "Could not find input file %s\n", inname); return (1); } if ((fpout = fopen(outname, "wb")) == NULL) { fprintf(STDERR, "Could not open output file %s\n", outname); fclose(fpin); return (1); } png_debug(0, "Allocating read and write structures\n"); #ifdef PNG_USER_MEM_SUPPORTED read_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL, (png_voidp)NULL, (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); #else read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL); #endif #if defined(PNG_NO_STDIO) png_set_error_fn(read_ptr, (png_voidp)inname, png_default_error, png_default_warning); #endif #ifdef PNG_USER_MEM_SUPPORTED write_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL, (png_voidp)NULL, (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); #else write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL); #endif #if defined(PNG_NO_STDIO) png_set_error_fn(write_ptr, (png_voidp)inname, png_default_error, png_default_warning); #endif png_debug(0, "Allocating read_info, write_info and end_info structures\n"); read_info_ptr = png_create_info_struct(read_ptr); write_info_ptr = png_create_info_struct(write_ptr); end_info_ptr = png_create_info_struct(read_ptr); write_end_info_ptr = png_create_info_struct(write_ptr); #ifdef PNG_USER_MEM_SUPPORTED #endif #ifdef PNG_SETJMP_SUPPORTED png_debug(0, "Setting jmp_env for read struct\n"); #ifdef USE_FAR_KEYWORD if (setjmp(jmp_env)) #else if (setjmp(png_jmp_env(read_ptr))) #endif { fprintf(STDERR, "%s -> %s: libpng read error\n", inname, outname); png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); png_destroy_info_struct(write_ptr, &write_end_info_ptr); png_destroy_write_struct(&write_ptr, &write_info_ptr); fclose(fpin); fclose(fpout); return (1); } #ifdef USE_FAR_KEYWORD png_memcpy(png_jmp_env(read_ptr),jmp_env,sizeof(jmp_buf)); #endif png_debug(0, "Setting jmp_env for write struct\n"); #ifdef USE_FAR_KEYWORD if (setjmp(jmp_env)) #else if (setjmp(png_jmp_env(write_ptr))) #endif { fprintf(STDERR, "%s -> %s: libpng write error\n", inname, outname); png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); png_destroy_info_struct(write_ptr, &write_end_info_ptr); png_destroy_write_struct(&write_ptr, &write_info_ptr); fclose(fpin); fclose(fpout); return (1); } #ifdef USE_FAR_KEYWORD png_memcpy(png_jmp_env(write_ptr),jmp_env,sizeof(jmp_buf)); #endif #endif png_debug(0, "Initializing input and output streams\n"); #if !defined(PNG_NO_STDIO) png_init_io(read_ptr, fpin); png_init_io(write_ptr, fpout); #else png_set_read_fn(read_ptr, (png_voidp)fpin, png_default_read_data); png_set_write_fn(write_ptr, (png_voidp)fpout, png_default_write_data, #if defined(PNG_WRITE_FLUSH_SUPPORTED) png_default_flush); #else NULL); #endif #endif if(status_dots_requested == 1) { png_set_write_status_fn(write_ptr, write_row_callback); png_set_read_status_fn(read_ptr, read_row_callback); } else { png_set_write_status_fn(write_ptr, NULL); png_set_read_status_fn(read_ptr, NULL); } #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) { int i; for(i=0; i<256; i++) filters_used[i]=0; png_set_read_user_transform_fn(read_ptr, count_filters); } #endif #if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) zero_samples=0; png_set_write_user_transform_fn(write_ptr, count_zero_samples); #endif #define HANDLE_CHUNK_IF_SAFE 2 #define HANDLE_CHUNK_ALWAYS 3 #if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) png_set_keep_unknown_chunks(read_ptr, HANDLE_CHUNK_ALWAYS, NULL, 0); #endif #if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) png_set_keep_unknown_chunks(write_ptr, HANDLE_CHUNK_IF_SAFE, NULL, 0); #endif png_debug(0, "Reading info struct\n"); png_read_info(read_ptr, read_info_ptr); png_debug(0, "Transferring info struct\n"); { int interlace_type, compression_type, filter_type; if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type)) { png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth, #if defined(PNG_WRITE_INTERLACING_SUPPORTED) color_type, interlace_type, compression_type, filter_type); #else color_type, PNG_INTERLACE_NONE, compression_type, filter_type); #endif } }
static int ReadPNG(char *Path, unsigned char **ImgData, int *width, int *length) { FILE *f; unsigned char *tmpRow = NULL; unsigned char *Tempstr = NULL; png_structp png_ptr; png_infop info_ptr; png_uint_32 png_width, png_height, scanline_width; int bit_depth, color_type, interlace_type, row; int channels; f = fopen(Path, "r"); if (!f) return (FALSE); /* we have to load the first 4 bytes of the png file, this contains png version info */ Tempstr = calloc(1, 4); if (fread(Tempstr, 1, 4, f) != 4) { free(Tempstr); fclose(f); return (FALSE); } /* check if valid png */ if (png_sig_cmp(Tempstr, (png_size_t) 0, 4)) printf("%s not a png?\n", Path); /* create the png reading structure, errors go to stderr */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { fclose(f); free(Tempstr); return (FALSE); } /* allocate info struct */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { fclose(f); png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL); free(Tempstr); return (FALSE); } /* set error handling */ #if PNG_LIBPNG_VER_SONUM >= 15 if (setjmp(png_jmpbuf(png_ptr))) #else /*PNG_LIBPNG_VER_SONUM >= 15*/ if (setjmp(png_ptr->jmpbuf)) #endif /*PNG_LIBPNG_VER_SONUM >= 15*/ { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); free(Tempstr); fclose(f); return (FALSE); } /* prepare the reader to ignore all recognized chunks whose data isn't * going to be used, i.e., all chunks recognized by libpng except for * IHDR, PLTE, IDAT, IEND, tRNS, bKGD, gAMA, and sRGB : */ { #ifndef HANDLE_CHUNK_NEVER /* prior to libpng-1.2.5, this macro was internal, so we define it here. */ # define HANDLE_CHUNK_NEVER 1 #endif /* these byte strings were copied from png.h. * If a future libpng version recognizes more chunks, add them * to this list. If a future version of readpng2.c recognizes * more chunks, delete them from this list. */ png_byte png_chunk_types_to_ignore[] = { 99, 72, 82, 77, '\0', /* cHRM */ 104, 73, 83, 84, '\0', /* hIST */ 105, 67, 67, 80, '\0', /* iCCP */ 105, 84, 88, 116, '\0', /* iTXt */ 111, 70, 70, 115, '\0', /* oFFs */ 112, 67, 65, 76, '\0', /* pCAL */ 115, 67, 65, 76, '\0', /* sCAL */ 112, 72, 89, 115, '\0', /* pHYs */ 115, 66, 73, 84, '\0', /* sBIT */ 115, 80, 76, 84, '\0', /* sPLT */ 116, 69, 88, 116, '\0', /* tEXt */ 116, 73, 77, 69, '\0', /* tIME */ 122, 84, 88, 116, '\0' }; /* zTXt */ #define NUM_PNG_CHUNK_TYPES_TO_IGNORE 13 png_set_keep_unknown_chunks(png_ptr, HANDLE_CHUNK_NEVER, png_chunk_types_to_ignore, NUM_PNG_CHUNK_TYPES_TO_IGNORE); } /* set input method */ png_init_io(png_ptr, f); /* tell libpng we have already read some bytes */ png_set_sig_bytes(png_ptr, 4); /* read all info */ png_read_info(png_ptr, info_ptr); /* get some characteristics of the file */ png_get_IHDR(png_ptr, info_ptr, &png_width, &png_height, &bit_depth, &color_type, &interlace_type, NULL, NULL); *width = (int)png_width; *length = (int)png_height; /*expand bit depth */ png_set_expand(png_ptr); png_set_interlace_handling(png_ptr); png_read_update_info(png_ptr, info_ptr); /* get size of scanline */ scanline_width = png_get_rowbytes(png_ptr, info_ptr); channels = png_get_channels(png_ptr, info_ptr); /* allocate texture memory */ *ImgData = (unsigned char *)malloc(png_width * png_height * 4); if (channels == 3) tmpRow = (unsigned char *)malloc(scanline_width * sizeof(unsigned char)); /* read the image line by line into the user's buffer */ for (row = 0; row < png_height; row++) { if (channels == 3) { Read3ByteRow(png_ptr, tmpRow, *ImgData, row, png_width); } else png_read_row(png_ptr, (unsigned char *)((*ImgData) + (row * scanline_width)), NULL); } /* finish reading the file */ png_read_end(png_ptr, NULL); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fclose(f); free(Tempstr); if (tmpRow) free(tmpRow); return (TRUE); }
int readpng2_init(mainprog_info *mainprog_ptr) { png_structp png_ptr; /* note: temporary variables! */ png_infop info_ptr; /* could also replace libpng warning-handler (final NULL), but no need: */ png_ptr = png_create_read_struct(png_get_libpng_ver(NULL), mainprog_ptr, readpng2_error_handler, readpng2_warning_handler); if (!png_ptr) return 4; /* out of memory */ info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, NULL, NULL); return 4; /* out of memory */ } /* we could create a second info struct here (end_info), but it's only * useful if we want to keep pre- and post-IDAT chunk info separated * (mainly for PNG-aware image editors and converters) */ /* setjmp() must be called in every function that calls a PNG-reading * libpng function, unless an alternate error handler was installed-- * but compatible error handlers must either use longjmp() themselves * (as in this program) or exit immediately, so here we are: */ if (setjmp(mainprog_ptr->jmpbuf)) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return 2; } #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED /* prepare the reader to ignore all recognized chunks whose data won't be * used, i.e., all chunks recognized by libpng except for IHDR, PLTE, IDAT, * IEND, tRNS, bKGD, gAMA, and sRGB (small performance improvement) */ { /* These byte strings were copied from png.h. If a future version * of readpng2.c recognizes more chunks, add them to this list. */ static PNG_CONST png_byte chunks_to_process[] = { 98, 75, 71, 68, '\0', /* bKGD */ 103, 65, 77, 65, '\0', /* gAMA */ 115, 82, 71, 66, '\0', /* sRGB */ }; /* Ignore all chunks except for IHDR, PLTE, tRNS, IDAT, and IEND */ png_set_keep_unknown_chunks(png_ptr, -1 /* PNG_HANDLE_CHUNK_NEVER */, NULL, -1); /* But do not ignore chunks in the "chunks_to_process" list */ png_set_keep_unknown_chunks(png_ptr, 0 /* PNG_HANDLE_CHUNK_AS_DEFAULT */, chunks_to_process, sizeof(chunks_to_process)/5); } #endif /* PNG_HANDLE_AS_UNKNOWN_SUPPORTED */ /* instead of doing png_init_io() here, now we set up our callback * functions for progressive decoding */ png_set_progressive_read_fn(png_ptr, mainprog_ptr, readpng2_info_callback, readpng2_row_callback, readpng2_end_callback); /* make sure we save our pointers for use in readpng2_decode_data() */ mainprog_ptr->png_ptr = png_ptr; mainprog_ptr->info_ptr = info_ptr; /* and that's all there is to initialization */ return 0; }
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->Hunk_AllocateTempMemory (sizeof (byte *) * height_); 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->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; }
static int read_png(FILE *fp) { png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); png_infop info_ptr = NULL; png_bytep row = NULL, display = NULL; if (png_ptr == NULL) return 0; if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); if (row != NULL) free(row); if (display != NULL) free(display); return 0; } png_init_io(png_ptr, fp); info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) png_error(png_ptr, "OOM allocating info structure"); png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0); png_read_info(png_ptr, info_ptr); { png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr); /* Failure to initialize these is harmless */ row = malloc(rowbytes); display = malloc(rowbytes); if (row == NULL || display == NULL) png_error(png_ptr, "OOM allocating row buffers"); { png_uint_32 height = png_get_image_height(png_ptr, info_ptr); # ifdef PNG_READ_INTERLACING_SUPPORTED int passes = png_set_interlace_handling(png_ptr); # else /* !READ_INTERLACING */ int passes = png_get_interlace_type(png_ptr, info_ptr) == PNG_INTERLACE_ADAM7 ? PNG_INTERLACE_ADAM7_PASSES : 1; # endif /* !READ_INTERLACING */ int pass; png_start_read_image(png_ptr); for (pass = 0; pass < passes; ++pass) { png_uint_32 y = height; # ifndef PNG_READ_INTERLACING_SUPPORTED if (passes == PNG_INTERLACE_ADAM7_PASSES) y = PNG_PASS_ROWS(y, pass); # endif /* READ_INTERLACING */ /* NOTE: this trashes the row each time; interlace handling won't * work, but this avoids memory thrashing for speed testing. */ while (y-- > 0) png_read_row(png_ptr, row, display); } } } /* Make sure to read to the end of the file: */ png_read_end(png_ptr, info_ptr); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(row); free(display); return 1; }
// Reads the header and initializes the output fields, if not NULL. // // @param stream Input data. Will be read to get enough information to properly // setup the codec. // @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL. // If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is // expected to continue to own it for the lifetime of the png_ptr. // @param outCodec Optional output variable. If non-NULL, will be set to a new // SkPngCodec on success. // @param png_ptrp Optional output variable. If non-NULL, will be set to a new // png_structp on success. // @param info_ptrp Optional output variable. If non-NULL, will be set to a new // png_infop on success; // @return true on success, in which case the caller is responsible for calling // png_destroy_read_struct(png_ptrp, info_ptrp). // If it returns false, the passed in fields (except stream) are unchanged. static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec** outCodec, png_structp* png_ptrp, png_infop* info_ptrp) { // The image is known to be a PNG. Decode enough to know the SkImageInfo. png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_fn, sk_warning_fn); if (!png_ptr) { return false; } AutoCleanPng autoClean(png_ptr); png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == nullptr) { return false; } autoClean.setInfoPtr(info_ptr); // FIXME: Could we use the return value of setjmp to specify the type of // error? if (setjmp(png_jmpbuf(png_ptr))) { return false; } png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn); #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED // Hookup our chunkReader so we can see any user-chunks the caller may be interested in. // This needs to be installed before we read the png header. Android may store ninepatch // chunks in the header. if (chunkReader) { png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0); png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_user_chunk); } #endif // 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_uint_32 origWidth, origHeight; int bitDepth, encodedColorType; png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, &encodedColorType, nullptr, nullptr, nullptr); // Tell libpng to strip 16 bit/color files down to 8 bits/color. // TODO: Should we handle this in SkSwizzler? Could this also benefit // RAW decodes? if (bitDepth == 16) { SkASSERT(PNG_COLOR_TYPE_PALETTE != encodedColorType); png_set_strip_16(png_ptr); } // Now determine the default colorType and alphaType and set the required transforms. // Often, we depend on SkSwizzler to perform any transforms that we need. However, we // still depend on libpng for many of the rare and PNG-specific cases. SkEncodedInfo::Color color; SkEncodedInfo::Alpha alpha; switch (encodedColorType) { case PNG_COLOR_TYPE_PALETTE: // Extract multiple pixels with bit depths of 1, 2, and 4 from a single // byte into separate bytes (useful for paletted and grayscale images). if (bitDepth < 8) { // TODO: Should we use SkSwizzler here? png_set_packing(png_ptr); } color = SkEncodedInfo::kPalette_Color; // Set the alpha depending on if a transparency chunk exists. alpha = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha; break; case PNG_COLOR_TYPE_RGB: if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { // Convert to RGBA if transparency chunk exists. png_set_tRNS_to_alpha(png_ptr); color = SkEncodedInfo::kRGBA_Color; alpha = SkEncodedInfo::kBinary_Alpha; } else { color = SkEncodedInfo::kRGB_Color; alpha = SkEncodedInfo::kOpaque_Alpha; } break; case PNG_COLOR_TYPE_GRAY: // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel. if (bitDepth < 8) { // TODO: Should we use SkSwizzler here? 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); color = SkEncodedInfo::kGrayAlpha_Color; alpha = SkEncodedInfo::kBinary_Alpha; } else { color = SkEncodedInfo::kGray_Color; alpha = SkEncodedInfo::kOpaque_Alpha; } break; case PNG_COLOR_TYPE_GRAY_ALPHA: color = SkEncodedInfo::kGrayAlpha_Color; alpha = SkEncodedInfo::kUnpremul_Alpha; break; case PNG_COLOR_TYPE_RGBA: color = SkEncodedInfo::kRGBA_Color; alpha = SkEncodedInfo::kUnpremul_Alpha; break; default: // All the color types have been covered above. SkASSERT(false); color = SkEncodedInfo::kRGBA_Color; alpha = SkEncodedInfo::kUnpremul_Alpha; } int numberPasses = png_set_interlace_handling(png_ptr); autoClean.release(); if (png_ptrp) { *png_ptrp = png_ptr; } if (info_ptrp) { *info_ptrp = info_ptr; } if (outCodec) { sk_sp<SkColorSpace> colorSpace = read_color_space(png_ptr, info_ptr); if (!colorSpace) { // Treat unmarked pngs as sRGB. colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); } SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8); if (1 == numberPasses) { *outCodec = new SkPngScanlineDecoder(origWidth, origHeight, info, stream, chunkReader, png_ptr, info_ptr, bitDepth, colorSpace); } else { *outCodec = new SkPngInterlacedScanlineDecoder(origWidth, origHeight, info, stream, chunkReader, png_ptr, info_ptr, bitDepth, numberPasses, colorSpace); } } return true; }
void nsPNGDecoder::InitInternal() { mCMSMode = gfxPlatform::GetCMSMode(); if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0) mCMSMode = eCMSMode_Off; mDisablePremultipliedAlpha = (mDecodeFlags & DECODER_NO_PREMULTIPLY_ALPHA) != 0; #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED static png_byte color_chunks[]= { 99, 72, 82, 77, '\0', /* cHRM */ 105, 67, 67, 80, '\0' }; /* iCCP */ static png_byte unused_chunks[]= { 98, 75, 71, 68, '\0', /* bKGD */ 104, 73, 83, 84, '\0', /* hIST */ 105, 84, 88, 116, '\0', /* iTXt */ 111, 70, 70, 115, '\0', /* oFFs */ 112, 67, 65, 76, '\0', /* pCAL */ 115, 67, 65, 76, '\0', /* sCAL */ 112, 72, 89, 115, '\0', /* pHYs */ 115, 66, 73, 84, '\0', /* sBIT */ 115, 80, 76, 84, '\0', /* sPLT */ 116, 69, 88, 116, '\0', /* tEXt */ 116, 73, 77, 69, '\0', /* tIME */ 122, 84, 88, 116, '\0' }; /* zTXt */ #endif // For size decodes, we only need a small buffer if (IsSizeDecode()) { mHeaderBuf = (uint8_t *)moz_xmalloc(BYTES_NEEDED_FOR_DIMENSIONS); return; } /* For full decodes, do png init stuff */ /* Initialize the container's source image header. */ /* Always decode to 24 bit pixdepth */ mPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, nsPNGDecoder::error_callback, nsPNGDecoder::warning_callback); if (!mPNG) { PostDecoderError(NS_ERROR_OUT_OF_MEMORY); return; } mInfo = png_create_info_struct(mPNG); if (!mInfo) { PostDecoderError(NS_ERROR_OUT_OF_MEMORY); png_destroy_read_struct(&mPNG, NULL, NULL); return; } #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED /* Ignore unused chunks */ if (mCMSMode == eCMSMode_Off) png_set_keep_unknown_chunks(mPNG, 1, color_chunks, 2); png_set_keep_unknown_chunks(mPNG, 1, unused_chunks, (int)sizeof(unused_chunks)/5); #endif #ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED if (mCMSMode != eCMSMode_Off) png_set_chunk_malloc_max(mPNG, 4000000L); #endif #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED #ifndef PR_LOGGING /* Disallow palette-index checking, for speed; we would ignore the warning * anyhow unless we have defined PR_LOGGING. This feature was added at * libpng version 1.5.10 and is disabled in the embedded libpng but enabled * by default in the system libpng. This call also disables it in the * system libpng, for decoding speed. Bug #745202. */ png_set_check_for_invalid_index(mPNG, 0); #endif #endif /* use this as libpng "progressive pointer" (retrieve in callbacks) */ png_set_progressive_read_fn(mPNG, static_cast<png_voidp>(this), nsPNGDecoder::info_callback, nsPNGDecoder::row_callback, nsPNGDecoder::end_callback); }
s32 pngDecSetParameter(PStream stream, PInParam in_param, POutParam out_param, PExtInParam extra_in_param = vm::null, PExtOutParam extra_out_param = vm::null) { if (in_param->outputPackFlag == CELL_PNGDEC_1BYTE_PER_NPIXEL) { fmt::throw_exception("Packing not supported! (%d)" HERE, in_param->outputPackFlag); } // flag to keep unknown chunks png_set_keep_unknown_chunks(stream->png_ptr, PNG_HANDLE_CHUNK_IF_SAFE, 0, 0); // Scale 16 bit depth down to 8 bit depth. if (stream->info.bitDepth == 16 && in_param->outputBitDepth == 8) { // PS3 uses png_set_strip_16, since png_set_scale_16 wasn't available back then. png_set_strip_16(stream->png_ptr); } // This shouldnt ever happen, but not sure what to do if it does, just want it logged for now if (stream->info.bitDepth != 16 && in_param->outputBitDepth == 16) cellPngDec.error("Output depth of 16 with non input depth of 16 specified!"); if (in_param->commandPtr != vm::null) cellPngDec.warning("Ignoring CommandPtr."); if (stream->info.colorSpace != in_param->outputColorSpace) { // check if we need to set alpha const bool inputHasAlpha = cellPngColorSpaceHasAlpha(stream->info.colorSpace); const bool outputWantsAlpha = cellPngColorSpaceHasAlpha(in_param->outputColorSpace); if (outputWantsAlpha && !inputHasAlpha) { if (in_param->outputAlphaSelect == CELL_PNGDEC_FIX_ALPHA) png_set_add_alpha(stream->png_ptr, in_param->outputColorAlpha, in_param->outputColorSpace == CELL_PNGDEC_ARGB ? PNG_FILLER_BEFORE : PNG_FILLER_AFTER); else { // Check if we can steal the alpha from a trns block if (png_get_valid(stream->png_ptr, stream->info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(stream->png_ptr); // if not, just set default of 0xff else png_set_add_alpha(stream->png_ptr, 0xff, in_param->outputColorSpace == CELL_PNGDEC_ARGB ? PNG_FILLER_BEFORE : PNG_FILLER_AFTER); } } else if (inputHasAlpha && !outputWantsAlpha) png_set_strip_alpha(stream->png_ptr); else if (in_param->outputColorSpace == CELL_PNGDEC_ARGB && stream->info.colorSpace == CELL_PNGDEC_RGBA) png_set_swap_alpha(stream->png_ptr); // Handle gray<->rgb colorspace conversions // rgb output if (in_param->outputColorSpace == CELL_PNGDEC_ARGB || in_param->outputColorSpace == CELL_PNGDEC_RGBA || in_param->outputColorSpace == CELL_PNGDEC_RGB) { if (stream->info.colorSpace == CELL_PNGDEC_PALETTE) png_set_palette_to_rgb(stream->png_ptr); if ((stream->info.colorSpace == CELL_PNGDEC_GRAYSCALE || stream->info.colorSpace == CELL_PNGDEC_GRAYSCALE_ALPHA) && stream->info.bitDepth < 8) png_set_expand_gray_1_2_4_to_8(stream->png_ptr); } // grayscale output else { if (stream->info.colorSpace == CELL_PNGDEC_ARGB || stream->info.colorSpace == CELL_PNGDEC_RGBA || stream->info.colorSpace == CELL_PNGDEC_RGB) { png_set_rgb_to_gray(stream->png_ptr, PNG_ERROR_ACTION_NONE, PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT); } else { // not sure what to do here cellPngDec.error("Grayscale / Palette to Grayscale / Palette conversion currently unsupported."); } } } stream->passes = png_set_interlace_handling(stream->png_ptr); // Update the info structure png_read_update_info(stream->png_ptr, stream->info_ptr); stream->out_param.outputWidth = stream->info.imageWidth; stream->out_param.outputHeight = stream->info.imageHeight; stream->out_param.outputBitDepth = in_param->outputBitDepth; stream->out_param.outputColorSpace = in_param->outputColorSpace; stream->out_param.outputMode = in_param->outputMode; stream->out_param.outputWidthByte = png_get_rowbytes(stream->png_ptr, stream->info_ptr); stream->out_param.outputComponents = png_get_channels(stream->png_ptr, stream->info_ptr); stream->packing = in_param->outputPackFlag; // Set the memory usage. We currently don't actually allocate memory for libpng through the callbacks, due to libpng needing a lot more memory compared to PS3 variant. stream->out_param.useMemorySpace = 0; if (extra_in_param) { if (extra_in_param->bufferMode != CELL_PNGDEC_LINE_MODE) { cellPngDec.error("Invalid Buffermode specified."); return CELL_PNGDEC_ERROR_ARG; } if (stream->passes > 1) { stream->outputCounts = 1; } else stream->outputCounts = extra_in_param->outputCounts; if (extra_out_param) { if (stream->outputCounts == 0) extra_out_param->outputHeight = stream->out_param.outputHeight; else extra_out_param->outputHeight = std::min(stream->outputCounts, stream->out_param.outputHeight.value()); extra_out_param->outputWidthByte = stream->out_param.outputWidthByte; } } *out_param = stream->out_param; return CELL_OK; }
bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, SkBitmap::Config prefConfig, Mode mode) { // SkAutoTrace apr("SkPNGImageDecoder::onDecode"); /* 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. */ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn, NULL); // png_voidp user_error_ptr, user_error_fn, user_warning_fn); if (png_ptr == NULL) { return false; } /* Allocate/initialize the memory for image information. */ 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 false; } PNGAutoClean autoClean(png_ptr, info_ptr); /* 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))) { return false; } /* If you are using replacement read functions, instead of calling * png_init_io() here you would call: */ png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn); /* where user_io_ptr is a structure you want available to the callbacks */ /* If we have already read some of the signature */ // png_set_sig_bytes(png_ptr, 0 /* sig_read */ ); // hookup our peeker so we can see any user-chunks the caller may be interested in png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0); if (this->getPeeker()) { png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk); } /* 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_uint_32 origWidth, origHeight; int bit_depth, color_type, interlace_type; png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL); SkBitmap::Config config; bool hasAlpha = false; bool doDither = this->getDitherImage(); // check for sBIT chunk data, in case we should disable dithering because // our data is not truely 8bits per component if (doDither) { #if 0 SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red, info_ptr->sig_bit.green, info_ptr->sig_bit.blue, info_ptr->sig_bit.alpha); #endif // 0 seems to indicate no information available if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) && pos_le(info_ptr->sig_bit.green, SK_G16_BITS) && pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) { doDither = false; } } if (color_type == PNG_COLOR_TYPE_PALETTE) { config = SkBitmap::kIndex8_Config; // defer sniffing for hasAlpha } else { png_color_16p transColor; png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &transColor); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) || PNG_COLOR_TYPE_RGB_ALPHA == color_type || PNG_COLOR_TYPE_GRAY_ALPHA == color_type) { hasAlpha = true; config = SkBitmap::kARGB_8888_Config; } else { // we get to choose the config config = prefConfig; if (config == SkBitmap::kNo_Config) { config = SkImageDecoder::GetDeviceConfig(); } if (config != SkBitmap::kRGB_565_Config && config != SkBitmap::kARGB_4444_Config) { config = SkBitmap::kARGB_8888_Config; } } } if (!this->chooseFromOneChoice(config, origWidth, origHeight)) { return false; } const int sampleSize = this->getSampleSize(); SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); decodedBitmap->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight(), 0); if (SkImageDecoder::kDecodeBounds_Mode == mode) { return true; } // from here down we are concerned with colortables and pixels /* tell libpng to strip 16 bit/color files down to 8 bits/color */ if (bit_depth == 16) { 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). */ if (bit_depth < 8) { 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_gray_1_2_4_to_8(png_ptr); } /* Make a grayscale image into RGB. */ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); } // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we // draw lots faster if we can flag the bitmap has being opaque bool reallyHasAlpha = false; SkColorTable* colorTable = NULL; if (color_type == PNG_COLOR_TYPE_PALETTE) { int num_palette; png_colorp palette; png_bytep trans; int num_trans; png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); /* BUGGY IMAGE WORKAROUND We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count which is a problem since we use the byte as an index. To work around this we grow the colortable by 1 (if its < 256) and duplicate the last color into that slot. */ int colorCount = num_palette + (num_palette < 256); colorTable = SkNEW_ARGS(SkColorTable, (colorCount)); SkPMColor* colorPtr = colorTable->lockColors(); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); hasAlpha = (num_trans > 0); } else { num_trans = 0; colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag); } // check for bad images that might make us crash if (num_trans > num_palette) { num_trans = num_palette; } int index = 0; int transLessThanFF = 0; for (; index < num_trans; index++) { transLessThanFF |= (int)*trans - 0xFF; *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->green, palette->blue); palette++; } reallyHasAlpha |= (transLessThanFF < 0); for (; index < num_palette; index++) { *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue); palette++; } // see BUGGY IMAGE WORKAROUND comment above if (num_palette < 256) { *colorPtr = colorPtr[-1]; } colorTable->unlockColors(true); } SkAutoUnref aur(colorTable); if (!this->allocPixelRef(decodedBitmap, colorTable)) { delete colorTable; return false; } SkAutoLockPixels alp(*decodedBitmap); /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ // if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) // ; // 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) */ if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) { png_set_filler(png_ptr, 0xff, 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: */ const int number_passes = interlace_type != PNG_INTERLACE_NONE ? png_set_interlace_handling(png_ptr) : 1; /* 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); if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) { for (int i = 0; i < number_passes; i++) { for (png_uint_32 y = 0; y < origHeight; y++) { uint8_t* bmRow = decodedBitmap->getAddr8(0, y); png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); } } } else { SkScaledBitmapSampler::SrcConfig sc; int srcBytesPerPixel = 4; if (SkBitmap::kIndex8_Config == config) { sc = SkScaledBitmapSampler::kIndex; srcBytesPerPixel = 1; } else if (hasAlpha) { sc = SkScaledBitmapSampler::kRGBA; } else { sc = SkScaledBitmapSampler::kRGBX; } SkAutoMalloc storage(origWidth * srcBytesPerPixel); const int height = decodedBitmap->height(); for (int i = 0; i < number_passes; i++) { if (!sampler.begin(decodedBitmap, sc, doDither)) { return false; } uint8_t* srcRow = (uint8_t*)storage.get(); skip_src_rows(png_ptr, srcRow, sampler.srcY0()); for (int y = 0; y < height; y++) { uint8_t* tmp = srcRow; png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1); reallyHasAlpha |= sampler.next(srcRow); if (y < height - 1) { skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1); } } // skip the rest of the rows (if any) png_uint_32 read = (height - 1) * sampler.srcDY() + sampler.srcY0() + 1; SkASSERT(read <= origHeight); skip_src_rows(png_ptr, srcRow, origHeight - read); } if (hasAlpha && !reallyHasAlpha) { SkDEBUGF(("Image doesn't really have alpha [%d %d]\n", origWidth, origHeight)); } } /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ png_read_end(png_ptr, info_ptr); decodedBitmap->setIsOpaque(!reallyHasAlpha); return true; }
int readpng2_init(mainprog_info *mainprog_ptr) { png_structp png_ptr; /* note: temporary variables! */ png_infop info_ptr; /* could also replace libpng warning-handler (final NULL), but no need: */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr, readpng2_error_handler, NULL); if (!png_ptr) return 4; /* out of memory */ info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, NULL, NULL); return 4; /* out of memory */ } /* we could create a second info struct here (end_info), but it's only * useful if we want to keep pre- and post-IDAT chunk info separated * (mainly for PNG-aware image editors and converters) */ /* setjmp() must be called in every function that calls a PNG-reading * libpng function, unless an alternate error handler was installed-- * but compatible error handlers must either use longjmp() themselves * (as in this program) or exit immediately, so here we are: */ if (setjmp(mainprog_ptr->jmpbuf)) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return 2; } #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED /* prepare the reader to ignore all recognized chunks whose data won't be * used, i.e., all chunks recognized by libpng except for IHDR, PLTE, IDAT, * IEND, tRNS, bKGD, gAMA, and sRGB (small performance improvement) */ { /* These byte strings were copied from png.h. If a future libpng * version recognizes more chunks, add them to this list. If a * future version of readpng2.c recognizes more chunks, delete them * from this list. */ static /* const */ png_byte chunks_to_ignore[] = { 99, 72, 82, 77, '\0', /* cHRM */ 104, 73, 83, 84, '\0', /* hIST */ 105, 67, 67, 80, '\0', /* iCCP */ 105, 84, 88, 116, '\0', /* iTXt */ 111, 70, 70, 115, '\0', /* oFFs */ 112, 67, 65, 76, '\0', /* pCAL */ 112, 72, 89, 115, '\0', /* pHYs */ 115, 66, 73, 84, '\0', /* sBIT */ 115, 67, 65, 76, '\0', /* sCAL */ 115, 80, 76, 84, '\0', /* sPLT */ 115, 84, 69, 82, '\0', /* sTER */ 116, 69, 88, 116, '\0', /* tEXt */ 116, 73, 77, 69, '\0', /* tIME */ 122, 84, 88, 116, '\0' /* zTXt */ }; png_set_keep_unknown_chunks(png_ptr, 1 /* PNG_HANDLE_CHUNK_NEVER */, chunks_to_ignore, sizeof(chunks_to_ignore)/5); } #endif /* PNG_HANDLE_AS_UNKNOWN_SUPPORTED */ /* instead of doing png_init_io() here, now we set up our callback * functions for progressive decoding */ png_set_progressive_read_fn(png_ptr, mainprog_ptr, readpng2_info_callback, readpng2_row_callback, readpng2_end_callback); /* make sure we save our pointers for use in readpng2_decode_data() */ mainprog_ptr->png_ptr = png_ptr; mainprog_ptr->info_ptr = info_ptr; /* and that's all there is to initialization */ return 0; }
bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, Mode mode) { // SkAutoTrace apr("SkPNGImageDecoder::onDecode"); /* 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. */ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn, NULL); // png_voidp user_error_ptr, user_error_fn, user_warning_fn); if (png_ptr == NULL) { return false; } /* Allocate/initialize the memory for image information. */ png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr, NULL, NULL); return false; } PNGAutoClean autoClean(png_ptr, info_ptr); /* 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))) { return false; } /* If you are using replacement read functions, instead of calling * png_init_io() here you would call: */ png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn); /* where user_io_ptr is a structure you want available to the callbacks */ /* If we have already read some of the signature */ // png_set_sig_bytes(png_ptr, 0 /* sig_read */ ); // hookup our peeker so we can see any user-chunks the caller may be interested in png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0); if (this->getPeeker()) { png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk); } /* 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_uint_32 origWidth, origHeight; int bit_depth, color_type, interlace_type; png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, &color_type, &interlace_type, NULL, NULL); /* tell libpng to strip 16 bit/color files down to 8 bits/color */ if (bit_depth == 16) { 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). */ if (bit_depth < 8) { 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_gray_1_2_4_to_8(png_ptr); } /* Make a grayscale image into RGB. */ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); } SkBitmap::Config config; bool hasAlpha = false; bool doDither = this->getDitherImage(); SkPMColor theTranspColor = 0; // 0 tells us not to try to match // check for sBIT chunk data, in case we should disable dithering because // our data is not truely 8bits per component if (doDither) { png_color_8p sig_bit = NULL; bool has_sbit = PNG_INFO_sBIT == png_get_sBIT(png_ptr, info_ptr, &sig_bit); #if 0 if (has_sbit) { SkDebugf("----- sBIT %d %d %d %d\n", sig_bit->red, sig_bit->green, sig_bit->blue, sig_bit->alpha); } #endif // 0 seems to indicate no information available if (has_sbit && pos_le(sig_bit->red, SK_R16_BITS) && pos_le(sig_bit->green, SK_G16_BITS) && pos_le(sig_bit->blue, SK_B16_BITS)) { doDither = false; } } if (color_type == PNG_COLOR_TYPE_PALETTE) { bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr); config = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha); // now see if we can upscale to their requested config if (!canUpscalePaletteToConfig(config, paletteHasAlpha)) { config = SkBitmap::kIndex8_Config; } } else { png_color_16p transpColor = NULL; int numTransp = 0; png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor); bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS); if (valid && numTransp == 1 && transpColor != NULL) { /* Compute our transparent color, which we'll match against later. We don't really handle 16bit components properly here, since we do our compare *after* the values have been knocked down to 8bit which means we will find more matches than we should. The real fix seems to be to see the actual 16bit components, do the compare, and then knock it down to 8bits ourselves. */ if (color_type & PNG_COLOR_MASK_COLOR) { if (16 == bit_depth) { theTranspColor = SkPackARGB32(0xFF, transpColor->red >> 8, transpColor->green >> 8, transpColor->blue >> 8); } else { theTranspColor = SkPackARGB32(0xFF, transpColor->red, transpColor->green, transpColor->blue); } } else { // gray if (16 == bit_depth) {
/* Test one file */ int test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) { static png_FILE_p fpin; static png_FILE_p fpout; /* "static" prevents setjmp corruption */ png_structp read_ptr; png_infop read_info_ptr, end_info_ptr; #ifdef PNG_WRITE_SUPPORTED png_structp write_ptr; png_infop write_info_ptr; png_infop write_end_info_ptr; #else png_structp write_ptr = NULL; png_infop write_info_ptr = NULL; png_infop write_end_info_ptr = NULL; #endif png_bytep row_buf; png_uint_32 y; png_uint_32 width, height; int num_pass, pass; int bit_depth, color_type; #ifdef PNG_SETJMP_SUPPORTED #ifdef USE_FAR_KEYWORD jmp_buf jmpbuf; #endif #endif #if defined(_WIN32_WCE) TCHAR path[MAX_PATH]; #endif char inbuf[256], outbuf[256]; row_buf = NULL; #if defined(_WIN32_WCE) MultiByteToWideChar(CP_ACP, 0, inname, -1, path, MAX_PATH); if ((fpin = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) #else if ((fpin = fopen(inname, "rb")) == NULL) #endif { fprintf(STDERR, "Could not find input file %s\n", inname); return (1); } #if defined(_WIN32_WCE) MultiByteToWideChar(CP_ACP, 0, outname, -1, path, MAX_PATH); if ((fpout = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL)) == INVALID_HANDLE_VALUE) #else if ((fpout = fopen(outname, "wb")) == NULL) #endif { fprintf(STDERR, "Could not open output file %s\n", outname); FCLOSE(fpin); return (1); } png_debug(0, "Allocating read and write structures\n"); #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG read_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, png_voidp_NULL, png_error_ptr_NULL, png_error_ptr_NULL, png_voidp_NULL, (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); #else read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, png_voidp_NULL, png_error_ptr_NULL, png_error_ptr_NULL); #endif #if defined(PNG_NO_STDIO) png_set_error_fn(read_ptr, (png_voidp)inname, pngtest_error, pngtest_warning); #endif #ifdef PNG_WRITE_SUPPORTED #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG write_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, png_voidp_NULL, png_error_ptr_NULL, png_error_ptr_NULL, png_voidp_NULL, (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); #else write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, png_voidp_NULL, png_error_ptr_NULL, png_error_ptr_NULL); #endif #if defined(PNG_NO_STDIO) png_set_error_fn(write_ptr, (png_voidp)inname, pngtest_error, pngtest_warning); #endif #endif png_debug(0, "Allocating read_info, write_info and end_info structures\n"); read_info_ptr = png_create_info_struct(read_ptr); end_info_ptr = png_create_info_struct(read_ptr); #ifdef PNG_WRITE_SUPPORTED write_info_ptr = png_create_info_struct(write_ptr); write_end_info_ptr = png_create_info_struct(write_ptr); #endif #ifdef PNG_SETJMP_SUPPORTED png_debug(0, "Setting jmpbuf for read struct\n"); #ifdef USE_FAR_KEYWORD if (setjmp(jmpbuf)) #else if (setjmp(png_jmpbuf(read_ptr))) #endif { fprintf(STDERR, "%s -> %s: libpng read error\n", inname, outname); if (row_buf) png_free(read_ptr, row_buf); png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); #ifdef PNG_WRITE_SUPPORTED png_destroy_info_struct(write_ptr, &write_end_info_ptr); png_destroy_write_struct(&write_ptr, &write_info_ptr); #endif FCLOSE(fpin); FCLOSE(fpout); return (1); } #ifdef USE_FAR_KEYWORD png_memcpy(png_jmpbuf(read_ptr),jmpbuf,png_sizeof(jmp_buf)); #endif #ifdef PNG_WRITE_SUPPORTED png_debug(0, "Setting jmpbuf for write struct\n"); #ifdef USE_FAR_KEYWORD if (setjmp(jmpbuf)) #else if (setjmp(png_jmpbuf(write_ptr))) #endif { fprintf(STDERR, "%s -> %s: libpng write error\n", inname, outname); png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); png_destroy_info_struct(write_ptr, &write_end_info_ptr); #ifdef PNG_WRITE_SUPPORTED png_destroy_write_struct(&write_ptr, &write_info_ptr); #endif FCLOSE(fpin); FCLOSE(fpout); return (1); } #ifdef USE_FAR_KEYWORD png_memcpy(png_jmpbuf(write_ptr),jmpbuf,png_sizeof(jmp_buf)); #endif #endif #endif png_debug(0, "Initializing input and output streams\n"); #if !defined(PNG_NO_STDIO) png_init_io(read_ptr, fpin); # ifdef PNG_WRITE_SUPPORTED png_init_io(write_ptr, fpout); # endif #else png_set_read_fn(read_ptr, (png_voidp)fpin, pngtest_read_data); # ifdef PNG_WRITE_SUPPORTED png_set_write_fn(write_ptr, (png_voidp)fpout, pngtest_write_data, # if defined(PNG_WRITE_FLUSH_SUPPORTED) pngtest_flush); # else NULL); # endif # endif #endif if(status_dots_requested == 1) { #ifdef PNG_WRITE_SUPPORTED png_set_write_status_fn(write_ptr, write_row_callback); #endif png_set_read_status_fn(read_ptr, read_row_callback); } else { #ifdef PNG_WRITE_SUPPORTED png_set_write_status_fn(write_ptr, png_write_status_ptr_NULL); #endif png_set_read_status_fn(read_ptr, png_read_status_ptr_NULL); } #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) { int i; for(i=0; i<256; i++) filters_used[i]=0; png_set_read_user_transform_fn(read_ptr, count_filters); } #endif #if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) zero_samples=0; png_set_write_user_transform_fn(write_ptr, count_zero_samples); #endif #if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) # ifndef PNG_HANDLE_CHUNK_ALWAYS # define PNG_HANDLE_CHUNK_ALWAYS 3 # endif png_set_keep_unknown_chunks(read_ptr, PNG_HANDLE_CHUNK_ALWAYS, png_bytep_NULL, 0); #endif #if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) # ifndef PNG_HANDLE_CHUNK_IF_SAFE # define PNG_HANDLE_CHUNK_IF_SAFE 2 # endif png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_IF_SAFE, png_bytep_NULL, 0); #endif png_debug(0, "Reading info struct\n"); png_read_info(read_ptr, read_info_ptr); png_debug(0, "Transferring info struct\n"); { int interlace_type, compression_type, filter_type; if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type)) { png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth, #if defined(PNG_WRITE_INTERLACING_SUPPORTED) color_type, interlace_type, compression_type, filter_type); #else color_type, PNG_INTERLACE_NONE, compression_type, filter_type); #endif } }
int readpng2_init(mainprog_info *mainprog_ptr) { png_structp png_ptr; /* note: temporary variables! */ png_infop info_ptr; /* could also replace libpng warning-handler (final NULL), but no need: */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr, readpng2_error_handler, NULL); if (!png_ptr) return 4; /* out of memory */ info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, NULL, NULL); return 4; /* out of memory */ } /* we could create a second info struct here (end_info), but it's only * useful if we want to keep pre- and post-IDAT chunk info separated * (mainly for PNG-aware image editors and converters) */ /* setjmp() must be called in every function that calls a PNG-reading * libpng function, unless an alternate error handler was installed-- * but compatible error handlers must either use longjmp() themselves * (as in this program) or exit immediately, so here we are: */ if (setjmp(mainprog_ptr->jmpbuf)) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return 2; } #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED /* prepare the reader to ignore all recognized chunks whose data won't be * used, i.e., all chunks recognized by libpng except for IHDR, PLTE, IDAT, * IEND, tRNS, bKGD, gAMA, and sRGB (small performance improvement) */ { /* These byte strings were copied from png.h. If a future libpng * version recognizes more chunks, add them to this list. If a * future version of readpng2.c recognizes more chunks, delete them * from this list. */ static const png_byte chunks_to_ignore[] = { 99, 72, 82, 77, '\0', /* cHRM */ 104, 73, 83, 84, '\0', /* hIST */ 105, 67, 67, 80, '\0', /* iCCP */ 105, 84, 88, 116, '\0', /* iTXt */ 111, 70, 70, 115, '\0', /* oFFs */ 112, 67, 65, 76, '\0', /* pCAL */ 112, 72, 89, 115, '\0', /* pHYs */ 115, 66, 73, 84, '\0', /* sBIT */ 115, 67, 65, 76, '\0', /* sCAL */ 115, 80, 76, 84, '\0', /* sPLT */ 115, 84, 69, 82, '\0', /* sTER */ 116, 69, 88, 116, '\0', /* tEXt */ 116, 73, 77, 69, '\0', /* tIME */ 122, 84, 88, 116, '\0' /* zTXt */ }; png_set_keep_unknown_chunks(png_ptr, 1 /* PNG_HANDLE_CHUNK_NEVER */, chunks_to_ignore, sizeof(chunks_to_ignore)/5); } #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */ /* instead of doing png_init_io() here, now we set up our callback * functions for progressive decoding */ png_set_progressive_read_fn(png_ptr, mainprog_ptr, readpng2_info_callback, readpng2_row_callback, readpng2_end_callback); /* * may as well enable or disable MMX routines here, if supported; * * to enable all: mask = png_get_mmx_flagmask ( * PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID); * flags = png_get_asm_flags (png_ptr); * flags |= mask; * png_set_asm_flags (png_ptr, flags); * * to disable all: mask = png_get_mmx_flagmask ( * PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID); * flags = png_get_asm_flags (png_ptr); * flags &= ~mask; * png_set_asm_flags (png_ptr, flags); */ #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) && \ defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) /* * WARNING: This preprocessor approach means that the following code * cannot be used with a libpng DLL older than 1.2.0--the * compiled-in symbols for the new functions will not exist. * (Could use dlopen() and dlsym() on Unix and corresponding * calls for Windows, but not portable...) */ { #ifdef PNG_ASSEMBLER_CODE_SUPPORTED png_uint_32 mmx_disable_mask = 0; png_uint_32 asm_flags, mmx_mask; int compilerID; if (mainprog_ptr->nommxfilters) mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ); if (mainprog_ptr->nommxcombine) mmx_disable_mask |= PNG_ASM_FLAG_MMX_READ_COMBINE_ROW; if (mainprog_ptr->nommxinterlace) mmx_disable_mask |= PNG_ASM_FLAG_MMX_READ_INTERLACE; asm_flags = png_get_asm_flags(png_ptr); png_set_asm_flags(png_ptr, asm_flags & ~mmx_disable_mask); /* Now query libpng's asm settings, just for yuks. Note that this * differs from the querying of its *potential* MMX capabilities * in readpng2_version_info(); this is true runtime verification. */ asm_flags = png_get_asm_flags(png_ptr); mmx_mask = png_get_mmx_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID); if (asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_COMPILED) fprintf(stderr, " MMX support (%s version) is compiled into libpng\n", compilerID == 1? "MSVC++" : (compilerID == 2? "GNU C" : "unknown")); else fprintf(stderr, " MMX support is not compiled into libpng\n"); fprintf(stderr, " MMX instructions are %ssupported by CPU\n", (asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU)? "" : "not "); fprintf(stderr, " MMX read support for combining rows is %sabled\n", (asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)? "en" : "dis"); fprintf(stderr, " MMX read support for expanding interlacing is %sabled\n", (asm_flags & PNG_ASM_FLAG_MMX_READ_INTERLACE)? "en" : "dis"); fprintf(stderr, " MMX read support for \"sub\" filter is %sabled\n", (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)? "en" : "dis"); fprintf(stderr, " MMX read support for \"up\" filter is %sabled\n", (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP)? "en" : "dis"); fprintf(stderr, " MMX read support for \"avg\" filter is %sabled\n", (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)? "en" : "dis"); fprintf(stderr, " MMX read support for \"Paeth\" filter is %sabled\n", (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)? "en" : "dis"); asm_flags &= (mmx_mask & ~( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ | PNG_ASM_FLAG_MMX_READ_INTERLACE \ | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH )); if (asm_flags) fprintf(stderr, " additional MMX support is also enabled (0x%02lx)\n", asm_flags); #else /* !PNG_ASSEMBLER_CODE_SUPPORTED */ fprintf(stderr, " MMX querying is disabled in libpng.\n"); #endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ } #endif /* make sure we save our pointers for use in readpng2_decode_data() */ mainprog_ptr->png_ptr = png_ptr; mainprog_ptr->info_ptr = info_ptr; /* and that's all there is to initialization */ return 0; }
DEF_TEST(Codec_pngChunkReader, r) { // Create a dummy bitmap. Use unpremul RGBA for libpng. SkBitmap bm; const int w = 1; const int h = 1; const SkImageInfo bmInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType); bm.setInfo(bmInfo); bm.allocPixels(); bm.eraseColor(SK_ColorBLUE); SkMD5::Digest goodDigest; md5(bm, &goodDigest); // Write to a png file. png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); REPORTER_ASSERT(r, png); if (!png) { return; } png_infop info = png_create_info_struct(png); REPORTER_ASSERT(r, info); if (!info) { png_destroy_write_struct(&png, nullptr); return; } if (setjmp(png_jmpbuf(png))) { ERRORF(r, "failed writing png"); png_destroy_write_struct(&png, &info); return; } SkDynamicMemoryWStream wStream; png_set_write_fn(png, (void*) (&wStream), codex_test_write_fn, nullptr); png_set_IHDR(png, info, (png_uint_32)w, (png_uint_32)h, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // Create some chunks that match the Android framework's use. static png_unknown_chunk gUnknowns[] = { { "npOl", (png_byte*)"outline", sizeof("outline"), PNG_HAVE_IHDR }, { "npLb", (png_byte*)"layoutBounds", sizeof("layoutBounds"), PNG_HAVE_IHDR }, { "npTc", (png_byte*)"ninePatchData", sizeof("ninePatchData"), PNG_HAVE_IHDR }, }; png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"npOl\0npLb\0npTc\0", 3); png_set_unknown_chunks(png, info, gUnknowns, SK_ARRAY_COUNT(gUnknowns)); #if PNG_LIBPNG_VER < 10600 /* Deal with unknown chunk location bug in 1.5.x and earlier */ png_set_unknown_chunk_location(png, info, 0, PNG_HAVE_IHDR); png_set_unknown_chunk_location(png, info, 1, PNG_HAVE_IHDR); #endif png_write_info(png, info); for (int j = 0; j < h; j++) { png_bytep row = (png_bytep)(bm.getAddr(0, j)); png_write_rows(png, &row, 1); } png_write_end(png, info); png_destroy_write_struct(&png, &info); class ChunkReader : public SkPngChunkReader { public: ChunkReader(skiatest::Reporter* r) : fReporter(r) { this->reset(); } bool readChunk(const char tag[], const void* data, size_t length) override { for (size_t i = 0; i < SK_ARRAY_COUNT(gUnknowns); ++i) { if (!strcmp(tag, (const char*) gUnknowns[i].name)) { // Tag matches. This should have been the first time we see it. REPORTER_ASSERT(fReporter, !fSeen[i]); fSeen[i] = true; // Data and length should match REPORTER_ASSERT(fReporter, length == gUnknowns[i].size); REPORTER_ASSERT(fReporter, !strcmp((const char*) data, (const char*) gUnknowns[i].data)); return true; } } ERRORF(fReporter, "Saw an unexpected unknown chunk."); return true; } bool allHaveBeenSeen() { bool ret = true; for (auto seen : fSeen) { ret &= seen; } return ret; } void reset() { sk_bzero(fSeen, sizeof(fSeen)); } private: skiatest::Reporter* fReporter; // Unowned bool fSeen[3]; }; ChunkReader chunkReader(r); // Now read the file with SkCodec. SkAutoTUnref<SkData> data(wStream.copyToData()); SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(data, &chunkReader)); REPORTER_ASSERT(r, codec); if (!codec) { return; } // Now compare to the original. SkBitmap decodedBm; decodedBm.setInfo(codec->getInfo()); decodedBm.allocPixels(); SkCodec::Result result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), decodedBm.rowBytes()); REPORTER_ASSERT(r, SkCodec::kSuccess == result); if (decodedBm.colorType() != bm.colorType()) { SkBitmap tmp; bool success = decodedBm.copyTo(&tmp, bm.colorType()); REPORTER_ASSERT(r, success); if (!success) { return; } tmp.swap(decodedBm); } compare_to_good_digest(r, goodDigest, decodedBm); REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen()); // Decoding again will read the chunks again. chunkReader.reset(); REPORTER_ASSERT(r, !chunkReader.allHaveBeenSeen()); result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), decodedBm.rowBytes()); REPORTER_ASSERT(r, SkCodec::kSuccess == result); REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen()); }
static uint8_t png_read( png_structp readPtr, png_image * image, uint32_t flags ) { // png_set_error_fn( readPtr, NULL, png_user_error, NULL ); png_infop infoPtr = png_create_info_struct( readPtr ); if (!infoPtr) { pngio_error( "Couldn't initialize PNG info struct." ); png_destroy_read_struct( & readPtr, NULL, NULL ); return 0; } if (setjmp( png_jmpbuf( readPtr ) )) { pngio_error( "An error occured while reading the PNG file." ); png_destroy_read_struct( & readPtr, & infoPtr, NULL ); png_image_free( image ); return 0; } png_set_sig_bytes( readPtr, 8 ); #ifdef PNG_APPLE_MODE_SUPPORTED if (png_get_apple_mode()) { png_set_keep_unknown_chunks( readPtr, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0 ); png_set_read_user_chunk_fn( readPtr, NULL, png_read_user_chunk ); } #endif png_read_info( readPtr, infoPtr ); png_uint_32 w = png_get_image_width( readPtr, infoPtr ); png_uint_32 h = png_get_image_height( readPtr, infoPtr ); png_uint_32 bitDepth = png_get_bit_depth( readPtr, infoPtr ); png_uint_32 channels = png_get_channels( readPtr, infoPtr ); png_uint_32 interlaceType = png_get_interlace_type( readPtr, infoPtr ); png_uint_32 colorType = png_get_color_type( readPtr, infoPtr ); switch (colorType) { case PNG_COLOR_TYPE_PALETTE: png_set_palette_to_rgb( readPtr ); channels = 3; break; case PNG_COLOR_TYPE_GRAY: if (bitDepth < 8) { png_set_expand_gray_1_2_4_to_8( readPtr ); bitDepth = 8; } png_set_gray_to_rgb( readPtr ); break; } if (png_get_valid( readPtr, infoPtr, PNG_INFO_tRNS )) { png_set_tRNS_to_alpha( readPtr ); channels += 1; } else if (!(colorType & PNG_COLOR_MASK_ALPHA)) { png_set_add_alpha( readPtr, 0xff, PNG_FILLER_AFTER ); } if (bitDepth == 16) { png_set_strip_16( readPtr ); } #ifdef PNG_APPLE_MODE_SUPPORTED if (png_get_apple_mode()) { if (flags & PNG_IMAGE_PREMULTIPLY_ALPHA) { png_set_read_user_transform_fn( readPtr, png_read_swap_transform ); } else { png_set_read_user_transform_fn( readPtr, png_read_swap_and_unpremultiply_transform ); } png_set_user_transform_info( readPtr, NULL, bitDepth, channels ); png_read_update_info( readPtr, infoPtr ); } else #endif { if (flags & PNG_IMAGE_PREMULTIPLY_ALPHA) { png_set_read_user_transform_fn( readPtr, png_read_premultiply_transform ); } } png_image_alloc( image, w, h ); png_bytep p = image->data; const size_t passCount = interlaceType == PNG_INTERLACE_NONE ? 1 : png_set_interlace_handling( readPtr ); const size_t bytesPerRow = w * 4; if (flags & PNG_IMAGE_FLIP_VERTICAL) { for (size_t pass = 0; pass < passCount; pass++) { for (size_t i = 0; i < h; i++) { png_read_row( readPtr, p + (bytesPerRow * (h - i - 1)), NULL ); } } } else { // png_bytep rp[h]; // for (size_t i = 0; i < h; i++) // { // rp[i] = p + (bytesPerRow * i); // } // png_read_image( readPtr, rp ); for (size_t pass = 0; pass < passCount; pass++) { for (size_t i = 0; i < h; i++) { png_read_row( readPtr, p + (bytesPerRow * i), NULL ); } } } png_destroy_read_struct( & readPtr, & infoPtr, NULL ); return 1; }