blob: 35bafc73a1e16fb68240e848e7f48e5e317f3830 [file] [log] [blame]
reed@google.comdb87c962012-11-02 21:11:12 +00001/*
humper@google.coma99a92c2013-02-20 16:42:06 +00002* Copyright 2012 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*/
reed@google.comdb87c962012-11-02 21:11:12 +00007
Brian Salomonb2d5d402019-09-10 10:11:52 -04008#include <cmath>
Mike Kleinc0bd9f92019-04-23 12:05:21 -05009#include "gm/gm.h"
Ben Wagner6a34f3a2019-05-01 10:59:30 -040010#include "include/core/SkBitmap.h"
11#include "include/core/SkBlurTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "include/core/SkCanvas.h"
Ben Wagner6a34f3a2019-05-01 10:59:30 -040013#include "include/core/SkColor.h"
Brian Salomonb2d5d402019-09-10 10:11:52 -040014#include "include/core/SkImage.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "include/core/SkMaskFilter.h"
Ben Wagner6a34f3a2019-05-01 10:59:30 -040016#include "include/core/SkMatrix.h"
17#include "include/core/SkPaint.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "include/core/SkPath.h"
Ben Wagner6a34f3a2019-05-01 10:59:30 -040019#include "include/core/SkPoint.h"
20#include "include/core/SkRect.h"
21#include "include/core/SkRefCnt.h"
22#include "include/core/SkScalar.h"
23#include "include/core/SkShader.h"
24#include "include/core/SkSize.h"
25#include "include/core/SkString.h"
Brian Salomonb2d5d402019-09-10 10:11:52 -040026#include "include/core/SkSurface.h"
Ben Wagner6a34f3a2019-05-01 10:59:30 -040027#include "include/core/SkTileMode.h"
28#include "include/core/SkTypes.h"
29#include "include/effects/SkGradientShader.h"
Brian Salomonb2d5d402019-09-10 10:11:52 -040030#include "include/gpu/GrContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050031#include "include/private/SkTo.h"
32#include "src/core/SkBlurMask.h"
Ben Wagner6a34f3a2019-05-01 10:59:30 -040033#include "src/core/SkMask.h"
Brian Salomonb2d5d402019-09-10 10:11:52 -040034#include "src/gpu/GrContextPriv.h"
35#include "tools/timer/TimeUtils.h"
reed@google.comdb87c962012-11-02 21:11:12 +000036
37#define STROKE_WIDTH SkIntToScalar(10)
38
39typedef void (*Proc)(SkCanvas*, const SkRect&, const SkPaint&);
40
41static void fill_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
42 canvas->drawRect(r, p);
43}
44
reed@google.comdb87c962012-11-02 21:11:12 +000045static void draw_donut(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
46 SkRect rect;
47 SkPath path;
skia.committer@gmail.com34587162012-11-20 02:01:23 +000048
reed@google.comdb87c962012-11-02 21:11:12 +000049 rect = r;
50 rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2);
51 path.addRect(rect);
52 rect = r;
53 rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
skia.committer@gmail.com34587162012-11-20 02:01:23 +000054
reed@google.comdb87c962012-11-02 21:11:12 +000055 path.addRect(rect);
Mike Reed7d34dc72019-11-26 12:17:17 -050056 path.setFillType(SkPathFillType::kEvenOdd);
skia.committer@gmail.com34587162012-11-20 02:01:23 +000057
reed@google.com808b70f2012-11-19 16:14:02 +000058 canvas->drawPath(path, p);
59}
reed@google.comdb87c962012-11-02 21:11:12 +000060
reed@google.com808b70f2012-11-19 16:14:02 +000061static void draw_donut_skewed(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
62 SkRect rect;
63 SkPath path;
skia.committer@gmail.com34587162012-11-20 02:01:23 +000064
reed@google.com808b70f2012-11-19 16:14:02 +000065 rect = r;
66 rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2);
67 path.addRect(rect);
68 rect = r;
69 rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
skia.committer@gmail.com34587162012-11-20 02:01:23 +000070
reed@google.com808b70f2012-11-19 16:14:02 +000071 rect.offset(7, -7);
skia.committer@gmail.com34587162012-11-20 02:01:23 +000072
reed@google.com808b70f2012-11-19 16:14:02 +000073 path.addRect(rect);
Mike Reed7d34dc72019-11-26 12:17:17 -050074 path.setFillType(SkPathFillType::kEvenOdd);
skia.committer@gmail.com34587162012-11-20 02:01:23 +000075
reed@google.comdb87c962012-11-02 21:11:12 +000076 canvas->drawPath(path, p);
77}
78
joshualitt341400e2014-12-18 11:54:13 -080079/*
80 * Spits out a dummy gradient to test blur with shader on paint
81 */
Robert Phillips09dfc472017-09-13 15:25:47 -040082static sk_sp<SkShader> make_radial() {
joshualitt341400e2014-12-18 11:54:13 -080083 SkPoint pts[2] = {
84 { 0, 0 },
85 { SkIntToScalar(100), SkIntToScalar(100) }
86 };
Mike Reedfae8fce2019-04-03 10:27:45 -040087 SkTileMode tm = SkTileMode::kClamp;
joshualitt341400e2014-12-18 11:54:13 -080088 const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, };
89 const SkScalar pos[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
90 SkMatrix scale;
91 scale.setScale(0.5f, 0.5f);
92 scale.postTranslate(25.f, 25.f);
93 SkPoint center0, center1;
94 center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
95 SkScalarAve(pts[0].fY, pts[1].fY));
96 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
97 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
reed2ad1aa62016-03-09 09:50:50 -080098 return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
99 center0, (pts[1].fX - pts[0].fX) / 2,
100 colors, pos, SK_ARRAY_COUNT(colors), tm,
101 0, &scale);
joshualitt341400e2014-12-18 11:54:13 -0800102}
103
reed@google.com53007a22012-11-26 14:39:50 +0000104typedef void (*PaintProc)(SkPaint*, SkScalar width);
105
reed@google.comdb87c962012-11-02 21:11:12 +0000106class BlurRectGM : public skiagm::GM {
reed@google.comdb87c962012-11-02 21:11:12 +0000107public:
Hal Canary594fe852019-07-18 13:35:49 -0400108 BlurRectGM(const char name[], U8CPU alpha) : fName(name), fAlpha(SkToU8(alpha)) {}
reed@google.comdb87c962012-11-02 21:11:12 +0000109
Hal Canary594fe852019-07-18 13:35:49 -0400110private:
111 sk_sp<SkMaskFilter> fMaskFilters[kLastEnum_SkBlurStyle + 1];
112 const char* fName;
113 SkAlpha fAlpha;
114
mtklein36352bf2015-03-25 18:17:31 -0700115 void onOnceBeforeDraw() override {
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000116 for (int i = 0; i <= kLastEnum_SkBlurStyle; ++i) {
Mike Reed1be1f8d2018-03-14 13:01:17 -0400117 fMaskFilters[i] = SkMaskFilter::MakeBlur((SkBlurStyle)i,
118 SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(STROKE_WIDTH/2)));
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000119 }
120 }
121
Hal Canary594fe852019-07-18 13:35:49 -0400122 SkString onShortName() override { return SkString(fName); }
reed@google.comdb87c962012-11-02 21:11:12 +0000123
Hal Canary594fe852019-07-18 13:35:49 -0400124 SkISize onISize() override { return {860, 820}; }
reed@google.comdb87c962012-11-02 21:11:12 +0000125
mtklein36352bf2015-03-25 18:17:31 -0700126 void onDraw(SkCanvas* canvas) override {
reed@google.comdb87c962012-11-02 21:11:12 +0000127 canvas->translate(STROKE_WIDTH*3/2, STROKE_WIDTH*3/2);
128
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000129 SkRect r = { 0, 0, 100, 50 };
130 SkScalar scales[] = { SK_Scalar1, 0.6f };
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000131
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000132 for (size_t s = 0; s < SK_ARRAY_COUNT(scales); ++s) {
133 canvas->save();
134 for (size_t f = 0; f < SK_ARRAY_COUNT(fMaskFilters); ++f) {
135 SkPaint paint;
136 paint.setMaskFilter(fMaskFilters[f]);
137 paint.setAlpha(fAlpha);
138
joshualitt341400e2014-12-18 11:54:13 -0800139 SkPaint paintWithRadial = paint;
Robert Phillips09dfc472017-09-13 15:25:47 -0400140 paintWithRadial.setShader(make_radial());
joshualitt341400e2014-12-18 11:54:13 -0800141
mtkleindbfd7ab2016-09-01 11:24:54 -0700142 constexpr Proc procs[] = {
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000143 fill_rect, draw_donut, draw_donut_skewed
144 };
145
146 canvas->save();
147 canvas->scale(scales[s], scales[s]);
148 this->drawProcs(canvas, r, paint, false, procs, SK_ARRAY_COUNT(procs));
149 canvas->translate(r.width() * 4/3, 0);
joshualitt341400e2014-12-18 11:54:13 -0800150 this->drawProcs(canvas, r, paintWithRadial, false, procs, SK_ARRAY_COUNT(procs));
151 canvas->translate(r.width() * 4/3, 0);
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000152 this->drawProcs(canvas, r, paint, true, procs, SK_ARRAY_COUNT(procs));
joshualitt341400e2014-12-18 11:54:13 -0800153 canvas->translate(r.width() * 4/3, 0);
154 this->drawProcs(canvas, r, paintWithRadial, true, procs, SK_ARRAY_COUNT(procs));
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000155 canvas->restore();
156
157 canvas->translate(0, SK_ARRAY_COUNT(procs) * r.height() * 4/3 * scales[s]);
158 }
159 canvas->restore();
joshualitt341400e2014-12-18 11:54:13 -0800160 canvas->translate(4 * r.width() * 4/3 * scales[s], 0);
reed@google.com53007a22012-11-26 14:39:50 +0000161 }
reed@google.comdb87c962012-11-02 21:11:12 +0000162 }
163
reed@google.comdb87c962012-11-02 21:11:12 +0000164 void drawProcs(SkCanvas* canvas, const SkRect& r, const SkPaint& paint,
165 bool doClip, const Proc procs[], size_t procsCount) {
166 SkAutoCanvasRestore acr(canvas, true);
167 for (size_t i = 0; i < procsCount; ++i) {
168 if (doClip) {
169 SkRect clipRect(r);
170 clipRect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
171 canvas->save();
172 canvas->clipRect(r);
173 }
174 procs[i](canvas, r, paint);
175 if (doClip) {
176 canvas->restore();
177 }
178 canvas->translate(0, r.height() * 4/3);
179 }
180 }
reed@google.comdb87c962012-11-02 21:11:12 +0000181};
182
halcanary2a243382015-09-09 08:16:41 -0700183DEF_SIMPLE_GM(blurrect_gallery, canvas, 1200, 1024) {
184 const int fGMWidth = 1200;
185 const int fPadding = 10;
186 const int fMargin = 100;
commit-bot@chromium.org3c1594a2014-05-28 21:52:12 +0000187
commit-bot@chromium.org3c1594a2014-05-28 21:52:12 +0000188 const int widths[] = {25, 5, 5, 100, 150, 25};
189 const int heights[] = {100, 100, 5, 25, 150, 25};
190 const SkBlurStyle styles[] = {kNormal_SkBlurStyle, kInner_SkBlurStyle, kOuter_SkBlurStyle};
191 const float radii[] = {20, 5, 10};
192
193 canvas->translate(50,20);
194
195 int cur_x = 0;
196 int cur_y = 0;
197
198 int max_height = 0;
199
200 for (size_t i = 0 ; i < SK_ARRAY_COUNT(widths) ; i++) {
201 int width = widths[i];
202 int height = heights[i];
203 SkRect r;
204 r.setWH(SkIntToScalar(width), SkIntToScalar(height));
205 SkAutoCanvasRestore autoRestore(canvas, true);
206
207 for (size_t j = 0 ; j < SK_ARRAY_COUNT(radii) ; j++) {
208 float radius = radii[j];
209 for (size_t k = 0 ; k < SK_ARRAY_COUNT(styles) ; k++) {
210 SkBlurStyle style = styles[k];
211
212 SkMask mask;
robertphillipse80eb922015-12-17 11:33:12 -0800213 if (!SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(radius),
214 &mask, r, style)) {
215 continue;
216 }
commit-bot@chromium.org3c1594a2014-05-28 21:52:12 +0000217
218 SkAutoMaskFreeImage amfi(mask.fImage);
219
220 SkBitmap bm;
221 bm.installMaskPixels(mask);
222
223 if (cur_x + bm.width() >= fGMWidth - fMargin) {
224 cur_x = 0;
225 cur_y += max_height + fPadding;
226 max_height = 0;
227 }
228
229 canvas->save();
commit-bot@chromium.org793ddd92014-05-28 22:42:31 +0000230 canvas->translate((SkScalar)cur_x, (SkScalar)cur_y);
commit-bot@chromium.org3c1594a2014-05-28 21:52:12 +0000231 canvas->translate(-(bm.width() - r.width())/2, -(bm.height()-r.height())/2);
halcanary96fcdcc2015-08-27 07:41:13 -0700232 canvas->drawBitmap(bm, 0.f, 0.f, nullptr);
commit-bot@chromium.org3c1594a2014-05-28 21:52:12 +0000233 canvas->restore();
234
235 cur_x += bm.width() + fPadding;
236 if (bm.height() > max_height)
237 max_height = bm.height();
238 }
239 }
240 }
halcanary2a243382015-09-09 08:16:41 -0700241}
humper@google.com7c7292c2013-01-04 20:29:03 +0000242
Brian Salomonb2d5d402019-09-10 10:11:52 -0400243namespace skiagm {
244
245// Compares actual blur rects with reference masks created by the GM. Animates sigma in viewer.
246class BlurRectCompareGM : public GM {
247protected:
248 SkString onShortName() override { return SkString("blurrect_compare"); }
249
250 SkISize onISize() override { return {900, 1220}; }
251
252 void onOnceBeforeDraw() override { this->prepareReferenceMasks(); }
253
Brian Salomon78be3eb2019-09-10 13:37:23 -0400254 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
Brian Salomon4fe30e12019-09-10 17:12:58 -0400255 if (canvas->imageInfo().colorType() == kUnknown_SkColorType ||
256 (canvas->getGrContext() && !canvas->getGrContext()->priv().asDirectContext())) {
Brian Salomon78be3eb2019-09-10 13:37:23 -0400257 *errorMsg = "Not supported when recording, relies on canvas->makeSurface()";
258 return DrawResult::kSkip;
259 }
Brian Salomonb2d5d402019-09-10 10:11:52 -0400260 int32_t ctxID = canvas->getGrContext() ? canvas->getGrContext()->priv().contextID() : 0;
261 if (fRecalcMasksForAnimation || !fActualMasks[0][0][0] || ctxID != fLastContextUniqueID) {
262 if (fRecalcMasksForAnimation) {
263 // Sigma is changing so references must also be recalculated.
264 this->prepareReferenceMasks();
265 }
266 this->prepareActualMasks(canvas);
267 this->prepareMaskDifferences(canvas);
268 fLastContextUniqueID = ctxID;
269 fRecalcMasksForAnimation = false;
270 }
271 canvas->clear(SK_ColorBLACK);
272 static constexpr float kMargin = 30;
273 float totalW = 0;
274 for (auto w : kSizes) {
275 totalW += w + kMargin;
276 }
277 canvas->translate(kMargin, kMargin);
278 for (int mode = 0; mode < 3; ++mode) {
279 canvas->save();
280 for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
281 auto sigma = kSigmas[sigmaIdx] + fSigmaAnimationBoost;
282 for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
283 auto h = kSizes[heightIdx];
284 canvas->save();
285 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
286 auto w = kSizes[widthIdx];
287 SkPaint paint;
288 paint.setColor(SK_ColorWHITE);
289 SkImage* img;
290 switch (mode) {
291 case 0:
292 img = fReferenceMasks[sigmaIdx][heightIdx][widthIdx].get();
293 break;
294 case 1:
295 img = fActualMasks[sigmaIdx][heightIdx][widthIdx].get();
296 break;
297 case 2:
298 img = fMaskDifferences[sigmaIdx][heightIdx][widthIdx].get();
299 // The error images are opaque, use kPlus so they are additive if
300 // the overlap between test cases.
301 paint.setBlendMode(SkBlendMode::kPlus);
302 break;
303 }
304 auto pad = PadForSigma(sigma);
305 canvas->drawImage(img, -pad, -pad, &paint);
306#if 0 // Uncomment to hairline stroke around blurred rect in red on top of the blur result.
307 // The rect is defined at integer coords. We inset by 1/2 pixel so our stroke lies on top
308 // of the edge pixels.
309 SkPaint stroke;
310 stroke.setColor(SK_ColorRED);
311 stroke.setStrokeWidth(0.f);
312 stroke.setStyle(SkPaint::kStroke_Style);
313 canvas->drawRect(SkRect::MakeWH(w, h).makeInset(0.5, 0.5), stroke);
314#endif
315 canvas->translate(w + kMargin, 0.f);
316 }
317 canvas->restore();
318 canvas->translate(0, h + kMargin);
319 }
320 }
321 canvas->restore();
322 canvas->translate(totalW + 2 * kMargin, 0);
323 }
Brian Salomon78be3eb2019-09-10 13:37:23 -0400324 return DrawResult::kOk;
Brian Salomonb2d5d402019-09-10 10:11:52 -0400325 }
326 bool onAnimate(double nanos) override {
327 fSigmaAnimationBoost = TimeUtils::SineWave(nanos, 5, 2.5f, 0.f, 2.f);
328 fRecalcMasksForAnimation = true;
329 return true;
330 }
331
332private:
333 void prepareReferenceMasks() {
334 auto create_reference_mask = [](int w, int h, float sigma, int numSubpixels) {
335 int pad = PadForSigma(sigma);
336 int maskW = w + 2 * pad;
337 int maskH = h + 2 * pad;
338 // We'll do all our calculations at subpixel resolution, so adjust params
339 w *= numSubpixels;
340 h *= numSubpixels;
341 sigma *= numSubpixels;
342 auto scale = SK_ScalarRoot2Over2 / sigma;
343 auto def_integral_approx = [scale](float a, float b) {
344 return 0.5f * (std::erf(b * scale) - std::erf(a * scale));
345 };
346 // Do the x-pass. Above/below rect are rows of zero. All rows that intersect the rect
347 // are the same. The row is calculated and stored at subpixel resolution.
348 SkASSERT(!(numSubpixels & 0b1));
349 std::unique_ptr<float[]> row(new float[maskW * numSubpixels]);
350 for (int col = 0; col < maskW * numSubpixels; ++col) {
351 // Compute distance to rect left in subpixel units
352 float ldiff = numSubpixels * pad - (col + 0.5f);
353 float rdiff = ldiff + w;
354 row[col] = def_integral_approx(ldiff, rdiff);
355 }
356 // y-pass
357 SkBitmap bmp;
358 bmp.allocPixels(SkImageInfo::MakeA8(maskW, maskH));
359 std::unique_ptr<float[]> accums(new float[maskW]);
360 const float accumScale = 1.f / (numSubpixels * numSubpixels);
361 for (int y = 0; y < maskH; ++y) {
362 // Initialize subpixel accumulation buffer for this row.
363 std::fill_n(accums.get(), maskW, 0);
364 for (int ys = 0; ys < numSubpixels; ++ys) {
365 // At each subpixel we want to integrate over the kernel centered at the
366 // subpixel multiplied by the x-pass. The x-pass is zero above and below the
367 // rect and constant valued from rect top to rect bottom. So we can get the
368 // integral of just the kernel from rect top to rect bottom and multiply by
369 // the single x-pass value from our precomputed row.
370 float tdiff = numSubpixels * pad - (y * numSubpixels + ys + 0.5f);
371 float bdiff = tdiff + h;
372 auto w = def_integral_approx(tdiff, bdiff);
373 for (int x = 0; x < maskW; ++x) {
374 for (int xs = 0; xs < numSubpixels; ++xs) {
375 int rowIdx = x * numSubpixels + xs;
376 accums[x] += w * row[rowIdx];
377 }
378 }
379 }
380 for (int x = 0; x < maskW; ++x) {
381 auto result = accums[x] * accumScale;
382 *bmp.getAddr8(x, y) = SkToU8(sk_float_round2int(255.f * result));
383 }
384 }
385 return SkImage::MakeFromBitmap(bmp);
386 };
387
388 // Number of times to subsample (in both X and Y). If fRecalcMasksForAnimation is true
389 // then we're animating, don't subsample as much to keep fps higher.
390 const int numSubpixels = fRecalcMasksForAnimation ? 2 : 8;
391
392 for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
393 auto sigma = kSigmas[sigmaIdx] + fSigmaAnimationBoost;
394 for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
395 auto h = kSizes[heightIdx];
396 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
397 auto w = kSizes[widthIdx];
398 fReferenceMasks[sigmaIdx][heightIdx][widthIdx] =
399 create_reference_mask(w, h, sigma, numSubpixels);
400 }
401 }
402 }
403 }
404
405 void prepareActualMasks(SkCanvas* canvas) {
406 for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
407 auto sigma = kSigmas[sigmaIdx] + fSigmaAnimationBoost;
408 for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
409 auto h = kSizes[heightIdx];
410 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
411 auto w = kSizes[widthIdx];
412 auto pad = PadForSigma(sigma);
413 auto ii = SkImageInfo::MakeA8(w + 2 * pad, h + 2 * pad);
414 auto surf = canvas->makeSurface(ii);
415 if (!surf) {
Brian Salomon032cf122019-09-11 18:05:36 -0400416 // Some GPUs don't have renderable A8 :(
417 surf = canvas->makeSurface(ii.makeColorType(kRGBA_8888_SkColorType));
418 if (!surf) {
419 return;
420 }
Brian Salomonb2d5d402019-09-10 10:11:52 -0400421 }
422 auto rect = SkRect::MakeXYWH(pad, pad, w, h);
423 SkPaint paint;
Brian Salomondb47b162019-09-18 11:17:09 -0400424 // Color doesn't matter if we're rendering to A8 but does if we promoted to
425 // RGBA above.
426 paint.setColor(SK_ColorWHITE);
Brian Salomonb2d5d402019-09-10 10:11:52 -0400427 paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma));
428 surf->getCanvas()->drawRect(rect, paint);
429 fActualMasks[sigmaIdx][heightIdx][widthIdx] = surf->makeImageSnapshot();
430 }
431 }
432 }
433 }
434
435 void prepareMaskDifferences(SkCanvas* canvas) {
436 for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
437 for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
438 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
439 const auto& r = fReferenceMasks[sigmaIdx][heightIdx][widthIdx];
440 const auto& a = fActualMasks[sigmaIdx][heightIdx][widthIdx];
441 auto& d = fMaskDifferences[sigmaIdx][heightIdx][widthIdx];
442 // The actual image might not be present if we're on an abandoned GrContext.
443 if (!a) {
444 d.reset();
445 continue;
446 }
447 SkASSERT(r->width() == a->width());
448 SkASSERT(r->height() == a->height());
449 auto ii = SkImageInfo::Make(r->width(), r->height(),
450 kRGBA_8888_SkColorType, kPremul_SkAlphaType);
451 auto surf = canvas->makeSurface(ii);
452 if (!surf) {
453 return;
454 }
455 // We visualize the difference by turning both the alpha masks into opaque green
456 // images (where alpha becomes the green channel) and then perform a
457 // SkBlendMode::kDifference between them.
458 SkPaint filterPaint;
459 filterPaint.setColor(SK_ColorWHITE);
460 // Actually 8 * alpha becomes green to really highlight differences.
461 static constexpr float kGreenifyM[] = {0, 0, 0, 0, 0,
462 0, 0, 0, 8, 0,
463 0, 0, 0, 0, 0,
464 0, 0, 0, 0, 1};
465 auto greenifyCF = SkColorFilters::Matrix(kGreenifyM);
466 SkPaint paint;
467 paint.setBlendMode(SkBlendMode::kSrc);
468 paint.setColorFilter(std::move(greenifyCF));
469 surf->getCanvas()->drawImage(a, 0, 0, &paint);
470 paint.setBlendMode(SkBlendMode::kDifference);
471 surf->getCanvas()->drawImage(r, 0, 0, &paint);
472 d = surf->makeImageSnapshot();
473 }
474 }
475 }
476 }
477
478 // Per side padding around mask images for a sigma. Make this overly generous to ensure bugs
479 // related to big blurs are fully visible.
480 static int PadForSigma(float sigma) { return sk_float_ceil2int(4 * sigma); }
481
482 static constexpr int kSizes[] = {1, 2, 4, 8, 16, 32};
483 static constexpr float kSigmas[] = {0.5f, 1.2f, 2.3f, 3.9f, 7.4f};
484 static constexpr size_t kNumSizes = SK_ARRAY_COUNT(kSizes);
485 static constexpr size_t kNumSigmas = SK_ARRAY_COUNT(kSigmas);
486
487 sk_sp<SkImage> fReferenceMasks[kNumSigmas][kNumSizes][kNumSizes];
488 sk_sp<SkImage> fActualMasks[kNumSigmas][kNumSizes][kNumSizes];
489 sk_sp<SkImage> fMaskDifferences[kNumSigmas][kNumSizes][kNumSizes];
490 int32_t fLastContextUniqueID;
491 // These are used only when animating.
492 float fSigmaAnimationBoost = 0;
493 bool fRecalcMasksForAnimation = false;
494};
495
496// Delete these when C++17.
497constexpr int BlurRectCompareGM::kSizes[];
498constexpr float BlurRectCompareGM::kSigmas[];
499constexpr size_t BlurRectCompareGM::kNumSizes;
500constexpr size_t BlurRectCompareGM::kNumSigmas;
501
502} // namespace skiagm
503
reed@google.comdb87c962012-11-02 21:11:12 +0000504//////////////////////////////////////////////////////////////////////////////
505
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000506DEF_GM(return new BlurRectGM("blurrects", 0xFF);)
Brian Salomonb2d5d402019-09-10 10:11:52 -0400507DEF_GM(return new skiagm::BlurRectCompareGM();)