author | Joe Drew <joe@drew.ca> |
Mon, 28 Jan 2013 12:27:35 -0500 | |
changeset 125633 | 682938749810c2effd0aede42b7a25c7a055133b |
parent 125632 | c86eb1ff89168e43b8668fc41f3e22f3c0eb5fbd |
child 125634 | 16cd97be284693480058cddb76a9d73f9272cc02 |
push id | 24461 |
push user | emorley@mozilla.com |
push date | Thu, 21 Mar 2013 11:51:51 +0000 |
treeherder | mozilla-central@a73a2b5c423b [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 716140 |
milestone | 22.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/image/decoders/GIF2.h +++ b/image/decoders/GIF2.h @@ -3,17 +3,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef _GIF_H_ #define _GIF_H_ #define MAX_LZW_BITS 12 #define MAX_BITS 4097 /* 2^MAX_LZW_BITS+1 */ #define MAX_COLORS 256 -#define MAX_HOLD_SIZE 256 +#define MIN_HOLD_SIZE 256 enum { GIF_TRAILER = 0x3B }; //';' enum { GIF_IMAGE_SEPARATOR = 0x2C }; //',' enum { GIF_EXTENSION_INTRODUCER = 0x21 }; //'!' enum { GIF_GRAPHIC_CONTROL_LABEL = 0xF9 }; enum { GIF_COMMENT_LABEL = 0xFE }; enum { GIF_PLAIN_TEXT_LABEL = 0x01 }; enum { GIF_APPLICATION_EXTENSION_LABEL = 0xFF }; @@ -23,16 +23,17 @@ enum { GIF_APPLICATION_EXTENSION_LABEL = */ // List of possible parsing states typedef enum { gif_type, gif_global_header, gif_global_colormap, gif_image_start, gif_image_header, + gif_image_header_continue, gif_image_colormap, gif_image_body, gif_lzw_start, gif_lzw, gif_sub_block, gif_extension, gif_control_extension, gif_consume_block, @@ -90,17 +91,17 @@ typedef struct gif_struct { int loop_count; /* Netscape specific extension block to control the number of animation loops a GIF renders. */ bool progressive_display; /* If TRUE, do Haeberli interlace hack */ bool interlaced; /* TRUE, if scanlines arrive interlaced order */ bool is_transparent; /* TRUE, if tpixel is valid */ uint16_t prefix[MAX_BITS]; /* LZW decoding tables */ - uint8_t hold[MAX_HOLD_SIZE]; /* Accumulation buffer */ + uint8_t* hold; /* Accumulation buffer */ uint32_t global_colormap[MAX_COLORS]; /* Default colormap if local not supplied */ uint8_t suffix[MAX_BITS]; /* LZW decoding tables */ uint8_t stack[MAX_BITS]; /* Base of LZW decoder stack */ } gif_struct; #endif
--- a/image/decoders/nsGIFDecoder2.cpp +++ b/image/decoders/nsGIFDecoder2.cpp @@ -50,18 +50,16 @@ mailing address. #include <algorithm> namespace mozilla { namespace image { /* * GETN(n, s) requests at least 'n' bytes available from 'q', at start of state 's' * - * Note, the hold will never need to be bigger than 256 bytes to gather up in the hold, - * as each GIF block (except colormaps) can never be bigger than 256 bytes. * Colormaps are directly copied in the resp. global_colormap or the local_colormap of the PAL image frame * So a fixed buffer in gif_struct is good enough. * This buffer is only needed to copy left-over data from one GifWrite call to the next */ #define GETN(n,s) \ PR_BEGIN_MACRO \ mGIFStruct.bytes_to_consume = (n); \ mGIFStruct.state = (s); \ @@ -91,19 +89,18 @@ nsGIFDecoder2::nsGIFDecoder2(RasterImage // Start with the version (GIF89a|GIF87a) mGIFStruct.state = gif_type; mGIFStruct.bytes_to_consume = 6; } nsGIFDecoder2::~nsGIFDecoder2() { - if (mGIFStruct.local_colormap) { - moz_free(mGIFStruct.local_colormap); - } + moz_free(mGIFStruct.local_colormap); + moz_free(mGIFStruct.hold); } void nsGIFDecoder2::FinishInternal() { NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call FinishInternal after error!"); // If the GIF got cut off, handle it anyway @@ -159,71 +156,39 @@ void nsGIFDecoder2::BeginGIF() PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height); // If we're doing a size decode, we have what we came for if (IsSizeDecode()) return; } //****************************************************************************** -nsresult nsGIFDecoder2::BeginImageFrame(uint16_t aDepth) +void nsGIFDecoder2::BeginImageFrame(uint16_t aDepth) { - uint32_t imageDataLength; - nsresult rv; gfxASurface::gfxImageFormat format; if (mGIFStruct.is_transparent) format = gfxASurface::ImageFormatARGB32; else format = gfxASurface::ImageFormatRGB24; // Use correct format, RGB for first frame, PAL for following frames // and include transparency to allow for optimization of opaque images if (mGIFStruct.images_decoded) { // Image data is stored with original depth and palette - rv = mImage.EnsureFrame(mGIFStruct.images_decoded, - mGIFStruct.x_offset, mGIFStruct.y_offset, - mGIFStruct.width, mGIFStruct.height, - format, aDepth, &mImageData, &imageDataLength, - &mColormap, &mColormapSize); - - // While EnsureFrame can reuse frames, we unconditionally increment - // mGIFStruct.images_decoded when we're done with a frame, so we both can - // and need to zero out the colormap and image data after every call to - // EnsureFrame. - if (NS_SUCCEEDED(rv) && mColormap) { - memset(mColormap, 0, mColormapSize); - } + NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset, + mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height, + format, aDepth); } else { // Regardless of depth of input, image is decoded into 24bit RGB - rv = mImage.EnsureFrame(mGIFStruct.images_decoded, - mGIFStruct.x_offset, mGIFStruct.y_offset, - mGIFStruct.width, mGIFStruct.height, - format, &mImageData, &imageDataLength); - } - - if (NS_FAILED(rv)) - return rv; - - memset(mImageData, 0, imageDataLength); - - // Tell the superclass we're starting a frame - PostFrameStart(); - - if (!mGIFStruct.images_decoded) { - // Send a onetime invalidation for the first frame if it has a y-axis offset. - // Otherwise, the area may never be refreshed and the placeholder will remain - // on the screen. (Bug 37589) - if (mGIFStruct.y_offset > 0) { - nsIntRect r(0, 0, mGIFStruct.screen_width, mGIFStruct.y_offset); - PostInvalidation(r); - } + NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset, + mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height, + format); } mCurrentFrame = mGIFStruct.images_decoded; - return NS_OK; } //****************************************************************************** void nsGIFDecoder2::EndImageFrame() { RasterImage::FrameAlpha alpha = RasterImage::kFrameHasAlpha; @@ -580,43 +545,47 @@ nsGIFDecoder2::WriteInternal(const char const uint8_t *q = buf; // Add what we have sofar to the block // If previous call to me left something in the hold first complete current block // Or if we are filling the colormaps, first complete the colormap uint8_t* p = (mGIFStruct.state == gif_global_colormap) ? (uint8_t*)mGIFStruct.global_colormap : (mGIFStruct.state == gif_image_colormap) ? (uint8_t*)mColormap : (mGIFStruct.bytes_in_hold) ? mGIFStruct.hold : nullptr; - if (p) { + + if (len == 0 && buf == nullptr) { + // We've just gotten the frame we asked for. Time to use the data we + // stashed away. + len = mGIFStruct.bytes_in_hold; + q = buf = p; + } else if (p) { // Add what we have sofar to the block uint32_t l = std::min(len, mGIFStruct.bytes_to_consume); memcpy(p+mGIFStruct.bytes_in_hold, buf, l); if (l < mGIFStruct.bytes_to_consume) { // Not enough in 'buf' to complete current block, get more mGIFStruct.bytes_in_hold += l; mGIFStruct.bytes_to_consume -= l; return; } - // Reset hold buffer count - mGIFStruct.bytes_in_hold = 0; // Point 'q' to complete block in hold (or in colormap) q = p; } // Invariant: // 'q' is start of current to be processed block (hold, colormap or buf) // 'bytes_to_consume' is number of bytes to consume from 'buf' // 'buf' points to the bytes to be consumed from the input buffer // 'len' is number of bytes left in input buffer from position 'buf'. // At entrance of the for loop will 'buf' will be moved 'bytes_to_consume' // to point to next buffer, 'len' is adjusted accordingly. // So that next round in for loop, q gets pointed to the next buffer. - for (;len >= mGIFStruct.bytes_to_consume; q=buf) { + for (;len >= mGIFStruct.bytes_to_consume; q=buf, mGIFStruct.bytes_in_hold = 0) { // Eat the current block from the buffer, q keeps pointed at current block buf += mGIFStruct.bytes_to_consume; len -= mGIFStruct.bytes_to_consume; switch (mGIFStruct.state) { case gif_lzw: if (!DoLzw(q)) { @@ -921,20 +890,50 @@ nsGIFDecoder2::WriteInternal(const char if (q[8] & 0x80) depth = (q[8]&0x07) + 1; uint32_t realDepth = depth; while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) { realDepth++; } // Mask to limit the color values within the colormap mColorMask = 0xFF >> (8 - realDepth); - nsresult rv = BeginImageFrame(realDepth); - if (NS_FAILED(rv) || !mImageData) { - mGIFStruct.state = gif_error; - break; + BeginImageFrame(realDepth); + + // We now need a new frame from the decoder framework. We leave all our + // data in the buffer as if it wasn't consumed, copy to our hold and return + // to the decoder framework. + uint32_t size = len + mGIFStruct.bytes_to_consume + mGIFStruct.bytes_in_hold; + if (size) { + if (SetHold(q, mGIFStruct.bytes_to_consume + mGIFStruct.bytes_in_hold, buf, len)) { + // Back into the decoder infrastructure so we can get called again. + GETN(9, gif_image_header_continue); + return; + } + } + } + break; + + case gif_image_header_continue: + { + // While decoders can reuse frames, we unconditionally increment + // mGIFStruct.images_decoded when we're done with a frame, so we both can + // and need to zero out the colormap and image data after every new frame. + memset(mImageData, 0, mImageDataLength); + if (mColormap) { + memset(mColormap, 0, mColormapSize); + } + + if (!mGIFStruct.images_decoded) { + // Send a onetime invalidation for the first frame if it has a y-axis offset. + // Otherwise, the area may never be refreshed and the placeholder will remain + // on the screen. (Bug 37589) + if (mGIFStruct.y_offset > 0) { + nsIntRect r(0, 0, mGIFStruct.screen_width, mGIFStruct.y_offset); + PostInvalidation(r); + } } if (q[8] & 0x40) { mGIFStruct.interlaced = true; mGIFStruct.ipass = 1; } else { mGIFStruct.interlaced = false; mGIFStruct.ipass = 0; @@ -943,17 +942,26 @@ nsGIFDecoder2::WriteInternal(const char /* Only apply the Haeberli display hack on the first frame */ mGIFStruct.progressive_display = (mGIFStruct.images_decoded == 0); /* Clear state from last image */ mGIFStruct.irow = 0; mGIFStruct.rows_remaining = mGIFStruct.height; mGIFStruct.rowp = mImageData; - /* bits per pixel is q[8]&0x07 */ + /* Depth of colors is determined by colormap */ + /* (q[8] & 0x80) indicates local colormap */ + /* bits per pixel is (q[8]&0x07 + 1) when local colormap is set */ + uint32_t depth = mGIFStruct.global_colormap_depth; + if (q[8] & 0x80) + depth = (q[8]&0x07) + 1; + uint32_t realDepth = depth; + while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) { + realDepth++; + } if (q[8] & 0x80) /* has a local colormap? */ { mGIFStruct.local_colormap_size = 1 << depth; if (!mGIFStruct.images_decoded) { // First frame has local colormap, allocate space for it // as the image frame doesn't have its own palette mColormapSize = sizeof(uint32_t) << realDepth; @@ -1022,17 +1030,17 @@ nsGIFDecoder2::WriteInternal(const char } else { /* See if there are any more images in this sequence. */ EndImageFrame(); GETN(1, gif_image_start); } break; case gif_done: - PostDecodeDone(); + PostDecodeDone(mGIFStruct.loop_count - 1); mGIFOpen = false; goto done; case gif_error: PostDataError(); return; // We shouldn't ever get here. @@ -1041,39 +1049,67 @@ nsGIFDecoder2::WriteInternal(const char } } // if an error state is set but no data remains, code flow reaches here if (mGIFStruct.state == gif_error) { PostDataError(); return; } - + // Copy the leftover into mGIFStruct.hold - mGIFStruct.bytes_in_hold = len; if (len) { // Add what we have sofar to the block - uint8_t* p = (mGIFStruct.state == gif_global_colormap) ? (uint8_t*)mGIFStruct.global_colormap : - (mGIFStruct.state == gif_image_colormap) ? (uint8_t*)mColormap : - mGIFStruct.hold; - memcpy(p, buf, len); + if (mGIFStruct.state != gif_global_colormap && mGIFStruct.state != gif_image_colormap) { + if (!SetHold(buf, len)) { + PostDataError(); + return; + } + } else { + uint8_t* p = (mGIFStruct.state == gif_global_colormap) ? (uint8_t*)mGIFStruct.global_colormap : + (uint8_t*)mColormap; + memcpy(p, buf, len); + mGIFStruct.bytes_in_hold = len; + } + mGIFStruct.bytes_to_consume -= len; } // We want to flush before returning if we're on the first frame done: if (!mGIFStruct.images_decoded) { FlushImageData(); mLastFlushedRow = mCurrentRow; mLastFlushedPass = mCurrentPass; } return; } +bool +nsGIFDecoder2::SetHold(const uint8_t* buf1, uint32_t count1, const uint8_t* buf2 /* = nullptr */, uint32_t count2 /* = 0 */) +{ + // We have to handle the case that buf currently points to hold + uint8_t* newHold = (uint8_t *) moz_malloc(std::max(uint32_t(MIN_HOLD_SIZE), count1 + count2)); + if (!newHold) { + mGIFStruct.state = gif_error; + return false; + } + + memcpy(newHold, buf1, count1); + if (buf2) { + memcpy(newHold + count1, buf2, count2); + } + + moz_free(mGIFStruct.hold); + mGIFStruct.hold = newHold; + mGIFStruct.bytes_in_hold = count1 + count2; + return true; +} + Telemetry::ID nsGIFDecoder2::SpeedHistogram() { return Telemetry::IMAGE_DECODE_SPEED_GIF; } } // namespace image
--- a/image/decoders/nsGIFDecoder2.h +++ b/image/decoders/nsGIFDecoder2.h @@ -30,24 +30,26 @@ public: virtual void FinishInternal(); virtual Telemetry::ID SpeedHistogram(); private: /* These functions will be called when the decoder has a decoded row, * frame size information, etc. */ void BeginGIF(); - nsresult BeginImageFrame(uint16_t aDepth); + void BeginImageFrame(uint16_t aDepth); void EndImageFrame(); void FlushImageData(); void FlushImageData(uint32_t fromRow, uint32_t rows); nsresult GifWrite(const uint8_t * buf, uint32_t numbytes); uint32_t OutputRow(); bool DoLzw(const uint8_t *q); + bool SetHold(const uint8_t* buf, uint32_t count, + const uint8_t* buf2 = nullptr, uint32_t count2 = 0); inline int ClearCode() const { return 1 << mGIFStruct.datasize; } int32_t mCurrentRow; int32_t mLastFlushedRow; uint32_t mOldColor; // The old value of the transparent pixel
--- a/image/decoders/nsPNGDecoder.cpp +++ b/image/decoders/nsPNGDecoder.cpp @@ -143,31 +143,23 @@ nsPNGDecoder::~nsPNGDecoder() nsMemory::Free(mHeaderBuf); } // CreateFrame() is used for both simple and animated images void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset, int32_t width, int32_t height, gfxASurface::gfxImageFormat format) { - uint32_t imageDataLength; - nsresult rv = mImage.EnsureFrame(GetFrameCount(), x_offset, y_offset, - width, height, format, - &mImageData, &imageDataLength); - if (NS_FAILED(rv)) - longjmp(png_jmpbuf(mPNG), 5); // NS_ERROR_OUT_OF_MEMORY + NeedNewFrame(GetFrameCount(), x_offset, y_offset, width, height, format); mFrameRect.x = x_offset; mFrameRect.y = y_offset; mFrameRect.width = width; mFrameRect.height = height; - // Tell the superclass we're starting a frame - PostFrameStart(); - PR_LOG(GetPNGDecoderAccountingLog(), PR_LOG_DEBUG, ("PNGDecoderAccounting: nsPNGDecoder::CreateFrame -- created " "image frame with %dx%d pixels in container %p", width, height, &mImage)); mFrameHasNoAlpha = true; } @@ -287,20 +279,16 @@ nsPNGDecoder::InitInternal() nsPNGDecoder::row_callback, nsPNGDecoder::end_callback); } void nsPNGDecoder::WriteInternal(const char *aBuffer, uint32_t aCount) { - // We use gotos, so we need to declare variables here - uint32_t width = 0; - uint32_t height = 0; - NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!"); // If we only want width/height, we don't need to go through libpng if (IsSizeDecode()) { // Are we done? if (mHeaderBytesRead == BYTES_NEEDED_FOR_DIMENSIONS) return; @@ -317,18 +305,18 @@ nsPNGDecoder::WriteInternal(const char * // Check that the signature bytes are right if (memcmp(mHeaderBuf, nsPNGDecoder::pngSignatureBytes, sizeof(pngSignatureBytes))) { PostDataError(); return; } // Grab the width and height, accounting for endianness (thanks libpng!) - width = png_get_uint_32(mHeaderBuf + WIDTH_OFFSET); - height = png_get_uint_32(mHeaderBuf + HEIGHT_OFFSET); + uint32_t width = png_get_uint_32(mHeaderBuf + WIDTH_OFFSET); + uint32_t height = png_get_uint_32(mHeaderBuf + HEIGHT_OFFSET); // Too big? if ((width > MOZ_PNG_MAX_DIMENSION) || (height > MOZ_PNG_MAX_DIMENSION)) { PostDataError(); return; } // Post our size to the superclass @@ -663,17 +651,22 @@ nsPNGDecoder::info_callback(png_structp /* Reject any ancillary chunk after IDAT with a bad CRC (bug #397593). * It would be better to show the default frame (if one has already been * successfully decoded) before bailing, but it's simpler to just bail * out with an error message. */ png_set_crc_action(png_ptr, PNG_CRC_NO_CHANGE, PNG_CRC_ERROR_QUIT); - return; + if (!decoder->mFrameIsHidden) { + /* We know that we need a new frame, so pause input so the decoder + * infrastructure can give it to us. + */ + png_process_data_pause(png_ptr, /* save = */ 1); + } } void nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) { /* libpng comments: * @@ -824,16 +817,21 @@ nsPNGDecoder::frame_info_callback(png_st decoder->mFrameIsHidden = false; x_offset = png_get_next_frame_x_offset(png_ptr, decoder->mInfo); y_offset = png_get_next_frame_y_offset(png_ptr, decoder->mInfo); width = png_get_next_frame_width(png_ptr, decoder->mInfo); height = png_get_next_frame_height(png_ptr, decoder->mInfo); decoder->CreateFrame(x_offset, y_offset, width, height, decoder->format); + + /* We know that we need a new frame, so pause input so the decoder + * infrastructure can give it to us. + */ + png_process_data_pause(png_ptr, /* save = */ 1); #endif } void nsPNGDecoder::end_callback(png_structp png_ptr, png_infop info_ptr) { /* libpng comments: *
--- a/image/decoders/nsPNGDecoder.h +++ b/image/decoders/nsPNGDecoder.h @@ -54,17 +54,18 @@ public: png_height; // Unused int png_bit_depth, png_color_type; if (png_get_IHDR(mPNG, mInfo, &png_width, &png_height, &png_bit_depth, &png_color_type, NULL, NULL, NULL)) { - return (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA && + return ((png_color_type == PNG_COLOR_TYPE_RGB_ALPHA || + png_color_type == PNG_COLOR_TYPE_RGB) && png_bit_depth == 8); } else { return false; } } public: png_structp mPNG;
--- a/image/src/Decoder.cpp +++ b/image/src/Decoder.cpp @@ -17,16 +17,17 @@ Decoder::Decoder(RasterImage &aImage) : mImage(aImage) , mImageData(nullptr) , mColormap(nullptr) , mDecodeFlags(0) , mDecodeDone(false) , mDataError(false) , mFrameCount(0) , mFailCode(NS_OK) + , mNeedsNewFrame(false) , mInitialized(false) , mSizeDecode(false) , mInFrame(false) , mIsAnimated(false) { } Decoder::~Decoder() @@ -77,16 +78,50 @@ Decoder::Write(const char* aBuffer, uint "Not allowed to make more decoder calls after error!"); // If a data error occured, just ignore future data if (HasDataError()) return; // Pass the data along to the implementation WriteInternal(aBuffer, aCount); + + // If the decoder told us that it needs a new frame to proceed, let's create + // one and call it again. + while (mNeedsNewFrame && !HasDataError()) { + nsresult rv; + if (mNewFrameData.mPaletteDepth) { + rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX, + mNewFrameData.mOffsetY, mNewFrameData.mWidth, + mNewFrameData.mHeight, mNewFrameData.mFormat, + mNewFrameData.mPaletteDepth, + &mImageData, &mImageDataLength, + &mColormap, &mColormapSize); + } else { + rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX, + mNewFrameData.mOffsetY, mNewFrameData.mWidth, + mNewFrameData.mHeight, mNewFrameData.mFormat, + &mImageData, &mImageDataLength); + } + + // Release our new frame data before talking to anyone else so they can + // tell us if they need yet another. + mNeedsNewFrame = false; + + if (NS_SUCCEEDED(rv)) { + // We've now created our frame, so be sure we keep track of it correctly. + PostFrameStart(); + + // Tell the decoder to use the data it saved when it asked for a new frame. + WriteInternal(nullptr, 0); + } else { + PostDataError(); + break; + } + } } void Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent) { // Implementation-specific finalization if (!HasError()) FinishInternal(); @@ -312,10 +347,26 @@ Decoder::PostDecoderError(nsresult aFail mFailCode = aFailureCode; // XXXbholley - we should report the image URI here, but imgContainer // needs to know its URI first NS_WARNING("Image decoding error - This is probably a bug!"); } +void +Decoder::NeedNewFrame(uint32_t framenum, uint32_t x_offset, uint32_t y_offset, + uint32_t width, uint32_t height, + gfxASurface::gfxImageFormat format, + uint8_t palette_depth /* = 0 */) +{ + // Decoders should never call NeedNewFrame without yielding back to Write(). + MOZ_ASSERT(!mNeedsNewFrame); + + // We don't want images going back in time or skipping frames. + MOZ_ASSERT(framenum == mFrameCount || framenum == (mFrameCount + 1)); + + mNewFrameData = NewFrameData(framenum, x_offset, y_offset, width, height, format, palette_depth); + mNeedsNewFrame = true; +} + } // namespace image } // namespace mozilla
--- a/image/src/Decoder.h +++ b/image/src/Decoder.h @@ -179,16 +179,25 @@ protected: // For animated images, specify the loop count. -1 means loop forever, 0 // means a single iteration, stopping on the last frame. void PostDecodeDone(int32_t aLoopCount = 0); // Data errors are the fault of the source data, decoder errors are our fault void PostDataError(); void PostDecoderError(nsresult aFailCode); + // This is called by decoders when they need a new frame. These decoders + // must then save the data they have been sent but not yet processed and + // return from WriteInternal. When the new frame is created, WriteInternal + // will be called again with nullptr and 0 as arguments. + void NeedNewFrame(uint32_t frameNum, uint32_t x_offset, uint32_t y_offset, + uint32_t width, uint32_t height, + gfxASurface::gfxImageFormat format, + uint8_t palette_depth = 0); + /* * Member variables. * */ RasterImage &mImage; RefPtr<imgDecoderObserver> mObserver; ImageMetadata mImageMetadata; @@ -203,16 +212,42 @@ protected: private: uint32_t mFrameCount; // Number of frames, including anything in-progress nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame. nsresult mFailCode; + struct NewFrameData + { + NewFrameData() + {} + + NewFrameData(uint32_t num, uint32_t offsetx, uint32_t offsety, + uint32_t width, uint32_t height, + gfxASurface::gfxImageFormat format, uint8_t paletteDepth) + : mFrameNum(num) + , mOffsetX(offsetx) + , mOffsetY(offsety) + , mWidth(width) + , mHeight(height) + , mFormat(format) + , mPaletteDepth(paletteDepth) + {} + uint32_t mFrameNum; + uint32_t mOffsetX; + uint32_t mOffsetY; + uint32_t mWidth; + uint32_t mHeight; + gfxASurface::gfxImageFormat mFormat; + uint8_t mPaletteDepth; + }; + NewFrameData mNewFrameData; + bool mNeedsNewFrame; bool mInitialized; bool mSizeDecode; bool mInFrame; bool mIsAnimated; }; } // namespace image } // namespace mozilla
--- a/layout/media/symbols.def.in +++ b/layout/media/symbols.def.in @@ -205,16 +205,17 @@ MOZ_PNG_get_IHDR MOZ_PNG_get_iCCP MOZ_PNG_get_io_ptr MOZ_PNG_get_progressive_ptr MOZ_PNG_get_sRGB MOZ_PNG_get_tRNS MOZ_PNG_get_valid MOZ_PNG_longjmp MOZ_PNG_process_data +MOZ_PNG_process_data_pause MOZ_PNG_progressive_combine_row MOZ_PNG_read_update_info MOZ_PNG_set_cHRM MOZ_PNG_set_crc_action MOZ_PNG_set_gAMA MOZ_PNG_set_gamma MOZ_PNG_set_gray_to_rgb MOZ_PNG_set_expand