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