• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 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/SkBlendMode.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkFont.h"
14 #include "include/core/SkImage.h"
15 #include "include/core/SkImageFilter.h"
16 #include "include/core/SkImageInfo.h"
17 #include "include/core/SkMaskFilter.h"
18 #include "include/core/SkMatrix.h"
19 #include "include/core/SkPaint.h"
20 #include "include/core/SkPicture.h"
21 #include "include/core/SkPictureRecorder.h"
22 #include "include/core/SkPoint.h"
23 #include "include/core/SkRect.h"
24 #include "include/core/SkRefCnt.h"
25 #include "include/core/SkScalar.h"
26 #include "include/core/SkShader.h"
27 #include "include/core/SkSize.h"
28 #include "include/core/SkString.h"
29 #include "include/core/SkSurface.h"
30 #include "include/core/SkTextBlob.h"
31 #include "include/core/SkTileMode.h"
32 #include "include/core/SkTypeface.h"
33 #include "include/core/SkTypes.h"
34 #include "include/effects/SkGradientShader.h"
35 #include "include/effects/SkImageFilters.h"
36 #include "include/effects/SkShaderMaskFilter.h"
37 #include "include/utils/SkRandom.h"
38 #include "src/core/SkCanvasPriv.h"
39 #include "tools/Resources.h"
40 #include "tools/ToolUtils.h"
41 
42 #include <string.h>
43 #include <initializer_list>
44 
45 // This GM tests out the deprecated Android-specific unclipped saveLayer "feature".
46 // In particular, it attempts to compare the performance of unclipped saveLayers with alternatives.
47 
save_layer_unclipped(SkCanvas * canvas,SkScalar l,SkScalar t,SkScalar r,SkScalar b)48 static void save_layer_unclipped(SkCanvas* canvas,
49                                  SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
50     SkRect rect = SkRect::MakeLTRB(l, t, r, b);
51     canvas->saveLayer({ &rect, nullptr, nullptr, nullptr, nullptr,
52                         (SkCanvas::SaveLayerFlags) SkCanvasPriv::kDontClipToLayer_SaveLayerFlag });
53 }
54 
do_draw(SkCanvas * canvas)55 static void do_draw(SkCanvas* canvas) {
56     SkPaint paint;
57     SkRandom rand;
58 
59     for (int i = 0; i < 20; ++i) {
60         paint.setColor(ToolUtils::color_to_565(rand.nextU() | (0xFF << 24)));
61         canvas->drawRect({ 15, 15, 290, 40 }, paint);
62         canvas->translate(0, 30);
63     }
64 }
65 
66 class UnclippedSaveLayerGM : public skiagm::GM {
67 public:
68     enum class Mode {
69         kClipped,
70         kUnclipped
71     };
72 
UnclippedSaveLayerGM(Mode mode)73     UnclippedSaveLayerGM(Mode mode) : fMode(mode) { this->setBGColor(SK_ColorWHITE); }
74 
75 protected:
runAsBench() const76     bool runAsBench() const override { return true; }
77 
onShortName()78     SkString onShortName() override {
79         if (Mode::kClipped == fMode) {
80             return SkString("savelayer_unclipped");
81         } else {
82             SkASSERT(Mode::kUnclipped == fMode);
83             return SkString("savelayer_clipped");
84         }
85     }
86 
onISize()87     SkISize onISize() override { return SkISize::Make(320, 640); }
88 
onDraw(SkCanvas * canvas)89     void onDraw(SkCanvas* canvas) override {
90         const SkScalar L = 10;
91         const SkScalar T = 10;
92         const SkScalar R = 310;
93         const SkScalar B = 630;
94 
95         canvas->clipRect({ L, T, R, B });
96 
97         for (int i = 0; i < 100; ++i) {
98             SkAutoCanvasRestore acr(canvas, true);
99             if (Mode::kClipped == fMode) {
100                 save_layer_unclipped(canvas, L, T, R, T + 20);
101                 save_layer_unclipped(canvas, L, B - 20, R, B);
102             } else {
103                 SkASSERT(Mode::kUnclipped == fMode);
104                 canvas->saveLayer({ L, T, R, B }, nullptr);
105             }
106 
107             do_draw(canvas);
108         }
109     }
110 
111 private:
112     Mode fMode;
113 
114     typedef skiagm::GM INHERITED;
115 };
116 DEF_GM(return new UnclippedSaveLayerGM(UnclippedSaveLayerGM::Mode::kClipped);)
DEF_GM(return new UnclippedSaveLayerGM (UnclippedSaveLayerGM::Mode::kUnclipped);)117 DEF_GM(return new UnclippedSaveLayerGM(UnclippedSaveLayerGM::Mode::kUnclipped);)
118 
119 DEF_SIMPLE_GM(picture_savelayer, canvas, 320, 640) {
120     SkPaint paint1, paint2, paint3;
121     paint1.setAlphaf(0.5f);
122     paint2.setAlphaf(0.25f);
123     paint3.setColor(0xFFFF0000);
124     SkRect rect1{40, 5, 80, 70}, rect2{5, 40, 70, 80}, rect3{10, 10, 70, 70};
125     // In the future, we might also test the clipped case by allowing i = 0
126     for(int i = 1; i < 2; ++i) {
127         canvas->translate(100 * i, 0);
128         auto flag = i ?
129                 (SkCanvas::SaveLayerFlags) SkCanvasPriv::kDontClipToLayer_SaveLayerFlag : 0;
130         canvas->saveLayer({ &rect1, &paint1, nullptr, nullptr, nullptr, flag});
131         canvas->saveLayer({ &rect2, &paint2, nullptr, nullptr, nullptr, flag});
132         canvas->drawRect(rect3, paint3);
133         canvas->restore();
134         canvas->restore();
135     }
136 };
137 
138 // Test kInitWithPrevious_SaveLayerFlag by drawing an image, save a layer with the flag, which
139 // should seed the layer with the image (from below). Then we punch a hole in the layer and
140 // restore with kPlus mode, which should show the mandrill super-bright on the outside, but
141 // normal where we punched the hole.
142 DEF_SIMPLE_GM(savelayer_initfromprev, canvas, 256, 256) {
143     canvas->drawImage(GetResourceAsImage("images/mandrill_256.png"), 0, 0, nullptr);
144 
145     SkCanvas::SaveLayerRec rec;
146     SkPaint paint;
147     paint.setBlendMode(SkBlendMode::kPlus);
148     rec.fSaveLayerFlags = SkCanvas::kInitWithPrevious_SaveLayerFlag;
149     rec.fPaint = &paint;
150     canvas->saveLayer(rec);
151     paint.setBlendMode(SkBlendMode::kClear);
152     canvas->drawCircle(128, 128, 96, paint);
153     canvas->restore();
154 };
155 
draw_mask(SkCanvas * canvas,int size)156 static void draw_mask(SkCanvas* canvas, int size) {
157     const SkScalar cx = size * SK_ScalarHalf,
158                    cy = cx;
159     const SkColor colors[] = { 0x00000000, 0xffff0000, 0x00000000, 0xffff0000, 0x00000000,
160                                0xffff0000, 0x00000000, 0xffff0000, 0x00000000 };
161 
162     SkPaint paint;
163     paint.setAntiAlias(true);
164     paint.setShader(SkGradientShader::MakeSweep(cx, cy, colors, nullptr, SK_ARRAY_COUNT(colors)));
165     canvas->drawPaint(paint);
166 
167     paint.setShader(SkGradientShader::MakeRadial({cx, cy}, size / 4, colors, nullptr, 2,
168                                                  SkTileMode::kClamp));
169     canvas->drawCircle(cx, cy, size / 4, paint);
170 }
171 
172 DEF_SIMPLE_GM(savelayer_clipmask, canvas, 1200, 1200) {
173     static constexpr int kSize = 100;
174     static constexpr SkRect kLayerBounds = { kSize * 0.25f, kSize * 0.25f,
175                                              kSize * 0.75f, kSize * 0.75f };
176     static constexpr struct {
177         const SkRect* bounds;
178         const SkScalar matrix[9];
179     } kConfigs[] = {
180         { nullptr, { 1     ,  0     ,   0,   0     , 1     ,   0,   0, 0, 1 } },
181         { nullptr, { 2     ,  0     ,   0,   0     , 2     ,   0,   0, 0, 1 } },
182         { nullptr, { 2     ,  0     , -50,   0     , 2     , -50,   0, 0, 1 } },
183         { nullptr, { 0.707f, -0.707f,  50,   0.707f, 0.707f, -20,   0, 0, 1 } },
184         { nullptr, { 0.5f  ,  0     ,  25,   0     , 0.5f  ,  25,   0, 0, 1 } },
185 
186         { &kLayerBounds, { 1     ,  0     ,   0,   0     , 1     ,   0,   0, 0, 1 } },
187         { &kLayerBounds, { 2     ,  0     ,   0,   0     , 2     ,   0,   0, 0, 1 } },
188         { &kLayerBounds, { 2     ,  0     , -50,   0     , 2     , -50,   0, 0, 1 } },
189         { &kLayerBounds, { 0.707f, -0.707f,  50,   0.707f, 0.707f, -20,   0, 0, 1 } },
190         { &kLayerBounds, { 0.5f  ,  0     ,  25,   0     , 0.5f  ,  25,   0, 0, 1 } },
191     };
192 
193     using MaskMakerFunc = sk_sp<SkImage> (*)(int size);
194     static const MaskMakerFunc kMaskMakers[] = {
__anonc8baa3500202() 195         [](int size) -> sk_sp<SkImage> {
196             auto surf = SkSurface::MakeRaster(SkImageInfo::MakeA8(size, size));
197             draw_mask(surf->getCanvas(), size);
198             return surf->makeImageSnapshot();
199         },
200 
__anonc8baa3500302() 201         [](int size) -> sk_sp<SkImage> {
202             auto surf = SkSurface::MakeRasterN32Premul(size, size);
203             draw_mask(surf->getCanvas(), size);
204             return surf->makeImageSnapshot();
205         },
206 
__anonc8baa3500402() 207         [](int size) -> sk_sp<SkImage> {
208             SkPictureRecorder recorder;
209             draw_mask(recorder.beginRecording(size, size), size);
210             return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(),
211                                             SkISize::Make(size, size),
212                                             nullptr, nullptr,
213                                             SkImage::BitDepth::kU8,
214                                             SkColorSpace::MakeSRGB());
215         }
216     };
217 
218     using PaintMakerFunc = SkPaint (*)();
219     static const PaintMakerFunc kPaintMakers[] = {
__anonc8baa3500502() 220         []() -> SkPaint { return SkPaint(); },
__anonc8baa3500602() 221         []() -> SkPaint {
222             SkPaint p; p.setImageFilter(SkImageFilters::Blur(2, 2, nullptr)); return p;
223         },
__anonc8baa3500702() 224         []() -> SkPaint { SkPaint p; p.setBlendMode(SkBlendMode::kSrcOut); return p; },
225     };
226 
227     canvas->drawColor(0xffcccccc);
228 
229     SkMatrix clipMatrix;
230     SkCanvas::SaveLayerRec rec;
231     rec.fClipMatrix = &clipMatrix;
232 
233     for (const auto& paintMaker : kPaintMakers) {
234         auto layerPaint = paintMaker();
235         rec.fPaint = &layerPaint;
236 
237         for (const auto& maskMaker : kMaskMakers) {
238             sk_sp<SkImage> mask = maskMaker(kSize);
239             rec.fClipMask = mask.get();
240 
241             canvas->save();
242             for (const auto cfg : kConfigs) {
243                 rec.fBounds = cfg.bounds;
244                 clipMatrix.set9(cfg.matrix);
245                 canvas->saveLayer(rec);
246 
247                 SkPaint paint;
248                 paint.setColor(0xff0000ff);
249                 canvas->drawRect(SkRect::MakeWH(50, 50), paint);
250                 paint.setColor(0xffff0000);
251                 canvas->drawRect(SkRect::MakeXYWH(50, 0, 50, 50), paint);
252                 paint.setColor(0xff00ff00);
253                 canvas->drawRect(SkRect::MakeXYWH(50, 50, 50, 50), paint);
254                 paint.setColor(0xffffff00);
255                 canvas->drawRect(SkRect::MakeXYWH(0, 50, 50, 50), paint);
256 
257                 canvas->restore();
258                 canvas->translate(120, 0);
259             }
260             canvas->restore();
261             canvas->translate(0, 120);
262         }
263     }
264 }
265 
266 DEF_SIMPLE_GM(savelayer_coverage, canvas, 500, 500) {
267     canvas->saveLayer(nullptr, nullptr);
268 
269     SkRect r = { 0, 0, 200, 200 };
270     SkPaint layerPaint;
271     layerPaint.setBlendMode(SkBlendMode::kModulate);
272 
273     auto image = GetResourceAsImage("images/mandrill_128.png");
274 
__anonc8baa3500802(SkCanvas* canvas, SkCanvas::SaveLayerRec& rec) 275     auto proc = [layerPaint](SkCanvas* canvas, SkCanvas::SaveLayerRec& rec) {
276         SkPaint paint;
277         paint.setColor(SK_ColorRED);
278 
279         canvas->saveLayer(rec);
280         canvas->drawCircle(100, 100, 50, paint);
281         paint.setColor(0x8800FF00);
282         canvas->drawRect({10, 90, 190, 110}, paint);
283         canvas->restore();
284     };
285 
286     const int yflags[] = { 0, SkCanvas::kInitWithPrevious_SaveLayerFlag };
287     for (int y = 0; y <= 1; ++y) {
288         const int xflags[] = { 0, SkCanvas::kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag };
289         for (int x = 0; x <= 1; ++x) {
290             canvas->save();
291             canvas->translate(x * 200.f, y * 200.f);
292 
293             SkCanvas::SaveLayerRec rec(&r, &layerPaint, yflags[y] | xflags[x]);
294             canvas->drawImageRect(image, r, nullptr);
295             proc(canvas, rec);
296 
297             canvas->restore();
298         }
299     }
300 
301     canvas->restore();
302 }
303 
304 DEF_SIMPLE_GM(savelayer_clipmask_maskfilter, canvas, 500, 500) {
305     // Offscreen surface for making the clip mask and mask filter images
306     auto surf = SkSurface::MakeRaster(SkImageInfo::MakeA8(100, 100));
307     SkPaint maskPaint;
308     maskPaint.setColor(SK_ColorWHITE);
309     maskPaint.setAntiAlias(true);
310 
311     // Draw a centered circle for the mask filter
312     surf->getCanvas()->clear(SK_ColorTRANSPARENT);
313     surf->getCanvas()->drawCircle(50.f, 50.f, 50.f, maskPaint);
314     auto maskFilterImage = surf->makeImageSnapshot();
315     sk_sp<SkMaskFilter> maskFilter = SkShaderMaskFilter::Make(maskFilterImage->makeShader());
316 
317     // Cut out a cross for the clip mask
318     surf->getCanvas()->clear(SK_ColorTRANSPARENT);
319     surf->getCanvas()->drawRect(SkRect::MakeLTRB(0.f, 0.f, 40.f, 40.f), maskPaint);
320     surf->getCanvas()->drawRect(SkRect::MakeLTRB(60.f, 0.f, 100.f, 40.f), maskPaint);
321     surf->getCanvas()->drawRect(SkRect::MakeLTRB(0.f, 60.f, 40.f, 100.f), maskPaint);
322     surf->getCanvas()->drawRect(SkRect::MakeLTRB(60.f, 60.f, 100.f, 100.f), maskPaint);
323     auto clipMaskImage = surf->makeImageSnapshot();
324     SkMatrix clipMatrix = SkMatrix::I();
325     SkRect clipBounds = SkRect::MakeWH(100, 100);
326 
327     // On the main canvas, save a 100x100 layer three times, applying clip mask, mask filter, or
328     // both, translating across the GM for each configuration.
329     canvas->clear(SK_ColorGRAY);
330 
331     canvas->translate(25.f, 0.f);
332 
333     // Clip mask only
334     SkCanvas::SaveLayerRec rec;
335     rec.fBounds = &clipBounds;
336     rec.fClipMask = clipMaskImage.get();
337     rec.fClipMatrix = &clipMatrix;
338     canvas->saveLayer(rec);
339     canvas->clear(SK_ColorWHITE);
340     canvas->restore();
341 
342     canvas->translate(125.f, 0.f);
343 
344     // Mask filter only
345     maskPaint.setMaskFilter(maskFilter);
346     rec.fClipMask = nullptr;
347     rec.fPaint = &maskPaint;
348     canvas->saveLayer(rec);
349     canvas->clear(SK_ColorWHITE);
350     canvas->restore();
351 
352     canvas->translate(125.f, 0.f);
353 
354     // Both
355     rec.fClipMask = clipMaskImage.get();
356     canvas->saveLayer(rec);
357     canvas->clear(SK_ColorWHITE);
358     canvas->restore();
359 }
360 
draw_cell(SkCanvas * canvas,sk_sp<SkTextBlob> blob,SkColor c,SkScalar w,SkScalar h,bool useDrawBehind)361 static void draw_cell(SkCanvas* canvas, sk_sp<SkTextBlob> blob, SkColor c, SkScalar w, SkScalar h,
362                       bool useDrawBehind) {
363     SkRect r = SkRect::MakeWH(w, h);
364     SkPaint p;
365     p.setColor(c);
366     p.setBlendMode(SkBlendMode::kSrc);
367     canvas->drawRect(r, p);
368     p.setBlendMode(SkBlendMode::kSrcOver);
369 
370     const SkScalar margin = 80;
371     r.fLeft = w - margin;
372 
373     // save the behind image
374     SkDEBUGCODE(int sc0 =) canvas->getSaveCount();
375     SkDEBUGCODE(int sc1 =) SkCanvasPriv::SaveBehind(canvas, &r);
376     SkDEBUGCODE(int sc2 =) canvas->getSaveCount();
377     SkASSERT(sc0 == sc1);
378     SkASSERT(sc0 + 1 == sc2);
379 
380     // draw the foreground (including over the 'behind' section)
381     p.setColor(SK_ColorBLACK);
382     canvas->drawTextBlob(blob, 10, 30, p);
383 
384     // draw the treatment
385     const SkPoint pts[] = { {r.fLeft,0}, {r.fRight, 0} };
386     const SkColor colors[] = { 0x88000000, 0x0 };
387     auto sh = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp);
388     p.setShader(sh);
389     p.setBlendMode(SkBlendMode::kDstIn);
390 
391     if (useDrawBehind) {
392         SkCanvasPriv::DrawBehind(canvas, p);
393     } else {
394         canvas->drawRect(r, p);
395     }
396 
397     // this should restore the behind image
398     canvas->restore();
399     SkDEBUGCODE(int sc3 =) canvas->getSaveCount();
400     SkASSERT(sc3 == sc0);
401 
402     // just outline where we expect the treatment to appear
403     p.reset();
404     p.setStyle(SkPaint::kStroke_Style);
405     p.setAlphaf(0.25f);
406 }
407 
draw_list(SkCanvas * canvas,sk_sp<SkTextBlob> blob,bool useDrawBehind)408 static void draw_list(SkCanvas* canvas, sk_sp<SkTextBlob> blob, bool useDrawBehind) {
409     SkAutoCanvasRestore acr(canvas, true);
410 
411     SkRandom rand;
412     SkScalar w = 400;
413     SkScalar h = 40;
414     for (int i = 0; i < 8; ++i) {
415         SkColor c = rand.nextU();   // ensure we're opaque
416         c = (c & 0xFFFFFF) | 0x80000000;
417         draw_cell(canvas, blob, c, w, h, useDrawBehind);
418         canvas->translate(0, h);
419     }
420 }
421 
422 DEF_SIMPLE_GM(save_behind, canvas, 830, 670) {
423     SkFont font;
424     font.setTypeface(ToolUtils::create_portable_typeface());
425     font.setSize(30);
426 
427     const char text[] = "This is a very long line of text";
428     auto blob = SkTextBlob::MakeFromText(text, strlen(text), font);
429 
430     for (bool useDrawBehind : {false, true}) {
431         canvas->save();
432 
433         draw_list(canvas, blob, useDrawBehind);
434         canvas->translate(0, 350);
435         canvas->saveLayer({0, 0, 400, 320}, nullptr);
436         draw_list(canvas, blob, useDrawBehind);
437         canvas->restore();
438 
439         canvas->restore();
440         canvas->translate(430, 0);
441     }
442 }
443 
444 #include "include/effects/SkGradientShader.h"
445 
446 DEF_SIMPLE_GM(savelayer_f16, canvas, 900, 300) {
447     int n = 15;
448     SkRect r{0, 0, 300, 300};
449     SkPaint paint;
450 
451     const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
452     paint.setShader(SkGradientShader::MakeSweep(r.centerX(), r.centerY(),
453                                                 colors, nullptr, SK_ARRAY_COUNT(colors)));
454 
455     canvas->drawOval(r, paint);
456 
457     paint.setAlphaf(1.0f/n);
458     paint.setBlendMode(SkBlendMode::kPlus);
459 
460     for (auto flags : {0, (int)SkCanvas::kF16ColorType}) {
461         canvas->translate(r.width(), 0);
462 
463         SkCanvas::SaveLayerRec rec;
464         rec.fSaveLayerFlags = flags;
465         canvas->saveLayer(rec);
466         for (int i = 0; i < n; ++i) {
467             canvas->drawOval(r, paint);
468         }
469         canvas->restore();
470     }
471 }
472