diff options
author | scroggo <scroggo@chromium.org> | 2016-10-24 12:28:30 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-10-24 12:28:30 -0700 |
commit | 3d3a65c488162ef1db0b35adf3235d012b04c88d (patch) | |
tree | ce515502471340a16ba030b18677ae846356125b /third_party/gif/SkGifImageReader.h | |
parent | d0a3b06ce2155b5455c8827415e4505c328b9003 (diff) |
Rename GIFImageReader to SkGifImageReader
The former could violate One Definition Rule in Google3, since other
projects that are based on Chrome/webkit also have GIFImageReader.
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2445653004
Review-Url: https://codereview.chromium.org/2445653004
Diffstat (limited to 'third_party/gif/SkGifImageReader.h')
-rw-r--r-- | third_party/gif/SkGifImageReader.h | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/third_party/gif/SkGifImageReader.h b/third_party/gif/SkGifImageReader.h new file mode 100644 index 0000000000..5dc0e4f6dd --- /dev/null +++ b/third_party/gif/SkGifImageReader.h @@ -0,0 +1,398 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef SkGifImageReader_h +#define SkGifImageReader_h + +// Define ourselves as the clientPtr. Mozilla just hacked their C++ callback class into this old C decoder, +// so we will too. +class SkGifCodec; + +#include "SkCodec.h" +#include "SkCodecPriv.h" +#include "SkCodecAnimation.h" +#include "SkColorTable.h" +#include "SkData.h" +#include "SkImageInfo.h" +#include "SkStreamBuffer.h" +#include "../private/SkTArray.h" +#include <memory> +#include <vector> + +typedef SkTArray<unsigned char, true> GIFRow; + + +#define MAX_DICTIONARY_ENTRY_BITS 12 +#define MAX_DICTIONARY_ENTRIES 4096 // 2^MAX_DICTIONARY_ENTRY_BITS +#define MAX_COLORS 256 +#define BYTES_PER_COLORMAP_ENTRY 3 + +constexpr int cLoopCountNotSeen = -2; +constexpr size_t kNotFound = static_cast<size_t>(-1); + +// List of possible parsing states. +enum GIFState { + GIFType, + GIFGlobalHeader, + GIFGlobalColormap, + GIFImageStart, + GIFImageHeader, + GIFImageColormap, + GIFImageBody, + GIFLZWStart, + GIFLZW, + GIFSubBlock, + GIFExtension, + GIFControlExtension, + GIFConsumeBlock, + GIFSkipBlock, + GIFDone, + GIFCommentExtension, + GIFApplicationExtension, + GIFNetscapeExtensionBlock, + GIFConsumeNetscapeExtension, + GIFConsumeComment +}; + +struct GIFFrameContext; + +// LZW decoder state machine. +class GIFLZWContext final : public SkNoncopyable { +public: + GIFLZWContext(SkGifCodec* client, const GIFFrameContext* frameContext) + : codesize(0) + , codemask(0) + , clearCode(0) + , avail(0) + , oldcode(0) + , firstchar(0) + , bits(0) + , datum(0) + , ipass(0) + , irow(0) + , rowsRemaining(0) + , rowIter(0) + , m_client(client) + , m_frameContext(frameContext) + { } + + bool prepareToDecode(); + bool outputRow(const unsigned char* rowBegin); + bool doLZW(const unsigned char* block, size_t bytesInBlock); + bool hasRemainingRows() { return rowsRemaining; } + +private: + // LZW decoding states and output states. + int codesize; + int codemask; + int clearCode; // Codeword used to trigger dictionary reset. + int avail; // Index of next available slot in dictionary. + int oldcode; + unsigned char firstchar; + int bits; // Number of unread bits in "datum". + int datum; // 32-bit input buffer. + int ipass; // Interlace pass; Ranges 1-4 if interlaced. + size_t irow; // Current output row, starting at zero. + size_t rowsRemaining; // Rows remaining to be output. + + unsigned short prefix[MAX_DICTIONARY_ENTRIES]; + unsigned char suffix[MAX_DICTIONARY_ENTRIES]; + unsigned short suffixLength[MAX_DICTIONARY_ENTRIES]; + GIFRow rowBuffer; // Single scanline temporary buffer. + unsigned char* rowIter; + + SkGifCodec* const m_client; + const GIFFrameContext* m_frameContext; +}; + +class GIFColorMap final { +public: + GIFColorMap() + : m_isDefined(false) + , m_colors(0) + , m_packColorProc(nullptr) + { + } + + void setNumColors(size_t colors) { + m_colors = colors; + } + + size_t numColors() const { return m_colors; } + + void setRawData(const char* data, size_t size) + { + // FIXME: Can we avoid this copy? + m_rawData = SkData::MakeWithCopy(data, size); + SkASSERT(m_colors * BYTES_PER_COLORMAP_ENTRY == size); + m_isDefined = true; + } + bool isDefined() const { return m_isDefined; } + + // Build RGBA table using the data stream. + sk_sp<SkColorTable> buildTable(SkColorType dstColorType, size_t transparentPixel) const; + +private: + bool m_isDefined; + size_t m_colors; + sk_sp<SkData> m_rawData; + mutable PackColorProc m_packColorProc; + mutable sk_sp<SkColorTable> m_table; +}; + +// LocalFrame output state machine. +struct GIFFrameContext : SkNoncopyable { +public: + GIFFrameContext(int id) + : m_frameId(id) + , m_xOffset(0) + , m_yOffset(0) + , m_width(0) + , m_height(0) + , m_transparentPixel(kNotFound) + , m_disposalMethod(SkCodecAnimation::Keep_DisposalMethod) + , m_requiredFrame(SkCodec::kNone) + , m_dataSize(0) + , m_progressiveDisplay(false) + , m_interlaced(false) + , m_delayTime(0) + , m_currentLzwBlock(0) + , m_isComplete(false) + , m_isHeaderDefined(false) + , m_isDataSizeDefined(false) + { + } + + ~GIFFrameContext() + { + } + + void addLzwBlock(const void* data, size_t size) + { + m_lzwBlocks.push_back(SkData::MakeWithCopy(data, size)); + } + + bool decode(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; } + size_t transparentPixel() const { return m_transparentPixel; } + void setTransparentPixel(size_t pixel) { m_transparentPixel = pixel; } + SkCodecAnimation::DisposalMethod getDisposalMethod() const { return m_disposalMethod; } + void setDisposalMethod(SkCodecAnimation::DisposalMethod disposalMethod) { m_disposalMethod = disposalMethod; } + size_t getRequiredFrame() const { return m_requiredFrame; } + void setRequiredFrame(size_t req) { m_requiredFrame = req; } + unsigned delayTime() const { return m_delayTime; } + void setDelayTime(unsigned delay) { m_delayTime = delay; } + bool isComplete() const { return m_isComplete; } + void setComplete() { m_isComplete = true; } + bool isHeaderDefined() const { return m_isHeaderDefined; } + void setHeaderDefined() { m_isHeaderDefined = true; } + bool isDataSizeDefined() const { return m_isDataSizeDefined; } + int dataSize() const { return m_dataSize; } + void setDataSize(int size) + { + m_dataSize = size; + m_isDataSizeDefined = true; + } + bool progressiveDisplay() const { return m_progressiveDisplay; } + void setProgressiveDisplay(bool progressiveDisplay) { m_progressiveDisplay = progressiveDisplay; } + bool interlaced() const { return m_interlaced; } + void setInterlaced(bool interlaced) { m_interlaced = interlaced; } + + void clearDecodeState() { m_lzwContext.reset(); } + const GIFColorMap& localColorMap() const { return m_localColorMap; } + GIFColorMap& localColorMap() { return m_localColorMap; } + +private: + int m_frameId; + unsigned m_xOffset; + unsigned m_yOffset; // With respect to "screen" origin. + unsigned m_width; + unsigned m_height; + size_t m_transparentPixel; // Index of transparent pixel. Value is kNotFound if there is no transparent pixel. + SkCodecAnimation::DisposalMethod m_disposalMethod; // Restore to background, leave in place, etc. + size_t m_requiredFrame; + int m_dataSize; + + bool m_progressiveDisplay; // If true, do Haeberli interlace hack. + bool m_interlaced; // True, if scanlines arrive interlaced order. + + unsigned m_delayTime; // Display time, in milliseconds, for this image in a multi-image GIF. + + std::unique_ptr<GIFLZWContext> m_lzwContext; + std::vector<sk_sp<SkData>> m_lzwBlocks; // LZW blocks for this frame. + GIFColorMap m_localColorMap; + + size_t m_currentLzwBlock; + bool m_isComplete; + bool m_isHeaderDefined; + bool m_isDataSizeDefined; +}; + +class SkGifImageReader final : public SkNoncopyable { +public: + // This takes ownership of stream. + SkGifImageReader(SkStream* stream) + : m_client(nullptr) + , m_state(GIFType) + , 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) + , m_firstFrameHasAlpha(false) + , m_firstFrameSupportsIndex8(false) + { + } + + ~SkGifImageReader() + { + } + + 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 GIFParseQuery { + // Parse enough to determine the size. Note that this parses the first frame's header, + // since we may decide to expand based on the frame's dimensions. + GIFSizeQuery = -1, + // Parse to the end, so we know about all frames. + GIFFrameCountQuery = -2, + }; + + // Parse incoming GIF data stream into internal data structures. + // Non-negative values are used to indicate to parse through that frame. + // Return true if parsing has progressed or there is not enough data. + // Return false if a fatal error is encountered. + bool parse(GIFParseQuery); + + // Decode the frame indicated by frameIndex. + // frameComplete will be set to true if the frame is completely decoded. + // The method returns false if there is an error. + bool decode(size_t frameIndex, bool* frameComplete); + + size_t imagesCount() const + { + if (m_frames.empty()) + return 0; + + // This avoids counting an empty frame when the file is truncated right after + // GIFControlExtension but before GIFImageHeader. + // FIXME: This extra complexity is not necessary and we should just report m_frames.size(). + return m_frames.back()->isHeaderDefined() ? m_frames.size() : m_frames.size() - 1; + } + int loopCount() const { return m_loopCount; } + + const GIFColorMap& globalColorMap() const + { + return m_globalColorMap; + } + + const GIFFrameContext* frameContext(size_t index) const + { + return index < m_frames.size() ? m_frames[index].get() : 0; + } + + void clearDecodeState() { + for (size_t index = 0; index < m_frames.size(); index++) { + m_frames[index]->clearDecodeState(); + } + } + + // Return the color table for frame index (which may be the global color table). + sk_sp<SkColorTable> getColorTable(SkColorType dstColorType, size_t index) const; + + bool firstFrameHasAlpha() const { return m_firstFrameHasAlpha; } + + bool firstFrameSupportsIndex8() const { return m_firstFrameSupportsIndex8; } + +private: + // Requires that one byte has been buffered into m_streamBuffer. + unsigned char getOneByte() const { + return reinterpret_cast<const unsigned char*>(m_streamBuffer.get())[0]; + } + + void addFrameIfNecessary(); + bool currentFrameIsFirstFrame() const + { + return m_frames.empty() || (m_frames.size() == 1u && !m_frames[0]->isComplete()); + } + + // Unowned pointer + SkGifCodec* m_client; + + // Parsing state machine. + GIFState m_state; // Current decoder master state. + size_t m_bytesToConsume; // Number of bytes to consume for next stage of parsing. + + // 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; + GIFColorMap m_globalColorMap; + int m_loopCount; // Netscape specific extension block to control the number of animation loops a GIF renders. + + std::vector<std::unique_ptr<GIFFrameContext>> m_frames; + + SkStreamBuffer m_streamBuffer; + bool m_parseCompleted; + + // These values can be computed before we create a GIFFrameContext, so we + // store them here instead of on m_frames[0]. + bool m_firstFrameHasAlpha; + bool m_firstFrameSupportsIndex8; +}; + +#endif |