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