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