You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
539 lines
22 KiB
539 lines
22 KiB
// 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) {
|
|
frameContext->fillIn(frameInfo, frameContext->isComplete());
|
|
|
|
auto* rect = &frameInfo->fFrameRect;
|
|
auto bounds = this->bounds();
|
|
if (!rect->intersect(bounds)) {
|
|
// If a frame is offscreen, it will have no effect on the output
|
|
// image. Modify its bounds to be consistent with the Wuffs
|
|
// implementation.
|
|
rect->setLTRB(std::min(rect->left(), bounds.right()),
|
|
std::min(rect->top(), bounds.bottom()),
|
|
std::min(rect->right(), bounds.right()),
|
|
std::min(rect->bottom(), bounds.bottom()));
|
|
}
|
|
}
|
|
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 + 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);
|
|
}
|
|
}
|
|
}
|