diff options
Diffstat (limited to 'third_party/gif')
-rw-r--r-- | third_party/gif/SkGifImageReader.cpp | 116 | ||||
-rw-r--r-- | third_party/gif/SkGifImageReader.h | 86 |
2 files changed, 81 insertions, 121 deletions
diff --git a/third_party/gif/SkGifImageReader.cpp b/third_party/gif/SkGifImageReader.cpp index 8c5cfa1ea8..0666fedbc8 100644 --- a/third_party/gif/SkGifImageReader.cpp +++ b/third_party/gif/SkGifImageReader.cpp @@ -131,19 +131,19 @@ bool SkGIFLZWContext::outputRow(const unsigned char* rowBegin) drowEnd = drowStart + rowDup; // Extend if bottom edge isn't covered because of the shift upward. - if (((m_frameContext->height() - 1) - drowEnd) <= rowShift) + if ((unsigned)((m_frameContext->height() - 1) - drowEnd) <= rowShift) drowEnd = m_frameContext->height() - 1; // Clamp first and last rows to upper and lower edge of image. if (drowStart < 0) drowStart = 0; - if ((unsigned)drowEnd >= m_frameContext->height()) + if (drowEnd >= m_frameContext->height()) drowEnd = m_frameContext->height() - 1; } // Protect against too much image data. - if ((unsigned)drowStart >= m_frameContext->height()) + if (drowStart >= m_frameContext->height()) return true; // CALLBACK: Let the client know we have decoded a row. @@ -159,7 +159,7 @@ bool SkGIFLZWContext::outputRow(const unsigned char* rowBegin) switch (ipass) { case 1: irow += 8; - if (irow >= m_frameContext->height()) { + if (irow >= (unsigned) m_frameContext->height()) { ipass++; irow = 4; } @@ -167,7 +167,7 @@ bool SkGIFLZWContext::outputRow(const unsigned char* rowBegin) case 2: irow += 8; - if (irow >= m_frameContext->height()) { + if (irow >= (unsigned) m_frameContext->height()) { ipass++; irow = 2; } @@ -175,7 +175,7 @@ bool SkGIFLZWContext::outputRow(const unsigned char* rowBegin) case 3: irow += 4; - if (irow >= m_frameContext->height()) { + if (irow >= (unsigned) m_frameContext->height()) { ipass++; irow = 1; } @@ -183,7 +183,7 @@ bool SkGIFLZWContext::outputRow(const unsigned char* rowBegin) case 4: irow += 2; - if (irow >= m_frameContext->height()) { + if (irow >= (unsigned) m_frameContext->height()) { ipass++; irow = 0; } @@ -192,7 +192,7 @@ bool SkGIFLZWContext::outputRow(const unsigned char* rowBegin) default: break; } - } while (irow > (m_frameContext->height() - 1)); + } while (irow > (unsigned) (m_frameContext->height() - 1)); } return true; } @@ -491,8 +491,8 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query) // screen. // Note that we don't inform the client of the size yet, as it might // change after we read the first frame's image header. - m_screenWidth = GETINT16(currentComponent); - m_screenHeight = GETINT16(currentComponent + 2); + fScreenWidth = GETINT16(currentComponent); + fScreenHeight = GETINT16(currentComponent + 2); const int globalColorMapColors = 2 << (currentComponent[4] & 0x07); @@ -623,7 +623,7 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query) currentFrame->setDisposalMethod(SkCodecAnimation::Keep_DisposalMethod); break; } - currentFrame->setDelayTime(GETINT16(currentComponent + 1) * 10); + currentFrame->setDuration(GETINT16(currentComponent + 1) * 10); GETN(1, SkGIFConsumeBlock); break; } @@ -706,7 +706,7 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query) } case SkGIFImageHeader: { - unsigned height, width, xOffset, yOffset; + int height, width, xOffset, yOffset; const unsigned char* currentComponent = reinterpret_cast<const unsigned char*>(m_streamBuffer.get()); @@ -730,8 +730,8 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query) // set to zero, since usually the first frame completely fills // the image. if (currentFrameIsFirstFrame()) { - m_screenHeight = std::max(m_screenHeight, yOffset + height); - m_screenWidth = std::max(m_screenWidth, xOffset + width); + fScreenHeight = std::max(fScreenHeight, yOffset + height); + fScreenWidth = std::max(fScreenWidth, xOffset + width); } // NOTE: Chromium placed this block after setHeaderDefined, down @@ -743,8 +743,8 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query) // Work around more broken GIF files that have zero image width or // height. if (!height || !width) { - height = m_screenHeight; - width = m_screenWidth; + height = fScreenHeight; + width = fScreenWidth; if (!height || !width) { // This prevents attempting to continue reading this invalid stream. GETN(0, SkGIFDone); @@ -756,13 +756,17 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query) // The three low-order bits of currentComponent[8] specify the bits per pixel. const int numColors = 2 << (currentComponent[8] & 0x7); if (currentFrameIsFirstFrame()) { - if (hasTransparentPixel(0, isLocalColormapDefined, numColors)) { + const int transPix = m_frames.empty() ? SkGIFColorMap::kNotFound + : m_frames[0]->transparentPixel(); + if (this->hasTransparency(transPix, + isLocalColormapDefined, numColors)) + { m_firstFrameHasAlpha = true; m_firstFrameSupportsIndex8 = true; } else { const bool frameIsSubset = xOffset > 0 || yOffset > 0 - || xOffset + width < m_screenWidth - || yOffset + height < m_screenHeight; + || width < fScreenWidth + || height < fScreenHeight; m_firstFrameHasAlpha = frameIsSubset; m_firstFrameSupportsIndex8 = !frameIsSubset; } @@ -780,7 +784,7 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query) } - currentFrame->setRect(xOffset, yOffset, width, height); + currentFrame->setXYWH(xOffset, yOffset, width, height); currentFrame->setInterlaced(SkToBool(currentComponent[8] & 0x40)); // Overlaying interlaced, transparent GIFs over @@ -852,20 +856,14 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query) return true; } -bool SkGifImageReader::hasTransparentPixel(int i, bool isLocalColormapDefined, - int localColors) { - SkASSERT(i >= 0); - if (m_frames.size() <= static_cast<size_t>(i)) { - // This should only happen when parsing the first frame. - SkASSERT(0 == i); - - // We did not see a Graphics Control Extension, so no transparent - // pixel was specified. But if there is no color table, this frame is - // still transparent. - return !isLocalColormapDefined && m_globalColorMap.numColors() == 0; +bool SkGifImageReader::hasTransparency(int transparentPixel, bool isLocalColormapDefined, + int localColors) const { + const int globalColors = m_globalColorMap.numColors(); + if (!isLocalColormapDefined && globalColors == 0) { + // No color table for this frame, so it is completely transparent. + return true; } - const int transparentPixel = m_frames[i]->transparentPixel(); if (transparentPixel < 0) { SkASSERT(SkGIFColorMap::kNotFound == transparentPixel); return false; @@ -875,16 +873,9 @@ bool SkGifImageReader::hasTransparentPixel(int i, bool isLocalColormapDefined, return transparentPixel < localColors; } - const int globalColors = m_globalColorMap.numColors(); - if (!globalColors) { - // No color table for this frame, so the frame is empty. - // This is technically different from having a transparent - // pixel, but we'll treat it the same - nothing to draw here. - return true; - } - // If there is a global color table, it will be parsed before reaching // here. If its numColors is set, it will be defined. + SkASSERT(globalColors > 0); SkASSERT(m_globalColorMap.isDefined()); return transparentPixel < globalColors; } @@ -893,7 +884,7 @@ void SkGifImageReader::addFrameIfNecessary() { if (m_frames.empty() || m_frames.back()->isComplete()) { const size_t i = m_frames.size(); - std::unique_ptr<SkGIFFrameContext> frame(new SkGIFFrameContext(static_cast<int>(i))); + std::unique_ptr<SkGIFFrameContext> frame(new SkGIFFrameContext(this, static_cast<int>(i))); m_frames.push_back(std::move(frame)); } } @@ -907,38 +898,43 @@ static SkIRect frame_rect_on_screen(SkIRect frameRect, return frameRect; } -static bool independent(const SkGIFFrameContext& frame) { +static bool independent(const SkFrame& frame) { return frame.getRequiredFrame() == SkCodec::kNone; } -static bool restore_bg(const SkGIFFrameContext& frame) { +static bool restore_bg(const SkFrame& frame) { return frame.getDisposalMethod() == SkCodecAnimation::RestoreBGColor_DisposalMethod; } -void SkGifImageReader::setAlphaAndRequiredFrame(SkGIFFrameContext* frame) { +bool SkGIFFrameContext::onReportsAlpha() const { + // Note: We could correct these after decoding - i.e. some frames may turn out to be + // independent and opaque if they do not use the transparent pixel, but that would require + // checking whether each pixel used the transparent index. + return m_owner->hasTransparency(this->transparentPixel(), + m_localColorMap.isDefined(), m_localColorMap.numColors()); +} + +void SkFrameHolder::setAlphaAndRequiredFrame(SkFrame* frame) { + const bool reportsAlpha = frame->reportsAlpha(); + const auto screenRect = SkIRect::MakeWH(fScreenWidth, fScreenHeight); + const auto frameRect = frame_rect_on_screen(frame->frameRect(), screenRect); + const int i = frame->frameId(); if (0 == i) { - frame->setHasAlpha(m_firstFrameHasAlpha); + frame->setHasAlpha(reportsAlpha || frameRect != screenRect); frame->setRequiredFrame(SkCodec::kNone); return; } - // Note: We could correct these after decoding - i.e. some frames may turn out to be - // independent and opaque if they do not use the transparent pixel, but that would require - // checking whether each pixel used the transparent index. - const SkGIFColorMap& localMap = frame->localColorMap(); - const bool transValid = hasTransparentPixel(i, localMap.isDefined(), localMap.numColors()); - - const auto screenRect = SkIRect::MakeWH(m_screenWidth, m_screenHeight); - const auto frameRect = frame_rect_on_screen(frame->frameRect(), screenRect); - if (!transValid && frameRect == screenRect) { - frame->setHasAlpha(false); + const bool blendWithPrevFrame = frame->getBlend() == SkCodecAnimation::Blend::kPriorFrame; + if ((!reportsAlpha || !blendWithPrevFrame) && frameRect == screenRect) { + frame->setHasAlpha(reportsAlpha); frame->setRequiredFrame(SkCodec::kNone); return; } - const SkGIFFrameContext* prevFrame = m_frames[i - 1].get(); + const SkFrame* prevFrame = this->getFrame(i-1); while (prevFrame->getDisposalMethod() == SkCodecAnimation::RestorePrevious_DisposalMethod) { const int prevId = prevFrame->frameId(); if (0 == prevId) { @@ -947,7 +943,7 @@ void SkGifImageReader::setAlphaAndRequiredFrame(SkGIFFrameContext* frame) { return; } - prevFrame = m_frames[prevId - 1].get(); + prevFrame = this->getFrame(prevId - 1); } const bool clearPrevFrame = restore_bg(*prevFrame); @@ -961,7 +957,7 @@ void SkGifImageReader::setAlphaAndRequiredFrame(SkGIFFrameContext* frame) { } } - if (transValid) { + if (reportsAlpha && blendWithPrevFrame) { // Note: We could be more aggressive here. If prevFrame clears // to background color and covers its required frame (and that // frame is independent), prevFrame could be marked independent. @@ -979,7 +975,7 @@ void SkGifImageReader::setAlphaAndRequiredFrame(SkGIFFrameContext* frame) { return; } - prevFrame = m_frames[prevRequiredFrame].get(); + prevFrame = this->getFrame(prevRequiredFrame); prevFrameRect = frame_rect_on_screen(prevFrame->frameRect(), screenRect); } @@ -998,7 +994,7 @@ void SkGifImageReader::setAlphaAndRequiredFrame(SkGIFFrameContext* frame) { SkASSERT(prevFrame->getDisposalMethod() == SkCodecAnimation::Keep_DisposalMethod); frame->setRequiredFrame(prevFrame->frameId()); - frame->setHasAlpha(prevFrame->hasAlpha()); + frame->setHasAlpha(prevFrame->hasAlpha() || (reportsAlpha && !blendWithPrevFrame)); } // FIXME: Move this method to close to doLZW(). diff --git a/third_party/gif/SkGifImageReader.h b/third_party/gif/SkGifImageReader.h index 4667f7963f..6c313e923b 100644 --- a/third_party/gif/SkGifImageReader.h +++ b/third_party/gif/SkGifImageReader.h @@ -47,6 +47,7 @@ class SkGifCodec; #include "SkCodecAnimation.h" #include "SkColorTable.h" #include "SkData.h" +#include "SkFrameHolder.h" #include "SkImageInfo.h" #include "SkStreamBuffer.h" #include "../private/SkTArray.h" @@ -85,7 +86,7 @@ enum SkGIFState { SkGIFConsumeComment }; -struct SkGIFFrameContext; +class SkGIFFrameContext; class SkGIFColorMap; // LZW decoder state machine. @@ -191,19 +192,15 @@ private: mutable sk_sp<SkColorTable> m_table; }; +class SkGifImageReader; + // LocalFrame output state machine. -struct SkGIFFrameContext : SkNoncopyable { +class SkGIFFrameContext : public SkFrame { public: - SkGIFFrameContext(int id) - : m_frameId(id) - , m_xOffset(0) - , m_yOffset(0) - , m_width(0) - , m_height(0) + SkGIFFrameContext(SkGifImageReader* reader, int id) + : INHERITED(id) + , m_owner(reader) , m_transparentPixel(SkGIFColorMap::kNotFound) - , m_hasAlpha(false) - , m_disposalMethod(SkCodecAnimation::Keep_DisposalMethod) - , m_requiredFrame(kUninitialized) , m_dataSize(0) , m_progressiveDisplay(false) , m_interlaced(false) @@ -226,31 +223,8 @@ public: bool decode(SkStreamBuffer*, SkGifCodec* client, bool* frameDecoded); - int frameId() const { return m_frameId; } - void setRect(unsigned x, unsigned y, unsigned width, unsigned height) - { - m_xOffset = x; - m_yOffset = y; - m_width = width; - m_height = height; - } - SkIRect frameRect() const { return SkIRect::MakeXYWH(m_xOffset, m_yOffset, m_width, m_height); } - unsigned xOffset() const { return m_xOffset; } - unsigned yOffset() const { return m_yOffset; } - unsigned width() const { return m_width; } - unsigned height() const { return m_height; } int transparentPixel() const { return m_transparentPixel; } void setTransparentPixel(int pixel) { m_transparentPixel = pixel; } - bool hasAlpha() const { return m_hasAlpha; } - void setHasAlpha(bool alpha) { m_hasAlpha = alpha; } - SkCodecAnimation::DisposalMethod getDisposalMethod() const { return m_disposalMethod; } - void setDisposalMethod(SkCodecAnimation::DisposalMethod disposalMethod) { m_disposalMethod = disposalMethod; } - - int getRequiredFrame() const { - SkASSERT(this->reachedStartOfData()); - return m_requiredFrame; - } - void setRequiredFrame(int req) { m_requiredFrame = req; } unsigned delayTime() const { return m_delayTime; } void setDelayTime(unsigned delay) { m_delayTime = delay; } @@ -274,24 +248,14 @@ public: const SkGIFColorMap& localColorMap() const { return m_localColorMap; } SkGIFColorMap& localColorMap() { return m_localColorMap; } - bool reachedStartOfData() const { return m_requiredFrame != kUninitialized; } +protected: + bool onReportsAlpha() const override; private: - static constexpr int kUninitialized = -2; + // Unowned pointer to the object that owns this frame. + const SkGifImageReader* m_owner; - int m_frameId; - unsigned m_xOffset; - unsigned m_yOffset; // With respect to "screen" origin. - unsigned m_width; - unsigned m_height; int m_transparentPixel; // Index of transparent pixel. Value is kNotFound if there is no transparent pixel. - // Cached value, taking into account: - // - m_transparentPixel - // - frameRect - // - previous required frame - bool m_hasAlpha; - SkCodecAnimation::DisposalMethod m_disposalMethod; // Restore to background, leave in place, etc. - int m_requiredFrame; int m_dataSize; bool m_progressiveDisplay; // If true, do Haeberli interlace hack. @@ -309,9 +273,11 @@ private: bool m_isComplete; bool m_isHeaderDefined; bool m_isDataSizeDefined; + + typedef SkFrame INHERITED; }; -class SkGifImageReader final : public SkNoncopyable { +class SkGifImageReader final : public SkFrameHolder { public: // This takes ownership of stream. SkGifImageReader(SkStream* stream) @@ -319,8 +285,6 @@ public: , m_state(SkGIFType) , m_bytesToConsume(6) // Number of bytes for GIF type, either "GIF87a" or "GIF89a". , m_version(0) - , m_screenWidth(0) - , m_screenHeight(0) , m_loopCount(cLoopCountNotSeen) , m_streamBuffer(stream) , m_parseCompleted(false) @@ -335,9 +299,6 @@ public: void setClient(SkGifCodec* client) { m_client = client; } - unsigned screenWidth() const { return m_screenWidth; } - unsigned screenHeight() const { return m_screenHeight; } - // Option to pass to parse(). All enums are negative, because a non-negative value is used to // indicate that the Reader should parse up to and including the frame indicated. enum SkGIFParseQuery { @@ -408,6 +369,16 @@ public: bool firstFrameSupportsIndex8() const { return m_firstFrameSupportsIndex8; } + // Helper function that returns whether an SkGIFFrameContext has transparency. + // This method is sometimes called before creating one/parsing its color map, + // so it cannot rely on SkGIFFrameContext::transparentPixel or ::localColorMap(). + bool hasTransparency(int transPix, bool hasLocalColorMap, int localMapColors) const; + +protected: + const SkFrame* onGetFrame(int i) const override { + return static_cast<const SkFrame*>(this->frameContext(i)); + } + private: // Requires that one byte has been buffered into m_streamBuffer. unsigned char getOneByte() const { @@ -415,11 +386,6 @@ private: } void addFrameIfNecessary(); - // Must be called *after* the SkGIFFrameContext's color table (if any) has been parsed. - void setAlphaAndRequiredFrame(SkGIFFrameContext*); - // This method is sometimes called before creating a SkGIFFrameContext, so it cannot rely - // on SkGIFFrameContext::localColorMap(). - bool hasTransparentPixel(int frameIndex, bool hasLocalColorMap, int localMapColors); bool currentFrameIsFirstFrame() const { return m_frames.empty() || (m_frames.size() == 1u && !m_frames[0]->isComplete()); @@ -434,8 +400,6 @@ private: // Global (multi-image) state. int m_version; // Either 89 for GIF89 or 87 for GIF87. - unsigned m_screenWidth; // Logical screen width & height. - unsigned m_screenHeight; SkGIFColorMap m_globalColorMap; static constexpr int cLoopCountNotSeen = -2; |