summaryrefslogtreecommitdiff
path: root/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp')
-rw-r--r--Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp150
1 files changed, 79 insertions, 71 deletions
diff --git a/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp b/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp
index 07304769c..f8c9893ce 100644
--- a/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp
+++ b/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -28,14 +28,11 @@
#include "GIFImageReader.h"
#include <limits>
-#include <wtf/PassOwnPtr.h>
namespace WebCore {
-GIFImageDecoder::GIFImageDecoder(ImageSource::AlphaOption alphaOption,
- ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
+GIFImageDecoder::GIFImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption)
: ImageDecoder(alphaOption, gammaAndColorProfileOption)
- , m_repetitionCount(cAnimationLoopOnce)
{
}
@@ -43,14 +40,14 @@ GIFImageDecoder::~GIFImageDecoder()
{
}
-void GIFImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
+void GIFImageDecoder::setData(SharedBuffer& data, bool allDataReceived)
{
if (failed())
return;
ImageDecoder::setData(data, allDataReceived);
if (m_reader)
- m_reader->setData(data);
+ m_reader->setData(&data);
}
bool GIFImageDecoder::isSizeAvailable()
@@ -61,25 +58,25 @@ bool GIFImageDecoder::isSizeAvailable()
return ImageDecoder::isSizeAvailable();
}
-bool GIFImageDecoder::setSize(unsigned width, unsigned height)
+bool GIFImageDecoder::setSize(const IntSize& size)
{
- if (ImageDecoder::isSizeAvailable() && size() == IntSize(width, height))
+ if (ImageDecoder::isSizeAvailable() && this->size() == size)
return true;
- if (!ImageDecoder::setSize(width, height))
+ if (!ImageDecoder::setSize(size))
return false;
prepareScaleDataIfNecessary();
return true;
}
-size_t GIFImageDecoder::frameCount()
+size_t GIFImageDecoder::frameCount() const
{
- decode(std::numeric_limits<unsigned>::max(), GIFFrameCountQuery);
+ const_cast<GIFImageDecoder*>(this)->decode(std::numeric_limits<unsigned>::max(), GIFFrameCountQuery);
return m_frameBufferCache.size();
}
-int GIFImageDecoder::repetitionCount() const
+RepetitionCount GIFImageDecoder::repetitionCount() const
{
// This value can arrive at any point in the image data stream. Most GIFs
// in the wild declare it near the beginning of the file, so it usually is
@@ -106,7 +103,7 @@ int GIFImageDecoder::repetitionCount() const
// later in the stream. It is also possible that no frames are in the
// stream. In these cases we should just loop once.
if (failed() || (m_reader && (!m_reader->imagesCount())))
- m_repetitionCount = cAnimationLoopOnce;
+ m_repetitionCount = RepetitionCountOnce;
else if (m_reader && m_reader->loopCount() != cLoopCountNotSeen)
m_repetitionCount = m_reader->loopCount();
return m_repetitionCount;
@@ -118,14 +115,14 @@ ImageFrame* GIFImageDecoder::frameBufferAtIndex(size_t index)
return 0;
ImageFrame& frame = m_frameBufferCache[index];
- if (frame.status() != ImageFrame::FrameComplete)
+ if (!frame.isComplete())
decode(index + 1, GIFFullQuery);
return &frame;
}
bool GIFImageDecoder::setFailed()
{
- m_reader.clear();
+ m_reader = nullptr;
return ImageDecoder::setFailed();
}
@@ -136,6 +133,10 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame)
if (m_frameBufferCache.isEmpty())
return; // Nothing to do.
+ // Lock the decodelock here, as we are going to destroy the GIFImageReader and doing so while
+ // there's an ongoing decode will cause a crash.
+ LockHolder locker(m_decodeLock);
+
// The "-1" here is tricky. It does not mean that |clearBeforeFrame| is the
// last frame we wish to preserve, but rather that we never want to clear
// the very last frame in the cache: it's empty (so clearing it is
@@ -157,26 +158,30 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame)
// not to have non-empty frames after the frame we're currently decoding.
// So, scan backwards from |end| as follows:
// * If the frame is empty, we're still past any frames we care about.
- // * If the frame is complete, but is DisposeOverwritePrevious, we'll
+ // * If the frame is complete, but is DisposalMethod::RestoreToPrevious, we'll
// skip over it in future initFrameBuffer() calls. We can clear it
// unless it's |end|, and keep scanning. For any other disposal method,
// stop scanning, as we've found the frame initFrameBuffer() will need
// next.
// * If the frame is partial, we're decoding it, so don't clear it; if it
- // has a disposal method other than DisposeOverwritePrevious, stop
+ // has a disposal method other than DisposalMethod::RestoreToPrevious, stop
// scanning, as we'll only need this frame when decoding the next one.
Vector<ImageFrame>::iterator i(end);
- for (; (i != m_frameBufferCache.begin()) && ((i->status() == ImageFrame::FrameEmpty) || (i->disposalMethod() == ImageFrame::DisposeOverwritePrevious)); --i) {
- if ((i->status() == ImageFrame::FrameComplete) && (i != end))
- i->clearPixelData();
+ for (; (i != m_frameBufferCache.begin()) && (i->isEmpty() || (i->disposalMethod() == ImageFrame::DisposalMethod::RestoreToPrevious)); --i) {
+ if (i->isComplete() && (i != end))
+ i->clear();
}
// Now |i| holds the last frame we need to preserve; clear prior frames.
for (Vector<ImageFrame>::iterator j(m_frameBufferCache.begin()); j != i; ++j) {
- ASSERT(j->status() != ImageFrame::FramePartial);
- if (j->status() != ImageFrame::FrameEmpty)
- j->clearPixelData();
+ ASSERT(!j->isPartial());
+ if (j->isEmpty())
+ j->clear();
}
+
+ // When some frames are cleared, the reader is out of sync, since the caller might ask for any frame not
+ // necessarily in the order expected by the reader. See https://bugs.webkit.org/show_bug.cgi?id=159089.
+ m_reader = nullptr;
}
bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, const Vector<unsigned char>& rowBuffer, size_t width, size_t rowNumber, unsigned repeatCount, bool writeTransparentPixels)
@@ -210,16 +215,16 @@ bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, const Vector<unsigned
// Initialize the frame if necessary.
ImageFrame& buffer = m_frameBufferCache[frameIndex];
- if (((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameIndex)) || !buffer.hasPixelData())
+ if ((buffer.isEmpty() && !initFrameBuffer(frameIndex)) || !buffer.hasBackingStore())
return false;
- ImageFrame::PixelData* currentAddress = buffer.getAddr(xBegin, yBegin);
+ RGBA32* currentAddress = buffer.backingStore()->pixelAt(xBegin, yBegin);
// Write one row's worth of data into the frame.
for (int x = xBegin; x < xEnd; ++x) {
const unsigned char sourceValue = rowBuffer[(m_scaled ? m_scaledColumns[x] : x) - frameContext->xOffset];
if ((!frameContext->isTransparent || (sourceValue != frameContext->tpixel)) && (sourceValue < colorMapSize)) {
const size_t colorIndex = static_cast<size_t>(sourceValue) * 3;
- buffer.setRGBA(currentAddress, colorMap[colorIndex], colorMap[colorIndex + 1], colorMap[colorIndex + 2], 255);
+ buffer.backingStore()->setPixel(currentAddress, colorMap[colorIndex], colorMap[colorIndex + 1], colorMap[colorIndex + 2], 255);
} else {
m_currentBufferSawAlpha = true;
// We may or may not need to write transparent pixels to the buffer.
@@ -230,34 +235,36 @@ bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, const Vector<unsigned
// beyond the first, or the initial passes will "show through" the
// later ones.
if (writeTransparentPixels)
- buffer.setRGBA(currentAddress, 0, 0, 0, 0);
+ buffer.backingStore()->setPixel(currentAddress, 0, 0, 0, 0);
}
++currentAddress;
}
// Tell the frame to copy the row data if need be.
if (repeatCount > 1)
- buffer.copyRowNTimes(xBegin, xEnd, yBegin, yEnd);
+ buffer.backingStore()->repeatFirstRow(IntRect(xBegin, yBegin, xEnd - xBegin , yEnd - yBegin));
return true;
}
-bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, ImageFrame::FrameDisposalMethod disposalMethod)
+bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, ImageFrame::DisposalMethod disposalMethod)
{
// Initialize the frame if necessary. Some GIFs insert do-nothing frames,
// in which case we never reach haveDecodedRow() before getting here.
ImageFrame& buffer = m_frameBufferCache[frameIndex];
- if ((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameIndex))
+ if (buffer.isEmpty() && !initFrameBuffer(frameIndex))
return false; // initFrameBuffer() has already called setFailed().
- buffer.setStatus(ImageFrame::FrameComplete);
+ buffer.setDecoding(ImageFrame::Decoding::Complete);
buffer.setDuration(frameDuration);
buffer.setDisposalMethod(disposalMethod);
if (!m_currentBufferSawAlpha) {
+ IntRect rect = buffer.backingStore()->frameRect();
+
// The whole frame was non-transparent, so it's possible that the entire
// resulting buffer was non-transparent, and we can setHasAlpha(false).
- if (buffer.originalFrameRect().contains(IntRect(IntPoint(), scaledSize())))
+ if (rect.contains(IntRect(IntPoint(), scaledSize())))
buffer.setHasAlpha(false);
else if (frameIndex) {
// Tricky case. This frame does not have alpha only if everywhere
@@ -265,22 +272,23 @@ bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration,
// true, we check the start state of the frame -- if it doesn't have
// alpha, we're safe.
//
- // First skip over prior DisposeOverwritePrevious frames (since they
+ // First skip over prior DisposalMethod::RestoreToPrevious frames (since they
// don't affect the start state of this frame) the same way we do in
// initFrameBuffer().
const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex];
- while (frameIndex && (prevBuffer->disposalMethod() == ImageFrame::DisposeOverwritePrevious))
+ while (frameIndex && (prevBuffer->disposalMethod() == ImageFrame::DisposalMethod::RestoreToPrevious))
prevBuffer = &m_frameBufferCache[--frameIndex];
- // Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then
+ // Now, if we're at a DisposalMethod::Unspecified or DisposalMethod::DoNotDispose frame, then
// we can say we have no alpha if that frame had no alpha. But
// since in initFrameBuffer() we already copied that frame's alpha
// state into the current frame's, we need do nothing at all here.
//
- // The only remaining case is a DisposeOverwriteBgcolor frame. If
+ // The only remaining case is a DisposalMethod::RestoreToBackground frame. If
// it had no alpha, and its rect is contained in the current frame's
// rect, we know the current frame has no alpha.
- if ((prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) && !prevBuffer->hasAlpha() && buffer.originalFrameRect().contains(prevBuffer->originalFrameRect()))
+ IntRect prevRect = prevBuffer->backingStore()->frameRect();
+ if ((prevBuffer->disposalMethod() == ImageFrame::DisposalMethod::RestoreToBackground) && !prevBuffer->hasAlpha() && rect.contains(prevRect))
buffer.setHasAlpha(false);
}
}
@@ -294,7 +302,7 @@ void GIFImageDecoder::gifComplete()
// going to be.
repetitionCount();
- m_reader.clear();
+ m_reader = nullptr;
}
void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query)
@@ -302,9 +310,10 @@ void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query)
if (failed())
return;
+ LockHolder locker(m_decodeLock);
if (!m_reader) {
- m_reader = adoptPtr(new GIFImageReader(this));
- m_reader->setData(m_data);
+ m_reader = std::make_unique<GIFImageReader>(this);
+ m_reader->setData(m_data.get());
}
if (query == GIFSizeQuery) {
@@ -318,10 +327,7 @@ void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query)
return;
}
- const size_t oldSize = m_frameBufferCache.size();
m_frameBufferCache.resize(m_reader->imagesCount());
- for (size_t i = oldSize; i < m_reader->imagesCount(); ++i)
- m_frameBufferCache[i].setPremultiplyAlpha(m_premultiplyAlpha);
if (query == GIFFrameCountQuery)
return;
@@ -342,66 +348,68 @@ bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex)
// Initialize the frame rect in our buffer.
const GIFFrameContext* frameContext = m_reader->frameContext();
IntRect frameRect(frameContext->xOffset, frameContext->yOffset, frameContext->width, frameContext->height);
-
- // Make sure the frameRect doesn't extend outside the buffer.
- if (frameRect.maxX() > size().width())
- frameRect.setWidth(size().width() - frameContext->xOffset);
- if (frameRect.maxY() > size().height())
- frameRect.setHeight(size().height() - frameContext->yOffset);
-
ImageFrame* const buffer = &m_frameBufferCache[frameIndex];
- int left = upperBoundScaledX(frameRect.x());
- int right = lowerBoundScaledX(frameRect.maxX(), left);
- int top = upperBoundScaledY(frameRect.y());
- int bottom = lowerBoundScaledY(frameRect.maxY(), top);
- buffer->setOriginalFrameRect(IntRect(left, top, right - left, bottom - top));
if (!frameIndex) {
// This is the first frame, so we're not relying on any previous data.
- if (!buffer->setSize(scaledSize().width(), scaledSize().height()))
+ if (!buffer->initialize(scaledSize(), m_premultiplyAlpha))
return setFailed();
} else {
// The starting state for this frame depends on the previous frame's
// disposal method.
//
- // Frames that use the DisposeOverwritePrevious method are effectively
+ // Frames that use the DisposalMethod::RestoreToPrevious method are effectively
// no-ops in terms of changing the starting state of a frame compared to
// the starting state of the previous frame, so skip over them. (If the
// first frame specifies this method, it will get treated like
- // DisposeOverwriteBgcolor below and reset to a completely empty image.)
+ // DisposalMethod::RestoreToBackground below and reset to a completely empty image.)
const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex];
- ImageFrame::FrameDisposalMethod prevMethod = prevBuffer->disposalMethod();
- while (frameIndex && (prevMethod == ImageFrame::DisposeOverwritePrevious)) {
+ ImageFrame::DisposalMethod prevMethod = prevBuffer->disposalMethod();
+ while (frameIndex && (prevMethod == ImageFrame::DisposalMethod::RestoreToPrevious)) {
prevBuffer = &m_frameBufferCache[--frameIndex];
prevMethod = prevBuffer->disposalMethod();
}
- ASSERT(prevBuffer->status() == ImageFrame::FrameComplete);
- if ((prevMethod == ImageFrame::DisposeNotSpecified) || (prevMethod == ImageFrame::DisposeKeep)) {
+ ASSERT(prevBuffer->isComplete());
+
+ if ((prevMethod == ImageFrame::DisposalMethod::Unspecified) || (prevMethod == ImageFrame::DisposalMethod::DoNotDispose)) {
// Preserve the last frame as the starting state for this frame.
- if (!buffer->copyBitmapData(*prevBuffer))
+ if (!prevBuffer->backingStore() || !buffer->initialize(*prevBuffer->backingStore()))
return setFailed();
} else {
// We want to clear the previous frame to transparent, without
// affecting pixels in the image outside of the frame.
- const IntRect& prevRect = prevBuffer->originalFrameRect();
+ IntRect prevRect = prevBuffer->backingStore()->frameRect();
const IntSize& bufferSize = scaledSize();
if (!frameIndex || prevRect.contains(IntRect(IntPoint(), scaledSize()))) {
// Clearing the first frame, or a frame the size of the whole
// image, results in a completely empty image.
- if (!buffer->setSize(bufferSize.width(), bufferSize.height()))
+ if (!buffer->initialize(bufferSize, m_premultiplyAlpha))
return setFailed();
} else {
// Copy the whole previous buffer, then clear just its frame.
- if (!buffer->copyBitmapData(*prevBuffer))
+ if (!prevBuffer->backingStore() || !buffer->initialize(*prevBuffer->backingStore()))
return setFailed();
- buffer->zeroFillFrameRect(prevRect);
+ buffer->backingStore()->clearRect(prevRect);
+ buffer->setHasAlpha(true);
}
}
}
+ // Make sure the frameRect doesn't extend outside the buffer.
+ if (frameRect.maxX() > size().width())
+ frameRect.setWidth(size().width() - frameContext->xOffset);
+ if (frameRect.maxY() > size().height())
+ frameRect.setHeight(size().height() - frameContext->yOffset);
+
+ int left = upperBoundScaledX(frameRect.x());
+ int right = lowerBoundScaledX(frameRect.maxX(), left);
+ int top = upperBoundScaledY(frameRect.y());
+ int bottom = lowerBoundScaledY(frameRect.maxY(), top);
+ buffer->backingStore()->setFrameRect(IntRect(left, top, right - left, bottom - top));
+
// Update our status to be partially complete.
- buffer->setStatus(ImageFrame::FramePartial);
+ buffer->setDecoding(ImageFrame::Decoding::Partial);
// Reset the alpha pixel tracker for this frame.
m_currentBufferSawAlpha = false;