xref: /aosp_15_r20/external/skia/gm/perlinnoise.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2013 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/SkColorFilter.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/core/SkPaint.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkRefCnt.h"
16 #include "include/core/SkScalar.h"
17 #include "include/core/SkShader.h"
18 #include "include/core/SkSize.h"
19 #include "include/core/SkString.h"
20 #include "include/effects/SkColorMatrix.h"
21 #include "include/effects/SkImageFilters.h"
22 #include "include/effects/SkPerlinNoiseShader.h"
23 
24 #include <utility>
25 
26 namespace {
27 
28 enum class Type {
29     kFractalNoise,
30     kTurbulence,
31 };
32 
noise_shader(Type type,float baseFrequencyX,float baseFrequencyY,int numOctaves,float seed,bool stitchTiles,SkISize size)33 sk_sp<SkShader> noise_shader(Type type,
34                              float baseFrequencyX,
35                              float baseFrequencyY,
36                              int numOctaves,
37                              float seed,
38                              bool stitchTiles,
39                              SkISize size) {
40     return (type == Type::kFractalNoise)
41                    ? SkShaders::MakeFractalNoise(baseFrequencyX,
42                                                  baseFrequencyY,
43                                                  numOctaves,
44                                                  seed,
45                                                  stitchTiles ? &size : nullptr)
46                    : SkShaders::MakeTurbulence(baseFrequencyX,
47                                                baseFrequencyY,
48                                                numOctaves,
49                                                seed,
50                                                stitchTiles ? &size : nullptr);
51 }
52 
53 class PerlinNoiseGM : public skiagm::GM {
54     static constexpr SkISize kSize = {80, 80};
55 
getName() const56     SkString getName() const override { return SkString("perlinnoise"); }
57 
getISize()58     SkISize getISize() override { return {220, 620}; }
59 
drawRect(SkCanvas * canvas,SkPoint pt,const SkPaint & paint,const SkISize & size)60     void drawRect(SkCanvas* canvas, SkPoint pt, const SkPaint& paint, const SkISize& size) {
61         canvas->save();
62         canvas->translate(pt.fX, pt.fY);
63         SkRect r = SkRect::MakeWH(SkIntToScalar(size.width()), SkIntToScalar(size.height()));
64         canvas->drawRect(r, paint);
65         canvas->restore();
66     }
67 
test(SkCanvas * canvas,SkPoint pt,Type type,bool stitch,SkVector baseFrequency,int numOctaves,float seed,SkISize tileSize={40, 40})68     void test(SkCanvas* canvas, SkPoint pt, Type type, bool stitch,
69               SkVector baseFrequency, int numOctaves, float seed, SkISize tileSize = {40, 40}) {
70         sk_sp<SkShader> shader = noise_shader(type,
71                                               baseFrequency.fX,
72                                               baseFrequency.fY,
73                                               numOctaves,
74                                               seed,
75                                               stitch,
76                                               tileSize);
77         SkPaint paint;
78         paint.setShader(std::move(shader));
79         if (stitch) {
80             this->drawRect(canvas, pt, paint, tileSize);
81             pt.fX += tileSize.width();
82             this->drawRect(canvas, pt, paint, tileSize);
83             pt.fY += tileSize.height();
84             this->drawRect(canvas, pt, paint, tileSize);
85             pt.fX -= tileSize.width();
86             this->drawRect(canvas, pt, paint, tileSize);
87         } else {
88             this->drawRect(canvas, pt, paint, kSize);
89         }
90     }
91 
onDraw(SkCanvas * canvas)92     void onDraw(SkCanvas* canvas) override {
93         this->test(canvas, SkPoint{  0,   0}, Type::kFractalNoise, /*stitch=*/false,
94                    SkVector{0.1f, 0.1f}, /*numOctaves=*/0, /*seed=*/0);
95         this->test(canvas, SkPoint{100,   0}, Type::kTurbulence, /*stitch=*/false,
96                    SkVector{0.1f, 0.1f}, /*numOctaves=*/0, /*seed=*/0);
97 
98         this->test(canvas, SkPoint{  0, 100}, Type::kFractalNoise, /*stitch=*/false,
99                    SkVector{0.1f, 0.1f}, /*numOctaves=*/2, /*seed=*/0);
100         this->test(canvas, SkPoint{100, 100}, Type::kFractalNoise, /*stitch=*/true,
101                    SkVector{0.05f, 0.1f}, /*numOctaves=*/1, /*seed=*/0);
102 
103         this->test(canvas, SkPoint{  0, 200}, Type::kTurbulence, /*stitch=*/true,
104                    SkVector{0.1f, 0.1f}, /*numOctaves=*/1, /*seed=*/0);
105         this->test(canvas, SkPoint{100, 200}, Type::kTurbulence, /*stitch=*/false,
106                    SkVector{0.2f, 0.4f}, /*numOctaves=*/5, /*seed=*/0);
107 
108         this->test(canvas, SkPoint{  0, 300}, Type::kFractalNoise, /*stitch=*/false,
109                    SkVector{0.1f, 0.1f}, /*numOctaves=*/3, /*seed=*/1);
110         this->test(canvas, SkPoint{100, 300}, Type::kFractalNoise, /*stitch=*/false,
111                    SkVector{0.1f, 0.1f}, /*numOctaves=*/3, /*seed=*/4);
112 
113         canvas->save();
114         canvas->scale(0.75f, 1.0f);
115 
116         this->test(canvas, SkPoint{  0, 400}, Type::kFractalNoise, /*stitch=*/false,
117                    SkVector{0.1f, 0.1f}, /*numOctaves=*/2, /*seed=*/0);
118         this->test(canvas, SkPoint{100, 400}, Type::kFractalNoise, /*stitch=*/true,
119                    SkVector{0.1f, 0.05f}, /*numOctaves=*/1, /*seed=*/0);
120 
121         canvas->restore();
122 
123         // Matches Chromium test case in svg/filters/feTurbulence-tiled.svg
124         this->test(canvas, SkPoint{  0, 500}, Type::kTurbulence, /*stitch=*/true,
125                    SkVector{0.03f, 0.03f}, /*numOctaves=*/1, /*seed=*/0, /*tileSize=*/{50, 50});
126 
127         // Matches Chromium test case in css3/filters/effect-reference.html
128         this->test(canvas, SkPoint{120, 500}, Type::kTurbulence, /*stitch=*/false,
129                    SkVector{0.05f, 0.05f}, /*numOctaves=*/2, /*seed=*/0);
130 
131     }
132 
133 private:
134     using INHERITED = GM;
135 };
136 
137 class PerlinNoiseLocalMatrixGM : public skiagm::GM {
138     static constexpr SkISize kSize = {80, 80};
139 
getName() const140     SkString getName() const override { return SkString("perlinnoise_localmatrix"); }
141 
getISize()142     SkISize getISize() override { return {640, 480}; }
143 
onDraw(SkCanvas * canvas)144     void onDraw(SkCanvas* canvas) override {
145         canvas->translate(10, 10);
146 
147         SkPaint paint;
148         paint.setShader(noise_shader(Type::kFractalNoise, 0.1f, 0.1f, 2, 0, false, kSize));
149 
150         const SkScalar w = SkIntToScalar(kSize.width());
151         const SkScalar h = SkIntToScalar(kSize.height());
152 
153         SkRect r = SkRect::MakeWH(w, h);
154         canvas->drawRect(r, paint);
155 
156         canvas->save();
157         canvas->translate(w * 5/4, 0);
158         canvas->drawRect(r, paint);
159         canvas->restore();
160 
161         canvas->save();
162         canvas->translate(0, h + 10);
163         canvas->scale(2, 2);
164         canvas->drawRect(r, paint);
165         canvas->restore();
166 
167         canvas->save();
168         canvas->translate(w + 100, h + 10);
169         canvas->scale(2, 2);
170         canvas->drawRect(r, paint);
171         canvas->restore();
172 
173         // The next row should draw the same as the previous, even though we are using a local
174         // matrix instead of the canvas.
175 
176         canvas->translate(0, h * 2 + 10);
177 
178         SkMatrix lm;
179         lm.setScale(2, 2);
180         paint.setShader(paint.getShader()->makeWithLocalMatrix(lm));
181         r.fRight += r.width();
182         r.fBottom += r.height();
183 
184         canvas->save();
185         canvas->translate(0, h + 10);
186         canvas->drawRect(r, paint);
187         canvas->restore();
188 
189         canvas->save();
190         canvas->translate(w + 100, h + 10);
191         canvas->drawRect(r, paint);
192         canvas->restore();
193     }
194 };
195 
196 // Demonstrate skbug.com/14166 (Perlin noise shader doesn't rotate correctly)
197 class PerlinNoiseRotatedGM : public skiagm::GM {
198     static constexpr SkISize kCellSize = { 100, 100 };
199     static constexpr SkISize kRectSize = { 60, 60 };
200     static constexpr int kPad = 10;
201     static constexpr int kCellsX = 3;
202     static constexpr int kCellsY = 2;
203 
getName() const204     SkString getName() const override { return SkString("perlinnoise_rotated"); }
205 
getISize()206     SkISize getISize() override {
207         return {2 * kPad + kCellsX * kCellSize.width(), 2 * kPad + kCellsY * kCellSize.height()};
208     }
209 
onDraw(SkCanvas * canvas)210     void onDraw(SkCanvas* canvas) override {
211         SkPaint outline;
212         outline.setColor(SK_ColorBLACK);
213         outline.setStrokeWidth(2.0f);
214         outline.setStyle(SkPaint::kStroke_Style);
215         outline.setAntiAlias(true);
216 
217         const SkRect kRectToDraw = SkRect::MakeWH(kRectSize.width(), kRectSize.height());
218         const SkRect kMarker = SkRect::MakeWH(5, 5);
219 
220         float yOffset = kPad;
221         for (auto type : { Type::kFractalNoise, Type::kTurbulence }) {
222             float xOffset = kPad;
223 
224             SkPaint p;
225             p.setShader(noise_shader(type, 0.05f, 0.05f, 1, 0, false, kRectSize));
226 
227             for (float rotation : {0.0f, 10.0f, 80.0f}) {
228                 int saveCount = canvas->save();
229                 canvas->translate(xOffset, yOffset);
230 
231                 canvas->drawRect(SkRect::MakeWH(kCellSize.fWidth, kCellSize.fHeight), outline);
232 
233                 canvas->save();
234 
235                 canvas->translate(kCellSize.fWidth / 2.0f, kCellSize.fHeight / 2.0f);
236                 canvas->rotate(rotation);
237                 canvas->translate(-kRectSize.fWidth/2.0f, -kRectSize.fHeight/2.0f);
238 
239                 canvas->drawRect(kRectToDraw, p);
240 
241                 canvas->drawRect(kRectToDraw, outline);
242                 canvas->drawRect(kMarker, outline);
243 
244                 canvas->restoreToCount(saveCount);
245 
246                 xOffset += kCellSize.width();
247             }
248 
249             yOffset += kCellSize.height();
250         }
251     }
252 };
253 
254 // Demonstrate skbug.com/14411 (Intel GPUs show artifacts when applying perlin noise to layers)
255 class PerlinNoiseLayeredGM : public skiagm::GM {
getName() const256     SkString getName() const override { return SkString("perlinnoise_layered"); }
257 
getISize()258     SkISize getISize() override { return {500, 500}; }
259 
onDraw(SkCanvas * canvas)260     void onDraw(SkCanvas* canvas) override {
261         const sk_sp<SkImageFilter> perlin = SkImageFilters::ColorFilter(
262                 SkColorFilters::Matrix(SkColorMatrix()),
263                 SkImageFilters::Shader(SkShaders::MakeFractalNoise(0.3f, 0.3f, 1, 4)));
264 
265         const SkPaint paint;
266         canvas->saveLayer(nullptr, &paint);
267         {
268             SkPaint p;
269             p.setImageFilter(perlin);
270             canvas->drawPaint(p);
271         }
272         canvas->restore();
273 
274         canvas->saveLayer(nullptr, nullptr);
275         {
276             SkPaint p;
277             p.setImageFilter(perlin);
278             canvas->drawPaint(p);
279         }
280         canvas->restore();
281     }
282 };
283 
284 } // anonymous namespace
285 
286 DEF_GM(return new PerlinNoiseGM;)
287 DEF_GM(return new PerlinNoiseLocalMatrixGM;)
288 DEF_GM(return new PerlinNoiseRotatedGM;)
289 DEF_GM(return new PerlinNoiseLayeredGM;)
290