blob: 1208afaa159233ae6eb57ee10671aac1adb020aa [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"
Robert Phillipse19babf2020-04-06 13:57:30 -040014#include "include/core/SkColorFilter.h"
Brian Salomonb2d5d402019-09-10 10:11:52 -040015#include "include/core/SkImage.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#include "include/core/SkMaskFilter.h"
Ben Wagner6a34f3a2019-05-01 10:59:30 -040017#include "include/core/SkMatrix.h"
18#include "include/core/SkPaint.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "include/core/SkPath.h"
Ben Wagner6a34f3a2019-05-01 10:59:30 -040020#include "include/core/SkPoint.h"
21#include "include/core/SkRect.h"
22#include "include/core/SkRefCnt.h"
23#include "include/core/SkScalar.h"
24#include "include/core/SkShader.h"
25#include "include/core/SkSize.h"
26#include "include/core/SkString.h"
Brian Salomonb2d5d402019-09-10 10:11:52 -040027#include "include/core/SkSurface.h"
Ben Wagner6a34f3a2019-05-01 10:59:30 -040028#include "include/core/SkTileMode.h"
29#include "include/core/SkTypes.h"
30#include "include/effects/SkGradientShader.h"
Robert Phillipsb7bfbc22020-07-01 12:55:01 -040031#include "include/gpu/GrRecordingContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050032#include "include/private/SkTo.h"
33#include "src/core/SkBlurMask.h"
Ben Wagner6a34f3a2019-05-01 10:59:30 -040034#include "src/core/SkMask.h"
Robert Phillips95c250c2020-06-29 15:36:12 -040035#include "src/gpu/GrRecordingContextPriv.h"
Brian Salomonb2d5d402019-09-10 10:11:52 -040036#include "tools/timer/TimeUtils.h"
reed@google.comdb87c962012-11-02 21:11:12 +000037
38#define STROKE_WIDTH SkIntToScalar(10)
39
40typedef void (*Proc)(SkCanvas*, const SkRect&, const SkPaint&);
41
42static void fill_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
43 canvas->drawRect(r, p);
44}
45
reed@google.comdb87c962012-11-02 21:11:12 +000046static void draw_donut(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
47 SkRect rect;
48 SkPath path;
skia.committer@gmail.com34587162012-11-20 02:01:23 +000049
reed@google.comdb87c962012-11-02 21:11:12 +000050 rect = r;
51 rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2);
52 path.addRect(rect);
53 rect = r;
54 rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
skia.committer@gmail.com34587162012-11-20 02:01:23 +000055
reed@google.comdb87c962012-11-02 21:11:12 +000056 path.addRect(rect);
Mike Reed7d34dc72019-11-26 12:17:17 -050057 path.setFillType(SkPathFillType::kEvenOdd);
skia.committer@gmail.com34587162012-11-20 02:01:23 +000058
reed@google.com808b70f2012-11-19 16:14:02 +000059 canvas->drawPath(path, p);
60}
reed@google.comdb87c962012-11-02 21:11:12 +000061
reed@google.com808b70f2012-11-19 16:14:02 +000062static void draw_donut_skewed(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
63 SkRect rect;
64 SkPath path;
skia.committer@gmail.com34587162012-11-20 02:01:23 +000065
reed@google.com808b70f2012-11-19 16:14:02 +000066 rect = r;
67 rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2);
68 path.addRect(rect);
69 rect = r;
70 rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
skia.committer@gmail.com34587162012-11-20 02:01:23 +000071
reed@google.com808b70f2012-11-19 16:14:02 +000072 rect.offset(7, -7);
skia.committer@gmail.com34587162012-11-20 02:01:23 +000073
reed@google.com808b70f2012-11-19 16:14:02 +000074 path.addRect(rect);
Mike Reed7d34dc72019-11-26 12:17:17 -050075 path.setFillType(SkPathFillType::kEvenOdd);
skia.committer@gmail.com34587162012-11-20 02:01:23 +000076
reed@google.comdb87c962012-11-02 21:11:12 +000077 canvas->drawPath(path, p);
78}
79
joshualitt341400e2014-12-18 11:54:13 -080080/*
81 * Spits out a dummy gradient to test blur with shader on paint
82 */
Robert Phillips09dfc472017-09-13 15:25:47 -040083static sk_sp<SkShader> make_radial() {
joshualitt341400e2014-12-18 11:54:13 -080084 SkPoint pts[2] = {
85 { 0, 0 },
86 { SkIntToScalar(100), SkIntToScalar(100) }
87 };
Mike Reedfae8fce2019-04-03 10:27:45 -040088 SkTileMode tm = SkTileMode::kClamp;
joshualitt341400e2014-12-18 11:54:13 -080089 const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, };
90 const SkScalar pos[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
91 SkMatrix scale;
92 scale.setScale(0.5f, 0.5f);
93 scale.postTranslate(25.f, 25.f);
94 SkPoint center0, center1;
95 center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
96 SkScalarAve(pts[0].fY, pts[1].fY));
97 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
98 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
reed2ad1aa62016-03-09 09:50:50 -080099 return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
100 center0, (pts[1].fX - pts[0].fX) / 2,
101 colors, pos, SK_ARRAY_COUNT(colors), tm,
102 0, &scale);
joshualitt341400e2014-12-18 11:54:13 -0800103}
104
reed@google.com53007a22012-11-26 14:39:50 +0000105typedef void (*PaintProc)(SkPaint*, SkScalar width);
106
reed@google.comdb87c962012-11-02 21:11:12 +0000107class BlurRectGM : public skiagm::GM {
reed@google.comdb87c962012-11-02 21:11:12 +0000108public:
Hal Canary594fe852019-07-18 13:35:49 -0400109 BlurRectGM(const char name[], U8CPU alpha) : fName(name), fAlpha(SkToU8(alpha)) {}
reed@google.comdb87c962012-11-02 21:11:12 +0000110
Hal Canary594fe852019-07-18 13:35:49 -0400111private:
112 sk_sp<SkMaskFilter> fMaskFilters[kLastEnum_SkBlurStyle + 1];
113 const char* fName;
114 SkAlpha fAlpha;
115
mtklein36352bf2015-03-25 18:17:31 -0700116 void onOnceBeforeDraw() override {
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000117 for (int i = 0; i <= kLastEnum_SkBlurStyle; ++i) {
Mike Reed1be1f8d2018-03-14 13:01:17 -0400118 fMaskFilters[i] = SkMaskFilter::MakeBlur((SkBlurStyle)i,
119 SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(STROKE_WIDTH/2)));
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000120 }
121 }
122
Hal Canary594fe852019-07-18 13:35:49 -0400123 SkString onShortName() override { return SkString(fName); }
reed@google.comdb87c962012-11-02 21:11:12 +0000124
Hal Canary594fe852019-07-18 13:35:49 -0400125 SkISize onISize() override { return {860, 820}; }
reed@google.comdb87c962012-11-02 21:11:12 +0000126
mtklein36352bf2015-03-25 18:17:31 -0700127 void onDraw(SkCanvas* canvas) override {
reed@google.comdb87c962012-11-02 21:11:12 +0000128 canvas->translate(STROKE_WIDTH*3/2, STROKE_WIDTH*3/2);
129
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000130 SkRect r = { 0, 0, 100, 50 };
131 SkScalar scales[] = { SK_Scalar1, 0.6f };
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000132
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000133 for (size_t s = 0; s < SK_ARRAY_COUNT(scales); ++s) {
134 canvas->save();
135 for (size_t f = 0; f < SK_ARRAY_COUNT(fMaskFilters); ++f) {
136 SkPaint paint;
137 paint.setMaskFilter(fMaskFilters[f]);
138 paint.setAlpha(fAlpha);
139
joshualitt341400e2014-12-18 11:54:13 -0800140 SkPaint paintWithRadial = paint;
Robert Phillips09dfc472017-09-13 15:25:47 -0400141 paintWithRadial.setShader(make_radial());
joshualitt341400e2014-12-18 11:54:13 -0800142
mtkleindbfd7ab2016-09-01 11:24:54 -0700143 constexpr Proc procs[] = {
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000144 fill_rect, draw_donut, draw_donut_skewed
145 };
146
147 canvas->save();
148 canvas->scale(scales[s], scales[s]);
149 this->drawProcs(canvas, r, paint, false, procs, SK_ARRAY_COUNT(procs));
150 canvas->translate(r.width() * 4/3, 0);
joshualitt341400e2014-12-18 11:54:13 -0800151 this->drawProcs(canvas, r, paintWithRadial, false, procs, SK_ARRAY_COUNT(procs));
152 canvas->translate(r.width() * 4/3, 0);
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000153 this->drawProcs(canvas, r, paint, true, procs, SK_ARRAY_COUNT(procs));
joshualitt341400e2014-12-18 11:54:13 -0800154 canvas->translate(r.width() * 4/3, 0);
155 this->drawProcs(canvas, r, paintWithRadial, true, procs, SK_ARRAY_COUNT(procs));
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000156 canvas->restore();
157
158 canvas->translate(0, SK_ARRAY_COUNT(procs) * r.height() * 4/3 * scales[s]);
159 }
160 canvas->restore();
joshualitt341400e2014-12-18 11:54:13 -0800161 canvas->translate(4 * r.width() * 4/3 * scales[s], 0);
reed@google.com53007a22012-11-26 14:39:50 +0000162 }
reed@google.comdb87c962012-11-02 21:11:12 +0000163 }
164
reed@google.comdb87c962012-11-02 21:11:12 +0000165 void drawProcs(SkCanvas* canvas, const SkRect& r, const SkPaint& paint,
166 bool doClip, const Proc procs[], size_t procsCount) {
167 SkAutoCanvasRestore acr(canvas, true);
168 for (size_t i = 0; i < procsCount; ++i) {
169 if (doClip) {
170 SkRect clipRect(r);
171 clipRect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
172 canvas->save();
173 canvas->clipRect(r);
174 }
175 procs[i](canvas, r, paint);
176 if (doClip) {
177 canvas->restore();
178 }
179 canvas->translate(0, r.height() * 4/3);
180 }
181 }
reed@google.comdb87c962012-11-02 21:11:12 +0000182};
183
halcanary2a243382015-09-09 08:16:41 -0700184DEF_SIMPLE_GM(blurrect_gallery, canvas, 1200, 1024) {
185 const int fGMWidth = 1200;
186 const int fPadding = 10;
187 const int fMargin = 100;
commit-bot@chromium.org3c1594a2014-05-28 21:52:12 +0000188
commit-bot@chromium.org3c1594a2014-05-28 21:52:12 +0000189 const int widths[] = {25, 5, 5, 100, 150, 25};
190 const int heights[] = {100, 100, 5, 25, 150, 25};
191 const SkBlurStyle styles[] = {kNormal_SkBlurStyle, kInner_SkBlurStyle, kOuter_SkBlurStyle};
192 const float radii[] = {20, 5, 10};
193
194 canvas->translate(50,20);
195
196 int cur_x = 0;
197 int cur_y = 0;
198
199 int max_height = 0;
200
201 for (size_t i = 0 ; i < SK_ARRAY_COUNT(widths) ; i++) {
202 int width = widths[i];
203 int height = heights[i];
204 SkRect r;
205 r.setWH(SkIntToScalar(width), SkIntToScalar(height));
206 SkAutoCanvasRestore autoRestore(canvas, true);
207
208 for (size_t j = 0 ; j < SK_ARRAY_COUNT(radii) ; j++) {
209 float radius = radii[j];
210 for (size_t k = 0 ; k < SK_ARRAY_COUNT(styles) ; k++) {
211 SkBlurStyle style = styles[k];
212
213 SkMask mask;
robertphillipse80eb922015-12-17 11:33:12 -0800214 if (!SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(radius),
215 &mask, r, style)) {
216 continue;
217 }
commit-bot@chromium.org3c1594a2014-05-28 21:52:12 +0000218
219 SkAutoMaskFreeImage amfi(mask.fImage);
220
221 SkBitmap bm;
222 bm.installMaskPixels(mask);
223
224 if (cur_x + bm.width() >= fGMWidth - fMargin) {
225 cur_x = 0;
226 cur_y += max_height + fPadding;
227 max_height = 0;
228 }
229
230 canvas->save();
commit-bot@chromium.org793ddd92014-05-28 22:42:31 +0000231 canvas->translate((SkScalar)cur_x, (SkScalar)cur_y);
commit-bot@chromium.org3c1594a2014-05-28 21:52:12 +0000232 canvas->translate(-(bm.width() - r.width())/2, -(bm.height()-r.height())/2);
halcanary96fcdcc2015-08-27 07:41:13 -0700233 canvas->drawBitmap(bm, 0.f, 0.f, nullptr);
commit-bot@chromium.org3c1594a2014-05-28 21:52:12 +0000234 canvas->restore();
235
236 cur_x += bm.width() + fPadding;
237 if (bm.height() > max_height)
238 max_height = bm.height();
239 }
240 }
241 }
halcanary2a243382015-09-09 08:16:41 -0700242}
humper@google.com7c7292c2013-01-04 20:29:03 +0000243
Brian Salomonb2d5d402019-09-10 10:11:52 -0400244namespace skiagm {
245
246// Compares actual blur rects with reference masks created by the GM. Animates sigma in viewer.
247class BlurRectCompareGM : public GM {
248protected:
249 SkString onShortName() override { return SkString("blurrect_compare"); }
250
251 SkISize onISize() override { return {900, 1220}; }
252
253 void onOnceBeforeDraw() override { this->prepareReferenceMasks(); }
254
Brian Salomon78be3eb2019-09-10 13:37:23 -0400255 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
Brian Salomon4fe30e12019-09-10 17:12:58 -0400256 if (canvas->imageInfo().colorType() == kUnknown_SkColorType ||
Robert Phillipsf8f45d92020-07-01 11:11:18 -0400257 (canvas->recordingContext() && !canvas->recordingContext()->asDirectContext())) {
Brian Salomon78be3eb2019-09-10 13:37:23 -0400258 *errorMsg = "Not supported when recording, relies on canvas->makeSurface()";
259 return DrawResult::kSkip;
260 }
Robert Phillips95c250c2020-06-29 15:36:12 -0400261 int32_t ctxID = canvas->recordingContext() ? canvas->recordingContext()->priv().contextID()
262 : 0;
Brian Salomonb2d5d402019-09-10 10:11:52 -0400263 if (fRecalcMasksForAnimation || !fActualMasks[0][0][0] || ctxID != fLastContextUniqueID) {
264 if (fRecalcMasksForAnimation) {
265 // Sigma is changing so references must also be recalculated.
266 this->prepareReferenceMasks();
267 }
268 this->prepareActualMasks(canvas);
269 this->prepareMaskDifferences(canvas);
270 fLastContextUniqueID = ctxID;
271 fRecalcMasksForAnimation = false;
272 }
273 canvas->clear(SK_ColorBLACK);
274 static constexpr float kMargin = 30;
275 float totalW = 0;
276 for (auto w : kSizes) {
277 totalW += w + kMargin;
278 }
279 canvas->translate(kMargin, kMargin);
280 for (int mode = 0; mode < 3; ++mode) {
281 canvas->save();
282 for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
283 auto sigma = kSigmas[sigmaIdx] + fSigmaAnimationBoost;
284 for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
285 auto h = kSizes[heightIdx];
286 canvas->save();
287 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
288 auto w = kSizes[widthIdx];
289 SkPaint paint;
290 paint.setColor(SK_ColorWHITE);
291 SkImage* img;
292 switch (mode) {
293 case 0:
294 img = fReferenceMasks[sigmaIdx][heightIdx][widthIdx].get();
295 break;
296 case 1:
297 img = fActualMasks[sigmaIdx][heightIdx][widthIdx].get();
298 break;
299 case 2:
300 img = fMaskDifferences[sigmaIdx][heightIdx][widthIdx].get();
301 // The error images are opaque, use kPlus so they are additive if
302 // the overlap between test cases.
303 paint.setBlendMode(SkBlendMode::kPlus);
304 break;
305 }
306 auto pad = PadForSigma(sigma);
307 canvas->drawImage(img, -pad, -pad, &paint);
308#if 0 // Uncomment to hairline stroke around blurred rect in red on top of the blur result.
309 // The rect is defined at integer coords. We inset by 1/2 pixel so our stroke lies on top
310 // of the edge pixels.
311 SkPaint stroke;
312 stroke.setColor(SK_ColorRED);
313 stroke.setStrokeWidth(0.f);
314 stroke.setStyle(SkPaint::kStroke_Style);
315 canvas->drawRect(SkRect::MakeWH(w, h).makeInset(0.5, 0.5), stroke);
316#endif
317 canvas->translate(w + kMargin, 0.f);
318 }
319 canvas->restore();
320 canvas->translate(0, h + kMargin);
321 }
322 }
323 canvas->restore();
324 canvas->translate(totalW + 2 * kMargin, 0);
325 }
Brian Salomon78be3eb2019-09-10 13:37:23 -0400326 return DrawResult::kOk;
Brian Salomonb2d5d402019-09-10 10:11:52 -0400327 }
328 bool onAnimate(double nanos) override {
329 fSigmaAnimationBoost = TimeUtils::SineWave(nanos, 5, 2.5f, 0.f, 2.f);
330 fRecalcMasksForAnimation = true;
331 return true;
332 }
333
334private:
335 void prepareReferenceMasks() {
336 auto create_reference_mask = [](int w, int h, float sigma, int numSubpixels) {
337 int pad = PadForSigma(sigma);
338 int maskW = w + 2 * pad;
339 int maskH = h + 2 * pad;
340 // We'll do all our calculations at subpixel resolution, so adjust params
341 w *= numSubpixels;
342 h *= numSubpixels;
343 sigma *= numSubpixels;
344 auto scale = SK_ScalarRoot2Over2 / sigma;
345 auto def_integral_approx = [scale](float a, float b) {
346 return 0.5f * (std::erf(b * scale) - std::erf(a * scale));
347 };
348 // Do the x-pass. Above/below rect are rows of zero. All rows that intersect the rect
349 // are the same. The row is calculated and stored at subpixel resolution.
350 SkASSERT(!(numSubpixels & 0b1));
351 std::unique_ptr<float[]> row(new float[maskW * numSubpixels]);
352 for (int col = 0; col < maskW * numSubpixels; ++col) {
353 // Compute distance to rect left in subpixel units
354 float ldiff = numSubpixels * pad - (col + 0.5f);
355 float rdiff = ldiff + w;
356 row[col] = def_integral_approx(ldiff, rdiff);
357 }
358 // y-pass
359 SkBitmap bmp;
360 bmp.allocPixels(SkImageInfo::MakeA8(maskW, maskH));
361 std::unique_ptr<float[]> accums(new float[maskW]);
362 const float accumScale = 1.f / (numSubpixels * numSubpixels);
363 for (int y = 0; y < maskH; ++y) {
364 // Initialize subpixel accumulation buffer for this row.
365 std::fill_n(accums.get(), maskW, 0);
366 for (int ys = 0; ys < numSubpixels; ++ys) {
367 // At each subpixel we want to integrate over the kernel centered at the
368 // subpixel multiplied by the x-pass. The x-pass is zero above and below the
369 // rect and constant valued from rect top to rect bottom. So we can get the
370 // integral of just the kernel from rect top to rect bottom and multiply by
371 // the single x-pass value from our precomputed row.
372 float tdiff = numSubpixels * pad - (y * numSubpixels + ys + 0.5f);
373 float bdiff = tdiff + h;
374 auto w = def_integral_approx(tdiff, bdiff);
375 for (int x = 0; x < maskW; ++x) {
376 for (int xs = 0; xs < numSubpixels; ++xs) {
377 int rowIdx = x * numSubpixels + xs;
378 accums[x] += w * row[rowIdx];
379 }
380 }
381 }
382 for (int x = 0; x < maskW; ++x) {
383 auto result = accums[x] * accumScale;
384 *bmp.getAddr8(x, y) = SkToU8(sk_float_round2int(255.f * result));
385 }
386 }
387 return SkImage::MakeFromBitmap(bmp);
388 };
389
390 // Number of times to subsample (in both X and Y). If fRecalcMasksForAnimation is true
391 // then we're animating, don't subsample as much to keep fps higher.
392 const int numSubpixels = fRecalcMasksForAnimation ? 2 : 8;
393
394 for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
395 auto sigma = kSigmas[sigmaIdx] + fSigmaAnimationBoost;
396 for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
397 auto h = kSizes[heightIdx];
398 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
399 auto w = kSizes[widthIdx];
400 fReferenceMasks[sigmaIdx][heightIdx][widthIdx] =
401 create_reference_mask(w, h, sigma, numSubpixels);
402 }
403 }
404 }
405 }
406
407 void prepareActualMasks(SkCanvas* canvas) {
408 for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
409 auto sigma = kSigmas[sigmaIdx] + fSigmaAnimationBoost;
410 for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
411 auto h = kSizes[heightIdx];
412 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
413 auto w = kSizes[widthIdx];
414 auto pad = PadForSigma(sigma);
415 auto ii = SkImageInfo::MakeA8(w + 2 * pad, h + 2 * pad);
416 auto surf = canvas->makeSurface(ii);
417 if (!surf) {
Brian Salomon032cf122019-09-11 18:05:36 -0400418 // Some GPUs don't have renderable A8 :(
419 surf = canvas->makeSurface(ii.makeColorType(kRGBA_8888_SkColorType));
420 if (!surf) {
421 return;
422 }
Brian Salomonb2d5d402019-09-10 10:11:52 -0400423 }
424 auto rect = SkRect::MakeXYWH(pad, pad, w, h);
425 SkPaint paint;
Brian Salomondb47b162019-09-18 11:17:09 -0400426 // Color doesn't matter if we're rendering to A8 but does if we promoted to
427 // RGBA above.
428 paint.setColor(SK_ColorWHITE);
Brian Salomonb2d5d402019-09-10 10:11:52 -0400429 paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma));
430 surf->getCanvas()->drawRect(rect, paint);
431 fActualMasks[sigmaIdx][heightIdx][widthIdx] = surf->makeImageSnapshot();
432 }
433 }
434 }
435 }
436
437 void prepareMaskDifferences(SkCanvas* canvas) {
438 for (size_t sigmaIdx = 0; sigmaIdx < kNumSigmas; ++sigmaIdx) {
439 for (size_t heightIdx = 0; heightIdx < kNumSizes; ++heightIdx) {
440 for (size_t widthIdx = 0; widthIdx < kNumSizes; ++widthIdx) {
441 const auto& r = fReferenceMasks[sigmaIdx][heightIdx][widthIdx];
442 const auto& a = fActualMasks[sigmaIdx][heightIdx][widthIdx];
443 auto& d = fMaskDifferences[sigmaIdx][heightIdx][widthIdx];
444 // The actual image might not be present if we're on an abandoned GrContext.
445 if (!a) {
446 d.reset();
447 continue;
448 }
449 SkASSERT(r->width() == a->width());
450 SkASSERT(r->height() == a->height());
451 auto ii = SkImageInfo::Make(r->width(), r->height(),
452 kRGBA_8888_SkColorType, kPremul_SkAlphaType);
453 auto surf = canvas->makeSurface(ii);
454 if (!surf) {
455 return;
456 }
457 // We visualize the difference by turning both the alpha masks into opaque green
458 // images (where alpha becomes the green channel) and then perform a
459 // SkBlendMode::kDifference between them.
460 SkPaint filterPaint;
461 filterPaint.setColor(SK_ColorWHITE);
462 // Actually 8 * alpha becomes green to really highlight differences.
463 static constexpr float kGreenifyM[] = {0, 0, 0, 0, 0,
464 0, 0, 0, 8, 0,
465 0, 0, 0, 0, 0,
466 0, 0, 0, 0, 1};
467 auto greenifyCF = SkColorFilters::Matrix(kGreenifyM);
468 SkPaint paint;
469 paint.setBlendMode(SkBlendMode::kSrc);
470 paint.setColorFilter(std::move(greenifyCF));
471 surf->getCanvas()->drawImage(a, 0, 0, &paint);
472 paint.setBlendMode(SkBlendMode::kDifference);
473 surf->getCanvas()->drawImage(r, 0, 0, &paint);
474 d = surf->makeImageSnapshot();
475 }
476 }
477 }
478 }
479
480 // Per side padding around mask images for a sigma. Make this overly generous to ensure bugs
481 // related to big blurs are fully visible.
482 static int PadForSigma(float sigma) { return sk_float_ceil2int(4 * sigma); }
483
484 static constexpr int kSizes[] = {1, 2, 4, 8, 16, 32};
485 static constexpr float kSigmas[] = {0.5f, 1.2f, 2.3f, 3.9f, 7.4f};
486 static constexpr size_t kNumSizes = SK_ARRAY_COUNT(kSizes);
487 static constexpr size_t kNumSigmas = SK_ARRAY_COUNT(kSigmas);
488
489 sk_sp<SkImage> fReferenceMasks[kNumSigmas][kNumSizes][kNumSizes];
490 sk_sp<SkImage> fActualMasks[kNumSigmas][kNumSizes][kNumSizes];
491 sk_sp<SkImage> fMaskDifferences[kNumSigmas][kNumSizes][kNumSizes];
492 int32_t fLastContextUniqueID;
493 // These are used only when animating.
494 float fSigmaAnimationBoost = 0;
495 bool fRecalcMasksForAnimation = false;
496};
497
498// Delete these when C++17.
499constexpr int BlurRectCompareGM::kSizes[];
500constexpr float BlurRectCompareGM::kSigmas[];
501constexpr size_t BlurRectCompareGM::kNumSizes;
502constexpr size_t BlurRectCompareGM::kNumSigmas;
503
504} // namespace skiagm
505
reed@google.comdb87c962012-11-02 21:11:12 +0000506//////////////////////////////////////////////////////////////////////////////
507
commit-bot@chromium.org7cced562014-01-10 23:10:13 +0000508DEF_GM(return new BlurRectGM("blurrects", 0xFF);)
Brian Salomonb2d5d402019-09-10 10:11:52 -0400509DEF_GM(return new skiagm::BlurRectCompareGM();)