123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533 |
- /*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- /*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * 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
- * 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
- * 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
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include "include/codec/SkCodecAnimation.h"
- #include "include/core/SkStream.h"
- #include "include/private/SkColorData.h"
- #include "src/codec/SkCodecPriv.h"
- #include "src/codec/SkColorTable.h"
- #include "src/codec/SkGifCodec.h"
- #include "src/codec/SkSwizzler.h"
- #include "src/core/SkMakeUnique.h"
- #include <algorithm>
- #define GIF87_STAMP "GIF87a"
- #define GIF89_STAMP "GIF89a"
- #define GIF_STAMP_LEN 6
- /*
- * Checks the start of the stream to see if the image is a gif
- */
- bool SkGifCodec::IsGif(const void* buf, size_t bytesRead) {
- if (bytesRead >= GIF_STAMP_LEN) {
- if (memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
- memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0)
- {
- return true;
- }
- }
- return false;
- }
- /*
- * Error function
- */
- static SkCodec::Result gif_error(const char* msg, SkCodec::Result result = SkCodec::kInvalidInput) {
- SkCodecPrintf("Gif Error: %s\n", msg);
- return result;
- }
- std::unique_ptr<SkCodec> SkGifCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
- Result* result) {
- std::unique_ptr<SkGifImageReader> reader(new SkGifImageReader(std::move(stream)));
- *result = reader->parse(SkGifImageReader::SkGIFSizeQuery);
- if (*result != kSuccess) {
- return nullptr;
- }
- // If no images are in the data, or the first header is not yet defined, we cannot
- // create a codec. In either case, the width and height are not yet known.
- auto* frame = reader->frameContext(0);
- if (!frame || !frame->isHeaderDefined()) {
- *result = kInvalidInput;
- return nullptr;
- }
- // isHeaderDefined() will not return true if the screen size is empty.
- SkASSERT(reader->screenHeight() > 0 && reader->screenWidth() > 0);
- const auto alpha = reader->firstFrameHasAlpha() ? SkEncodedInfo::kBinary_Alpha
- : SkEncodedInfo::kOpaque_Alpha;
- // Use kPalette since Gifs are encoded with a color table.
- // FIXME: Gifs can actually be encoded with 4-bits per pixel. Using 8 works, but we could skip
- // expanding to 8 bits and take advantage of the SkSwizzler to work from 4.
- auto encodedInfo = SkEncodedInfo::Make(reader->screenWidth(), reader->screenHeight(),
- SkEncodedInfo::kPalette_Color, alpha, 8);
- return std::unique_ptr<SkCodec>(new SkGifCodec(std::move(encodedInfo), reader.release()));
- }
- bool SkGifCodec::onRewind() {
- fReader->clearDecodeState();
- return true;
- }
- SkGifCodec::SkGifCodec(SkEncodedInfo&& encodedInfo, SkGifImageReader* reader)
- : INHERITED(std::move(encodedInfo), skcms_PixelFormat_RGBA_8888, nullptr)
- , fReader(reader)
- , fTmpBuffer(nullptr)
- , fSwizzler(nullptr)
- , fCurrColorTable(nullptr)
- , fCurrColorTableIsReal(false)
- , fFilledBackground(false)
- , fFirstCallToIncrementalDecode(false)
- , fDst(nullptr)
- , fDstRowBytes(0)
- , fRowsDecoded(0)
- {
- reader->setClient(this);
- }
- int SkGifCodec::onGetFrameCount() {
- fReader->parse(SkGifImageReader::SkGIFFrameCountQuery);
- return fReader->imagesCount();
- }
- bool SkGifCodec::onGetFrameInfo(int i, SkCodec::FrameInfo* frameInfo) const {
- if (i >= fReader->imagesCount()) {
- return false;
- }
- const SkGIFFrameContext* frameContext = fReader->frameContext(i);
- SkASSERT(frameContext->reachedStartOfData());
- if (frameInfo) {
- frameInfo->fDuration = frameContext->getDuration();
- frameInfo->fRequiredFrame = frameContext->getRequiredFrame();
- frameInfo->fFullyReceived = frameContext->isComplete();
- frameInfo->fAlphaType = frameContext->hasAlpha() ? kUnpremul_SkAlphaType
- : kOpaque_SkAlphaType;
- frameInfo->fDisposalMethod = frameContext->getDisposalMethod();
- }
- return true;
- }
- int SkGifCodec::onGetRepetitionCount() {
- fReader->parse(SkGifImageReader::SkGIFLoopCountQuery);
- return fReader->loopCount();
- }
- static constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
- void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, int frameIndex) {
- SkColorType colorTableColorType = dstInfo.colorType();
- if (this->colorXform()) {
- colorTableColorType = kXformSrcColorType;
- }
- sk_sp<SkColorTable> currColorTable = fReader->getColorTable(colorTableColorType, frameIndex);
- fCurrColorTableIsReal = static_cast<bool>(currColorTable);
- if (!fCurrColorTableIsReal) {
- // This is possible for an empty frame. Create a dummy with one value (transparent).
- SkPMColor color = SK_ColorTRANSPARENT;
- fCurrColorTable.reset(new SkColorTable(&color, 1));
- } else if (this->colorXform() && !this->xformOnDecode()) {
- SkPMColor dstColors[256];
- this->applyColorXform(dstColors, currColorTable->readColors(),
- currColorTable->count());
- fCurrColorTable.reset(new SkColorTable(dstColors, currColorTable->count()));
- } else {
- fCurrColorTable = std::move(currColorTable);
- }
- }
- SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, const Options& opts) {
- if (opts.fSubset) {
- return gif_error("Subsets not supported.\n", kUnimplemented);
- }
- const int frameIndex = opts.fFrameIndex;
- if (frameIndex > 0 && kRGB_565_SkColorType == dstInfo.colorType()) {
- // FIXME: In theory, we might be able to support this, but it's not clear that it
- // is necessary (Chromium does not decode to 565, and Android does not decode
- // frames beyond the first). Disabling it because it is somewhat difficult:
- // - If there is a transparent pixel, and this frame draws on top of another frame
- // (if the frame is independent with a transparent pixel, we should not decode to
- // 565 anyway, since it is not opaque), we need to skip drawing the transparent
- // pixels (see writeTransparentPixels in haveDecodedRow). We currently do this by
- // first swizzling into temporary memory, then copying into the destination. (We
- // let the swizzler handle it first because it may need to sample.) After
- // swizzling to 565, we do not know which pixels in our temporary memory
- // correspond to the transparent pixel, so we do not know what to skip. We could
- // special case the non-sampled case (no need to swizzle), but as this is
- // currently unused we can just not support it.
- return gif_error("Cannot decode multiframe gif (except frame 0) as 565.\n",
- kInvalidConversion);
- }
- const auto* frame = fReader->frameContext(frameIndex);
- SkASSERT(frame);
- if (0 == frameIndex) {
- // SkCodec does not have a way to just parse through frame 0, so we
- // have to do so manually, here.
- fReader->parse((SkGifImageReader::SkGIFParseQuery) 0);
- if (!frame->reachedStartOfData()) {
- // We have parsed enough to know that there is a color map, but cannot
- // parse the map itself yet. Exit now, so we do not build an incorrect
- // table.
- return gif_error("color map not available yet\n", kIncompleteInput);
- }
- } else {
- // Parsing happened in SkCodec::getPixels.
- SkASSERT(frameIndex < fReader->imagesCount());
- SkASSERT(frame->reachedStartOfData());
- }
- if (this->xformOnDecode()) {
- fXformBuffer.reset(new uint32_t[dstInfo.width()]);
- sk_bzero(fXformBuffer.get(), dstInfo.width() * sizeof(uint32_t));
- }
- fTmpBuffer.reset(new uint8_t[dstInfo.minRowBytes()]);
- this->initializeColorTable(dstInfo, frameIndex);
- this->initializeSwizzler(dstInfo, frameIndex);
- SkASSERT(fCurrColorTable);
- return kSuccess;
- }
- void SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, int frameIndex) {
- const SkGIFFrameContext* frame = fReader->frameContext(frameIndex);
- // This is only called by prepareToDecode, which ensures frameIndex is in range.
- SkASSERT(frame);
- const int xBegin = frame->xOffset();
- const int xEnd = std::min(frame->frameRect().right(), fReader->screenWidth());
- // CreateSwizzler only reads left and right of the frame. We cannot use the frame's raw
- // frameRect, since it might extend beyond the edge of the frame.
- SkIRect swizzleRect = SkIRect::MakeLTRB(xBegin, 0, xEnd, 0);
- SkImageInfo swizzlerInfo = dstInfo;
- if (this->colorXform()) {
- swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType);
- if (kPremul_SkAlphaType == dstInfo.alphaType()) {
- swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType);
- }
- }
- // The default Options should be fine:
- // - we'll ignore if the memory is zero initialized - unless we're the first frame, this won't
- // matter anyway.
- // - subsets are not supported for gif
- // - the swizzler does not need to know about the frame.
- // We may not be able to use the real Options anyway, since getPixels does not store it (due to
- // a bug).
- fSwizzler = SkSwizzler::Make(this->getEncodedInfo(), fCurrColorTable->readColors(),
- swizzlerInfo, Options(), &swizzleRect);
- SkASSERT(fSwizzler.get());
- }
- /*
- * Initiates the gif decode
- */
- SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
- void* pixels, size_t dstRowBytes,
- const Options& opts,
- int* rowsDecoded) {
- Result result = this->prepareToDecode(dstInfo, opts);
- switch (result) {
- case kSuccess:
- break;
- case kIncompleteInput:
- // onStartIncrementalDecode treats this as incomplete, since it may
- // provide more data later, but in this case, no more data will be
- // provided, and there is nothing to draw. We also cannot return
- // kIncompleteInput, which will make SkCodec attempt to fill
- // remaining rows, but that requires an SkSwizzler, which we have
- // not created.
- return kInvalidInput;
- default:
- return result;
- }
- if (dstInfo.dimensions() != this->dimensions()) {
- return gif_error("Scaling not supported.\n", kInvalidScale);
- }
- fDst = pixels;
- fDstRowBytes = dstRowBytes;
- return this->decodeFrame(true, opts, rowsDecoded);
- }
- SkCodec::Result SkGifCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
- void* pixels, size_t dstRowBytes,
- const SkCodec::Options& opts) {
- Result result = this->prepareToDecode(dstInfo, opts);
- if (result != kSuccess) {
- return result;
- }
- fDst = pixels;
- fDstRowBytes = dstRowBytes;
- fFirstCallToIncrementalDecode = true;
- return kSuccess;
- }
- SkCodec::Result SkGifCodec::onIncrementalDecode(int* rowsDecoded) {
- // It is possible the client has appended more data. Parse, if needed.
- const auto& options = this->options();
- const int frameIndex = options.fFrameIndex;
- fReader->parse((SkGifImageReader::SkGIFParseQuery) frameIndex);
- const bool firstCallToIncrementalDecode = fFirstCallToIncrementalDecode;
- fFirstCallToIncrementalDecode = false;
- return this->decodeFrame(firstCallToIncrementalDecode, options, rowsDecoded);
- }
- SkCodec::Result SkGifCodec::decodeFrame(bool firstAttempt, const Options& opts, int* rowsDecoded) {
- const SkImageInfo& dstInfo = this->dstInfo();
- const int scaledHeight = get_scaled_dimension(dstInfo.height(), fSwizzler->sampleY());
- const int frameIndex = opts.fFrameIndex;
- SkASSERT(frameIndex < fReader->imagesCount());
- const SkGIFFrameContext* frameContext = fReader->frameContext(frameIndex);
- if (firstAttempt) {
- // rowsDecoded reports how many rows have been initialized, so a layer above
- // can fill the rest. In some cases, we fill the background before decoding
- // (or it is already filled for us), so we report rowsDecoded to be the full
- // height.
- bool filledBackground = false;
- if (frameContext->getRequiredFrame() == kNoFrame) {
- // We may need to clear to transparent for one of the following reasons:
- // - The frameRect does not cover the full bounds. haveDecodedRow will
- // only draw inside the frameRect, so we need to clear the rest.
- // - The frame is interlaced. There is no obvious way to fill
- // afterwards for an incomplete image. (FIXME: Does the first pass
- // cover all rows? If so, we do not have to fill here.)
- // - There is no color table for this frame. In that case will not
- // draw anything, so we need to fill.
- if (frameContext->frameRect() != this->bounds()
- || frameContext->interlaced() || !fCurrColorTableIsReal) {
- auto fillInfo = dstInfo.makeWH(fSwizzler->fillWidth(), scaledHeight);
- SkSampler::Fill(fillInfo, fDst, fDstRowBytes, opts.fZeroInitialized);
- filledBackground = true;
- }
- } else {
- // Not independent.
- // SkCodec ensured that the prior frame has been decoded.
- filledBackground = true;
- }
- fFilledBackground = filledBackground;
- if (filledBackground) {
- // Report the full (scaled) height, since the client will never need to fill.
- fRowsDecoded = scaledHeight;
- } else {
- // This will be updated by haveDecodedRow.
- fRowsDecoded = 0;
- }
- }
- if (!fCurrColorTableIsReal) {
- // Nothing to draw this frame.
- return kSuccess;
- }
- bool frameDecoded = false;
- const bool fatalError = !fReader->decode(frameIndex, &frameDecoded);
- if (fatalError || !frameDecoded || fRowsDecoded != scaledHeight) {
- if (rowsDecoded) {
- *rowsDecoded = fRowsDecoded;
- }
- if (fatalError) {
- return kErrorInInput;
- }
- return kIncompleteInput;
- }
- return kSuccess;
- }
- void SkGifCodec::applyXformRow(const SkImageInfo& dstInfo, void* dst, const uint8_t* src) const {
- if (this->xformOnDecode()) {
- SkASSERT(this->colorXform());
- fSwizzler->swizzle(fXformBuffer.get(), src);
- const int xformWidth = get_scaled_dimension(dstInfo.width(), fSwizzler->sampleX());
- this->applyColorXform(dst, fXformBuffer.get(), xformWidth);
- } else {
- fSwizzler->swizzle(dst, src);
- }
- }
- template <typename T>
- static void blend_line(void* dstAsVoid, const void* srcAsVoid, int width) {
- T* dst = reinterpret_cast<T*>(dstAsVoid);
- const T* src = reinterpret_cast<const T*>(srcAsVoid);
- while (width --> 0) {
- if (*src != 0) { // GIF pixels are either transparent (== 0) or opaque (!= 0).
- *dst = *src;
- }
- src++;
- dst++;
- }
- }
- void SkGifCodec::haveDecodedRow(int frameIndex, const unsigned char* rowBegin,
- int rowNumber, int repeatCount, bool writeTransparentPixels)
- {
- const SkGIFFrameContext* frameContext = fReader->frameContext(frameIndex);
- // The pixel data and coordinates supplied to us are relative to the frame's
- // origin within the entire image size, i.e.
- // (frameContext->xOffset, frameContext->yOffset). There is no guarantee
- // that width == (size().width() - frameContext->xOffset), so
- // we must ensure we don't run off the end of either the source data or the
- // row's X-coordinates.
- const int width = frameContext->width();
- const int xBegin = frameContext->xOffset();
- const int yBegin = frameContext->yOffset() + rowNumber;
- const int xEnd = std::min(xBegin + width, this->dimensions().width());
- const int yEnd = std::min(yBegin + rowNumber + repeatCount, this->dimensions().height());
- // FIXME: No need to make the checks on width/xBegin/xEnd for every row. We could instead do
- // this once in prepareToDecode.
- if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= yBegin))
- return;
- // yBegin is the first row in the non-sampled image. dstRow will be the row in the output,
- // after potentially scaling it.
- int dstRow = yBegin;
- const int sampleY = fSwizzler->sampleY();
- if (sampleY > 1) {
- // Check to see whether this row or one that falls in the repeatCount is needed in the
- // output.
- bool foundNecessaryRow = false;
- for (int i = 0; i < repeatCount; i++) {
- const int potentialRow = yBegin + i;
- if (fSwizzler->rowNeeded(potentialRow)) {
- dstRow = potentialRow / sampleY;
- const int scaledHeight = get_scaled_dimension(this->dstInfo().height(), sampleY);
- if (dstRow >= scaledHeight) {
- return;
- }
- foundNecessaryRow = true;
- repeatCount -= i;
- repeatCount = (repeatCount - 1) / sampleY + 1;
- // Make sure the repeatCount does not take us beyond the end of the dst
- if (dstRow + repeatCount > scaledHeight) {
- repeatCount = scaledHeight - dstRow;
- SkASSERT(repeatCount >= 1);
- }
- break;
- }
- }
- if (!foundNecessaryRow) {
- return;
- }
- } else {
- // Make sure the repeatCount does not take us beyond the end of the dst
- SkASSERT(this->dstInfo().height() >= yBegin);
- repeatCount = SkTMin(repeatCount, this->dstInfo().height() - yBegin);
- }
- if (!fFilledBackground) {
- // At this point, we are definitely going to write the row, so count it towards the number
- // of rows decoded.
- // We do not consider the repeatCount, which only happens for interlaced, in which case we
- // have already set fRowsDecoded to the proper value (reflecting that we have filled the
- // background).
- fRowsDecoded++;
- }
- // decodeFrame will early exit if this is false, so this method will not be
- // called.
- SkASSERT(fCurrColorTableIsReal);
- // The swizzler takes care of offsetting into the dst width-wise.
- void* dstLine = SkTAddOffset<void>(fDst, dstRow * fDstRowBytes);
- // We may or may not need to write transparent pixels to the buffer.
- // If we're compositing against a previous image, it's wrong, but if
- // we're decoding an interlaced gif and displaying it "Haeberli"-style,
- // we must write these for passes beyond the first, or the initial passes
- // will "show through" the later ones.
- const auto dstInfo = this->dstInfo();
- if (writeTransparentPixels) {
- this->applyXformRow(dstInfo, dstLine, rowBegin);
- } else {
- this->applyXformRow(dstInfo, fTmpBuffer.get(), rowBegin);
- size_t offsetBytes = fSwizzler->swizzleOffsetBytes();
- if (dstInfo.colorType() == kRGBA_F16_SkColorType) {
- // Account for the fact that post-swizzling we converted to F16,
- // which is twice as wide.
- offsetBytes *= 2;
- }
- const void* src = SkTAddOffset<void>(fTmpBuffer.get(), offsetBytes);
- void* dst = SkTAddOffset<void>(dstLine, offsetBytes);
- switch (dstInfo.colorType()) {
- case kBGRA_8888_SkColorType:
- case kRGBA_8888_SkColorType:
- blend_line<uint32_t>(dst, src, fSwizzler->swizzleWidth());
- break;
- case kRGBA_F16_SkColorType:
- blend_line<uint64_t>(dst, src, fSwizzler->swizzleWidth());
- break;
- default:
- SkASSERT(false);
- return;
- }
- }
- // Tell the frame to copy the row data if need be.
- if (repeatCount > 1) {
- const size_t bytesPerPixel = this->dstInfo().bytesPerPixel();
- const size_t bytesToCopy = fSwizzler->swizzleWidth() * bytesPerPixel;
- void* copiedLine = SkTAddOffset<void>(dstLine, fSwizzler->swizzleOffsetBytes());
- void* dst = copiedLine;
- for (int i = 1; i < repeatCount; i++) {
- dst = SkTAddOffset<void>(dst, fDstRowBytes);
- memcpy(dst, copiedLine, bytesToCopy);
- }
- }
- }
|