void GIFImageDecoder::prepEmptyFrameBuffer(RGBA32Buffer* buffer) { //+ 7/13/09 CSidhall - Added for alloc fail handling and cache space arranging. if(!buffer) return; buffer->setHasAlpha(true); int size = m_size.width() * m_size.height(); 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 GIF 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); // Unlock buffer->bytes().resize(size); if(buffer->bytes().data()) buffer->bytes().fill(0); }
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); }