}
break;
}
+ case SkCodec::kInvalidConversion:
+ if (i > 0 && (decodeInfo.colorType() == kRGB_565_SkColorType
+ || decodeInfo.colorType() == kIndex_8_SkColorType)) {
+ return Error::Nonfatal(SkStringPrintf(
+ "Cannot decode frame %i to 565/Index8 (%s).", i, fPath.c_str()));
+ }
+ // Fall through.
default:
return SkStringPrintf("Couldn't getPixels for frame %i in %s.",
i, fPath.c_str());
}
const size_t frameIndex = opts.fFrameIndex;
- if (frameIndex > 0 && dstInfo.colorType() == kIndex_8_SkColorType) {
- // FIXME: It is possible that a later frame can be decoded to index8, if it does one of the
- // following:
- // - Covers the entire previous frame
- // - Shares a color table (and transparent index) with any prior frames that are showing.
- // We must support index8 for the first frame to be backwards compatible on Android, but
- // we do not (currently) need to support later frames as index8.
- return gif_error("Cannot decode multiframe gif (except frame 0) as index 8.\n",
- kInvalidConversion);
+ if (frameIndex > 0) {
+ switch (dstInfo.colorType()) {
+ case kIndex_8_SkColorType:
+ // FIXME: It is possible that a later frame can be decoded to index8, if it does one
+ // of the following:
+ // - Covers the entire previous frame
+ // - Shares a color table (and transparent index) with any prior frames that are
+ // showing.
+ // We must support index8 for the first frame to be backwards compatible on Android,
+ // but we do not (currently) need to support later frames as index8.
+ return gif_error("Cannot decode multiframe gif (except frame 0) as index 8.\n",
+ kInvalidConversion);
+ case kRGB_565_SkColorType:
+ // 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);
+ default:
+ break;
+ }
}
fReader->parse((SkGifImageReader::SkGIFParseQuery) frameIndex);
// 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 || dstInfo.colorType() == kRGB_565_SkColorType) {
+ if (writeTransparentPixels) {
fSwizzler->swizzle(dstLine, rowBegin);
} else {
// We cannot swizzle directly into the dst, since that will write the transparent pixels.
// Perform decoding for this frame. frameComplete will be true if the entire frame is decoded.
// Returns false if a decoding error occurred. This is a fatal error and causes the SkGifImageReader to set the "decode failed" flag.
// Otherwise, either not enough data is available to decode further than before, or the new data has been decoded successfully; returns true in this case.
-bool SkGIFFrameContext::decode(SkGifCodec* client, bool* frameComplete)
+bool SkGIFFrameContext::decode(SkGifCodec* client, const SkGIFColorMap& globalMap, bool* frameComplete)
{
*frameComplete = false;
if (!m_lzwContext) {
return true;
m_lzwContext.reset(new SkGIFLZWContext(client, this));
- if (!m_lzwContext->prepareToDecode()) {
+ if (!m_lzwContext->prepareToDecode(globalMap)) {
m_lzwContext.reset();
return false;
}
{
SkGIFFrameContext* currentFrame = m_frames[frameIndex].get();
- return currentFrame->decode(m_client, frameComplete);
+ return currentFrame->decode(m_client, m_globalColorMap, frameComplete);
}
// Parse incoming GIF data stream into internal data structures.
}
// FIXME: Move this method to close to doLZW().
-bool SkGIFLZWContext::prepareToDecode()
+bool SkGIFLZWContext::prepareToDecode(const SkGIFColorMap& globalMap)
{
SkASSERT(m_frameContext->isDataSizeDefined() && m_frameContext->isHeaderDefined());
datum = bits = 0;
ipass = m_frameContext->interlaced() ? 1 : 0;
irow = 0;
- alwaysWriteTransparentPixels = !m_frameContext->interlaced()
- && m_frameContext->getRequiredFrame() == SkCodec::kNone;
+ alwaysWriteTransparentPixels = false;
+ if (m_frameContext->getRequiredFrame() == SkCodec::kNone) {
+ if (!m_frameContext->interlaced()) {
+ alwaysWriteTransparentPixels = true;
+ } else {
+ // The frame is interlaced, so we do not want to write transparent
+ // pixels. But if there are no transparent pixels anyway, there is
+ // no harm in taking the alwaysWriteTransparentPixels path, which
+ // is faster, and it also supports 565.
+ // Since the frame is independent, it does not matter whether the
+ // frame is subset (nothing behind it needs to show through). So we
+ // only need to know whether there is a valid transparent pixel.
+ // This is a little counterintuitive - we want to "always write
+ // transparent pixels" if there ARE NO transparent pixels, so we
+ // check to see whether the pixel index is >= numColors.
+ const auto& localMap = m_frameContext->localColorMap();
+ const auto trans = m_frameContext->transparentPixel();
+ if (localMap.isDefined()) {
+ alwaysWriteTransparentPixels = trans >= localMap.numColors();
+ } else {
+ // Note that if the map is not defined, the value of
+ // alwaysWriteTransparentPixels is meaningless, since without
+ // any color table, we will skip drawing entirely.
+ // FIXME: We could even skip calling prepareToDecode in that
+ // case, meaning we can SkASSERT(globalMap.isDefined())
+ alwaysWriteTransparentPixels = trans >= globalMap.numColors();
+ }
+ }
+ }
// We want to know the longest sequence encodable by a dictionary with
// SK_MAX_DICTIONARY_ENTRIES entries. If we ignore the need to encode the base
};
struct SkGIFFrameContext;
+class SkGIFColorMap;
// LZW decoder state machine.
class SkGIFLZWContext final : public SkNoncopyable {
, m_frameContext(frameContext)
{ }
- bool prepareToDecode();
+ bool prepareToDecode(const SkGIFColorMap& globalMap);
bool outputRow(const unsigned char* rowBegin);
bool doLZW(const unsigned char* block, size_t bytesInBlock);
bool hasRemainingRows() { return SkToBool(rowsRemaining); }
m_lzwBlocks.push_back(SkData::MakeWithCopy(data, size));
}
- bool decode(SkGifCodec* client, bool* frameDecoded);
+ bool decode(SkGifCodec* client, const SkGIFColorMap& globalMap, bool* frameDecoded);
int frameId() const { return m_frameId; }
void setRect(unsigned x, unsigned y, unsigned width, unsigned height)