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 <scroggo@google.com> Reviewed-by: Derek Sollenberger <djsollen@google.com>
This commit is contained in:
parent
23e0cf2963
commit
42ee284567
2
BUILD.gn
2
BUILD.gn
@ -771,10 +771,10 @@ component("skia") {
|
|||||||
sources += skia_xps_sources
|
sources += skia_xps_sources
|
||||||
sources += [
|
sources += [
|
||||||
"src/android/SkAndroidFrameworkUtils.cpp",
|
"src/android/SkAndroidFrameworkUtils.cpp",
|
||||||
|
"src/android/SkAnimatedImage.cpp",
|
||||||
"src/android/SkBitmapRegionCodec.cpp",
|
"src/android/SkBitmapRegionCodec.cpp",
|
||||||
"src/android/SkBitmapRegionDecoder.cpp",
|
"src/android/SkBitmapRegionDecoder.cpp",
|
||||||
"src/codec/SkAndroidCodec.cpp",
|
"src/codec/SkAndroidCodec.cpp",
|
||||||
"src/codec/SkAnimatedImage.cpp",
|
|
||||||
"src/codec/SkBmpBaseCodec.cpp",
|
"src/codec/SkBmpBaseCodec.cpp",
|
||||||
"src/codec/SkBmpCodec.cpp",
|
"src/codec/SkBmpCodec.cpp",
|
||||||
"src/codec/SkBmpMaskCodec.cpp",
|
"src/codec/SkBmpMaskCodec.cpp",
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
#include "SkCodecAnimation.h"
|
#include "SkCodecAnimation.h"
|
||||||
#include "SkDrawable.h"
|
#include "SkDrawable.h"
|
||||||
|
|
||||||
class SkCodec;
|
class SkAndroidCodec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thread unsafe drawable for drawing animated images (e.g. GIF).
|
* Thread unsafe drawable for drawing animated images (e.g. GIF).
|
||||||
@ -20,12 +20,12 @@ class SkCodec;
|
|||||||
class SK_API SkAnimatedImage : public SkDrawable {
|
class SK_API SkAnimatedImage : public SkDrawable {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Create an SkAnimatedImage from the SkCodec.
|
* Create an SkAnimatedImage from the SkAndroidCodec.
|
||||||
*
|
*
|
||||||
* Returns null on failure to allocate pixels. On success, this will
|
* Returns null on failure to allocate pixels. On success, this will
|
||||||
* decode the first frame. It will not animate until start() is called.
|
* decode the first frame. It will not animate until start() is called.
|
||||||
*/
|
*/
|
||||||
static sk_sp<SkAnimatedImage> MakeFromCodec(std::unique_ptr<SkCodec>);
|
static sk_sp<SkAnimatedImage> Make(std::unique_ptr<SkAndroidCodec>);
|
||||||
|
|
||||||
~SkAnimatedImage() override;
|
~SkAnimatedImage() override;
|
||||||
|
|
||||||
@ -70,15 +70,15 @@ private:
|
|||||||
bool copyTo(Frame*) const;
|
bool copyTo(Frame*) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<SkCodec> fCodec;
|
std::unique_ptr<SkAndroidCodec> fCodec;
|
||||||
bool fFinished;
|
bool fFinished;
|
||||||
bool fRunning;
|
bool fRunning;
|
||||||
double fNowMS;
|
double fNowMS;
|
||||||
double fRemainingMS;
|
double fRemainingMS;
|
||||||
Frame fActiveFrame;
|
Frame fActiveFrame;
|
||||||
Frame fRestoreFrame;
|
Frame fRestoreFrame;
|
||||||
|
|
||||||
SkAnimatedImage(std::unique_ptr<SkCodec>);
|
SkAnimatedImage(std::unique_ptr<SkAndroidCodec>);
|
||||||
|
|
||||||
typedef SkDrawable INHERITED;
|
typedef SkDrawable INHERITED;
|
||||||
};
|
};
|
@ -231,12 +231,12 @@ public:
|
|||||||
return this->getAndroidPixels(info, pixels, rowBytes);
|
return this->getAndroidPixels(info, pixels, rowBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SkCodec* codec() const { return fCodec.get(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
SkAndroidCodec(SkCodec*);
|
SkAndroidCodec(SkCodec*);
|
||||||
|
|
||||||
SkCodec* codec() const { return fCodec.get(); }
|
|
||||||
|
|
||||||
virtual SkISize onGetSampledDimensions(int sampleSize) const = 0;
|
virtual SkISize onGetSampledDimensions(int sampleSize) const = 0;
|
||||||
|
|
||||||
virtual bool onGetSupportedSubset(SkIRect* desiredSubset) const = 0;
|
virtual bool onGetSupportedSubset(SkIRect* desiredSubset) const = 0;
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
* found in the LICENSE file.
|
* found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "SkAndroidCodec.h"
|
||||||
#include "SkAnimatedImage.h"
|
#include "SkAnimatedImage.h"
|
||||||
#include "SkAnimTimer.h"
|
#include "SkAnimTimer.h"
|
||||||
#include "SkCanvas.h"
|
#include "SkCanvas.h"
|
||||||
#include "SkCodec.h"
|
|
||||||
#include "SkPaint.h"
|
#include "SkPaint.h"
|
||||||
#include "SkPictureRecorder.h"
|
#include "SkPictureRecorder.h"
|
||||||
#include "SkRect.h"
|
#include "SkRect.h"
|
||||||
@ -74,7 +74,7 @@ protected:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fImage = SkAnimatedImage::MakeFromCodec(std::move(codec));
|
fImage = SkAnimatedImage::Make(SkAndroidCodec::MakeFromCodec(std::move(codec)));
|
||||||
if (!fImage) {
|
if (!fImage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,13 @@
|
|||||||
* found in the LICENSE file.
|
* found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "SkAndroidCodec.h"
|
||||||
#include "SkAnimatedImage.h"
|
#include "SkAnimatedImage.h"
|
||||||
#include "SkCanvas.h"
|
#include "SkCanvas.h"
|
||||||
#include "SkCodec.h"
|
#include "SkCodec.h"
|
||||||
#include "SkCodecPriv.h"
|
#include "SkCodecPriv.h"
|
||||||
|
|
||||||
sk_sp<SkAnimatedImage> SkAnimatedImage::MakeFromCodec(std::unique_ptr<SkCodec> codec) {
|
sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec) {
|
||||||
if (!codec) {
|
if (!codec) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -27,7 +28,7 @@ sk_sp<SkAnimatedImage> SkAnimatedImage::MakeFromCodec(std::unique_ptr<SkCodec> c
|
|||||||
// Sentinel value for starting at the beginning.
|
// Sentinel value for starting at the beginning.
|
||||||
static constexpr double kInit = -1.0;
|
static constexpr double kInit = -1.0;
|
||||||
|
|
||||||
SkAnimatedImage::SkAnimatedImage(std::unique_ptr<SkCodec> codec)
|
SkAnimatedImage::SkAnimatedImage(std::unique_ptr<SkAndroidCodec> codec)
|
||||||
: fCodec(std::move(codec))
|
: fCodec(std::move(codec))
|
||||||
, fFinished(false)
|
, fFinished(false)
|
||||||
, fRunning(false)
|
, fRunning(false)
|
||||||
@ -93,7 +94,7 @@ double SkAnimatedImage::update(double msecs) {
|
|||||||
fNowMS = msecs;
|
fNowMS = msecs;
|
||||||
const double msSinceLastUpdate = fNowMS - lastUpdateMS;
|
const double msSinceLastUpdate = fNowMS - lastUpdateMS;
|
||||||
|
|
||||||
const int frameCount = fCodec->getFrameCount();
|
const int frameCount = fCodec->codec()->getFrameCount();
|
||||||
int frameToDecode = SkCodec::kNone;
|
int frameToDecode = SkCodec::kNone;
|
||||||
if (kInit == msecs) {
|
if (kInit == msecs) {
|
||||||
frameToDecode = 0;
|
frameToDecode = 0;
|
||||||
@ -110,7 +111,7 @@ double SkAnimatedImage::update(double msecs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SkCodec::FrameInfo frameInfo;
|
SkCodec::FrameInfo frameInfo;
|
||||||
if (fCodec->getFrameInfo(frameToDecode, &frameInfo)) {
|
if (fCodec->codec()->getFrameInfo(frameToDecode, &frameInfo)) {
|
||||||
if (!frameInfo.fFullyReceived) {
|
if (!frameInfo.fFullyReceived) {
|
||||||
SkCodecPrintf("Frame %i not fully received\n", frameToDecode);
|
SkCodecPrintf("Frame %i not fully received\n", frameToDecode);
|
||||||
fFinished = true;
|
fFinished = true;
|
||||||
@ -126,7 +127,7 @@ double SkAnimatedImage::update(double msecs) {
|
|||||||
SkCodecPrintf("Skipping frame %i\n", frameToDecode);
|
SkCodecPrintf("Skipping frame %i\n", frameToDecode);
|
||||||
pastUpdate -= frameInfo.fDuration;
|
pastUpdate -= frameInfo.fDuration;
|
||||||
frameToDecode = (frameToDecode + 1) % frameCount;
|
frameToDecode = (frameToDecode + 1) % frameCount;
|
||||||
if (!fCodec->getFrameInfo(frameToDecode, &frameInfo)) {
|
if (!fCodec->codec()->getFrameInfo(frameToDecode, &frameInfo)) {
|
||||||
SkCodecPrintf("Could not getFrameInfo for frame %i",
|
SkCodecPrintf("Could not getFrameInfo for frame %i",
|
||||||
frameToDecode);
|
frameToDecode);
|
||||||
// Prior call to getFrameInfo succeeded, so use that one.
|
// Prior call to getFrameInfo succeeded, so use that one.
|
||||||
@ -222,9 +223,10 @@ double SkAnimatedImage::update(double msecs) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = fCodec->getPixels(dst->info(), dst->getPixels(), dst->rowBytes(), &options);
|
auto result = fCodec->codec()->getPixels(dst->info(), dst->getPixels(), dst->rowBytes(),
|
||||||
|
&options);
|
||||||
if (result != SkCodec::kSuccess) {
|
if (result != SkCodec::kSuccess) {
|
||||||
SkCodecPrintf("error %i, frame %i of %i\n", result, frameToDecode, fCodec->getFrameCount());
|
SkCodecPrintf("error %i, frame %i of %i\n", result, frameToDecode, frameCount);
|
||||||
// Reset to the beginning.
|
// Reset to the beginning.
|
||||||
fActiveFrame.fIndex = SkCodec::kNone;
|
fActiveFrame.fIndex = SkCodec::kNone;
|
||||||
return 0.0;
|
return 0.0;
|
@ -237,8 +237,8 @@ SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels,
|
|||||||
return kInvalidParameters;
|
return kInvalidParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.fSubset || info.dimensions() != fSrcInfo.dimensions()) {
|
if (options.fSubset) {
|
||||||
// If we add support for these, we need to update the code that zeroes
|
// If we add support for this, we need to update the code that zeroes
|
||||||
// a kRestoreBGColor frame.
|
// a kRestoreBGColor frame.
|
||||||
return kInvalidParameters;
|
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
|
// If a frame after the required frame is provided, there is no
|
||||||
// need to clear, since it must be covered by the desired frame.
|
// need to clear, since it must be covered by the desired frame.
|
||||||
if (options.fPriorFrame == requiredFrame) {
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -402,7 +402,7 @@ SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
|
|||||||
SkASSERT(0 == index || index < fFrameHolder.size());
|
SkASSERT(0 == index || index < fFrameHolder.size());
|
||||||
|
|
||||||
const auto& srcInfo = this->getInfo();
|
const auto& srcInfo = this->getInfo();
|
||||||
SkASSERT(0 == index || (!options.fSubset && dstInfo.dimensions() == srcInfo.dimensions()));
|
SkASSERT(0 == index || !options.fSubset);
|
||||||
|
|
||||||
WebPDecoderConfig config;
|
WebPDecoderConfig config;
|
||||||
if (0 == WebPInitDecoderConfig(&config)) {
|
if (0 == WebPInitDecoderConfig(&config)) {
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
* found in the LICENSE file.
|
* found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "SkAndroidCodec.h"
|
||||||
#include "SkBitmap.h"
|
#include "SkBitmap.h"
|
||||||
#include "SkCodec.h"
|
#include "SkCodec.h"
|
||||||
#include "SkCommonFlags.h"
|
#include "SkCommonFlags.h"
|
||||||
@ -386,3 +387,52 @@ DEF_TEST(Codec_frames, r) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that a webp image can be animated scaled down. This image has a
|
||||||
|
// kRestoreBG frame, so it is an interesting image to test. After decoding that
|
||||||
|
// frame, we have to erase its rectangle. The rectangle has to be adjusted
|
||||||
|
// based on the scaled size.
|
||||||
|
DEF_TEST(AndroidCodec_animated, r) {
|
||||||
|
if (GetResourcePath().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* file = "images/required.webp";
|
||||||
|
sk_sp<SkData> data(GetResourceAsData(file));
|
||||||
|
if (!data) {
|
||||||
|
ERRORF(r, "Missing %s", file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
|
||||||
|
if (!codec) {
|
||||||
|
ERRORF(r, "Failed to decode %s", file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto info = codec->getInfo().makeAlphaType(kPremul_SkAlphaType);
|
||||||
|
|
||||||
|
for (int sampleSize : { 8, 32, 100 }) {
|
||||||
|
auto dimensions = codec->codec()->getScaledDimensions(1.0f / sampleSize);
|
||||||
|
info = info.makeWH(dimensions.width(), dimensions.height());
|
||||||
|
SkBitmap bm;
|
||||||
|
bm.allocPixels(info);
|
||||||
|
|
||||||
|
SkCodec::Options options;
|
||||||
|
for (int i = 0; i < codec->codec()->getFrameCount(); ++i) {
|
||||||
|
SkCodec::FrameInfo frameInfo;
|
||||||
|
REPORTER_ASSERT(r, codec->codec()->getFrameInfo(i, &frameInfo));
|
||||||
|
if (5 == i) {
|
||||||
|
REPORTER_ASSERT(r, frameInfo.fDisposalMethod
|
||||||
|
== SkCodecAnimation::DisposalMethod::kRestoreBGColor);
|
||||||
|
}
|
||||||
|
options.fFrameIndex = i;
|
||||||
|
options.fPriorFrame = i - 1;
|
||||||
|
info = info.makeAlphaType(frameInfo.fAlphaType);
|
||||||
|
|
||||||
|
const auto result = codec->codec()->getPixels(info, bm.getPixels(), bm.rowBytes(),
|
||||||
|
&options);
|
||||||
|
REPORTER_ASSERT(r, result == SkCodec::kSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user