2ac6ce8e6e
Change-Id: I70e295a677b8cac3d578e3cd57472c833af03877 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/354336 Reviewed-by: Florin Malita <fmalita@chromium.org> Commit-Queue: Mike Reed <reed@google.com>
166 lines
6.8 KiB
C++
166 lines
6.8 KiB
C++
/*
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "gm/gm.h"
|
|
#include "include/android/SkAnimatedImage.h"
|
|
#include "include/codec/SkAndroidCodec.h"
|
|
#include "include/codec/SkCodec.h"
|
|
#include "include/core/SkBlendMode.h"
|
|
#include "include/core/SkCanvas.h"
|
|
#include "include/core/SkData.h"
|
|
#include "include/core/SkPathBuilder.h"
|
|
#include "include/core/SkPathTypes.h"
|
|
#include "include/core/SkPicture.h"
|
|
#include "include/core/SkPictureRecorder.h"
|
|
#include "include/core/SkRRect.h"
|
|
#include "tools/Resources.h"
|
|
|
|
static sk_sp<SkPicture> post_processor(const SkRect& bounds) {
|
|
int radius = (bounds.width() + bounds.height()) / 6;
|
|
SkPathBuilder pathBuilder;
|
|
pathBuilder.setFillType(SkPathFillType::kInverseEvenOdd)
|
|
.addRRect(SkRRect::MakeRectXY(bounds, radius, radius));
|
|
|
|
SkPaint paint;
|
|
paint.setAntiAlias(true);
|
|
paint.setColor(SK_ColorTRANSPARENT);
|
|
paint.setBlendMode(SkBlendMode::kSrc);
|
|
|
|
SkPictureRecorder recorder;
|
|
auto* canvas = recorder.beginRecording(bounds);
|
|
canvas->drawPath(pathBuilder.detach(), paint);
|
|
return recorder.finishRecordingAsPicture();
|
|
}
|
|
|
|
class AnimatedImageGM : public skiagm::GM {
|
|
const char* fPath;
|
|
const char* fName;
|
|
const int fStep;
|
|
const SkIRect fCropRect;
|
|
SkISize fSize;
|
|
int fTranslate;
|
|
sk_sp<SkData> fData;
|
|
|
|
static const int kMaxFrames = 2;
|
|
|
|
void init() {
|
|
if (!fData) {
|
|
fData = GetResourceAsData(fPath);
|
|
auto codec = SkCodec::MakeFromData(fData);
|
|
auto dimensions = codec->dimensions();
|
|
|
|
fTranslate = std::max(dimensions.width(), dimensions.height()) // may be rotated
|
|
* 1.25f // will be scaled up
|
|
+ 2; // padding
|
|
|
|
fSize = { fTranslate * kMaxFrames
|
|
* 2 // crop and no-crop
|
|
* 2, // post-process and no post-process
|
|
fTranslate * 4 // 4 scales
|
|
* 2 }; // newPictureSnapshot and getCurrentFrame
|
|
}
|
|
}
|
|
public:
|
|
AnimatedImageGM(const char* path, const char* name, int step, SkIRect cropRect)
|
|
: fPath(path)
|
|
, fName(name)
|
|
, fStep(step)
|
|
, fCropRect(cropRect)
|
|
, fSize{0, 0}
|
|
, fTranslate(0)
|
|
{}
|
|
~AnimatedImageGM() override = default;
|
|
|
|
SkString onShortName() override {
|
|
return SkStringPrintf("%s_animated_image", fName);
|
|
}
|
|
|
|
SkISize onISize() override {
|
|
this->init();
|
|
return fSize;
|
|
}
|
|
|
|
void onDraw(SkCanvas* canvas) override {
|
|
this->init();
|
|
for (bool usePic : { true, false }) {
|
|
auto drawProc = [canvas, usePic](const sk_sp<SkAnimatedImage>& animatedImage) {
|
|
if (usePic) {
|
|
sk_sp<SkPicture> pic(animatedImage->newPictureSnapshot());
|
|
canvas->drawPicture(pic);
|
|
} else {
|
|
auto image = animatedImage->getCurrentFrame();
|
|
canvas->drawImage(image, 0, 0, nullptr);
|
|
}
|
|
};
|
|
for (float scale : { 1.25f, 1.0f, .75f, .5f }) {
|
|
canvas->save();
|
|
for (bool doCrop : { false, true }) {
|
|
for (bool doPostProcess : { false, true }) {
|
|
auto codec = SkCodec::MakeFromData(fData);
|
|
const auto origin = codec->getOrigin();
|
|
auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
|
|
auto info = androidCodec->getInfo();
|
|
const auto unscaledSize = SkEncodedOriginSwapsWidthHeight(origin)
|
|
? SkISize{ info.height(), info.width() } : info.dimensions();
|
|
|
|
SkISize scaledSize = { SkScalarFloorToInt(unscaledSize.width() * scale) ,
|
|
SkScalarFloorToInt(unscaledSize.height() * scale) };
|
|
info = info.makeDimensions(scaledSize);
|
|
|
|
auto cropRect = SkIRect::MakeSize(scaledSize);
|
|
if (doCrop) {
|
|
auto matrix = SkMatrix::RectToRect(SkRect::Make(unscaledSize),
|
|
SkRect::Make(scaledSize));
|
|
matrix.preConcat(SkEncodedOriginToMatrix(origin,
|
|
unscaledSize.width(), unscaledSize.height()));
|
|
SkRect cropRectFloat = SkRect::Make(fCropRect);
|
|
matrix.mapRect(&cropRectFloat);
|
|
cropRectFloat.roundOut(&cropRect);
|
|
}
|
|
|
|
sk_sp<SkPicture> postProcessor = doPostProcess
|
|
? post_processor(SkRect::Make(cropRect.size())) : nullptr;
|
|
auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec),
|
|
info, cropRect, std::move(postProcessor));
|
|
animatedImage->setRepetitionCount(0);
|
|
|
|
for (int frame = 0; frame < kMaxFrames; frame++) {
|
|
{
|
|
SkAutoCanvasRestore acr(canvas, doCrop);
|
|
if (doCrop) {
|
|
canvas->translate(cropRect.left(), cropRect.top());
|
|
}
|
|
drawProc(animatedImage);
|
|
}
|
|
|
|
canvas->translate(fTranslate, 0);
|
|
const auto duration = animatedImage->currentFrameDuration();
|
|
if (duration == SkAnimatedImage::kFinished) {
|
|
break;
|
|
}
|
|
for (int i = 0; i < fStep; i++) {
|
|
animatedImage->decodeNextFrame();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
canvas->restore();
|
|
canvas->translate(0, fTranslate);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
DEF_GM( return new AnimatedImageGM("images/stoplight_h.webp", "stoplight", 2,
|
|
// Deliberately not centered in X or Y, and shows all three
|
|
// lights, but otherwise arbitrary.
|
|
SkIRect::MakeLTRB(5, 6, 11, 29)); )
|
|
DEF_GM( return new AnimatedImageGM("images/flightAnim.gif", "flight", 20,
|
|
// Deliberately starts in the upper left corner to exercise
|
|
// a special case, but otherwise arbitrary.
|
|
SkIRect::MakeLTRB(0, 0, 300, 200)); )
|