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