// FIXME: Currently sharing interlaced callback for all rows and subset. It's not // as expensive as the subset version of non-interlaced, but it still does extra // work. void interlacedRowCallback(png_bytep row, int rowNum, int pass) { if (rowNum < fFirstRow || rowNum > fLastRow) { // Ignore this row return; } png_bytep oldRow = fInterlaceBuffer.get() + (rowNum - fFirstRow) * fPng_rowbytes; png_progressive_combine_row(this->png_ptr(), oldRow, row); if (0 == pass) { // The first pass initializes all rows. SkASSERT(row); SkASSERT(fLinesDecoded == rowNum - fFirstRow); fLinesDecoded++; } else { SkASSERT(fLinesDecoded == fLastRow - fFirstRow + 1); if (fNumberPasses - 1 == pass && rowNum == fLastRow) { // Last pass, and we have read all of the rows we care about. Note that // we do not care about reading anything beyond the end of the image (or // beyond the last scanline requested). fInterlacedComplete = true; // Fake error to stop decoding scanlines. longjmp(png_jmpbuf(this->png_ptr()), kStopDecoding); } } }
static void user_endrow_callback (png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) { GstPngDec *pngdec = NULL; pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr)); /* If buffer_out doesn't exist, it means buffer_alloc failed, which * will already have set the return code */ if (new_row && GST_IS_BUFFER (pngdec->current_frame->output_buffer)) { GstVideoFrame frame; GstBuffer *buffer = pngdec->current_frame->output_buffer; size_t offset; guint8 *data; if (!gst_video_frame_map (&frame, &pngdec->output_state->info, buffer, GST_MAP_WRITE)) { pngdec->ret = GST_FLOW_ERROR; return; } data = GST_VIDEO_FRAME_COMP_DATA (&frame, 0); offset = row_num * GST_VIDEO_FRAME_COMP_STRIDE (&frame, 0); GST_LOG ("got row %u at pass %d, copying in buffer %p at offset %" G_GSIZE_FORMAT, (guint) row_num, pass, pngdec->current_frame->output_buffer, offset); png_progressive_combine_row (pngdec->png, data + offset, new_row); gst_video_frame_unmap (&frame); pngdec->ret = GST_FLOW_OK; } else pngdec->ret = GST_FLOW_OK; }
row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) { /* 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 NULL. 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-NULL 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 NULL 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. */ }
void png_row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) { struct cached_image *cimg; #ifdef REPACK_16 unsigned char *tmp; int channels; #endif /* #ifdef REPACK_16 */ cimg=global_cimg; #ifdef REPACK_16 if (cimg->buffer_bytes_per_pixel>4) { channels=cimg->buffer_bytes_per_pixel/sizeof(unsigned short); if (PNG_INTERLACE_NONE==png_get_interlace_type(png_ptr, ((struct png_decoder *)cimg->decoder)->info_ptr)) { unsigned_short_from_2char((unsigned short *)(cimg->buffer+cimg ->buffer_bytes_per_pixel *cimg->width *row_num), new_row, cimg->width *channels); }else{ if ((unsigned)cimg->width > MAXINT / 2 / channels) overalloc(); tmp=mem_alloc(cimg->width*2*channels); a2char_from_unsigned_short(tmp, (unsigned short *)(cimg->buffer +cimg->buffer_bytes_per_pixel *cimg->width*row_num), cimg->width*channels); png_progressive_combine_row(png_ptr, tmp, new_row); unsigned_short_from_2char((unsigned short *)(cimg->buffer +cimg->buffer_bytes_per_pixel *cimg->width*row_num), tmp, cimg->width*channels); mem_free(tmp); } }else #endif /* #ifdef REPACK_16 */ { png_progressive_combine_row(png_ptr, cimg->buffer+cimg->buffer_bytes_per_pixel *cimg->width*row_num, new_row); } cimg->rows_added=1; }
void haveRow(unsigned int rowNum, int pass, unsigned char* data) { if (interlaced) { png_byte pngDepth = png_get_bit_depth(pngReadStruct, pngInfoStruct); Q_ASSERT(pngDepth <= depth * 8); requestScanline(rowNum, scanlineBuf); png_progressive_combine_row(pngReadStruct, scanlineBuf, data); notifyScanline(pass + 1, scanlineBuf); } else notifyScanline(pass + 1, data); }
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 ); }
/* This function is called when each row of image data is complete */ void row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) { if(row_num >= height) { // bad row number } png_progressive_combine_row(png_ptr, row_pointers[row_num], new_row); for(int n=0;n<width;n++) { uint32_t pixel = inlineget_pixel(new_row,pixel_depth,n); if(pixel_depth == 1) { if(pixel!=0) pixel = 0xFFFFFFFF; } nsdl_pointS(inline_data_layer,n,row_num,pixel); } }
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); }
/* 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 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); }
void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int) { if (m_frameBufferCache.isEmpty()) return; // Initialize the framebuffer if needed. ImageFrame& buffer = m_frameBufferCache[0]; if (buffer.status() == ImageFrame::FrameEmpty) { png_structp png = m_reader->pngPtr(); if (!buffer.setSize(size().width(), size().height())) { longjmp(JMPBUF(png), 1); return; } unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr())) { m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height()); if (!m_reader->interlaceBuffer()) { longjmp(JMPBUF(png), 1); return; } } #if USE(QCMSLIB) if (m_reader->colorTransform()) { m_reader->createRowBuffer(colorChannels * size().width()); if (!m_reader->rowBuffer()) { longjmp(JMPBUF(png), 1); return; } } #endif buffer.setStatus(ImageFrame::FramePartial); buffer.setHasAlpha(false); // For PNGs, the frame always fills the entire image. buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); } /* libpng comments (here to explain what follows). * * 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 NULL. * 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. */ // Nothing to do if the row is unchanged, or the row is outside // the image bounds: libpng may send extra rows, ignore them to // make our lives easier. if (!rowBuffer) return; int y = rowIndex; if (y < 0 || y >= size().height()) return; /* libpng comments (continued). * * For the non-NULL 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 NULL 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. */ bool hasAlpha = m_reader->hasAlpha(); png_bytep row = rowBuffer; if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { unsigned colorChannels = hasAlpha ? 4 : 3; row = interlaceBuffer + (rowIndex * colorChannels * size().width()); png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); } #if USE(QCMSLIB) if (qcms_transform* transform = m_reader->colorTransform()) { qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width()); row = m_reader->rowBuffer(); } #endif // Write the decoded row pixels to the frame buffer. The repetitive // form of the row write loops is for speed. ImageFrame::PixelData* address = buffer.getAddr(0, y); unsigned alphaMask = 255; int width = size().width(); png_bytep pixel = row; if (hasAlpha) { if (buffer.premultiplyAlpha()) { for (int x = 0; x < width; ++x, pixel += 4) { buffer.setRGBAPremultiply(address++, pixel[0], pixel[1], pixel[2], pixel[3]); alphaMask &= pixel[3]; } } else { for (int x = 0; x < width; ++x, pixel += 4) { buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], pixel[3]); alphaMask &= pixel[3]; } } } else { for (int x = 0; x < width; ++x, pixel += 3) { buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], 255); } } if (alphaMask != 255 && !buffer.hasAlpha()) buffer.setHasAlpha(true); buffer.setPixelsChanged(true); }
void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass) { if (m_frameBufferCache.isEmpty()) return; // Initialize the framebuffer if needed. ImageFrame& buffer = m_frameBufferCache[0]; if (buffer.status() == ImageFrame::FrameEmpty) { if (!buffer.setSize(scaledSize().width(), scaledSize().height())) { longjmp(JMPBUF(m_reader->pngPtr()), 1); return; } buffer.setStatus(ImageFrame::FramePartial); buffer.setHasAlpha(false); buffer.setColorProfile(m_colorProfile); // For PNGs, the frame always fills the entire image. buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); if (png_get_interlace_type(m_reader->pngPtr(), m_reader->infoPtr()) != PNG_INTERLACE_NONE) m_reader->createInterlaceBuffer((m_reader->hasAlpha() ? 4 : 3) * size().width() * size().height()); } if (!rowBuffer) return; // libpng comments (pasted in here to explain what follows) /* * 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 NULL. * 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-NULL 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 NULL 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. */ png_structp png = m_reader->pngPtr(); bool hasAlpha = m_reader->hasAlpha(); unsigned colorChannels = hasAlpha ? 4 : 3; png_bytep row; png_bytep interlaceBuffer = m_reader->interlaceBuffer(); if (interlaceBuffer) { row = interlaceBuffer + (rowIndex * colorChannels * size().width()); png_progressive_combine_row(png, row, rowBuffer); } else row = rowBuffer; // Copy the data into our buffer. int width = scaledSize().width(); int destY = scaledY(rowIndex); // Check that the row is within the image bounds. LibPNG may supply an extra row. if (destY < 0 || destY >= scaledSize().height()) return; bool nonTrivialAlpha = false; for (int x = 0; x < width; ++x) { png_bytep pixel = row + (m_scaled ? m_scaledColumns[x] : x) * colorChannels; unsigned alpha = hasAlpha ? pixel[3] : 255; buffer.setRGBA(x, destY, pixel[0], pixel[1], pixel[2], alpha); nonTrivialAlpha |= alpha < 255; } if (nonTrivialAlpha && !buffer.hasAlpha()) buffer.setHasAlpha(nonTrivialAlpha); }
void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass) { // Resize to the width and height of the image. RGBA32Buffer& buffer = m_frameBufferCache[0]; if (buffer.status() == RGBA32Buffer::FrameEmpty) { // Let's resize our buffer now to the correct width/height. RGBA32Array& bytes = buffer.bytes(); bytes.resize(m_size.width() * m_size.height()); // Update our status to be partially complete. buffer.setStatus(RGBA32Buffer::FramePartial); // For PNGs, the frame always fills the entire image. buffer.setRect(IntRect(0, 0, m_size.width(), m_size.height())); if (reader()->pngPtr()->interlaced) reader()->createInterlaceBuffer((reader()->hasAlpha() ? 4 : 3) * m_size.width() * m_size.height()); } if (rowBuffer == 0) return; /* libpng comments (pasted in here to explain what follows) * * 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 NULL. * 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-NULL 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 NULL 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. */ png_structp png = reader()->pngPtr(); bool hasAlpha = reader()->hasAlpha(); unsigned colorChannels = hasAlpha ? 4 : 3; png_bytep row; png_bytep interlaceBuffer = reader()->interlaceBuffer(); if (interlaceBuffer) { row = interlaceBuffer + (rowIndex * colorChannels * m_size.width()); png_progressive_combine_row(png, row, rowBuffer); } else row = rowBuffer; // Copy the data into our buffer. int width = m_size.width(); unsigned* dst = buffer.bytes().data() + rowIndex * width; bool sawAlpha = false; for (int i = 0; i < width; i++) { unsigned red = *row++; unsigned green = *row++; unsigned blue = *row++; unsigned alpha = (hasAlpha ? *row++ : 255); RGBA32Buffer::setRGBA(*dst++, red, green, blue, alpha); if (!sawAlpha && alpha < 255) { sawAlpha = true; buffer.setHasAlpha(true); } } buffer.ensureHeight(rowIndex + 1); }
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); } } }
/* * @brief process each row of data provided by libpng * @note callback from libpng * * @param [in] new_row pointer to start of next row of data * @param [in] row_num number of row in image */ void apng_ani::row_callback(png_bytep new_row, png_uint_32 row_num) { png_progressive_combine_row(_pngp, _frame_raw.rows.at(row_num), new_row); }
void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass) { // Resize to the width and height of the image. RGBA32Buffer& buffer = m_frameBufferCache[0]; //+ 7/13/09 CSidhall - Added buffer size check RGBA32Array& bytes = buffer.bytes(); int curSize = bytes.size(); // Verify that buffer is actually there int size = m_size.width() * m_size.height(); if ((buffer.status() == RGBA32Buffer::FrameEmpty) || (size != curSize)) { setImagePruneLockStatus(true); // Lock against pruning this resource #ifdef _DEBUG static bool overflowFlag = false; // Avoid too many asserts bool cachePrune = cache()->pruneImages(size << 2); if(cachePrune == false) { if(!overflowFlag) { char buffer[256]; sprintf(buffer, "RAM Cache Overflow: cache is too small to decode PNG image: %d bytes\n", (size<<2) ); EAW_ASSERT_MSG(cachePrune, buffer); overflowFlag = true; } // Could exit here but we get into image skipping issues that still need to be worked out. // return false; } #else cache()->pruneImages(size << 2); #endif setImagePruneLockStatus(false); // Let's resize our buffer now to the correct width/height. bytes.resize(size); if(!bytes.data()) return; //- CS // Update our status to be partially complete. buffer.setStatus(RGBA32Buffer::FramePartial); // For PNGs, the frame always fills the entire image. buffer.setRect(IntRect(0, 0, m_size.width(), m_size.height())); if (reader()->pngPtr()->interlaced) reader()->createInterlaceBuffer((reader()->hasAlpha() ? 4 : 3) * m_size.width() * m_size.height()); } if (rowBuffer == 0) return; /* libpng comments (pasted in here to explain what follows) * * 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 NULL. * 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-NULL 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 NULL 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. */ png_structp png = reader()->pngPtr(); bool hasAlpha = reader()->hasAlpha(); unsigned colorChannels = hasAlpha ? 4 : 3; png_bytep row; png_bytep interlaceBuffer = reader()->interlaceBuffer(); if (interlaceBuffer) { row = interlaceBuffer + (rowIndex * colorChannels * m_size.width()); png_progressive_combine_row(png, row, rowBuffer); } else row = rowBuffer; // Old code for reference: /* // Copy the data into our buffer. int width = m_size.width(); unsigned* dst = buffer.bytes().data() + rowIndex * width; bool sawAlpha = false; for (int i = 0; i < width; i++) { unsigned red = *row++; unsigned green = *row++; unsigned blue = *row++; unsigned alpha = (hasAlpha ? *row++ : 255); RGBA32Buffer::setRGBA(*dst++, red, green, blue, alpha); if (!sawAlpha && alpha < 255) { sawAlpha = true; buffer.setHasAlpha(true); } } */ // New code: // EA/Alex Mole: don't check for alpha at //every single pixel, only do it once per row if( hasAlpha ) { // Copy the data into our buffer. int width = m_size.width(); unsigned* dst = buffer.bytes().data() + rowIndex * width; // EA/Alex Mole: instead of testing whether we've seen any alpha at each pixel, // we can AND together all of the alpha values and see if we end up // with 255 or not. unsigned minalpha = 255; for (int i = 0; i < width; i++) { unsigned red = *row++; unsigned green = *row++; unsigned blue = *row++; // EA/Alex Mole unsigned alpha = *row++; minalpha = minalpha & alpha; RGBA32Buffer::setRGBA(*dst++, red, green, blue, alpha); } // EA/Alex Mole: see comment above minalpha declaration if (minalpha != 255) { buffer.setHasAlpha(true); } } else { // Copy the data into our buffer. int width = m_size.width(); unsigned* dst = buffer.bytes().data() + rowIndex * width; for (int i = 0; i < width; i++) { unsigned red = *row++; unsigned green = *row++; unsigned blue = *row++; RGBA32Buffer::setRGBWithPresetAlpha(*dst++, red, green, blue); } } buffer.ensureHeight(rowIndex + 1); }