1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
|
// Copyright 2015 Google Inc.
// Use of this source code is governed by the BSD-3-Clause license that can be
// found in the LICENSE.md 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 "SkGifCodec.h"
#include "SkLibGifCodec.h"
#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/SkSwizzler.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,
SkCodec::Result* result) {
std::unique_ptr<SkGifImageReader> reader(new SkGifImageReader(std::move(stream)));
*result = reader->parse(SkGifImageReader::SkGIFSizeQuery);
if (*result != SkCodec::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 = SkCodec::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 SkLibGifCodec(std::move(encodedInfo), reader.release()));
}
bool SkLibGifCodec::onRewind() {
fReader->clearDecodeState();
return true;
}
SkLibGifCodec::SkLibGifCodec(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 SkLibGifCodec::onGetFrameCount() {
fReader->parse(SkGifImageReader::SkGIFFrameCountQuery);
return fReader->imagesCount();
}
bool SkLibGifCodec::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 SkLibGifCodec::onGetRepetitionCount() {
fReader->parse(SkGifImageReader::SkGIFLoopCountQuery);
return fReader->loopCount();
}
static constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
void SkLibGifCodec::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 SkLibGifCodec::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 SkLibGifCodec::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 SkLibGifCodec::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 SkLibGifCodec::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 SkLibGifCodec::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 SkLibGifCodec::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 SkLibGifCodec::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 SkLibGifCodec::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 = std::min(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);
}
}
}
|