From 42ee2845673c38f6d70f0d8ddf7e26dce8aa61d3 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Sun, 14 Jan 2018 14:46:51 -0500 Subject: Use SkAndroidCodec in SkAnimatedImage Bug: b/63909536 Bug: b/63908092 SkAnimatedImage is designed around a specific Android use case, so move it into the android folders. Make SkAnimatedImage hold an SkAndroidCodec (instead of an SkCodec). Expose fCodec so that SkAnimatedImage can animate by using the internal SkCodec. Update the sample to use SkAndroidCodec. Allow webp to decode a scaled down animation. For RestoreBG frames, adjust the frameRect (which is erased) to account for the scaling. Add a test to verify that we decode a webp with a RestoreBG frame successfully. Disable scaling for later frames in other formats (GIF, for now), since the code for erasing a RestoreBG frame is currently unaware of the sampling. Change-Id: I5dd2b86138f2c7f6adcd08dce1bd49040f7dc224 Reviewed-on: https://skia-review.googlesource.com/94621 Commit-Queue: Leon Scroggins Reviewed-by: Derek Sollenberger --- src/codec/SkAnimatedImage.cpp | 236 ------------------------------------------ src/codec/SkCodec.cpp | 28 ++++- src/codec/SkWebpCodec.cpp | 2 +- 3 files changed, 26 insertions(+), 240 deletions(-) delete mode 100644 src/codec/SkAnimatedImage.cpp (limited to 'src/codec') diff --git a/src/codec/SkAnimatedImage.cpp b/src/codec/SkAnimatedImage.cpp deleted file mode 100644 index dcb4bb93bf..0000000000 --- a/src/codec/SkAnimatedImage.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright 2018 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkAnimatedImage.h" -#include "SkCanvas.h" -#include "SkCodec.h" -#include "SkCodecPriv.h" - -sk_sp SkAnimatedImage::MakeFromCodec(std::unique_ptr codec) { - if (!codec) { - return nullptr; - } - - auto image = sk_sp(new SkAnimatedImage(std::move(codec))); - if (!image->fActiveFrame.fBitmap.getPixels()) { - // tryAllocPixels failed. - return nullptr; - } - - return image; -} - -// Sentinel value for starting at the beginning. -static constexpr double kInit = -1.0; - -SkAnimatedImage::SkAnimatedImage(std::unique_ptr codec) - : fCodec(std::move(codec)) - , fFinished(false) - , fRunning(false) - , fNowMS(kInit) - , fRemainingMS(kInit) -{ - if (!fActiveFrame.fBitmap.tryAllocPixels(fCodec->getInfo())) { - return; - } - - this->update(kInit); -} - -SkAnimatedImage::~SkAnimatedImage() { } - -SkRect SkAnimatedImage::onGetBounds() { - return SkRect::Make(fCodec->getInfo().bounds()); -} - -void SkAnimatedImage::onDraw(SkCanvas* canvas) { - canvas->drawBitmap(fActiveFrame.fBitmap, 0, 0); -} - -SkAnimatedImage::Frame::Frame() - : fIndex(SkCodec::kNone) -{} - -bool SkAnimatedImage::Frame::copyTo(Frame* dst) const { - if (dst->fBitmap.getPixels()) { - dst->fBitmap.setAlphaType(fBitmap.alphaType()); - } else if (!dst->fBitmap.tryAllocPixels(fBitmap.info())) { - return false; - } - - memcpy(dst->fBitmap.getPixels(), fBitmap.getPixels(), fBitmap.computeByteSize()); - dst->fIndex = fIndex; - dst->fDisposalMethod = fDisposalMethod; - return true; -} - -void SkAnimatedImage::start() { - fRunning = true; -} - -void SkAnimatedImage::stop() { - fRunning = false; -} - -void SkAnimatedImage::reset() { - this->update(kInit); -} - -static bool is_restore_previous(SkCodecAnimation::DisposalMethod dispose) { - return SkCodecAnimation::DisposalMethod::kRestorePrevious == dispose; -} - -double SkAnimatedImage::update(double msecs) { - if (fFinished) { - return std::numeric_limits::max(); - } - - const double lastUpdateMS = fNowMS; - fNowMS = msecs; - const double msSinceLastUpdate = fNowMS - lastUpdateMS; - - const int frameCount = fCodec->getFrameCount(); - int frameToDecode = SkCodec::kNone; - if (kInit == msecs) { - frameToDecode = 0; - } else { - if (!fRunning || lastUpdateMS == kInit) { - return kInit; - } - if (msSinceLastUpdate < fRemainingMS) { - fRemainingMS -= msSinceLastUpdate; - return fRemainingMS + fNowMS; - } else { - frameToDecode = (fActiveFrame.fIndex + 1) % frameCount; - } - } - - SkCodec::FrameInfo frameInfo; - if (fCodec->getFrameInfo(frameToDecode, &frameInfo)) { - if (!frameInfo.fFullyReceived) { - SkCodecPrintf("Frame %i not fully received\n", frameToDecode); - fFinished = true; - return std::numeric_limits::max(); - } - - if (kInit == msecs) { - fRemainingMS = frameInfo.fDuration; - } else { - // Check to see whether we should skip this frame. - double pastUpdate = msSinceLastUpdate - fRemainingMS; - while (pastUpdate >= frameInfo.fDuration) { - SkCodecPrintf("Skipping frame %i\n", frameToDecode); - pastUpdate -= frameInfo.fDuration; - frameToDecode = (frameToDecode + 1) % frameCount; - if (!fCodec->getFrameInfo(frameToDecode, &frameInfo)) { - SkCodecPrintf("Could not getFrameInfo for frame %i", - frameToDecode); - // Prior call to getFrameInfo succeeded, so use that one. - frameToDecode--; - fFinished = true; - if (frameToDecode < 0) { - return std::numeric_limits::max(); - } - } - } - fRemainingMS = frameInfo.fDuration - pastUpdate; - } - } else { - fFinished = true; - if (0 == frameToDecode) { - // Static image. This is okay. - frameInfo.fRequiredFrame = SkCodec::kNone; - frameInfo.fAlphaType = fCodec->getInfo().alphaType(); - // These fields won't be read. - frameInfo.fDuration = INT_MAX; - frameInfo.fFullyReceived = true; - } else { - SkCodecPrintf("Error getting frameInfo for frame %i\n", - frameToDecode); - return std::numeric_limits::max(); - } - } - - if (frameToDecode == fActiveFrame.fIndex) { - return fRemainingMS + fNowMS; - } - - if (frameToDecode == fRestoreFrame.fIndex) { - SkTSwap(fActiveFrame, fRestoreFrame); - return fRemainingMS + fNowMS; - } - - // The following code makes an effort to avoid overwriting a frame that will - // be used again. If frame |i| is_restore_previous, frame |i+1| will not - // depend on frame |i|, so do not overwrite frame |i-1|, which may be needed - // for frame |i+1|. - // We could be even smarter about which frames to save by looking at the - // entire dependency chain. - SkCodec::Options options; - options.fFrameIndex = frameToDecode; - if (frameInfo.fRequiredFrame == SkCodec::kNone) { - if (is_restore_previous(frameInfo.fDisposalMethod)) { - // frameToDecode will be discarded immediately after drawing, so - // do not overwrite a frame which could possibly be used in the - // future. - if (fActiveFrame.fIndex != SkCodec::kNone && - !is_restore_previous(fActiveFrame.fDisposalMethod)) { - SkTSwap(fActiveFrame, fRestoreFrame); - } - } - } else { - auto validPriorFrame = [&frameInfo, &frameToDecode](const Frame& frame) { - if (SkCodec::kNone == frame.fIndex || is_restore_previous(frame.fDisposalMethod)) { - return false; - } - - return frame.fIndex >= frameInfo.fRequiredFrame && frame.fIndex < frameToDecode; - }; - if (validPriorFrame(fActiveFrame)) { - if (is_restore_previous(frameInfo.fDisposalMethod)) { - // fActiveFrame is a good frame to use for this one, but we - // don't want to overwrite it. - fActiveFrame.copyTo(&fRestoreFrame); - } - options.fPriorFrame = fActiveFrame.fIndex; - } else if (validPriorFrame(fRestoreFrame)) { - if (!is_restore_previous(frameInfo.fDisposalMethod)) { - SkTSwap(fActiveFrame, fRestoreFrame); - } else if (!fRestoreFrame.copyTo(&fActiveFrame)) { - SkCodecPrintf("Failed to restore frame\n"); - fFinished = true; - return std::numeric_limits::max(); - } - options.fPriorFrame = fActiveFrame.fIndex; - } - } - - auto alphaType = kOpaque_SkAlphaType == frameInfo.fAlphaType ? - kOpaque_SkAlphaType : kPremul_SkAlphaType; - SkBitmap* dst = &fActiveFrame.fBitmap; - if (dst->getPixels()) { - SkAssertResult(dst->setAlphaType(alphaType)); - } else { - auto info = fCodec->getInfo().makeAlphaType(alphaType); - if (!dst->tryAllocPixels(info)) { - fFinished = true; - return std::numeric_limits::max(); - } - } - - auto result = fCodec->getPixels(dst->info(), dst->getPixels(), dst->rowBytes(), &options); - if (result != SkCodec::kSuccess) { - SkCodecPrintf("error %i, frame %i of %i\n", result, frameToDecode, fCodec->getFrameCount()); - // Reset to the beginning. - fActiveFrame.fIndex = SkCodec::kNone; - return 0.0; - } - - fActiveFrame.fIndex = frameToDecode; - fActiveFrame.fDisposalMethod = frameInfo.fDisposalMethod; - return fRemainingMS + fNowMS; -} diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp index d0fb43a053..b2902ca75f 100644 --- a/src/codec/SkCodec.cpp +++ b/src/codec/SkCodec.cpp @@ -237,8 +237,8 @@ SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels, return kInvalidParameters; } - if (options.fSubset || info.dimensions() != fSrcInfo.dimensions()) { - // If we add support for these, we need to update the code that zeroes + if (options.fSubset) { + // If we add support for this, we need to update the code that zeroes // a kRestoreBGColor frame. return kInvalidParameters; } @@ -275,7 +275,29 @@ SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels, // If a frame after the required frame is provided, there is no // need to clear, since it must be covered by the desired frame. if (options.fPriorFrame == requiredFrame) { - zero_rect(info, pixels, rowBytes, prevFrame->frameRect()); + SkIRect prevRect = prevFrame->frameRect(); + if (info.dimensions() != fSrcInfo.dimensions()) { + auto src = SkRect::Make(fSrcInfo.dimensions()); + auto dst = SkRect::Make(info.dimensions()); + SkMatrix map = SkMatrix::MakeRectToRect(src, dst, + SkMatrix::kCenter_ScaleToFit); + SkRect asRect = SkRect::Make(prevRect); + if (!map.mapRect(&asRect)) { + return kInternalError; + } + asRect.roundIn(&prevRect); + if (prevRect.isEmpty()) { + // Down-scaling shrank the empty portion to nothing, + // so nothing to zero. + break; + } + if (!prevRect.intersect(SkIRect::MakeSize(info.dimensions()))) { + SkCodecPrintf("rectangles do not intersect!"); + SkASSERT(false); + break; + } + } + zero_rect(info, pixels, rowBytes, prevRect); } break; default: diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp index ca63349c0e..aa3547dca5 100644 --- a/src/codec/SkWebpCodec.cpp +++ b/src/codec/SkWebpCodec.cpp @@ -402,7 +402,7 @@ SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, SkASSERT(0 == index || index < fFrameHolder.size()); const auto& srcInfo = this->getInfo(); - SkASSERT(0 == index || (!options.fSubset && dstInfo.dimensions() == srcInfo.dimensions())); + SkASSERT(0 == index || !options.fSubset); WebPDecoderConfig config; if (0 == WebPInitDecoderConfig(&config)) { -- cgit v1.2.3