xref: /aosp_15_r20/external/skia/gm/blurrect.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2012 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include <cmath>
9*c8dee2aaSAndroid Build Coastguard Worker #include "gm/gm.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBlurTypes.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorFilter.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMaskFilter.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathBuilder.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkShader.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTileMode.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkGradientShader.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrRecordingContext.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkBlurMask.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkMask.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrRecordingContextPriv.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "tools/timer/TimeUtils.h"
37*c8dee2aaSAndroid Build Coastguard Worker 
38*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
39*c8dee2aaSAndroid Build Coastguard Worker 
40*c8dee2aaSAndroid Build Coastguard Worker #define STROKE_WIDTH    SkIntToScalar(10)
41*c8dee2aaSAndroid Build Coastguard Worker 
42*c8dee2aaSAndroid Build Coastguard Worker typedef void (*Proc)(SkCanvas*, const SkRect&, const SkPaint&);
43*c8dee2aaSAndroid Build Coastguard Worker 
fill_rect(SkCanvas * canvas,const SkRect & r,const SkPaint & p)44*c8dee2aaSAndroid Build Coastguard Worker static void fill_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
45*c8dee2aaSAndroid Build Coastguard Worker     canvas->drawRect(r, p);
46*c8dee2aaSAndroid Build Coastguard Worker }
47*c8dee2aaSAndroid Build Coastguard Worker 
draw_donut(SkCanvas * canvas,const SkRect & r,const SkPaint & p)48*c8dee2aaSAndroid Build Coastguard Worker static void draw_donut(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
49*c8dee2aaSAndroid Build Coastguard Worker     SkRect        rect;
50*c8dee2aaSAndroid Build Coastguard Worker     SkPathBuilder path;
51*c8dee2aaSAndroid Build Coastguard Worker 
52*c8dee2aaSAndroid Build Coastguard Worker     rect = r;
53*c8dee2aaSAndroid Build Coastguard Worker     rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2);
54*c8dee2aaSAndroid Build Coastguard Worker     path.addRect(rect);
55*c8dee2aaSAndroid Build Coastguard Worker     rect = r;
56*c8dee2aaSAndroid Build Coastguard Worker     rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
57*c8dee2aaSAndroid Build Coastguard Worker 
58*c8dee2aaSAndroid Build Coastguard Worker     path.addRect(rect);
59*c8dee2aaSAndroid Build Coastguard Worker     path.setFillType(SkPathFillType::kEvenOdd);
60*c8dee2aaSAndroid Build Coastguard Worker 
61*c8dee2aaSAndroid Build Coastguard Worker     canvas->drawPath(path.detach(), p);
62*c8dee2aaSAndroid Build Coastguard Worker }
63*c8dee2aaSAndroid Build Coastguard Worker 
draw_donut_skewed(SkCanvas * canvas,const SkRect & r,const SkPaint & p)64*c8dee2aaSAndroid Build Coastguard Worker static void draw_donut_skewed(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
65*c8dee2aaSAndroid Build Coastguard Worker     SkRect        rect;
66*c8dee2aaSAndroid Build Coastguard Worker     SkPathBuilder path;
67*c8dee2aaSAndroid Build Coastguard Worker 
68*c8dee2aaSAndroid Build Coastguard Worker     rect = r;
69*c8dee2aaSAndroid Build Coastguard Worker     rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2);
70*c8dee2aaSAndroid Build Coastguard Worker     path.addRect(rect);
71*c8dee2aaSAndroid Build Coastguard Worker     rect = r;
72*c8dee2aaSAndroid Build Coastguard Worker     rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
73*c8dee2aaSAndroid Build Coastguard Worker 
74*c8dee2aaSAndroid Build Coastguard Worker     rect.offset(7, -7);
75*c8dee2aaSAndroid Build Coastguard Worker 
76*c8dee2aaSAndroid Build Coastguard Worker     path.addRect(rect);
77*c8dee2aaSAndroid Build Coastguard Worker     path.setFillType(SkPathFillType::kEvenOdd);
78*c8dee2aaSAndroid Build Coastguard Worker 
79*c8dee2aaSAndroid Build Coastguard Worker     canvas->drawPath(path.detach(), p);
80*c8dee2aaSAndroid Build Coastguard Worker }
81*c8dee2aaSAndroid Build Coastguard Worker 
82*c8dee2aaSAndroid Build Coastguard Worker /*
83*c8dee2aaSAndroid Build Coastguard Worker  * Spits out an arbitrary gradient to test blur with shader on paint
84*c8dee2aaSAndroid Build Coastguard Worker  */
make_radial()85*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkShader> make_radial() {
86*c8dee2aaSAndroid Build Coastguard Worker     SkPoint pts[2] = {
87*c8dee2aaSAndroid Build Coastguard Worker         { 0, 0 },
88*c8dee2aaSAndroid Build Coastguard Worker         { SkIntToScalar(100), SkIntToScalar(100) }
89*c8dee2aaSAndroid Build Coastguard Worker     };
90*c8dee2aaSAndroid Build Coastguard Worker     SkTileMode tm = SkTileMode::kClamp;
91*c8dee2aaSAndroid Build Coastguard Worker     const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, };
92*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar pos[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
93*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix scale;
94*c8dee2aaSAndroid Build Coastguard Worker     scale.setScale(0.5f, 0.5f);
95*c8dee2aaSAndroid Build Coastguard Worker     scale.postTranslate(25.f, 25.f);
96*c8dee2aaSAndroid Build Coastguard Worker     SkPoint center0, center1;
97*c8dee2aaSAndroid Build Coastguard Worker     center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
98*c8dee2aaSAndroid Build Coastguard Worker                 SkScalarAve(pts[0].fY, pts[1].fY));
99*c8dee2aaSAndroid Build Coastguard Worker     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
100*c8dee2aaSAndroid Build Coastguard Worker                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
101*c8dee2aaSAndroid Build Coastguard Worker     return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
102*c8dee2aaSAndroid Build Coastguard Worker                                                  center0, (pts[1].fX - pts[0].fX) / 2,
103*c8dee2aaSAndroid Build Coastguard Worker                                                  colors, pos, std::size(colors), tm,
104*c8dee2aaSAndroid Build Coastguard Worker                                                  0, &scale);
105*c8dee2aaSAndroid Build Coastguard Worker }
106*c8dee2aaSAndroid Build Coastguard Worker 
107*c8dee2aaSAndroid Build Coastguard Worker typedef void (*PaintProc)(SkPaint*, SkScalar width);
108*c8dee2aaSAndroid Build Coastguard Worker 
109*c8dee2aaSAndroid Build Coastguard Worker class BlurRectGM : public skiagm::GM {
110*c8dee2aaSAndroid Build Coastguard Worker public:
BlurRectGM(const char name[],U8CPU alpha)111*c8dee2aaSAndroid Build Coastguard Worker     BlurRectGM(const char name[], U8CPU alpha) : fName(name), fAlpha(SkToU8(alpha)) {}
112*c8dee2aaSAndroid Build Coastguard Worker 
113*c8dee2aaSAndroid Build Coastguard Worker private:
114*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkMaskFilter> fMaskFilters[kLastEnum_SkBlurStyle + 1];
115*c8dee2aaSAndroid Build Coastguard Worker     const char* fName;
116*c8dee2aaSAndroid Build Coastguard Worker     SkAlpha fAlpha;
117*c8dee2aaSAndroid Build Coastguard Worker 
onOnceBeforeDraw()118*c8dee2aaSAndroid Build Coastguard Worker     void onOnceBeforeDraw() override {
119*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i <= kLastEnum_SkBlurStyle; ++i) {
120*c8dee2aaSAndroid Build Coastguard Worker             fMaskFilters[i] = SkMaskFilter::MakeBlur((SkBlurStyle)i,
121*c8dee2aaSAndroid Build Coastguard Worker                                   SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(STROKE_WIDTH/2)));
122*c8dee2aaSAndroid Build Coastguard Worker         }
123*c8dee2aaSAndroid Build Coastguard Worker     }
124*c8dee2aaSAndroid Build Coastguard Worker 
getName() const125*c8dee2aaSAndroid Build Coastguard Worker     SkString getName() const override { return SkString(fName); }
126*c8dee2aaSAndroid Build Coastguard Worker 
getISize()127*c8dee2aaSAndroid Build Coastguard Worker     SkISize getISize() override { return {860, 820}; }
128*c8dee2aaSAndroid Build Coastguard Worker 
onDraw(SkCanvas * canvas)129*c8dee2aaSAndroid Build Coastguard Worker     void onDraw(SkCanvas* canvas) override {
130*c8dee2aaSAndroid Build Coastguard Worker         canvas->translate(STROKE_WIDTH*3/2, STROKE_WIDTH*3/2);
131*c8dee2aaSAndroid Build Coastguard Worker 
132*c8dee2aaSAndroid Build Coastguard Worker         SkRect  r = { 0, 0, 100, 50 };
133*c8dee2aaSAndroid Build Coastguard Worker         SkScalar scales[] = { SK_Scalar1, 0.6f };
134*c8dee2aaSAndroid Build Coastguard Worker 
135*c8dee2aaSAndroid Build Coastguard Worker         for (size_t s = 0; s < std::size(scales); ++s) {
136*c8dee2aaSAndroid Build Coastguard Worker             canvas->save();
137*c8dee2aaSAndroid Build Coastguard Worker             for (size_t f = 0; f < std::size(fMaskFilters); ++f) {
138*c8dee2aaSAndroid Build Coastguard Worker                 SkPaint paint;
139*c8dee2aaSAndroid Build Coastguard Worker                 paint.setMaskFilter(fMaskFilters[f]);
140*c8dee2aaSAndroid Build Coastguard Worker                 paint.setAlpha(fAlpha);
141*c8dee2aaSAndroid Build Coastguard Worker 
142*c8dee2aaSAndroid Build Coastguard Worker                 SkPaint paintWithRadial = paint;
143*c8dee2aaSAndroid Build Coastguard Worker                 paintWithRadial.setShader(make_radial());
144*c8dee2aaSAndroid Build Coastguard Worker 
145*c8dee2aaSAndroid Build Coastguard Worker                 constexpr Proc procs[] = {
146*c8dee2aaSAndroid Build Coastguard Worker                     fill_rect, draw_donut, draw_donut_skewed
147*c8dee2aaSAndroid Build Coastguard Worker                 };
148*c8dee2aaSAndroid Build Coastguard Worker 
149*c8dee2aaSAndroid Build Coastguard Worker                 canvas->save();
150*c8dee2aaSAndroid Build Coastguard Worker                 canvas->scale(scales[s], scales[s]);
151*c8dee2aaSAndroid Build Coastguard Worker                 this->drawProcs(canvas, r, paint, false, procs, std::size(procs));
152*c8dee2aaSAndroid Build Coastguard Worker                 canvas->translate(r.width() * 4/3, 0);
153*c8dee2aaSAndroid Build Coastguard Worker                 this->drawProcs(canvas, r, paintWithRadial, false, procs, std::size(procs));
154*c8dee2aaSAndroid Build Coastguard Worker                 canvas->translate(r.width() * 4/3, 0);
155*c8dee2aaSAndroid Build Coastguard Worker                 this->drawProcs(canvas, r, paint, true, procs, std::size(procs));
156*c8dee2aaSAndroid Build Coastguard Worker                 canvas->translate(r.width() * 4/3, 0);
157*c8dee2aaSAndroid Build Coastguard Worker                 this->drawProcs(canvas, r, paintWithRadial, true, procs, std::size(procs));
158*c8dee2aaSAndroid Build Coastguard Worker                 canvas->restore();
159*c8dee2aaSAndroid Build Coastguard Worker 
160*c8dee2aaSAndroid Build Coastguard Worker                 canvas->translate(0, std::size(procs) * r.height() * 4/3 * scales[s]);
161*c8dee2aaSAndroid Build Coastguard Worker             }
162*c8dee2aaSAndroid Build Coastguard Worker             canvas->restore();
163*c8dee2aaSAndroid Build Coastguard Worker             canvas->translate(4 * r.width() * 4/3 * scales[s], 0);
164*c8dee2aaSAndroid Build Coastguard Worker         }
165*c8dee2aaSAndroid Build Coastguard Worker     }
166*c8dee2aaSAndroid Build Coastguard Worker 
drawProcs(SkCanvas * canvas,const SkRect & r,const SkPaint & paint,bool doClip,const Proc procs[],size_t procsCount)167*c8dee2aaSAndroid Build Coastguard Worker     void drawProcs(SkCanvas* canvas, const SkRect& r, const SkPaint& paint,
168*c8dee2aaSAndroid Build Coastguard Worker                    bool doClip, const Proc procs[], size_t procsCount) {
169*c8dee2aaSAndroid Build Coastguard Worker         SkAutoCanvasRestore acr(canvas, true);
170*c8dee2aaSAndroid Build Coastguard Worker         for (size_t i = 0; i < procsCount; ++i) {
171*c8dee2aaSAndroid Build Coastguard Worker             if (doClip) {
172*c8dee2aaSAndroid Build Coastguard Worker                 SkRect clipRect(r);
173*c8dee2aaSAndroid Build Coastguard Worker                 clipRect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
174*c8dee2aaSAndroid Build Coastguard Worker                 canvas->save();
175*c8dee2aaSAndroid Build Coastguard Worker                 canvas->clipRect(r);
176*c8dee2aaSAndroid Build Coastguard Worker             }
177*c8dee2aaSAndroid Build Coastguard Worker             procs[i](canvas, r, paint);
178*c8dee2aaSAndroid Build Coastguard Worker             if (doClip) {
179*c8dee2aaSAndroid Build Coastguard Worker                 canvas->restore();
180*c8dee2aaSAndroid Build Coastguard Worker             }
181*c8dee2aaSAndroid Build Coastguard Worker             canvas->translate(0, r.height() * 4/3);
182*c8dee2aaSAndroid Build Coastguard Worker         }
183*c8dee2aaSAndroid Build Coastguard Worker     }
184*c8dee2aaSAndroid Build Coastguard Worker };
185*c8dee2aaSAndroid Build Coastguard Worker 
186*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(blurrect_gallery, canvas, 1200, 1024) {
187*c8dee2aaSAndroid Build Coastguard Worker         const int fGMWidth = 1200;
188*c8dee2aaSAndroid Build Coastguard Worker         const int fPadding = 10;
189*c8dee2aaSAndroid Build Coastguard Worker         const int fMargin = 100;
190*c8dee2aaSAndroid Build Coastguard Worker 
191*c8dee2aaSAndroid Build Coastguard Worker         const int widths[] = {25, 5, 5, 100, 150, 25};
192*c8dee2aaSAndroid Build Coastguard Worker         const int heights[] = {100, 100, 5, 25, 150, 25};
193*c8dee2aaSAndroid Build Coastguard Worker         const SkBlurStyle styles[] = {kNormal_SkBlurStyle, kInner_SkBlurStyle, kOuter_SkBlurStyle};
194*c8dee2aaSAndroid Build Coastguard Worker         const float radii[] = {20, 5, 10};
195*c8dee2aaSAndroid Build Coastguard Worker 
196*c8dee2aaSAndroid Build Coastguard Worker         canvas->translate(50,20);
197*c8dee2aaSAndroid Build Coastguard Worker 
198*c8dee2aaSAndroid Build Coastguard Worker         int cur_x = 0;
199*c8dee2aaSAndroid Build Coastguard Worker         int cur_y = 0;
200*c8dee2aaSAndroid Build Coastguard Worker 
201*c8dee2aaSAndroid Build Coastguard Worker         int max_height = 0;
202*c8dee2aaSAndroid Build Coastguard Worker 
203*c8dee2aaSAndroid Build Coastguard Worker         for (size_t i = 0 ; i < std::size(widths) ; i++) {
204*c8dee2aaSAndroid Build Coastguard Worker             int width = widths[i];
205*c8dee2aaSAndroid Build Coastguard Worker             int height = heights[i];
206*c8dee2aaSAndroid Build Coastguard Worker             SkRect r;
207*c8dee2aaSAndroid Build Coastguard Worker             r.setWH(SkIntToScalar(width), SkIntToScalar(height));
208*c8dee2aaSAndroid Build Coastguard Worker             SkAutoCanvasRestore autoRestore(canvas, true);
209*c8dee2aaSAndroid Build Coastguard Worker 
210*c8dee2aaSAndroid Build Coastguard Worker             for (size_t j = 0 ; j < std::size(radii) ; j++) {
211*c8dee2aaSAndroid Build Coastguard Worker                 float radius = radii[j];
212*c8dee2aaSAndroid Build Coastguard Worker                 for (size_t k = 0 ; k < std::size(styles) ; k++) {
213*c8dee2aaSAndroid Build Coastguard Worker                     SkBlurStyle style = styles[k];
214*c8dee2aaSAndroid Build Coastguard Worker 
215*c8dee2aaSAndroid Build Coastguard Worker                     SkMaskBuilder mask;
216*c8dee2aaSAndroid Build Coastguard Worker                     if (!SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(radius),
217*c8dee2aaSAndroid Build Coastguard Worker                                               &mask, r, style)) {
218*c8dee2aaSAndroid Build Coastguard Worker                         continue;
219*c8dee2aaSAndroid Build Coastguard Worker                     }
220*c8dee2aaSAndroid Build Coastguard Worker 
221*c8dee2aaSAndroid Build Coastguard Worker                     SkAutoMaskFreeImage amfi(mask.image());
222*c8dee2aaSAndroid Build Coastguard Worker 
223*c8dee2aaSAndroid Build Coastguard Worker                     SkBitmap bm;
224*c8dee2aaSAndroid Build Coastguard Worker                     bm.installMaskPixels(mask);
225*c8dee2aaSAndroid Build Coastguard Worker 
226*c8dee2aaSAndroid Build Coastguard Worker                     if (cur_x + bm.width() >= fGMWidth - fMargin) {
227*c8dee2aaSAndroid Build Coastguard Worker                         cur_x = 0;
228*c8dee2aaSAndroid Build Coastguard Worker                         cur_y += max_height + fPadding;
229*c8dee2aaSAndroid Build Coastguard Worker                         max_height = 0;
230*c8dee2aaSAndroid Build Coastguard Worker                     }
231*c8dee2aaSAndroid Build Coastguard Worker 
232*c8dee2aaSAndroid Build Coastguard Worker                     canvas->save();
233*c8dee2aaSAndroid Build Coastguard Worker                     canvas->translate((SkScalar)cur_x, (SkScalar)cur_y);
234*c8dee2aaSAndroid Build Coastguard Worker                     canvas->translate(-(bm.width() - r.width())/2, -(bm.height()-r.height())/2);
235*c8dee2aaSAndroid Build Coastguard Worker                     canvas->drawImage(bm.asImage(), 0.f, 0.f);
236*c8dee2aaSAndroid Build Coastguard Worker                     canvas->restore();
237*c8dee2aaSAndroid Build Coastguard Worker 
238*c8dee2aaSAndroid Build Coastguard Worker                     cur_x += bm.width() + fPadding;
239*c8dee2aaSAndroid Build Coastguard Worker                     if (bm.height() > max_height)
240*c8dee2aaSAndroid Build Coastguard Worker                         max_height = bm.height();
241*c8dee2aaSAndroid Build Coastguard Worker                 }
242*c8dee2aaSAndroid Build Coastguard Worker             }
243*c8dee2aaSAndroid Build Coastguard Worker         }
244*c8dee2aaSAndroid Build Coastguard Worker }
245*c8dee2aaSAndroid Build Coastguard Worker 
246*c8dee2aaSAndroid Build Coastguard Worker namespace skiagm {
247*c8dee2aaSAndroid Build Coastguard Worker 
248*c8dee2aaSAndroid Build Coastguard Worker // Compares actual blur rects with reference masks created by the GM. Animates sigma in viewer.
249*c8dee2aaSAndroid Build Coastguard Worker class BlurRectCompareGM : public GM {
250*c8dee2aaSAndroid Build Coastguard Worker protected:
getName() const251*c8dee2aaSAndroid Build Coastguard Worker     SkString getName() const override { return SkString("blurrect_compare"); }
252*c8dee2aaSAndroid Build Coastguard Worker 
getISize()253*c8dee2aaSAndroid Build Coastguard Worker     SkISize getISize() override { return {900, 1220}; }
254*c8dee2aaSAndroid Build Coastguard Worker 
onOnceBeforeDraw()255*c8dee2aaSAndroid Build Coastguard Worker     void onOnceBeforeDraw() override { this->prepareReferenceMasks(); }
256*c8dee2aaSAndroid Build Coastguard Worker 
onDraw(SkCanvas * canvas,SkString * errorMsg)257*c8dee2aaSAndroid Build Coastguard Worker     DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
258*c8dee2aaSAndroid Build Coastguard Worker         if (canvas->imageInfo().colorType() == kUnknown_SkColorType ||
259*c8dee2aaSAndroid Build Coastguard Worker             (canvas->recordingContext() && !canvas->recordingContext()->asDirectContext())) {
260*c8dee2aaSAndroid Build Coastguard Worker             *errorMsg = "Not supported when recording, relies on canvas->makeSurface()";
261*c8dee2aaSAndroid Build Coastguard Worker             return DrawResult::kSkip;
262*c8dee2aaSAndroid Build Coastguard Worker         }
263*c8dee2aaSAndroid Build Coastguard Worker         int32_t ctxID = canvas->recordingContext() ? canvas->recordingContext()->priv().contextID()
264*c8dee2aaSAndroid Build Coastguard Worker                                                    : 0;
265*c8dee2aaSAndroid Build Coastguard Worker         if (fRecalcMasksForAnimation || !fActualMasks[0][0][0] || ctxID != fLastContextUniqueID) {
266*c8dee2aaSAndroid Build Coastguard Worker             if (fRecalcMasksForAnimation) {
267*c8dee2aaSAndroid Build Coastguard Worker                 // Sigma is changing so references must also be recalculated.
268*c8dee2aaSAndroid Build Coastguard Worker                 this->prepareReferenceMasks();
269*c8dee2aaSAndroid Build Coastguard Worker             }
270*c8dee2aaSAndroid Build Coastguard Worker             this->prepareActualMasks(canvas);
271*c8dee2aaSAndroid Build Coastguard Worker             this->prepareMaskDifferences(canvas);
272*c8dee2aaSAndroid Build Coastguard Worker             fLastContextUniqueID = ctxID;
273*c8dee2aaSAndroid Build Coastguard Worker             fRecalcMasksForAnimation = false;
274*c8dee2aaSAndroid Build Coastguard Worker         }
275*c8dee2aaSAndroid Build Coastguard Worker         canvas->clear(SK_ColorBLACK);
276*c8dee2aaSAndroid Build Coastguard Worker         static constexpr float kMargin = 30;
277*c8dee2aaSAndroid Build Coastguard Worker         float totalW = 0;
278*c8dee2aaSAndroid Build Coastguard Worker         for (auto w : kSizes) {
279*c8dee2aaSAndroid Build Coastguard Worker             totalW += w + kMargin;
280*c8dee2aaSAndroid Build Coastguard Worker         }
281*c8dee2aaSAndroid Build Coastguard Worker         canvas->translate(kMargin, kMargin);
282*c8dee2aaSAndroid Build Coastguard Worker         for (int mode = 0; mode < 3; ++mode) {
283*c8dee2aaSAndroid Build Coastguard Worker             canvas->save();
284*c8dee2aaSAndroid Build Coastguard Worker             for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
285*c8dee2aaSAndroid Build Coastguard Worker                 auto sigma = kSigmas[sigmaIdx] + fSigmaAnimationBoost;
286*c8dee2aaSAndroid Build Coastguard Worker                 for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
287*c8dee2aaSAndroid Build Coastguard Worker                     auto h = kSizes[heightIdx];
288*c8dee2aaSAndroid Build Coastguard Worker                     canvas->save();
289*c8dee2aaSAndroid Build Coastguard Worker                     for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
290*c8dee2aaSAndroid Build Coastguard Worker                         auto w = kSizes[widthIdx];
291*c8dee2aaSAndroid Build Coastguard Worker                         SkPaint paint;
292*c8dee2aaSAndroid Build Coastguard Worker                         paint.setColor(SK_ColorWHITE);
293*c8dee2aaSAndroid Build Coastguard Worker                         SkImage* img;
294*c8dee2aaSAndroid Build Coastguard Worker                         switch (mode) {
295*c8dee2aaSAndroid Build Coastguard Worker                             case 0:
296*c8dee2aaSAndroid Build Coastguard Worker                                 img = fReferenceMasks[sigmaIdx][heightIdx][widthIdx].get();
297*c8dee2aaSAndroid Build Coastguard Worker                                 break;
298*c8dee2aaSAndroid Build Coastguard Worker                             case 1:
299*c8dee2aaSAndroid Build Coastguard Worker                                 img = fActualMasks[sigmaIdx][heightIdx][widthIdx].get();
300*c8dee2aaSAndroid Build Coastguard Worker                                 break;
301*c8dee2aaSAndroid Build Coastguard Worker                             case 2:
302*c8dee2aaSAndroid Build Coastguard Worker                                 img = fMaskDifferences[sigmaIdx][heightIdx][widthIdx].get();
303*c8dee2aaSAndroid Build Coastguard Worker                                 // The error images are opaque, use kPlus so they are additive if
304*c8dee2aaSAndroid Build Coastguard Worker                                 // the overlap between test cases.
305*c8dee2aaSAndroid Build Coastguard Worker                                 paint.setBlendMode(SkBlendMode::kPlus);
306*c8dee2aaSAndroid Build Coastguard Worker                                 break;
307*c8dee2aaSAndroid Build Coastguard Worker                         }
308*c8dee2aaSAndroid Build Coastguard Worker                         auto pad = PadForSigma(sigma);
309*c8dee2aaSAndroid Build Coastguard Worker                         canvas->drawImage(img, -pad, -pad, SkSamplingOptions(), &paint);
310*c8dee2aaSAndroid Build Coastguard Worker #if 0  // Uncomment to hairline stroke around blurred rect in red on top of the blur result.
311*c8dee2aaSAndroid Build Coastguard Worker        // The rect is defined at integer coords. We inset by 1/2 pixel so our stroke lies on top
312*c8dee2aaSAndroid Build Coastguard Worker        // of the edge pixels.
313*c8dee2aaSAndroid Build Coastguard Worker                         SkPaint stroke;
314*c8dee2aaSAndroid Build Coastguard Worker                         stroke.setColor(SK_ColorRED);
315*c8dee2aaSAndroid Build Coastguard Worker                         stroke.setStrokeWidth(0.f);
316*c8dee2aaSAndroid Build Coastguard Worker                         stroke.setStyle(SkPaint::kStroke_Style);
317*c8dee2aaSAndroid Build Coastguard Worker                         canvas->drawRect(SkRect::MakeWH(w, h).makeInset(0.5, 0.5), stroke);
318*c8dee2aaSAndroid Build Coastguard Worker #endif
319*c8dee2aaSAndroid Build Coastguard Worker                         canvas->translate(w + kMargin, 0.f);
320*c8dee2aaSAndroid Build Coastguard Worker                     }
321*c8dee2aaSAndroid Build Coastguard Worker                     canvas->restore();
322*c8dee2aaSAndroid Build Coastguard Worker                     canvas->translate(0, h + kMargin);
323*c8dee2aaSAndroid Build Coastguard Worker                 }
324*c8dee2aaSAndroid Build Coastguard Worker             }
325*c8dee2aaSAndroid Build Coastguard Worker             canvas->restore();
326*c8dee2aaSAndroid Build Coastguard Worker             canvas->translate(totalW + 2 * kMargin, 0);
327*c8dee2aaSAndroid Build Coastguard Worker         }
328*c8dee2aaSAndroid Build Coastguard Worker         return DrawResult::kOk;
329*c8dee2aaSAndroid Build Coastguard Worker     }
onAnimate(double nanos)330*c8dee2aaSAndroid Build Coastguard Worker     bool onAnimate(double nanos) override {
331*c8dee2aaSAndroid Build Coastguard Worker         fSigmaAnimationBoost = TimeUtils::SineWave(nanos, 5, 2.5f, 0.f, 2.f);
332*c8dee2aaSAndroid Build Coastguard Worker         fRecalcMasksForAnimation = true;
333*c8dee2aaSAndroid Build Coastguard Worker         return true;
334*c8dee2aaSAndroid Build Coastguard Worker     }
335*c8dee2aaSAndroid Build Coastguard Worker 
336*c8dee2aaSAndroid Build Coastguard Worker private:
prepareReferenceMasks()337*c8dee2aaSAndroid Build Coastguard Worker     void prepareReferenceMasks() {
338*c8dee2aaSAndroid Build Coastguard Worker         auto create_reference_mask = [](int w, int h, float sigma, int numSubpixels) {
339*c8dee2aaSAndroid Build Coastguard Worker             int pad = PadForSigma(sigma);
340*c8dee2aaSAndroid Build Coastguard Worker             int maskW = w + 2 * pad;
341*c8dee2aaSAndroid Build Coastguard Worker             int maskH = h + 2 * pad;
342*c8dee2aaSAndroid Build Coastguard Worker             // We'll do all our calculations at subpixel resolution, so adjust params
343*c8dee2aaSAndroid Build Coastguard Worker             w *= numSubpixels;
344*c8dee2aaSAndroid Build Coastguard Worker             h *= numSubpixels;
345*c8dee2aaSAndroid Build Coastguard Worker             sigma *= numSubpixels;
346*c8dee2aaSAndroid Build Coastguard Worker             auto scale = SK_ScalarRoot2Over2 / sigma;
347*c8dee2aaSAndroid Build Coastguard Worker             auto def_integral_approx = [scale](float a, float b) {
348*c8dee2aaSAndroid Build Coastguard Worker                 return 0.5f * (std::erf(b * scale) - std::erf(a * scale));
349*c8dee2aaSAndroid Build Coastguard Worker             };
350*c8dee2aaSAndroid Build Coastguard Worker             // Do the x-pass. Above/below rect are rows of zero. All rows that intersect the rect
351*c8dee2aaSAndroid Build Coastguard Worker             // are the same. The row is calculated and stored at subpixel resolution.
352*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(!(numSubpixels & 0b1));
353*c8dee2aaSAndroid Build Coastguard Worker             std::unique_ptr<float[]> row(new float[maskW * numSubpixels]);
354*c8dee2aaSAndroid Build Coastguard Worker             for (int col = 0; col < maskW * numSubpixels; ++col) {
355*c8dee2aaSAndroid Build Coastguard Worker                 // Compute distance to rect left in subpixel units
356*c8dee2aaSAndroid Build Coastguard Worker                 float ldiff = numSubpixels * pad - (col + 0.5f);
357*c8dee2aaSAndroid Build Coastguard Worker                 float rdiff = ldiff + w;
358*c8dee2aaSAndroid Build Coastguard Worker                 row[col] = def_integral_approx(ldiff, rdiff);
359*c8dee2aaSAndroid Build Coastguard Worker             }
360*c8dee2aaSAndroid Build Coastguard Worker             // y-pass
361*c8dee2aaSAndroid Build Coastguard Worker             SkBitmap bmp;
362*c8dee2aaSAndroid Build Coastguard Worker             bmp.allocPixels(SkImageInfo::MakeA8(maskW, maskH));
363*c8dee2aaSAndroid Build Coastguard Worker             std::unique_ptr<float[]> accums(new float[maskW]);
364*c8dee2aaSAndroid Build Coastguard Worker             const float accumScale = 1.f / (numSubpixels * numSubpixels);
365*c8dee2aaSAndroid Build Coastguard Worker             for (int y = 0; y < maskH; ++y) {
366*c8dee2aaSAndroid Build Coastguard Worker                 // Initialize subpixel accumulation buffer for this row.
367*c8dee2aaSAndroid Build Coastguard Worker                 std::fill_n(accums.get(), maskW, 0);
368*c8dee2aaSAndroid Build Coastguard Worker                 for (int ys = 0; ys < numSubpixels; ++ys) {
369*c8dee2aaSAndroid Build Coastguard Worker                     // At each subpixel we want to integrate over the kernel centered at the
370*c8dee2aaSAndroid Build Coastguard Worker                     // subpixel multiplied by the x-pass. The x-pass is zero above and below the
371*c8dee2aaSAndroid Build Coastguard Worker                     // rect and constant valued from rect top to rect bottom. So we can get the
372*c8dee2aaSAndroid Build Coastguard Worker                     // integral of just the kernel from rect top to rect bottom and multiply by
373*c8dee2aaSAndroid Build Coastguard Worker                     // the single x-pass value from our precomputed row.
374*c8dee2aaSAndroid Build Coastguard Worker                     float tdiff = numSubpixels * pad - (y * numSubpixels + ys + 0.5f);
375*c8dee2aaSAndroid Build Coastguard Worker                     float bdiff = tdiff + h;
376*c8dee2aaSAndroid Build Coastguard Worker                     auto integral = def_integral_approx(tdiff, bdiff);
377*c8dee2aaSAndroid Build Coastguard Worker                     for (int x = 0; x < maskW; ++x) {
378*c8dee2aaSAndroid Build Coastguard Worker                         for (int xs = 0; xs < numSubpixels; ++xs) {
379*c8dee2aaSAndroid Build Coastguard Worker                             int rowIdx = x * numSubpixels + xs;
380*c8dee2aaSAndroid Build Coastguard Worker                             accums[x] += integral * row[rowIdx];
381*c8dee2aaSAndroid Build Coastguard Worker                         }
382*c8dee2aaSAndroid Build Coastguard Worker                     }
383*c8dee2aaSAndroid Build Coastguard Worker                 }
384*c8dee2aaSAndroid Build Coastguard Worker                 for (int x = 0; x < maskW; ++x) {
385*c8dee2aaSAndroid Build Coastguard Worker                     auto result = accums[x] * accumScale;
386*c8dee2aaSAndroid Build Coastguard Worker                     *bmp.getAddr8(x, y) = SkToU8(sk_float_round2int(255.f * result));
387*c8dee2aaSAndroid Build Coastguard Worker                 }
388*c8dee2aaSAndroid Build Coastguard Worker             }
389*c8dee2aaSAndroid Build Coastguard Worker             return bmp.asImage();
390*c8dee2aaSAndroid Build Coastguard Worker         };
391*c8dee2aaSAndroid Build Coastguard Worker 
392*c8dee2aaSAndroid Build Coastguard Worker         // Number of times to subsample (in both X and Y). If fRecalcMasksForAnimation is true
393*c8dee2aaSAndroid Build Coastguard Worker         // then we're animating, don't subsample as much to keep fps higher.
394*c8dee2aaSAndroid Build Coastguard Worker         const int numSubpixels = fRecalcMasksForAnimation ? 2 : 8;
395*c8dee2aaSAndroid Build Coastguard Worker 
396*c8dee2aaSAndroid Build Coastguard Worker         for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
397*c8dee2aaSAndroid Build Coastguard Worker             auto sigma = kSigmas[sigmaIdx] + fSigmaAnimationBoost;
398*c8dee2aaSAndroid Build Coastguard Worker             for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
399*c8dee2aaSAndroid Build Coastguard Worker                 auto h = kSizes[heightIdx];
400*c8dee2aaSAndroid Build Coastguard Worker                 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
401*c8dee2aaSAndroid Build Coastguard Worker                     auto w = kSizes[widthIdx];
402*c8dee2aaSAndroid Build Coastguard Worker                     fReferenceMasks[sigmaIdx][heightIdx][widthIdx] =
403*c8dee2aaSAndroid Build Coastguard Worker                             create_reference_mask(w, h, sigma, numSubpixels);
404*c8dee2aaSAndroid Build Coastguard Worker                 }
405*c8dee2aaSAndroid Build Coastguard Worker             }
406*c8dee2aaSAndroid Build Coastguard Worker         }
407*c8dee2aaSAndroid Build Coastguard Worker     }
408*c8dee2aaSAndroid Build Coastguard Worker 
prepareActualMasks(SkCanvas * canvas)409*c8dee2aaSAndroid Build Coastguard Worker     void prepareActualMasks(SkCanvas* canvas) {
410*c8dee2aaSAndroid Build Coastguard Worker         for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
411*c8dee2aaSAndroid Build Coastguard Worker             auto sigma = kSigmas[sigmaIdx] + fSigmaAnimationBoost;
412*c8dee2aaSAndroid Build Coastguard Worker             for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
413*c8dee2aaSAndroid Build Coastguard Worker                 auto h = kSizes[heightIdx];
414*c8dee2aaSAndroid Build Coastguard Worker                 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
415*c8dee2aaSAndroid Build Coastguard Worker                     auto w = kSizes[widthIdx];
416*c8dee2aaSAndroid Build Coastguard Worker                     auto pad = PadForSigma(sigma);
417*c8dee2aaSAndroid Build Coastguard Worker                     auto ii = SkImageInfo::MakeA8(w + 2 * pad, h + 2 * pad);
418*c8dee2aaSAndroid Build Coastguard Worker                     auto surf = canvas->makeSurface(ii);
419*c8dee2aaSAndroid Build Coastguard Worker                     if (!surf) {
420*c8dee2aaSAndroid Build Coastguard Worker                         // Some GPUs don't have renderable A8 :(
421*c8dee2aaSAndroid Build Coastguard Worker                         surf = canvas->makeSurface(ii.makeColorType(kRGBA_8888_SkColorType));
422*c8dee2aaSAndroid Build Coastguard Worker                         if (!surf) {
423*c8dee2aaSAndroid Build Coastguard Worker                             return;
424*c8dee2aaSAndroid Build Coastguard Worker                         }
425*c8dee2aaSAndroid Build Coastguard Worker                     }
426*c8dee2aaSAndroid Build Coastguard Worker                     auto rect = SkRect::MakeXYWH(pad, pad, w, h);
427*c8dee2aaSAndroid Build Coastguard Worker                     SkPaint paint;
428*c8dee2aaSAndroid Build Coastguard Worker                     // Color doesn't matter if we're rendering to A8 but does if we promoted to
429*c8dee2aaSAndroid Build Coastguard Worker                     // RGBA above.
430*c8dee2aaSAndroid Build Coastguard Worker                     paint.setColor(SK_ColorWHITE);
431*c8dee2aaSAndroid Build Coastguard Worker                     paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma));
432*c8dee2aaSAndroid Build Coastguard Worker                     surf->getCanvas()->drawRect(rect, paint);
433*c8dee2aaSAndroid Build Coastguard Worker                     fActualMasks[sigmaIdx][heightIdx][widthIdx] = surf->makeImageSnapshot();
434*c8dee2aaSAndroid Build Coastguard Worker                 }
435*c8dee2aaSAndroid Build Coastguard Worker             }
436*c8dee2aaSAndroid Build Coastguard Worker         }
437*c8dee2aaSAndroid Build Coastguard Worker     }
438*c8dee2aaSAndroid Build Coastguard Worker 
prepareMaskDifferences(SkCanvas * canvas)439*c8dee2aaSAndroid Build Coastguard Worker     void prepareMaskDifferences(SkCanvas* canvas) {
440*c8dee2aaSAndroid Build Coastguard Worker         for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
441*c8dee2aaSAndroid Build Coastguard Worker             for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
442*c8dee2aaSAndroid Build Coastguard Worker                 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
443*c8dee2aaSAndroid Build Coastguard Worker                     const auto& r =  fReferenceMasks[sigmaIdx][heightIdx][widthIdx];
444*c8dee2aaSAndroid Build Coastguard Worker                     const auto& a =     fActualMasks[sigmaIdx][heightIdx][widthIdx];
445*c8dee2aaSAndroid Build Coastguard Worker                     auto& d       = fMaskDifferences[sigmaIdx][heightIdx][widthIdx];
446*c8dee2aaSAndroid Build Coastguard Worker                     // The actual image might not be present if we're on an abandoned GrContext.
447*c8dee2aaSAndroid Build Coastguard Worker                     if (!a) {
448*c8dee2aaSAndroid Build Coastguard Worker                         d.reset();
449*c8dee2aaSAndroid Build Coastguard Worker                         continue;
450*c8dee2aaSAndroid Build Coastguard Worker                     }
451*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(r->width() == a->width());
452*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(r->height() == a->height());
453*c8dee2aaSAndroid Build Coastguard Worker                     auto ii = SkImageInfo::Make(r->width(), r->height(),
454*c8dee2aaSAndroid Build Coastguard Worker                                                 kRGBA_8888_SkColorType, kPremul_SkAlphaType);
455*c8dee2aaSAndroid Build Coastguard Worker                     auto surf = canvas->makeSurface(ii);
456*c8dee2aaSAndroid Build Coastguard Worker                     if (!surf) {
457*c8dee2aaSAndroid Build Coastguard Worker                         return;
458*c8dee2aaSAndroid Build Coastguard Worker                     }
459*c8dee2aaSAndroid Build Coastguard Worker                     // We visualize the difference by turning both the alpha masks into opaque green
460*c8dee2aaSAndroid Build Coastguard Worker                     // images (where alpha becomes the green channel) and then perform a
461*c8dee2aaSAndroid Build Coastguard Worker                     // SkBlendMode::kDifference between them.
462*c8dee2aaSAndroid Build Coastguard Worker                     SkPaint filterPaint;
463*c8dee2aaSAndroid Build Coastguard Worker                     filterPaint.setColor(SK_ColorWHITE);
464*c8dee2aaSAndroid Build Coastguard Worker                     // Actually 8 * alpha becomes green to really highlight differences.
465*c8dee2aaSAndroid Build Coastguard Worker                     static constexpr float kGreenifyM[] = {0, 0, 0, 0, 0,
466*c8dee2aaSAndroid Build Coastguard Worker                                                            0, 0, 0, 8, 0,
467*c8dee2aaSAndroid Build Coastguard Worker                                                            0, 0, 0, 0, 0,
468*c8dee2aaSAndroid Build Coastguard Worker                                                            0, 0, 0, 0, 1};
469*c8dee2aaSAndroid Build Coastguard Worker                     auto greenifyCF = SkColorFilters::Matrix(kGreenifyM);
470*c8dee2aaSAndroid Build Coastguard Worker                     SkPaint paint;
471*c8dee2aaSAndroid Build Coastguard Worker                     paint.setBlendMode(SkBlendMode::kSrc);
472*c8dee2aaSAndroid Build Coastguard Worker                     paint.setColorFilter(std::move(greenifyCF));
473*c8dee2aaSAndroid Build Coastguard Worker                     surf->getCanvas()->drawImage(a, 0, 0, SkSamplingOptions(), &paint);
474*c8dee2aaSAndroid Build Coastguard Worker                     paint.setBlendMode(SkBlendMode::kDifference);
475*c8dee2aaSAndroid Build Coastguard Worker                     surf->getCanvas()->drawImage(r, 0, 0, SkSamplingOptions(), &paint);
476*c8dee2aaSAndroid Build Coastguard Worker                     d = surf->makeImageSnapshot();
477*c8dee2aaSAndroid Build Coastguard Worker                 }
478*c8dee2aaSAndroid Build Coastguard Worker             }
479*c8dee2aaSAndroid Build Coastguard Worker         }
480*c8dee2aaSAndroid Build Coastguard Worker     }
481*c8dee2aaSAndroid Build Coastguard Worker 
482*c8dee2aaSAndroid Build Coastguard Worker     // Per side padding around mask images for a sigma. Make this overly generous to ensure bugs
483*c8dee2aaSAndroid Build Coastguard Worker     // related to big blurs are fully visible.
PadForSigma(float sigma)484*c8dee2aaSAndroid Build Coastguard Worker     static int PadForSigma(float sigma) { return sk_float_ceil2int(4 * sigma); }
485*c8dee2aaSAndroid Build Coastguard Worker 
486*c8dee2aaSAndroid Build Coastguard Worker     inline static constexpr int kSizes[] = {1, 2, 4, 8, 16, 32};
487*c8dee2aaSAndroid Build Coastguard Worker     inline static constexpr float kSigmas[] = {0.5f, 1.2f, 2.3f, 3.9f, 7.4f};
488*c8dee2aaSAndroid Build Coastguard Worker     inline static constexpr size_t kNumSizes = std::size(kSizes);
489*c8dee2aaSAndroid Build Coastguard Worker     inline static constexpr size_t kNumSigmas = std::size(kSigmas);
490*c8dee2aaSAndroid Build Coastguard Worker 
491*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkImage> fReferenceMasks[kNumSigmas][kNumSizes][kNumSizes];
492*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkImage> fActualMasks[kNumSigmas][kNumSizes][kNumSizes];
493*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkImage> fMaskDifferences[kNumSigmas][kNumSizes][kNumSizes];
494*c8dee2aaSAndroid Build Coastguard Worker     int32_t fLastContextUniqueID;
495*c8dee2aaSAndroid Build Coastguard Worker     // These are used only when animating.
496*c8dee2aaSAndroid Build Coastguard Worker     float fSigmaAnimationBoost = 0;
497*c8dee2aaSAndroid Build Coastguard Worker     bool fRecalcMasksForAnimation = false;
498*c8dee2aaSAndroid Build Coastguard Worker };
499*c8dee2aaSAndroid Build Coastguard Worker 
500*c8dee2aaSAndroid Build Coastguard Worker }  // namespace skiagm
501*c8dee2aaSAndroid Build Coastguard Worker 
502*c8dee2aaSAndroid Build Coastguard Worker //////////////////////////////////////////////////////////////////////////////
503*c8dee2aaSAndroid Build Coastguard Worker 
504*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new BlurRectGM("blurrects", 0xFF);)
DEF_GM(return new skiagm::BlurRectCompareGM ();)505*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new skiagm::BlurRectCompareGM();)
506*c8dee2aaSAndroid Build Coastguard Worker 
507*c8dee2aaSAndroid Build Coastguard Worker //////////////////////////////////////////////////////////////////////////////
508*c8dee2aaSAndroid Build Coastguard Worker 
509*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(blur_matrix_rect, canvas, 650, 685) {
510*c8dee2aaSAndroid Build Coastguard Worker     static constexpr auto kRect = SkRect::MakeWH(14, 60);
511*c8dee2aaSAndroid Build Coastguard Worker     static constexpr float kSigmas[] = {0.5f, 1.2f, 2.3f, 3.9f, 7.4f};
512*c8dee2aaSAndroid Build Coastguard Worker     static constexpr size_t kNumSigmas = std::size(kSigmas);
513*c8dee2aaSAndroid Build Coastguard Worker 
514*c8dee2aaSAndroid Build Coastguard Worker     const SkPoint c = {kRect.centerX(), kRect.centerY()};
515*c8dee2aaSAndroid Build Coastguard Worker 
516*c8dee2aaSAndroid Build Coastguard Worker     std::vector<SkMatrix> matrices;
517*c8dee2aaSAndroid Build Coastguard Worker 
518*c8dee2aaSAndroid Build Coastguard Worker     matrices.push_back(SkMatrix::RotateDeg(4.f, c));
519*c8dee2aaSAndroid Build Coastguard Worker 
520*c8dee2aaSAndroid Build Coastguard Worker     matrices.push_back(SkMatrix::RotateDeg(63.f, c));
521*c8dee2aaSAndroid Build Coastguard Worker 
522*c8dee2aaSAndroid Build Coastguard Worker     matrices.push_back(SkMatrix::RotateDeg(30.f, c));
523*c8dee2aaSAndroid Build Coastguard Worker     matrices.back().preScale(1.1f, .5f);
524*c8dee2aaSAndroid Build Coastguard Worker 
525*c8dee2aaSAndroid Build Coastguard Worker     matrices.push_back(SkMatrix::RotateDeg(147.f, c));
526*c8dee2aaSAndroid Build Coastguard Worker     matrices.back().preScale(3.f, .1f);
527*c8dee2aaSAndroid Build Coastguard Worker 
528*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix mirror;
529*c8dee2aaSAndroid Build Coastguard Worker     mirror.setAll(0, 1, 0,
530*c8dee2aaSAndroid Build Coastguard Worker                   1, 0, 0,
531*c8dee2aaSAndroid Build Coastguard Worker                   0, 0, 1);
532*c8dee2aaSAndroid Build Coastguard Worker     matrices.push_back(SkMatrix::Concat(mirror, matrices.back()));
533*c8dee2aaSAndroid Build Coastguard Worker 
534*c8dee2aaSAndroid Build Coastguard Worker     matrices.push_back(SkMatrix::RotateDeg(197.f, c));
535*c8dee2aaSAndroid Build Coastguard Worker     matrices.back().preSkew(.3f, -.5f);
536*c8dee2aaSAndroid Build Coastguard Worker 
537*c8dee2aaSAndroid Build Coastguard Worker     auto bounds = SkRect::MakeEmpty();
538*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& m : matrices) {
539*c8dee2aaSAndroid Build Coastguard Worker         SkRect mapped;
540*c8dee2aaSAndroid Build Coastguard Worker         m.mapRect(&mapped, kRect);
541*c8dee2aaSAndroid Build Coastguard Worker         bounds.joinNonEmptyArg(mapped.makeSorted());
542*c8dee2aaSAndroid Build Coastguard Worker     }
543*c8dee2aaSAndroid Build Coastguard Worker     float blurPad = 2.f*kSigmas[kNumSigmas - 1];
544*c8dee2aaSAndroid Build Coastguard Worker     bounds.outset(blurPad, blurPad);
545*c8dee2aaSAndroid Build Coastguard Worker     canvas->translate(-bounds.left(), -bounds.top());
546*c8dee2aaSAndroid Build Coastguard Worker     for (auto sigma : kSigmas) {
547*c8dee2aaSAndroid Build Coastguard Worker         SkPaint paint;
548*c8dee2aaSAndroid Build Coastguard Worker         paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma));
549*c8dee2aaSAndroid Build Coastguard Worker         canvas->save();
550*c8dee2aaSAndroid Build Coastguard Worker         for (const auto& m : matrices) {
551*c8dee2aaSAndroid Build Coastguard Worker             canvas->save();
552*c8dee2aaSAndroid Build Coastguard Worker             canvas->concat(m);
553*c8dee2aaSAndroid Build Coastguard Worker             canvas->drawRect(kRect, paint);
554*c8dee2aaSAndroid Build Coastguard Worker             canvas->restore();
555*c8dee2aaSAndroid Build Coastguard Worker             canvas->translate(0, bounds.height());
556*c8dee2aaSAndroid Build Coastguard Worker         }
557*c8dee2aaSAndroid Build Coastguard Worker         canvas->restore();
558*c8dee2aaSAndroid Build Coastguard Worker         canvas->translate(bounds.width(), 0);
559*c8dee2aaSAndroid Build Coastguard Worker     }
560*c8dee2aaSAndroid Build Coastguard Worker }
561