Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 1 | /* |
Ethan Nicholas | 130fb3f | 2018-02-01 12:14:34 -0500 | [diff] [blame] | 2 | * Copyright 2018 Google Inc. |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
Ethan Nicholas | 130fb3f | 2018-02-01 12:14:34 -0500 | [diff] [blame] | 8 | /************************************************************************************************** |
| 9 | *** This file was autogenerated from GrRRectBlurEffect.fp; do not modify. |
| 10 | **************************************************************************************************/ |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 11 | #include "GrRRectBlurEffect.h" |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 12 | |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 13 | #include "include/gpu/GrDirectContext.h" |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 14 | #include "include/gpu/GrRecordingContext.h" |
Robert Phillips | 1ee21cd | 2020-09-03 13:40:52 -0400 | [diff] [blame] | 15 | #include "src/core/SkAutoMalloc.h" |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 16 | #include "src/core/SkGpuBlurUtils.h" |
| 17 | #include "src/core/SkRRectPriv.h" |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 18 | #include "src/gpu/GrBitmapTextureMaker.h" |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 19 | #include "src/gpu/GrCaps.h" |
Adlai Holler | a069304 | 2020-10-14 11:23:11 -0400 | [diff] [blame] | 20 | #include "src/gpu/GrDirectContextPriv.h" |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 21 | #include "src/gpu/GrPaint.h" |
| 22 | #include "src/gpu/GrProxyProvider.h" |
| 23 | #include "src/gpu/GrRecordingContextPriv.h" |
| 24 | #include "src/gpu/GrRenderTargetContext.h" |
| 25 | #include "src/gpu/GrStyle.h" |
Robert Phillips | d464feb | 2020-10-08 11:00:02 -0400 | [diff] [blame] | 26 | #include "src/gpu/GrThreadSafeCache.h" |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 27 | #include "src/gpu/effects/GrTextureEffect.h" |
| 28 | |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 29 | static constexpr auto kBlurredRRectMaskOrigin = kTopLeft_GrSurfaceOrigin; |
Robert Phillips | 5bc0b65 | 2020-09-02 09:49:38 -0400 | [diff] [blame] | 30 | |
| 31 | static void make_blurred_rrect_key(GrUniqueKey* key, |
| 32 | const SkRRect& rrectToDraw, |
| 33 | float xformedSigma) { |
Michael Ludwig | e267464 | 2020-10-21 13:01:34 -0400 | [diff] [blame] | 34 | SkASSERT(!SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma)); |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 35 | static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); |
Robert Phillips | 5bc0b65 | 2020-09-02 09:49:38 -0400 | [diff] [blame] | 36 | |
| 37 | GrUniqueKey::Builder builder(key, kDomain, 9, "RoundRect Blur Mask"); |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 38 | builder[0] = SkScalarCeilToInt(xformedSigma - 1 / 6.0f); |
| 39 | |
| 40 | int index = 1; |
Robert Phillips | 5bc0b65 | 2020-09-02 09:49:38 -0400 | [diff] [blame] | 41 | // TODO: this is overkill for _simple_ circular rrects |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 42 | for (auto c : {SkRRect::kUpperLeft_Corner, SkRRect::kUpperRight_Corner, |
| 43 | SkRRect::kLowerRight_Corner, SkRRect::kLowerLeft_Corner}) { |
| 44 | SkASSERT(SkScalarIsInt(rrectToDraw.radii(c).fX) && SkScalarIsInt(rrectToDraw.radii(c).fY)); |
| 45 | builder[index++] = SkScalarCeilToInt(rrectToDraw.radii(c).fX); |
| 46 | builder[index++] = SkScalarCeilToInt(rrectToDraw.radii(c).fY); |
| 47 | } |
| 48 | builder.finish(); |
Robert Phillips | 5bc0b65 | 2020-09-02 09:49:38 -0400 | [diff] [blame] | 49 | } |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 50 | |
Robert Phillips | d464feb | 2020-10-08 11:00:02 -0400 | [diff] [blame] | 51 | static bool fillin_view_on_gpu(GrDirectContext* dContext, |
| 52 | const GrSurfaceProxyView& lazyView, |
| 53 | sk_sp<GrThreadSafeCache::Trampoline> trampoline, |
| 54 | const SkRRect& rrectToDraw, |
| 55 | const SkISize& dimensions, |
| 56 | float xformedSigma) { |
Michael Ludwig | e267464 | 2020-10-21 13:01:34 -0400 | [diff] [blame] | 57 | SkASSERT(!SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma)); |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 58 | std::unique_ptr<GrRenderTargetContext> rtc = GrRenderTargetContext::MakeWithFallback( |
| 59 | dContext, GrColorType::kAlpha_8, nullptr, SkBackingFit::kExact, dimensions, 1, |
Robert Phillips | 5bc0b65 | 2020-09-02 09:49:38 -0400 | [diff] [blame] | 60 | GrMipmapped::kNo, GrProtected::kNo, kBlurredRRectMaskOrigin); |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 61 | if (!rtc) { |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 62 | return false; |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 63 | } |
| 64 | |
| 65 | GrPaint paint; |
| 66 | |
| 67 | rtc->clear(SK_PMColor4fTRANSPARENT); |
| 68 | rtc->drawRRect(nullptr, std::move(paint), GrAA::kYes, SkMatrix::I(), rrectToDraw, |
| 69 | GrStyle::SimpleFill()); |
| 70 | |
| 71 | GrSurfaceProxyView srcView = rtc->readSurfaceView(); |
| 72 | if (!srcView) { |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 73 | return false; |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 74 | } |
| 75 | SkASSERT(srcView.asTextureProxy()); |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 76 | auto rtc2 = SkGpuBlurUtils::GaussianBlur(dContext, |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 77 | std::move(srcView), |
| 78 | rtc->colorInfo().colorType(), |
| 79 | rtc->colorInfo().alphaType(), |
| 80 | nullptr, |
| 81 | SkIRect::MakeSize(dimensions), |
| 82 | SkIRect::MakeSize(dimensions), |
| 83 | xformedSigma, |
| 84 | xformedSigma, |
| 85 | SkTileMode::kClamp, |
| 86 | SkBackingFit::kExact); |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 87 | if (!rtc2 || !rtc2->readSurfaceView()) { |
| 88 | return false; |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 89 | } |
| 90 | |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 91 | auto view = rtc2->readSurfaceView(); |
| 92 | SkASSERT(view.swizzle() == lazyView.swizzle()); |
| 93 | SkASSERT(view.origin() == lazyView.origin()); |
| 94 | trampoline->fProxy = view.asTextureProxyRef(); |
| 95 | |
| 96 | return true; |
Robert Phillips | 5bc0b65 | 2020-09-02 09:49:38 -0400 | [diff] [blame] | 97 | } |
| 98 | |
Robert Phillips | 1ee21cd | 2020-09-03 13:40:52 -0400 | [diff] [blame] | 99 | // Evaluate the vertical blur at the specified 'y' value given the location of the top of the |
| 100 | // rrect. |
| 101 | static uint8_t eval_V(float top, int y, const uint8_t* integral, int integralSize, float sixSigma) { |
| 102 | if (top < 0) { |
| 103 | return 0; // an empty column |
| 104 | } |
| 105 | |
| 106 | float fT = (top - y - 0.5f) * (integralSize / sixSigma); |
| 107 | if (fT < 0) { |
| 108 | return 255; |
| 109 | } else if (fT >= integralSize - 1) { |
| 110 | return 0; |
| 111 | } |
| 112 | |
| 113 | int lower = (int)fT; |
| 114 | float frac = fT - lower; |
| 115 | |
| 116 | SkASSERT(lower + 1 < integralSize); |
| 117 | |
| 118 | return integral[lower] * (1.0f - frac) + integral[lower + 1] * frac; |
| 119 | } |
| 120 | |
| 121 | // Apply a gaussian 'kernel' horizontally at the specified 'x', 'y' location. |
| 122 | static uint8_t eval_H(int x, |
| 123 | int y, |
| 124 | const std::vector<float>& topVec, |
| 125 | const float* kernel, |
| 126 | int kernelSize, |
| 127 | const uint8_t* integral, |
| 128 | int integralSize, |
| 129 | float sixSigma) { |
| 130 | SkASSERT(0 <= x && x < (int)topVec.size()); |
| 131 | SkASSERT(kernelSize % 2); |
| 132 | |
| 133 | float accum = 0.0f; |
| 134 | |
| 135 | int xSampleLoc = x - (kernelSize / 2); |
| 136 | for (int i = 0; i < kernelSize; ++i, ++xSampleLoc) { |
| 137 | if (xSampleLoc < 0 || xSampleLoc >= (int)topVec.size()) { |
| 138 | continue; |
| 139 | } |
| 140 | |
| 141 | accum += kernel[i] * eval_V(topVec[xSampleLoc], y, integral, integralSize, sixSigma); |
| 142 | } |
| 143 | |
| 144 | return accum + 0.5f; |
| 145 | } |
| 146 | |
| 147 | // Create a cpu-side blurred-rrect mask that is close to the version the gpu would've produced. |
| 148 | // The match needs to be close bc the cpu- and gpu-generated version must be interchangeable. |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 149 | static GrSurfaceProxyView create_mask_on_cpu(GrRecordingContext* rContext, |
Robert Phillips | 1ee21cd | 2020-09-03 13:40:52 -0400 | [diff] [blame] | 150 | const SkRRect& rrectToDraw, |
| 151 | const SkISize& dimensions, |
| 152 | float xformedSigma) { |
Michael Ludwig | e267464 | 2020-10-21 13:01:34 -0400 | [diff] [blame] | 153 | SkASSERT(!SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma)); |
| 154 | int radius = SkGpuBlurUtils::SigmaRadius(xformedSigma); |
Robert Phillips | 1ee21cd | 2020-09-03 13:40:52 -0400 | [diff] [blame] | 155 | int kernelSize = 2 * radius + 1; |
| 156 | |
| 157 | SkASSERT(kernelSize % 2); |
| 158 | SkASSERT(dimensions.width() % 2); |
| 159 | SkASSERT(dimensions.height() % 2); |
| 160 | |
| 161 | SkVector radii = rrectToDraw.getSimpleRadii(); |
| 162 | SkASSERT(SkScalarNearlyEqual(radii.fX, radii.fY)); |
| 163 | |
| 164 | const int halfWidthPlus1 = (dimensions.width() / 2) + 1; |
| 165 | const int halfHeightPlus1 = (dimensions.height() / 2) + 1; |
| 166 | |
| 167 | std::unique_ptr<float[]> kernel(new float[kernelSize]); |
| 168 | |
Michael Ludwig | e267464 | 2020-10-21 13:01:34 -0400 | [diff] [blame] | 169 | SkGpuBlurUtils::Compute1DGaussianKernel(kernel.get(), xformedSigma, radius); |
Robert Phillips | 1ee21cd | 2020-09-03 13:40:52 -0400 | [diff] [blame] | 170 | |
| 171 | SkBitmap integral; |
Michael Ludwig | e267464 | 2020-10-21 13:01:34 -0400 | [diff] [blame] | 172 | if (!SkGpuBlurUtils::CreateIntegralTable(6 * xformedSigma, &integral)) { |
Robert Phillips | 1ee21cd | 2020-09-03 13:40:52 -0400 | [diff] [blame] | 173 | return {}; |
| 174 | } |
| 175 | |
| 176 | SkBitmap result; |
| 177 | if (!result.tryAllocPixels(SkImageInfo::MakeA8(dimensions.width(), dimensions.height()))) { |
| 178 | return {}; |
| 179 | } |
| 180 | |
| 181 | std::vector<float> topVec; |
| 182 | topVec.reserve(dimensions.width()); |
| 183 | for (int x = 0; x < dimensions.width(); ++x) { |
| 184 | if (x < rrectToDraw.rect().fLeft || x > rrectToDraw.rect().fRight) { |
| 185 | topVec.push_back(-1); |
| 186 | } else { |
| 187 | if (x + 0.5f < rrectToDraw.rect().fLeft + radii.fX) { // in the circular section |
| 188 | float xDist = rrectToDraw.rect().fLeft + radii.fX - x - 0.5f; |
| 189 | float h = sqrtf(radii.fX * radii.fX - xDist * xDist); |
| 190 | SkASSERT(0 <= h && h < radii.fY); |
| 191 | topVec.push_back(rrectToDraw.rect().fTop + radii.fX - h + 3 * xformedSigma); |
| 192 | } else { |
| 193 | topVec.push_back(rrectToDraw.rect().fTop + 3 * xformedSigma); |
| 194 | } |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | for (int y = 0; y < halfHeightPlus1; ++y) { |
| 199 | uint8_t* scanline = result.getAddr8(0, y); |
| 200 | |
| 201 | for (int x = 0; x < halfWidthPlus1; ++x) { |
| 202 | scanline[x] = eval_H(x, y, topVec, kernel.get(), kernelSize, integral.getAddr8(0, 0), |
| 203 | integral.width(), 6 * xformedSigma); |
| 204 | scanline[dimensions.width() - x - 1] = scanline[x]; |
| 205 | } |
| 206 | |
| 207 | memcpy(result.getAddr8(0, dimensions.height() - y - 1), scanline, result.rowBytes()); |
| 208 | } |
| 209 | |
| 210 | result.setImmutable(); |
| 211 | |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 212 | GrBitmapTextureMaker maker(rContext, result, GrImageTexGenPolicy::kNew_Uncached_Budgeted); |
| 213 | auto view = maker.view(GrMipmapped::kNo); |
| 214 | if (!view) { |
Robert Phillips | 1ee21cd | 2020-09-03 13:40:52 -0400 | [diff] [blame] | 215 | return {}; |
| 216 | } |
| 217 | |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 218 | SkASSERT(view.origin() == kBlurredRRectMaskOrigin); |
| 219 | return view; |
| 220 | } |
| 221 | |
Robert Phillips | 5bc0b65 | 2020-09-02 09:49:38 -0400 | [diff] [blame] | 222 | static std::unique_ptr<GrFragmentProcessor> find_or_create_rrect_blur_mask_fp( |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 223 | GrRecordingContext* rContext, |
Robert Phillips | 5bc0b65 | 2020-09-02 09:49:38 -0400 | [diff] [blame] | 224 | const SkRRect& rrectToDraw, |
| 225 | const SkISize& dimensions, |
| 226 | float xformedSigma) { |
Michael Ludwig | e267464 | 2020-10-21 13:01:34 -0400 | [diff] [blame] | 227 | SkASSERT(!SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma)); |
Robert Phillips | 5bc0b65 | 2020-09-02 09:49:38 -0400 | [diff] [blame] | 228 | GrUniqueKey key; |
Brian Osman | 55b69f5 | 2020-09-30 01:41:15 +0000 | [diff] [blame] | 229 | make_blurred_rrect_key(&key, rrectToDraw, xformedSigma); |
Robert Phillips | 4cf00a8 | 2020-09-29 15:43:10 -0400 | [diff] [blame] | 230 | |
Robert Phillips | d464feb | 2020-10-08 11:00:02 -0400 | [diff] [blame] | 231 | auto threadSafeCache = rContext->priv().threadSafeCache(); |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 232 | |
Robert Phillips | 5bc0b65 | 2020-09-02 09:49:38 -0400 | [diff] [blame] | 233 | // It seems like we could omit this matrix and modify the shader code to not normalize |
| 234 | // the coords used to sample the texture effect. However, the "proxyDims" value in the |
| 235 | // shader is not always the actual the proxy dimensions. This is because 'dimensions' here |
| 236 | // was computed using integer corner radii as determined in |
| 237 | // SkComputeBlurredRRectParams whereas the shader code uses the float radius to compute |
| 238 | // 'proxyDims'. Why it draws correctly with these unequal values is a mystery for the ages. |
| 239 | auto m = SkMatrix::Scale(dimensions.width(), dimensions.height()); |
Robert Phillips | 5bc0b65 | 2020-09-02 09:49:38 -0400 | [diff] [blame] | 240 | |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 241 | GrSurfaceProxyView view; |
Robert Phillips | 5bc0b65 | 2020-09-02 09:49:38 -0400 | [diff] [blame] | 242 | |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 243 | if (GrDirectContext* dContext = rContext->asDirectContext()) { |
| 244 | // The gpu thread gets priority over the recording threads. If the gpu thread is first, |
| 245 | // it crams a lazy proxy into the cache and then fills it in later. |
Robert Phillips | d464feb | 2020-10-08 11:00:02 -0400 | [diff] [blame] | 246 | auto[lazyView, trampoline] = |
| 247 | GrThreadSafeCache::CreateLazyView(dContext, GrColorType::kAlpha_8, dimensions, |
| 248 | kBlurredRRectMaskOrigin, SkBackingFit::kExact); |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 249 | if (!lazyView) { |
| 250 | return nullptr; |
| 251 | } |
| 252 | |
Robert Phillips | d464feb | 2020-10-08 11:00:02 -0400 | [diff] [blame] | 253 | view = threadSafeCache->findOrAdd(key, lazyView); |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 254 | if (view != lazyView) { |
| 255 | SkASSERT(view.asTextureProxy()); |
| 256 | SkASSERT(view.origin() == kBlurredRRectMaskOrigin); |
| 257 | return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, m); |
| 258 | } |
| 259 | |
| 260 | if (!fillin_view_on_gpu(dContext, lazyView, std::move(trampoline), rrectToDraw, dimensions, |
| 261 | xformedSigma)) { |
| 262 | // In this case something has gone disastrously wrong so set up to drop the draw |
| 263 | // that needed this resource and reduce future pollution of the cache. |
Robert Phillips | d464feb | 2020-10-08 11:00:02 -0400 | [diff] [blame] | 264 | threadSafeCache->remove(key); |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 265 | return nullptr; |
| 266 | } |
Brian Osman | 55b69f5 | 2020-09-30 01:41:15 +0000 | [diff] [blame] | 267 | } else { |
Robert Phillips | d464feb | 2020-10-08 11:00:02 -0400 | [diff] [blame] | 268 | view = threadSafeCache->find(key); |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 269 | if (view) { |
| 270 | SkASSERT(view.asTextureProxy()); |
| 271 | SkASSERT(view.origin() == kBlurredRRectMaskOrigin); |
| 272 | return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, m); |
| 273 | } |
| 274 | |
| 275 | view = create_mask_on_cpu(rContext, rrectToDraw, dimensions, xformedSigma); |
| 276 | if (!view) { |
| 277 | return nullptr; |
| 278 | } |
| 279 | |
Robert Phillips | d464feb | 2020-10-08 11:00:02 -0400 | [diff] [blame] | 280 | view = threadSafeCache->add(key, view); |
Brian Osman | 55b69f5 | 2020-09-30 01:41:15 +0000 | [diff] [blame] | 281 | } |
| 282 | |
Robert Phillips | aac5728 | 2020-09-30 10:23:38 -0400 | [diff] [blame] | 283 | SkASSERT(view.asTextureProxy()); |
| 284 | SkASSERT(view.origin() == kBlurredRRectMaskOrigin); |
| 285 | return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, m); |
Robert Phillips | a1c4f1a | 2020-08-24 14:42:39 -0400 | [diff] [blame] | 286 | } |
| 287 | |
John Stiles | 4ca8884 | 2020-06-08 17:20:01 -0400 | [diff] [blame] | 288 | std::unique_ptr<GrFragmentProcessor> GrRRectBlurEffect::Make( |
| 289 | std::unique_ptr<GrFragmentProcessor> inputFP, |
| 290 | GrRecordingContext* context, |
| 291 | float sigma, |
| 292 | float xformedSigma, |
| 293 | const SkRRect& srcRRect, |
| 294 | const SkRRect& devRRect) { |
Brian Salomon | 8ff2449 | 2020-09-28 11:39:09 -0400 | [diff] [blame] | 295 | // Should've been caught up-stream |
| 296 | #ifdef SK_DEBUG |
| 297 | SkASSERTF(!SkRRectPriv::IsCircle(devRRect), "Unexpected circle. %d\n\t%s\n\t%s", |
| 298 | SkRRectPriv::IsCircle(srcRRect), srcRRect.dumpToString(true).c_str(), |
| 299 | devRRect.dumpToString(true).c_str()); |
| 300 | SkASSERTF(!devRRect.isRect(), "Unexpected rect. %d\n\t%s\n\t%s", srcRRect.isRect(), |
| 301 | srcRRect.dumpToString(true).c_str(), devRRect.dumpToString(true).c_str()); |
| 302 | #endif |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 303 | // TODO: loosen this up |
Mike Reed | 242135a | 2018-02-22 13:41:39 -0500 | [diff] [blame] | 304 | if (!SkRRectPriv::IsSimpleCircular(devRRect)) { |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 305 | return nullptr; |
| 306 | } |
| 307 | |
Michael Ludwig | e267464 | 2020-10-21 13:01:34 -0400 | [diff] [blame] | 308 | if (SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma)) { |
| 309 | return inputFP; |
| 310 | } |
| 311 | |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 312 | // Make sure we can successfully ninepatch this rrect -- the blur sigma has to be |
| 313 | // sufficiently small relative to both the size of the corner radius and the |
| 314 | // width (and height) of the rrect. |
Mike Klein | d6ab77a | 2019-03-21 08:18:24 -0500 | [diff] [blame] | 315 | SkRRect rrectToDraw; |
Brian Salomon | 9f2b86c | 2019-10-22 10:37:46 -0400 | [diff] [blame] | 316 | SkISize dimensions; |
Michael Ludwig | e267464 | 2020-10-21 13:01:34 -0400 | [diff] [blame] | 317 | SkScalar ignored[SkGpuBlurUtils::kBlurRRectMaxDivisions]; |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 318 | |
Michael Ludwig | e267464 | 2020-10-21 13:01:34 -0400 | [diff] [blame] | 319 | bool ninePatchable = SkGpuBlurUtils::ComputeBlurredRRectParams( |
| 320 | srcRRect, devRRect, sigma, xformedSigma, &rrectToDraw, &dimensions, ignored, ignored, |
| 321 | ignored, ignored); |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 322 | if (!ninePatchable) { |
| 323 | return nullptr; |
| 324 | } |
| 325 | |
Brian Salomon | 71f6cfd | 2020-06-17 17:14:12 -0400 | [diff] [blame] | 326 | std::unique_ptr<GrFragmentProcessor> maskFP = |
| 327 | find_or_create_rrect_blur_mask_fp(context, rrectToDraw, dimensions, xformedSigma); |
| 328 | if (!maskFP) { |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 329 | return nullptr; |
| 330 | } |
| 331 | |
Mike Reed | 242135a | 2018-02-22 13:41:39 -0500 | [diff] [blame] | 332 | return std::unique_ptr<GrFragmentProcessor>( |
John Stiles | 4ca8884 | 2020-06-08 17:20:01 -0400 | [diff] [blame] | 333 | new GrRRectBlurEffect(std::move(inputFP), xformedSigma, devRRect.getBounds(), |
Brian Salomon | 71f6cfd | 2020-06-17 17:14:12 -0400 | [diff] [blame] | 334 | SkRRectPriv::GetSimpleRadii(devRRect).fX, std::move(maskFP))); |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 335 | } |
John Stiles | 45f5b03 | 2020-07-27 17:31:29 -0400 | [diff] [blame] | 336 | #include "src/core/SkUtils.h" |
Greg Daniel | 456f9b5 | 2020-03-05 19:14:18 +0000 | [diff] [blame] | 337 | #include "src/gpu/GrTexture.h" |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 338 | #include "src/gpu/glsl/GrGLSLFragmentProcessor.h" |
| 339 | #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" |
| 340 | #include "src/gpu/glsl/GrGLSLProgramBuilder.h" |
| 341 | #include "src/sksl/SkSLCPP.h" |
| 342 | #include "src/sksl/SkSLUtil.h" |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 343 | class GrGLSLRRectBlurEffect : public GrGLSLFragmentProcessor { |
| 344 | public: |
| 345 | GrGLSLRRectBlurEffect() {} |
| 346 | void emitCode(EmitArgs& args) override { |
| 347 | GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
Mike Klein | d6ab77a | 2019-03-21 08:18:24 -0500 | [diff] [blame] | 348 | const GrRRectBlurEffect& _outer = args.fFp.cast<GrRRectBlurEffect>(); |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 349 | (void)_outer; |
Ethan Nicholas | bcd51e8 | 2019-04-09 10:40:41 -0400 | [diff] [blame] | 350 | auto sigma = _outer.sigma; |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 351 | (void)sigma; |
Ethan Nicholas | bcd51e8 | 2019-04-09 10:40:41 -0400 | [diff] [blame] | 352 | auto rect = _outer.rect; |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 353 | (void)rect; |
Ethan Nicholas | bcd51e8 | 2019-04-09 10:40:41 -0400 | [diff] [blame] | 354 | auto cornerRadius = _outer.cornerRadius; |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 355 | (void)cornerRadius; |
Ethan Nicholas | 16464c3 | 2020-04-06 13:53:05 -0400 | [diff] [blame] | 356 | cornerRadiusVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, |
| 357 | kHalf_GrSLType, "cornerRadius"); |
| 358 | proxyRectVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, |
| 359 | kFloat4_GrSLType, "proxyRect"); |
| 360 | blurRadiusVar = args.fUniformHandler->addUniform(&_outer, kFragment_GrShaderFlag, |
| 361 | kHalf_GrSLType, "blurRadius"); |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 362 | fragBuilder->codeAppendf( |
Brian Salomon | 5e961b0 | 2020-10-21 14:28:47 -0400 | [diff] [blame] | 363 | R"SkSL(float2 translatedFragPosFloat = sk_FragCoord.xy - %s.xy; |
| 364 | float2 proxyCenter = (%s.zw - %s.xy) * 0.5; |
John Stiles | 5081942 | 2020-06-18 13:00:38 -0400 | [diff] [blame] | 365 | half edgeSize = (2.0 * %s + %s) + 0.5; |
Brian Salomon | 5e961b0 | 2020-10-21 14:28:47 -0400 | [diff] [blame] | 366 | translatedFragPosFloat -= proxyCenter; |
| 367 | half2 fragDirection = half2(sign(translatedFragPosFloat)); |
| 368 | translatedFragPosFloat = abs(translatedFragPosFloat); |
| 369 | half2 translatedFragPosHalf = half2(translatedFragPosFloat - (proxyCenter - float(edgeSize))); |
| 370 | translatedFragPosHalf = max(translatedFragPosHalf, 0.0); |
| 371 | translatedFragPosHalf *= fragDirection; |
| 372 | translatedFragPosHalf += half2(edgeSize); |
John Stiles | 5081942 | 2020-06-18 13:00:38 -0400 | [diff] [blame] | 373 | half2 proxyDims = half2(2.0 * edgeSize); |
Brian Salomon | 5e961b0 | 2020-10-21 14:28:47 -0400 | [diff] [blame] | 374 | half2 texCoord = translatedFragPosHalf / proxyDims;)SkSL", |
Ethan Nicholas | bcd51e8 | 2019-04-09 10:40:41 -0400 | [diff] [blame] | 375 | args.fUniformHandler->getUniformCStr(proxyRectVar), |
John Stiles | a2d46a1 | 2020-06-11 14:12:44 -0400 | [diff] [blame] | 376 | args.fUniformHandler->getUniformCStr(proxyRectVar), |
| 377 | args.fUniformHandler->getUniformCStr(proxyRectVar), |
Ethan Nicholas | bcd51e8 | 2019-04-09 10:40:41 -0400 | [diff] [blame] | 378 | args.fUniformHandler->getUniformCStr(blurRadiusVar), |
John Stiles | a2d46a1 | 2020-06-11 14:12:44 -0400 | [diff] [blame] | 379 | args.fUniformHandler->getUniformCStr(cornerRadiusVar)); |
Brian Salomon | 5e961b0 | 2020-10-21 14:28:47 -0400 | [diff] [blame] | 380 | SkString _sample17574 = this->invokeChild(0, args); |
John Stiles | 5081942 | 2020-06-18 13:00:38 -0400 | [diff] [blame] | 381 | fragBuilder->codeAppendf( |
| 382 | R"SkSL( |
| 383 | half4 inputColor = %s;)SkSL", |
Brian Salomon | 5e961b0 | 2020-10-21 14:28:47 -0400 | [diff] [blame] | 384 | _sample17574.c_str()); |
| 385 | SkString _coords17622("float2(texCoord)"); |
| 386 | SkString _sample17622 = this->invokeChild(1, args, _coords17622.c_str()); |
John Stiles | 5081942 | 2020-06-18 13:00:38 -0400 | [diff] [blame] | 387 | fragBuilder->codeAppendf( |
| 388 | R"SkSL( |
| 389 | %s = inputColor * %s; |
| 390 | )SkSL", |
Brian Salomon | 5e961b0 | 2020-10-21 14:28:47 -0400 | [diff] [blame] | 391 | args.fOutputColor, _sample17622.c_str()); |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 392 | } |
| 393 | |
| 394 | private: |
| 395 | void onSetData(const GrGLSLProgramDataManager& pdman, |
Mike Klein | d6ab77a | 2019-03-21 08:18:24 -0500 | [diff] [blame] | 396 | const GrFragmentProcessor& _proc) override { |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 397 | const GrRRectBlurEffect& _outer = _proc.cast<GrRRectBlurEffect>(); |
Ethan Nicholas | bcd51e8 | 2019-04-09 10:40:41 -0400 | [diff] [blame] | 398 | { pdman.set1f(cornerRadiusVar, (_outer.cornerRadius)); } |
| 399 | auto sigma = _outer.sigma; |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 400 | (void)sigma; |
Ethan Nicholas | bcd51e8 | 2019-04-09 10:40:41 -0400 | [diff] [blame] | 401 | auto rect = _outer.rect; |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 402 | (void)rect; |
Ethan Nicholas | bcd51e8 | 2019-04-09 10:40:41 -0400 | [diff] [blame] | 403 | UniformHandle& cornerRadius = cornerRadiusVar; |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 404 | (void)cornerRadius; |
Ethan Nicholas | bcd51e8 | 2019-04-09 10:40:41 -0400 | [diff] [blame] | 405 | UniformHandle& proxyRect = proxyRectVar; |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 406 | (void)proxyRect; |
Ethan Nicholas | bcd51e8 | 2019-04-09 10:40:41 -0400 | [diff] [blame] | 407 | UniformHandle& blurRadius = blurRadiusVar; |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 408 | (void)blurRadius; |
| 409 | |
| 410 | float blurRadiusValue = 3.f * SkScalarCeilToScalar(sigma - 1 / 6.0f); |
| 411 | pdman.set1f(blurRadius, blurRadiusValue); |
| 412 | |
| 413 | SkRect outset = rect; |
| 414 | outset.outset(blurRadiusValue, blurRadiusValue); |
| 415 | pdman.set4f(proxyRect, outset.fLeft, outset.fTop, outset.fRight, outset.fBottom); |
| 416 | } |
Ethan Nicholas | bcd51e8 | 2019-04-09 10:40:41 -0400 | [diff] [blame] | 417 | UniformHandle proxyRectVar; |
| 418 | UniformHandle blurRadiusVar; |
| 419 | UniformHandle cornerRadiusVar; |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 420 | }; |
| 421 | GrGLSLFragmentProcessor* GrRRectBlurEffect::onCreateGLSLInstance() const { |
| 422 | return new GrGLSLRRectBlurEffect(); |
| 423 | } |
Mike Klein | d6ab77a | 2019-03-21 08:18:24 -0500 | [diff] [blame] | 424 | void GrRRectBlurEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 425 | GrProcessorKeyBuilder* b) const {} |
| 426 | bool GrRRectBlurEffect::onIsEqual(const GrFragmentProcessor& other) const { |
| 427 | const GrRRectBlurEffect& that = other.cast<GrRRectBlurEffect>(); |
| 428 | (void)that; |
Ethan Nicholas | bcd51e8 | 2019-04-09 10:40:41 -0400 | [diff] [blame] | 429 | if (sigma != that.sigma) return false; |
| 430 | if (rect != that.rect) return false; |
| 431 | if (cornerRadius != that.cornerRadius) return false; |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 432 | return true; |
| 433 | } |
John Stiles | 735a5a7 | 2020-08-26 10:21:10 -0400 | [diff] [blame] | 434 | bool GrRRectBlurEffect::usesExplicitReturn() const { return false; } |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 435 | GrRRectBlurEffect::GrRRectBlurEffect(const GrRRectBlurEffect& src) |
| 436 | : INHERITED(kGrRRectBlurEffect_ClassID, src.optimizationFlags()) |
Ethan Nicholas | bcd51e8 | 2019-04-09 10:40:41 -0400 | [diff] [blame] | 437 | , sigma(src.sigma) |
| 438 | , rect(src.rect) |
Brian Salomon | 71f6cfd | 2020-06-17 17:14:12 -0400 | [diff] [blame] | 439 | , cornerRadius(src.cornerRadius) { |
Brian Osman | 12c5d29 | 2020-07-13 16:11:35 -0400 | [diff] [blame] | 440 | this->cloneAndRegisterAllChildProcessors(src); |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 441 | } |
| 442 | std::unique_ptr<GrFragmentProcessor> GrRRectBlurEffect::clone() const { |
John Stiles | fbd050b | 2020-08-03 13:21:46 -0400 | [diff] [blame] | 443 | return std::make_unique<GrRRectBlurEffect>(*this); |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 444 | } |
John Stiles | 8d9bf64 | 2020-08-12 15:07:45 -0400 | [diff] [blame] | 445 | #if GR_TEST_UTILS |
John Stiles | cab5886 | 2020-08-12 15:47:06 -0400 | [diff] [blame] | 446 | SkString GrRRectBlurEffect::onDumpInfo() const { |
| 447 | return SkStringPrintf("(sigma=%f, rect=float4(%f, %f, %f, %f), cornerRadius=%f)", sigma, |
| 448 | rect.left(), rect.top(), rect.right(), rect.bottom(), cornerRadius); |
John Stiles | 47b4e22 | 2020-08-12 09:56:50 -0400 | [diff] [blame] | 449 | } |
| 450 | #endif |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 451 | GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRRectBlurEffect); |
| 452 | #if GR_TEST_UTILS |
| 453 | std::unique_ptr<GrFragmentProcessor> GrRRectBlurEffect::TestCreate(GrProcessorTestData* d) { |
Mike Klein | d6ab77a | 2019-03-21 08:18:24 -0500 | [diff] [blame] | 454 | SkScalar w = d->fRandom->nextRangeScalar(100.f, 1000.f); |
| 455 | SkScalar h = d->fRandom->nextRangeScalar(100.f, 1000.f); |
| 456 | SkScalar r = d->fRandom->nextRangeF(1.f, 9.f); |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 457 | SkScalar sigma = d->fRandom->nextRangeF(1.f, 10.f); |
Mike Klein | d6ab77a | 2019-03-21 08:18:24 -0500 | [diff] [blame] | 458 | SkRRect rrect; |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 459 | rrect.setRectXY(SkRect::MakeWH(w, h), r, r); |
John Stiles | 6609cb6 | 2020-07-17 14:52:12 -0400 | [diff] [blame] | 460 | return GrRRectBlurEffect::Make(d->inputFP(), d->context(), sigma, sigma, rrect, rrect); |
Ethan Nicholas | 297d6ef | 2017-12-20 12:00:11 -0500 | [diff] [blame] | 461 | } |
| 462 | #endif |