xref: /aosp_15_r20/external/skia/gm/animated_image_orientation.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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