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