1111#include " GrRRectBlurEffect.h"
1212
1313#include " include/gpu/GrRecordingContext.h"
14+ #include " src/core/SkAutoMalloc.h"
1415#include " src/core/SkBlurPriv.h"
1516#include " src/core/SkGpuBlurUtils.h"
1617#include " src/core/SkRRectPriv.h"
@@ -83,6 +84,137 @@ static GrSurfaceProxyView create_mask_on_gpu(GrRecordingContext* context,
8384 return rtc2->readSurfaceView ();
8485}
8586
87+ // TODO: merge w/ copy in SkGpuBlurUtils.cpp
88+ static int sigma_radius (float sigma) {
89+ SkASSERT (sigma >= 0 );
90+ return static_cast <int >(ceilf (sigma * 3 .0f ));
91+ }
92+
93+ // Evaluate the vertical blur at the specified 'y' value given the location of the top of the
94+ // rrect.
95+ static uint8_t eval_V (float top, int y, const uint8_t * integral, int integralSize, float sixSigma) {
96+ if (top < 0 ) {
97+ return 0 ; // an empty column
98+ }
99+
100+ float fT = (top - y - 0 .5f ) * (integralSize / sixSigma);
101+ if (fT < 0 ) {
102+ return 255 ;
103+ } else if (fT >= integralSize - 1 ) {
104+ return 0 ;
105+ }
106+
107+ int lower = (int )fT ;
108+ float frac = fT - lower;
109+
110+ SkASSERT (lower + 1 < integralSize);
111+
112+ return integral[lower] * (1 .0f - frac) + integral[lower + 1 ] * frac;
113+ }
114+
115+ // Apply a gaussian 'kernel' horizontally at the specified 'x', 'y' location.
116+ static uint8_t eval_H (int x,
117+ int y,
118+ const std::vector<float >& topVec,
119+ const float * kernel,
120+ int kernelSize,
121+ const uint8_t * integral,
122+ int integralSize,
123+ float sixSigma) {
124+ SkASSERT (0 <= x && x < (int )topVec.size ());
125+ SkASSERT (kernelSize % 2 );
126+
127+ float accum = 0 .0f ;
128+
129+ int xSampleLoc = x - (kernelSize / 2 );
130+ for (int i = 0 ; i < kernelSize; ++i, ++xSampleLoc) {
131+ if (xSampleLoc < 0 || xSampleLoc >= (int )topVec.size ()) {
132+ continue ;
133+ }
134+
135+ accum += kernel[i] * eval_V (topVec[xSampleLoc], y, integral, integralSize, sixSigma);
136+ }
137+
138+ return accum + 0 .5f ;
139+ }
140+
141+ // Create a cpu-side blurred-rrect mask that is close to the version the gpu would've produced.
142+ // The match needs to be close bc the cpu- and gpu-generated version must be interchangeable.
143+ static GrSurfaceProxyView create_mask_on_cpu (GrRecordingContext* context,
144+ const SkRRect& rrectToDraw,
145+ const SkISize& dimensions,
146+ float xformedSigma) {
147+ int radius = sigma_radius (xformedSigma);
148+ int kernelSize = 2 * radius + 1 ;
149+
150+ SkASSERT (kernelSize % 2 );
151+ SkASSERT (dimensions.width () % 2 );
152+ SkASSERT (dimensions.height () % 2 );
153+
154+ SkVector radii = rrectToDraw.getSimpleRadii ();
155+ SkASSERT (SkScalarNearlyEqual (radii.fX , radii.fY ));
156+
157+ const int halfWidthPlus1 = (dimensions.width () / 2 ) + 1 ;
158+ const int halfHeightPlus1 = (dimensions.height () / 2 ) + 1 ;
159+
160+ std::unique_ptr<float []> kernel (new float [kernelSize]);
161+
162+ SkFillIn1DGaussianKernel (kernel.get (), xformedSigma, radius);
163+
164+ SkBitmap integral;
165+ if (!SkCreateIntegralTable (6 * xformedSigma, &integral)) {
166+ return {};
167+ }
168+
169+ SkBitmap result;
170+ if (!result.tryAllocPixels (SkImageInfo::MakeA8 (dimensions.width (), dimensions.height ()))) {
171+ return {};
172+ }
173+
174+ std::vector<float > topVec;
175+ topVec.reserve (dimensions.width ());
176+ for (int x = 0 ; x < dimensions.width (); ++x) {
177+ if (x < rrectToDraw.rect ().fLeft || x > rrectToDraw.rect ().fRight ) {
178+ topVec.push_back (-1 );
179+ } else {
180+ if (x + 0 .5f < rrectToDraw.rect ().fLeft + radii.fX ) { // in the circular section
181+ float xDist = rrectToDraw.rect ().fLeft + radii.fX - x - 0 .5f ;
182+ float h = sqrtf (radii.fX * radii.fX - xDist * xDist);
183+ SkASSERT (0 <= h && h < radii.fY );
184+ topVec.push_back (rrectToDraw.rect ().fTop + radii.fX - h + 3 * xformedSigma);
185+ } else {
186+ topVec.push_back (rrectToDraw.rect ().fTop + 3 * xformedSigma);
187+ }
188+ }
189+ }
190+
191+ for (int y = 0 ; y < halfHeightPlus1; ++y) {
192+ uint8_t * scanline = result.getAddr8 (0 , y);
193+
194+ for (int x = 0 ; x < halfWidthPlus1; ++x) {
195+ scanline[x] = eval_H (x, y, topVec, kernel.get (), kernelSize, integral.getAddr8 (0 , 0 ),
196+ integral.width (), 6 * xformedSigma);
197+ scanline[dimensions.width () - x - 1 ] = scanline[x];
198+ }
199+
200+ memcpy (result.getAddr8 (0 , dimensions.height () - y - 1 ), scanline, result.rowBytes ());
201+ }
202+
203+ result.setImmutable ();
204+
205+ GrProxyProvider* proxyProvider = context->priv ().proxyProvider ();
206+
207+ sk_sp<GrTextureProxy> proxy = proxyProvider->createProxyFromBitmap (
208+ result, GrMipmapped::kNo , SkBackingFit::kExact , SkBudgeted::kYes );
209+ if (!proxy) {
210+ return {};
211+ }
212+
213+ GrSwizzle swizzle =
214+ context->priv ().caps ()->getReadSwizzle (proxy->backendFormat (), GrColorType::kAlpha_8 );
215+ return {std::move (proxy), kBlurredRRectMaskOrigin , swizzle};
216+ }
217+
86218static std::unique_ptr<GrFragmentProcessor> find_or_create_rrect_blur_mask_fp (
87219 GrRecordingContext* context,
88220 const SkRRect& rrectToDraw,
@@ -106,7 +238,12 @@ static std::unique_ptr<GrFragmentProcessor> find_or_create_rrect_blur_mask_fp(
106238 return GrTextureEffect::Make (std::move (view), kPremul_SkAlphaType , m);
107239 }
108240
109- auto mask = create_mask_on_gpu (context, rrectToDraw, dimensions, xformedSigma);
241+ GrSurfaceProxyView mask;
242+ if (proxyProvider->isDDLProvider () == GrDDLProvider::kNo ) {
243+ mask = create_mask_on_gpu (context, rrectToDraw, dimensions, xformedSigma);
244+ } else {
245+ mask = create_mask_on_cpu (context, rrectToDraw, dimensions, xformedSigma);
246+ }
110247 if (!mask) {
111248 return nullptr ;
112249 }
@@ -200,18 +337,18 @@ half2 texCoord = translatedFragPos / proxyDims;)SkSL",
200337 args.fUniformHandler ->getUniformCStr (proxyRectVar),
201338 args.fUniformHandler ->getUniformCStr (blurRadiusVar),
202339 args.fUniformHandler ->getUniformCStr (cornerRadiusVar));
203- SkString _sample10076 = this ->invokeChild (0 , args);
340+ SkString _sample15531 = this ->invokeChild (0 , args);
204341 fragBuilder->codeAppendf (
205342 R"SkSL(
206343half4 inputColor = %s;)SkSL" ,
207- _sample10076 .c_str ());
208- SkString _coords10124 (" float2(texCoord)" );
209- SkString _sample10124 = this ->invokeChild (1 , args, _coords10124 .c_str ());
344+ _sample15531 .c_str ());
345+ SkString _coords15579 (" float2(texCoord)" );
346+ SkString _sample15579 = this ->invokeChild (1 , args, _coords15579 .c_str ());
210347 fragBuilder->codeAppendf (
211348 R"SkSL(
212349%s = inputColor * %s;
213350)SkSL" ,
214- args.fOutputColor , _sample10124 .c_str ());
351+ args.fOutputColor , _sample15579 .c_str ());
215352 }
216353
217354private:
0 commit comments