xref: /aosp_15_r20/external/skia/gm/anisotropic.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2015 Google Inc.
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/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkColorSpace.h"
12 #include "include/core/SkImage.h"
13 #include "include/core/SkPaint.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkScalar.h"
16 #include "include/core/SkShader.h"
17 #include "include/core/SkSize.h"
18 #include "include/core/SkString.h"
19 #include "include/core/SkSurface.h"
20 #include "include/gpu/GpuTypes.h"
21 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
22 
23 namespace skiagm {
24 
25 // This GM exercises anisotropic image scaling.
26 class AnisotropicGM : public GM {
27 public:
28     enum class Mode { kLinear, kMip, kAniso };
29 
AnisotropicGM(Mode mode)30     AnisotropicGM(Mode mode) : fMode(mode) {
31         switch (fMode) {
32             case Mode::kLinear:
33                 fSampling = SkSamplingOptions(SkFilterMode::kLinear);
34                 break;
35             case Mode::kMip:
36                 fSampling = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
37                 break;
38             case Mode::kAniso:
39                 fSampling = SkSamplingOptions::Aniso(16);
40                 break;
41         }
42         this->setBGColor(0xFFCCCCCC);
43     }
44 
45 protected:
getName() const46     SkString getName() const override {
47         SkString name("anisotropic_image_scale_");
48         switch (fMode) {
49             case Mode::kLinear:
50                 name += "linear";
51                 break;
52             case Mode::kMip:
53                 name += "mip";
54                 break;
55             case Mode::kAniso:
56                 name += "aniso";
57                 break;
58         }
59         return name;
60     }
61 
getISize()62     SkISize getISize() override {
63         return SkISize::Make(2*kImageSize + 3*kSpacer,
64                              kNumVertImages*kImageSize + (kNumVertImages+1)*kSpacer);
65     }
66 
67     // Create an image consisting of lines radiating from its center
onOnceBeforeDraw()68     void onOnceBeforeDraw() override {
69         constexpr int kNumLines = 100;
70         constexpr SkScalar kAngleStep = 360.0f / kNumLines;
71         constexpr int kInnerOffset = 10;
72 
73         auto info = SkImageInfo::MakeN32(kImageSize, kImageSize, kOpaque_SkAlphaType);
74         auto surf = SkSurfaces::Raster(info);
75         auto canvas = surf->getCanvas();
76 
77         canvas->clear(SK_ColorWHITE);
78 
79         SkPaint p;
80         p.setAntiAlias(true);
81 
82         SkScalar angle = 0.0f, sin, cos;
83 
84         canvas->translate(kImageSize/2.0f, kImageSize/2.0f);
85         for (int i = 0; i < kNumLines; ++i, angle += kAngleStep) {
86             sin = SkScalarSin(angle);
87             cos = SkScalarCos(angle);
88             canvas->drawLine(cos * kInnerOffset, sin * kInnerOffset,
89                              cos * kImageSize/2, sin * kImageSize/2, p);
90         }
91         fImage = surf->makeImageSnapshot();
92     }
93 
draw(SkCanvas * canvas,int x,int y,int xSize,int ySize)94     void draw(SkCanvas* canvas, int x, int y, int xSize, int ySize) {
95         SkRect r = SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
96                                     SkIntToScalar(xSize), SkIntToScalar(ySize));
97         canvas->drawImageRect(fImage, r, fSampling);
98     }
99 
onDraw(SkCanvas * canvas)100     void onDraw(SkCanvas* canvas) override {
101         SkScalar gScales[] = { 0.9f, 0.8f, 0.75f, 0.6f, 0.5f, 0.4f, 0.25f, 0.2f, 0.1f };
102 
103         SkASSERT(kNumVertImages-1 == (int)std::size(gScales)/2);
104 
105         // Minimize vertically
106         for (int i = 0; i < (int)std::size(gScales); ++i) {
107             int height = SkScalarFloorToInt(fImage->height() * gScales[i]);
108 
109             int yOff;
110             if (i <= (int)std::size(gScales)/2) {
111                 yOff = kSpacer + i * (fImage->height() + kSpacer);
112             } else {
113                 // Position the more highly squashed images with their less squashed counterparts
114                 yOff = (std::size(gScales) - i) * (fImage->height() + kSpacer) - height;
115             }
116 
117             this->draw(canvas, kSpacer, yOff, fImage->width(), height);
118         }
119 
120         // Minimize horizontally
121         for (int i = 0; i < (int)std::size(gScales); ++i) {
122             int width = SkScalarFloorToInt(fImage->width() * gScales[i]);
123 
124             int xOff, yOff;
125             if (i <= (int)std::size(gScales)/2) {
126                 xOff = fImage->width() + 2*kSpacer;
127                 yOff = kSpacer + i * (fImage->height() + kSpacer);
128             } else {
129                 // Position the more highly squashed images with their less squashed counterparts
130                 xOff = fImage->width() + 2*kSpacer + fImage->width() - width;
131                 yOff = kSpacer + (std::size(gScales) - i - 1) * (fImage->height() + kSpacer);
132             }
133 
134             this->draw(canvas, xOff, yOff, width, fImage->height());
135         }
136     }
137 
138 private:
139     inline static constexpr int kImageSize     = 256;
140     inline static constexpr int kSpacer        = 10;
141     inline static constexpr int kNumVertImages = 5;
142 
143     sk_sp<SkImage>    fImage;
144     SkSamplingOptions fSampling;
145     Mode              fMode;
146 
147     using INHERITED = GM;
148 };
149 
150 //////////////////////////////////////////////////////////////////////////////
151 
152 DEF_GM(return new AnisotropicGM(AnisotropicGM::Mode::kLinear);)
153 DEF_GM(return new AnisotropicGM(AnisotropicGM::Mode::kMip);)
154 DEF_GM(return new AnisotropicGM(AnisotropicGM::Mode::kAniso);)
155 
156 //////////////////////////////////////////////////////////////////////////////
157 
158 class AnisoMipsGM : public GM {
159 public:
160     AnisoMipsGM() = default;
161 
162 protected:
getName() const163     SkString getName() const override { return SkString("anisomips"); }
164 
getISize()165     SkISize getISize() override { return SkISize::Make(520, 260); }
166 
updateImage(SkSurface * surf,SkColor color)167     sk_sp<SkImage> updateImage(SkSurface* surf, SkColor color) {
168         surf->getCanvas()->clear(color);
169         SkPaint paint;
170         paint.setColor(~color | 0xFF000000);
171         surf->getCanvas()->drawRect(SkRect::MakeLTRB(surf->width() *2/5.f,
172                                                      surf->height()*2/5.f,
173                                                      surf->width() *3/5.f,
174                                                      surf->height()*3/5.f),
175                                     paint);
176         return surf->makeImageSnapshot()->withDefaultMipmaps();
177     }
178 
onDraw(SkCanvas * canvas)179     void onDraw(SkCanvas* canvas) override {
180         auto ct = canvas->imageInfo().colorType() == kUnknown_SkColorType
181                           ? kRGBA_8888_SkColorType
182                           : canvas->imageInfo().colorType();
183         auto ii = SkImageInfo::Make(kImageSize,
184                                     kImageSize,
185                                     ct,
186                                     kPremul_SkAlphaType,
187                                     canvas->imageInfo().refColorSpace());
188         // In GPU mode we want a surface that is created with mipmaps to ensure that we exercise the
189         // case where the SkSurface and SkImage share a texture. If the surface texture isn't
190         // created with MIPs then asking for a mipmapped image will cause a copy to a mipped
191         // texture.
192         sk_sp<SkSurface> surface;
193         if (auto rc = canvas->recordingContext()) {
194             surface = SkSurfaces::RenderTarget(rc,
195                                                skgpu::Budgeted::kYes,
196                                                ii,
197                                                /* sampleCount= */ 1,
198                                                kTopLeft_GrSurfaceOrigin,
199                                                /*surfaceProps=*/nullptr,
200                                                /*shouldCreateWithMips=*/true);
201             if (!surface) {
202                 // We could be in an abandoned context situation.
203                 return;
204             }
205         } else {
206             surface = canvas->makeSurface(ii);
207             if (!surface) {  // could be a recording canvas.
208                 surface = SkSurfaces::Raster(ii);
209             }
210         }
211 
212         static constexpr float kScales[] = {1.f, 0.5f, 0.25f, 0.125f};
213         SkColor kColors[] = {0xFFF0F0F0, SK_ColorBLUE, SK_ColorGREEN, SK_ColorRED};
214         static const SkSamplingOptions kSampling = SkSamplingOptions::Aniso(16);
215 
216         for (bool shader : {false, true}) {
217             int c = 0;
218             canvas->save();
219             for (float sy : kScales) {
220                 canvas->save();
221                 for (float sx : kScales) {
222                     canvas->save();
223                     canvas->scale(sx, sy);
224                     auto image = this->updateImage(surface.get(), kColors[c]);
225                     if (shader) {
226                         SkPaint paint;
227                         paint.setShader(image->makeShader(kSampling));
228                         canvas->drawRect(SkRect::Make(image->dimensions()), paint);
229                     } else {
230                         canvas->drawImage(image, 0, 0, kSampling);
231                     }
232                     canvas->restore();
233                     canvas->translate(ii.width() * sx + kPad, 0);
234                     c = (c + 1) % std::size(kColors);
235                 }
236                 canvas->restore();
237                 canvas->translate(0, ii.width() * sy + kPad);
238             }
239             canvas->restore();
240             for (float sx : kScales) {
241                 canvas->translate(ii.width() * sx + kPad, 0);
242             }
243         }
244     }
245 
246 private:
247     inline static constexpr int kImageSize = 128;
248     inline static constexpr int kPad = 5;
249 
250     using INHERITED = GM;
251 };
252 
253 //////////////////////////////////////////////////////////////////////////////
254 
255 DEF_GM(return new AnisoMipsGM();)
256 
257 }  // namespace skiagm
258