/// Expand the colormap from RGB to Packed ARGB as needed by Cairo. /// And apply any LCMS transformation. static void ConvertColormap(uint32_t* aColormap, uint32_t 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 uint8_t* from = ((uint8_t*)aColormap) + 3 * aColors; uint32_t* to = aColormap + aColors; // Convert color entries to Cairo format // set up for loops below if (!aColors) { return; } uint32_t 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 = gfxPackedPixel(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 = gfxPackedPixel(0xFF, from[0], from[1], from[2]); } }
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.out_color_space == MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB) { // 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++ = gfxPackedPixel(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++ = gfxPackedPixel(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); } }
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 nsWEBPDecoder::WriteInternal(const char *aBuffer, uint32_t aCount, DecodeStrategy) { NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!"); const uint8_t* buf = (const uint8_t*)aBuffer; VP8StatusCode rv = WebPIAppend(mDecoder, buf, aCount); if (rv == VP8_STATUS_OUT_OF_MEMORY) { PostDecoderError(NS_ERROR_OUT_OF_MEMORY); return; } else if (rv == VP8_STATUS_INVALID_PARAM || rv == VP8_STATUS_BITSTREAM_ERROR) { PostDataError(); return; } else if (rv == VP8_STATUS_UNSUPPORTED_FEATURE || rv == VP8_STATUS_USER_ABORT) { PostDecoderError(NS_ERROR_FAILURE); return; } // Catch any remaining erroneous return value. if (rv != VP8_STATUS_OK && rv != VP8_STATUS_SUSPENDED) { PostDecoderError(NS_ERROR_FAILURE); return; } int lastLineRead = -1; int height = 0; int width = 0; int stride = 0; mData = WebPIDecGetRGB(mDecoder, &lastLineRead, &width, &height, &stride); if (lastLineRead == -1 || !mData) return; if (width <= 0 || height <= 0) { PostDataError(); return; } if (!HasSize()) PostSize(width, height); if (IsSizeDecode()) return; uint32_t imagelength; // First incremental Image data chunk. Special handling required. if (mLastLine == 0 && lastLineRead > 0) { imgFrame* aFrame; nsresult res = mImage.EnsureFrame(0, 0, 0, width, height, gfxASurface::ImageFormatARGB32, (uint8_t**)&mImageData, &imagelength, &aFrame); if (NS_FAILED(res) || !mImageData) { PostDecoderError(NS_ERROR_FAILURE); return; } } if (!mImageData) { PostDecoderError(NS_ERROR_FAILURE); return; } if (lastLineRead > mLastLine) { for (int line = mLastLine; line < lastLineRead; line++) { uint32_t *cptr32 = (uint32_t*)(mImageData + (line * width)); uint8_t *cptr8 = mData + (line * stride); for (int pix = 0; pix < width; pix++, cptr8 += 4) { // if((cptr8[3] != 0) && (cptr8[0] != 0) && (cptr8[1] != 0) && (cptr8[2] != 0)) *cptr32++ = gfxPackedPixel(cptr8[3], cptr8[0], cptr8[1], cptr8[2]); } } // Invalidate nsIntRect r(0, mLastLine, width, lastLineRead); PostInvalidation(r); } mLastLine = lastLineRead; return; }