1 /*
2 * Copyright 2014 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/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkFilterQuality.h"
13 #include "include/core/SkImage.h"
14 #include "include/core/SkImageFilter.h"
15 #include "include/core/SkMatrix.h"
16 #include "include/core/SkPaint.h"
17 #include "include/core/SkPath.h"
18 #include "include/core/SkPicture.h"
19 #include "include/core/SkPictureRecorder.h"
20 #include "include/core/SkPoint.h"
21 #include "include/core/SkRRect.h"
22 #include "include/core/SkRect.h"
23 #include "include/core/SkRefCnt.h"
24 #include "include/core/SkScalar.h"
25 #include "include/core/SkSize.h"
26 #include "include/core/SkString.h"
27 #include "include/core/SkSurface.h"
28 #include "include/core/SkTypes.h"
29 #include "include/effects/SkImageFilters.h"
30 #include "include/private/SkTArray.h"
31
32 #include <utility>
33
34 namespace skiagm {
35
36 // Each method of this type must draw its geometry inside 'r' using 'p'
37 typedef void(*drawMth)(SkCanvas* canvas, const SkRect& r, const SkPaint& p);
38
draw_rect(SkCanvas * canvas,const SkRect & r,const SkPaint & p)39 static void draw_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
40 canvas->drawRect(r, p);
41 }
42
draw_oval(SkCanvas * canvas,const SkRect & r,const SkPaint & p)43 static void draw_oval(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
44 canvas->drawOval(r, p);
45 }
46
draw_rrect(SkCanvas * canvas,const SkRect & r,const SkPaint & p)47 static void draw_rrect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
48 SkScalar xRad = r.width() / 4.0f;
49 SkScalar yRad = r.height() / 4.0f;
50
51 SkRRect rr;
52 rr.setRectXY(r, xRad, yRad);
53 canvas->drawRRect(rr, p);
54 }
55
draw_drrect(SkCanvas * canvas,const SkRect & r,const SkPaint & p)56 static void draw_drrect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
57 SkScalar xRad = r.width() / 4.0f;
58 SkScalar yRad = r.height() / 4.0f;
59
60 SkRRect outer;
61 outer.setRectXY(r, xRad, yRad);
62 SkRRect inner = outer;
63 inner.inset(xRad, yRad);
64 canvas->drawDRRect(outer, inner, p);
65 }
66
draw_path(SkCanvas * canvas,const SkRect & r,const SkPaint & p)67 static void draw_path(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
68 canvas->drawPath(SkPath::Polygon({
69 {r.fLeft, r.fTop},
70 {r.fLeft, r.fBottom},
71 {r.fRight, r.fBottom},
72 }, true), p);
73 }
74
draw_points(SkCanvas * canvas,const SkRect & r,const SkPaint & p)75 static void draw_points(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
76 SkPoint pts0[2] = { { r.fLeft, r.fTop }, { r.fRight, r.fBottom } };
77 SkPoint pts1[2] = { { r.fLeft, r.fBottom }, { r.fRight, r.fTop } };
78
79 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts0, p);
80 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts1, p);
81 }
82
draw_bitmap(SkCanvas * canvas,const SkRect & r,const SkPaint & p)83 static void draw_bitmap(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
84 SkBitmap bm;
85
86 bm.allocN32Pixels(64, 64);
87 SkCanvas temp(bm);
88 temp.clear(SK_ColorMAGENTA);
89
90 canvas->drawImageRect(bm.asImage(), r, SkSamplingOptions(), &p);
91 }
92
93 constexpr drawMth gDrawMthds[] = {
94 draw_rect, draw_oval, draw_rrect, draw_drrect, draw_path, draw_points, draw_bitmap
95 };
96
add_paint(SkTArray<SkPaint> * paints,sk_sp<SkImageFilter> filter)97 static void add_paint(SkTArray<SkPaint>* paints, sk_sp<SkImageFilter> filter) {
98 SkPaint& p = paints->push_back();
99 p.setImageFilter(std::move(filter));
100 SkASSERT(p.canComputeFastBounds());
101 }
102
103 // Create a selection of imagefilter-based paints to test
create_paints(SkTArray<SkPaint> * paints,sk_sp<SkImageFilter> source)104 static void create_paints(SkTArray<SkPaint>* paints, sk_sp<SkImageFilter> source) {
105 {
106 SkMatrix scale;
107 scale.setScale(2.0f, 2.0f);
108
109 sk_sp<SkImageFilter> scaleMIF(
110 SkImageFilters::MatrixTransform(scale, SkSamplingOptions(SkFilterMode::kLinear),
111 source));
112
113 add_paint(paints, std::move(scaleMIF));
114 }
115
116 {
117 SkMatrix rot;
118 rot.setRotate(-33.3f);
119
120 sk_sp<SkImageFilter> rotMIF(
121 SkImageFilters::MatrixTransform(rot, SkSamplingOptions(SkFilterMode::kLinear), source));
122
123 add_paint(paints, std::move(rotMIF));
124 }
125
126 {
127 SkRect src = SkRect::MakeXYWH(20, 20, 10, 10);
128 SkRect dst = SkRect::MakeXYWH(30, 30, 30, 30);
129 sk_sp<SkImageFilter> tileIF(SkImageFilters::Tile(src, dst, nullptr));
130
131 add_paint(paints, std::move(tileIF));
132 }
133
134 {
135 sk_sp<SkImageFilter> dsif =
136 SkImageFilters::DropShadow(10.0f, 10.0f, 3.0f, 3.0f, SK_ColorRED, source);
137
138 add_paint(paints, std::move(dsif));
139 }
140
141 {
142 sk_sp<SkImageFilter> dsif =
143 SkImageFilters::DropShadowOnly(27.0f, 27.0f, 3.0f, 3.0f, SK_ColorRED, source);
144
145 add_paint(paints, std::move(dsif));
146 }
147
148 add_paint(paints, SkImageFilters::Blur(3, 3, source));
149 add_paint(paints, SkImageFilters::Offset(15, 15, source));
150 }
151
152 // This GM visualizes the fast bounds for various combinations of geometry
153 // and image filter
154 class ImageFilterFastBoundGM : public GM {
155 public:
ImageFilterFastBoundGM()156 ImageFilterFastBoundGM() {
157 this->setBGColor(0xFFCCCCCC);
158 }
159
160 protected:
161 static constexpr int kTileWidth = 100;
162 static constexpr int kTileHeight = 100;
163 static constexpr int kNumVertTiles = 7;
164 static constexpr int kNumXtraCols = 2;
165
onShortName()166 SkString onShortName() override { return SkString("filterfastbounds"); }
167
onISize()168 SkISize onISize() override {
169 return SkISize::Make((SK_ARRAY_COUNT(gDrawMthds) + kNumXtraCols) * kTileWidth,
170 kNumVertTiles * kTileHeight);
171 }
172
draw_geom_with_paint(drawMth draw,const SkIPoint & off,SkCanvas * canvas,const SkPaint & p)173 static void draw_geom_with_paint(drawMth draw, const SkIPoint& off,
174 SkCanvas* canvas, const SkPaint& p) {
175 SkPaint redStroked;
176 redStroked.setColor(SK_ColorRED);
177 redStroked.setStyle(SkPaint::kStroke_Style);
178
179 SkPaint blueStroked;
180 blueStroked.setColor(SK_ColorBLUE);
181 blueStroked.setStyle(SkPaint::kStroke_Style);
182
183 const SkRect r = SkRect::MakeLTRB(20, 20, 30, 30);
184 SkRect storage;
185
186 canvas->save();
187 canvas->translate(SkIntToScalar(off.fX), SkIntToScalar(off.fY));
188 canvas->scale(1.5f, 1.5f);
189
190 const SkRect& fastBound = p.computeFastBounds(r, &storage);
191
192 canvas->save();
193 canvas->clipRect(fastBound);
194 (*draw)(canvas, r, p);
195 canvas->restore();
196
197 canvas->drawRect(r, redStroked);
198 canvas->drawRect(fastBound, blueStroked);
199 canvas->restore();
200 }
201
draw_savelayer_with_paint(const SkIPoint & off,SkCanvas * canvas,const SkPaint & p)202 static void draw_savelayer_with_paint(const SkIPoint& off,
203 SkCanvas* canvas,
204 const SkPaint& p) {
205 SkPaint redStroked;
206 redStroked.setColor(SK_ColorRED);
207 redStroked.setStyle(SkPaint::kStroke_Style);
208
209 SkPaint blueStroked;
210 blueStroked.setColor(SK_ColorBLUE);
211 blueStroked.setStyle(SkPaint::kStroke_Style);
212
213 const SkRect bounds = SkRect::MakeWH(10, 10);
214 SkRect storage;
215
216 canvas->save();
217 canvas->translate(30, 30);
218 canvas->translate(SkIntToScalar(off.fX), SkIntToScalar(off.fY));
219 canvas->scale(1.5f, 1.5f);
220
221 const SkRect& fastBound = p.computeFastBounds(bounds, &storage);
222
223 canvas->saveLayer(&fastBound, &p);
224 canvas->restore();
225
226 canvas->drawRect(bounds, redStroked);
227 canvas->drawRect(fastBound, blueStroked);
228 canvas->restore();
229 }
230
onDraw(SkCanvas * canvas)231 void onDraw(SkCanvas* canvas) override {
232
233 SkPaint blackFill;
234
235 //-----------
236 // Normal paints (no source)
237 SkTArray<SkPaint> paints;
238 create_paints(&paints, nullptr);
239
240 //-----------
241 // Paints with a PictureImageFilter as a source
242 sk_sp<SkPicture> pic;
243
244 {
245 SkPictureRecorder rec;
246
247 SkCanvas* c = rec.beginRecording(10, 10);
248 c->drawRect(SkRect::MakeWH(10, 10), blackFill);
249 pic = rec.finishRecordingAsPicture();
250 }
251
252 SkTArray<SkPaint> pifPaints;
253 create_paints(&pifPaints, SkImageFilters::Picture(pic));
254
255 //-----------
256 // Paints with a SkImageSource as a source
257
258 auto surface(SkSurface::MakeRasterN32Premul(10, 10));
259 {
260 SkPaint p;
261 SkCanvas* temp = surface->getCanvas();
262 temp->clear(SK_ColorYELLOW);
263 p.setColor(SK_ColorBLUE);
264 temp->drawRect(SkRect::MakeLTRB(5, 5, 10, 10), p);
265 p.setColor(SK_ColorGREEN);
266 temp->drawRect(SkRect::MakeLTRB(5, 0, 10, 5), p);
267 }
268
269 sk_sp<SkImage> image(surface->makeImageSnapshot());
270 sk_sp<SkImageFilter> imageSource(SkImageFilters::Image(std::move(image)));
271 SkTArray<SkPaint> bmsPaints;
272 create_paints(&bmsPaints, std::move(imageSource));
273
274 //-----------
275 SkASSERT(paints.count() == kNumVertTiles);
276 SkASSERT(paints.count() == pifPaints.count());
277 SkASSERT(paints.count() == bmsPaints.count());
278
279 // horizontal separators
280 for (int i = 1; i < paints.count(); ++i) {
281 canvas->drawLine(0,
282 i*SkIntToScalar(kTileHeight),
283 SkIntToScalar((SK_ARRAY_COUNT(gDrawMthds) + kNumXtraCols)*kTileWidth),
284 i*SkIntToScalar(kTileHeight),
285 blackFill);
286 }
287 // vertical separators
288 for (int i = 0; i < (int)SK_ARRAY_COUNT(gDrawMthds) + kNumXtraCols; ++i) {
289 canvas->drawLine(SkIntToScalar(i * kTileWidth),
290 0,
291 SkIntToScalar(i * kTileWidth),
292 SkIntToScalar(paints.count() * kTileWidth),
293 blackFill);
294 }
295
296 // A column of saveLayers with PictureImageFilters
297 for (int i = 0; i < pifPaints.count(); ++i) {
298 draw_savelayer_with_paint(SkIPoint::Make(0, i*kTileHeight),
299 canvas, pifPaints[i]);
300 }
301
302 // A column of saveLayers with BitmapSources
303 for (int i = 0; i < pifPaints.count(); ++i) {
304 draw_savelayer_with_paint(SkIPoint::Make(kTileWidth, i*kTileHeight),
305 canvas, bmsPaints[i]);
306 }
307
308 // Multiple columns with different geometry
309 for (int i = 0; i < (int)SK_ARRAY_COUNT(gDrawMthds); ++i) {
310 for (int j = 0; j < paints.count(); ++j) {
311 draw_geom_with_paint(*gDrawMthds[i],
312 SkIPoint::Make((i+kNumXtraCols) * kTileWidth, j*kTileHeight),
313 canvas, paints[j]);
314 }
315 }
316
317 }
318
319 private:
320 using INHERITED = GM;
321 };
322
323 //////////////////////////////////////////////////////////////////////////////
324
325 DEF_GM(return new ImageFilterFastBoundGM;)
326 } // namespace skiagm
327