/** * Expand the colormap from RGB to Packed ARGB as needed by Cairo. * And apply any LCMS transformation. */ static void ConvertColormap(PRUint32 *aColormap, PRUint32 aColors) { // Apply CMS transformation if enabled and available if (gfxPlatform::GetCMSMode() == eCMSMode_All) { qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); if (transform) qcms_transform_data(transform, aColormap, aColormap, aColors); } // Convert from the GIF's RGB format to the Cairo format. // Work from end to begin, because of the in-place expansion PRUint8 *from = ((PRUint8 *)aColormap) + 3 * aColors; PRUint32 *to = aColormap + aColors; // Convert color entries to Cairo format // set up for loops below if (!aColors) return; PRUint32 c = aColors; // copy as bytes until source pointer is 32-bit-aligned // NB: can't use 32-bit reads, they might read off the end of the buffer for (; (NS_PTR_TO_UINT32(from) & 0x3) && c; --c) { from -= 3; *--to = GFX_PACKED_PIXEL(0xFF, from[0], from[1], from[2]); } // bulk copy of pixels. while (c >= 4) { from -= 12; to -= 4; c -= 4; GFX_BLOCK_RGB_TO_FRGB(from,to); } // copy remaining pixel(s) // NB: can't use 32-bit reads, they might read off the end of the buffer while (c--) { from -= 3; *--to = GFX_PACKED_PIXEL(0xFF, from[0], from[1], from[2]); } }
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 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. */ 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) { PRInt32 width = decoder->mFrameRect.width; PRUint32 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); } PRUint32 bpr = width * sizeof(PRUint32); PRUint32 *cptr32 = (PRUint32*)(decoder->mImageData + (row_num*bpr)); bool rowHasNoAlpha = true; if (decoder->mTransform) { if (decoder->mCMSLine) { qcms_transform_data(decoder->mTransform, line, decoder->mCMSLine, iwidth); /* copy alpha over */ PRUint32 channels = decoder->mChannels; if (channels == 2 || channels == 4) { for (PRUint32 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 gfxASurface::ImageFormatRGB24: { // counter for while() loops below PRUint32 idx = iwidth; // copy as bytes until source pointer is 32-bit-aligned for (; (NS_PTR_TO_UINT32(line) & 0x3) && idx; --idx) { *cptr32++ = GFX_PACKED_PIXEL(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++ = GFX_PACKED_PIXEL(0xFF, line[0], line[1], line[2]); line += 3; } } break; case gfxASurface::ImageFormatARGB32: { if (!decoder->mDisablePremultipliedAlpha) { for (PRUint32 x=width; x>0; --x) { *cptr32++ = GFX_PACKED_PIXEL(line[3], line[0], line[1], line[2]); if (line[3] != 0xff) rowHasNoAlpha = PR_FALSE; line += 4; } } else { for (PRUint32 x=width; x>0; --x) { *cptr32++ = GFX_PACKED_PIXEL_NO_PREMULTIPLY(line[3], line[0], line[1], line[2]); if (line[3] != 0xff) rowHasNoAlpha = PR_FALSE; line += 4; } } } break; default: longjmp(png_jmpbuf(decoder->mPNG), 1); } if (!rowHasNoAlpha) decoder->mFrameHasNoAlpha = PR_FALSE; PRUint32 numFrames = decoder->mImage->GetNumFrames(); if (numFrames <= 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 nsJPEGDecoder::OutputScanlines(bool* suspend) { *suspend = false; const uint32_t top = mInfo.output_scanline; while ((mInfo.output_scanline < mInfo.output_height)) { /* Use the Cairo image buffer as scanline buffer */ uint32_t *imageRow = ((uint32_t*)mImageData) + (mInfo.output_scanline * mInfo.output_width); if (mInfo.cconvert->color_convert == ycc_rgb_convert_argb) { /* Special case: scanline will be directly converted into packed ARGB */ if (jpeg_read_scanlines(&mInfo, (JSAMPARRAY)&imageRow, 1) != 1) { *suspend = true; /* suspend */ break; } continue; /* all done for this row! */ } JSAMPROW sampleRow = (JSAMPROW)imageRow; if (mInfo.output_components == 3) { /* Put the pixels at end of row to enable in-place expansion */ sampleRow += mInfo.output_width; } /* Request one scanline. Returns 0 or 1 scanlines. */ if (jpeg_read_scanlines(&mInfo, &sampleRow, 1) != 1) { *suspend = true; /* suspend */ break; } if (mTransform) { JSAMPROW source = sampleRow; if (mInfo.out_color_space == JCS_GRAYSCALE) { /* Convert from the 1byte grey pixels at begin of row to the 3byte RGB byte pixels at 'end' of row */ sampleRow += mInfo.output_width; } qcms_transform_data(mTransform, source, sampleRow, mInfo.output_width); /* Move 3byte RGB data to end of row */ if (mInfo.out_color_space == JCS_CMYK) { memmove(sampleRow + mInfo.output_width, sampleRow, 3 * mInfo.output_width); sampleRow += mInfo.output_width; } } else { if (mInfo.out_color_space == JCS_CMYK) { /* Convert from CMYK to RGB */ /* We cannot convert directly to Cairo, as the CMSRGBTransform may wants to do a RGB transform... */ /* Would be better to have platform CMSenabled transformation from CMYK to (A)RGB... */ cmyk_convert_rgb((JSAMPROW)imageRow, mInfo.output_width); sampleRow += mInfo.output_width; } if (mCMSMode == eCMSMode_All) { /* No embedded ICC profile - treat as sRGB */ qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); if (transform) { qcms_transform_data(transform, sampleRow, sampleRow, mInfo.output_width); } } } // counter for while() loops below uint32_t idx = mInfo.output_width; // copy as bytes until source pointer is 32-bit-aligned for (; (NS_PTR_TO_UINT32(sampleRow) & 0x3) && idx; --idx) { *imageRow++ = GFX_PACKED_PIXEL(0xFF, sampleRow[0], sampleRow[1], sampleRow[2]); sampleRow += 3; } // copy pixels in blocks of 4 while (idx >= 4) { GFX_BLOCK_RGB_TO_FRGB(sampleRow, imageRow); idx -= 4; sampleRow += 12; imageRow += 4; } // copy remaining pixel(s) while (idx--) { // 32-bit read of final pixel will exceed buffer, so read bytes *imageRow++ = GFX_PACKED_PIXEL(0xFF, sampleRow[0], sampleRow[1], sampleRow[2]); sampleRow += 3; } } if (top != mInfo.output_scanline) { nsIntRect r(0, top, mInfo.output_width, mInfo.output_scanline-top); PostInvalidation(r); } }