1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2020 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "gm/gm.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/android/SkAnimatedImage.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkAndroidCodec.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkCodec.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBlendMode.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathBuilder.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathTypes.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPicture.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPictureRecorder.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRRect.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "tools/Resources.h"
22*c8dee2aaSAndroid Build Coastguard Worker
post_processor(const SkRect & bounds)23*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkPicture> post_processor(const SkRect& bounds) {
24*c8dee2aaSAndroid Build Coastguard Worker int radius = (bounds.width() + bounds.height()) / 6;
25*c8dee2aaSAndroid Build Coastguard Worker SkPathBuilder pathBuilder;
26*c8dee2aaSAndroid Build Coastguard Worker pathBuilder.setFillType(SkPathFillType::kInverseEvenOdd)
27*c8dee2aaSAndroid Build Coastguard Worker .addRRect(SkRRect::MakeRectXY(bounds, radius, radius));
28*c8dee2aaSAndroid Build Coastguard Worker
29*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
30*c8dee2aaSAndroid Build Coastguard Worker paint.setAntiAlias(true);
31*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(SK_ColorTRANSPARENT);
32*c8dee2aaSAndroid Build Coastguard Worker paint.setBlendMode(SkBlendMode::kSrc);
33*c8dee2aaSAndroid Build Coastguard Worker
34*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
35*c8dee2aaSAndroid Build Coastguard Worker auto* canvas = recorder.beginRecording(bounds);
36*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(pathBuilder.detach(), paint);
37*c8dee2aaSAndroid Build Coastguard Worker return recorder.finishRecordingAsPicture();
38*c8dee2aaSAndroid Build Coastguard Worker }
39*c8dee2aaSAndroid Build Coastguard Worker
40*c8dee2aaSAndroid Build Coastguard Worker class AnimatedImageGM : public skiagm::GM {
41*c8dee2aaSAndroid Build Coastguard Worker const char* fPath;
42*c8dee2aaSAndroid Build Coastguard Worker const char* fName;
43*c8dee2aaSAndroid Build Coastguard Worker const int fStep;
44*c8dee2aaSAndroid Build Coastguard Worker const SkIRect fCropRect;
45*c8dee2aaSAndroid Build Coastguard Worker SkISize fSize;
46*c8dee2aaSAndroid Build Coastguard Worker int fTranslate;
47*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> fData;
48*c8dee2aaSAndroid Build Coastguard Worker
49*c8dee2aaSAndroid Build Coastguard Worker static const int kMaxFrames = 2;
50*c8dee2aaSAndroid Build Coastguard Worker
init()51*c8dee2aaSAndroid Build Coastguard Worker void init() {
52*c8dee2aaSAndroid Build Coastguard Worker if (!fData) {
53*c8dee2aaSAndroid Build Coastguard Worker fData = GetResourceAsData(fPath);
54*c8dee2aaSAndroid Build Coastguard Worker auto codec = SkCodec::MakeFromData(fData);
55*c8dee2aaSAndroid Build Coastguard Worker auto dimensions = codec->dimensions();
56*c8dee2aaSAndroid Build Coastguard Worker
57*c8dee2aaSAndroid Build Coastguard Worker fTranslate = std::max(dimensions.width(), dimensions.height()) // may be rotated
58*c8dee2aaSAndroid Build Coastguard Worker * 1.25f // will be scaled up
59*c8dee2aaSAndroid Build Coastguard Worker + 2; // padding
60*c8dee2aaSAndroid Build Coastguard Worker
61*c8dee2aaSAndroid Build Coastguard Worker fSize = { fTranslate * kMaxFrames
62*c8dee2aaSAndroid Build Coastguard Worker * 2 // crop and no-crop
63*c8dee2aaSAndroid Build Coastguard Worker * 2, // post-process and no post-process
64*c8dee2aaSAndroid Build Coastguard Worker fTranslate * 4 // 4 scales
65*c8dee2aaSAndroid Build Coastguard Worker * 2 }; // makePictureSnapshot and getCurrentFrame
66*c8dee2aaSAndroid Build Coastguard Worker }
67*c8dee2aaSAndroid Build Coastguard Worker }
68*c8dee2aaSAndroid Build Coastguard Worker public:
AnimatedImageGM(const char * path,const char * name,int step,SkIRect cropRect)69*c8dee2aaSAndroid Build Coastguard Worker AnimatedImageGM(const char* path, const char* name, int step, SkIRect cropRect)
70*c8dee2aaSAndroid Build Coastguard Worker : fPath(path)
71*c8dee2aaSAndroid Build Coastguard Worker , fName(name)
72*c8dee2aaSAndroid Build Coastguard Worker , fStep(step)
73*c8dee2aaSAndroid Build Coastguard Worker , fCropRect(cropRect)
74*c8dee2aaSAndroid Build Coastguard Worker , fSize{0, 0}
75*c8dee2aaSAndroid Build Coastguard Worker , fTranslate(0)
76*c8dee2aaSAndroid Build Coastguard Worker {}
77*c8dee2aaSAndroid Build Coastguard Worker ~AnimatedImageGM() override = default;
78*c8dee2aaSAndroid Build Coastguard Worker
getName() const79*c8dee2aaSAndroid Build Coastguard Worker SkString getName() const override { return SkStringPrintf("%s_animated_image", fName); }
80*c8dee2aaSAndroid Build Coastguard Worker
getISize()81*c8dee2aaSAndroid Build Coastguard Worker SkISize getISize() override {
82*c8dee2aaSAndroid Build Coastguard Worker this->init();
83*c8dee2aaSAndroid Build Coastguard Worker return fSize;
84*c8dee2aaSAndroid Build Coastguard Worker }
85*c8dee2aaSAndroid Build Coastguard Worker
onDraw(SkCanvas * canvas)86*c8dee2aaSAndroid Build Coastguard Worker void onDraw(SkCanvas* canvas) override {
87*c8dee2aaSAndroid Build Coastguard Worker this->init();
88*c8dee2aaSAndroid Build Coastguard Worker for (bool usePic : { true, false }) {
89*c8dee2aaSAndroid Build Coastguard Worker auto drawProc = [canvas, usePic](const sk_sp<SkAnimatedImage>& animatedImage) {
90*c8dee2aaSAndroid Build Coastguard Worker if (usePic) {
91*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> pic = animatedImage->makePictureSnapshot();
92*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPicture(pic);
93*c8dee2aaSAndroid Build Coastguard Worker } else {
94*c8dee2aaSAndroid Build Coastguard Worker auto image = animatedImage->getCurrentFrame();
95*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(image, 0, 0);
96*c8dee2aaSAndroid Build Coastguard Worker }
97*c8dee2aaSAndroid Build Coastguard Worker };
98*c8dee2aaSAndroid Build Coastguard Worker for (float scale : { 1.25f, 1.0f, .75f, .5f }) {
99*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
100*c8dee2aaSAndroid Build Coastguard Worker for (bool doCrop : { false, true }) {
101*c8dee2aaSAndroid Build Coastguard Worker for (bool doPostProcess : { false, true }) {
102*c8dee2aaSAndroid Build Coastguard Worker auto codec = SkCodec::MakeFromData(fData);
103*c8dee2aaSAndroid Build Coastguard Worker const auto origin = codec->getOrigin();
104*c8dee2aaSAndroid Build Coastguard Worker auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
105*c8dee2aaSAndroid Build Coastguard Worker auto info = androidCodec->getInfo();
106*c8dee2aaSAndroid Build Coastguard Worker const auto unscaledSize = SkEncodedOriginSwapsWidthHeight(origin)
107*c8dee2aaSAndroid Build Coastguard Worker ? SkISize{ info.height(), info.width() } : info.dimensions();
108*c8dee2aaSAndroid Build Coastguard Worker
109*c8dee2aaSAndroid Build Coastguard Worker SkISize scaledSize = { SkScalarFloorToInt(unscaledSize.width() * scale) ,
110*c8dee2aaSAndroid Build Coastguard Worker SkScalarFloorToInt(unscaledSize.height() * scale) };
111*c8dee2aaSAndroid Build Coastguard Worker info = info.makeDimensions(scaledSize);
112*c8dee2aaSAndroid Build Coastguard Worker
113*c8dee2aaSAndroid Build Coastguard Worker auto cropRect = SkIRect::MakeSize(scaledSize);
114*c8dee2aaSAndroid Build Coastguard Worker if (doCrop) {
115*c8dee2aaSAndroid Build Coastguard Worker auto matrix = SkMatrix::RectToRect(SkRect::Make(unscaledSize),
116*c8dee2aaSAndroid Build Coastguard Worker SkRect::Make(scaledSize));
117*c8dee2aaSAndroid Build Coastguard Worker matrix.preConcat(SkEncodedOriginToMatrix(origin,
118*c8dee2aaSAndroid Build Coastguard Worker unscaledSize.width(), unscaledSize.height()));
119*c8dee2aaSAndroid Build Coastguard Worker SkRect cropRectFloat = SkRect::Make(fCropRect);
120*c8dee2aaSAndroid Build Coastguard Worker matrix.mapRect(&cropRectFloat);
121*c8dee2aaSAndroid Build Coastguard Worker cropRectFloat.roundOut(&cropRect);
122*c8dee2aaSAndroid Build Coastguard Worker }
123*c8dee2aaSAndroid Build Coastguard Worker
124*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPicture> postProcessor = doPostProcess
125*c8dee2aaSAndroid Build Coastguard Worker ? post_processor(SkRect::Make(cropRect.size())) : nullptr;
126*c8dee2aaSAndroid Build Coastguard Worker auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec),
127*c8dee2aaSAndroid Build Coastguard Worker info, cropRect, std::move(postProcessor));
128*c8dee2aaSAndroid Build Coastguard Worker animatedImage->setRepetitionCount(0);
129*c8dee2aaSAndroid Build Coastguard Worker
130*c8dee2aaSAndroid Build Coastguard Worker for (int frame = 0; frame < kMaxFrames; frame++) {
131*c8dee2aaSAndroid Build Coastguard Worker {
132*c8dee2aaSAndroid Build Coastguard Worker SkAutoCanvasRestore acr(canvas, doCrop);
133*c8dee2aaSAndroid Build Coastguard Worker if (doCrop) {
134*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(cropRect.left(), cropRect.top());
135*c8dee2aaSAndroid Build Coastguard Worker }
136*c8dee2aaSAndroid Build Coastguard Worker drawProc(animatedImage);
137*c8dee2aaSAndroid Build Coastguard Worker }
138*c8dee2aaSAndroid Build Coastguard Worker
139*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(fTranslate, 0);
140*c8dee2aaSAndroid Build Coastguard Worker const auto duration = animatedImage->currentFrameDuration();
141*c8dee2aaSAndroid Build Coastguard Worker if (duration == SkAnimatedImage::kFinished) {
142*c8dee2aaSAndroid Build Coastguard Worker break;
143*c8dee2aaSAndroid Build Coastguard Worker }
144*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < fStep; i++) {
145*c8dee2aaSAndroid Build Coastguard Worker animatedImage->decodeNextFrame();
146*c8dee2aaSAndroid Build Coastguard Worker }
147*c8dee2aaSAndroid Build Coastguard Worker }
148*c8dee2aaSAndroid Build Coastguard Worker }
149*c8dee2aaSAndroid Build Coastguard Worker }
150*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
151*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(0, fTranslate);
152*c8dee2aaSAndroid Build Coastguard Worker }
153*c8dee2aaSAndroid Build Coastguard Worker }
154*c8dee2aaSAndroid Build Coastguard Worker }
155*c8dee2aaSAndroid Build Coastguard Worker };
156*c8dee2aaSAndroid Build Coastguard Worker
157*c8dee2aaSAndroid Build Coastguard Worker DEF_GM( return new AnimatedImageGM("images/stoplight_h.webp", "stoplight", 2,
158*c8dee2aaSAndroid Build Coastguard Worker // Deliberately not centered in X or Y, and shows all three
159*c8dee2aaSAndroid Build Coastguard Worker // lights, but otherwise arbitrary.
160*c8dee2aaSAndroid Build Coastguard Worker SkIRect::MakeLTRB(5, 6, 11, 29)); )
161*c8dee2aaSAndroid Build Coastguard Worker DEF_GM( return new AnimatedImageGM("images/flightAnim.gif", "flight", 20,
162*c8dee2aaSAndroid Build Coastguard Worker // Deliberately starts in the upper left corner to exercise
163*c8dee2aaSAndroid Build Coastguard Worker // a special case, but otherwise arbitrary.
164*c8dee2aaSAndroid Build Coastguard Worker SkIRect::MakeLTRB(0, 0, 300, 200)); )
165