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