blob: caca08ccbd3e37c5ac3066da63da84a85e71b26b [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);
56 path.setFillType(SkPath::kEvenOdd_FillType);
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);
74 path.setFillType(SkPath::kEvenOdd_FillType);
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
254 void onDraw(SkCanvas* canvas) override {
255 int32_t ctxID = canvas->getGrContext() ? canvas->getGrContext()->priv().contextID() : 0;
256 if (fRecalcMasksForAnimation || !fActualMasks[0][0][0] || ctxID != fLastContextUniqueID) {
257 if (fRecalcMasksForAnimation) {
258 // Sigma is changing so references must also be recalculated.
259 this->prepareReferenceMasks();
260 }
261 this->prepareActualMasks(canvas);
262 this->prepareMaskDifferences(canvas);
263 fLastContextUniqueID = ctxID;
264 fRecalcMasksForAnimation = false;
265 }
266 canvas->clear(SK_ColorBLACK);
267 static constexpr float kMargin = 30;
268 float totalW = 0;
269 for (auto w : kSizes) {
270 totalW += w + kMargin;
271 }
272 canvas->translate(kMargin, kMargin);
273 for (int mode = 0; mode < 3; ++mode) {
274 canvas->save();
275 for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
276 auto sigma = kSigmas[sigmaIdx] + fSigmaAnimationBoost;
277 for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
278 auto h = kSizes[heightIdx];
279 canvas->save();
280 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
281 auto w = kSizes[widthIdx];
282 SkPaint paint;
283 paint.setColor(SK_ColorWHITE);
284 SkImage* img;
285 switch (mode) {
286 case 0:
287 img = fReferenceMasks[sigmaIdx][heightIdx][widthIdx].get();
288 break;
289 case 1:
290 img = fActualMasks[sigmaIdx][heightIdx][widthIdx].get();
291 break;
292 case 2:
293 img = fMaskDifferences[sigmaIdx][heightIdx][widthIdx].get();
294 // The error images are opaque, use kPlus so they are additive if
295 // the overlap between test cases.
296 paint.setBlendMode(SkBlendMode::kPlus);
297 break;
298 }
299 auto pad = PadForSigma(sigma);
300 canvas->drawImage(img, -pad, -pad, &paint);
301#if 0 // Uncomment to hairline stroke around blurred rect in red on top of the blur result.
302 // The rect is defined at integer coords. We inset by 1/2 pixel so our stroke lies on top
303 // of the edge pixels.
304 SkPaint stroke;
305 stroke.setColor(SK_ColorRED);
306 stroke.setStrokeWidth(0.f);
307 stroke.setStyle(SkPaint::kStroke_Style);
308 canvas->drawRect(SkRect::MakeWH(w, h).makeInset(0.5, 0.5), stroke);
309#endif
310 canvas->translate(w + kMargin, 0.f);
311 }
312 canvas->restore();
313 canvas->translate(0, h + kMargin);
314 }
315 }
316 canvas->restore();
317 canvas->translate(totalW + 2 * kMargin, 0);
318 }
319 }
320 bool onAnimate(double nanos) override {
321 fSigmaAnimationBoost = TimeUtils::SineWave(nanos, 5, 2.5f, 0.f, 2.f);
322 fRecalcMasksForAnimation = true;
323 return true;
324 }
325
326private:
327 void prepareReferenceMasks() {
328 auto create_reference_mask = [](int w, int h, float sigma, int numSubpixels) {
329 int pad = PadForSigma(sigma);
330 int maskW = w + 2 * pad;
331 int maskH = h + 2 * pad;
332 // We'll do all our calculations at subpixel resolution, so adjust params
333 w *= numSubpixels;
334 h *= numSubpixels;
335 sigma *= numSubpixels;
336 auto scale = SK_ScalarRoot2Over2 / sigma;
337 auto def_integral_approx = [scale](float a, float b) {
338 return 0.5f * (std::erf(b * scale) - std::erf(a * scale));
339 };
340 // Do the x-pass. Above/below rect are rows of zero. All rows that intersect the rect
341 // are the same. The row is calculated and stored at subpixel resolution.
342 SkASSERT(!(numSubpixels & 0b1));
343 std::unique_ptr<float[]> row(new float[maskW * numSubpixels]);
344 for (int col = 0; col < maskW * numSubpixels; ++col) {
345 // Compute distance to rect left in subpixel units
346 float ldiff = numSubpixels * pad - (col + 0.5f);
347 float rdiff = ldiff + w;
348 row[col] = def_integral_approx(ldiff, rdiff);
349 }
350 // y-pass
351 SkBitmap bmp;
352 bmp.allocPixels(SkImageInfo::MakeA8(maskW, maskH));
353 std::unique_ptr<float[]> accums(new float[maskW]);
354 const float accumScale = 1.f / (numSubpixels * numSubpixels);
355 for (int y = 0; y < maskH; ++y) {
356 // Initialize subpixel accumulation buffer for this row.
357 std::fill_n(accums.get(), maskW, 0);
358 for (int ys = 0; ys < numSubpixels; ++ys) {
359 // At each subpixel we want to integrate over the kernel centered at the
360 // subpixel multiplied by the x-pass. The x-pass is zero above and below the
361 // rect and constant valued from rect top to rect bottom. So we can get the
362 // integral of just the kernel from rect top to rect bottom and multiply by
363 // the single x-pass value from our precomputed row.
364 float tdiff = numSubpixels * pad - (y * numSubpixels + ys + 0.5f);
365 float bdiff = tdiff + h;
366 auto w = def_integral_approx(tdiff, bdiff);
367 for (int x = 0; x < maskW; ++x) {
368 for (int xs = 0; xs < numSubpixels; ++xs) {
369 int rowIdx = x * numSubpixels + xs;
370 accums[x] += w * row[rowIdx];
371 }
372 }
373 }
374 for (int x = 0; x < maskW; ++x) {
375 auto result = accums[x] * accumScale;
376 *bmp.getAddr8(x, y) = SkToU8(sk_float_round2int(255.f * result));
377 }
378 }
379 return SkImage::MakeFromBitmap(bmp);
380 };
381
382 // Number of times to subsample (in both X and Y). If fRecalcMasksForAnimation is true
383 // then we're animating, don't subsample as much to keep fps higher.
384 const int numSubpixels = fRecalcMasksForAnimation ? 2 : 8;
385
386 for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
387 auto sigma = kSigmas[sigmaIdx] + fSigmaAnimationBoost;
388 for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
389 auto h = kSizes[heightIdx];
390 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
391 auto w = kSizes[widthIdx];
392 fReferenceMasks[sigmaIdx][heightIdx][widthIdx] =
393 create_reference_mask(w, h, sigma, numSubpixels);
394 }
395 }
396 }
397 }
398
399 void prepareActualMasks(SkCanvas* canvas) {
400 for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
401 auto sigma = kSigmas[sigmaIdx] + fSigmaAnimationBoost;
402 for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
403 auto h = kSizes[heightIdx];
404 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
405 auto w = kSizes[widthIdx];
406 auto pad = PadForSigma(sigma);
407 auto ii = SkImageInfo::MakeA8(w + 2 * pad, h + 2 * pad);
408 auto surf = canvas->makeSurface(ii);
409 if (!surf) {
410 return;
411 }
412 auto rect = SkRect::MakeXYWH(pad, pad, w, h);
413 SkPaint paint;
414 paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma));
415 surf->getCanvas()->drawRect(rect, paint);
416 fActualMasks[sigmaIdx][heightIdx][widthIdx] = surf->makeImageSnapshot();
417 }
418 }
419 }
420 }
421
422 void prepareMaskDifferences(SkCanvas* canvas) {
423 for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
424 for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
425 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
426 const auto& r = fReferenceMasks[sigmaIdx][heightIdx][widthIdx];
427 const auto& a = fActualMasks[sigmaIdx][heightIdx][widthIdx];
428 auto& d = fMaskDifferences[sigmaIdx][heightIdx][widthIdx];
429 // The actual image might not be present if we're on an abandoned GrContext.
430 if (!a) {
431 d.reset();
432 continue;
433 }
434 SkASSERT(r->width() == a->width());
435 SkASSERT(r->height() == a->height());
436 auto ii = SkImageInfo::Make(r->width(), r->height(),
437 kRGBA_8888_SkColorType, kPremul_SkAlphaType);
438 auto surf = canvas->makeSurface(ii);
439 if (!surf) {
440 return;
441 }
442 // We visualize the difference by turning both the alpha masks into opaque green
443 // images (where alpha becomes the green channel) and then perform a
444 // SkBlendMode::kDifference between them.
445 SkPaint filterPaint;
446 filterPaint.setColor(SK_ColorWHITE);
447 // Actually 8 * alpha becomes green to really highlight differences.
448 static constexpr float kGreenifyM[] = {0, 0, 0, 0, 0,
449 0, 0, 0, 8, 0,
450 0, 0, 0, 0, 0,
451 0, 0, 0, 0, 1};
452 auto greenifyCF = SkColorFilters::Matrix(kGreenifyM);
453 SkPaint paint;
454 paint.setBlendMode(SkBlendMode::kSrc);
455 paint.setColorFilter(std::move(greenifyCF));
456 surf->getCanvas()->drawImage(a, 0, 0, &paint);
457 paint.setBlendMode(SkBlendMode::kDifference);
458 surf->getCanvas()->drawImage(r, 0, 0, &paint);
459 d = surf->makeImageSnapshot();
460 }
461 }
462 }
463 }
464
465 // Per side padding around mask images for a sigma. Make this overly generous to ensure bugs
466 // related to big blurs are fully visible.
467 static int PadForSigma(float sigma) { return sk_float_ceil2int(4 * sigma); }
468
469 static constexpr int kSizes[] = {1, 2, 4, 8, 16, 32};
470 static constexpr float kSigmas[] = {0.5f, 1.2f, 2.3f, 3.9f, 7.4f};
471 static constexpr size_t kNumSizes = SK_ARRAY_COUNT(kSizes);
472 static constexpr size_t kNumSigmas = SK_ARRAY_COUNT(kSigmas);
473
474 sk_sp<SkImage> fReferenceMasks[kNumSigmas][kNumSizes][kNumSizes];
475 sk_sp<SkImage> fActualMasks[kNumSigmas][kNumSizes][kNumSizes];
476 sk_sp<SkImage> fMaskDifferences[kNumSigmas][kNumSizes][kNumSizes];
477 int32_t fLastContextUniqueID;
478 // These are used only when animating.
479 float fSigmaAnimationBoost = 0;
480 bool fRecalcMasksForAnimation = false;
481};
482
483// Delete these when C++17.
484constexpr int BlurRectCompareGM::kSizes[];
485constexpr float BlurRectCompareGM::kSigmas[];
486constexpr size_t BlurRectCompareGM::kNumSizes;
487constexpr size_t BlurRectCompareGM::kNumSigmas;
488
489} // namespace skiagm
490
reed@google.comdb87c962012-11-02 21:11:12 +0000491//////////////////////////////////////////////////////////////////////////////
492
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000493DEF_GM(return new BlurRectGM("blurrects", 0xFF);)
Brian Salomonb2d5d402019-09-10 10:11:52 -0400494DEF_GM(return new skiagm::BlurRectCompareGM();)