* 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;
}
auto* frame = reader->frameContext(0);
if (!frame || !frame->isHeaderDefined()) {
*result = kInvalidInput;
return nullptr;
}
SkASSERT(reader->screenHeight() > 0 && reader->screenWidth() > 0);
const auto alpha = reader->firstFrameHasAlpha() ? SkEncodedInfo::kBinary_Alpha
: SkEncodedInfo::kOpaque_Alpha;
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) {
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()) {
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) {
fReader->parse((SkGifImageReader::SkGIFParseQuery) 0);
if (!frame->reachedStartOfData()) {
return gif_error("color map not available yet\n", kIncompleteInput);
}
} else {
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);
SkASSERT(frame);
const int xBegin = frame->xOffset();
const int xEnd = std::min(frame->frameRect().right(), fReader->screenWidth());
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);
}
}
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:
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) {
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) {
bool filledBackground = false;
if (frameContext->getRequiredFrame() == kNoFrame) {
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 {
filledBackground = true;
}
fFilledBackground = filledBackground;
if (filledBackground) {
fRowsDecoded = scaledHeight;
} else {
fRowsDecoded = 0;
}
}
if (!fCurrColorTableIsReal) {
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) {
*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);
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());
if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= yBegin))
return;
int dstRow = yBegin;
const int sampleY = fSwizzler->sampleY();
if (sampleY > 1) {
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;
if (dstRow + repeatCount > scaledHeight) {
repeatCount = scaledHeight - dstRow;
SkASSERT(repeatCount >= 1);
}
break;
}
}
if (!foundNecessaryRow) {
return;
}
} else {
SkASSERT(this->dstInfo().height() >= yBegin);
repeatCount = SkTMin(repeatCount, this->dstInfo().height() - yBegin);
}
if (!fFilledBackground) {
fRowsDecoded++;
}
SkASSERT(fCurrColorTableIsReal);
void* dstLine = SkTAddOffset<void>(fDst, dstRow * fDstRowBytes);
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) {
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;
}
}
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);
}
}
}