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); } } }
void ReadPixelsIntoImageSurface(GLContext* gl, gfxImageSurface* dest) { gl->MakeCurrent(); MOZ_ASSERT(dest->GetSize() != gfxIntSize(0, 0)); /* gfxImageFormat::ARGB32: * RGBA+UByte: be[RGBA], le[ABGR] * RGBA+UInt: le[RGBA] * BGRA+UInt: le[BGRA] * BGRA+UIntRev: le[ARGB] * * gfxImageFormat::RGB16_565: * RGB+UShort: le[rrrrrggg,gggbbbbb] */ bool hasAlpha = dest->Format() == gfxImageFormat::ARGB32; int destPixelSize; GLenum destFormat; GLenum destType; switch (dest->Format()) { case gfxImageFormat::RGB24: // XRGB case gfxImageFormat::ARGB32: destPixelSize = 4; // Needs host (little) endian ARGB. destFormat = LOCAL_GL_BGRA; destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; break; case gfxImageFormat::RGB16_565: destPixelSize = 2; destFormat = LOCAL_GL_RGB; destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV; break; default: MOZ_CRASH("Bad format."); } MOZ_ASSERT(dest->Stride() == dest->Width() * destPixelSize); GLenum readFormat = destFormat; GLenum readType = destType; bool needsTempSurf = !GetActualReadFormats(gl, destFormat, destType, readFormat, readType); nsAutoPtr<gfxImageSurface> tempSurf; gfxImageSurface* readSurf = nullptr; int readPixelSize = 0; if (needsTempSurf) { if (gl->DebugMode()) { NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!"); } SurfaceFormat readFormatGFX; switch (readFormat) { case LOCAL_GL_RGBA: case LOCAL_GL_BGRA: { readFormatGFX = hasAlpha ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8; break; } case LOCAL_GL_RGB: { MOZ_ASSERT(readPixelSize == 2); MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV); readFormatGFX = SurfaceFormat::R5G6B5; break; } default: { MOZ_CRASH("Bad read format."); } } switch (readType) { case LOCAL_GL_UNSIGNED_BYTE: { MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); readPixelSize = 4; break; } case LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV: { MOZ_ASSERT(readFormat == LOCAL_GL_BGRA); readPixelSize = 4; break; } case LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV: { MOZ_ASSERT(readFormat == LOCAL_GL_RGB); readPixelSize = 2; break; } default: { MOZ_CRASH("Bad read type."); } } tempSurf = new gfxImageSurface(dest->GetSize(), SurfaceFormatToImageFormat(readFormatGFX), false); readSurf = tempSurf; } else { readPixelSize = destPixelSize; readSurf = dest; } MOZ_ASSERT(readPixelSize); GLint currentPackAlignment = 0; gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, ¤tPackAlignment); if (currentPackAlignment != readPixelSize) gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readPixelSize); GLsizei width = dest->Width(); GLsizei height = dest->Height(); readSurf->Flush(); gl->fReadPixels(0, 0, width, height, readFormat, readType, readSurf->Data()); readSurf->MarkDirty(); if (currentPackAlignment != readPixelSize) gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment); if (readSurf != dest) { MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE); // So we just copied in RGBA in big endian, or le: 0xAABBGGRR. // We want 0xAARRGGBB, so swap R and B: dest->Flush(); RefPtr<DataSourceSurface> readDSurf = Factory::CreateWrappingDataSourceSurface(readSurf->Data(), readSurf->Stride(), ToIntSize(readSurf->GetSize()), ImageFormatToSurfaceFormat(readSurf->Format())); SwapRAndBComponents(readDSurf); dest->MarkDirty(); gfxContext ctx(dest); ctx.SetOperator(gfxContext::OPERATOR_SOURCE); ctx.SetSource(readSurf); ctx.Paint(); } // Check if GL is giving back 1.0 alpha for // RGBA reads to RGBA images from no-alpha buffers. #ifdef XP_MACOSX if (gl->WorkAroundDriverBugs() && gl->Vendor() == gl::GLVendor::NVIDIA && dest->Format() == gfxImageFormat::ARGB32 && width && height) { GLint alphaBits = 0; gl->fGetIntegerv(LOCAL_GL_ALPHA_BITS, &alphaBits); if (!alphaBits) { const uint32_t alphaMask = gfxPackedPixelNoPreMultiply(0xff,0,0,0); dest->Flush(); uint32_t* itr = (uint32_t*)dest->Data(); uint32_t testPixel = *itr; if ((testPixel & alphaMask) != alphaMask) { // We need to set the alpha channel to 1.0 manually. uint32_t* itrEnd = itr + width*height; // Stride is guaranteed to be width*4. for (; itr != itrEnd; itr++) { *itr |= alphaMask; } } dest->MarkDirty(); } } #endif }