void nsPNGDecoder::end_callback(png_structp png_ptr, png_infop info_ptr) { /* libpng comments: * * this function is called when the whole image has been read, * including any chunks after the image (up to and including * the IEND). You will usually have the same info chunk as you * had in the header, although some data may have been added * to the comments and time fields. * * Most people won't do much here, perhaps setting a flag that * marks the image as finished. */ nsPNGDecoder* decoder = static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr)); // We shouldn't get here if we've hit an error MOZ_ASSERT(!decoder->HasError(), "Finishing up PNG but hit error!"); int32_t loop_count = 0; #ifdef PNG_APNG_SUPPORTED if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) { int32_t num_plays = png_get_num_plays(png_ptr, info_ptr); loop_count = num_plays - 1; } #endif // Send final notifications decoder->EndImageFrame(); decoder->PostDecodeDone(loop_count); }
// got the header of a new frame that's coming void nsPNGDecoder::frame_info_callback(png_structp png_ptr, png_uint_32 frame_num) { png_uint_32 x_offset, y_offset; int32_t width, height; nsPNGDecoder* decoder = static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr)); // old frame is done decoder->EndImageFrame(); // Only the first frame can be hidden, so unhide unconditionally here. decoder->mFrameIsHidden = false; x_offset = png_get_next_frame_x_offset(png_ptr, decoder->mInfo); y_offset = png_get_next_frame_y_offset(png_ptr, decoder->mInfo); width = png_get_next_frame_width(png_ptr, decoder->mInfo); height = png_get_next_frame_height(png_ptr, decoder->mInfo); decoder->CreateFrame(x_offset, y_offset, width, height, decoder->format); if (decoder->NeedsNewFrame()) { // We know that we need a new frame, so pause input so the decoder // infrastructure can give it to us. png_process_data_pause(png_ptr, /* save = */ 1); } }
static void read_data_fn(png_structp png_ptr, png_bytep data, png_size_t length) { struct png_t* png = (struct png_t*)png_get_progressive_ptr(png_ptr); if(png->pos + length > png->size) png_error(png_ptr, "Read Error"); memcpy(data, &png->data[png->pos], length); png->pos += length; }
void PngReader::end_callback( png_structp png, png_infop info ) { PngReader* reader = ( PngReader* )png_get_progressive_ptr( png ); reader->m_finished = true; }
/* Called after reading the entire image */ static void png_end_callback (png_structp png_read_ptr, png_infop png_info_ptr) { LoadContext* lc; lc = png_get_progressive_ptr(png_read_ptr); if (lc->fatal_error_occurred) return; }
void pngDecEndCallback(png_structp png_ptr, png_infop info) { PngStream* stream = (PngStream*)png_get_progressive_ptr(png_ptr); if (!stream) { cellPngDec.error("Failed to obtain streamPtr in endCallback."); return; } stream->endOfFile = true; }
void pngDecInfoCallback(png_structp png_ptr, png_infop info) { PngStream* stream = (PngStream*)png_get_progressive_ptr(png_ptr); if (!stream) { cellPngDec.error("Failed to obtain streamPtr in rowCallback."); return; } const size_t remaining = png_process_data_pause(png_ptr, false); stream->buffer->cursor += (stream->buffer->length - remaining); }
static void row_callback(png_structp png_s, png_bytep new_row, png_uint_32 row_num, int pass) { nspng_content *png_c = png_get_progressive_ptr(png_s); unsigned long rowbytes = png_c->rowbytes; unsigned char *buffer, *row; /* Give up if there's no bitmap */ if (png_c->bitmap == NULL) return; /* Abort if we've not got any data */ if (new_row == NULL) return; /* Get bitmap buffer */ buffer = bitmap_get_buffer(png_c->bitmap); if (buffer == NULL) { /* No buffer, bail out */ longjmp(png_jmpbuf(png_s), 1); } /* Calculate address of row start */ row = buffer + (png_c->rowstride * row_num); /* Handle interlaced sprites using the Adam7 algorithm */ if (png_c->interlace) { unsigned long dst_off; unsigned long src_off = 0; unsigned int start, step; start = interlace_start[pass]; step = interlace_step[pass]; row_num = interlace_row_start[pass] + interlace_row_step[pass] * row_num; /* Copy the data to our current row taking interlacing * into consideration */ row = buffer + (png_c->rowstride * row_num); for (dst_off = start; dst_off < rowbytes; dst_off += step) { row[dst_off++] = new_row[src_off++]; row[dst_off++] = new_row[src_off++]; row[dst_off++] = new_row[src_off++]; row[dst_off++] = new_row[src_off++]; } } else { /* Do a fast memcpy of the row data */ memcpy(row, new_row, rowbytes); } }
void PngReader::row_callback( png_structp png, png_bytep row, png_uint_32 rowpos, int pass ) { if( !row ) { return; } PngReader* reader = ( PngReader* )png_get_progressive_ptr( png ); png_progressive_combine_row( png, reader->m_data->m_data + reader->m_stride * ( reader->m_height - rowpos - 1 ), row ); }
static void _png_get_row_func(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) { FXPNG_Context* p = (FXPNG_Context*)png_get_progressive_ptr(png_ptr); if (!p) return; CCodec_PngModule* pModule = (CCodec_PngModule*)p->parent_ptr; uint8_t* src_buf = nullptr; if (!pModule->AskScanlineBufCallback(p->child_ptr, row_num, src_buf)) { png_error(png_ptr, "Ask Scanline buffer Callback Error"); } if (src_buf) { png_progressive_combine_row(png_ptr, src_buf, new_row); } pModule->FillScanlineBufCompletedCallback(p->child_ptr, pass, row_num); }
bool decode(const SharedBuffer& data, bool sizeOnly) { m_decodingSizeOnly = sizeOnly; PNGImageDecoder* decoder = static_cast<PNGImageDecoder*>(png_get_progressive_ptr(m_png)); // We need to do the setjmp here. Otherwise bad things will happen. if (setjmp(JMPBUF(m_png))) return decoder->setFailed(); const char* segment; while (unsigned segmentLength = data.getSomeData(segment, m_readOffset)) { m_readOffset += segmentLength; m_currentBufferSize = m_readOffset; png_process_data(m_png, m_info, reinterpret_cast<png_bytep>(const_cast<char*>(segment)), segmentLength); // We explicitly specify the superclass isSizeAvailable() because we // merely want to check if we've managed to set the size, not // (recursively) trigger additional decoding if we haven't. if (sizeOnly ? decoder->ImageDecoder::isSizeAvailable() : decoder->isComplete()) return true; } return false; }
/* Called for each row; note that you will get duplicate row numbers for interlaced PNGs */ static void png_row_callback (png_structp png_read_ptr, png_bytep new_row, png_uint_32 row_num, int pass_num) { LoadContext* lc; guchar* old_row = NULL; lc = png_get_progressive_ptr(png_read_ptr); if (lc->fatal_error_occurred) return; if (row_num >= lc->pixbuf->height) { lc->fatal_error_occurred = TRUE; if (lc->error && *lc->error == NULL) { g_set_error_literal (lc->error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Fatal error reading PNG image file")); } return; } if (lc->first_row_seen_in_chunk < 0) { lc->first_row_seen_in_chunk = row_num; lc->first_pass_seen_in_chunk = pass_num; } lc->max_row_seen_in_chunk = MAX(lc->max_row_seen_in_chunk, ((gint)row_num)); lc->last_row_seen_in_chunk = row_num; lc->last_pass_seen_in_chunk = pass_num; old_row = lc->pixbuf->pixels + (row_num * lc->pixbuf->rowstride); png_progressive_combine_row(lc->png_read_ptr, old_row, new_row); }
void PngReader::info_callback( png_structp png, png_infop info ) { PngReader* reader = ( PngReader* )png_get_progressive_ptr( png ); int bitdepth; int colortype; png_get_IHDR( png, info, ( png_uint_32* )&reader->m_width, ( png_uint_32* )&reader->m_height, &bitdepth, &colortype, 0, 0, 0 ); formattable[ reader->m_format ].setformat( png, info, bitdepth, colortype ); png_set_interlace_handling( png ); png_read_update_info( png, info ); reader->m_stride = reader->m_width * formattable[ reader->m_format ].pixelstride; size_t size = reader->m_stride * reader->m_height; reader->m_data = DataBuffer::create( size, size, 0 ); memset( reader->m_data->m_data, 0, size ); }
void pngDecRowCallback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) { PngStream* stream = (PngStream*)png_get_progressive_ptr(png_ptr); if (!stream) { cellPngDec.error("Failed to obtain streamPtr in rowCallback."); return; } // we have to check this everytime as this func can be called multiple times per row, and/or only once per row if (stream->nextRow + stream->outputCounts == row_num) stream->nextRow = row_num; if (stream->ppuContext && (stream->nextRow == row_num || pass > 0)) { if (pass > 0 ) { stream->cbDispInfo->scanPassCount = pass; stream->cbDispInfo->nextOutputStartY = row_num; } else { stream->cbDispInfo->scanPassCount = 0; stream->cbDispInfo->nextOutputStartY = 0; } stream->cbDispInfo->outputImage = stream->cbDispParam->nextOutputImage; stream->cbCtrlDisp.cbCtrlDispFunc(*stream->ppuContext, stream->cbDispInfo, stream->cbDispParam, stream->cbCtrlDisp.cbCtrlDispArg); stream->cbDispInfo->outputStartY = row_num; } u8* data; if (pass > 0) data = static_cast<u8*>(stream->cbDispParam->nextOutputImage.get_ptr()); else data = static_cast<u8*>(stream->cbDispParam->nextOutputImage.get_ptr()) + ((row_num - stream->cbDispInfo->outputStartY) * stream->cbDispInfo->outputFrameWidthByte); png_progressive_combine_row(png_ptr, data, new_row); }
/** * info_callback -- PNG header has been completely received, prepare to process * image data */ static void info_callback(png_structp png_s, png_infop info) { int interlace; png_uint_32 width, height; nspng_content *png_c = png_get_progressive_ptr(png_s); width = png_get_image_width(png_s, info); height = png_get_image_height(png_s, info); interlace = png_get_interlace_type(png_s, info); png_c->base.width = width; png_c->base.height = height; png_c->base.size += width * height * 4; /* see if progressive-conversion should continue */ if (image_cache_speculate((struct content *)png_c) == false) { longjmp(png_jmpbuf(png_s), CBERR_NOPRE); } /* Claim the required memory for the converted PNG */ png_c->bitmap = bitmap_create(width, height, BITMAP_NEW); if (png_c->bitmap == NULL) { /* Failed to create bitmap skip pre-conversion */ longjmp(png_jmpbuf(png_s), CBERR_NOPRE); } png_c->rowstride = bitmap_get_rowstride(png_c->bitmap); png_c->bpp = bitmap_get_bpp(png_c->bitmap); nspng_setup_transforms(png_s, info); png_c->rowbytes = png_get_rowbytes(png_s, info); png_c->interlace = (interlace == PNG_INTERLACE_ADAM7); LOG(("size %li * %li, rowbytes %zu", (unsigned long)width, (unsigned long)height, png_c->rowbytes)); }
// got the header of a new frame that's coming void nsPNGDecoder::frame_info_callback(png_structp png_ptr, png_uint_32 frame_num) { #ifdef PNG_APNG_SUPPORTED png_uint_32 x_offset, y_offset; PRInt32 width, height; nsPNGDecoder *decoder = static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr)); // old frame is done decoder->EndImageFrame(); // Only the first frame can be hidden, so unhide unconditionally here. decoder->mFrameIsHidden = PR_FALSE; x_offset = png_get_next_frame_x_offset(png_ptr, decoder->mInfo); y_offset = png_get_next_frame_y_offset(png_ptr, decoder->mInfo); width = png_get_next_frame_width(png_ptr, decoder->mInfo); height = png_get_next_frame_height(png_ptr, decoder->mInfo); decoder->CreateFrame(x_offset, y_offset, width, height, decoder->format); #endif }
static void readpng2_info_callback(png_structp png_ptr, png_infop info_ptr) { mainprog_info *mainprog_ptr; int color_type, bit_depth; png_uint_32 width, height; #ifdef PNG_FLOATING_POINT_SUPPORTED double gamma; #else png_fixed_point gamma; #endif /* setjmp() doesn't make sense here, because we'd either have to exit(), * longjmp() ourselves, or return control to libpng, which doesn't want * to see us again. By not doing anything here, libpng will instead jump * to readpng2_decode_data(), which can return an error value to the main * program. */ /* retrieve the pointer to our special-purpose struct, using the png_ptr * that libpng passed back to us (i.e., not a global this time--there's * no real difference for a single image, but for a multithreaded browser * decoding several PNG images at the same time, one needs to avoid mixing * up different images' structs) */ mainprog_ptr = png_get_progressive_ptr(png_ptr); if (mainprog_ptr == NULL) { /* we be hosed */ fprintf(stderr, "readpng2 error: main struct not recoverable in info_callback.\n"); fflush(stderr); return; /* * Alternatively, we could call our error-handler just like libpng * does, which would effectively terminate the program. Since this * can only happen if png_ptr gets redirected somewhere odd or the * main PNG struct gets wiped, we're probably toast anyway. (If * png_ptr itself is NULL, we would not have been called.) */ } /* this is just like in the non-progressive case */ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); mainprog_ptr->width = (ulg)width; mainprog_ptr->height = (ulg)height; /* since we know we've read all of the PNG file's "header" (i.e., up * to IDAT), we can check for a background color here */ if (mainprog_ptr->need_bgcolor && png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) { png_color_16p pBackground; /* it is not obvious from the libpng documentation, but this function * takes a pointer to a pointer, and it always returns valid red, * green and blue values, regardless of color_type: */ png_get_bKGD(png_ptr, info_ptr, &pBackground); /* however, it always returns the raw bKGD data, regardless of any * bit-depth transformations, so check depth and adjust if necessary */ if (bit_depth == 16) { mainprog_ptr->bg_red = pBackground->red >> 8; mainprog_ptr->bg_green = pBackground->green >> 8; mainprog_ptr->bg_blue = pBackground->blue >> 8; } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) { /* int number_passes; NOT USED */ png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_type; unsigned int channels; png_bytep trans = NULL; int num_trans = 0; nsPNGDecoder *decoder = static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr)); /* always decode to 24-bit RGB or 32-bit RGBA */ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type); /* Are we too big? */ if (width > MOZ_PNG_MAX_DIMENSION || height > MOZ_PNG_MAX_DIMENSION) longjmp(png_jmpbuf(decoder->mPNG), 1); // Post our size to the superclass decoder->PostSize(width, height); if (decoder->HasError()) { // Setting the size lead to an error; this can happen when for example // a multipart channel sends an image of a different size. longjmp(png_jmpbuf(decoder->mPNG), 1); } 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)) { int sample_max = (1 << bit_depth); png_color_16p trans_values; png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values); /* libpng doesn't reject a tRNS chunk with out-of-range samples so we check it here to avoid setting up a useless opacity channel or producing unexpected transparent pixels when using libpng-1.2.19 through 1.2.26 (bug #428045) */ if ((color_type == PNG_COLOR_TYPE_GRAY && (int)trans_values->gray > sample_max) || (color_type == PNG_COLOR_TYPE_RGB && ((int)trans_values->red > sample_max || (int)trans_values->green > sample_max || (int)trans_values->blue > sample_max))) { /* clear the tRNS valid flag and release tRNS memory */ png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); } else png_set_expand(png_ptr); } if (bit_depth == 16) png_set_strip_16(png_ptr); qcms_data_type inType; PRUint32 intent = -1; PRUint32 pIntent; if (decoder->mCMSMode != eCMSMode_Off) { intent = gfxPlatform::GetRenderingIntent(); decoder->mInProfile = PNGGetColorProfile(png_ptr, info_ptr, color_type, &inType, &pIntent); /* If we're not mandating an intent, use the one from the image. */ if (intent == PRUint32(-1)) intent = pIntent; } if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) { qcms_data_type outType; if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) outType = QCMS_DATA_RGBA_8; else outType = QCMS_DATA_RGB_8; decoder->mTransform = qcms_transform_create(decoder->mInProfile, inType, gfxPlatform::GetCMSOutputProfile(), outType, (qcms_intent)intent); } else { png_set_gray_to_rgb(png_ptr); // only do gamma correction if CMS isn't entirely disabled if (decoder->mCMSMode != eCMSMode_Off) PNGDoGammaCorrection(png_ptr, info_ptr); if (decoder->mCMSMode == eCMSMode_All) { if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) decoder->mTransform = gfxPlatform::GetCMSRGBATransform(); else decoder->mTransform = gfxPlatform::GetCMSRGBTransform(); } } /* let libpng expand interlaced images */ if (interlace_type == PNG_INTERLACE_ADAM7) { /* number_passes = */ png_set_interlace_handling(png_ptr); } /* now all of those things we set above are used to update various struct * members and whatnot, after which we can get channels, rowbytes, etc. */ png_read_update_info(png_ptr, info_ptr); decoder->mChannels = channels = png_get_channels(png_ptr, info_ptr); /*---------------------------------------------------------------*/ /* copy PNG info into imagelib structs (formerly png_set_dims()) */ /*---------------------------------------------------------------*/ PRInt32 alpha_bits = 1; if (channels == 2 || channels == 4) { /* check if alpha is coming from a tRNS chunk and is binary */ if (num_trans) { /* if it's not an indexed color image, tRNS means binary */ if (color_type == PNG_COLOR_TYPE_PALETTE) { for (int i=0; i<num_trans; i++) { if ((trans[i] != 0) && (trans[i] != 255)) { alpha_bits = 8; break; } } } } else { alpha_bits = 8; } } if (channels == 1 || channels == 3) decoder->format = gfxASurface::ImageFormatRGB24; else if (channels == 2 || channels == 4) decoder->format = gfxASurface::ImageFormatARGB32; #ifdef PNG_APNG_SUPPORTED if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback, NULL); if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) { decoder->mFrameIsHidden = PR_TRUE; } else { #endif decoder->CreateFrame(0, 0, width, height, decoder->format); #ifdef PNG_APNG_SUPPORTED } #endif if (decoder->mTransform && (channels <= 2 || interlace_type == PNG_INTERLACE_ADAM7)) { PRUint32 bpp[] = { 0, 3, 4, 3, 4 }; decoder->mCMSLine = (PRUint8 *)moz_malloc(bpp[channels] * width); if (!decoder->mCMSLine) { longjmp(png_jmpbuf(decoder->mPNG), 5); // NS_ERROR_OUT_OF_MEMORY } } if (interlace_type == PNG_INTERLACE_ADAM7) { if (height < PR_INT32_MAX / (width * channels)) decoder->interlacebuf = (PRUint8 *)moz_malloc(channels * width * height); if (!decoder->interlacebuf) { longjmp(png_jmpbuf(decoder->mPNG), 5); // NS_ERROR_OUT_OF_MEMORY } } /* Reject any ancillary chunk after IDAT with a bad CRC (bug #397593). * It would be better to show the default frame (if one has already been * successfully decoded) before bailing, but it's simpler to just bail * out with an error message. */ png_set_crc_action(png_ptr, PNG_CRC_NO_CHANGE, PNG_CRC_ERROR_QUIT); return; }
static void _png_get_header_func(png_structp png_ptr, png_infop info_ptr) { FXPNG_Context* p = (FXPNG_Context*)png_get_progressive_ptr(png_ptr); if (!p) return; CCodec_PngModule* pModule = (CCodec_PngModule*)p->parent_ptr; if (!pModule) return; png_uint_32 width = 0, height = 0; int bpc = 0, color_type = 0, color_type1 = 0, pass = 0; double gamma = 1.0; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bpc, &color_type, nullptr, nullptr, nullptr); color_type1 = color_type; if (bpc > 8) { png_set_strip_16(png_ptr); } else if (bpc < 8) { png_set_expand_gray_1_2_4_to_8(png_ptr); } bpc = 8; if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); } pass = png_set_interlace_handling(png_ptr); if (!pModule->ReadHeaderCallback(p->child_ptr, width, height, bpc, pass, &color_type, &gamma)) { png_error(p->png_ptr, "Read Header Callback Error"); } int intent; if (png_get_sRGB(png_ptr, info_ptr, &intent)) { png_set_gamma(png_ptr, gamma, 0.45455); } else { double image_gamma; if (png_get_gAMA(png_ptr, info_ptr, &image_gamma)) { png_set_gamma(png_ptr, gamma, image_gamma); } else { png_set_gamma(png_ptr, gamma, 0.45455); } } switch (color_type) { case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: { if (color_type1 & PNG_COLOR_MASK_COLOR) { png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587); } } break; case PNG_COLOR_TYPE_PALETTE: if (color_type1 != PNG_COLOR_TYPE_PALETTE) { png_error(p->png_ptr, "Not Support Output Palette Now"); } case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: if (!(color_type1 & PNG_COLOR_MASK_COLOR)) { png_set_gray_to_rgb(png_ptr); } png_set_bgr(png_ptr); break; } if (!(color_type & PNG_COLOR_MASK_ALPHA)) { png_set_strip_alpha(png_ptr); } if (color_type & PNG_COLOR_MASK_ALPHA && !(color_type1 & PNG_COLOR_MASK_ALPHA)) { png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); } png_read_update_info(png_ptr, info_ptr); }
static void saveData(png_structp png_def, png_bytep data, png_size_t size ) { FILE* f = (FILE*) png_get_progressive_ptr(png_def); if(fwrite(data, 1, size, f) != (size_t)size) throw std::runtime_error("File write error"); }
void decodingFailed(png_structp png, png_const_charp errorMsg) { static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->decodingFailed(); longjmp(png->jmpbuf, 1); }
/** * Supplied to libpng to call when it has read enough data to determine * bounds. */ static void InfoCallback(png_structp png_ptr, png_infop info_ptr) { // png_get_progressive_ptr returns the pointer we set on the png_ptr with // png_set_progressive_read_fn static_cast<AutoCleanPng*>(png_get_progressive_ptr(png_ptr))->infoCallback(); }
/* Called at the start of the progressive load, once we have image info */ static void png_info_callback (png_structp png_read_ptr, png_infop png_info_ptr) { LoadContext* lc; png_uint_32 width, height; png_textp png_text_ptr; int i, num_texts; int color_type; gboolean have_alpha = FALSE; gchar *icc_profile_base64; const gchar *icc_profile_title; const gchar *icc_profile; png_uint_32 icc_profile_size; guint32 retval; gint compression_type; lc = png_get_progressive_ptr(png_read_ptr); if (lc->fatal_error_occurred) return; if (!setup_png_transformations(lc->png_read_ptr, lc->png_info_ptr, lc->error, &width, &height, &color_type)) { lc->fatal_error_occurred = TRUE; return; } /* If we have alpha, set a flag */ if (color_type & PNG_COLOR_MASK_ALPHA) have_alpha = TRUE; if (lc->size_func) { gint w = width; gint h = height; (* lc->size_func) (&w, &h, lc->notify_user_data); if (w == 0 || h == 0) { lc->fatal_error_occurred = TRUE; if (lc->error && *lc->error == NULL) { g_set_error_literal (lc->error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Transformed PNG has zero width or height.")); } return; } } lc->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, have_alpha, 8, width, height); if (lc->pixbuf == NULL) { /* Failed to allocate memory */ lc->fatal_error_occurred = TRUE; if (lc->error && *lc->error == NULL) { g_set_error (lc->error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Insufficient memory to store a %lu by %lu image; try exiting some applications to reduce memory usage"), (gulong) width, (gulong) height); } return; } /* Extract text chunks and attach them as pixbuf options */ if (png_get_text (png_read_ptr, png_info_ptr, &png_text_ptr, &num_texts)) { for (i = 0; i < num_texts; i++) { gchar *key, *value; if (png_text_to_pixbuf_option (png_text_ptr[i], &key, &value)) { gdk_pixbuf_set_option (lc->pixbuf, key, value); g_free (key); g_free (value); } } } #if defined(PNG_cHRM_SUPPORTED) /* Extract embedded ICC profile */ retval = png_get_iCCP (png_read_ptr, png_info_ptr, (png_charpp) &icc_profile_title, &compression_type, (png_bytepp) &icc_profile, &icc_profile_size); if (retval != 0) { icc_profile_base64 = g_base64_encode ((const guchar *) icc_profile, (gsize)icc_profile_size); gdk_pixbuf_set_option (lc->pixbuf, "icc-profile", icc_profile_base64); g_free (icc_profile_base64); } #endif /* Notify the client that we are ready to go */ if (lc->prepare_func) (* lc->prepare_func) (lc->pixbuf, NULL, lc->notify_user_data); return; }
static SkPngNormalDecoder* GetDecoder(png_structp png_ptr) { return static_cast<SkPngNormalDecoder*>(png_get_progressive_ptr(png_ptr)); }
static void InterlacedRowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int pass) { auto decoder = static_cast<SkPngInterlacedDecoder*>(png_get_progressive_ptr(png_ptr)); decoder->interlacedRowCallback(row, rowNum, pass); }
// Called when a row is ready. static void PNGAPI rowAvailable(png_structp png, png_bytep rowBuffer, png_uint_32 rowIndex, int interlacePass) { static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->rowAvailable(rowBuffer, rowIndex, interlacePass); }
// Called when we have obtained the header information (including the size). static void PNGAPI headerAvailable(png_structp png, png_infop) { static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->headerAvailable(); }
void nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr) { // int number_passes; NOT USED png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_type; unsigned int channels; png_bytep trans = nullptr; int num_trans = 0; nsPNGDecoder* decoder = static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr)); // Always decode to 24-bit RGB or 32-bit RGBA png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type); // Are we too big? if (width > MOZ_PNG_MAX_DIMENSION || height > MOZ_PNG_MAX_DIMENSION) { png_longjmp(decoder->mPNG, 1); } // Post our size to the superclass decoder->PostSize(width, height); if (decoder->HasError()) { // Setting the size led to an error. png_longjmp(decoder->mPNG, 1); } 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_color_16p trans_values; png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values); // libpng doesn't reject a tRNS chunk with out-of-range samples // so we check it here to avoid setting up a useless opacity // channel or producing unexpected transparent pixels (bug #428045) if (bit_depth < 16) { png_uint_16 sample_max = (1 << bit_depth) - 1; if ((color_type == PNG_COLOR_TYPE_GRAY && trans_values->gray > sample_max) || (color_type == PNG_COLOR_TYPE_RGB && (trans_values->red > sample_max || trans_values->green > sample_max || trans_values->blue > sample_max))) { // clear the tRNS valid flag and release tRNS memory png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); num_trans = 0; } } if (num_trans != 0) { png_set_expand(png_ptr); } } if (bit_depth == 16) { png_set_scale_16(png_ptr); } qcms_data_type inType = QCMS_DATA_RGBA_8; uint32_t intent = -1; uint32_t pIntent; if (decoder->mCMSMode != eCMSMode_Off) { intent = gfxPlatform::GetRenderingIntent(); decoder->mInProfile = PNGGetColorProfile(png_ptr, info_ptr, color_type, &inType, &pIntent); // If we're not mandating an intent, use the one from the image. if (intent == uint32_t(-1)) { intent = pIntent; } } if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) { qcms_data_type outType; if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) { outType = QCMS_DATA_RGBA_8; } else { outType = QCMS_DATA_RGB_8; } decoder->mTransform = qcms_transform_create(decoder->mInProfile, inType, gfxPlatform::GetCMSOutputProfile(), outType, (qcms_intent)intent); } else { png_set_gray_to_rgb(png_ptr); // only do gamma correction if CMS isn't entirely disabled if (decoder->mCMSMode != eCMSMode_Off) { PNGDoGammaCorrection(png_ptr, info_ptr); } if (decoder->mCMSMode == eCMSMode_All) { if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) { decoder->mTransform = gfxPlatform::GetCMSRGBATransform(); } else { decoder->mTransform = gfxPlatform::GetCMSRGBTransform(); } } } // let libpng expand interlaced images if (interlace_type == PNG_INTERLACE_ADAM7) { // number_passes = png_set_interlace_handling(png_ptr); } // now all of those things we set above are used to update various struct // members and whatnot, after which we can get channels, rowbytes, etc. png_read_update_info(png_ptr, info_ptr); decoder->mChannels = channels = png_get_channels(png_ptr, info_ptr); //---------------------------------------------------------------// // copy PNG info into imagelib structs (formerly png_set_dims()) // //---------------------------------------------------------------// if (channels == 1 || channels == 3) { decoder->format = gfx::SurfaceFormat::B8G8R8X8; } else if (channels == 2 || channels == 4) { decoder->format = gfx::SurfaceFormat::B8G8R8A8; } else { png_longjmp(decoder->mPNG, 1); // invalid number of channels } #ifdef PNG_APNG_SUPPORTED if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) { png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback, nullptr); } if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) { decoder->mFrameIsHidden = true; } else { #endif decoder->CreateFrame(0, 0, width, height, decoder->format); #ifdef PNG_APNG_SUPPORTED } #endif if (decoder->mTransform && (channels <= 2 || interlace_type == PNG_INTERLACE_ADAM7)) { uint32_t bpp[] = { 0, 3, 4, 3, 4 }; decoder->mCMSLine = (uint8_t*)malloc(bpp[channels] * width); if (!decoder->mCMSLine) { png_longjmp(decoder->mPNG, 5); // NS_ERROR_OUT_OF_MEMORY } } if (interlace_type == PNG_INTERLACE_ADAM7) { if (height < INT32_MAX / (width * channels)) { decoder->interlacebuf = (uint8_t*)malloc(channels * width * height); } if (!decoder->interlacebuf) { png_longjmp(decoder->mPNG, 5); // NS_ERROR_OUT_OF_MEMORY } } if (decoder->NeedsNewFrame()) { // We know that we need a new frame, so pause input so the decoder // infrastructure can give it to us. png_process_data_pause(png_ptr, /* save = */ 1); } }
// Called when we have completely finished decoding the image. static void PNGAPI pngComplete(png_structp png, png_infop) { static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete(); }
void nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) { /* libpng comments: * * this function is called for every row in the image. If the * image is interlacing, and you turned on the interlace handler, * this function will be called for every row in every pass. * Some of these rows will not be changed from the previous pass. * When the row is not changed, the new_row variable will be * nullptr. The rows and passes are called in order, so you don't * really need the row_num and pass, but I'm supplying them * because it may make your life easier. * * For the non-nullptr rows of interlaced images, you must call * png_progressive_combine_row() passing in the row and the * old row. You can call this function for nullptr rows (it will * just return) and for non-interlaced images (it just does the * memcpy for you) if it will make the code easier. Thus, you * can just do this for all cases: * * png_progressive_combine_row(png_ptr, old_row, new_row); * * where old_row is what was displayed for previous rows. Note * that the first pass (pass == 0 really) will completely cover * the old row, so the rows do not have to be initialized. After * the first pass (and only for interlaced images), you will have * to pass the current row, and the function will combine the * old row and the new row. */ nsPNGDecoder* decoder = static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr)); // skip this frame if (decoder->mFrameIsHidden) { return; } if (row_num >= (png_uint_32) decoder->mFrameRect.height) { return; } if (new_row) { int32_t width = decoder->mFrameRect.width; uint32_t iwidth = decoder->mFrameRect.width; png_bytep line = new_row; if (decoder->interlacebuf) { line = decoder->interlacebuf + (row_num * decoder->mChannels * width); png_progressive_combine_row(png_ptr, line, new_row); } uint32_t bpr = width * sizeof(uint32_t); uint32_t* cptr32 = (uint32_t*)(decoder->mImageData + (row_num*bpr)); if (decoder->mTransform) { if (decoder->mCMSLine) { qcms_transform_data(decoder->mTransform, line, decoder->mCMSLine, iwidth); // copy alpha over uint32_t channels = decoder->mChannels; if (channels == 2 || channels == 4) { for (uint32_t i = 0; i < iwidth; i++) decoder->mCMSLine[4 * i + 3] = line[channels * i + channels - 1]; } line = decoder->mCMSLine; } else { qcms_transform_data(decoder->mTransform, line, line, iwidth); } } switch (decoder->format) { case gfx::SurfaceFormat::B8G8R8X8: { // counter for while() loops below uint32_t idx = iwidth; // copy as bytes until source pointer is 32-bit-aligned for (; (NS_PTR_TO_UINT32(line) & 0x3) && idx; --idx) { *cptr32++ = gfxPackedPixel(0xFF, line[0], line[1], line[2]); line += 3; } // copy pixels in blocks of 4 while (idx >= 4) { GFX_BLOCK_RGB_TO_FRGB(line, cptr32); idx -= 4; line += 12; cptr32 += 4; } // copy remaining pixel(s) while (idx--) { // 32-bit read of final pixel will exceed buffer, so read bytes *cptr32++ = gfxPackedPixel(0xFF, line[0], line[1], line[2]); line += 3; } } break; case gfx::SurfaceFormat::B8G8R8A8: { if (!decoder->mDisablePremultipliedAlpha) { for (uint32_t x=width; x>0; --x) { *cptr32++ = gfxPackedPixel(line[3], line[0], line[1], line[2]); line += 4; } } else { for (uint32_t x=width; x>0; --x) { *cptr32++ = gfxPackedPixelNoPreMultiply(line[3], line[0], line[1], line[2]); line += 4; } } } break; default: png_longjmp(decoder->mPNG, 1); } if (decoder->mNumFrames <= 1) { // Only do incremental image display for the first frame // XXXbholley - this check should be handled in the superclass nsIntRect r(0, row_num, width, 1); decoder->PostInvalidation(r); } } }