Index: third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp |
diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp |
index ea896bd0370b078d13b7eb7b5563712d6da8fd1c..1028e9eb230750021f02a389717c1e8ae14d64e5 100644 |
--- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp |
+++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp |
@@ -38,9 +38,7 @@ |
#include "platform/image-decoders/png/PNGImageDecoder.h" |
-#include "platform/image-decoders/png/PNGImageReader.h" |
#include "png.h" |
-#include "wtf/PtrUtil.h" |
#include <memory> |
namespace blink { |
@@ -50,10 +48,149 @@ PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, |
size_t maxDecodedBytes, |
size_t offset) |
: ImageDecoder(alphaOption, colorBehavior, maxDecodedBytes), |
- m_offset(offset) {} |
+ m_reader(this, offset), |
+ m_frameCount(0), |
+ m_currentFrame(0), |
+ m_repetitionCount(cAnimationLoopOnce), |
+ m_hasAlphaChannel(false), |
+ m_currentBufferSawAlpha(false), |
+ m_isParsing(false) {} |
PNGImageDecoder::~PNGImageDecoder() {} |
+bool PNGImageDecoder::setFailed() { |
+ // Update the frame count to make sure it reflects the most up to date number |
+ // of frames that were parsed. We don't want to set the decoder to the failed |
+ // state if the reader was able to successfully parse some frames. |
+ if (m_isParsing) |
+ m_frameCount = m_reader.frameCount(); |
+ |
+ // There are three cases in which the decoder is invalidated: |
+ // 1) No frames are received so far. |
+ // 2) The image is not animated. |
+ // 3) Decoding for the first frame fails. |
+ if (m_frameCount == 0 || (m_reader.parseCompleted() && m_frameCount == 1) || |
+ (!m_isParsing && m_currentFrame == 0)) |
+ return ImageDecoder::setFailed(); |
+ |
+ // If decoding fails for later frames, we still want to show earlier frames, |
+ // but the frame count needs to be adjusted. We do not want to shrink |
+ // |m_frameBufferCache|, since clients may store pointers to later frames. |
+ if (!m_isParsing) { |
+ m_frameBufferCache[m_currentFrame].clearPixelData(); |
+ m_frameBufferCache[m_currentFrame].setStatus(ImageFrame::FrameEmpty); |
+ m_frameCount = m_currentFrame; |
+ } |
+ |
+ // Set parseCompleted to true, so parse() returns early and thus won't update |
+ // |m_frameCount| to the pre-failure value when it is called again. |
+ m_reader.setParseCompleted(); |
+ return false; |
+} |
+ |
+size_t PNGImageDecoder::decodeFrameCount() { |
+ parse(PNGImageReader::PNGParseQuery::PNGMetaDataQuery); |
+ return m_frameCount; |
+} |
+ |
+void PNGImageDecoder::decode(size_t index) { |
+ if (failed()) |
+ return; |
+ |
+ updateAggressivePurging(index); |
+ |
+ Vector<size_t> framesToDecode = findFramesToDecode(index); |
+ for (auto i = framesToDecode.rbegin(); i != framesToDecode.rend(); i++) { |
+ m_currentFrame = *i; |
+ m_reader.decode(*m_data, *i); |
+ if (failed() || !postDecodeProcessing(*i)) |
+ return; |
+ } |
+} |
+ |
+void PNGImageDecoder::clearFrameBuffer(size_t frameIndex) { |
+ m_reader.clearDecodeState(frameIndex); |
+ ImageDecoder::clearFrameBuffer(frameIndex); |
+} |
+ |
+void PNGImageDecoder::onInitFrameBuffer(size_t index) { |
+ unsigned colorChannels = m_hasAlphaChannel ? 4 : 3; |
+ if (PNG_INTERLACE_ADAM7 == |
+ png_get_interlace_type(m_reader.pngPtr(), m_reader.infoPtr())) { |
+ m_reader.createInterlaceBuffer(colorChannels * size().width() * |
+ size().height()); |
+ if (!m_reader.interlaceBuffer()) |
+ setFailed(); |
+ } |
+} |
+ |
+bool PNGImageDecoder::canReusePreviousFrameBuffer(size_t frameIndex) const { |
+ DCHECK(frameIndex < m_frameBufferCache.size()); |
+ return m_frameBufferCache[frameIndex].getDisposalMethod() != |
+ ImageFrame::DisposeOverwritePrevious; |
+} |
+ |
+void PNGImageDecoder::parse(PNGImageReader::PNGParseQuery query) { |
+ if (failed() || m_reader.parseCompleted()) |
+ return; |
+ |
+ m_isParsing = true; |
+ if (!m_reader.parse(*m_data, query) && isAllDataReceived()) |
+ setFailed(); |
+ else if (query == PNGImageReader::PNGParseQuery::PNGMetaDataQuery) |
+ m_frameCount = m_reader.frameCount(); |
+ |
+ m_isParsing = false; |
+} |
+ |
+void PNGImageDecoder::setRepetitionCount(size_t repetitionCount) { |
+ m_repetitionCount = |
+ (repetitionCount == 0) ? cAnimationLoopInfinite : repetitionCount; |
+} |
+ |
+// This matches the existing behavior to loop once if decoding fails, but this |
+// should be changed to stick with m_repetitionCount to match other browsers. |
+// See crbug.com/267883 |
+int PNGImageDecoder::repetitionCount() const { |
+ if (m_reader.parseCompleted() && m_reader.frameCount() == 1) |
+ return cAnimationNone; |
+ return failed() ? cAnimationLoopOnce : m_repetitionCount; |
+} |
+ |
+static inline ImageFrame::DisposalMethod getDisposalMethod( |
+ uint8_t disposalMethod) { |
+ switch (disposalMethod) { |
+ case kAPNGDisposeKeep: |
+ return ImageFrame::DisposalMethod::DisposeKeep; |
+ case kAPNGDisposeBgcolor: |
+ return ImageFrame::DisposalMethod::DisposeOverwriteBgcolor; |
+ case kAPNGDisposePrevious: |
+ return ImageFrame::DisposalMethod::DisposeOverwritePrevious; |
+ default: |
+ return ImageFrame::DisposalMethod::DisposeNotSpecified; |
+ } |
+} |
+ |
+static inline ImageFrame::AlphaBlendSource getAlphaBlend(uint8_t alphaBlend) { |
+ if (alphaBlend == kAPNGAlphaBlendPreviousFrame) |
+ return ImageFrame::AlphaBlendSource::BlendAtopPreviousFrame; |
+ return ImageFrame::AlphaBlendSource::BlendAtopBgcolor; |
+} |
+ |
+void PNGImageDecoder::initializeNewFrame(size_t index) { |
+ const PNGImageReader::FrameInfo& frameInfo = m_reader.frameInfo(index); |
+ ImageFrame* buffer = &m_frameBufferCache[index]; |
+ |
+ IntRect frameRectWithinSize = |
+ intersection(frameInfo.frameRect, {IntPoint(), size()}); |
+ buffer->setOriginalFrameRect(frameRectWithinSize); |
+ buffer->setDuration(frameInfo.duration); |
+ buffer->setDisposalMethod(getDisposalMethod(frameInfo.disposalMethod)); |
+ buffer->setAlphaBlendSource(getAlphaBlend(frameInfo.alphaBlend)); |
+ buffer->setRequiredPreviousFrameIndex( |
+ findRequiredPreviousFrame(index, false)); |
+} |
+ |
inline float pngFixedToFloat(png_fixed_point x) { |
return ((float)x) * 0.00001f; |
} |
@@ -109,24 +246,10 @@ inline sk_sp<SkColorSpace> readColorSpace(png_structp png, png_infop info) { |
} |
void PNGImageDecoder::headerAvailable() { |
- png_structp png = m_reader->pngPtr(); |
- png_infop info = m_reader->infoPtr(); |
- png_uint_32 width = png_get_image_width(png, info); |
- png_uint_32 height = png_get_image_height(png, info); |
- |
- // Protect against large PNGs. See http://bugzil.la/251381 for more details. |
- const unsigned long maxPNGSize = 1000000UL; |
- if (width > maxPNGSize || height > maxPNGSize) { |
- longjmp(JMPBUF(png), 1); |
- return; |
- } |
- |
- // Set the image size now that the image header is available. |
- if (!setSize(width, height)) { |
- longjmp(JMPBUF(png), 1); |
- return; |
- } |
+ png_structp png = m_reader.pngPtr(); |
+ png_infop info = m_reader.infoPtr(); |
+ png_uint_32 width, height; |
int bitDepth, colorType, interlaceType, compressionType, filterType, channels; |
png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, |
&interlaceType, &compressionType, &filterType); |
@@ -152,16 +275,36 @@ void PNGImageDecoder::headerAvailable() { |
colorType == PNG_COLOR_TYPE_GRAY_ALPHA) |
png_set_gray_to_rgb(png); |
- if ((colorType & PNG_COLOR_MASK_COLOR) && !ignoresColorSpace()) { |
- // We only support color profiles for color PALETTE and RGB[A] PNG. |
- // Supporting color profiles for gray-scale images is slightly tricky, at |
- // least using the CoreGraphics ICC library, because we expand gray-scale |
- // images to RGB but we do not similarly transform the color profile. We'd |
- // either need to transform the color profile or we'd need to decode into a |
- // gray-scale image buffer and hand that to CoreGraphics. |
- sk_sp<SkColorSpace> colorSpace = readColorSpace(png, info); |
- if (colorSpace) { |
- setEmbeddedColorSpace(colorSpace); |
+ // Only set the size and the color space of the image once. Since non-first |
+ // frames also use this method, we don't want them to override the size of |
+ // the image to the size of their frame rect. Frames also don't specify their |
+ // own color space, so we need to set it only once. |
+ if (!isDecodedSizeAvailable()) { |
+ // Protect against large PNGs. See http://bugzil.la/251381 for more details. |
+ const unsigned long maxPNGSize = 1000000UL; |
+ if (width > maxPNGSize || height > maxPNGSize) { |
+ longjmp(JMPBUF(png), 1); |
+ return; |
+ } |
+ |
+ // Set the image size now that the image header is available. |
+ if (!setSize(width, height)) { |
+ longjmp(JMPBUF(png), 1); |
+ return; |
+ } |
+ |
+ if ((colorType & PNG_COLOR_MASK_COLOR) && !ignoresColorSpace()) { |
+ // We only support color profiles for color PALETTE and RGB[A] PNG. |
+ // Supporting color profiles for gray-scale images is slightly tricky, at |
+ // least using the CoreGraphics ICC library, because we expand gray-scale |
+ // images to RGB but we do not similarly transform the color profile. We'd |
+ // either need to transform the color profile or we'd need to decode into |
+ // a |
+ // gray-scale image buffer and hand that to CoreGraphics. |
+ sk_sp<SkColorSpace> colorSpace = readColorSpace(png, info); |
+ if (colorSpace) { |
+ setEmbeddedColorSpace(colorSpace); |
+ } |
} |
} |
@@ -199,20 +342,8 @@ void PNGImageDecoder::headerAvailable() { |
channels = png_get_channels(png, info); |
ASSERT(channels == 3 || channels == 4); |
- m_reader->setHasAlpha(channels == 4); |
- |
- if (m_reader->decodingSizeOnly()) { |
-// If we only needed the size, halt the reader. |
-#if PNG_LIBPNG_VER_MAJOR > 1 || \ |
- (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5) |
- // Passing '0' tells png_process_data_pause() not to cache unprocessed data. |
- m_reader->setReadOffset(m_reader->currentBufferSize() - |
- png_process_data_pause(png, 0)); |
-#else |
- m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size); |
- png->buffer_size = 0; |
-#endif |
- } |
+ m_hasAlphaChannel = (channels == 4); |
+ m_currentBufferSawAlpha = false; |
} |
void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, |
@@ -221,88 +352,82 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, |
if (m_frameBufferCache.isEmpty()) |
return; |
- // Initialize the framebuffer if needed. |
- ImageFrame& buffer = m_frameBufferCache[0]; |
- if (buffer.getStatus() == ImageFrame::FrameEmpty) { |
- png_structp png = m_reader->pngPtr(); |
- if (!buffer.setSizeAndColorSpace(size().width(), size().height(), |
- colorSpaceForSkImages())) { |
- longjmp(JMPBUF(png), 1); |
- return; |
- } |
- |
- unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; |
- if (PNG_INTERLACE_ADAM7 == |
- png_get_interlace_type(png, m_reader->infoPtr())) { |
- m_reader->createInterlaceBuffer(colorChannels * size().width() * |
- size().height()); |
- if (!m_reader->interlaceBuffer()) { |
- longjmp(JMPBUF(png), 1); |
- return; |
- } |
- } |
- |
- buffer.setStatus(ImageFrame::FramePartial); |
- buffer.setHasAlpha(false); |
+ // When a client calls ImageDecoder::setMemoryAllocator *before* decoding the |
+ // frame count, the first frame won't get initialized correctly. The call will |
+ // resize |m_frameBufferCache| to 1, and therefore ImageDecoder::frameCount |
+ // will *not* call initializeNewFrame for the first frame, whether it is a |
+ // static image or the first frame of an animated image. Amongst others, the |
+ // frame rect will not be set. If this is the case, initialize the frame here. |
+ if (m_frameBufferCache[0].originalFrameRect().size() == IntSize(0, 0)) |
+ initializeNewFrame(0); |
- // For PNGs, the frame always fills the entire image. |
- buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); |
+ if (!initFrameBuffer(m_currentFrame)) { |
+ setFailed(); |
+ return; |
} |
+ // This frameRect is already clipped, so that it fits within the size of the |
+ // image. This is done in initializeNewFrame() after a frameCount() call. |
+ ImageFrame& buffer = m_frameBufferCache[m_currentFrame]; |
+ const IntRect& frameRect = buffer.originalFrameRect(); |
+ |
/* libpng comments (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. |
- */ |
- |
- // Nothing to do if the row is unchanged, or the row is outside |
- // the image bounds: libpng may send extra rows, ignore them to |
- // make our lives easier. |
+ * |
+ * 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. |
+ */ |
+ |
+ // Nothing to do if the row is unchanged, or the row is outside the image |
+ // bounds. In the case that a frame presents more data than the indicated |
+ // frame size, ignore the extra rows and thus use the frame size as the |
+ // source of truth. libpng may also send extra rows. Ignore those as well. |
+ // This prevents us from trying to write outside of the image bounds. |
if (!rowBuffer) |
return; |
- int y = rowIndex; |
- if (y < 0 || y >= size().height()) |
+ int y = rowIndex + frameRect.y(); |
+ DCHECK_GE(y, 0); |
+ if (y >= size().height()) |
return; |
/* libpng comments (continued). |
- * |
- * 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. |
- */ |
- |
- bool hasAlpha = m_reader->hasAlpha(); |
+ * |
+ * 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. |
+ */ |
+ |
+ bool hasAlpha = m_hasAlphaChannel; |
png_bytep row = rowBuffer; |
- if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { |
+ if (png_bytep interlaceBuffer = m_reader.interlaceBuffer()) { |
unsigned colorChannels = hasAlpha ? 4 : 3; |
row = interlaceBuffer + (rowIndex * colorChannels * size().width()); |
- png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); |
+ png_progressive_combine_row(m_reader.pngPtr(), row, rowBuffer); |
} |
// Write the decoded row pixels to the frame buffer. The repetitive |
// form of the row write loops is for speed. |
- ImageFrame::PixelData* const dstRow = buffer.getAddr(0, y); |
+ ImageFrame::PixelData* const dstRow = buffer.getAddr(frameRect.x(), y); |
unsigned alphaMask = 255; |
- int width = size().width(); |
+ int width = frameRect.width(); |
png_bytep srcPtr = row; |
if (hasAlpha) { |
@@ -324,20 +449,48 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, |
srcPtr = (png_bytep)dstRow; |
} |
- if (buffer.premultiplyAlpha()) { |
- for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
- dstPixel++, srcPtr += 4) { |
- buffer.setRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], |
- srcPtr[3]); |
- alphaMask &= srcPtr[3]; |
+ if (m_frameBufferCache[m_currentFrame].getAlphaBlendSource() == |
+ ImageFrame::BlendAtopBgcolor) { |
+ if (buffer.premultiplyAlpha()) { |
+ for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
+ dstPixel++, srcPtr += 4) { |
+ buffer.setRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], |
+ srcPtr[3]); |
+ alphaMask &= srcPtr[3]; |
+ } |
+ } else { |
+ for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
+ dstPixel++, srcPtr += 4) { |
+ buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], |
+ srcPtr[3]); |
+ alphaMask &= srcPtr[3]; |
+ } |
} |
} else { |
- for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
- dstPixel++, srcPtr += 4) { |
- buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], srcPtr[3]); |
- alphaMask &= srcPtr[3]; |
+ // Now, the blend method is ImageFrame::BlendAtopPreviousFrame. Since the |
+ // frame data of the previous frame is copied at initFrameBuffer, we can |
+ // blend the pixel of this frame, stored in |srcPtr|, over the previous |
+ // pixel stored in |dstPixel|. |
+ if (buffer.premultiplyAlpha()) { |
+ for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
+ dstPixel++, srcPtr += 4) { |
+ buffer.blendRGBAPremultiplied(dstPixel, srcPtr[0], srcPtr[1], |
+ srcPtr[2], srcPtr[3]); |
+ alphaMask &= srcPtr[3]; |
+ } |
+ } else { |
+ for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
+ dstPixel++, srcPtr += 4) { |
+ buffer.blendRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], |
+ srcPtr[3]); |
+ alphaMask &= srcPtr[3]; |
+ } |
} |
} |
+ |
+ if (alphaMask != 255 && !m_currentBufferSawAlpha) |
+ m_currentBufferSawAlpha = true; |
+ |
} else { |
for (auto *dstPixel = dstRow; dstPixel < dstRow + width; |
dstPixel++, srcPtr += 3) { |
@@ -353,38 +506,64 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, |
} |
} |
- if (alphaMask != 255 && !buffer.hasAlpha()) |
- buffer.setHasAlpha(true); |
- |
buffer.setPixelsChanged(true); |
} |
-void PNGImageDecoder::complete() { |
- if (m_frameBufferCache.isEmpty()) |
- return; |
- |
- m_frameBufferCache[0].setStatus(ImageFrame::FrameComplete); |
+bool PNGImageDecoder::frameIsCompleteAtIndex(size_t index) const { |
+ // For non-animated images, return whether the status of the frame is |
+ // ImageFrame::FrameComplete through ImageDecoder::frameIsCompleteAtIndex. |
+ // We only know that the frame is fully received when it is successfully |
+ // decoded, since there is no explicit check whether the IEND chunk has been |
+ // received. |
+ if (index == 0 && m_reader.parseCompleted() && m_frameCount == 1) |
+ return ImageDecoder::frameIsCompleteAtIndex(index); |
+ |
+ // For first frames of animated images, PNGImageReader exposes whether it is |
+ // fully received through firstFrameFullyReceived(). Non-first frames are |
+ // reported by |m_reader| once it has parsed all data for that frame, so we |
+ // can simply return if the index is below the reported frame count. We have |
+ // to use |m_frameCount| here, and not |m_frameBufferCache|.size(), since |
+ // |m_frameCount| gets adjusted when decoding or parsing fails, but |
+ // |m_frameBufferCache| does not, as explained above at |
+ // PNGImageDecoder::setFailed(). |
+ if (index == 0) |
+ return m_reader.firstFrameFullyReceived(); |
+ return (index < m_frameCount); |
} |
-inline bool isComplete(const PNGImageDecoder* decoder) { |
- return decoder->frameIsCompleteAtIndex(0); |
+float PNGImageDecoder::frameDurationAtIndex(size_t index) const { |
+ return (index < m_frameCount ? m_frameBufferCache[index].duration() : 0); |
} |
-void PNGImageDecoder::decode(bool onlySize) { |
- if (failed()) |
+void PNGImageDecoder::complete() { |
+ if (m_frameBufferCache.isEmpty()) |
return; |
- if (!m_reader) |
- m_reader = WTF::makeUnique<PNGImageReader>(this, m_offset); |
- |
- // If we couldn't decode the image but have received all the data, decoding |
- // has failed. |
- if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) |
- setFailed(); |
- |
- // If decoding is done or failed, we don't need the PNGImageReader anymore. |
- if (isComplete(this) || failed()) |
- m_reader.reset(); |
+ // We don't know for sure at this point if we've received enough pixels to |
+ // completely fill the frame. This is not taken into account. The error could |
+ // be in the indicated frame size, or in the frame data chunks. |
+ // |
+ // If the frame size is wrong, we could correct for that, but later frames |
+ // may depend on this frame's size when they're initialized in |
+ // initFrameBuffer(), in the case the disposal method of this frame is |
+ // DisposeOverwriteBgColor. There is no informed decision that can be made |
+ // about what the author intended, so we stay with the indicated frame size. |
+ // |
+ // In the case the data chunk is too small, there's not much we can do. This |
+ // method only gets called when the frame end has been received, so the |
+ // encoder indicated that all frame data is received. It could be an encoding |
+ // error, or it may be an intentional file size reduction. |
+ // |
+ // Therefore, the frame size and pixels that aren't decoded are left as-is. |
+ |
+ if (m_reader.interlaceBuffer()) |
+ m_reader.clearInterlaceBuffer(); |
+ |
+ ImageFrame& buffer = m_frameBufferCache[m_currentFrame]; |
+ buffer.setStatus(ImageFrame::FrameComplete); |
+ |
+ if (!m_currentBufferSawAlpha) |
+ correctAlphaWhenFrameBufferSawNoAlpha(m_currentFrame); |
} |
} // namespace blink |