xref: /aosp_15_r20/external/skia/src/gpu/ganesh/GrBlurUtils.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2015 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrBlurUtils.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkAlphaType.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBlendMode.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBlurTypes.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkM44.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRRect.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRegion.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSamplingOptions.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStrokeRec.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurfaceProps.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTileMode.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkRuntimeEffect.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/GpuTypes.h"
38*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrDirectContext.h"
39*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrRecordingContext.h"
40*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrTypes.h"
41*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkColorData.h"
42*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
43*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkFixed.h"
44*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkFloatingPoint.h"
45*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMath.h"
46*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTemplates.h"
47*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/gpu/ganesh/GrTypesPriv.h"
48*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkFloatBits.h"
49*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkTLazy.h"
50*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkBlurMaskFilterImpl.h"
51*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkDraw.h"
52*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkMask.h"
53*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkMaskFilterBase.h"
54*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRRectPriv.h"
55*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRuntimeEffectPriv.h"
56*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTraceEvent.h"
57*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/BlurUtils.h"
58*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ResourceKey.h"
59*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/SkBackingFit.h"
60*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/Swizzle.h"
61*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrCaps.h"
62*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrClip.h"
63*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrColorInfo.h"
64*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrColorSpaceXform.h"
65*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrDirectContextPriv.h"
66*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrFixedClip.h"
67*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrFragmentProcessor.h"
68*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrFragmentProcessors.h"
69*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrPaint.h"
70*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrRecordingContextPriv.h"
71*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSamplerState.h"
72*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrShaderCaps.h"
73*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrStyle.h"
74*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSurfaceProxy.h"
75*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSurfaceProxyView.h"
76*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrTextureProxy.h"
77*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrThreadSafeCache.h"
78*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrUtil.h"
79*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/SkGr.h"
80*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/SurfaceContext.h"
81*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/SurfaceDrawContext.h"
82*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/SurfaceFillContext.h"
83*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrBlendFragmentProcessor.h"
84*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrMatrixEffect.h"
85*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrSkSLFP.h"
86*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrTextureEffect.h"
87*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/geometry/GrStyledShape.h"
88*c8dee2aaSAndroid Build Coastguard Worker 
89*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
90*c8dee2aaSAndroid Build Coastguard Worker #include <array>
91*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
92*c8dee2aaSAndroid Build Coastguard Worker #include <initializer_list>
93*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
94*c8dee2aaSAndroid Build Coastguard Worker #include <tuple>
95*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
96*c8dee2aaSAndroid Build Coastguard Worker 
97*c8dee2aaSAndroid Build Coastguard Worker namespace GrBlurUtils {
98*c8dee2aaSAndroid Build Coastguard Worker 
clip_bounds_quick_reject(const SkIRect & clipBounds,const SkIRect & rect)99*c8dee2aaSAndroid Build Coastguard Worker static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& rect) {
100*c8dee2aaSAndroid Build Coastguard Worker     return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBounds, rect);
101*c8dee2aaSAndroid Build Coastguard Worker }
102*c8dee2aaSAndroid Build Coastguard Worker 
103*c8dee2aaSAndroid Build Coastguard Worker static constexpr auto kMaskOrigin = kTopLeft_GrSurfaceOrigin;
104*c8dee2aaSAndroid Build Coastguard Worker 
105*c8dee2aaSAndroid Build Coastguard Worker // Draw a mask using the supplied paint. Since the coverage/geometry
106*c8dee2aaSAndroid Build Coastguard Worker // is already burnt into the mask this boils down to a rect draw.
107*c8dee2aaSAndroid Build Coastguard Worker // Return true if the mask was successfully drawn.
draw_mask(skgpu::ganesh::SurfaceDrawContext * sdc,const GrClip * clip,const SkMatrix & viewMatrix,const SkIRect & maskBounds,GrPaint && paint,GrSurfaceProxyView mask)108*c8dee2aaSAndroid Build Coastguard Worker static bool draw_mask(skgpu::ganesh::SurfaceDrawContext* sdc,
109*c8dee2aaSAndroid Build Coastguard Worker                       const GrClip* clip,
110*c8dee2aaSAndroid Build Coastguard Worker                       const SkMatrix& viewMatrix,
111*c8dee2aaSAndroid Build Coastguard Worker                       const SkIRect& maskBounds,
112*c8dee2aaSAndroid Build Coastguard Worker                       GrPaint&& paint,
113*c8dee2aaSAndroid Build Coastguard Worker                       GrSurfaceProxyView mask) {
114*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix inverse;
115*c8dee2aaSAndroid Build Coastguard Worker     if (!viewMatrix.invert(&inverse)) {
116*c8dee2aaSAndroid Build Coastguard Worker         return false;
117*c8dee2aaSAndroid Build Coastguard Worker     }
118*c8dee2aaSAndroid Build Coastguard Worker 
119*c8dee2aaSAndroid Build Coastguard Worker     mask.concatSwizzle(skgpu::Swizzle("aaaa"));
120*c8dee2aaSAndroid Build Coastguard Worker 
121*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix matrix = SkMatrix::Translate(-SkIntToScalar(maskBounds.fLeft),
122*c8dee2aaSAndroid Build Coastguard Worker                                           -SkIntToScalar(maskBounds.fTop));
123*c8dee2aaSAndroid Build Coastguard Worker     matrix.preConcat(viewMatrix);
124*c8dee2aaSAndroid Build Coastguard Worker     paint.setCoverageFragmentProcessor(
125*c8dee2aaSAndroid Build Coastguard Worker             GrTextureEffect::Make(std::move(mask), kUnknown_SkAlphaType, matrix));
126*c8dee2aaSAndroid Build Coastguard Worker 
127*c8dee2aaSAndroid Build Coastguard Worker     sdc->fillPixelsWithLocalMatrix(clip, std::move(paint), maskBounds, inverse);
128*c8dee2aaSAndroid Build Coastguard Worker     return true;
129*c8dee2aaSAndroid Build Coastguard Worker }
130*c8dee2aaSAndroid Build Coastguard Worker 
mask_release_proc(void * addr,void *)131*c8dee2aaSAndroid Build Coastguard Worker static void mask_release_proc(void* addr, void* /*context*/) {
132*c8dee2aaSAndroid Build Coastguard Worker     SkMaskBuilder::FreeImage(addr);
133*c8dee2aaSAndroid Build Coastguard Worker }
134*c8dee2aaSAndroid Build Coastguard Worker 
135*c8dee2aaSAndroid Build Coastguard Worker // This stores the mapping from an unclipped, integerized, device-space, shape bounds to
136*c8dee2aaSAndroid Build Coastguard Worker // the filtered mask's draw rect.
137*c8dee2aaSAndroid Build Coastguard Worker struct DrawRectData {
138*c8dee2aaSAndroid Build Coastguard Worker     SkIVector fOffset;
139*c8dee2aaSAndroid Build Coastguard Worker     SkISize   fSize;
140*c8dee2aaSAndroid Build Coastguard Worker };
141*c8dee2aaSAndroid Build Coastguard Worker 
create_data(const SkIRect & drawRect,const SkIRect & origDevBounds)142*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkData> create_data(const SkIRect& drawRect, const SkIRect& origDevBounds) {
143*c8dee2aaSAndroid Build Coastguard Worker 
144*c8dee2aaSAndroid Build Coastguard Worker     DrawRectData drawRectData { {drawRect.fLeft - origDevBounds.fLeft,
145*c8dee2aaSAndroid Build Coastguard Worker                                  drawRect.fTop - origDevBounds.fTop},
146*c8dee2aaSAndroid Build Coastguard Worker                                 drawRect.size() };
147*c8dee2aaSAndroid Build Coastguard Worker 
148*c8dee2aaSAndroid Build Coastguard Worker     return SkData::MakeWithCopy(&drawRectData, sizeof(drawRectData));
149*c8dee2aaSAndroid Build Coastguard Worker }
150*c8dee2aaSAndroid Build Coastguard Worker 
extract_draw_rect_from_data(SkData * data,const SkIRect & origDevBounds)151*c8dee2aaSAndroid Build Coastguard Worker static SkIRect extract_draw_rect_from_data(SkData* data, const SkIRect& origDevBounds) {
152*c8dee2aaSAndroid Build Coastguard Worker     auto drawRectData = static_cast<const DrawRectData*>(data->data());
153*c8dee2aaSAndroid Build Coastguard Worker 
154*c8dee2aaSAndroid Build Coastguard Worker     return SkIRect::MakeXYWH(origDevBounds.fLeft + drawRectData->fOffset.fX,
155*c8dee2aaSAndroid Build Coastguard Worker                              origDevBounds.fTop + drawRectData->fOffset.fY,
156*c8dee2aaSAndroid Build Coastguard Worker                              drawRectData->fSize.fWidth,
157*c8dee2aaSAndroid Build Coastguard Worker                              drawRectData->fSize.fHeight);
158*c8dee2aaSAndroid Build Coastguard Worker }
159*c8dee2aaSAndroid Build Coastguard Worker 
sw_create_filtered_mask(GrRecordingContext * rContext,const SkMatrix & viewMatrix,const GrStyledShape & shape,const SkMaskFilter * filter,const SkIRect & unclippedDevShapeBounds,const SkIRect & clipBounds,SkIRect * drawRect,skgpu::UniqueKey * key)160*c8dee2aaSAndroid Build Coastguard Worker static GrSurfaceProxyView sw_create_filtered_mask(GrRecordingContext* rContext,
161*c8dee2aaSAndroid Build Coastguard Worker                                                   const SkMatrix& viewMatrix,
162*c8dee2aaSAndroid Build Coastguard Worker                                                   const GrStyledShape& shape,
163*c8dee2aaSAndroid Build Coastguard Worker                                                   const SkMaskFilter* filter,
164*c8dee2aaSAndroid Build Coastguard Worker                                                   const SkIRect& unclippedDevShapeBounds,
165*c8dee2aaSAndroid Build Coastguard Worker                                                   const SkIRect& clipBounds,
166*c8dee2aaSAndroid Build Coastguard Worker                                                   SkIRect* drawRect,
167*c8dee2aaSAndroid Build Coastguard Worker                                                   skgpu::UniqueKey* key) {
168*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(filter);
169*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!shape.style().applies());
170*c8dee2aaSAndroid Build Coastguard Worker 
171*c8dee2aaSAndroid Build Coastguard Worker     auto threadSafeCache = rContext->priv().threadSafeCache();
172*c8dee2aaSAndroid Build Coastguard Worker 
173*c8dee2aaSAndroid Build Coastguard Worker     GrSurfaceProxyView filteredMaskView;
174*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> data;
175*c8dee2aaSAndroid Build Coastguard Worker 
176*c8dee2aaSAndroid Build Coastguard Worker     if (key->isValid()) {
177*c8dee2aaSAndroid Build Coastguard Worker         std::tie(filteredMaskView, data) = threadSafeCache->findWithData(*key);
178*c8dee2aaSAndroid Build Coastguard Worker     }
179*c8dee2aaSAndroid Build Coastguard Worker 
180*c8dee2aaSAndroid Build Coastguard Worker     if (filteredMaskView) {
181*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(data);
182*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(kMaskOrigin == filteredMaskView.origin());
183*c8dee2aaSAndroid Build Coastguard Worker 
184*c8dee2aaSAndroid Build Coastguard Worker         *drawRect = extract_draw_rect_from_data(data.get(), unclippedDevShapeBounds);
185*c8dee2aaSAndroid Build Coastguard Worker     } else {
186*c8dee2aaSAndroid Build Coastguard Worker         SkStrokeRec::InitStyle fillOrHairline = shape.style().isSimpleHairline()
187*c8dee2aaSAndroid Build Coastguard Worker                                                         ? SkStrokeRec::kHairline_InitStyle
188*c8dee2aaSAndroid Build Coastguard Worker                                                         : SkStrokeRec::kFill_InitStyle;
189*c8dee2aaSAndroid Build Coastguard Worker 
190*c8dee2aaSAndroid Build Coastguard Worker         // TODO: it seems like we could create an SkDraw here and set its fMatrix field rather
191*c8dee2aaSAndroid Build Coastguard Worker         // than explicitly transforming the path to device space.
192*c8dee2aaSAndroid Build Coastguard Worker         SkPath devPath;
193*c8dee2aaSAndroid Build Coastguard Worker 
194*c8dee2aaSAndroid Build Coastguard Worker         shape.asPath(&devPath);
195*c8dee2aaSAndroid Build Coastguard Worker 
196*c8dee2aaSAndroid Build Coastguard Worker         devPath.transform(viewMatrix);
197*c8dee2aaSAndroid Build Coastguard Worker 
198*c8dee2aaSAndroid Build Coastguard Worker         SkMaskBuilder srcM, dstM;
199*c8dee2aaSAndroid Build Coastguard Worker         if (!SkDraw::DrawToMask(devPath, clipBounds, filter, &viewMatrix, &srcM,
200*c8dee2aaSAndroid Build Coastguard Worker                                 SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode,
201*c8dee2aaSAndroid Build Coastguard Worker                                 fillOrHairline)) {
202*c8dee2aaSAndroid Build Coastguard Worker             return {};
203*c8dee2aaSAndroid Build Coastguard Worker         }
204*c8dee2aaSAndroid Build Coastguard Worker         SkAutoMaskFreeImage autoSrc(srcM.image());
205*c8dee2aaSAndroid Build Coastguard Worker 
206*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(SkMask::kA8_Format == srcM.fFormat);
207*c8dee2aaSAndroid Build Coastguard Worker 
208*c8dee2aaSAndroid Build Coastguard Worker         if (!as_MFB(filter)->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
209*c8dee2aaSAndroid Build Coastguard Worker             return {};
210*c8dee2aaSAndroid Build Coastguard Worker         }
211*c8dee2aaSAndroid Build Coastguard Worker         // this will free-up dstM when we're done (allocated in filterMask())
212*c8dee2aaSAndroid Build Coastguard Worker         SkAutoMaskFreeImage autoDst(dstM.image());
213*c8dee2aaSAndroid Build Coastguard Worker 
214*c8dee2aaSAndroid Build Coastguard Worker         if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) {
215*c8dee2aaSAndroid Build Coastguard Worker             return {};
216*c8dee2aaSAndroid Build Coastguard Worker         }
217*c8dee2aaSAndroid Build Coastguard Worker 
218*c8dee2aaSAndroid Build Coastguard Worker         // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
219*c8dee2aaSAndroid Build Coastguard Worker         // the current clip (and identity matrix) and GrPaint settings
220*c8dee2aaSAndroid Build Coastguard Worker         SkBitmap bm;
221*c8dee2aaSAndroid Build Coastguard Worker         if (!bm.installPixels(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()),
222*c8dee2aaSAndroid Build Coastguard Worker                               autoDst.release(), dstM.fRowBytes, mask_release_proc, nullptr)) {
223*c8dee2aaSAndroid Build Coastguard Worker             return {};
224*c8dee2aaSAndroid Build Coastguard Worker         }
225*c8dee2aaSAndroid Build Coastguard Worker         bm.setImmutable();
226*c8dee2aaSAndroid Build Coastguard Worker 
227*c8dee2aaSAndroid Build Coastguard Worker         std::tie(filteredMaskView, std::ignore) = GrMakeUncachedBitmapProxyView(
228*c8dee2aaSAndroid Build Coastguard Worker                 rContext, bm, skgpu::Mipmapped::kNo, SkBackingFit::kApprox);
229*c8dee2aaSAndroid Build Coastguard Worker         if (!filteredMaskView) {
230*c8dee2aaSAndroid Build Coastguard Worker             return {};
231*c8dee2aaSAndroid Build Coastguard Worker         }
232*c8dee2aaSAndroid Build Coastguard Worker 
233*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(kMaskOrigin == filteredMaskView.origin());
234*c8dee2aaSAndroid Build Coastguard Worker 
235*c8dee2aaSAndroid Build Coastguard Worker         *drawRect = dstM.fBounds;
236*c8dee2aaSAndroid Build Coastguard Worker 
237*c8dee2aaSAndroid Build Coastguard Worker         if (key->isValid()) {
238*c8dee2aaSAndroid Build Coastguard Worker             key->setCustomData(create_data(*drawRect, unclippedDevShapeBounds));
239*c8dee2aaSAndroid Build Coastguard Worker             std::tie(filteredMaskView, data) = threadSafeCache->addWithData(*key, filteredMaskView);
240*c8dee2aaSAndroid Build Coastguard Worker             // If we got a different view back from 'addWithData' it could have a different drawRect
241*c8dee2aaSAndroid Build Coastguard Worker             *drawRect = extract_draw_rect_from_data(data.get(), unclippedDevShapeBounds);
242*c8dee2aaSAndroid Build Coastguard Worker         }
243*c8dee2aaSAndroid Build Coastguard Worker     }
244*c8dee2aaSAndroid Build Coastguard Worker 
245*c8dee2aaSAndroid Build Coastguard Worker     return filteredMaskView;
246*c8dee2aaSAndroid Build Coastguard Worker }
247*c8dee2aaSAndroid Build Coastguard Worker 
248*c8dee2aaSAndroid Build Coastguard Worker // Create a mask of 'shape' and return the resulting surfaceDrawContext
create_mask_GPU(GrRecordingContext * rContext,const SkIRect & maskRect,const SkMatrix & origViewMatrix,const GrStyledShape & shape,int sampleCnt)249*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<skgpu::ganesh::SurfaceDrawContext> create_mask_GPU(
250*c8dee2aaSAndroid Build Coastguard Worker         GrRecordingContext* rContext,
251*c8dee2aaSAndroid Build Coastguard Worker         const SkIRect& maskRect,
252*c8dee2aaSAndroid Build Coastguard Worker         const SkMatrix& origViewMatrix,
253*c8dee2aaSAndroid Build Coastguard Worker         const GrStyledShape& shape,
254*c8dee2aaSAndroid Build Coastguard Worker         int sampleCnt) {
255*c8dee2aaSAndroid Build Coastguard Worker     // We cache blur masks. Use default surface props here so we can use the same cached mask
256*c8dee2aaSAndroid Build Coastguard Worker     // regardless of the final dst surface.
257*c8dee2aaSAndroid Build Coastguard Worker     SkSurfaceProps defaultSurfaceProps;
258*c8dee2aaSAndroid Build Coastguard Worker 
259*c8dee2aaSAndroid Build Coastguard Worker     // Use GetApproxSize to implement our own approximate size matching, but demand
260*c8dee2aaSAndroid Build Coastguard Worker     // a "SkBackingFit::kExact" size match on the actual render target. We do this because the
261*c8dee2aaSAndroid Build Coastguard Worker     // filter will reach outside the src bounds, so we need to pre-clear these values to ensure a
262*c8dee2aaSAndroid Build Coastguard Worker     // "decal" sampling effect (i.e., ensure reads outside the src bounds return alpha=0).
263*c8dee2aaSAndroid Build Coastguard Worker     //
264*c8dee2aaSAndroid Build Coastguard Worker     // FIXME: Reads outside the left and top edges will actually clamp to the edge pixel. And in the
265*c8dee2aaSAndroid Build Coastguard Worker     // event that GetApproxSize does not change the size, reads outside the right and/or bottom will
266*c8dee2aaSAndroid Build Coastguard Worker     // do the same. We should offset our filter within the render target and expand the size as
267*c8dee2aaSAndroid Build Coastguard Worker     // needed to guarantee at least 1px of padding on all sides.
268*c8dee2aaSAndroid Build Coastguard Worker     auto approxSize = skgpu::GetApproxSize(maskRect.size());
269*c8dee2aaSAndroid Build Coastguard Worker     auto sdc = skgpu::ganesh::SurfaceDrawContext::MakeWithFallback(rContext,
270*c8dee2aaSAndroid Build Coastguard Worker                                                                    GrColorType::kAlpha_8,
271*c8dee2aaSAndroid Build Coastguard Worker                                                                    nullptr,
272*c8dee2aaSAndroid Build Coastguard Worker                                                                    SkBackingFit::kExact,
273*c8dee2aaSAndroid Build Coastguard Worker                                                                    approxSize,
274*c8dee2aaSAndroid Build Coastguard Worker                                                                    defaultSurfaceProps,
275*c8dee2aaSAndroid Build Coastguard Worker                                                                    sampleCnt,
276*c8dee2aaSAndroid Build Coastguard Worker                                                                    skgpu::Mipmapped::kNo,
277*c8dee2aaSAndroid Build Coastguard Worker                                                                    GrProtected::kNo,
278*c8dee2aaSAndroid Build Coastguard Worker                                                                    kMaskOrigin);
279*c8dee2aaSAndroid Build Coastguard Worker     if (!sdc) {
280*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
281*c8dee2aaSAndroid Build Coastguard Worker     }
282*c8dee2aaSAndroid Build Coastguard Worker 
283*c8dee2aaSAndroid Build Coastguard Worker     sdc->clear(SK_PMColor4fTRANSPARENT);
284*c8dee2aaSAndroid Build Coastguard Worker 
285*c8dee2aaSAndroid Build Coastguard Worker     GrPaint maskPaint;
286*c8dee2aaSAndroid Build Coastguard Worker     maskPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
287*c8dee2aaSAndroid Build Coastguard Worker 
288*c8dee2aaSAndroid Build Coastguard Worker     // setup new clip
289*c8dee2aaSAndroid Build Coastguard Worker     GrFixedClip clip(sdc->dimensions(), SkIRect::MakeWH(maskRect.width(), maskRect.height()));
290*c8dee2aaSAndroid Build Coastguard Worker 
291*c8dee2aaSAndroid Build Coastguard Worker     // Draw the mask into maskTexture with the path's integerized top-left at the origin using
292*c8dee2aaSAndroid Build Coastguard Worker     // maskPaint.
293*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix viewMatrix = origViewMatrix;
294*c8dee2aaSAndroid Build Coastguard Worker     viewMatrix.postTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
295*c8dee2aaSAndroid Build Coastguard Worker     sdc->drawShape(&clip, std::move(maskPaint), GrAA::kYes, viewMatrix, GrStyledShape(shape));
296*c8dee2aaSAndroid Build Coastguard Worker     return sdc;
297*c8dee2aaSAndroid Build Coastguard Worker }
298*c8dee2aaSAndroid Build Coastguard Worker 
get_unclipped_shape_dev_bounds(const GrStyledShape & shape,const SkMatrix & matrix,SkIRect * devBounds)299*c8dee2aaSAndroid Build Coastguard Worker static bool get_unclipped_shape_dev_bounds(const GrStyledShape& shape, const SkMatrix& matrix,
300*c8dee2aaSAndroid Build Coastguard Worker                                            SkIRect* devBounds) {
301*c8dee2aaSAndroid Build Coastguard Worker     SkRect shapeDevBounds;
302*c8dee2aaSAndroid Build Coastguard Worker     if (shape.inverseFilled()) {
303*c8dee2aaSAndroid Build Coastguard Worker         shapeDevBounds = {SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity,
304*c8dee2aaSAndroid Build Coastguard Worker                           SK_ScalarInfinity, SK_ScalarInfinity};
305*c8dee2aaSAndroid Build Coastguard Worker     } else {
306*c8dee2aaSAndroid Build Coastguard Worker         SkRect shapeBounds = shape.styledBounds();
307*c8dee2aaSAndroid Build Coastguard Worker         if (shapeBounds.isEmpty()) {
308*c8dee2aaSAndroid Build Coastguard Worker             return false;
309*c8dee2aaSAndroid Build Coastguard Worker         }
310*c8dee2aaSAndroid Build Coastguard Worker         matrix.mapRect(&shapeDevBounds, shapeBounds);
311*c8dee2aaSAndroid Build Coastguard Worker     }
312*c8dee2aaSAndroid Build Coastguard Worker     // Even though these are "unclipped" bounds we still clip to the int32_t range.
313*c8dee2aaSAndroid Build Coastguard Worker     // This is the largest int32_t that is representable exactly as a float. The next 63 larger ints
314*c8dee2aaSAndroid Build Coastguard Worker     // would round down to this value when cast to a float, but who really cares.
315*c8dee2aaSAndroid Build Coastguard Worker     // INT32_MIN is exactly representable.
316*c8dee2aaSAndroid Build Coastguard Worker     static constexpr int32_t kMaxInt = 2147483520;
317*c8dee2aaSAndroid Build Coastguard Worker     if (!shapeDevBounds.intersect(SkRect::MakeLTRB(INT32_MIN, INT32_MIN, kMaxInt, kMaxInt))) {
318*c8dee2aaSAndroid Build Coastguard Worker         return false;
319*c8dee2aaSAndroid Build Coastguard Worker     }
320*c8dee2aaSAndroid Build Coastguard Worker     // Make sure that the resulting SkIRect can have representable width and height
321*c8dee2aaSAndroid Build Coastguard Worker     if (SkScalarRoundToInt(shapeDevBounds.width()) > kMaxInt ||
322*c8dee2aaSAndroid Build Coastguard Worker         SkScalarRoundToInt(shapeDevBounds.height()) > kMaxInt) {
323*c8dee2aaSAndroid Build Coastguard Worker         return false;
324*c8dee2aaSAndroid Build Coastguard Worker     }
325*c8dee2aaSAndroid Build Coastguard Worker     shapeDevBounds.roundOut(devBounds);
326*c8dee2aaSAndroid Build Coastguard Worker     return true;
327*c8dee2aaSAndroid Build Coastguard Worker }
328*c8dee2aaSAndroid Build Coastguard Worker 
329*c8dee2aaSAndroid Build Coastguard Worker // Gets the shape bounds, the clip bounds, and the intersection (if any). Returns false if there
330*c8dee2aaSAndroid Build Coastguard Worker // is no intersection.
get_shape_and_clip_bounds(skgpu::ganesh::SurfaceDrawContext * sdc,const GrClip * clip,const GrStyledShape & shape,const SkMatrix & matrix,SkIRect * unclippedDevShapeBounds,SkIRect * devClipBounds)331*c8dee2aaSAndroid Build Coastguard Worker static bool get_shape_and_clip_bounds(skgpu::ganesh::SurfaceDrawContext* sdc,
332*c8dee2aaSAndroid Build Coastguard Worker                                       const GrClip* clip,
333*c8dee2aaSAndroid Build Coastguard Worker                                       const GrStyledShape& shape,
334*c8dee2aaSAndroid Build Coastguard Worker                                       const SkMatrix& matrix,
335*c8dee2aaSAndroid Build Coastguard Worker                                       SkIRect* unclippedDevShapeBounds,
336*c8dee2aaSAndroid Build Coastguard Worker                                       SkIRect* devClipBounds) {
337*c8dee2aaSAndroid Build Coastguard Worker     // compute bounds as intersection of rt size, clip, and path
338*c8dee2aaSAndroid Build Coastguard Worker     *devClipBounds = clip ? clip->getConservativeBounds()
339*c8dee2aaSAndroid Build Coastguard Worker                           : SkIRect::MakeWH(sdc->width(), sdc->height());
340*c8dee2aaSAndroid Build Coastguard Worker 
341*c8dee2aaSAndroid Build Coastguard Worker     if (!get_unclipped_shape_dev_bounds(shape, matrix, unclippedDevShapeBounds)) {
342*c8dee2aaSAndroid Build Coastguard Worker         *unclippedDevShapeBounds = SkIRect::MakeEmpty();
343*c8dee2aaSAndroid Build Coastguard Worker         return false;
344*c8dee2aaSAndroid Build Coastguard Worker     }
345*c8dee2aaSAndroid Build Coastguard Worker 
346*c8dee2aaSAndroid Build Coastguard Worker     return true;
347*c8dee2aaSAndroid Build Coastguard Worker }
348*c8dee2aaSAndroid Build Coastguard Worker 
349*c8dee2aaSAndroid Build Coastguard Worker /**
350*c8dee2aaSAndroid Build Coastguard Worker  *  If we cannot create a FragmentProcess for a mask filter, we might have special logic for
351*c8dee2aaSAndroid Build Coastguard Worker  *  it here. That code path requires constructing a src mask as input. Since that is a potentially
352*c8dee2aaSAndroid Build Coastguard Worker  *  expensive operation, this function tests if filter_mask would succeed if the mask
353*c8dee2aaSAndroid Build Coastguard Worker  *  were to be created.
354*c8dee2aaSAndroid Build Coastguard Worker  *
355*c8dee2aaSAndroid Build Coastguard Worker  *  'maskRect' returns the device space portion of the mask that the filter needs. The mask
356*c8dee2aaSAndroid Build Coastguard Worker  *  passed into 'filter_mask' should have the same extent as 'maskRect' but be
357*c8dee2aaSAndroid Build Coastguard Worker  *  translated to the upper-left corner of the mask (i.e., (maskRect.fLeft, maskRect.fTop)
358*c8dee2aaSAndroid Build Coastguard Worker  *  appears at (0, 0) in the mask).
359*c8dee2aaSAndroid Build Coastguard Worker  *
360*c8dee2aaSAndroid Build Coastguard Worker  * Logically, how this works is:
361*c8dee2aaSAndroid Build Coastguard Worker  *    can_filter_mask is called
362*c8dee2aaSAndroid Build Coastguard Worker  *    if (it returns true)
363*c8dee2aaSAndroid Build Coastguard Worker  *        the returned mask rect is used for quick rejecting
364*c8dee2aaSAndroid Build Coastguard Worker  *            the mask rect is used to generate the mask
365*c8dee2aaSAndroid Build Coastguard Worker  *            filter_mask is called to filter the mask
366*c8dee2aaSAndroid Build Coastguard Worker  *
367*c8dee2aaSAndroid Build Coastguard Worker  * TODO: this should work as:
368*c8dee2aaSAndroid Build Coastguard Worker  *    if (can_filter_mask(devShape, ...)) // rect, rrect, drrect, path
369*c8dee2aaSAndroid Build Coastguard Worker  *        filter_mask(devShape, ...)
370*c8dee2aaSAndroid Build Coastguard Worker  * this would hide the RRect special case and the mask generation
371*c8dee2aaSAndroid Build Coastguard Worker  */
can_filter_mask(const SkMaskFilterBase * maskFilter,const GrStyledShape & shape,const SkIRect & devSpaceShapeBounds,const SkIRect & clipBounds,const SkMatrix & ctm,SkIRect * maskRect)372*c8dee2aaSAndroid Build Coastguard Worker static bool can_filter_mask(const SkMaskFilterBase* maskFilter,
373*c8dee2aaSAndroid Build Coastguard Worker                             const GrStyledShape& shape,
374*c8dee2aaSAndroid Build Coastguard Worker                             const SkIRect& devSpaceShapeBounds,
375*c8dee2aaSAndroid Build Coastguard Worker                             const SkIRect& clipBounds,
376*c8dee2aaSAndroid Build Coastguard Worker                             const SkMatrix& ctm,
377*c8dee2aaSAndroid Build Coastguard Worker                             SkIRect* maskRect) {
378*c8dee2aaSAndroid Build Coastguard Worker     if (maskFilter->type() != SkMaskFilterBase::Type::kBlur) {
379*c8dee2aaSAndroid Build Coastguard Worker         return false;
380*c8dee2aaSAndroid Build Coastguard Worker     }
381*c8dee2aaSAndroid Build Coastguard Worker     auto bmf = static_cast<const SkBlurMaskFilterImpl*>(maskFilter);
382*c8dee2aaSAndroid Build Coastguard Worker     SkScalar xformedSigma = bmf->computeXformedSigma(ctm);
383*c8dee2aaSAndroid Build Coastguard Worker     if (skgpu::BlurIsEffectivelyIdentity(xformedSigma)) {
384*c8dee2aaSAndroid Build Coastguard Worker         *maskRect = devSpaceShapeBounds;
385*c8dee2aaSAndroid Build Coastguard Worker         return maskRect->intersect(clipBounds);
386*c8dee2aaSAndroid Build Coastguard Worker     }
387*c8dee2aaSAndroid Build Coastguard Worker 
388*c8dee2aaSAndroid Build Coastguard Worker     if (maskRect) {
389*c8dee2aaSAndroid Build Coastguard Worker         float sigma3 = 3 * xformedSigma;
390*c8dee2aaSAndroid Build Coastguard Worker 
391*c8dee2aaSAndroid Build Coastguard Worker         // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
392*c8dee2aaSAndroid Build Coastguard Worker         SkIRect clipRect = clipBounds.makeOutset(sigma3, sigma3);
393*c8dee2aaSAndroid Build Coastguard Worker         SkIRect srcRect = devSpaceShapeBounds.makeOutset(sigma3, sigma3);
394*c8dee2aaSAndroid Build Coastguard Worker 
395*c8dee2aaSAndroid Build Coastguard Worker         if (!srcRect.intersect(clipRect)) {
396*c8dee2aaSAndroid Build Coastguard Worker             srcRect.setEmpty();
397*c8dee2aaSAndroid Build Coastguard Worker         }
398*c8dee2aaSAndroid Build Coastguard Worker         *maskRect = srcRect;
399*c8dee2aaSAndroid Build Coastguard Worker     }
400*c8dee2aaSAndroid Build Coastguard Worker 
401*c8dee2aaSAndroid Build Coastguard Worker     // We prefer to blur paths with small blur radii on the CPU.
402*c8dee2aaSAndroid Build Coastguard Worker     static const SkScalar kMIN_GPU_BLUR_SIZE  = SkIntToScalar(64);
403*c8dee2aaSAndroid Build Coastguard Worker     static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32);
404*c8dee2aaSAndroid Build Coastguard Worker 
405*c8dee2aaSAndroid Build Coastguard Worker     if (devSpaceShapeBounds.width() <= kMIN_GPU_BLUR_SIZE &&
406*c8dee2aaSAndroid Build Coastguard Worker         devSpaceShapeBounds.height() <= kMIN_GPU_BLUR_SIZE &&
407*c8dee2aaSAndroid Build Coastguard Worker         xformedSigma <= kMIN_GPU_BLUR_SIGMA) {
408*c8dee2aaSAndroid Build Coastguard Worker         return false;
409*c8dee2aaSAndroid Build Coastguard Worker     }
410*c8dee2aaSAndroid Build Coastguard Worker 
411*c8dee2aaSAndroid Build Coastguard Worker     return true;
412*c8dee2aaSAndroid Build Coastguard Worker }
413*c8dee2aaSAndroid Build Coastguard Worker 
414*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
415*c8dee2aaSAndroid Build Coastguard Worker //  Circle Blur
416*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
417*c8dee2aaSAndroid Build Coastguard Worker 
create_profile_effect(GrRecordingContext * rContext,const SkRect & circle,float sigma,float * solidRadius,float * textureRadius)418*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<GrFragmentProcessor> create_profile_effect(GrRecordingContext* rContext,
419*c8dee2aaSAndroid Build Coastguard Worker                                                                   const SkRect& circle,
420*c8dee2aaSAndroid Build Coastguard Worker                                                                   float sigma,
421*c8dee2aaSAndroid Build Coastguard Worker                                                                   float* solidRadius,
422*c8dee2aaSAndroid Build Coastguard Worker                                                                   float* textureRadius) {
423*c8dee2aaSAndroid Build Coastguard Worker     float circleR = circle.width() / 2.0f;
424*c8dee2aaSAndroid Build Coastguard Worker     if (!SkIsFinite(circleR) || circleR < SK_ScalarNearlyZero) {
425*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
426*c8dee2aaSAndroid Build Coastguard Worker     }
427*c8dee2aaSAndroid Build Coastguard Worker 
428*c8dee2aaSAndroid Build Coastguard Worker     auto threadSafeCache = rContext->priv().threadSafeCache();
429*c8dee2aaSAndroid Build Coastguard Worker 
430*c8dee2aaSAndroid Build Coastguard Worker     // Profile textures are cached by the ratio of sigma to circle radius and by the size of the
431*c8dee2aaSAndroid Build Coastguard Worker     // profile texture (binned by powers of 2).
432*c8dee2aaSAndroid Build Coastguard Worker     SkScalar sigmaToCircleRRatio = sigma / circleR;
433*c8dee2aaSAndroid Build Coastguard Worker     // When sigma is really small this becomes a equivalent to convolving a Gaussian with a
434*c8dee2aaSAndroid Build Coastguard Worker     // half-plane. Similarly, in the extreme high ratio cases circle becomes a point WRT to the
435*c8dee2aaSAndroid Build Coastguard Worker     // Guassian and the profile texture is a just a Gaussian evaluation. However, we haven't yet
436*c8dee2aaSAndroid Build Coastguard Worker     // implemented this latter optimization.
437*c8dee2aaSAndroid Build Coastguard Worker     sigmaToCircleRRatio = std::min(sigmaToCircleRRatio, 8.f);
438*c8dee2aaSAndroid Build Coastguard Worker     SkFixed sigmaToCircleRRatioFixed;
439*c8dee2aaSAndroid Build Coastguard Worker     static const SkScalar kHalfPlaneThreshold = 0.1f;
440*c8dee2aaSAndroid Build Coastguard Worker     bool useHalfPlaneApprox = false;
441*c8dee2aaSAndroid Build Coastguard Worker     if (sigmaToCircleRRatio <= kHalfPlaneThreshold) {
442*c8dee2aaSAndroid Build Coastguard Worker         useHalfPlaneApprox = true;
443*c8dee2aaSAndroid Build Coastguard Worker         sigmaToCircleRRatioFixed = 0;
444*c8dee2aaSAndroid Build Coastguard Worker         *solidRadius = circleR - 3 * sigma;
445*c8dee2aaSAndroid Build Coastguard Worker         *textureRadius = 6 * sigma;
446*c8dee2aaSAndroid Build Coastguard Worker     } else {
447*c8dee2aaSAndroid Build Coastguard Worker         // Convert to fixed point for the key.
448*c8dee2aaSAndroid Build Coastguard Worker         sigmaToCircleRRatioFixed = SkScalarToFixed(sigmaToCircleRRatio);
449*c8dee2aaSAndroid Build Coastguard Worker         // We shave off some bits to reduce the number of unique entries. We could probably
450*c8dee2aaSAndroid Build Coastguard Worker         // shave off more than we do.
451*c8dee2aaSAndroid Build Coastguard Worker         sigmaToCircleRRatioFixed &= ~0xff;
452*c8dee2aaSAndroid Build Coastguard Worker         sigmaToCircleRRatio = SkFixedToScalar(sigmaToCircleRRatioFixed);
453*c8dee2aaSAndroid Build Coastguard Worker         sigma = circleR * sigmaToCircleRRatio;
454*c8dee2aaSAndroid Build Coastguard Worker         *solidRadius = 0;
455*c8dee2aaSAndroid Build Coastguard Worker         *textureRadius = circleR + 3 * sigma;
456*c8dee2aaSAndroid Build Coastguard Worker     }
457*c8dee2aaSAndroid Build Coastguard Worker 
458*c8dee2aaSAndroid Build Coastguard Worker     static constexpr int kProfileTextureWidth = 512;
459*c8dee2aaSAndroid Build Coastguard Worker     // This would be kProfileTextureWidth/textureRadius if it weren't for the fact that we do
460*c8dee2aaSAndroid Build Coastguard Worker     // the calculation of the profile coord in a coord space that has already been scaled by
461*c8dee2aaSAndroid Build Coastguard Worker     // 1 / textureRadius. This is done to avoid overflow in length().
462*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix texM = SkMatrix::Scale(kProfileTextureWidth, 1.f);
463*c8dee2aaSAndroid Build Coastguard Worker 
464*c8dee2aaSAndroid Build Coastguard Worker     static const skgpu::UniqueKey::Domain kDomain = skgpu::UniqueKey::GenerateDomain();
465*c8dee2aaSAndroid Build Coastguard Worker     skgpu::UniqueKey key;
466*c8dee2aaSAndroid Build Coastguard Worker     skgpu::UniqueKey::Builder builder(&key, kDomain, 1, "1-D Circular Blur");
467*c8dee2aaSAndroid Build Coastguard Worker     builder[0] = sigmaToCircleRRatioFixed;
468*c8dee2aaSAndroid Build Coastguard Worker     builder.finish();
469*c8dee2aaSAndroid Build Coastguard Worker 
470*c8dee2aaSAndroid Build Coastguard Worker     GrSurfaceProxyView profileView = threadSafeCache->find(key);
471*c8dee2aaSAndroid Build Coastguard Worker     if (profileView) {
472*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(profileView.asTextureProxy());
473*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(profileView.origin() == kTopLeft_GrSurfaceOrigin);
474*c8dee2aaSAndroid Build Coastguard Worker         return GrTextureEffect::Make(std::move(profileView), kPremul_SkAlphaType, texM);
475*c8dee2aaSAndroid Build Coastguard Worker     }
476*c8dee2aaSAndroid Build Coastguard Worker 
477*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm;
478*c8dee2aaSAndroid Build Coastguard Worker     if (useHalfPlaneApprox) {
479*c8dee2aaSAndroid Build Coastguard Worker         bm = skgpu::CreateHalfPlaneProfile(kProfileTextureWidth);
480*c8dee2aaSAndroid Build Coastguard Worker     } else {
481*c8dee2aaSAndroid Build Coastguard Worker         // Rescale params to the size of the texture we're creating.
482*c8dee2aaSAndroid Build Coastguard Worker         SkScalar scale = kProfileTextureWidth / *textureRadius;
483*c8dee2aaSAndroid Build Coastguard Worker         bm = skgpu::CreateCircleProfile(sigma * scale, circleR * scale, kProfileTextureWidth);
484*c8dee2aaSAndroid Build Coastguard Worker     }
485*c8dee2aaSAndroid Build Coastguard Worker 
486*c8dee2aaSAndroid Build Coastguard Worker     profileView = std::get<0>(GrMakeUncachedBitmapProxyView(rContext, bm));
487*c8dee2aaSAndroid Build Coastguard Worker     if (!profileView) {
488*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
489*c8dee2aaSAndroid Build Coastguard Worker     }
490*c8dee2aaSAndroid Build Coastguard Worker 
491*c8dee2aaSAndroid Build Coastguard Worker     profileView = threadSafeCache->add(key, profileView);
492*c8dee2aaSAndroid Build Coastguard Worker     return GrTextureEffect::Make(std::move(profileView), kPremul_SkAlphaType, texM);
493*c8dee2aaSAndroid Build Coastguard Worker }
494*c8dee2aaSAndroid Build Coastguard Worker 
make_circle_blur(GrRecordingContext * context,const SkRect & circle,float sigma)495*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<GrFragmentProcessor> make_circle_blur(GrRecordingContext* context,
496*c8dee2aaSAndroid Build Coastguard Worker                                                              const SkRect& circle,
497*c8dee2aaSAndroid Build Coastguard Worker                                                              float sigma) {
498*c8dee2aaSAndroid Build Coastguard Worker     if (skgpu::BlurIsEffectivelyIdentity(sigma)) {
499*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
500*c8dee2aaSAndroid Build Coastguard Worker     }
501*c8dee2aaSAndroid Build Coastguard Worker 
502*c8dee2aaSAndroid Build Coastguard Worker     float solidRadius;
503*c8dee2aaSAndroid Build Coastguard Worker     float textureRadius;
504*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<GrFragmentProcessor> profile =
505*c8dee2aaSAndroid Build Coastguard Worker             create_profile_effect(context, circle, sigma, &solidRadius, &textureRadius);
506*c8dee2aaSAndroid Build Coastguard Worker     if (!profile) {
507*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
508*c8dee2aaSAndroid Build Coastguard Worker     }
509*c8dee2aaSAndroid Build Coastguard Worker 
510*c8dee2aaSAndroid Build Coastguard Worker     static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
511*c8dee2aaSAndroid Build Coastguard Worker         "uniform shader blurProfile;"
512*c8dee2aaSAndroid Build Coastguard Worker         "uniform half4 circleData;"
513*c8dee2aaSAndroid Build Coastguard Worker 
514*c8dee2aaSAndroid Build Coastguard Worker         "half4 main(float2 xy) {"
515*c8dee2aaSAndroid Build Coastguard Worker             // We just want to compute "(length(vec) - circleData.z + 0.5) * circleData.w" but need
516*c8dee2aaSAndroid Build Coastguard Worker             // to rearrange to avoid passing large values to length() that would overflow.
517*c8dee2aaSAndroid Build Coastguard Worker             "half2 vec = half2((sk_FragCoord.xy - circleData.xy) * circleData.w);"
518*c8dee2aaSAndroid Build Coastguard Worker             "half dist = length(vec) + (0.5 - circleData.z) * circleData.w;"
519*c8dee2aaSAndroid Build Coastguard Worker             "return blurProfile.eval(half2(dist, 0.5)).aaaa;"
520*c8dee2aaSAndroid Build Coastguard Worker         "}"
521*c8dee2aaSAndroid Build Coastguard Worker     );
522*c8dee2aaSAndroid Build Coastguard Worker 
523*c8dee2aaSAndroid Build Coastguard Worker     SkV4 circleData = {circle.centerX(), circle.centerY(), solidRadius, 1.f / textureRadius};
524*c8dee2aaSAndroid Build Coastguard Worker     auto circleBlurFP = GrSkSLFP::Make(effect, "CircleBlur", /*inputFP=*/nullptr,
525*c8dee2aaSAndroid Build Coastguard Worker                                        GrSkSLFP::OptFlags::kCompatibleWithCoverageAsAlpha,
526*c8dee2aaSAndroid Build Coastguard Worker                                        "blurProfile", GrSkSLFP::IgnoreOptFlags(std::move(profile)),
527*c8dee2aaSAndroid Build Coastguard Worker                                        "circleData", circleData);
528*c8dee2aaSAndroid Build Coastguard Worker     // Modulate blur with the input color.
529*c8dee2aaSAndroid Build Coastguard Worker     return GrBlendFragmentProcessor::Make<SkBlendMode::kModulate>(std::move(circleBlurFP),
530*c8dee2aaSAndroid Build Coastguard Worker                                                                   /*dst=*/nullptr);
531*c8dee2aaSAndroid Build Coastguard Worker }
532*c8dee2aaSAndroid Build Coastguard Worker 
533*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
534*c8dee2aaSAndroid Build Coastguard Worker //  Rect Blur
535*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
536*c8dee2aaSAndroid Build Coastguard Worker 
make_rect_integral_fp(GrRecordingContext * rContext,float sixSigma)537*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<GrFragmentProcessor> make_rect_integral_fp(GrRecordingContext* rContext,
538*c8dee2aaSAndroid Build Coastguard Worker                                                                   float sixSigma) {
539*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!skgpu::BlurIsEffectivelyIdentity(sixSigma / 6.f));
540*c8dee2aaSAndroid Build Coastguard Worker     auto threadSafeCache = rContext->priv().threadSafeCache();
541*c8dee2aaSAndroid Build Coastguard Worker 
542*c8dee2aaSAndroid Build Coastguard Worker     int width = skgpu::ComputeIntegralTableWidth(sixSigma);
543*c8dee2aaSAndroid Build Coastguard Worker 
544*c8dee2aaSAndroid Build Coastguard Worker     static const skgpu::UniqueKey::Domain kDomain = skgpu::UniqueKey::GenerateDomain();
545*c8dee2aaSAndroid Build Coastguard Worker     skgpu::UniqueKey key;
546*c8dee2aaSAndroid Build Coastguard Worker     skgpu::UniqueKey::Builder builder(&key, kDomain, 1, "Rect Blur Mask");
547*c8dee2aaSAndroid Build Coastguard Worker     builder[0] = width;
548*c8dee2aaSAndroid Build Coastguard Worker     builder.finish();
549*c8dee2aaSAndroid Build Coastguard Worker 
550*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix m = SkMatrix::Scale(width / sixSigma, 1.f);
551*c8dee2aaSAndroid Build Coastguard Worker 
552*c8dee2aaSAndroid Build Coastguard Worker     GrSurfaceProxyView view = threadSafeCache->find(key);
553*c8dee2aaSAndroid Build Coastguard Worker 
554*c8dee2aaSAndroid Build Coastguard Worker     if (view) {
555*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
556*c8dee2aaSAndroid Build Coastguard Worker         return GrTextureEffect::Make(
557*c8dee2aaSAndroid Build Coastguard Worker                 std::move(view), kPremul_SkAlphaType, m, GrSamplerState::Filter::kLinear);
558*c8dee2aaSAndroid Build Coastguard Worker     }
559*c8dee2aaSAndroid Build Coastguard Worker 
560*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bitmap = skgpu::CreateIntegralTable(width);
561*c8dee2aaSAndroid Build Coastguard Worker     if (bitmap.empty()) {
562*c8dee2aaSAndroid Build Coastguard Worker         return {};
563*c8dee2aaSAndroid Build Coastguard Worker     }
564*c8dee2aaSAndroid Build Coastguard Worker 
565*c8dee2aaSAndroid Build Coastguard Worker     view = std::get<0>(GrMakeUncachedBitmapProxyView(rContext, bitmap));
566*c8dee2aaSAndroid Build Coastguard Worker     if (!view) {
567*c8dee2aaSAndroid Build Coastguard Worker         return {};
568*c8dee2aaSAndroid Build Coastguard Worker     }
569*c8dee2aaSAndroid Build Coastguard Worker 
570*c8dee2aaSAndroid Build Coastguard Worker     view = threadSafeCache->add(key, view);
571*c8dee2aaSAndroid Build Coastguard Worker 
572*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
573*c8dee2aaSAndroid Build Coastguard Worker     return GrTextureEffect::Make(
574*c8dee2aaSAndroid Build Coastguard Worker             std::move(view), kPremul_SkAlphaType, m, GrSamplerState::Filter::kLinear);
575*c8dee2aaSAndroid Build Coastguard Worker }
576*c8dee2aaSAndroid Build Coastguard Worker 
make_rect_blur(GrRecordingContext * context,const GrShaderCaps & caps,const SkRect & srcRect,const SkMatrix & viewMatrix,float transformedSigma)577*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<GrFragmentProcessor> make_rect_blur(GrRecordingContext* context,
578*c8dee2aaSAndroid Build Coastguard Worker                                                            const GrShaderCaps& caps,
579*c8dee2aaSAndroid Build Coastguard Worker                                                            const SkRect& srcRect,
580*c8dee2aaSAndroid Build Coastguard Worker                                                            const SkMatrix& viewMatrix,
581*c8dee2aaSAndroid Build Coastguard Worker                                                            float transformedSigma) {
582*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(viewMatrix.preservesRightAngles());
583*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(srcRect.isSorted());
584*c8dee2aaSAndroid Build Coastguard Worker 
585*c8dee2aaSAndroid Build Coastguard Worker     if (skgpu::BlurIsEffectivelyIdentity(transformedSigma)) {
586*c8dee2aaSAndroid Build Coastguard Worker         // No need to blur the rect
587*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
588*c8dee2aaSAndroid Build Coastguard Worker     }
589*c8dee2aaSAndroid Build Coastguard Worker 
590*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix invM;
591*c8dee2aaSAndroid Build Coastguard Worker     SkRect rect;
592*c8dee2aaSAndroid Build Coastguard Worker     if (viewMatrix.rectStaysRect()) {
593*c8dee2aaSAndroid Build Coastguard Worker         invM = SkMatrix::I();
594*c8dee2aaSAndroid Build Coastguard Worker         // We can do everything in device space when the src rect projects to a rect in device space
595*c8dee2aaSAndroid Build Coastguard Worker         SkAssertResult(viewMatrix.mapRect(&rect, srcRect));
596*c8dee2aaSAndroid Build Coastguard Worker     } else {
597*c8dee2aaSAndroid Build Coastguard Worker         // The view matrix may scale, perhaps anisotropically. But we want to apply our device space
598*c8dee2aaSAndroid Build Coastguard Worker         // "transformedSigma" to the delta of frag coord from the rect edges. Factor out the scaling
599*c8dee2aaSAndroid Build Coastguard Worker         // to define a space that is purely rotation/translation from device space (and scale from
600*c8dee2aaSAndroid Build Coastguard Worker         // src space) We'll meet in the middle: pre-scale the src rect to be in this space and then
601*c8dee2aaSAndroid Build Coastguard Worker         // apply the inverse of the rotation/translation portion to the frag coord.
602*c8dee2aaSAndroid Build Coastguard Worker         SkMatrix m;
603*c8dee2aaSAndroid Build Coastguard Worker         SkSize scale;
604*c8dee2aaSAndroid Build Coastguard Worker         if (!viewMatrix.decomposeScale(&scale, &m)) {
605*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
606*c8dee2aaSAndroid Build Coastguard Worker         }
607*c8dee2aaSAndroid Build Coastguard Worker         if (!m.invert(&invM)) {
608*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
609*c8dee2aaSAndroid Build Coastguard Worker         }
610*c8dee2aaSAndroid Build Coastguard Worker         rect = {srcRect.left() * scale.width(),
611*c8dee2aaSAndroid Build Coastguard Worker                 srcRect.top() * scale.height(),
612*c8dee2aaSAndroid Build Coastguard Worker                 srcRect.right() * scale.width(),
613*c8dee2aaSAndroid Build Coastguard Worker                 srcRect.bottom() * scale.height()};
614*c8dee2aaSAndroid Build Coastguard Worker     }
615*c8dee2aaSAndroid Build Coastguard Worker 
616*c8dee2aaSAndroid Build Coastguard Worker     if (!caps.fFloatIs32Bits) {
617*c8dee2aaSAndroid Build Coastguard Worker         // We promote the math that gets us into the Gaussian space to full float when the rect
618*c8dee2aaSAndroid Build Coastguard Worker         // coords are large. If we don't have full float then fail. We could probably clip the rect
619*c8dee2aaSAndroid Build Coastguard Worker         // to an outset device bounds instead.
620*c8dee2aaSAndroid Build Coastguard Worker         if (SkScalarAbs(rect.fLeft) > 16000.f || SkScalarAbs(rect.fTop) > 16000.f ||
621*c8dee2aaSAndroid Build Coastguard Worker             SkScalarAbs(rect.fRight) > 16000.f || SkScalarAbs(rect.fBottom) > 16000.f) {
622*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
623*c8dee2aaSAndroid Build Coastguard Worker         }
624*c8dee2aaSAndroid Build Coastguard Worker     }
625*c8dee2aaSAndroid Build Coastguard Worker 
626*c8dee2aaSAndroid Build Coastguard Worker     const float sixSigma = 6 * transformedSigma;
627*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<GrFragmentProcessor> integral = make_rect_integral_fp(context, sixSigma);
628*c8dee2aaSAndroid Build Coastguard Worker     if (!integral) {
629*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
630*c8dee2aaSAndroid Build Coastguard Worker     }
631*c8dee2aaSAndroid Build Coastguard Worker 
632*c8dee2aaSAndroid Build Coastguard Worker     // In the fast variant we think of the midpoint of the integral texture as aligning with the
633*c8dee2aaSAndroid Build Coastguard Worker     // closest rect edge both in x and y. To simplify texture coord calculation we inset the rect so
634*c8dee2aaSAndroid Build Coastguard Worker     // that the edge of the inset rect corresponds to t = 0 in the texture. It actually simplifies
635*c8dee2aaSAndroid Build Coastguard Worker     // things a bit in the !isFast case, too.
636*c8dee2aaSAndroid Build Coastguard Worker     float threeSigma = sixSigma / 2;
637*c8dee2aaSAndroid Build Coastguard Worker     SkRect insetRect = {rect.left() + threeSigma,
638*c8dee2aaSAndroid Build Coastguard Worker                         rect.top() + threeSigma,
639*c8dee2aaSAndroid Build Coastguard Worker                         rect.right() - threeSigma,
640*c8dee2aaSAndroid Build Coastguard Worker                         rect.bottom() - threeSigma};
641*c8dee2aaSAndroid Build Coastguard Worker 
642*c8dee2aaSAndroid Build Coastguard Worker     // In our fast variant we find the nearest horizontal and vertical edges and for each do a
643*c8dee2aaSAndroid Build Coastguard Worker     // lookup in the integral texture for each and multiply them. When the rect is less than 6 sigma
644*c8dee2aaSAndroid Build Coastguard Worker     // wide then things aren't so simple and we have to consider both the left and right edge of the
645*c8dee2aaSAndroid Build Coastguard Worker     // rectangle (and similar in y).
646*c8dee2aaSAndroid Build Coastguard Worker     bool isFast = insetRect.isSorted();
647*c8dee2aaSAndroid Build Coastguard Worker 
648*c8dee2aaSAndroid Build Coastguard Worker     static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
649*c8dee2aaSAndroid Build Coastguard Worker         // Effect that is a LUT for integral of normal distribution. The value at x:[0,6*sigma] is
650*c8dee2aaSAndroid Build Coastguard Worker         // the integral from -inf to (3*sigma - x). I.e. x is mapped from [0, 6*sigma] to
651*c8dee2aaSAndroid Build Coastguard Worker         // [3*sigma to -3*sigma]. The flip saves a reversal in the shader.
652*c8dee2aaSAndroid Build Coastguard Worker         "uniform shader integral;"
653*c8dee2aaSAndroid Build Coastguard Worker 
654*c8dee2aaSAndroid Build Coastguard Worker         "uniform float4 rect;"
655*c8dee2aaSAndroid Build Coastguard Worker         "uniform int isFast;"  // specialized
656*c8dee2aaSAndroid Build Coastguard Worker 
657*c8dee2aaSAndroid Build Coastguard Worker         "half4 main(float2 pos) {"
658*c8dee2aaSAndroid Build Coastguard Worker             "half xCoverage, yCoverage;"
659*c8dee2aaSAndroid Build Coastguard Worker             "if (bool(isFast)) {"
660*c8dee2aaSAndroid Build Coastguard Worker                 // Get the smaller of the signed distance from the frag coord to the left and right
661*c8dee2aaSAndroid Build Coastguard Worker                 // edges and similar for y.
662*c8dee2aaSAndroid Build Coastguard Worker                 // The integral texture goes "backwards" (from 3*sigma to -3*sigma), So, the below
663*c8dee2aaSAndroid Build Coastguard Worker                 // computations align the left edge of the integral texture with the inset rect's
664*c8dee2aaSAndroid Build Coastguard Worker                 // edge extending outward 6 * sigma from the inset rect.
665*c8dee2aaSAndroid Build Coastguard Worker                 "half2 xy = max(half2(rect.LT - pos), half2(pos - rect.RB));"
666*c8dee2aaSAndroid Build Coastguard Worker                 "xCoverage = integral.eval(half2(xy.x, 0.5)).a;"
667*c8dee2aaSAndroid Build Coastguard Worker                 "yCoverage = integral.eval(half2(xy.y, 0.5)).a;"
668*c8dee2aaSAndroid Build Coastguard Worker             "} else {"
669*c8dee2aaSAndroid Build Coastguard Worker                 // We just consider just the x direction here. In practice we compute x and y
670*c8dee2aaSAndroid Build Coastguard Worker                 // separately and multiply them together.
671*c8dee2aaSAndroid Build Coastguard Worker                 // We define our coord system so that the point at which we're evaluating a kernel
672*c8dee2aaSAndroid Build Coastguard Worker                 // defined by the normal distribution (K) at 0. In this coord system let L be left
673*c8dee2aaSAndroid Build Coastguard Worker                 // edge and R be the right edge of the rectangle.
674*c8dee2aaSAndroid Build Coastguard Worker                 // We can calculate C by integrating K with the half infinite ranges outside the
675*c8dee2aaSAndroid Build Coastguard Worker                 // L to R range and subtracting from 1:
676*c8dee2aaSAndroid Build Coastguard Worker                 //   C = 1 - <integral of K from from -inf to  L> - <integral of K from R to inf>
677*c8dee2aaSAndroid Build Coastguard Worker                 // K is symmetric about x=0 so:
678*c8dee2aaSAndroid Build Coastguard Worker                 //   C = 1 - <integral of K from from -inf to  L> - <integral of K from -inf to -R>
679*c8dee2aaSAndroid Build Coastguard Worker 
680*c8dee2aaSAndroid Build Coastguard Worker                 // The integral texture goes "backwards" (from 3*sigma to -3*sigma) which is
681*c8dee2aaSAndroid Build Coastguard Worker                 // factored in to the below calculations.
682*c8dee2aaSAndroid Build Coastguard Worker                 // Also, our rect uniform was pre-inset by 3 sigma from the actual rect being
683*c8dee2aaSAndroid Build Coastguard Worker                 // blurred, also factored in.
684*c8dee2aaSAndroid Build Coastguard Worker                 "half4 rect = half4(half2(rect.LT - pos), half2(pos - rect.RB));"
685*c8dee2aaSAndroid Build Coastguard Worker                 "xCoverage = 1 - integral.eval(half2(rect.L, 0.5)).a"
686*c8dee2aaSAndroid Build Coastguard Worker                               "- integral.eval(half2(rect.R, 0.5)).a;"
687*c8dee2aaSAndroid Build Coastguard Worker                 "yCoverage = 1 - integral.eval(half2(rect.T, 0.5)).a"
688*c8dee2aaSAndroid Build Coastguard Worker                               "- integral.eval(half2(rect.B, 0.5)).a;"
689*c8dee2aaSAndroid Build Coastguard Worker             "}"
690*c8dee2aaSAndroid Build Coastguard Worker             "return half4(xCoverage * yCoverage);"
691*c8dee2aaSAndroid Build Coastguard Worker         "}"
692*c8dee2aaSAndroid Build Coastguard Worker     );
693*c8dee2aaSAndroid Build Coastguard Worker 
694*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<GrFragmentProcessor> fp =
695*c8dee2aaSAndroid Build Coastguard Worker             GrSkSLFP::Make(effect, "RectBlur", /*inputFP=*/nullptr,
696*c8dee2aaSAndroid Build Coastguard Worker                            GrSkSLFP::OptFlags::kCompatibleWithCoverageAsAlpha,
697*c8dee2aaSAndroid Build Coastguard Worker                            "integral", GrSkSLFP::IgnoreOptFlags(std::move(integral)),
698*c8dee2aaSAndroid Build Coastguard Worker                            "rect", insetRect,
699*c8dee2aaSAndroid Build Coastguard Worker                            "isFast", GrSkSLFP::Specialize<int>(isFast));
700*c8dee2aaSAndroid Build Coastguard Worker     // Modulate blur with the input color.
701*c8dee2aaSAndroid Build Coastguard Worker     fp = GrBlendFragmentProcessor::Make<SkBlendMode::kModulate>(std::move(fp),
702*c8dee2aaSAndroid Build Coastguard Worker                                                                 /*dst=*/nullptr);
703*c8dee2aaSAndroid Build Coastguard Worker     if (!invM.isIdentity()) {
704*c8dee2aaSAndroid Build Coastguard Worker         fp = GrMatrixEffect::Make(invM, std::move(fp));
705*c8dee2aaSAndroid Build Coastguard Worker     }
706*c8dee2aaSAndroid Build Coastguard Worker     return GrFragmentProcessor::DeviceSpace(std::move(fp));
707*c8dee2aaSAndroid Build Coastguard Worker }
708*c8dee2aaSAndroid Build Coastguard Worker 
709*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
710*c8dee2aaSAndroid Build Coastguard Worker //  RRect Blur
711*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
712*c8dee2aaSAndroid Build Coastguard Worker 
713*c8dee2aaSAndroid Build Coastguard Worker static constexpr auto kBlurredRRectMaskOrigin = kTopLeft_GrSurfaceOrigin;
714*c8dee2aaSAndroid Build Coastguard Worker 
make_blurred_rrect_key(skgpu::UniqueKey * key,const SkRRect & rrectToDraw,float xformedSigma)715*c8dee2aaSAndroid Build Coastguard Worker static void make_blurred_rrect_key(skgpu::UniqueKey* key,
716*c8dee2aaSAndroid Build Coastguard Worker                                    const SkRRect& rrectToDraw,
717*c8dee2aaSAndroid Build Coastguard Worker                                    float xformedSigma) {
718*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!skgpu::BlurIsEffectivelyIdentity(xformedSigma));
719*c8dee2aaSAndroid Build Coastguard Worker     static const skgpu::UniqueKey::Domain kDomain = skgpu::UniqueKey::GenerateDomain();
720*c8dee2aaSAndroid Build Coastguard Worker 
721*c8dee2aaSAndroid Build Coastguard Worker     skgpu::UniqueKey::Builder builder(key, kDomain, 9, "RoundRect Blur Mask");
722*c8dee2aaSAndroid Build Coastguard Worker     builder[0] = SkScalarCeilToInt(xformedSigma - 1 / 6.0f);
723*c8dee2aaSAndroid Build Coastguard Worker 
724*c8dee2aaSAndroid Build Coastguard Worker     int index = 1;
725*c8dee2aaSAndroid Build Coastguard Worker     // TODO: this is overkill for _simple_ circular rrects
726*c8dee2aaSAndroid Build Coastguard Worker     for (auto c : {SkRRect::kUpperLeft_Corner,
727*c8dee2aaSAndroid Build Coastguard Worker                    SkRRect::kUpperRight_Corner,
728*c8dee2aaSAndroid Build Coastguard Worker                    SkRRect::kLowerRight_Corner,
729*c8dee2aaSAndroid Build Coastguard Worker                    SkRRect::kLowerLeft_Corner}) {
730*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(SkScalarIsInt(rrectToDraw.radii(c).fX) && SkScalarIsInt(rrectToDraw.radii(c).fY));
731*c8dee2aaSAndroid Build Coastguard Worker         builder[index++] = SkScalarCeilToInt(rrectToDraw.radii(c).fX);
732*c8dee2aaSAndroid Build Coastguard Worker         builder[index++] = SkScalarCeilToInt(rrectToDraw.radii(c).fY);
733*c8dee2aaSAndroid Build Coastguard Worker     }
734*c8dee2aaSAndroid Build Coastguard Worker     builder.finish();
735*c8dee2aaSAndroid Build Coastguard Worker }
736*c8dee2aaSAndroid Build Coastguard Worker 
fillin_view_on_gpu(GrDirectContext * dContext,const GrSurfaceProxyView & lazyView,GrThreadSafeCache::Trampoline * trampoline,const SkRRect & rrectToDraw,const SkISize & dimensions,float xformedSigma)737*c8dee2aaSAndroid Build Coastguard Worker static bool fillin_view_on_gpu(GrDirectContext* dContext,
738*c8dee2aaSAndroid Build Coastguard Worker                                const GrSurfaceProxyView& lazyView,
739*c8dee2aaSAndroid Build Coastguard Worker                                GrThreadSafeCache::Trampoline* trampoline,
740*c8dee2aaSAndroid Build Coastguard Worker                                const SkRRect& rrectToDraw,
741*c8dee2aaSAndroid Build Coastguard Worker                                const SkISize& dimensions,
742*c8dee2aaSAndroid Build Coastguard Worker                                float xformedSigma) {
743*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!skgpu::BlurIsEffectivelyIdentity(xformedSigma));
744*c8dee2aaSAndroid Build Coastguard Worker 
745*c8dee2aaSAndroid Build Coastguard Worker     // We cache blur masks. Use default surface props here so we can use the same cached mask
746*c8dee2aaSAndroid Build Coastguard Worker     // regardless of the final dst surface.
747*c8dee2aaSAndroid Build Coastguard Worker     SkSurfaceProps defaultSurfaceProps;
748*c8dee2aaSAndroid Build Coastguard Worker 
749*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<skgpu::ganesh::SurfaceDrawContext> sdc =
750*c8dee2aaSAndroid Build Coastguard Worker             skgpu::ganesh::SurfaceDrawContext::MakeWithFallback(dContext,
751*c8dee2aaSAndroid Build Coastguard Worker                                                                 GrColorType::kAlpha_8,
752*c8dee2aaSAndroid Build Coastguard Worker                                                                 nullptr,
753*c8dee2aaSAndroid Build Coastguard Worker                                                                 SkBackingFit::kExact,
754*c8dee2aaSAndroid Build Coastguard Worker                                                                 dimensions,
755*c8dee2aaSAndroid Build Coastguard Worker                                                                 defaultSurfaceProps,
756*c8dee2aaSAndroid Build Coastguard Worker                                                                 1,
757*c8dee2aaSAndroid Build Coastguard Worker                                                                 skgpu::Mipmapped::kNo,
758*c8dee2aaSAndroid Build Coastguard Worker                                                                 GrProtected::kNo,
759*c8dee2aaSAndroid Build Coastguard Worker                                                                 kBlurredRRectMaskOrigin);
760*c8dee2aaSAndroid Build Coastguard Worker     if (!sdc) {
761*c8dee2aaSAndroid Build Coastguard Worker         return false;
762*c8dee2aaSAndroid Build Coastguard Worker     }
763*c8dee2aaSAndroid Build Coastguard Worker 
764*c8dee2aaSAndroid Build Coastguard Worker     GrPaint paint;
765*c8dee2aaSAndroid Build Coastguard Worker 
766*c8dee2aaSAndroid Build Coastguard Worker     sdc->clear(SK_PMColor4fTRANSPARENT);
767*c8dee2aaSAndroid Build Coastguard Worker     sdc->drawRRect(nullptr,
768*c8dee2aaSAndroid Build Coastguard Worker                    std::move(paint),
769*c8dee2aaSAndroid Build Coastguard Worker                    GrAA::kYes,
770*c8dee2aaSAndroid Build Coastguard Worker                    SkMatrix::I(),
771*c8dee2aaSAndroid Build Coastguard Worker                    rrectToDraw,
772*c8dee2aaSAndroid Build Coastguard Worker                    GrStyle::SimpleFill());
773*c8dee2aaSAndroid Build Coastguard Worker 
774*c8dee2aaSAndroid Build Coastguard Worker     GrSurfaceProxyView srcView = sdc->readSurfaceView();
775*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(srcView.asTextureProxy());
776*c8dee2aaSAndroid Build Coastguard Worker     auto rtc2 = GaussianBlur(dContext,
777*c8dee2aaSAndroid Build Coastguard Worker                              std::move(srcView),
778*c8dee2aaSAndroid Build Coastguard Worker                              sdc->colorInfo().colorType(),
779*c8dee2aaSAndroid Build Coastguard Worker                              sdc->colorInfo().alphaType(),
780*c8dee2aaSAndroid Build Coastguard Worker                              nullptr,
781*c8dee2aaSAndroid Build Coastguard Worker                              SkIRect::MakeSize(dimensions),
782*c8dee2aaSAndroid Build Coastguard Worker                              SkIRect::MakeSize(dimensions),
783*c8dee2aaSAndroid Build Coastguard Worker                              xformedSigma,
784*c8dee2aaSAndroid Build Coastguard Worker                              xformedSigma,
785*c8dee2aaSAndroid Build Coastguard Worker                              SkTileMode::kClamp,
786*c8dee2aaSAndroid Build Coastguard Worker                              SkBackingFit::kExact);
787*c8dee2aaSAndroid Build Coastguard Worker     if (!rtc2 || !rtc2->readSurfaceView()) {
788*c8dee2aaSAndroid Build Coastguard Worker         return false;
789*c8dee2aaSAndroid Build Coastguard Worker     }
790*c8dee2aaSAndroid Build Coastguard Worker 
791*c8dee2aaSAndroid Build Coastguard Worker     auto view = rtc2->readSurfaceView();
792*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(view.swizzle() == lazyView.swizzle());
793*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(view.origin() == lazyView.origin());
794*c8dee2aaSAndroid Build Coastguard Worker     trampoline->fProxy = view.asTextureProxyRef();
795*c8dee2aaSAndroid Build Coastguard Worker 
796*c8dee2aaSAndroid Build Coastguard Worker     return true;
797*c8dee2aaSAndroid Build Coastguard Worker }
798*c8dee2aaSAndroid Build Coastguard Worker 
799*c8dee2aaSAndroid Build Coastguard Worker // Create a cpu-side blurred-rrect mask that is close to the version the gpu would've produced.
800*c8dee2aaSAndroid Build Coastguard Worker // The match needs to be close bc the cpu- and gpu-generated version must be interchangeable.
create_mask_on_cpu(GrRecordingContext * rContext,const SkRRect & rrectToDraw,const SkISize & dimensions,float xformedSigma)801*c8dee2aaSAndroid Build Coastguard Worker static GrSurfaceProxyView create_mask_on_cpu(GrRecordingContext* rContext,
802*c8dee2aaSAndroid Build Coastguard Worker                                              const SkRRect& rrectToDraw,
803*c8dee2aaSAndroid Build Coastguard Worker                                              const SkISize& dimensions,
804*c8dee2aaSAndroid Build Coastguard Worker                                              float xformedSigma) {
805*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap result = skgpu::CreateRRectBlurMask(rrectToDraw, dimensions, xformedSigma);
806*c8dee2aaSAndroid Build Coastguard Worker     if (result.empty()) {
807*c8dee2aaSAndroid Build Coastguard Worker         return {};
808*c8dee2aaSAndroid Build Coastguard Worker     }
809*c8dee2aaSAndroid Build Coastguard Worker 
810*c8dee2aaSAndroid Build Coastguard Worker     auto view = std::get<0>(GrMakeUncachedBitmapProxyView(rContext, result));
811*c8dee2aaSAndroid Build Coastguard Worker     if (!view) {
812*c8dee2aaSAndroid Build Coastguard Worker         return {};
813*c8dee2aaSAndroid Build Coastguard Worker     }
814*c8dee2aaSAndroid Build Coastguard Worker 
815*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(view.origin() == kBlurredRRectMaskOrigin);
816*c8dee2aaSAndroid Build Coastguard Worker     return view;
817*c8dee2aaSAndroid Build Coastguard Worker }
818*c8dee2aaSAndroid Build Coastguard Worker 
find_or_create_rrect_blur_mask_fp(GrRecordingContext * rContext,const SkRRect & rrectToDraw,const SkISize & dimensions,float xformedSigma)819*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<GrFragmentProcessor> find_or_create_rrect_blur_mask_fp(
820*c8dee2aaSAndroid Build Coastguard Worker         GrRecordingContext* rContext,
821*c8dee2aaSAndroid Build Coastguard Worker         const SkRRect& rrectToDraw,
822*c8dee2aaSAndroid Build Coastguard Worker         const SkISize& dimensions,
823*c8dee2aaSAndroid Build Coastguard Worker         float xformedSigma) {
824*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!skgpu::BlurIsEffectivelyIdentity(xformedSigma));
825*c8dee2aaSAndroid Build Coastguard Worker     skgpu::UniqueKey key;
826*c8dee2aaSAndroid Build Coastguard Worker     make_blurred_rrect_key(&key, rrectToDraw, xformedSigma);
827*c8dee2aaSAndroid Build Coastguard Worker 
828*c8dee2aaSAndroid Build Coastguard Worker     auto threadSafeCache = rContext->priv().threadSafeCache();
829*c8dee2aaSAndroid Build Coastguard Worker 
830*c8dee2aaSAndroid Build Coastguard Worker     // It seems like we could omit this matrix and modify the shader code to not normalize
831*c8dee2aaSAndroid Build Coastguard Worker     // the coords used to sample the texture effect. However, the "proxyDims" value in the
832*c8dee2aaSAndroid Build Coastguard Worker     // shader is not always the actual the proxy dimensions. This is because 'dimensions' here
833*c8dee2aaSAndroid Build Coastguard Worker     // was computed using integer corner radii as determined in
834*c8dee2aaSAndroid Build Coastguard Worker     // SkComputeBlurredRRectParams whereas the shader code uses the float radius to compute
835*c8dee2aaSAndroid Build Coastguard Worker     // 'proxyDims'. Why it draws correctly with these unequal values is a mystery for the ages.
836*c8dee2aaSAndroid Build Coastguard Worker     auto m = SkMatrix::Scale(dimensions.width(), dimensions.height());
837*c8dee2aaSAndroid Build Coastguard Worker 
838*c8dee2aaSAndroid Build Coastguard Worker     GrSurfaceProxyView view;
839*c8dee2aaSAndroid Build Coastguard Worker 
840*c8dee2aaSAndroid Build Coastguard Worker     if (GrDirectContext* dContext = rContext->asDirectContext()) {
841*c8dee2aaSAndroid Build Coastguard Worker         // The gpu thread gets priority over the recording threads. If the gpu thread is first,
842*c8dee2aaSAndroid Build Coastguard Worker         // it crams a lazy proxy into the cache and then fills it in later.
843*c8dee2aaSAndroid Build Coastguard Worker         auto [lazyView, trampoline] = GrThreadSafeCache::CreateLazyView(dContext,
844*c8dee2aaSAndroid Build Coastguard Worker                                                                         GrColorType::kAlpha_8,
845*c8dee2aaSAndroid Build Coastguard Worker                                                                         dimensions,
846*c8dee2aaSAndroid Build Coastguard Worker                                                                         kBlurredRRectMaskOrigin,
847*c8dee2aaSAndroid Build Coastguard Worker                                                                         SkBackingFit::kExact);
848*c8dee2aaSAndroid Build Coastguard Worker         if (!lazyView) {
849*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
850*c8dee2aaSAndroid Build Coastguard Worker         }
851*c8dee2aaSAndroid Build Coastguard Worker 
852*c8dee2aaSAndroid Build Coastguard Worker         view = threadSafeCache->findOrAdd(key, lazyView);
853*c8dee2aaSAndroid Build Coastguard Worker         if (view != lazyView) {
854*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(view.asTextureProxy());
855*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(view.origin() == kBlurredRRectMaskOrigin);
856*c8dee2aaSAndroid Build Coastguard Worker             return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, m);
857*c8dee2aaSAndroid Build Coastguard Worker         }
858*c8dee2aaSAndroid Build Coastguard Worker 
859*c8dee2aaSAndroid Build Coastguard Worker         if (!fillin_view_on_gpu(dContext,
860*c8dee2aaSAndroid Build Coastguard Worker                                 lazyView,
861*c8dee2aaSAndroid Build Coastguard Worker                                 trampoline.get(),
862*c8dee2aaSAndroid Build Coastguard Worker                                 rrectToDraw,
863*c8dee2aaSAndroid Build Coastguard Worker                                 dimensions,
864*c8dee2aaSAndroid Build Coastguard Worker                                 xformedSigma)) {
865*c8dee2aaSAndroid Build Coastguard Worker             // In this case something has gone disastrously wrong so set up to drop the draw
866*c8dee2aaSAndroid Build Coastguard Worker             // that needed this resource and reduce future pollution of the cache.
867*c8dee2aaSAndroid Build Coastguard Worker             threadSafeCache->remove(key);
868*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
869*c8dee2aaSAndroid Build Coastguard Worker         }
870*c8dee2aaSAndroid Build Coastguard Worker     } else {
871*c8dee2aaSAndroid Build Coastguard Worker         view = threadSafeCache->find(key);
872*c8dee2aaSAndroid Build Coastguard Worker         if (view) {
873*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(view.asTextureProxy());
874*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(view.origin() == kBlurredRRectMaskOrigin);
875*c8dee2aaSAndroid Build Coastguard Worker             return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, m);
876*c8dee2aaSAndroid Build Coastguard Worker         }
877*c8dee2aaSAndroid Build Coastguard Worker 
878*c8dee2aaSAndroid Build Coastguard Worker         view = create_mask_on_cpu(rContext, rrectToDraw, dimensions, xformedSigma);
879*c8dee2aaSAndroid Build Coastguard Worker         if (!view) {
880*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
881*c8dee2aaSAndroid Build Coastguard Worker         }
882*c8dee2aaSAndroid Build Coastguard Worker 
883*c8dee2aaSAndroid Build Coastguard Worker         view = threadSafeCache->add(key, view);
884*c8dee2aaSAndroid Build Coastguard Worker     }
885*c8dee2aaSAndroid Build Coastguard Worker 
886*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(view.asTextureProxy());
887*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(view.origin() == kBlurredRRectMaskOrigin);
888*c8dee2aaSAndroid Build Coastguard Worker     return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, m);
889*c8dee2aaSAndroid Build Coastguard Worker }
890*c8dee2aaSAndroid Build Coastguard Worker 
make_rrect_blur(GrRecordingContext * context,float sigma,float xformedSigma,const SkRRect & srcRRect,const SkRRect & devRRect)891*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<GrFragmentProcessor> make_rrect_blur(GrRecordingContext* context,
892*c8dee2aaSAndroid Build Coastguard Worker                                                             float sigma,
893*c8dee2aaSAndroid Build Coastguard Worker                                                             float xformedSigma,
894*c8dee2aaSAndroid Build Coastguard Worker                                                             const SkRRect& srcRRect,
895*c8dee2aaSAndroid Build Coastguard Worker                                                             const SkRRect& devRRect) {
896*c8dee2aaSAndroid Build Coastguard Worker     SkASSERTF(!SkRRectPriv::IsCircle(devRRect),
897*c8dee2aaSAndroid Build Coastguard Worker               "Unexpected circle. %d\n\t%s\n\t%s",
898*c8dee2aaSAndroid Build Coastguard Worker               SkRRectPriv::IsCircle(srcRRect),
899*c8dee2aaSAndroid Build Coastguard Worker               srcRRect.dumpToString(true).c_str(),
900*c8dee2aaSAndroid Build Coastguard Worker               devRRect.dumpToString(true).c_str());
901*c8dee2aaSAndroid Build Coastguard Worker     SkASSERTF(!devRRect.isRect(),
902*c8dee2aaSAndroid Build Coastguard Worker               "Unexpected rect. %d\n\t%s\n\t%s",
903*c8dee2aaSAndroid Build Coastguard Worker               srcRRect.isRect(),
904*c8dee2aaSAndroid Build Coastguard Worker               srcRRect.dumpToString(true).c_str(),
905*c8dee2aaSAndroid Build Coastguard Worker               devRRect.dumpToString(true).c_str());
906*c8dee2aaSAndroid Build Coastguard Worker 
907*c8dee2aaSAndroid Build Coastguard Worker     // TODO: loosen this up
908*c8dee2aaSAndroid Build Coastguard Worker     if (!SkRRectPriv::IsSimpleCircular(devRRect)) {
909*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
910*c8dee2aaSAndroid Build Coastguard Worker     }
911*c8dee2aaSAndroid Build Coastguard Worker 
912*c8dee2aaSAndroid Build Coastguard Worker     if (skgpu::BlurIsEffectivelyIdentity(xformedSigma)) {
913*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
914*c8dee2aaSAndroid Build Coastguard Worker     }
915*c8dee2aaSAndroid Build Coastguard Worker 
916*c8dee2aaSAndroid Build Coastguard Worker     // Make sure we can successfully ninepatch this rrect -- the blur sigma has to be sufficiently
917*c8dee2aaSAndroid Build Coastguard Worker     // small relative to both the size of the corner radius and the width (and height) of the rrect.
918*c8dee2aaSAndroid Build Coastguard Worker     SkRRect rrectToDraw;
919*c8dee2aaSAndroid Build Coastguard Worker     SkISize dimensions;
920*c8dee2aaSAndroid Build Coastguard Worker     SkScalar ignored[kBlurRRectMaxDivisions];
921*c8dee2aaSAndroid Build Coastguard Worker 
922*c8dee2aaSAndroid Build Coastguard Worker     bool ninePatchable = ComputeBlurredRRectParams(srcRRect,
923*c8dee2aaSAndroid Build Coastguard Worker                                                    devRRect,
924*c8dee2aaSAndroid Build Coastguard Worker                                                    sigma,
925*c8dee2aaSAndroid Build Coastguard Worker                                                    xformedSigma,
926*c8dee2aaSAndroid Build Coastguard Worker                                                    &rrectToDraw,
927*c8dee2aaSAndroid Build Coastguard Worker                                                    &dimensions,
928*c8dee2aaSAndroid Build Coastguard Worker                                                    ignored,
929*c8dee2aaSAndroid Build Coastguard Worker                                                    ignored,
930*c8dee2aaSAndroid Build Coastguard Worker                                                    ignored,
931*c8dee2aaSAndroid Build Coastguard Worker                                                    ignored);
932*c8dee2aaSAndroid Build Coastguard Worker     if (!ninePatchable) {
933*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
934*c8dee2aaSAndroid Build Coastguard Worker     }
935*c8dee2aaSAndroid Build Coastguard Worker 
936*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<GrFragmentProcessor> maskFP =
937*c8dee2aaSAndroid Build Coastguard Worker             find_or_create_rrect_blur_mask_fp(context, rrectToDraw, dimensions, xformedSigma);
938*c8dee2aaSAndroid Build Coastguard Worker     if (!maskFP) {
939*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
940*c8dee2aaSAndroid Build Coastguard Worker     }
941*c8dee2aaSAndroid Build Coastguard Worker 
942*c8dee2aaSAndroid Build Coastguard Worker     static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
943*c8dee2aaSAndroid Build Coastguard Worker         "uniform shader ninePatchFP;"
944*c8dee2aaSAndroid Build Coastguard Worker 
945*c8dee2aaSAndroid Build Coastguard Worker         "uniform half cornerRadius;"
946*c8dee2aaSAndroid Build Coastguard Worker         "uniform float4 proxyRect;"
947*c8dee2aaSAndroid Build Coastguard Worker         "uniform half blurRadius;"
948*c8dee2aaSAndroid Build Coastguard Worker 
949*c8dee2aaSAndroid Build Coastguard Worker         "half4 main(float2 xy) {"
950*c8dee2aaSAndroid Build Coastguard Worker             // Warp the fragment position to the appropriate part of the 9-patch blur texture by
951*c8dee2aaSAndroid Build Coastguard Worker             // snipping out the middle section of the proxy rect.
952*c8dee2aaSAndroid Build Coastguard Worker             "float2 translatedFragPosFloat = sk_FragCoord.xy - proxyRect.LT;"
953*c8dee2aaSAndroid Build Coastguard Worker             "float2 proxyCenter = (proxyRect.RB - proxyRect.LT) * 0.5;"
954*c8dee2aaSAndroid Build Coastguard Worker             "half edgeSize = 2.0 * blurRadius + cornerRadius + 0.5;"
955*c8dee2aaSAndroid Build Coastguard Worker 
956*c8dee2aaSAndroid Build Coastguard Worker             // Position the fragment so that (0, 0) marks the center of the proxy rectangle.
957*c8dee2aaSAndroid Build Coastguard Worker             // Negative coordinates are on the left/top side and positive numbers are on the
958*c8dee2aaSAndroid Build Coastguard Worker             // right/bottom.
959*c8dee2aaSAndroid Build Coastguard Worker             "translatedFragPosFloat -= proxyCenter;"
960*c8dee2aaSAndroid Build Coastguard Worker 
961*c8dee2aaSAndroid Build Coastguard Worker             // Temporarily strip off the fragment's sign. x/y are now strictly increasing as we
962*c8dee2aaSAndroid Build Coastguard Worker             // move away from the center.
963*c8dee2aaSAndroid Build Coastguard Worker             "half2 fragDirection = half2(sign(translatedFragPosFloat));"
964*c8dee2aaSAndroid Build Coastguard Worker             "translatedFragPosFloat = abs(translatedFragPosFloat);"
965*c8dee2aaSAndroid Build Coastguard Worker 
966*c8dee2aaSAndroid Build Coastguard Worker             // Our goal is to snip out the "middle section" of the proxy rect (everything but the
967*c8dee2aaSAndroid Build Coastguard Worker             // edge). We've repositioned our fragment position so that (0, 0) is the centerpoint
968*c8dee2aaSAndroid Build Coastguard Worker             // and x/y are always positive, so we can subtract here and interpret negative results
969*c8dee2aaSAndroid Build Coastguard Worker             // as being within the middle section.
970*c8dee2aaSAndroid Build Coastguard Worker             "half2 translatedFragPosHalf = half2(translatedFragPosFloat - (proxyCenter - edgeSize));"
971*c8dee2aaSAndroid Build Coastguard Worker 
972*c8dee2aaSAndroid Build Coastguard Worker             // Remove the middle section by clamping to zero.
973*c8dee2aaSAndroid Build Coastguard Worker             "translatedFragPosHalf = max(translatedFragPosHalf, 0);"
974*c8dee2aaSAndroid Build Coastguard Worker 
975*c8dee2aaSAndroid Build Coastguard Worker             // Reapply the fragment's sign, so that negative coordinates once again mean left/top
976*c8dee2aaSAndroid Build Coastguard Worker             // side and positive means bottom/right side.
977*c8dee2aaSAndroid Build Coastguard Worker             "translatedFragPosHalf *= fragDirection;"
978*c8dee2aaSAndroid Build Coastguard Worker 
979*c8dee2aaSAndroid Build Coastguard Worker             // Offset the fragment so that (0, 0) marks the upper-left again, instead of the center
980*c8dee2aaSAndroid Build Coastguard Worker             // point.
981*c8dee2aaSAndroid Build Coastguard Worker             "translatedFragPosHalf += half2(edgeSize);"
982*c8dee2aaSAndroid Build Coastguard Worker 
983*c8dee2aaSAndroid Build Coastguard Worker             "half2 proxyDims = half2(2.0 * edgeSize);"
984*c8dee2aaSAndroid Build Coastguard Worker             "half2 texCoord = translatedFragPosHalf / proxyDims;"
985*c8dee2aaSAndroid Build Coastguard Worker 
986*c8dee2aaSAndroid Build Coastguard Worker             "return ninePatchFP.eval(texCoord).aaaa;"
987*c8dee2aaSAndroid Build Coastguard Worker         "}"
988*c8dee2aaSAndroid Build Coastguard Worker     );
989*c8dee2aaSAndroid Build Coastguard Worker 
990*c8dee2aaSAndroid Build Coastguard Worker     float cornerRadius = SkRRectPriv::GetSimpleRadii(devRRect).fX;
991*c8dee2aaSAndroid Build Coastguard Worker     float blurRadius = 3.f * SkScalarCeilToScalar(xformedSigma - 1 / 6.0f);
992*c8dee2aaSAndroid Build Coastguard Worker     SkRect proxyRect = devRRect.getBounds().makeOutset(blurRadius, blurRadius);
993*c8dee2aaSAndroid Build Coastguard Worker 
994*c8dee2aaSAndroid Build Coastguard Worker     auto rrectBlurFP = GrSkSLFP::Make(effect, "RRectBlur", /*inputFP=*/nullptr,
995*c8dee2aaSAndroid Build Coastguard Worker                                       GrSkSLFP::OptFlags::kCompatibleWithCoverageAsAlpha,
996*c8dee2aaSAndroid Build Coastguard Worker                                       "ninePatchFP", GrSkSLFP::IgnoreOptFlags(std::move(maskFP)),
997*c8dee2aaSAndroid Build Coastguard Worker                                       "cornerRadius", cornerRadius,
998*c8dee2aaSAndroid Build Coastguard Worker                                       "proxyRect", proxyRect,
999*c8dee2aaSAndroid Build Coastguard Worker                                       "blurRadius", blurRadius);
1000*c8dee2aaSAndroid Build Coastguard Worker     // Modulate blur with the input color.
1001*c8dee2aaSAndroid Build Coastguard Worker     return GrBlendFragmentProcessor::Make<SkBlendMode::kModulate>(std::move(rrectBlurFP),
1002*c8dee2aaSAndroid Build Coastguard Worker                                                                   /*dst=*/nullptr);
1003*c8dee2aaSAndroid Build Coastguard Worker }
1004*c8dee2aaSAndroid Build Coastguard Worker 
1005*c8dee2aaSAndroid Build Coastguard Worker /**
1006*c8dee2aaSAndroid Build Coastguard Worker  *  Try to directly render the mask filter into the target. Returns true if drawing was
1007*c8dee2aaSAndroid Build Coastguard Worker  *  successful. If false is returned then paint is unmodified.
1008*c8dee2aaSAndroid Build Coastguard Worker  */
direct_filter_mask(GrRecordingContext * context,const SkMaskFilterBase * maskFilter,skgpu::ganesh::SurfaceDrawContext * sdc,GrPaint && paint,const GrClip * clip,const SkMatrix & viewMatrix,const GrStyledShape & shape)1009*c8dee2aaSAndroid Build Coastguard Worker static bool direct_filter_mask(GrRecordingContext* context,
1010*c8dee2aaSAndroid Build Coastguard Worker                                const SkMaskFilterBase* maskFilter,
1011*c8dee2aaSAndroid Build Coastguard Worker                                skgpu::ganesh::SurfaceDrawContext* sdc,
1012*c8dee2aaSAndroid Build Coastguard Worker                                GrPaint&& paint,
1013*c8dee2aaSAndroid Build Coastguard Worker                                const GrClip* clip,
1014*c8dee2aaSAndroid Build Coastguard Worker                                const SkMatrix& viewMatrix,
1015*c8dee2aaSAndroid Build Coastguard Worker                                const GrStyledShape& shape) {
1016*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(sdc);
1017*c8dee2aaSAndroid Build Coastguard Worker     if (maskFilter->type() != SkMaskFilterBase::Type::kBlur) {
1018*c8dee2aaSAndroid Build Coastguard Worker         return false;
1019*c8dee2aaSAndroid Build Coastguard Worker     }
1020*c8dee2aaSAndroid Build Coastguard Worker     auto bmf = static_cast<const SkBlurMaskFilterImpl*>(maskFilter);
1021*c8dee2aaSAndroid Build Coastguard Worker 
1022*c8dee2aaSAndroid Build Coastguard Worker     if (bmf->blurStyle() != kNormal_SkBlurStyle) {
1023*c8dee2aaSAndroid Build Coastguard Worker         return false;
1024*c8dee2aaSAndroid Build Coastguard Worker     }
1025*c8dee2aaSAndroid Build Coastguard Worker 
1026*c8dee2aaSAndroid Build Coastguard Worker     // TODO: we could handle blurred stroked circles
1027*c8dee2aaSAndroid Build Coastguard Worker     if (!shape.style().isSimpleFill()) {
1028*c8dee2aaSAndroid Build Coastguard Worker         return false;
1029*c8dee2aaSAndroid Build Coastguard Worker     }
1030*c8dee2aaSAndroid Build Coastguard Worker 
1031*c8dee2aaSAndroid Build Coastguard Worker     SkScalar xformedSigma = bmf->computeXformedSigma(viewMatrix);
1032*c8dee2aaSAndroid Build Coastguard Worker     if (skgpu::BlurIsEffectivelyIdentity(xformedSigma)) {
1033*c8dee2aaSAndroid Build Coastguard Worker         sdc->drawShape(clip, std::move(paint), GrAA::kYes, viewMatrix, GrStyledShape(shape));
1034*c8dee2aaSAndroid Build Coastguard Worker         return true;
1035*c8dee2aaSAndroid Build Coastguard Worker     }
1036*c8dee2aaSAndroid Build Coastguard Worker 
1037*c8dee2aaSAndroid Build Coastguard Worker     SkRRect srcRRect;
1038*c8dee2aaSAndroid Build Coastguard Worker     bool inverted;
1039*c8dee2aaSAndroid Build Coastguard Worker     if (!shape.asRRect(&srcRRect, &inverted) || inverted) {
1040*c8dee2aaSAndroid Build Coastguard Worker         return false;
1041*c8dee2aaSAndroid Build Coastguard Worker     }
1042*c8dee2aaSAndroid Build Coastguard Worker 
1043*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<GrFragmentProcessor> fp;
1044*c8dee2aaSAndroid Build Coastguard Worker 
1045*c8dee2aaSAndroid Build Coastguard Worker     SkRRect devRRect;
1046*c8dee2aaSAndroid Build Coastguard Worker     bool devRRectIsValid = srcRRect.transform(viewMatrix, &devRRect);
1047*c8dee2aaSAndroid Build Coastguard Worker 
1048*c8dee2aaSAndroid Build Coastguard Worker     bool devRRectIsCircle = devRRectIsValid && SkRRectPriv::IsCircle(devRRect);
1049*c8dee2aaSAndroid Build Coastguard Worker 
1050*c8dee2aaSAndroid Build Coastguard Worker     bool canBeRect = srcRRect.isRect() && viewMatrix.preservesRightAngles();
1051*c8dee2aaSAndroid Build Coastguard Worker     bool canBeCircle = (SkRRectPriv::IsCircle(srcRRect) && viewMatrix.isSimilarity()) ||
1052*c8dee2aaSAndroid Build Coastguard Worker                        devRRectIsCircle;
1053*c8dee2aaSAndroid Build Coastguard Worker 
1054*c8dee2aaSAndroid Build Coastguard Worker     if (canBeRect || canBeCircle) {
1055*c8dee2aaSAndroid Build Coastguard Worker         if (canBeRect) {
1056*c8dee2aaSAndroid Build Coastguard Worker             fp = make_rect_blur(context, *context->priv().caps()->shaderCaps(),
1057*c8dee2aaSAndroid Build Coastguard Worker                                 srcRRect.rect(), viewMatrix, xformedSigma);
1058*c8dee2aaSAndroid Build Coastguard Worker         } else {
1059*c8dee2aaSAndroid Build Coastguard Worker             SkRect devBounds;
1060*c8dee2aaSAndroid Build Coastguard Worker             if (devRRectIsCircle) {
1061*c8dee2aaSAndroid Build Coastguard Worker                 devBounds = devRRect.getBounds();
1062*c8dee2aaSAndroid Build Coastguard Worker             } else {
1063*c8dee2aaSAndroid Build Coastguard Worker                 SkPoint center = {srcRRect.getBounds().centerX(), srcRRect.getBounds().centerY()};
1064*c8dee2aaSAndroid Build Coastguard Worker                 viewMatrix.mapPoints(&center, 1);
1065*c8dee2aaSAndroid Build Coastguard Worker                 SkScalar radius = viewMatrix.mapVector(0, srcRRect.width()/2.f).length();
1066*c8dee2aaSAndroid Build Coastguard Worker                 devBounds = {center.x() - radius,
1067*c8dee2aaSAndroid Build Coastguard Worker                              center.y() - radius,
1068*c8dee2aaSAndroid Build Coastguard Worker                              center.x() + radius,
1069*c8dee2aaSAndroid Build Coastguard Worker                              center.y() + radius};
1070*c8dee2aaSAndroid Build Coastguard Worker             }
1071*c8dee2aaSAndroid Build Coastguard Worker             fp = make_circle_blur(context, devBounds, xformedSigma);
1072*c8dee2aaSAndroid Build Coastguard Worker         }
1073*c8dee2aaSAndroid Build Coastguard Worker 
1074*c8dee2aaSAndroid Build Coastguard Worker         if (!fp) {
1075*c8dee2aaSAndroid Build Coastguard Worker             return false;
1076*c8dee2aaSAndroid Build Coastguard Worker         }
1077*c8dee2aaSAndroid Build Coastguard Worker 
1078*c8dee2aaSAndroid Build Coastguard Worker         SkRect srcProxyRect = srcRRect.rect();
1079*c8dee2aaSAndroid Build Coastguard Worker         // Determine how much to outset the src rect to ensure we hit pixels within three sigma.
1080*c8dee2aaSAndroid Build Coastguard Worker         SkScalar outsetX = 3.0f*xformedSigma;
1081*c8dee2aaSAndroid Build Coastguard Worker         SkScalar outsetY = 3.0f*xformedSigma;
1082*c8dee2aaSAndroid Build Coastguard Worker         if (viewMatrix.isScaleTranslate()) {
1083*c8dee2aaSAndroid Build Coastguard Worker             outsetX /= SkScalarAbs(viewMatrix.getScaleX());
1084*c8dee2aaSAndroid Build Coastguard Worker             outsetY /= SkScalarAbs(viewMatrix.getScaleY());
1085*c8dee2aaSAndroid Build Coastguard Worker         } else {
1086*c8dee2aaSAndroid Build Coastguard Worker             SkSize scale;
1087*c8dee2aaSAndroid Build Coastguard Worker             if (!viewMatrix.decomposeScale(&scale, nullptr)) {
1088*c8dee2aaSAndroid Build Coastguard Worker                 return false;
1089*c8dee2aaSAndroid Build Coastguard Worker             }
1090*c8dee2aaSAndroid Build Coastguard Worker             outsetX /= scale.width();
1091*c8dee2aaSAndroid Build Coastguard Worker             outsetY /= scale.height();
1092*c8dee2aaSAndroid Build Coastguard Worker         }
1093*c8dee2aaSAndroid Build Coastguard Worker         srcProxyRect.outset(outsetX, outsetY);
1094*c8dee2aaSAndroid Build Coastguard Worker 
1095*c8dee2aaSAndroid Build Coastguard Worker         paint.setCoverageFragmentProcessor(std::move(fp));
1096*c8dee2aaSAndroid Build Coastguard Worker         sdc->drawRect(clip, std::move(paint), GrAA::kNo, viewMatrix, srcProxyRect);
1097*c8dee2aaSAndroid Build Coastguard Worker         return true;
1098*c8dee2aaSAndroid Build Coastguard Worker     }
1099*c8dee2aaSAndroid Build Coastguard Worker     if (!viewMatrix.isScaleTranslate()) {
1100*c8dee2aaSAndroid Build Coastguard Worker         return false;
1101*c8dee2aaSAndroid Build Coastguard Worker     }
1102*c8dee2aaSAndroid Build Coastguard Worker     if (!devRRectIsValid || !SkRRectPriv::AllCornersCircular(devRRect)) {
1103*c8dee2aaSAndroid Build Coastguard Worker         return false;
1104*c8dee2aaSAndroid Build Coastguard Worker     }
1105*c8dee2aaSAndroid Build Coastguard Worker 
1106*c8dee2aaSAndroid Build Coastguard Worker     fp = make_rrect_blur(context, bmf->sigma(), xformedSigma, srcRRect, devRRect);
1107*c8dee2aaSAndroid Build Coastguard Worker     if (!fp) {
1108*c8dee2aaSAndroid Build Coastguard Worker         return false;
1109*c8dee2aaSAndroid Build Coastguard Worker     }
1110*c8dee2aaSAndroid Build Coastguard Worker 
1111*c8dee2aaSAndroid Build Coastguard Worker     if (!bmf->ignoreXform()) {
1112*c8dee2aaSAndroid Build Coastguard Worker         SkRect srcProxyRect = srcRRect.rect();
1113*c8dee2aaSAndroid Build Coastguard Worker         srcProxyRect.outset(3.0f*bmf->sigma(), 3.0f*bmf->sigma());
1114*c8dee2aaSAndroid Build Coastguard Worker         paint.setCoverageFragmentProcessor(std::move(fp));
1115*c8dee2aaSAndroid Build Coastguard Worker         sdc->drawRect(clip, std::move(paint), GrAA::kNo, viewMatrix, srcProxyRect);
1116*c8dee2aaSAndroid Build Coastguard Worker     } else {
1117*c8dee2aaSAndroid Build Coastguard Worker         SkMatrix inverse;
1118*c8dee2aaSAndroid Build Coastguard Worker         if (!viewMatrix.invert(&inverse)) {
1119*c8dee2aaSAndroid Build Coastguard Worker             return false;
1120*c8dee2aaSAndroid Build Coastguard Worker         }
1121*c8dee2aaSAndroid Build Coastguard Worker 
1122*c8dee2aaSAndroid Build Coastguard Worker         SkIRect proxyBounds;
1123*c8dee2aaSAndroid Build Coastguard Worker         float extra=3.f*SkScalarCeilToScalar(xformedSigma-1/6.0f);
1124*c8dee2aaSAndroid Build Coastguard Worker         devRRect.rect().makeOutset(extra, extra).roundOut(&proxyBounds);
1125*c8dee2aaSAndroid Build Coastguard Worker 
1126*c8dee2aaSAndroid Build Coastguard Worker         paint.setCoverageFragmentProcessor(std::move(fp));
1127*c8dee2aaSAndroid Build Coastguard Worker         sdc->fillPixelsWithLocalMatrix(clip, std::move(paint), proxyBounds, inverse);
1128*c8dee2aaSAndroid Build Coastguard Worker     }
1129*c8dee2aaSAndroid Build Coastguard Worker 
1130*c8dee2aaSAndroid Build Coastguard Worker     return true;
1131*c8dee2aaSAndroid Build Coastguard Worker }
1132*c8dee2aaSAndroid Build Coastguard Worker 
1133*c8dee2aaSAndroid Build Coastguard Worker // The key and clip-bounds are computed together because the caching decision can impact the
1134*c8dee2aaSAndroid Build Coastguard Worker // clip-bound - since we only cache un-clipped masks the clip can be removed entirely.
1135*c8dee2aaSAndroid Build Coastguard Worker // A 'false' return value indicates that the shape is known to be clipped away.
compute_key_and_clip_bounds(skgpu::UniqueKey * maskKey,SkIRect * boundsForClip,const GrCaps * caps,const SkMatrix & viewMatrix,bool inverseFilled,const SkMaskFilterBase * maskFilter,const GrStyledShape & shape,const SkIRect & unclippedDevShapeBounds,const SkIRect & devClipBounds)1136*c8dee2aaSAndroid Build Coastguard Worker static bool compute_key_and_clip_bounds(skgpu::UniqueKey* maskKey,
1137*c8dee2aaSAndroid Build Coastguard Worker                                         SkIRect* boundsForClip,
1138*c8dee2aaSAndroid Build Coastguard Worker                                         const GrCaps* caps,
1139*c8dee2aaSAndroid Build Coastguard Worker                                         const SkMatrix& viewMatrix,
1140*c8dee2aaSAndroid Build Coastguard Worker                                         bool inverseFilled,
1141*c8dee2aaSAndroid Build Coastguard Worker                                         const SkMaskFilterBase* maskFilter,
1142*c8dee2aaSAndroid Build Coastguard Worker                                         const GrStyledShape& shape,
1143*c8dee2aaSAndroid Build Coastguard Worker                                         const SkIRect& unclippedDevShapeBounds,
1144*c8dee2aaSAndroid Build Coastguard Worker                                         const SkIRect& devClipBounds) {
1145*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(maskFilter);
1146*c8dee2aaSAndroid Build Coastguard Worker     *boundsForClip = devClipBounds;
1147*c8dee2aaSAndroid Build Coastguard Worker 
1148*c8dee2aaSAndroid Build Coastguard Worker #ifndef SK_DISABLE_MASKFILTERED_MASK_CACHING
1149*c8dee2aaSAndroid Build Coastguard Worker     // To prevent overloading the cache with entries during animations we limit the cache of masks
1150*c8dee2aaSAndroid Build Coastguard Worker     // to cases where the matrix preserves axis alignment.
1151*c8dee2aaSAndroid Build Coastguard Worker     bool useCache = !inverseFilled && viewMatrix.preservesAxisAlignment() &&
1152*c8dee2aaSAndroid Build Coastguard Worker                     shape.hasUnstyledKey() && as_MFB(maskFilter)->asABlur(nullptr);
1153*c8dee2aaSAndroid Build Coastguard Worker 
1154*c8dee2aaSAndroid Build Coastguard Worker     if (useCache) {
1155*c8dee2aaSAndroid Build Coastguard Worker         SkIRect clippedMaskRect, unClippedMaskRect;
1156*c8dee2aaSAndroid Build Coastguard Worker         can_filter_mask(maskFilter, shape, unclippedDevShapeBounds, devClipBounds,
1157*c8dee2aaSAndroid Build Coastguard Worker                         viewMatrix, &clippedMaskRect);
1158*c8dee2aaSAndroid Build Coastguard Worker         if (clippedMaskRect.isEmpty()) {
1159*c8dee2aaSAndroid Build Coastguard Worker             return false;
1160*c8dee2aaSAndroid Build Coastguard Worker         }
1161*c8dee2aaSAndroid Build Coastguard Worker         can_filter_mask(maskFilter, shape, unclippedDevShapeBounds, unclippedDevShapeBounds,
1162*c8dee2aaSAndroid Build Coastguard Worker                         viewMatrix, &unClippedMaskRect);
1163*c8dee2aaSAndroid Build Coastguard Worker 
1164*c8dee2aaSAndroid Build Coastguard Worker         // Use the cache only if >50% of the filtered mask is visible.
1165*c8dee2aaSAndroid Build Coastguard Worker         int unclippedWidth = unClippedMaskRect.width();
1166*c8dee2aaSAndroid Build Coastguard Worker         int unclippedHeight = unClippedMaskRect.height();
1167*c8dee2aaSAndroid Build Coastguard Worker         int64_t unclippedArea = sk_64_mul(unclippedWidth, unclippedHeight);
1168*c8dee2aaSAndroid Build Coastguard Worker         int64_t clippedArea = sk_64_mul(clippedMaskRect.width(), clippedMaskRect.height());
1169*c8dee2aaSAndroid Build Coastguard Worker         int maxTextureSize = caps->maxTextureSize();
1170*c8dee2aaSAndroid Build Coastguard Worker         if (unclippedArea > 2 * clippedArea || unclippedWidth > maxTextureSize ||
1171*c8dee2aaSAndroid Build Coastguard Worker             unclippedHeight > maxTextureSize) {
1172*c8dee2aaSAndroid Build Coastguard Worker             useCache = false;
1173*c8dee2aaSAndroid Build Coastguard Worker         } else {
1174*c8dee2aaSAndroid Build Coastguard Worker             // Make the clip not affect the mask
1175*c8dee2aaSAndroid Build Coastguard Worker             *boundsForClip = unclippedDevShapeBounds;
1176*c8dee2aaSAndroid Build Coastguard Worker         }
1177*c8dee2aaSAndroid Build Coastguard Worker     }
1178*c8dee2aaSAndroid Build Coastguard Worker 
1179*c8dee2aaSAndroid Build Coastguard Worker     if (useCache) {
1180*c8dee2aaSAndroid Build Coastguard Worker         static const skgpu::UniqueKey::Domain kDomain = skgpu::UniqueKey::GenerateDomain();
1181*c8dee2aaSAndroid Build Coastguard Worker         skgpu::UniqueKey::Builder builder(maskKey, kDomain, 5 + 2 + shape.unstyledKeySize(),
1182*c8dee2aaSAndroid Build Coastguard Worker                                           "Mask Filtered Masks");
1183*c8dee2aaSAndroid Build Coastguard Worker 
1184*c8dee2aaSAndroid Build Coastguard Worker         // We require the upper left 2x2 of the matrix to match exactly for a cache hit.
1185*c8dee2aaSAndroid Build Coastguard Worker         SkScalar sx = viewMatrix.get(SkMatrix::kMScaleX);
1186*c8dee2aaSAndroid Build Coastguard Worker         SkScalar sy = viewMatrix.get(SkMatrix::kMScaleY);
1187*c8dee2aaSAndroid Build Coastguard Worker         SkScalar kx = viewMatrix.get(SkMatrix::kMSkewX);
1188*c8dee2aaSAndroid Build Coastguard Worker         SkScalar ky = viewMatrix.get(SkMatrix::kMSkewY);
1189*c8dee2aaSAndroid Build Coastguard Worker         SkScalar tx = viewMatrix.get(SkMatrix::kMTransX);
1190*c8dee2aaSAndroid Build Coastguard Worker         SkScalar ty = viewMatrix.get(SkMatrix::kMTransY);
1191*c8dee2aaSAndroid Build Coastguard Worker         // Allow 8 bits each in x and y of subpixel positioning. But, note that we're allowing
1192*c8dee2aaSAndroid Build Coastguard Worker         // reuse for integer translations.
1193*c8dee2aaSAndroid Build Coastguard Worker         SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00;
1194*c8dee2aaSAndroid Build Coastguard Worker         SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00;
1195*c8dee2aaSAndroid Build Coastguard Worker 
1196*c8dee2aaSAndroid Build Coastguard Worker         builder[0] = SkFloat2Bits(sx);
1197*c8dee2aaSAndroid Build Coastguard Worker         builder[1] = SkFloat2Bits(sy);
1198*c8dee2aaSAndroid Build Coastguard Worker         builder[2] = SkFloat2Bits(kx);
1199*c8dee2aaSAndroid Build Coastguard Worker         builder[3] = SkFloat2Bits(ky);
1200*c8dee2aaSAndroid Build Coastguard Worker         // Distinguish between hairline and filled paths. For hairlines, we also need to include
1201*c8dee2aaSAndroid Build Coastguard Worker         // the cap. (SW grows hairlines by 0.5 pixel with round and square caps). Note that
1202*c8dee2aaSAndroid Build Coastguard Worker         // stroke-and-fill of hairlines is turned into pure fill by SkStrokeRec, so this covers
1203*c8dee2aaSAndroid Build Coastguard Worker         // all cases we might see.
1204*c8dee2aaSAndroid Build Coastguard Worker         uint32_t styleBits = shape.style().isSimpleHairline()
1205*c8dee2aaSAndroid Build Coastguard Worker                                     ? ((shape.style().strokeRec().getCap() << 1) | 1)
1206*c8dee2aaSAndroid Build Coastguard Worker                                     : 0;
1207*c8dee2aaSAndroid Build Coastguard Worker         builder[4] = fracX | (fracY >> 8) | (styleBits << 16);
1208*c8dee2aaSAndroid Build Coastguard Worker 
1209*c8dee2aaSAndroid Build Coastguard Worker         SkMaskFilterBase::BlurRec rec;
1210*c8dee2aaSAndroid Build Coastguard Worker         SkAssertResult(as_MFB(maskFilter)->asABlur(&rec));
1211*c8dee2aaSAndroid Build Coastguard Worker 
1212*c8dee2aaSAndroid Build Coastguard Worker         builder[5] = rec.fStyle;  // TODO: we could put this with the other style bits
1213*c8dee2aaSAndroid Build Coastguard Worker         builder[6] = SkFloat2Bits(rec.fSigma);
1214*c8dee2aaSAndroid Build Coastguard Worker         shape.writeUnstyledKey(&builder[7]);
1215*c8dee2aaSAndroid Build Coastguard Worker     }
1216*c8dee2aaSAndroid Build Coastguard Worker #endif
1217*c8dee2aaSAndroid Build Coastguard Worker 
1218*c8dee2aaSAndroid Build Coastguard Worker     return true;
1219*c8dee2aaSAndroid Build Coastguard Worker }
1220*c8dee2aaSAndroid Build Coastguard Worker 
1221*c8dee2aaSAndroid Build Coastguard Worker /**
1222*c8dee2aaSAndroid Build Coastguard Worker  * This function is used to implement filters that require an explicit src mask. It should only
1223*c8dee2aaSAndroid Build Coastguard Worker  * be called if can_filter_mask returned true and the maskRect param should be the output from
1224*c8dee2aaSAndroid Build Coastguard Worker  * that call.
1225*c8dee2aaSAndroid Build Coastguard Worker  * Implementations are free to get the GrContext from the src texture in order to create
1226*c8dee2aaSAndroid Build Coastguard Worker  * additional textures and perform multiple passes.
1227*c8dee2aaSAndroid Build Coastguard Worker  */
filter_mask(GrRecordingContext * context,const SkMaskFilterBase * maskFilter,GrSurfaceProxyView srcView,GrColorType srcColorType,SkAlphaType srcAlphaType,const SkMatrix & ctm,const SkIRect & maskRect)1228*c8dee2aaSAndroid Build Coastguard Worker static GrSurfaceProxyView filter_mask(GrRecordingContext* context,
1229*c8dee2aaSAndroid Build Coastguard Worker                                       const SkMaskFilterBase* maskFilter,
1230*c8dee2aaSAndroid Build Coastguard Worker                                       GrSurfaceProxyView srcView,
1231*c8dee2aaSAndroid Build Coastguard Worker                                       GrColorType srcColorType,
1232*c8dee2aaSAndroid Build Coastguard Worker                                       SkAlphaType srcAlphaType,
1233*c8dee2aaSAndroid Build Coastguard Worker                                       const SkMatrix& ctm,
1234*c8dee2aaSAndroid Build Coastguard Worker                                       const SkIRect& maskRect) {
1235*c8dee2aaSAndroid Build Coastguard Worker     if (maskFilter->type() != SkMaskFilterBase::Type::kBlur) {
1236*c8dee2aaSAndroid Build Coastguard Worker         return {};
1237*c8dee2aaSAndroid Build Coastguard Worker     }
1238*c8dee2aaSAndroid Build Coastguard Worker     auto bmf = static_cast<const SkBlurMaskFilterImpl*>(maskFilter);
1239*c8dee2aaSAndroid Build Coastguard Worker     // 'maskRect' isn't snapped to the UL corner but the mask in 'src' is.
1240*c8dee2aaSAndroid Build Coastguard Worker     const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height());
1241*c8dee2aaSAndroid Build Coastguard Worker 
1242*c8dee2aaSAndroid Build Coastguard Worker     SkScalar xformedSigma = bmf->computeXformedSigma(ctm);
1243*c8dee2aaSAndroid Build Coastguard Worker 
1244*c8dee2aaSAndroid Build Coastguard Worker     // If we're doing a normal blur, we can clobber the pathTexture in the
1245*c8dee2aaSAndroid Build Coastguard Worker     // gaussianBlur.  Otherwise, we need to save it for later compositing.
1246*c8dee2aaSAndroid Build Coastguard Worker     bool isNormalBlur = (kNormal_SkBlurStyle == bmf->blurStyle());
1247*c8dee2aaSAndroid Build Coastguard Worker     auto srcBounds = SkIRect::MakeSize(srcView.proxy()->dimensions());
1248*c8dee2aaSAndroid Build Coastguard Worker     auto surfaceDrawContext = GaussianBlur(context,
1249*c8dee2aaSAndroid Build Coastguard Worker                                             srcView,
1250*c8dee2aaSAndroid Build Coastguard Worker                                             srcColorType,
1251*c8dee2aaSAndroid Build Coastguard Worker                                             srcAlphaType,
1252*c8dee2aaSAndroid Build Coastguard Worker                                             nullptr,
1253*c8dee2aaSAndroid Build Coastguard Worker                                             clipRect,
1254*c8dee2aaSAndroid Build Coastguard Worker                                             srcBounds,
1255*c8dee2aaSAndroid Build Coastguard Worker                                             xformedSigma,
1256*c8dee2aaSAndroid Build Coastguard Worker                                             xformedSigma,
1257*c8dee2aaSAndroid Build Coastguard Worker                                             SkTileMode::kClamp);
1258*c8dee2aaSAndroid Build Coastguard Worker     if (!surfaceDrawContext || !surfaceDrawContext->asTextureProxy()) {
1259*c8dee2aaSAndroid Build Coastguard Worker         return {};
1260*c8dee2aaSAndroid Build Coastguard Worker     }
1261*c8dee2aaSAndroid Build Coastguard Worker 
1262*c8dee2aaSAndroid Build Coastguard Worker     if (!isNormalBlur) {
1263*c8dee2aaSAndroid Build Coastguard Worker         GrPaint paint;
1264*c8dee2aaSAndroid Build Coastguard Worker         // Blend pathTexture over blurTexture.
1265*c8dee2aaSAndroid Build Coastguard Worker         paint.setCoverageFragmentProcessor(GrTextureEffect::Make(std::move(srcView), srcAlphaType));
1266*c8dee2aaSAndroid Build Coastguard Worker         if (kInner_SkBlurStyle == bmf->blurStyle()) {
1267*c8dee2aaSAndroid Build Coastguard Worker             // inner:  dst = dst * src
1268*c8dee2aaSAndroid Build Coastguard Worker             paint.setCoverageSetOpXPFactory(SkRegion::kIntersect_Op);
1269*c8dee2aaSAndroid Build Coastguard Worker         } else if (kSolid_SkBlurStyle == bmf->blurStyle()) {
1270*c8dee2aaSAndroid Build Coastguard Worker             // solid:  dst = src + dst - src * dst
1271*c8dee2aaSAndroid Build Coastguard Worker             //             = src + (1 - src) * dst
1272*c8dee2aaSAndroid Build Coastguard Worker             paint.setCoverageSetOpXPFactory(SkRegion::kUnion_Op);
1273*c8dee2aaSAndroid Build Coastguard Worker         } else if (kOuter_SkBlurStyle == bmf->blurStyle()) {
1274*c8dee2aaSAndroid Build Coastguard Worker             // outer:  dst = dst * (1 - src)
1275*c8dee2aaSAndroid Build Coastguard Worker             //             = 0 * src + (1 - src) * dst
1276*c8dee2aaSAndroid Build Coastguard Worker             paint.setCoverageSetOpXPFactory(SkRegion::kDifference_Op);
1277*c8dee2aaSAndroid Build Coastguard Worker         } else {
1278*c8dee2aaSAndroid Build Coastguard Worker             paint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
1279*c8dee2aaSAndroid Build Coastguard Worker         }
1280*c8dee2aaSAndroid Build Coastguard Worker 
1281*c8dee2aaSAndroid Build Coastguard Worker         surfaceDrawContext->fillPixelsWithLocalMatrix(nullptr, std::move(paint), clipRect,
1282*c8dee2aaSAndroid Build Coastguard Worker                                                       SkMatrix::I());
1283*c8dee2aaSAndroid Build Coastguard Worker     }
1284*c8dee2aaSAndroid Build Coastguard Worker 
1285*c8dee2aaSAndroid Build Coastguard Worker     return surfaceDrawContext->readSurfaceView();
1286*c8dee2aaSAndroid Build Coastguard Worker }
1287*c8dee2aaSAndroid Build Coastguard Worker 
hw_create_filtered_mask(GrDirectContext * dContext,skgpu::ganesh::SurfaceDrawContext * sdc,const SkMatrix & viewMatrix,const GrStyledShape & shape,const SkMaskFilterBase * filter,const SkIRect & unclippedDevShapeBounds,const SkIRect & clipBounds,SkIRect * maskRect,skgpu::UniqueKey * key)1288*c8dee2aaSAndroid Build Coastguard Worker static GrSurfaceProxyView hw_create_filtered_mask(GrDirectContext* dContext,
1289*c8dee2aaSAndroid Build Coastguard Worker                                                   skgpu::ganesh::SurfaceDrawContext* sdc,
1290*c8dee2aaSAndroid Build Coastguard Worker                                                   const SkMatrix& viewMatrix,
1291*c8dee2aaSAndroid Build Coastguard Worker                                                   const GrStyledShape& shape,
1292*c8dee2aaSAndroid Build Coastguard Worker                                                   const SkMaskFilterBase* filter,
1293*c8dee2aaSAndroid Build Coastguard Worker                                                   const SkIRect& unclippedDevShapeBounds,
1294*c8dee2aaSAndroid Build Coastguard Worker                                                   const SkIRect& clipBounds,
1295*c8dee2aaSAndroid Build Coastguard Worker                                                   SkIRect* maskRect,
1296*c8dee2aaSAndroid Build Coastguard Worker                                                   skgpu::UniqueKey* key) {
1297*c8dee2aaSAndroid Build Coastguard Worker     if (!can_filter_mask(filter, shape, unclippedDevShapeBounds, clipBounds, viewMatrix,
1298*c8dee2aaSAndroid Build Coastguard Worker                          maskRect)) {
1299*c8dee2aaSAndroid Build Coastguard Worker         return {};
1300*c8dee2aaSAndroid Build Coastguard Worker     }
1301*c8dee2aaSAndroid Build Coastguard Worker 
1302*c8dee2aaSAndroid Build Coastguard Worker     if (clip_bounds_quick_reject(clipBounds, *maskRect)) {
1303*c8dee2aaSAndroid Build Coastguard Worker         // clipped out
1304*c8dee2aaSAndroid Build Coastguard Worker         return {};
1305*c8dee2aaSAndroid Build Coastguard Worker     }
1306*c8dee2aaSAndroid Build Coastguard Worker 
1307*c8dee2aaSAndroid Build Coastguard Worker     auto threadSafeCache = dContext->priv().threadSafeCache();
1308*c8dee2aaSAndroid Build Coastguard Worker 
1309*c8dee2aaSAndroid Build Coastguard Worker     GrSurfaceProxyView lazyView;
1310*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<GrThreadSafeCache::Trampoline> trampoline;
1311*c8dee2aaSAndroid Build Coastguard Worker 
1312*c8dee2aaSAndroid Build Coastguard Worker     if (key->isValid()) {
1313*c8dee2aaSAndroid Build Coastguard Worker         // In this case, we want GPU-filtered masks to have priority over SW-generated ones so
1314*c8dee2aaSAndroid Build Coastguard Worker         // we pre-emptively add a lazy-view to the cache and fill it in later.
1315*c8dee2aaSAndroid Build Coastguard Worker         std::tie(lazyView, trampoline) = GrThreadSafeCache::CreateLazyView(
1316*c8dee2aaSAndroid Build Coastguard Worker                 dContext, GrColorType::kAlpha_8, maskRect->size(),
1317*c8dee2aaSAndroid Build Coastguard Worker                 kMaskOrigin, SkBackingFit::kApprox);
1318*c8dee2aaSAndroid Build Coastguard Worker         if (!lazyView) {
1319*c8dee2aaSAndroid Build Coastguard Worker             return {}; // fall back to a SW-created mask - 'create_mask_GPU' probably won't succeed
1320*c8dee2aaSAndroid Build Coastguard Worker         }
1321*c8dee2aaSAndroid Build Coastguard Worker 
1322*c8dee2aaSAndroid Build Coastguard Worker         key->setCustomData(create_data(*maskRect, unclippedDevShapeBounds));
1323*c8dee2aaSAndroid Build Coastguard Worker         auto [cachedView, data] = threadSafeCache->findOrAddWithData(*key, lazyView);
1324*c8dee2aaSAndroid Build Coastguard Worker         if (cachedView != lazyView) {
1325*c8dee2aaSAndroid Build Coastguard Worker             // In this case, the gpu-thread lost out to a recording thread - use its result.
1326*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(data);
1327*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(cachedView.asTextureProxy());
1328*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(cachedView.origin() == kMaskOrigin);
1329*c8dee2aaSAndroid Build Coastguard Worker 
1330*c8dee2aaSAndroid Build Coastguard Worker             *maskRect = extract_draw_rect_from_data(data.get(), unclippedDevShapeBounds);
1331*c8dee2aaSAndroid Build Coastguard Worker             return cachedView;
1332*c8dee2aaSAndroid Build Coastguard Worker         }
1333*c8dee2aaSAndroid Build Coastguard Worker     }
1334*c8dee2aaSAndroid Build Coastguard Worker 
1335*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<skgpu::ganesh::SurfaceDrawContext> maskSDC(
1336*c8dee2aaSAndroid Build Coastguard Worker             create_mask_GPU(dContext, *maskRect, viewMatrix, shape, sdc->numSamples()));
1337*c8dee2aaSAndroid Build Coastguard Worker     if (!maskSDC) {
1338*c8dee2aaSAndroid Build Coastguard Worker         if (key->isValid()) {
1339*c8dee2aaSAndroid Build Coastguard Worker             // It is very unlikely that 'create_mask_GPU' will fail after 'CreateLazyView'
1340*c8dee2aaSAndroid Build Coastguard Worker             // succeeded but, if it does, remove the lazy-view from the cache and fallback to
1341*c8dee2aaSAndroid Build Coastguard Worker             // a SW-created mask. Note that any recording threads that glommed onto the
1342*c8dee2aaSAndroid Build Coastguard Worker             // lazy-view will have to, later, drop those draws.
1343*c8dee2aaSAndroid Build Coastguard Worker             threadSafeCache->remove(*key);
1344*c8dee2aaSAndroid Build Coastguard Worker         }
1345*c8dee2aaSAndroid Build Coastguard Worker         return {};
1346*c8dee2aaSAndroid Build Coastguard Worker     }
1347*c8dee2aaSAndroid Build Coastguard Worker 
1348*c8dee2aaSAndroid Build Coastguard Worker     auto filteredMaskView = filter_mask(dContext, filter,
1349*c8dee2aaSAndroid Build Coastguard Worker                                                   maskSDC->readSurfaceView(),
1350*c8dee2aaSAndroid Build Coastguard Worker                                                   maskSDC->colorInfo().colorType(),
1351*c8dee2aaSAndroid Build Coastguard Worker                                                   maskSDC->colorInfo().alphaType(),
1352*c8dee2aaSAndroid Build Coastguard Worker                                                   viewMatrix,
1353*c8dee2aaSAndroid Build Coastguard Worker                                                   *maskRect);
1354*c8dee2aaSAndroid Build Coastguard Worker     if (!filteredMaskView) {
1355*c8dee2aaSAndroid Build Coastguard Worker         if (key->isValid()) {
1356*c8dee2aaSAndroid Build Coastguard Worker             // Remove the lazy-view from the cache and fallback to a SW-created mask. Note that
1357*c8dee2aaSAndroid Build Coastguard Worker             // any recording threads that glommed onto the lazy-view will have to, later, drop
1358*c8dee2aaSAndroid Build Coastguard Worker             // those draws.
1359*c8dee2aaSAndroid Build Coastguard Worker             threadSafeCache->remove(*key);
1360*c8dee2aaSAndroid Build Coastguard Worker         }
1361*c8dee2aaSAndroid Build Coastguard Worker         return {};
1362*c8dee2aaSAndroid Build Coastguard Worker     }
1363*c8dee2aaSAndroid Build Coastguard Worker 
1364*c8dee2aaSAndroid Build Coastguard Worker     if (key->isValid()) {
1365*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(filteredMaskView.dimensions() == lazyView.dimensions());
1366*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(filteredMaskView.swizzle() == lazyView.swizzle());
1367*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(filteredMaskView.origin() == lazyView.origin());
1368*c8dee2aaSAndroid Build Coastguard Worker 
1369*c8dee2aaSAndroid Build Coastguard Worker         trampoline->fProxy = filteredMaskView.asTextureProxyRef();
1370*c8dee2aaSAndroid Build Coastguard Worker         return lazyView;
1371*c8dee2aaSAndroid Build Coastguard Worker     }
1372*c8dee2aaSAndroid Build Coastguard Worker 
1373*c8dee2aaSAndroid Build Coastguard Worker     return filteredMaskView;
1374*c8dee2aaSAndroid Build Coastguard Worker }
1375*c8dee2aaSAndroid Build Coastguard Worker 
draw_shape_with_mask_filter(GrRecordingContext * rContext,skgpu::ganesh::SurfaceDrawContext * sdc,const GrClip * clip,GrPaint && paint,const SkMatrix & viewMatrix,const SkMaskFilterBase * maskFilter,const GrStyledShape & origShape)1376*c8dee2aaSAndroid Build Coastguard Worker static void draw_shape_with_mask_filter(GrRecordingContext* rContext,
1377*c8dee2aaSAndroid Build Coastguard Worker                                         skgpu::ganesh::SurfaceDrawContext* sdc,
1378*c8dee2aaSAndroid Build Coastguard Worker                                         const GrClip* clip,
1379*c8dee2aaSAndroid Build Coastguard Worker                                         GrPaint&& paint,
1380*c8dee2aaSAndroid Build Coastguard Worker                                         const SkMatrix& viewMatrix,
1381*c8dee2aaSAndroid Build Coastguard Worker                                         const SkMaskFilterBase* maskFilter,
1382*c8dee2aaSAndroid Build Coastguard Worker                                         const GrStyledShape& origShape) {
1383*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(maskFilter);
1384*c8dee2aaSAndroid Build Coastguard Worker 
1385*c8dee2aaSAndroid Build Coastguard Worker     const GrStyledShape* shape = &origShape;
1386*c8dee2aaSAndroid Build Coastguard Worker     SkTLazy<GrStyledShape> tmpShape;
1387*c8dee2aaSAndroid Build Coastguard Worker 
1388*c8dee2aaSAndroid Build Coastguard Worker     if (origShape.style().applies()) {
1389*c8dee2aaSAndroid Build Coastguard Worker         SkScalar styleScale =  GrStyle::MatrixToScaleFactor(viewMatrix);
1390*c8dee2aaSAndroid Build Coastguard Worker         if (styleScale == 0) {
1391*c8dee2aaSAndroid Build Coastguard Worker             return;
1392*c8dee2aaSAndroid Build Coastguard Worker         }
1393*c8dee2aaSAndroid Build Coastguard Worker 
1394*c8dee2aaSAndroid Build Coastguard Worker         tmpShape.init(origShape.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, styleScale));
1395*c8dee2aaSAndroid Build Coastguard Worker         if (tmpShape->isEmpty()) {
1396*c8dee2aaSAndroid Build Coastguard Worker             return;
1397*c8dee2aaSAndroid Build Coastguard Worker         }
1398*c8dee2aaSAndroid Build Coastguard Worker 
1399*c8dee2aaSAndroid Build Coastguard Worker         shape = tmpShape.get();
1400*c8dee2aaSAndroid Build Coastguard Worker     }
1401*c8dee2aaSAndroid Build Coastguard Worker 
1402*c8dee2aaSAndroid Build Coastguard Worker     if (direct_filter_mask(rContext, maskFilter, sdc, std::move(paint), clip, viewMatrix, *shape)) {
1403*c8dee2aaSAndroid Build Coastguard Worker         // the mask filter was able to draw itself directly, so there's nothing
1404*c8dee2aaSAndroid Build Coastguard Worker         // left to do.
1405*c8dee2aaSAndroid Build Coastguard Worker         return;
1406*c8dee2aaSAndroid Build Coastguard Worker     }
1407*c8dee2aaSAndroid Build Coastguard Worker     assert_alive(paint);
1408*c8dee2aaSAndroid Build Coastguard Worker 
1409*c8dee2aaSAndroid Build Coastguard Worker     // If the path is hairline, ignore inverse fill.
1410*c8dee2aaSAndroid Build Coastguard Worker     bool inverseFilled = shape->inverseFilled() &&
1411*c8dee2aaSAndroid Build Coastguard Worker                          !GrIsStrokeHairlineOrEquivalent(shape->style(), viewMatrix, nullptr);
1412*c8dee2aaSAndroid Build Coastguard Worker 
1413*c8dee2aaSAndroid Build Coastguard Worker     SkIRect unclippedDevShapeBounds, devClipBounds;
1414*c8dee2aaSAndroid Build Coastguard Worker     if (!get_shape_and_clip_bounds(sdc, clip, *shape, viewMatrix,
1415*c8dee2aaSAndroid Build Coastguard Worker                                    &unclippedDevShapeBounds, &devClipBounds)) {
1416*c8dee2aaSAndroid Build Coastguard Worker         // TODO: just cons up an opaque mask here
1417*c8dee2aaSAndroid Build Coastguard Worker         if (!inverseFilled) {
1418*c8dee2aaSAndroid Build Coastguard Worker             return;
1419*c8dee2aaSAndroid Build Coastguard Worker         }
1420*c8dee2aaSAndroid Build Coastguard Worker     }
1421*c8dee2aaSAndroid Build Coastguard Worker 
1422*c8dee2aaSAndroid Build Coastguard Worker     skgpu::UniqueKey maskKey;
1423*c8dee2aaSAndroid Build Coastguard Worker     SkIRect boundsForClip;
1424*c8dee2aaSAndroid Build Coastguard Worker     if (!compute_key_and_clip_bounds(&maskKey, &boundsForClip,
1425*c8dee2aaSAndroid Build Coastguard Worker                                      sdc->caps(),
1426*c8dee2aaSAndroid Build Coastguard Worker                                      viewMatrix, inverseFilled,
1427*c8dee2aaSAndroid Build Coastguard Worker                                      maskFilter, *shape,
1428*c8dee2aaSAndroid Build Coastguard Worker                                      unclippedDevShapeBounds,
1429*c8dee2aaSAndroid Build Coastguard Worker                                      devClipBounds)) {
1430*c8dee2aaSAndroid Build Coastguard Worker         return; // 'shape' was entirely clipped out
1431*c8dee2aaSAndroid Build Coastguard Worker     }
1432*c8dee2aaSAndroid Build Coastguard Worker 
1433*c8dee2aaSAndroid Build Coastguard Worker     GrSurfaceProxyView filteredMaskView;
1434*c8dee2aaSAndroid Build Coastguard Worker     SkIRect maskRect;
1435*c8dee2aaSAndroid Build Coastguard Worker 
1436*c8dee2aaSAndroid Build Coastguard Worker     if (auto dContext = rContext->asDirectContext()) {
1437*c8dee2aaSAndroid Build Coastguard Worker         filteredMaskView = hw_create_filtered_mask(dContext, sdc,
1438*c8dee2aaSAndroid Build Coastguard Worker                                                    viewMatrix, *shape, maskFilter,
1439*c8dee2aaSAndroid Build Coastguard Worker                                                    unclippedDevShapeBounds, boundsForClip,
1440*c8dee2aaSAndroid Build Coastguard Worker                                                    &maskRect, &maskKey);
1441*c8dee2aaSAndroid Build Coastguard Worker         if (filteredMaskView) {
1442*c8dee2aaSAndroid Build Coastguard Worker             if (draw_mask(sdc, clip, viewMatrix, maskRect, std::move(paint),
1443*c8dee2aaSAndroid Build Coastguard Worker                           std::move(filteredMaskView))) {
1444*c8dee2aaSAndroid Build Coastguard Worker                 // This path is completely drawn
1445*c8dee2aaSAndroid Build Coastguard Worker                 return;
1446*c8dee2aaSAndroid Build Coastguard Worker             }
1447*c8dee2aaSAndroid Build Coastguard Worker             assert_alive(paint);
1448*c8dee2aaSAndroid Build Coastguard Worker         }
1449*c8dee2aaSAndroid Build Coastguard Worker     }
1450*c8dee2aaSAndroid Build Coastguard Worker 
1451*c8dee2aaSAndroid Build Coastguard Worker     // Either HW mask rendering failed or we're in a DDL recording thread
1452*c8dee2aaSAndroid Build Coastguard Worker     filteredMaskView = sw_create_filtered_mask(rContext,
1453*c8dee2aaSAndroid Build Coastguard Worker                                                viewMatrix, *shape, maskFilter,
1454*c8dee2aaSAndroid Build Coastguard Worker                                                unclippedDevShapeBounds, boundsForClip,
1455*c8dee2aaSAndroid Build Coastguard Worker                                                &maskRect, &maskKey);
1456*c8dee2aaSAndroid Build Coastguard Worker     if (filteredMaskView) {
1457*c8dee2aaSAndroid Build Coastguard Worker         if (draw_mask(sdc, clip, viewMatrix, maskRect, std::move(paint),
1458*c8dee2aaSAndroid Build Coastguard Worker                       std::move(filteredMaskView))) {
1459*c8dee2aaSAndroid Build Coastguard Worker             return;
1460*c8dee2aaSAndroid Build Coastguard Worker         }
1461*c8dee2aaSAndroid Build Coastguard Worker         assert_alive(paint);
1462*c8dee2aaSAndroid Build Coastguard Worker     }
1463*c8dee2aaSAndroid Build Coastguard Worker }
1464*c8dee2aaSAndroid Build Coastguard Worker 
ComputeBlurredRRectParams(const SkRRect & srcRRect,const SkRRect & devRRect,SkScalar sigma,SkScalar xformedSigma,SkRRect * rrectToDraw,SkISize * widthHeight,SkScalar rectXs[kBlurRRectMaxDivisions],SkScalar rectYs[kBlurRRectMaxDivisions],SkScalar texXs[kBlurRRectMaxDivisions],SkScalar texYs[kBlurRRectMaxDivisions])1465*c8dee2aaSAndroid Build Coastguard Worker bool ComputeBlurredRRectParams(const SkRRect& srcRRect,
1466*c8dee2aaSAndroid Build Coastguard Worker                                const SkRRect& devRRect,
1467*c8dee2aaSAndroid Build Coastguard Worker                                SkScalar sigma,
1468*c8dee2aaSAndroid Build Coastguard Worker                                SkScalar xformedSigma,
1469*c8dee2aaSAndroid Build Coastguard Worker                                SkRRect* rrectToDraw,
1470*c8dee2aaSAndroid Build Coastguard Worker                                SkISize* widthHeight,
1471*c8dee2aaSAndroid Build Coastguard Worker                                SkScalar rectXs[kBlurRRectMaxDivisions],
1472*c8dee2aaSAndroid Build Coastguard Worker                                SkScalar rectYs[kBlurRRectMaxDivisions],
1473*c8dee2aaSAndroid Build Coastguard Worker                                SkScalar texXs[kBlurRRectMaxDivisions],
1474*c8dee2aaSAndroid Build Coastguard Worker                                SkScalar texYs[kBlurRRectMaxDivisions]) {
1475*c8dee2aaSAndroid Build Coastguard Worker     unsigned int devBlurRadius = 3 * SkScalarCeilToInt(xformedSigma - 1 / 6.0f);
1476*c8dee2aaSAndroid Build Coastguard Worker     SkScalar srcBlurRadius = 3.0f * sigma;
1477*c8dee2aaSAndroid Build Coastguard Worker 
1478*c8dee2aaSAndroid Build Coastguard Worker     const SkRect& devOrig = devRRect.getBounds();
1479*c8dee2aaSAndroid Build Coastguard Worker     const SkVector& devRadiiUL = devRRect.radii(SkRRect::kUpperLeft_Corner);
1480*c8dee2aaSAndroid Build Coastguard Worker     const SkVector& devRadiiUR = devRRect.radii(SkRRect::kUpperRight_Corner);
1481*c8dee2aaSAndroid Build Coastguard Worker     const SkVector& devRadiiLR = devRRect.radii(SkRRect::kLowerRight_Corner);
1482*c8dee2aaSAndroid Build Coastguard Worker     const SkVector& devRadiiLL = devRRect.radii(SkRRect::kLowerLeft_Corner);
1483*c8dee2aaSAndroid Build Coastguard Worker 
1484*c8dee2aaSAndroid Build Coastguard Worker     const int devLeft = SkScalarCeilToInt(std::max<SkScalar>(devRadiiUL.fX, devRadiiLL.fX));
1485*c8dee2aaSAndroid Build Coastguard Worker     const int devTop = SkScalarCeilToInt(std::max<SkScalar>(devRadiiUL.fY, devRadiiUR.fY));
1486*c8dee2aaSAndroid Build Coastguard Worker     const int devRight = SkScalarCeilToInt(std::max<SkScalar>(devRadiiUR.fX, devRadiiLR.fX));
1487*c8dee2aaSAndroid Build Coastguard Worker     const int devBot = SkScalarCeilToInt(std::max<SkScalar>(devRadiiLL.fY, devRadiiLR.fY));
1488*c8dee2aaSAndroid Build Coastguard Worker 
1489*c8dee2aaSAndroid Build Coastguard Worker     // This is a conservative check for nine-patchability
1490*c8dee2aaSAndroid Build Coastguard Worker     if (devOrig.fLeft + devLeft + devBlurRadius >= devOrig.fRight - devRight - devBlurRadius ||
1491*c8dee2aaSAndroid Build Coastguard Worker         devOrig.fTop + devTop + devBlurRadius >= devOrig.fBottom - devBot - devBlurRadius) {
1492*c8dee2aaSAndroid Build Coastguard Worker         return false;
1493*c8dee2aaSAndroid Build Coastguard Worker     }
1494*c8dee2aaSAndroid Build Coastguard Worker 
1495*c8dee2aaSAndroid Build Coastguard Worker     const SkVector& srcRadiiUL = srcRRect.radii(SkRRect::kUpperLeft_Corner);
1496*c8dee2aaSAndroid Build Coastguard Worker     const SkVector& srcRadiiUR = srcRRect.radii(SkRRect::kUpperRight_Corner);
1497*c8dee2aaSAndroid Build Coastguard Worker     const SkVector& srcRadiiLR = srcRRect.radii(SkRRect::kLowerRight_Corner);
1498*c8dee2aaSAndroid Build Coastguard Worker     const SkVector& srcRadiiLL = srcRRect.radii(SkRRect::kLowerLeft_Corner);
1499*c8dee2aaSAndroid Build Coastguard Worker 
1500*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar srcLeft = std::max<SkScalar>(srcRadiiUL.fX, srcRadiiLL.fX);
1501*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar srcTop = std::max<SkScalar>(srcRadiiUL.fY, srcRadiiUR.fY);
1502*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar srcRight = std::max<SkScalar>(srcRadiiUR.fX, srcRadiiLR.fX);
1503*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar srcBot = std::max<SkScalar>(srcRadiiLL.fY, srcRadiiLR.fY);
1504*c8dee2aaSAndroid Build Coastguard Worker 
1505*c8dee2aaSAndroid Build Coastguard Worker     int newRRWidth = 2 * devBlurRadius + devLeft + devRight + 1;
1506*c8dee2aaSAndroid Build Coastguard Worker     int newRRHeight = 2 * devBlurRadius + devTop + devBot + 1;
1507*c8dee2aaSAndroid Build Coastguard Worker     widthHeight->fWidth = newRRWidth + 2 * devBlurRadius;
1508*c8dee2aaSAndroid Build Coastguard Worker     widthHeight->fHeight = newRRHeight + 2 * devBlurRadius;
1509*c8dee2aaSAndroid Build Coastguard Worker 
1510*c8dee2aaSAndroid Build Coastguard Worker     const SkRect srcProxyRect = srcRRect.getBounds().makeOutset(srcBlurRadius, srcBlurRadius);
1511*c8dee2aaSAndroid Build Coastguard Worker 
1512*c8dee2aaSAndroid Build Coastguard Worker     rectXs[0] = srcProxyRect.fLeft;
1513*c8dee2aaSAndroid Build Coastguard Worker     rectXs[1] = srcProxyRect.fLeft + 2 * srcBlurRadius + srcLeft;
1514*c8dee2aaSAndroid Build Coastguard Worker     rectXs[2] = srcProxyRect.fRight - 2 * srcBlurRadius - srcRight;
1515*c8dee2aaSAndroid Build Coastguard Worker     rectXs[3] = srcProxyRect.fRight;
1516*c8dee2aaSAndroid Build Coastguard Worker 
1517*c8dee2aaSAndroid Build Coastguard Worker     rectYs[0] = srcProxyRect.fTop;
1518*c8dee2aaSAndroid Build Coastguard Worker     rectYs[1] = srcProxyRect.fTop + 2 * srcBlurRadius + srcTop;
1519*c8dee2aaSAndroid Build Coastguard Worker     rectYs[2] = srcProxyRect.fBottom - 2 * srcBlurRadius - srcBot;
1520*c8dee2aaSAndroid Build Coastguard Worker     rectYs[3] = srcProxyRect.fBottom;
1521*c8dee2aaSAndroid Build Coastguard Worker 
1522*c8dee2aaSAndroid Build Coastguard Worker     texXs[0] = 0.0f;
1523*c8dee2aaSAndroid Build Coastguard Worker     texXs[1] = 2.0f * devBlurRadius + devLeft;
1524*c8dee2aaSAndroid Build Coastguard Worker     texXs[2] = 2.0f * devBlurRadius + devLeft + 1;
1525*c8dee2aaSAndroid Build Coastguard Worker     texXs[3] = SkIntToScalar(widthHeight->fWidth);
1526*c8dee2aaSAndroid Build Coastguard Worker 
1527*c8dee2aaSAndroid Build Coastguard Worker     texYs[0] = 0.0f;
1528*c8dee2aaSAndroid Build Coastguard Worker     texYs[1] = 2.0f * devBlurRadius + devTop;
1529*c8dee2aaSAndroid Build Coastguard Worker     texYs[2] = 2.0f * devBlurRadius + devTop + 1;
1530*c8dee2aaSAndroid Build Coastguard Worker     texYs[3] = SkIntToScalar(widthHeight->fHeight);
1531*c8dee2aaSAndroid Build Coastguard Worker 
1532*c8dee2aaSAndroid Build Coastguard Worker     const SkRect newRect = SkRect::MakeXYWH(SkIntToScalar(devBlurRadius),
1533*c8dee2aaSAndroid Build Coastguard Worker                                             SkIntToScalar(devBlurRadius),
1534*c8dee2aaSAndroid Build Coastguard Worker                                             SkIntToScalar(newRRWidth),
1535*c8dee2aaSAndroid Build Coastguard Worker                                             SkIntToScalar(newRRHeight));
1536*c8dee2aaSAndroid Build Coastguard Worker     SkVector newRadii[4];
1537*c8dee2aaSAndroid Build Coastguard Worker     newRadii[0] = {SkScalarCeilToScalar(devRadiiUL.fX), SkScalarCeilToScalar(devRadiiUL.fY)};
1538*c8dee2aaSAndroid Build Coastguard Worker     newRadii[1] = {SkScalarCeilToScalar(devRadiiUR.fX), SkScalarCeilToScalar(devRadiiUR.fY)};
1539*c8dee2aaSAndroid Build Coastguard Worker     newRadii[2] = {SkScalarCeilToScalar(devRadiiLR.fX), SkScalarCeilToScalar(devRadiiLR.fY)};
1540*c8dee2aaSAndroid Build Coastguard Worker     newRadii[3] = {SkScalarCeilToScalar(devRadiiLL.fX), SkScalarCeilToScalar(devRadiiLL.fY)};
1541*c8dee2aaSAndroid Build Coastguard Worker 
1542*c8dee2aaSAndroid Build Coastguard Worker     rrectToDraw->setRectRadii(newRect, newRadii);
1543*c8dee2aaSAndroid Build Coastguard Worker     return true;
1544*c8dee2aaSAndroid Build Coastguard Worker }
1545*c8dee2aaSAndroid Build Coastguard Worker 
DrawShapeWithMaskFilter(GrRecordingContext * rContext,skgpu::ganesh::SurfaceDrawContext * sdc,const GrClip * clip,const GrStyledShape & shape,GrPaint && paint,const SkMatrix & viewMatrix,const SkMaskFilter * mf)1546*c8dee2aaSAndroid Build Coastguard Worker void DrawShapeWithMaskFilter(GrRecordingContext* rContext,
1547*c8dee2aaSAndroid Build Coastguard Worker                              skgpu::ganesh::SurfaceDrawContext* sdc,
1548*c8dee2aaSAndroid Build Coastguard Worker                              const GrClip* clip,
1549*c8dee2aaSAndroid Build Coastguard Worker                              const GrStyledShape& shape,
1550*c8dee2aaSAndroid Build Coastguard Worker                              GrPaint&& paint,
1551*c8dee2aaSAndroid Build Coastguard Worker                              const SkMatrix& viewMatrix,
1552*c8dee2aaSAndroid Build Coastguard Worker                              const SkMaskFilter* mf) {
1553*c8dee2aaSAndroid Build Coastguard Worker     draw_shape_with_mask_filter(rContext, sdc, clip, std::move(paint),
1554*c8dee2aaSAndroid Build Coastguard Worker                                 viewMatrix, as_MFB(mf), shape);
1555*c8dee2aaSAndroid Build Coastguard Worker }
1556*c8dee2aaSAndroid Build Coastguard Worker 
DrawShapeWithMaskFilter(GrRecordingContext * rContext,skgpu::ganesh::SurfaceDrawContext * sdc,const GrClip * clip,const SkPaint & paint,const SkMatrix & ctm,const GrStyledShape & shape)1557*c8dee2aaSAndroid Build Coastguard Worker void DrawShapeWithMaskFilter(GrRecordingContext* rContext,
1558*c8dee2aaSAndroid Build Coastguard Worker                              skgpu::ganesh::SurfaceDrawContext* sdc,
1559*c8dee2aaSAndroid Build Coastguard Worker                              const GrClip* clip,
1560*c8dee2aaSAndroid Build Coastguard Worker                              const SkPaint& paint,
1561*c8dee2aaSAndroid Build Coastguard Worker                              const SkMatrix& ctm,
1562*c8dee2aaSAndroid Build Coastguard Worker                              const GrStyledShape& shape) {
1563*c8dee2aaSAndroid Build Coastguard Worker     if (rContext->abandoned()) {
1564*c8dee2aaSAndroid Build Coastguard Worker         return;
1565*c8dee2aaSAndroid Build Coastguard Worker     }
1566*c8dee2aaSAndroid Build Coastguard Worker 
1567*c8dee2aaSAndroid Build Coastguard Worker     GrPaint grPaint;
1568*c8dee2aaSAndroid Build Coastguard Worker     if (!SkPaintToGrPaint(rContext, sdc->colorInfo(), paint, ctm, sdc->surfaceProps(), &grPaint)) {
1569*c8dee2aaSAndroid Build Coastguard Worker         return;
1570*c8dee2aaSAndroid Build Coastguard Worker     }
1571*c8dee2aaSAndroid Build Coastguard Worker 
1572*c8dee2aaSAndroid Build Coastguard Worker     SkMaskFilterBase* mf = as_MFB(paint.getMaskFilter());
1573*c8dee2aaSAndroid Build Coastguard Worker     if (mf && !GrFragmentProcessors::IsSupported(mf)) {
1574*c8dee2aaSAndroid Build Coastguard Worker         // The MaskFilter wasn't already handled in SkPaintToGrPaint
1575*c8dee2aaSAndroid Build Coastguard Worker         draw_shape_with_mask_filter(rContext, sdc, clip, std::move(grPaint), ctm, mf, shape);
1576*c8dee2aaSAndroid Build Coastguard Worker     } else {
1577*c8dee2aaSAndroid Build Coastguard Worker         sdc->drawShape(clip, std::move(grPaint), sdc->chooseAA(paint), ctm, GrStyledShape(shape));
1578*c8dee2aaSAndroid Build Coastguard Worker     }
1579*c8dee2aaSAndroid Build Coastguard Worker }
1580*c8dee2aaSAndroid Build Coastguard Worker 
1581*c8dee2aaSAndroid Build Coastguard Worker 
1582*c8dee2aaSAndroid Build Coastguard Worker // =================== Gaussian Blur =========================================
1583*c8dee2aaSAndroid Build Coastguard Worker 
1584*c8dee2aaSAndroid Build Coastguard Worker namespace {
1585*c8dee2aaSAndroid Build Coastguard Worker 
1586*c8dee2aaSAndroid Build Coastguard Worker enum class Direction { kX, kY };
1587*c8dee2aaSAndroid Build Coastguard Worker 
make_texture_effect(const GrCaps * caps,GrSurfaceProxyView srcView,SkAlphaType srcAlphaType,const GrSamplerState & sampler,const SkIRect & srcSubset,const SkIRect & srcRelativeDstRect,const SkISize & radii)1588*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> make_texture_effect(const GrCaps* caps,
1589*c8dee2aaSAndroid Build Coastguard Worker                                                          GrSurfaceProxyView srcView,
1590*c8dee2aaSAndroid Build Coastguard Worker                                                          SkAlphaType srcAlphaType,
1591*c8dee2aaSAndroid Build Coastguard Worker                                                          const GrSamplerState& sampler,
1592*c8dee2aaSAndroid Build Coastguard Worker                                                          const SkIRect& srcSubset,
1593*c8dee2aaSAndroid Build Coastguard Worker                                                          const SkIRect& srcRelativeDstRect,
1594*c8dee2aaSAndroid Build Coastguard Worker                                                          const SkISize& radii) {
1595*c8dee2aaSAndroid Build Coastguard Worker     // It's pretty common to blur a subset of an input texture. In reduced shader mode we always
1596*c8dee2aaSAndroid Build Coastguard Worker     // apply the wrap mode in the shader.
1597*c8dee2aaSAndroid Build Coastguard Worker     if (caps->reducedShaderMode()) {
1598*c8dee2aaSAndroid Build Coastguard Worker         return GrTextureEffect::MakeSubset(std::move(srcView),
1599*c8dee2aaSAndroid Build Coastguard Worker                                            srcAlphaType,
1600*c8dee2aaSAndroid Build Coastguard Worker                                            SkMatrix::I(),
1601*c8dee2aaSAndroid Build Coastguard Worker                                            sampler,
1602*c8dee2aaSAndroid Build Coastguard Worker                                            SkRect::Make(srcSubset),
1603*c8dee2aaSAndroid Build Coastguard Worker                                            *caps,
1604*c8dee2aaSAndroid Build Coastguard Worker                                            GrTextureEffect::kDefaultBorder,
1605*c8dee2aaSAndroid Build Coastguard Worker                                            /*alwaysUseShaderTileMode=*/true);
1606*c8dee2aaSAndroid Build Coastguard Worker     } else {
1607*c8dee2aaSAndroid Build Coastguard Worker         // Inset because we expect to be invoked at pixel centers
1608*c8dee2aaSAndroid Build Coastguard Worker         SkRect domain = SkRect::Make(srcRelativeDstRect);
1609*c8dee2aaSAndroid Build Coastguard Worker         domain.inset(0.5f, 0.5f);
1610*c8dee2aaSAndroid Build Coastguard Worker         domain.outset(radii.width(), radii.height());
1611*c8dee2aaSAndroid Build Coastguard Worker         return GrTextureEffect::MakeSubset(std::move(srcView),
1612*c8dee2aaSAndroid Build Coastguard Worker                                            srcAlphaType,
1613*c8dee2aaSAndroid Build Coastguard Worker                                            SkMatrix::I(),
1614*c8dee2aaSAndroid Build Coastguard Worker                                            sampler,
1615*c8dee2aaSAndroid Build Coastguard Worker                                            SkRect::Make(srcSubset),
1616*c8dee2aaSAndroid Build Coastguard Worker                                            domain,
1617*c8dee2aaSAndroid Build Coastguard Worker                                            *caps);
1618*c8dee2aaSAndroid Build Coastguard Worker     }
1619*c8dee2aaSAndroid Build Coastguard Worker }
1620*c8dee2aaSAndroid Build Coastguard Worker 
1621*c8dee2aaSAndroid Build Coastguard Worker } // end namespace
1622*c8dee2aaSAndroid Build Coastguard Worker 
1623*c8dee2aaSAndroid Build Coastguard Worker /**
1624*c8dee2aaSAndroid Build Coastguard Worker  * Draws 'dstRect' into 'surfaceFillContext' evaluating a 1D Gaussian over 'srcView'. The src rect
1625*c8dee2aaSAndroid Build Coastguard Worker  * is 'dstRect' offset by 'dstToSrcOffset'. 'mode' and 'bounds' are applied to the src coords.
1626*c8dee2aaSAndroid Build Coastguard Worker  */
convolve_gaussian_1d(skgpu::ganesh::SurfaceFillContext * sfc,GrSurfaceProxyView srcView,const SkIRect & srcSubset,SkIVector dstToSrcOffset,const SkIRect & dstRect,SkAlphaType srcAlphaType,Direction direction,int radius,float sigma,SkTileMode mode)1627*c8dee2aaSAndroid Build Coastguard Worker static void convolve_gaussian_1d(skgpu::ganesh::SurfaceFillContext* sfc,
1628*c8dee2aaSAndroid Build Coastguard Worker                                  GrSurfaceProxyView srcView,
1629*c8dee2aaSAndroid Build Coastguard Worker                                  const SkIRect& srcSubset,
1630*c8dee2aaSAndroid Build Coastguard Worker                                  SkIVector dstToSrcOffset,
1631*c8dee2aaSAndroid Build Coastguard Worker                                  const SkIRect& dstRect,
1632*c8dee2aaSAndroid Build Coastguard Worker                                  SkAlphaType srcAlphaType,
1633*c8dee2aaSAndroid Build Coastguard Worker                                  Direction direction,
1634*c8dee2aaSAndroid Build Coastguard Worker                                  int radius,
1635*c8dee2aaSAndroid Build Coastguard Worker                                  float sigma,
1636*c8dee2aaSAndroid Build Coastguard Worker                                  SkTileMode mode) {
1637*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(radius && !skgpu::BlurIsEffectivelyIdentity(sigma));
1638*c8dee2aaSAndroid Build Coastguard Worker     auto srcRect = dstRect.makeOffset(dstToSrcOffset);
1639*c8dee2aaSAndroid Build Coastguard Worker 
1640*c8dee2aaSAndroid Build Coastguard Worker     std::array<SkV4, skgpu::kMaxBlurSamples/2> offsetsAndKernel;
1641*c8dee2aaSAndroid Build Coastguard Worker     skgpu::Compute1DBlurLinearKernel(sigma, radius, offsetsAndKernel);
1642*c8dee2aaSAndroid Build Coastguard Worker 
1643*c8dee2aaSAndroid Build Coastguard Worker     // The child of the 1D linear blur effect must be linearly sampled.
1644*c8dee2aaSAndroid Build Coastguard Worker     GrSamplerState sampler{SkTileModeToWrapMode(mode), GrSamplerState::Filter::kLinear};
1645*c8dee2aaSAndroid Build Coastguard Worker 
1646*c8dee2aaSAndroid Build Coastguard Worker     SkISize radii = {direction == Direction::kX ? radius : 0,
1647*c8dee2aaSAndroid Build Coastguard Worker                      direction == Direction::kY ? radius : 0};
1648*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<GrFragmentProcessor> child = make_texture_effect(sfc->caps(),
1649*c8dee2aaSAndroid Build Coastguard Worker                                                                      std::move(srcView),
1650*c8dee2aaSAndroid Build Coastguard Worker                                                                      srcAlphaType,
1651*c8dee2aaSAndroid Build Coastguard Worker                                                                      sampler,
1652*c8dee2aaSAndroid Build Coastguard Worker                                                                      srcSubset,
1653*c8dee2aaSAndroid Build Coastguard Worker                                                                      srcRect,
1654*c8dee2aaSAndroid Build Coastguard Worker                                                                      radii);
1655*c8dee2aaSAndroid Build Coastguard Worker 
1656*c8dee2aaSAndroid Build Coastguard Worker     auto conv = GrSkSLFP::Make(skgpu::GetLinearBlur1DEffect(radius),
1657*c8dee2aaSAndroid Build Coastguard Worker                                "GaussianBlur1D",
1658*c8dee2aaSAndroid Build Coastguard Worker                                /*inputFP=*/nullptr,
1659*c8dee2aaSAndroid Build Coastguard Worker                                GrSkSLFP::OptFlags::kCompatibleWithCoverageAsAlpha,
1660*c8dee2aaSAndroid Build Coastguard Worker                                "offsetsAndKernel", SkSpan<SkV4>{offsetsAndKernel},
1661*c8dee2aaSAndroid Build Coastguard Worker                                "dir", direction == Direction::kX ? SkV2{1.f, 0.f}
1662*c8dee2aaSAndroid Build Coastguard Worker                                                                  : SkV2{0.f, 1.f},
1663*c8dee2aaSAndroid Build Coastguard Worker                                "child", std::move(child));
1664*c8dee2aaSAndroid Build Coastguard Worker     sfc->fillRectToRectWithFP(srcRect, dstRect, std::move(conv));
1665*c8dee2aaSAndroid Build Coastguard Worker }
1666*c8dee2aaSAndroid Build Coastguard Worker 
convolve_gaussian_2d(GrRecordingContext * rContext,GrSurfaceProxyView srcView,GrColorType srcColorType,const SkIRect & srcBounds,const SkIRect & dstBounds,int radiusX,int radiusY,SkScalar sigmaX,SkScalar sigmaY,SkTileMode mode,sk_sp<SkColorSpace> finalCS,SkBackingFit dstFit)1667*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<skgpu::ganesh::SurfaceDrawContext> convolve_gaussian_2d(
1668*c8dee2aaSAndroid Build Coastguard Worker         GrRecordingContext* rContext,
1669*c8dee2aaSAndroid Build Coastguard Worker         GrSurfaceProxyView srcView,
1670*c8dee2aaSAndroid Build Coastguard Worker         GrColorType srcColorType,
1671*c8dee2aaSAndroid Build Coastguard Worker         const SkIRect& srcBounds,
1672*c8dee2aaSAndroid Build Coastguard Worker         const SkIRect& dstBounds,
1673*c8dee2aaSAndroid Build Coastguard Worker         int radiusX,
1674*c8dee2aaSAndroid Build Coastguard Worker         int radiusY,
1675*c8dee2aaSAndroid Build Coastguard Worker         SkScalar sigmaX,
1676*c8dee2aaSAndroid Build Coastguard Worker         SkScalar sigmaY,
1677*c8dee2aaSAndroid Build Coastguard Worker         SkTileMode mode,
1678*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<SkColorSpace> finalCS,
1679*c8dee2aaSAndroid Build Coastguard Worker         SkBackingFit dstFit) {
1680*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(radiusX && radiusY);
1681*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!skgpu::BlurIsEffectivelyIdentity(sigmaX) &&
1682*c8dee2aaSAndroid Build Coastguard Worker              !skgpu::BlurIsEffectivelyIdentity(sigmaY));
1683*c8dee2aaSAndroid Build Coastguard Worker     // Create the sdc with default SkSurfaceProps. Gaussian blurs will soon use a
1684*c8dee2aaSAndroid Build Coastguard Worker     // SurfaceFillContext, at which point the SkSurfaceProps won't exist anymore.
1685*c8dee2aaSAndroid Build Coastguard Worker     auto sdc = skgpu::ganesh::SurfaceDrawContext::Make(
1686*c8dee2aaSAndroid Build Coastguard Worker             rContext,
1687*c8dee2aaSAndroid Build Coastguard Worker             srcColorType,
1688*c8dee2aaSAndroid Build Coastguard Worker             std::move(finalCS),
1689*c8dee2aaSAndroid Build Coastguard Worker             dstFit,
1690*c8dee2aaSAndroid Build Coastguard Worker             dstBounds.size(),
1691*c8dee2aaSAndroid Build Coastguard Worker             SkSurfaceProps(),
1692*c8dee2aaSAndroid Build Coastguard Worker             /*label=*/"SurfaceDrawContext_ConvolveGaussian2d",
1693*c8dee2aaSAndroid Build Coastguard Worker             /* sampleCnt= */ 1,
1694*c8dee2aaSAndroid Build Coastguard Worker             skgpu::Mipmapped::kNo,
1695*c8dee2aaSAndroid Build Coastguard Worker             srcView.proxy()->isProtected(),
1696*c8dee2aaSAndroid Build Coastguard Worker             srcView.origin());
1697*c8dee2aaSAndroid Build Coastguard Worker     if (!sdc) {
1698*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
1699*c8dee2aaSAndroid Build Coastguard Worker     }
1700*c8dee2aaSAndroid Build Coastguard Worker 
1701*c8dee2aaSAndroid Build Coastguard Worker     // GaussianBlur() should have downsampled the request until we can handle the 2D blur with
1702*c8dee2aaSAndroid Build Coastguard Worker     // just a uniform array, which is asserted inside the Compute function.
1703*c8dee2aaSAndroid Build Coastguard Worker     const SkISize radii{radiusX, radiusY};
1704*c8dee2aaSAndroid Build Coastguard Worker     std::array<SkV4, skgpu::kMaxBlurSamples/4> kernel;
1705*c8dee2aaSAndroid Build Coastguard Worker     std::array<SkV4, skgpu::kMaxBlurSamples/2> offsets;
1706*c8dee2aaSAndroid Build Coastguard Worker     skgpu::Compute2DBlurKernel({sigmaX, sigmaY}, radii, kernel);
1707*c8dee2aaSAndroid Build Coastguard Worker     skgpu::Compute2DBlurOffsets(radii, offsets);
1708*c8dee2aaSAndroid Build Coastguard Worker 
1709*c8dee2aaSAndroid Build Coastguard Worker     GrSamplerState sampler{SkTileModeToWrapMode(mode), GrSamplerState::Filter::kNearest};
1710*c8dee2aaSAndroid Build Coastguard Worker     auto child = make_texture_effect(sdc->caps(),
1711*c8dee2aaSAndroid Build Coastguard Worker                                      std::move(srcView),
1712*c8dee2aaSAndroid Build Coastguard Worker                                      kPremul_SkAlphaType,
1713*c8dee2aaSAndroid Build Coastguard Worker                                      sampler,
1714*c8dee2aaSAndroid Build Coastguard Worker                                      srcBounds,
1715*c8dee2aaSAndroid Build Coastguard Worker                                      dstBounds,
1716*c8dee2aaSAndroid Build Coastguard Worker                                      radii);
1717*c8dee2aaSAndroid Build Coastguard Worker     auto conv = GrSkSLFP::Make(skgpu::GetBlur2DEffect(radii),
1718*c8dee2aaSAndroid Build Coastguard Worker                                "GaussianBlur2D",
1719*c8dee2aaSAndroid Build Coastguard Worker                                /*inputFP=*/nullptr,
1720*c8dee2aaSAndroid Build Coastguard Worker                                GrSkSLFP::OptFlags::kNone,
1721*c8dee2aaSAndroid Build Coastguard Worker                                "kernel", SkSpan<SkV4>{kernel},
1722*c8dee2aaSAndroid Build Coastguard Worker                                "offsets", SkSpan<SkV4>{offsets},
1723*c8dee2aaSAndroid Build Coastguard Worker                                "child", std::move(child));
1724*c8dee2aaSAndroid Build Coastguard Worker 
1725*c8dee2aaSAndroid Build Coastguard Worker     GrPaint paint;
1726*c8dee2aaSAndroid Build Coastguard Worker     paint.setColorFragmentProcessor(std::move(conv));
1727*c8dee2aaSAndroid Build Coastguard Worker     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
1728*c8dee2aaSAndroid Build Coastguard Worker 
1729*c8dee2aaSAndroid Build Coastguard Worker     // 'dstBounds' is actually in 'srcView' proxy space. It represents the blurred area from src
1730*c8dee2aaSAndroid Build Coastguard Worker     // space that we want to capture in the new RTC at {0, 0}. Hence, we use its size as the rect to
1731*c8dee2aaSAndroid Build Coastguard Worker     // draw and it directly as the local rect.
1732*c8dee2aaSAndroid Build Coastguard Worker     sdc->fillRectToRect(nullptr,
1733*c8dee2aaSAndroid Build Coastguard Worker                         std::move(paint),
1734*c8dee2aaSAndroid Build Coastguard Worker                         GrAA::kNo,
1735*c8dee2aaSAndroid Build Coastguard Worker                         SkMatrix::I(),
1736*c8dee2aaSAndroid Build Coastguard Worker                         SkRect::Make(dstBounds.size()),
1737*c8dee2aaSAndroid Build Coastguard Worker                         SkRect::Make(dstBounds));
1738*c8dee2aaSAndroid Build Coastguard Worker 
1739*c8dee2aaSAndroid Build Coastguard Worker     return sdc;
1740*c8dee2aaSAndroid Build Coastguard Worker }
1741*c8dee2aaSAndroid Build Coastguard Worker 
convolve_gaussian(GrRecordingContext * rContext,GrSurfaceProxyView srcView,GrColorType srcColorType,SkAlphaType srcAlphaType,SkIRect srcBounds,SkIRect dstBounds,Direction direction,int radius,float sigma,SkTileMode mode,sk_sp<SkColorSpace> finalCS,SkBackingFit fit)1742*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<skgpu::ganesh::SurfaceDrawContext> convolve_gaussian(
1743*c8dee2aaSAndroid Build Coastguard Worker         GrRecordingContext* rContext,
1744*c8dee2aaSAndroid Build Coastguard Worker         GrSurfaceProxyView srcView,
1745*c8dee2aaSAndroid Build Coastguard Worker         GrColorType srcColorType,
1746*c8dee2aaSAndroid Build Coastguard Worker         SkAlphaType srcAlphaType,
1747*c8dee2aaSAndroid Build Coastguard Worker         SkIRect srcBounds,
1748*c8dee2aaSAndroid Build Coastguard Worker         SkIRect dstBounds,
1749*c8dee2aaSAndroid Build Coastguard Worker         Direction direction,
1750*c8dee2aaSAndroid Build Coastguard Worker         int radius,
1751*c8dee2aaSAndroid Build Coastguard Worker         float sigma,
1752*c8dee2aaSAndroid Build Coastguard Worker         SkTileMode mode,
1753*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<SkColorSpace> finalCS,
1754*c8dee2aaSAndroid Build Coastguard Worker         SkBackingFit fit) {
1755*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(radius > 0 && !skgpu::BlurIsEffectivelyIdentity(sigma));
1756*c8dee2aaSAndroid Build Coastguard Worker     // Logically we're creating an infinite blur of 'srcBounds' of 'srcView' with 'mode' tiling
1757*c8dee2aaSAndroid Build Coastguard Worker     // and then capturing the 'dstBounds' portion in a new RTC where the top left of 'dstBounds' is
1758*c8dee2aaSAndroid Build Coastguard Worker     // at {0, 0} in the new RTC.
1759*c8dee2aaSAndroid Build Coastguard Worker     //
1760*c8dee2aaSAndroid Build Coastguard Worker     // Create the sdc with default SkSurfaceProps. Gaussian blurs will soon use a
1761*c8dee2aaSAndroid Build Coastguard Worker     // SurfaceFillContext, at which point the SkSurfaceProps won't exist anymore.
1762*c8dee2aaSAndroid Build Coastguard Worker     auto dstSDC =
1763*c8dee2aaSAndroid Build Coastguard Worker             skgpu::ganesh::SurfaceDrawContext::Make(rContext,
1764*c8dee2aaSAndroid Build Coastguard Worker                                                     srcColorType,
1765*c8dee2aaSAndroid Build Coastguard Worker                                                     std::move(finalCS),
1766*c8dee2aaSAndroid Build Coastguard Worker                                                     fit,
1767*c8dee2aaSAndroid Build Coastguard Worker                                                     dstBounds.size(),
1768*c8dee2aaSAndroid Build Coastguard Worker                                                     SkSurfaceProps(),
1769*c8dee2aaSAndroid Build Coastguard Worker                                                     /*label=*/"SurfaceDrawContext_ConvolveGaussian",
1770*c8dee2aaSAndroid Build Coastguard Worker                                                     /* sampleCnt= */ 1,
1771*c8dee2aaSAndroid Build Coastguard Worker                                                     skgpu::Mipmapped::kNo,
1772*c8dee2aaSAndroid Build Coastguard Worker                                                     srcView.proxy()->isProtected(),
1773*c8dee2aaSAndroid Build Coastguard Worker                                                     srcView.origin());
1774*c8dee2aaSAndroid Build Coastguard Worker     if (!dstSDC) {
1775*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
1776*c8dee2aaSAndroid Build Coastguard Worker     }
1777*c8dee2aaSAndroid Build Coastguard Worker     // This represents the translation from 'dstSurfaceDrawContext' coords to 'srcView' coords.
1778*c8dee2aaSAndroid Build Coastguard Worker     auto rtcToSrcOffset = dstBounds.topLeft();
1779*c8dee2aaSAndroid Build Coastguard Worker 
1780*c8dee2aaSAndroid Build Coastguard Worker     auto srcBackingBounds = SkIRect::MakeSize(srcView.proxy()->backingStoreDimensions());
1781*c8dee2aaSAndroid Build Coastguard Worker     // We've implemented splitting the dst bounds up into areas that do and do not need to
1782*c8dee2aaSAndroid Build Coastguard Worker     // use shader based tiling but only for some modes...
1783*c8dee2aaSAndroid Build Coastguard Worker     bool canSplit = mode == SkTileMode::kDecal || mode == SkTileMode::kClamp;
1784*c8dee2aaSAndroid Build Coastguard Worker     // ...but it's not worth doing the splitting if we'll get HW tiling instead of shader tiling.
1785*c8dee2aaSAndroid Build Coastguard Worker     bool canHWTile =
1786*c8dee2aaSAndroid Build Coastguard Worker             srcBounds.contains(srcBackingBounds) &&
1787*c8dee2aaSAndroid Build Coastguard Worker             !rContext->priv().caps()->reducedShaderMode() &&  // this mode always uses shader tiling
1788*c8dee2aaSAndroid Build Coastguard Worker             !(mode == SkTileMode::kDecal && !rContext->priv().caps()->clampToBorderSupport());
1789*c8dee2aaSAndroid Build Coastguard Worker     if (!canSplit || canHWTile) {
1790*c8dee2aaSAndroid Build Coastguard Worker         auto dstRect = SkIRect::MakeSize(dstBounds.size());
1791*c8dee2aaSAndroid Build Coastguard Worker         convolve_gaussian_1d(dstSDC.get(),
1792*c8dee2aaSAndroid Build Coastguard Worker                              std::move(srcView),
1793*c8dee2aaSAndroid Build Coastguard Worker                              srcBounds,
1794*c8dee2aaSAndroid Build Coastguard Worker                              rtcToSrcOffset,
1795*c8dee2aaSAndroid Build Coastguard Worker                              dstRect,
1796*c8dee2aaSAndroid Build Coastguard Worker                              srcAlphaType,
1797*c8dee2aaSAndroid Build Coastguard Worker                              direction,
1798*c8dee2aaSAndroid Build Coastguard Worker                              radius,
1799*c8dee2aaSAndroid Build Coastguard Worker                              sigma,
1800*c8dee2aaSAndroid Build Coastguard Worker                              mode);
1801*c8dee2aaSAndroid Build Coastguard Worker         return dstSDC;
1802*c8dee2aaSAndroid Build Coastguard Worker     }
1803*c8dee2aaSAndroid Build Coastguard Worker 
1804*c8dee2aaSAndroid Build Coastguard Worker     // 'left' and 'right' are the sub rects of 'srcBounds' where 'mode' must be enforced.
1805*c8dee2aaSAndroid Build Coastguard Worker     // 'mid' is the area where we can ignore the mode because the kernel does not reach to the
1806*c8dee2aaSAndroid Build Coastguard Worker     // edge of 'srcBounds'.
1807*c8dee2aaSAndroid Build Coastguard Worker     SkIRect mid, left, right;
1808*c8dee2aaSAndroid Build Coastguard Worker     // 'top' and 'bottom' are areas of 'dstBounds' that are entirely above/below 'srcBounds'.
1809*c8dee2aaSAndroid Build Coastguard Worker     // These are areas that we can simply clear in the dst in kDecal mode. If 'srcBounds'
1810*c8dee2aaSAndroid Build Coastguard Worker     // straddles the top edge of 'dstBounds' then 'top' will be inverted and we will skip
1811*c8dee2aaSAndroid Build Coastguard Worker     // processing for the rect. Similar for 'bottom'. The positional/directional labels above refer
1812*c8dee2aaSAndroid Build Coastguard Worker     // to the Direction::kX case and one should think of these as 'left' and 'right' for
1813*c8dee2aaSAndroid Build Coastguard Worker     // Direction::kY.
1814*c8dee2aaSAndroid Build Coastguard Worker     SkIRect top, bottom;
1815*c8dee2aaSAndroid Build Coastguard Worker     if (Direction::kX == direction) {
1816*c8dee2aaSAndroid Build Coastguard Worker         top = {dstBounds.left(), dstBounds.top(), dstBounds.right(), srcBounds.top()};
1817*c8dee2aaSAndroid Build Coastguard Worker         bottom = {dstBounds.left(), srcBounds.bottom(), dstBounds.right(), dstBounds.bottom()};
1818*c8dee2aaSAndroid Build Coastguard Worker 
1819*c8dee2aaSAndroid Build Coastguard Worker         // Inset for sub-rect of 'srcBounds' where the x-dir kernel doesn't reach the edges, clipped
1820*c8dee2aaSAndroid Build Coastguard Worker         // vertically to dstBounds.
1821*c8dee2aaSAndroid Build Coastguard Worker         int midA = std::max(srcBounds.top(), dstBounds.top());
1822*c8dee2aaSAndroid Build Coastguard Worker         int midB = std::min(srcBounds.bottom(), dstBounds.bottom());
1823*c8dee2aaSAndroid Build Coastguard Worker         mid = {srcBounds.left() + radius, midA, srcBounds.right() - radius, midB};
1824*c8dee2aaSAndroid Build Coastguard Worker         if (mid.isEmpty()) {
1825*c8dee2aaSAndroid Build Coastguard Worker             // There is no middle where the bounds can be ignored. Make the left span the whole
1826*c8dee2aaSAndroid Build Coastguard Worker             // width of dst and we will not draw mid or right.
1827*c8dee2aaSAndroid Build Coastguard Worker             left = {dstBounds.left(), mid.top(), dstBounds.right(), mid.bottom()};
1828*c8dee2aaSAndroid Build Coastguard Worker         } else {
1829*c8dee2aaSAndroid Build Coastguard Worker             left = {dstBounds.left(), mid.top(), mid.left(), mid.bottom()};
1830*c8dee2aaSAndroid Build Coastguard Worker             right = {mid.right(), mid.top(), dstBounds.right(), mid.bottom()};
1831*c8dee2aaSAndroid Build Coastguard Worker         }
1832*c8dee2aaSAndroid Build Coastguard Worker     } else {
1833*c8dee2aaSAndroid Build Coastguard Worker         // This is the same as the x direction code if you turn your head 90 degrees CCW. Swap x and
1834*c8dee2aaSAndroid Build Coastguard Worker         // y and swap top/bottom with left/right.
1835*c8dee2aaSAndroid Build Coastguard Worker         top = {dstBounds.left(), dstBounds.top(), srcBounds.left(), dstBounds.bottom()};
1836*c8dee2aaSAndroid Build Coastguard Worker         bottom = {srcBounds.right(), dstBounds.top(), dstBounds.right(), dstBounds.bottom()};
1837*c8dee2aaSAndroid Build Coastguard Worker 
1838*c8dee2aaSAndroid Build Coastguard Worker         int midA = std::max(srcBounds.left(), dstBounds.left());
1839*c8dee2aaSAndroid Build Coastguard Worker         int midB = std::min(srcBounds.right(), dstBounds.right());
1840*c8dee2aaSAndroid Build Coastguard Worker         mid = {midA, srcBounds.top() + radius, midB, srcBounds.bottom() - radius};
1841*c8dee2aaSAndroid Build Coastguard Worker 
1842*c8dee2aaSAndroid Build Coastguard Worker         if (mid.isEmpty()) {
1843*c8dee2aaSAndroid Build Coastguard Worker             left = {mid.left(), dstBounds.top(), mid.right(), dstBounds.bottom()};
1844*c8dee2aaSAndroid Build Coastguard Worker         } else {
1845*c8dee2aaSAndroid Build Coastguard Worker             left = {mid.left(), dstBounds.top(), mid.right(), mid.top()};
1846*c8dee2aaSAndroid Build Coastguard Worker             right = {mid.left(), mid.bottom(), mid.right(), dstBounds.bottom()};
1847*c8dee2aaSAndroid Build Coastguard Worker         }
1848*c8dee2aaSAndroid Build Coastguard Worker     }
1849*c8dee2aaSAndroid Build Coastguard Worker 
1850*c8dee2aaSAndroid Build Coastguard Worker     auto convolve = [&](SkIRect rect) {
1851*c8dee2aaSAndroid Build Coastguard Worker         // Transform rect into the render target's coord system.
1852*c8dee2aaSAndroid Build Coastguard Worker         rect.offset(-rtcToSrcOffset);
1853*c8dee2aaSAndroid Build Coastguard Worker         convolve_gaussian_1d(dstSDC.get(),
1854*c8dee2aaSAndroid Build Coastguard Worker                              srcView,
1855*c8dee2aaSAndroid Build Coastguard Worker                              srcBounds,
1856*c8dee2aaSAndroid Build Coastguard Worker                              rtcToSrcOffset,
1857*c8dee2aaSAndroid Build Coastguard Worker                              rect,
1858*c8dee2aaSAndroid Build Coastguard Worker                              srcAlphaType,
1859*c8dee2aaSAndroid Build Coastguard Worker                              direction,
1860*c8dee2aaSAndroid Build Coastguard Worker                              radius,
1861*c8dee2aaSAndroid Build Coastguard Worker                              sigma,
1862*c8dee2aaSAndroid Build Coastguard Worker                              mode);
1863*c8dee2aaSAndroid Build Coastguard Worker     };
1864*c8dee2aaSAndroid Build Coastguard Worker     auto clear = [&](SkIRect rect) {
1865*c8dee2aaSAndroid Build Coastguard Worker         // Transform rect into the render target's coord system.
1866*c8dee2aaSAndroid Build Coastguard Worker         rect.offset(-rtcToSrcOffset);
1867*c8dee2aaSAndroid Build Coastguard Worker         dstSDC->clearAtLeast(rect, SK_PMColor4fTRANSPARENT);
1868*c8dee2aaSAndroid Build Coastguard Worker     };
1869*c8dee2aaSAndroid Build Coastguard Worker 
1870*c8dee2aaSAndroid Build Coastguard Worker     // Doing mid separately will cause two draws to occur (left and right batch together). At
1871*c8dee2aaSAndroid Build Coastguard Worker     // small sizes of mid it is worse to issue more draws than to just execute the slightly
1872*c8dee2aaSAndroid Build Coastguard Worker     // more complicated shader that implements the tile mode across mid. This threshold is
1873*c8dee2aaSAndroid Build Coastguard Worker     // very arbitrary right now. It is believed that a 21x44 mid on a Moto G4 is a significant
1874*c8dee2aaSAndroid Build Coastguard Worker     // regression compared to doing one draw but it has not been locally evaluated or tuned.
1875*c8dee2aaSAndroid Build Coastguard Worker     // The optimal cutoff is likely to vary by GPU.
1876*c8dee2aaSAndroid Build Coastguard Worker     if (!mid.isEmpty() && mid.width() * mid.height() < 256 * 256) {
1877*c8dee2aaSAndroid Build Coastguard Worker         left.join(mid);
1878*c8dee2aaSAndroid Build Coastguard Worker         left.join(right);
1879*c8dee2aaSAndroid Build Coastguard Worker         mid = SkIRect::MakeEmpty();
1880*c8dee2aaSAndroid Build Coastguard Worker         right = SkIRect::MakeEmpty();
1881*c8dee2aaSAndroid Build Coastguard Worker         // It's unknown whether for kDecal it'd be better to expand the draw rather than a draw and
1882*c8dee2aaSAndroid Build Coastguard Worker         // up to two clears.
1883*c8dee2aaSAndroid Build Coastguard Worker         if (mode == SkTileMode::kClamp) {
1884*c8dee2aaSAndroid Build Coastguard Worker             left.join(top);
1885*c8dee2aaSAndroid Build Coastguard Worker             left.join(bottom);
1886*c8dee2aaSAndroid Build Coastguard Worker             top = SkIRect::MakeEmpty();
1887*c8dee2aaSAndroid Build Coastguard Worker             bottom = SkIRect::MakeEmpty();
1888*c8dee2aaSAndroid Build Coastguard Worker         }
1889*c8dee2aaSAndroid Build Coastguard Worker     }
1890*c8dee2aaSAndroid Build Coastguard Worker 
1891*c8dee2aaSAndroid Build Coastguard Worker     if (!top.isEmpty()) {
1892*c8dee2aaSAndroid Build Coastguard Worker         if (mode == SkTileMode::kDecal) {
1893*c8dee2aaSAndroid Build Coastguard Worker             clear(top);
1894*c8dee2aaSAndroid Build Coastguard Worker         } else {
1895*c8dee2aaSAndroid Build Coastguard Worker             convolve(top);
1896*c8dee2aaSAndroid Build Coastguard Worker         }
1897*c8dee2aaSAndroid Build Coastguard Worker     }
1898*c8dee2aaSAndroid Build Coastguard Worker 
1899*c8dee2aaSAndroid Build Coastguard Worker     if (!bottom.isEmpty()) {
1900*c8dee2aaSAndroid Build Coastguard Worker         if (mode == SkTileMode::kDecal) {
1901*c8dee2aaSAndroid Build Coastguard Worker             clear(bottom);
1902*c8dee2aaSAndroid Build Coastguard Worker         } else {
1903*c8dee2aaSAndroid Build Coastguard Worker             convolve(bottom);
1904*c8dee2aaSAndroid Build Coastguard Worker         }
1905*c8dee2aaSAndroid Build Coastguard Worker     }
1906*c8dee2aaSAndroid Build Coastguard Worker 
1907*c8dee2aaSAndroid Build Coastguard Worker     if (mid.isEmpty()) {
1908*c8dee2aaSAndroid Build Coastguard Worker         convolve(left);
1909*c8dee2aaSAndroid Build Coastguard Worker     } else {
1910*c8dee2aaSAndroid Build Coastguard Worker         convolve(left);
1911*c8dee2aaSAndroid Build Coastguard Worker         convolve(right);
1912*c8dee2aaSAndroid Build Coastguard Worker         convolve(mid);
1913*c8dee2aaSAndroid Build Coastguard Worker     }
1914*c8dee2aaSAndroid Build Coastguard Worker     return dstSDC;
1915*c8dee2aaSAndroid Build Coastguard Worker }
1916*c8dee2aaSAndroid Build Coastguard Worker 
1917*c8dee2aaSAndroid Build Coastguard Worker // Expand the contents of 'src' to fit in 'dstSize'. At this point, we are expanding an intermediate
1918*c8dee2aaSAndroid Build Coastguard Worker // image, so there's no need to account for a proxy offset from the original input.
reexpand(GrRecordingContext * rContext,std::unique_ptr<skgpu::ganesh::SurfaceContext> src,const SkRect & srcBounds,SkISize dstSize,sk_sp<SkColorSpace> colorSpace,SkBackingFit fit)1919*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<skgpu::ganesh::SurfaceDrawContext> reexpand(
1920*c8dee2aaSAndroid Build Coastguard Worker         GrRecordingContext* rContext,
1921*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<skgpu::ganesh::SurfaceContext> src,
1922*c8dee2aaSAndroid Build Coastguard Worker         const SkRect& srcBounds,
1923*c8dee2aaSAndroid Build Coastguard Worker         SkISize dstSize,
1924*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<SkColorSpace> colorSpace,
1925*c8dee2aaSAndroid Build Coastguard Worker         SkBackingFit fit) {
1926*c8dee2aaSAndroid Build Coastguard Worker     GrSurfaceProxyView srcView = src->readSurfaceView();
1927*c8dee2aaSAndroid Build Coastguard Worker     if (!srcView.asTextureProxy()) {
1928*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
1929*c8dee2aaSAndroid Build Coastguard Worker     }
1930*c8dee2aaSAndroid Build Coastguard Worker 
1931*c8dee2aaSAndroid Build Coastguard Worker     GrColorType srcColorType = src->colorInfo().colorType();
1932*c8dee2aaSAndroid Build Coastguard Worker     SkAlphaType srcAlphaType = src->colorInfo().alphaType();
1933*c8dee2aaSAndroid Build Coastguard Worker 
1934*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_USE_PADDED_BLUR_UPSCALE)
1935*c8dee2aaSAndroid Build Coastguard Worker     // The blur output completely filled the src SurfaceContext, so that is our subset boundary,
1936*c8dee2aaSAndroid Build Coastguard Worker     // ensuring we don't access undefined pixels in the approx-fit backing texture.
1937*c8dee2aaSAndroid Build Coastguard Worker     SkRect srcContent = SkRect::MakeIWH(src->width(), src->height());
1938*c8dee2aaSAndroid Build Coastguard Worker #endif
1939*c8dee2aaSAndroid Build Coastguard Worker 
1940*c8dee2aaSAndroid Build Coastguard Worker     src.reset();  // no longer needed
1941*c8dee2aaSAndroid Build Coastguard Worker 
1942*c8dee2aaSAndroid Build Coastguard Worker     // Create the sdc with default SkSurfaceProps. Gaussian blurs will soon use a
1943*c8dee2aaSAndroid Build Coastguard Worker     // SurfaceFillContext, at which point the SkSurfaceProps won't exist anymore.
1944*c8dee2aaSAndroid Build Coastguard Worker     auto dstSDC = skgpu::ganesh::SurfaceDrawContext::Make(rContext,
1945*c8dee2aaSAndroid Build Coastguard Worker                                                           srcColorType,
1946*c8dee2aaSAndroid Build Coastguard Worker                                                           std::move(colorSpace),
1947*c8dee2aaSAndroid Build Coastguard Worker                                                           fit,
1948*c8dee2aaSAndroid Build Coastguard Worker                                                           dstSize,
1949*c8dee2aaSAndroid Build Coastguard Worker                                                           SkSurfaceProps(),
1950*c8dee2aaSAndroid Build Coastguard Worker                                                           /*label=*/"SurfaceDrawContext_Reexpand",
1951*c8dee2aaSAndroid Build Coastguard Worker                                                           /* sampleCnt= */ 1,
1952*c8dee2aaSAndroid Build Coastguard Worker                                                           skgpu::Mipmapped::kNo,
1953*c8dee2aaSAndroid Build Coastguard Worker                                                           srcView.proxy()->isProtected(),
1954*c8dee2aaSAndroid Build Coastguard Worker                                                           srcView.origin());
1955*c8dee2aaSAndroid Build Coastguard Worker     if (!dstSDC) {
1956*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
1957*c8dee2aaSAndroid Build Coastguard Worker     }
1958*c8dee2aaSAndroid Build Coastguard Worker 
1959*c8dee2aaSAndroid Build Coastguard Worker     GrPaint paint;
1960*c8dee2aaSAndroid Build Coastguard Worker     auto fp = GrTextureEffect::MakeSubset(std::move(srcView),
1961*c8dee2aaSAndroid Build Coastguard Worker                                           srcAlphaType,
1962*c8dee2aaSAndroid Build Coastguard Worker                                           SkMatrix::I(),
1963*c8dee2aaSAndroid Build Coastguard Worker                                           GrSamplerState::Filter::kLinear,
1964*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_USE_PADDED_BLUR_UPSCALE)
1965*c8dee2aaSAndroid Build Coastguard Worker                                           srcContent,
1966*c8dee2aaSAndroid Build Coastguard Worker #else
1967*c8dee2aaSAndroid Build Coastguard Worker                                           srcBounds,
1968*c8dee2aaSAndroid Build Coastguard Worker                                           srcBounds,
1969*c8dee2aaSAndroid Build Coastguard Worker #endif
1970*c8dee2aaSAndroid Build Coastguard Worker                                           *rContext->priv().caps());
1971*c8dee2aaSAndroid Build Coastguard Worker     paint.setColorFragmentProcessor(std::move(fp));
1972*c8dee2aaSAndroid Build Coastguard Worker     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
1973*c8dee2aaSAndroid Build Coastguard Worker 
1974*c8dee2aaSAndroid Build Coastguard Worker     dstSDC->fillRectToRect(
1975*c8dee2aaSAndroid Build Coastguard Worker             nullptr, std::move(paint), GrAA::kNo, SkMatrix::I(), SkRect::Make(dstSize), srcBounds);
1976*c8dee2aaSAndroid Build Coastguard Worker 
1977*c8dee2aaSAndroid Build Coastguard Worker     return dstSDC;
1978*c8dee2aaSAndroid Build Coastguard Worker }
1979*c8dee2aaSAndroid Build Coastguard Worker 
two_pass_gaussian(GrRecordingContext * rContext,GrSurfaceProxyView srcView,GrColorType srcColorType,SkAlphaType srcAlphaType,sk_sp<SkColorSpace> colorSpace,SkIRect srcBounds,SkIRect dstBounds,float sigmaX,float sigmaY,int radiusX,int radiusY,SkTileMode mode,SkBackingFit fit)1980*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<skgpu::ganesh::SurfaceDrawContext> two_pass_gaussian(
1981*c8dee2aaSAndroid Build Coastguard Worker         GrRecordingContext* rContext,
1982*c8dee2aaSAndroid Build Coastguard Worker         GrSurfaceProxyView srcView,
1983*c8dee2aaSAndroid Build Coastguard Worker         GrColorType srcColorType,
1984*c8dee2aaSAndroid Build Coastguard Worker         SkAlphaType srcAlphaType,
1985*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<SkColorSpace> colorSpace,
1986*c8dee2aaSAndroid Build Coastguard Worker         SkIRect srcBounds,
1987*c8dee2aaSAndroid Build Coastguard Worker         SkIRect dstBounds,
1988*c8dee2aaSAndroid Build Coastguard Worker         float sigmaX,
1989*c8dee2aaSAndroid Build Coastguard Worker         float sigmaY,
1990*c8dee2aaSAndroid Build Coastguard Worker         int radiusX,
1991*c8dee2aaSAndroid Build Coastguard Worker         int radiusY,
1992*c8dee2aaSAndroid Build Coastguard Worker         SkTileMode mode,
1993*c8dee2aaSAndroid Build Coastguard Worker         SkBackingFit fit) {
1994*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(radiusX || radiusY);
1995*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<skgpu::ganesh::SurfaceDrawContext> dstSDC;
1996*c8dee2aaSAndroid Build Coastguard Worker     if (radiusX > 0) {
1997*c8dee2aaSAndroid Build Coastguard Worker         SkBackingFit xFit = radiusY > 0 ? SkBackingFit::kApprox : fit;
1998*c8dee2aaSAndroid Build Coastguard Worker         // Expand the dstBounds vertically to produce necessary content for the y-pass. Then we will
1999*c8dee2aaSAndroid Build Coastguard Worker         // clip these in a tile-mode dependent way to ensure the tile-mode gets implemented
2000*c8dee2aaSAndroid Build Coastguard Worker         // correctly. However, if we're not going to do a y-pass then we must use the original
2001*c8dee2aaSAndroid Build Coastguard Worker         // dstBounds without clipping to produce the correct output size.
2002*c8dee2aaSAndroid Build Coastguard Worker         SkIRect xPassDstBounds = dstBounds;
2003*c8dee2aaSAndroid Build Coastguard Worker         if (radiusY) {
2004*c8dee2aaSAndroid Build Coastguard Worker             xPassDstBounds.outset(0, radiusY);
2005*c8dee2aaSAndroid Build Coastguard Worker             if (mode == SkTileMode::kRepeat || mode == SkTileMode::kMirror) {
2006*c8dee2aaSAndroid Build Coastguard Worker                 int srcH = srcBounds.height();
2007*c8dee2aaSAndroid Build Coastguard Worker                 int srcTop = srcBounds.top();
2008*c8dee2aaSAndroid Build Coastguard Worker                 if (mode == SkTileMode::kMirror) {
2009*c8dee2aaSAndroid Build Coastguard Worker                     srcTop -= srcH;
2010*c8dee2aaSAndroid Build Coastguard Worker                     srcH *= 2;
2011*c8dee2aaSAndroid Build Coastguard Worker                 }
2012*c8dee2aaSAndroid Build Coastguard Worker 
2013*c8dee2aaSAndroid Build Coastguard Worker                 float floatH = srcH;
2014*c8dee2aaSAndroid Build Coastguard Worker                 // First row above the dst rect where we should restart the tile mode.
2015*c8dee2aaSAndroid Build Coastguard Worker                 int n = sk_float_floor2int_no_saturate((xPassDstBounds.top() - srcTop) / floatH);
2016*c8dee2aaSAndroid Build Coastguard Worker                 int topClip = srcTop + n * srcH;
2017*c8dee2aaSAndroid Build Coastguard Worker 
2018*c8dee2aaSAndroid Build Coastguard Worker                 // First row above below the dst rect where we should restart the tile mode.
2019*c8dee2aaSAndroid Build Coastguard Worker                 n = sk_float_ceil2int_no_saturate((xPassDstBounds.bottom() - srcBounds.bottom()) /
2020*c8dee2aaSAndroid Build Coastguard Worker                                                   floatH);
2021*c8dee2aaSAndroid Build Coastguard Worker                 int bottomClip = srcBounds.bottom() + n * srcH;
2022*c8dee2aaSAndroid Build Coastguard Worker 
2023*c8dee2aaSAndroid Build Coastguard Worker                 xPassDstBounds.fTop = std::max(xPassDstBounds.top(), topClip);
2024*c8dee2aaSAndroid Build Coastguard Worker                 xPassDstBounds.fBottom = std::min(xPassDstBounds.bottom(), bottomClip);
2025*c8dee2aaSAndroid Build Coastguard Worker             } else {
2026*c8dee2aaSAndroid Build Coastguard Worker                 if (xPassDstBounds.fBottom <= srcBounds.top()) {
2027*c8dee2aaSAndroid Build Coastguard Worker                     if (mode == SkTileMode::kDecal) {
2028*c8dee2aaSAndroid Build Coastguard Worker                         return nullptr;
2029*c8dee2aaSAndroid Build Coastguard Worker                     }
2030*c8dee2aaSAndroid Build Coastguard Worker                     xPassDstBounds.fTop = srcBounds.top();
2031*c8dee2aaSAndroid Build Coastguard Worker                     xPassDstBounds.fBottom = xPassDstBounds.fTop + 1;
2032*c8dee2aaSAndroid Build Coastguard Worker                 } else if (xPassDstBounds.fTop >= srcBounds.bottom()) {
2033*c8dee2aaSAndroid Build Coastguard Worker                     if (mode == SkTileMode::kDecal) {
2034*c8dee2aaSAndroid Build Coastguard Worker                         return nullptr;
2035*c8dee2aaSAndroid Build Coastguard Worker                     }
2036*c8dee2aaSAndroid Build Coastguard Worker                     xPassDstBounds.fBottom = srcBounds.bottom();
2037*c8dee2aaSAndroid Build Coastguard Worker                     xPassDstBounds.fTop = xPassDstBounds.fBottom - 1;
2038*c8dee2aaSAndroid Build Coastguard Worker                 } else {
2039*c8dee2aaSAndroid Build Coastguard Worker                     xPassDstBounds.fTop = std::max(xPassDstBounds.fTop, srcBounds.top());
2040*c8dee2aaSAndroid Build Coastguard Worker                     xPassDstBounds.fBottom = std::min(xPassDstBounds.fBottom, srcBounds.bottom());
2041*c8dee2aaSAndroid Build Coastguard Worker                 }
2042*c8dee2aaSAndroid Build Coastguard Worker                 int leftSrcEdge = srcBounds.fLeft - radiusX;
2043*c8dee2aaSAndroid Build Coastguard Worker                 int rightSrcEdge = srcBounds.fRight + radiusX;
2044*c8dee2aaSAndroid Build Coastguard Worker                 if (mode == SkTileMode::kClamp) {
2045*c8dee2aaSAndroid Build Coastguard Worker                     // In clamp the column just outside the src bounds has the same value as the
2046*c8dee2aaSAndroid Build Coastguard Worker                     // column just inside, unlike decal.
2047*c8dee2aaSAndroid Build Coastguard Worker                     leftSrcEdge += 1;
2048*c8dee2aaSAndroid Build Coastguard Worker                     rightSrcEdge -= 1;
2049*c8dee2aaSAndroid Build Coastguard Worker                 }
2050*c8dee2aaSAndroid Build Coastguard Worker                 if (xPassDstBounds.fRight <= leftSrcEdge) {
2051*c8dee2aaSAndroid Build Coastguard Worker                     if (mode == SkTileMode::kDecal) {
2052*c8dee2aaSAndroid Build Coastguard Worker                         return nullptr;
2053*c8dee2aaSAndroid Build Coastguard Worker                     }
2054*c8dee2aaSAndroid Build Coastguard Worker                     xPassDstBounds.fLeft = xPassDstBounds.fRight - 1;
2055*c8dee2aaSAndroid Build Coastguard Worker                 } else {
2056*c8dee2aaSAndroid Build Coastguard Worker                     xPassDstBounds.fLeft = std::max(xPassDstBounds.fLeft, leftSrcEdge);
2057*c8dee2aaSAndroid Build Coastguard Worker                 }
2058*c8dee2aaSAndroid Build Coastguard Worker                 if (xPassDstBounds.fLeft >= rightSrcEdge) {
2059*c8dee2aaSAndroid Build Coastguard Worker                     if (mode == SkTileMode::kDecal) {
2060*c8dee2aaSAndroid Build Coastguard Worker                         return nullptr;
2061*c8dee2aaSAndroid Build Coastguard Worker                     }
2062*c8dee2aaSAndroid Build Coastguard Worker                     xPassDstBounds.fRight = xPassDstBounds.fLeft + 1;
2063*c8dee2aaSAndroid Build Coastguard Worker                 } else {
2064*c8dee2aaSAndroid Build Coastguard Worker                     xPassDstBounds.fRight = std::min(xPassDstBounds.fRight, rightSrcEdge);
2065*c8dee2aaSAndroid Build Coastguard Worker                 }
2066*c8dee2aaSAndroid Build Coastguard Worker             }
2067*c8dee2aaSAndroid Build Coastguard Worker         }
2068*c8dee2aaSAndroid Build Coastguard Worker         dstSDC = convolve_gaussian(rContext,
2069*c8dee2aaSAndroid Build Coastguard Worker                                    std::move(srcView),
2070*c8dee2aaSAndroid Build Coastguard Worker                                    srcColorType,
2071*c8dee2aaSAndroid Build Coastguard Worker                                    srcAlphaType,
2072*c8dee2aaSAndroid Build Coastguard Worker                                    srcBounds,
2073*c8dee2aaSAndroid Build Coastguard Worker                                    xPassDstBounds,
2074*c8dee2aaSAndroid Build Coastguard Worker                                    Direction::kX,
2075*c8dee2aaSAndroid Build Coastguard Worker                                    radiusX,
2076*c8dee2aaSAndroid Build Coastguard Worker                                    sigmaX,
2077*c8dee2aaSAndroid Build Coastguard Worker                                    mode,
2078*c8dee2aaSAndroid Build Coastguard Worker                                    colorSpace,
2079*c8dee2aaSAndroid Build Coastguard Worker                                    xFit);
2080*c8dee2aaSAndroid Build Coastguard Worker         if (!dstSDC) {
2081*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
2082*c8dee2aaSAndroid Build Coastguard Worker         }
2083*c8dee2aaSAndroid Build Coastguard Worker         srcView = dstSDC->readSurfaceView();
2084*c8dee2aaSAndroid Build Coastguard Worker         SkIVector newDstBoundsOffset = dstBounds.topLeft() - xPassDstBounds.topLeft();
2085*c8dee2aaSAndroid Build Coastguard Worker         dstBounds = SkIRect::MakeSize(dstBounds.size()).makeOffset(newDstBoundsOffset);
2086*c8dee2aaSAndroid Build Coastguard Worker         srcBounds = SkIRect::MakeSize(xPassDstBounds.size());
2087*c8dee2aaSAndroid Build Coastguard Worker     }
2088*c8dee2aaSAndroid Build Coastguard Worker 
2089*c8dee2aaSAndroid Build Coastguard Worker     if (!radiusY) {
2090*c8dee2aaSAndroid Build Coastguard Worker         return dstSDC;
2091*c8dee2aaSAndroid Build Coastguard Worker     }
2092*c8dee2aaSAndroid Build Coastguard Worker 
2093*c8dee2aaSAndroid Build Coastguard Worker     return convolve_gaussian(rContext,
2094*c8dee2aaSAndroid Build Coastguard Worker                              std::move(srcView),
2095*c8dee2aaSAndroid Build Coastguard Worker                              srcColorType,
2096*c8dee2aaSAndroid Build Coastguard Worker                              srcAlphaType,
2097*c8dee2aaSAndroid Build Coastguard Worker                              srcBounds,
2098*c8dee2aaSAndroid Build Coastguard Worker                              dstBounds,
2099*c8dee2aaSAndroid Build Coastguard Worker                              Direction::kY,
2100*c8dee2aaSAndroid Build Coastguard Worker                              radiusY,
2101*c8dee2aaSAndroid Build Coastguard Worker                              sigmaY,
2102*c8dee2aaSAndroid Build Coastguard Worker                              mode,
2103*c8dee2aaSAndroid Build Coastguard Worker                              std::move(colorSpace),
2104*c8dee2aaSAndroid Build Coastguard Worker                              fit);
2105*c8dee2aaSAndroid Build Coastguard Worker }
2106*c8dee2aaSAndroid Build Coastguard Worker 
GaussianBlur(GrRecordingContext * rContext,GrSurfaceProxyView srcView,GrColorType srcColorType,SkAlphaType srcAlphaType,sk_sp<SkColorSpace> colorSpace,SkIRect dstBounds,SkIRect srcBounds,float sigmaX,float sigmaY,SkTileMode mode,SkBackingFit fit)2107*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<skgpu::ganesh::SurfaceDrawContext> GaussianBlur(GrRecordingContext* rContext,
2108*c8dee2aaSAndroid Build Coastguard Worker                                                                 GrSurfaceProxyView srcView,
2109*c8dee2aaSAndroid Build Coastguard Worker                                                                 GrColorType srcColorType,
2110*c8dee2aaSAndroid Build Coastguard Worker                                                                 SkAlphaType srcAlphaType,
2111*c8dee2aaSAndroid Build Coastguard Worker                                                                 sk_sp<SkColorSpace> colorSpace,
2112*c8dee2aaSAndroid Build Coastguard Worker                                                                 SkIRect dstBounds,
2113*c8dee2aaSAndroid Build Coastguard Worker                                                                 SkIRect srcBounds,
2114*c8dee2aaSAndroid Build Coastguard Worker                                                                 float sigmaX,
2115*c8dee2aaSAndroid Build Coastguard Worker                                                                 float sigmaY,
2116*c8dee2aaSAndroid Build Coastguard Worker                                                                 SkTileMode mode,
2117*c8dee2aaSAndroid Build Coastguard Worker                                                                 SkBackingFit fit) {
2118*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(rContext);
2119*c8dee2aaSAndroid Build Coastguard Worker     TRACE_EVENT2("skia.gpu", "GaussianBlur", "sigmaX", sigmaX, "sigmaY", sigmaY);
2120*c8dee2aaSAndroid Build Coastguard Worker 
2121*c8dee2aaSAndroid Build Coastguard Worker     if (!srcView.asTextureProxy()) {
2122*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
2123*c8dee2aaSAndroid Build Coastguard Worker     }
2124*c8dee2aaSAndroid Build Coastguard Worker 
2125*c8dee2aaSAndroid Build Coastguard Worker     int maxRenderTargetSize = rContext->priv().caps()->maxRenderTargetSize();
2126*c8dee2aaSAndroid Build Coastguard Worker     if (dstBounds.width() > maxRenderTargetSize || dstBounds.height() > maxRenderTargetSize) {
2127*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
2128*c8dee2aaSAndroid Build Coastguard Worker     }
2129*c8dee2aaSAndroid Build Coastguard Worker 
2130*c8dee2aaSAndroid Build Coastguard Worker     int radiusX = skgpu::BlurSigmaRadius(sigmaX);
2131*c8dee2aaSAndroid Build Coastguard Worker     int radiusY = skgpu::BlurSigmaRadius(sigmaY);
2132*c8dee2aaSAndroid Build Coastguard Worker     // Attempt to reduce the srcBounds in order to detect that we can set the sigmas to zero or
2133*c8dee2aaSAndroid Build Coastguard Worker     // to reduce the amount of work to rescale the source if sigmas are large. TODO: Could consider
2134*c8dee2aaSAndroid Build Coastguard Worker     // how to minimize the required source bounds for repeat/mirror modes.
2135*c8dee2aaSAndroid Build Coastguard Worker     if (mode == SkTileMode::kClamp || mode == SkTileMode::kDecal) {
2136*c8dee2aaSAndroid Build Coastguard Worker         SkIRect reach = dstBounds.makeOutset(radiusX, radiusY);
2137*c8dee2aaSAndroid Build Coastguard Worker         SkIRect intersection;
2138*c8dee2aaSAndroid Build Coastguard Worker         if (!intersection.intersect(reach, srcBounds)) {
2139*c8dee2aaSAndroid Build Coastguard Worker             if (mode == SkTileMode::kDecal) {
2140*c8dee2aaSAndroid Build Coastguard Worker                 return nullptr;
2141*c8dee2aaSAndroid Build Coastguard Worker             } else {
2142*c8dee2aaSAndroid Build Coastguard Worker                 if (reach.fLeft >= srcBounds.fRight) {
2143*c8dee2aaSAndroid Build Coastguard Worker                     srcBounds.fLeft = srcBounds.fRight - 1;
2144*c8dee2aaSAndroid Build Coastguard Worker                 } else if (reach.fRight <= srcBounds.fLeft) {
2145*c8dee2aaSAndroid Build Coastguard Worker                     srcBounds.fRight = srcBounds.fLeft + 1;
2146*c8dee2aaSAndroid Build Coastguard Worker                 }
2147*c8dee2aaSAndroid Build Coastguard Worker                 if (reach.fTop >= srcBounds.fBottom) {
2148*c8dee2aaSAndroid Build Coastguard Worker                     srcBounds.fTop = srcBounds.fBottom - 1;
2149*c8dee2aaSAndroid Build Coastguard Worker                 } else if (reach.fBottom <= srcBounds.fTop) {
2150*c8dee2aaSAndroid Build Coastguard Worker                     srcBounds.fBottom = srcBounds.fTop + 1;
2151*c8dee2aaSAndroid Build Coastguard Worker                 }
2152*c8dee2aaSAndroid Build Coastguard Worker             }
2153*c8dee2aaSAndroid Build Coastguard Worker         } else {
2154*c8dee2aaSAndroid Build Coastguard Worker             srcBounds = intersection;
2155*c8dee2aaSAndroid Build Coastguard Worker         }
2156*c8dee2aaSAndroid Build Coastguard Worker     }
2157*c8dee2aaSAndroid Build Coastguard Worker 
2158*c8dee2aaSAndroid Build Coastguard Worker     if (mode != SkTileMode::kDecal) {
2159*c8dee2aaSAndroid Build Coastguard Worker         // All non-decal tile modes are equivalent for one pixel width/height src and amount to a
2160*c8dee2aaSAndroid Build Coastguard Worker         // single color value repeated at each column/row. Applying the normalized kernel to that
2161*c8dee2aaSAndroid Build Coastguard Worker         // column/row yields that same color. So no blurring is necessary.
2162*c8dee2aaSAndroid Build Coastguard Worker         if (srcBounds.width() == 1) {
2163*c8dee2aaSAndroid Build Coastguard Worker             sigmaX = 0.f;
2164*c8dee2aaSAndroid Build Coastguard Worker             radiusX = 0;
2165*c8dee2aaSAndroid Build Coastguard Worker         }
2166*c8dee2aaSAndroid Build Coastguard Worker         if (srcBounds.height() == 1) {
2167*c8dee2aaSAndroid Build Coastguard Worker             sigmaY = 0.f;
2168*c8dee2aaSAndroid Build Coastguard Worker             radiusY = 0;
2169*c8dee2aaSAndroid Build Coastguard Worker         }
2170*c8dee2aaSAndroid Build Coastguard Worker     }
2171*c8dee2aaSAndroid Build Coastguard Worker 
2172*c8dee2aaSAndroid Build Coastguard Worker     // If we determined that there is no blurring necessary in either direction then just do a
2173*c8dee2aaSAndroid Build Coastguard Worker     // a draw that applies the tile mode.
2174*c8dee2aaSAndroid Build Coastguard Worker     if (!radiusX && !radiusY) {
2175*c8dee2aaSAndroid Build Coastguard Worker         // Create the sdc with default SkSurfaceProps. Gaussian blurs will soon use a
2176*c8dee2aaSAndroid Build Coastguard Worker         // SurfaceFillContext, at which point the SkSurfaceProps won't exist anymore.
2177*c8dee2aaSAndroid Build Coastguard Worker         auto result =
2178*c8dee2aaSAndroid Build Coastguard Worker                 skgpu::ganesh::SurfaceDrawContext::Make(rContext,
2179*c8dee2aaSAndroid Build Coastguard Worker                                                         srcColorType,
2180*c8dee2aaSAndroid Build Coastguard Worker                                                         std::move(colorSpace),
2181*c8dee2aaSAndroid Build Coastguard Worker                                                         fit,
2182*c8dee2aaSAndroid Build Coastguard Worker                                                         dstBounds.size(),
2183*c8dee2aaSAndroid Build Coastguard Worker                                                         SkSurfaceProps(),
2184*c8dee2aaSAndroid Build Coastguard Worker                                                         /*label=*/"SurfaceDrawContext_GaussianBlur",
2185*c8dee2aaSAndroid Build Coastguard Worker                                                         /* sampleCnt= */ 1,
2186*c8dee2aaSAndroid Build Coastguard Worker                                                         skgpu::Mipmapped::kNo,
2187*c8dee2aaSAndroid Build Coastguard Worker                                                         srcView.proxy()->isProtected(),
2188*c8dee2aaSAndroid Build Coastguard Worker                                                         srcView.origin());
2189*c8dee2aaSAndroid Build Coastguard Worker         if (!result) {
2190*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
2191*c8dee2aaSAndroid Build Coastguard Worker         }
2192*c8dee2aaSAndroid Build Coastguard Worker         GrSamplerState sampler(SkTileModeToWrapMode(mode), GrSamplerState::Filter::kNearest);
2193*c8dee2aaSAndroid Build Coastguard Worker         auto fp = GrTextureEffect::MakeSubset(std::move(srcView),
2194*c8dee2aaSAndroid Build Coastguard Worker                                               srcAlphaType,
2195*c8dee2aaSAndroid Build Coastguard Worker                                               SkMatrix::I(),
2196*c8dee2aaSAndroid Build Coastguard Worker                                               sampler,
2197*c8dee2aaSAndroid Build Coastguard Worker                                               SkRect::Make(srcBounds),
2198*c8dee2aaSAndroid Build Coastguard Worker                                               SkRect::Make(dstBounds),
2199*c8dee2aaSAndroid Build Coastguard Worker                                               *rContext->priv().caps());
2200*c8dee2aaSAndroid Build Coastguard Worker         result->fillRectToRectWithFP(dstBounds, SkIRect::MakeSize(dstBounds.size()), std::move(fp));
2201*c8dee2aaSAndroid Build Coastguard Worker         return result;
2202*c8dee2aaSAndroid Build Coastguard Worker     }
2203*c8dee2aaSAndroid Build Coastguard Worker 
2204*c8dee2aaSAndroid Build Coastguard Worker     // Any sigma higher than the limit for the 1D linear-filtered Gaussian blur is downsampled. If
2205*c8dee2aaSAndroid Build Coastguard Worker     // the sigma in X and Y just so happen to fit in the 2D limit, we'll use that. The 2D limit is
2206*c8dee2aaSAndroid Build Coastguard Worker     // always less than the linear blur sigma limit.
2207*c8dee2aaSAndroid Build Coastguard Worker     static constexpr float kMaxSigma = skgpu::kMaxLinearBlurSigma;
2208*c8dee2aaSAndroid Build Coastguard Worker     if (sigmaX <= kMaxSigma && sigmaY <= kMaxSigma) {
2209*c8dee2aaSAndroid Build Coastguard Worker         // For really small blurs (certainly no wider than 5x5 on desktop GPUs) it is faster to just
2210*c8dee2aaSAndroid Build Coastguard Worker         // launch a single non separable kernel vs two launches.
2211*c8dee2aaSAndroid Build Coastguard Worker         const int kernelSize = skgpu::BlurKernelWidth(radiusX) * skgpu::BlurKernelWidth(radiusY);
2212*c8dee2aaSAndroid Build Coastguard Worker         if (radiusX > 0 && radiusY > 0 &&
2213*c8dee2aaSAndroid Build Coastguard Worker             kernelSize <= skgpu::kMaxBlurSamples &&
2214*c8dee2aaSAndroid Build Coastguard Worker             !rContext->priv().caps()->reducedShaderMode()) {
2215*c8dee2aaSAndroid Build Coastguard Worker             // Apply the proxy offset to src bounds and offset directly
2216*c8dee2aaSAndroid Build Coastguard Worker             return convolve_gaussian_2d(rContext,
2217*c8dee2aaSAndroid Build Coastguard Worker                                         std::move(srcView),
2218*c8dee2aaSAndroid Build Coastguard Worker                                         srcColorType,
2219*c8dee2aaSAndroid Build Coastguard Worker                                         srcBounds,
2220*c8dee2aaSAndroid Build Coastguard Worker                                         dstBounds,
2221*c8dee2aaSAndroid Build Coastguard Worker                                         radiusX,
2222*c8dee2aaSAndroid Build Coastguard Worker                                         radiusY,
2223*c8dee2aaSAndroid Build Coastguard Worker                                         sigmaX,
2224*c8dee2aaSAndroid Build Coastguard Worker                                         sigmaY,
2225*c8dee2aaSAndroid Build Coastguard Worker                                         mode,
2226*c8dee2aaSAndroid Build Coastguard Worker                                         std::move(colorSpace),
2227*c8dee2aaSAndroid Build Coastguard Worker                                         fit);
2228*c8dee2aaSAndroid Build Coastguard Worker         }
2229*c8dee2aaSAndroid Build Coastguard Worker         // This will automatically degenerate into a single pass of X or Y if only one of the
2230*c8dee2aaSAndroid Build Coastguard Worker         // radii are non-zero.
2231*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(skgpu::BlurLinearKernelWidth(radiusX) <= skgpu::kMaxBlurSamples &&
2232*c8dee2aaSAndroid Build Coastguard Worker                  skgpu::BlurLinearKernelWidth(radiusY) <= skgpu::kMaxBlurSamples);
2233*c8dee2aaSAndroid Build Coastguard Worker         return two_pass_gaussian(rContext,
2234*c8dee2aaSAndroid Build Coastguard Worker                                  std::move(srcView),
2235*c8dee2aaSAndroid Build Coastguard Worker                                  srcColorType,
2236*c8dee2aaSAndroid Build Coastguard Worker                                  srcAlphaType,
2237*c8dee2aaSAndroid Build Coastguard Worker                                  std::move(colorSpace),
2238*c8dee2aaSAndroid Build Coastguard Worker                                  srcBounds,
2239*c8dee2aaSAndroid Build Coastguard Worker                                  dstBounds,
2240*c8dee2aaSAndroid Build Coastguard Worker                                  sigmaX,
2241*c8dee2aaSAndroid Build Coastguard Worker                                  sigmaY,
2242*c8dee2aaSAndroid Build Coastguard Worker                                  radiusX,
2243*c8dee2aaSAndroid Build Coastguard Worker                                  radiusY,
2244*c8dee2aaSAndroid Build Coastguard Worker                                  mode,
2245*c8dee2aaSAndroid Build Coastguard Worker                                  fit);
2246*c8dee2aaSAndroid Build Coastguard Worker     }
2247*c8dee2aaSAndroid Build Coastguard Worker 
2248*c8dee2aaSAndroid Build Coastguard Worker     GrColorInfo colorInfo(srcColorType, srcAlphaType, colorSpace);
2249*c8dee2aaSAndroid Build Coastguard Worker     auto srcCtx = rContext->priv().makeSC(srcView, colorInfo);
2250*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(srcCtx);
2251*c8dee2aaSAndroid Build Coastguard Worker 
2252*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_USE_PADDED_BLUR_UPSCALE)
2253*c8dee2aaSAndroid Build Coastguard Worker     // When we are in clamp mode any artifacts in the edge pixels due to downscaling may be
2254*c8dee2aaSAndroid Build Coastguard Worker     // exacerbated because of the tile mode. The particularly egregious case is when the original
2255*c8dee2aaSAndroid Build Coastguard Worker     // image has transparent black around the edges and the downscaling pulls in some non-zero
2256*c8dee2aaSAndroid Build Coastguard Worker     // values from the interior. Ultimately it'd be better for performance if the calling code could
2257*c8dee2aaSAndroid Build Coastguard Worker     // give us extra context around the blur to account for this. We don't currently have a good way
2258*c8dee2aaSAndroid Build Coastguard Worker     // to communicate this up stack. So we leave a 1 pixel border around the rescaled src bounds.
2259*c8dee2aaSAndroid Build Coastguard Worker     // We populate the top 1 pixel tall row of this border by rescaling the top row of the original
2260*c8dee2aaSAndroid Build Coastguard Worker     // source bounds into it. Because this is only rescaling in x (i.e. rescaling a 1 pixel high
2261*c8dee2aaSAndroid Build Coastguard Worker     // row into a shorter but still 1 pixel high row) we won't read any interior values. And similar
2262*c8dee2aaSAndroid Build Coastguard Worker     // for the other three borders. We'll adjust the source/dest bounds rescaled blur so that this
2263*c8dee2aaSAndroid Build Coastguard Worker     // border of extra pixels is used as the edge pixels for clamp mode but the dest bounds
2264*c8dee2aaSAndroid Build Coastguard Worker     // corresponds only to the pixels inside the border (the normally rescaled pixels inside this
2265*c8dee2aaSAndroid Build Coastguard Worker     // border).
2266*c8dee2aaSAndroid Build Coastguard Worker     // Moreover, if we clamped the rescaled size to 1 column or row then we still have a sigma
2267*c8dee2aaSAndroid Build Coastguard Worker     // that is greater than kMaxSigma. By using a pad and making the src 3 wide/tall instead of
2268*c8dee2aaSAndroid Build Coastguard Worker     // 1 we can recurse again and do another downscale. Since mirror and repeat modes are trivial
2269*c8dee2aaSAndroid Build Coastguard Worker     // for a single col/row we only add padding based on sigma exceeding kMaxSigma for decal.
2270*c8dee2aaSAndroid Build Coastguard Worker     int padX = mode == SkTileMode::kClamp || (mode == SkTileMode::kDecal && sigmaX > kMaxSigma) ? 1
2271*c8dee2aaSAndroid Build Coastguard Worker                                                                                                 : 0;
2272*c8dee2aaSAndroid Build Coastguard Worker     int padY = mode == SkTileMode::kClamp || (mode == SkTileMode::kDecal && sigmaY > kMaxSigma) ? 1
2273*c8dee2aaSAndroid Build Coastguard Worker                                                                                                 : 0;
2274*c8dee2aaSAndroid Build Coastguard Worker #endif
2275*c8dee2aaSAndroid Build Coastguard Worker 
2276*c8dee2aaSAndroid Build Coastguard Worker     float scaleX = sigmaX > kMaxSigma ? kMaxSigma / sigmaX : 1.f;
2277*c8dee2aaSAndroid Build Coastguard Worker     float scaleY = sigmaY > kMaxSigma ? kMaxSigma / sigmaY : 1.f;
2278*c8dee2aaSAndroid Build Coastguard Worker     // We round down here so that when we recalculate sigmas we know they will be below
2279*c8dee2aaSAndroid Build Coastguard Worker     // kMaxSigma (but clamp to 1 do we don't have an empty texture).
2280*c8dee2aaSAndroid Build Coastguard Worker     SkISize rescaledSize = {std::max(sk_float_floor2int(srcBounds.width() * scaleX), 1),
2281*c8dee2aaSAndroid Build Coastguard Worker                             std::max(sk_float_floor2int(srcBounds.height() * scaleY), 1)};
2282*c8dee2aaSAndroid Build Coastguard Worker     // Compute the sigmas using the actual scale factors used once we integerized the
2283*c8dee2aaSAndroid Build Coastguard Worker     // rescaledSize.
2284*c8dee2aaSAndroid Build Coastguard Worker     scaleX = static_cast<float>(rescaledSize.width()) / srcBounds.width();
2285*c8dee2aaSAndroid Build Coastguard Worker     scaleY = static_cast<float>(rescaledSize.height()) / srcBounds.height();
2286*c8dee2aaSAndroid Build Coastguard Worker     sigmaX *= scaleX;
2287*c8dee2aaSAndroid Build Coastguard Worker     sigmaY *= scaleY;
2288*c8dee2aaSAndroid Build Coastguard Worker 
2289*c8dee2aaSAndroid Build Coastguard Worker #if !defined(SK_USE_PADDED_BLUR_UPSCALE)
2290*c8dee2aaSAndroid Build Coastguard Worker     // Historically, padX and padY were calculated after scaling sigmaX,Y, which meant that they
2291*c8dee2aaSAndroid Build Coastguard Worker     // would never be greater than kMaxSigma. This causes pixel diffs so must be guarded along with
2292*c8dee2aaSAndroid Build Coastguard Worker     // the rest of the padding dst behavior.
2293*c8dee2aaSAndroid Build Coastguard Worker     int padX = mode == SkTileMode::kClamp || (mode == SkTileMode::kDecal && sigmaX > kMaxSigma) ? 1
2294*c8dee2aaSAndroid Build Coastguard Worker                                                                                                 : 0;
2295*c8dee2aaSAndroid Build Coastguard Worker     int padY = mode == SkTileMode::kClamp || (mode == SkTileMode::kDecal && sigmaY > kMaxSigma) ? 1
2296*c8dee2aaSAndroid Build Coastguard Worker                                                                                                 : 0;
2297*c8dee2aaSAndroid Build Coastguard Worker #endif
2298*c8dee2aaSAndroid Build Coastguard Worker 
2299*c8dee2aaSAndroid Build Coastguard Worker     // Create the sdc with default SkSurfaceProps. Gaussian blurs will soon use a
2300*c8dee2aaSAndroid Build Coastguard Worker     // SurfaceFillContext, at which point the SkSurfaceProps won't exist anymore.
2301*c8dee2aaSAndroid Build Coastguard Worker     auto rescaledSDC = skgpu::ganesh::SurfaceDrawContext::Make(
2302*c8dee2aaSAndroid Build Coastguard Worker             srcCtx->recordingContext(),
2303*c8dee2aaSAndroid Build Coastguard Worker             colorInfo.colorType(),
2304*c8dee2aaSAndroid Build Coastguard Worker             colorInfo.refColorSpace(),
2305*c8dee2aaSAndroid Build Coastguard Worker             SkBackingFit::kApprox,
2306*c8dee2aaSAndroid Build Coastguard Worker             {rescaledSize.width() + 2 * padX, rescaledSize.height() + 2 * padY},
2307*c8dee2aaSAndroid Build Coastguard Worker             SkSurfaceProps(),
2308*c8dee2aaSAndroid Build Coastguard Worker             /*label=*/"RescaledSurfaceDrawContext",
2309*c8dee2aaSAndroid Build Coastguard Worker             /* sampleCnt= */ 1,
2310*c8dee2aaSAndroid Build Coastguard Worker             skgpu::Mipmapped::kNo,
2311*c8dee2aaSAndroid Build Coastguard Worker             srcCtx->asSurfaceProxy()->isProtected(),
2312*c8dee2aaSAndroid Build Coastguard Worker             srcCtx->origin());
2313*c8dee2aaSAndroid Build Coastguard Worker     if (!rescaledSDC) {
2314*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
2315*c8dee2aaSAndroid Build Coastguard Worker     }
2316*c8dee2aaSAndroid Build Coastguard Worker     if ((padX || padY) && mode == SkTileMode::kDecal) {
2317*c8dee2aaSAndroid Build Coastguard Worker         rescaledSDC->clear(SkPMColor4f{0, 0, 0, 0});
2318*c8dee2aaSAndroid Build Coastguard Worker     }
2319*c8dee2aaSAndroid Build Coastguard Worker     if (!srcCtx->rescaleInto(rescaledSDC.get(),
2320*c8dee2aaSAndroid Build Coastguard Worker                              SkIRect::MakeSize(rescaledSize).makeOffset(padX, padY),
2321*c8dee2aaSAndroid Build Coastguard Worker                              srcBounds,
2322*c8dee2aaSAndroid Build Coastguard Worker                              SkSurface::RescaleGamma::kSrc,
2323*c8dee2aaSAndroid Build Coastguard Worker                              SkSurface::RescaleMode::kRepeatedLinear)) {
2324*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
2325*c8dee2aaSAndroid Build Coastguard Worker     }
2326*c8dee2aaSAndroid Build Coastguard Worker     if (mode == SkTileMode::kClamp) {
2327*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(padX == 1 && padY == 1);
2328*c8dee2aaSAndroid Build Coastguard Worker         // Rather than run a potentially multi-pass rescaler on single rows/columns we just do a
2329*c8dee2aaSAndroid Build Coastguard Worker         // single bilerp draw. If we find this quality unacceptable we should think more about how
2330*c8dee2aaSAndroid Build Coastguard Worker         // to rescale these with better quality but without 4 separate multi-pass downscales.
2331*c8dee2aaSAndroid Build Coastguard Worker         auto cheapDownscale = [&](SkIRect dstRect, SkIRect srcRect) {
2332*c8dee2aaSAndroid Build Coastguard Worker             rescaledSDC->drawTexture(nullptr,
2333*c8dee2aaSAndroid Build Coastguard Worker                                      srcCtx->readSurfaceView(),
2334*c8dee2aaSAndroid Build Coastguard Worker                                      srcAlphaType,
2335*c8dee2aaSAndroid Build Coastguard Worker                                      GrSamplerState::Filter::kLinear,
2336*c8dee2aaSAndroid Build Coastguard Worker                                      GrSamplerState::MipmapMode::kNone,
2337*c8dee2aaSAndroid Build Coastguard Worker                                      SkBlendMode::kSrc,
2338*c8dee2aaSAndroid Build Coastguard Worker                                      SK_PMColor4fWHITE,
2339*c8dee2aaSAndroid Build Coastguard Worker                                      SkRect::Make(srcRect),
2340*c8dee2aaSAndroid Build Coastguard Worker                                      SkRect::Make(dstRect),
2341*c8dee2aaSAndroid Build Coastguard Worker                                      GrQuadAAFlags::kNone,
2342*c8dee2aaSAndroid Build Coastguard Worker                                      SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint,
2343*c8dee2aaSAndroid Build Coastguard Worker                                      SkMatrix::I(),
2344*c8dee2aaSAndroid Build Coastguard Worker                                      nullptr);
2345*c8dee2aaSAndroid Build Coastguard Worker         };
2346*c8dee2aaSAndroid Build Coastguard Worker         auto [dw, dh] = rescaledSize;
2347*c8dee2aaSAndroid Build Coastguard Worker         // The are the src rows and columns from the source that we will scale into the dst padding.
2348*c8dee2aaSAndroid Build Coastguard Worker         float sLCol = srcBounds.left();
2349*c8dee2aaSAndroid Build Coastguard Worker         float sTRow = srcBounds.top();
2350*c8dee2aaSAndroid Build Coastguard Worker         float sRCol = srcBounds.right() - 1;
2351*c8dee2aaSAndroid Build Coastguard Worker         float sBRow = srcBounds.bottom() - 1;
2352*c8dee2aaSAndroid Build Coastguard Worker 
2353*c8dee2aaSAndroid Build Coastguard Worker         int sx = srcBounds.left();
2354*c8dee2aaSAndroid Build Coastguard Worker         int sy = srcBounds.top();
2355*c8dee2aaSAndroid Build Coastguard Worker         int sw = srcBounds.width();
2356*c8dee2aaSAndroid Build Coastguard Worker         int sh = srcBounds.height();
2357*c8dee2aaSAndroid Build Coastguard Worker 
2358*c8dee2aaSAndroid Build Coastguard Worker         // Downscale the edges from the original source. These draws should batch together (and with
2359*c8dee2aaSAndroid Build Coastguard Worker         // the above interior rescaling when it is a single pass).
2360*c8dee2aaSAndroid Build Coastguard Worker         cheapDownscale(SkIRect::MakeXYWH(0, 1, 1, dh), SkIRect::MakeXYWH(sLCol, sy, 1, sh));
2361*c8dee2aaSAndroid Build Coastguard Worker         cheapDownscale(SkIRect::MakeXYWH(1, 0, dw, 1), SkIRect::MakeXYWH(sx, sTRow, sw, 1));
2362*c8dee2aaSAndroid Build Coastguard Worker         cheapDownscale(SkIRect::MakeXYWH(dw + 1, 1, 1, dh), SkIRect::MakeXYWH(sRCol, sy, 1, sh));
2363*c8dee2aaSAndroid Build Coastguard Worker         cheapDownscale(SkIRect::MakeXYWH(1, dh + 1, dw, 1), SkIRect::MakeXYWH(sx, sBRow, sw, 1));
2364*c8dee2aaSAndroid Build Coastguard Worker 
2365*c8dee2aaSAndroid Build Coastguard Worker         // Copy the corners from the original source. These would batch with the edges except that
2366*c8dee2aaSAndroid Build Coastguard Worker         // at time of writing we recognize these can use kNearest and downgrade the filter. So they
2367*c8dee2aaSAndroid Build Coastguard Worker         // batch with each other but not the edge draws.
2368*c8dee2aaSAndroid Build Coastguard Worker         cheapDownscale(SkIRect::MakeXYWH(0, 0, 1, 1), SkIRect::MakeXYWH(sLCol, sTRow, 1, 1));
2369*c8dee2aaSAndroid Build Coastguard Worker         cheapDownscale(SkIRect::MakeXYWH(dw + 1, 0, 1, 1), SkIRect::MakeXYWH(sRCol, sTRow, 1, 1));
2370*c8dee2aaSAndroid Build Coastguard Worker         cheapDownscale(SkIRect::MakeXYWH(dw + 1, dh + 1, 1, 1),
2371*c8dee2aaSAndroid Build Coastguard Worker                        SkIRect::MakeXYWH(sRCol, sBRow, 1, 1));
2372*c8dee2aaSAndroid Build Coastguard Worker         cheapDownscale(SkIRect::MakeXYWH(0, dh + 1, 1, 1), SkIRect::MakeXYWH(sLCol, sBRow, 1, 1));
2373*c8dee2aaSAndroid Build Coastguard Worker     }
2374*c8dee2aaSAndroid Build Coastguard Worker     srcView = rescaledSDC->readSurfaceView();
2375*c8dee2aaSAndroid Build Coastguard Worker     // Drop the contexts so we don't hold the proxies longer than necessary.
2376*c8dee2aaSAndroid Build Coastguard Worker     rescaledSDC.reset();
2377*c8dee2aaSAndroid Build Coastguard Worker     srcCtx.reset();
2378*c8dee2aaSAndroid Build Coastguard Worker 
2379*c8dee2aaSAndroid Build Coastguard Worker     // Compute the dst bounds in the scaled down space. First move the origin to be at the top
2380*c8dee2aaSAndroid Build Coastguard Worker     // left since we trimmed off everything above and to the left of the original src bounds during
2381*c8dee2aaSAndroid Build Coastguard Worker     // the rescale.
2382*c8dee2aaSAndroid Build Coastguard Worker     SkRect scaledDstBounds = SkRect::Make(dstBounds.makeOffset(-srcBounds.topLeft()));
2383*c8dee2aaSAndroid Build Coastguard Worker     scaledDstBounds.fLeft *= scaleX;
2384*c8dee2aaSAndroid Build Coastguard Worker     scaledDstBounds.fTop *= scaleY;
2385*c8dee2aaSAndroid Build Coastguard Worker     scaledDstBounds.fRight *= scaleX;
2386*c8dee2aaSAndroid Build Coastguard Worker     scaledDstBounds.fBottom *= scaleY;
2387*c8dee2aaSAndroid Build Coastguard Worker     // Account for padding in our rescaled src, if any.
2388*c8dee2aaSAndroid Build Coastguard Worker     scaledDstBounds.offset(padX, padY);
2389*c8dee2aaSAndroid Build Coastguard Worker     // Turn the scaled down dst bounds into an integer pixel rect, adding 1px of padding to help
2390*c8dee2aaSAndroid Build Coastguard Worker     // with boundary sampling during re-expansion when there are extreme scale factors. This is
2391*c8dee2aaSAndroid Build Coastguard Worker     // particularly important when the blurs extend across Chrome raster tiles; w/o it the re-expand
2392*c8dee2aaSAndroid Build Coastguard Worker     // produces visible seams: crbug.com/1500021.
2393*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_USE_PADDED_BLUR_UPSCALE)
2394*c8dee2aaSAndroid Build Coastguard Worker     static constexpr int kDstPadding = 1;
2395*c8dee2aaSAndroid Build Coastguard Worker #else
2396*c8dee2aaSAndroid Build Coastguard Worker     static constexpr int kDstPadding = 0;
2397*c8dee2aaSAndroid Build Coastguard Worker #endif
2398*c8dee2aaSAndroid Build Coastguard Worker     auto scaledDstBoundsI = scaledDstBounds.roundOut();
2399*c8dee2aaSAndroid Build Coastguard Worker     scaledDstBoundsI.outset(kDstPadding, kDstPadding);
2400*c8dee2aaSAndroid Build Coastguard Worker 
2401*c8dee2aaSAndroid Build Coastguard Worker     SkIRect scaledSrcBounds = SkIRect::MakeSize(srcView.dimensions());
2402*c8dee2aaSAndroid Build Coastguard Worker     auto sdc = GaussianBlur(rContext,
2403*c8dee2aaSAndroid Build Coastguard Worker                             std::move(srcView),
2404*c8dee2aaSAndroid Build Coastguard Worker                             srcColorType,
2405*c8dee2aaSAndroid Build Coastguard Worker                             srcAlphaType,
2406*c8dee2aaSAndroid Build Coastguard Worker                             colorSpace,
2407*c8dee2aaSAndroid Build Coastguard Worker                             scaledDstBoundsI,
2408*c8dee2aaSAndroid Build Coastguard Worker                             scaledSrcBounds,
2409*c8dee2aaSAndroid Build Coastguard Worker                             sigmaX,
2410*c8dee2aaSAndroid Build Coastguard Worker                             sigmaY,
2411*c8dee2aaSAndroid Build Coastguard Worker                             mode,
2412*c8dee2aaSAndroid Build Coastguard Worker                             fit);
2413*c8dee2aaSAndroid Build Coastguard Worker     if (!sdc) {
2414*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
2415*c8dee2aaSAndroid Build Coastguard Worker     }
2416*c8dee2aaSAndroid Build Coastguard Worker 
2417*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(sdc->width() == scaledDstBoundsI.width() &&
2418*c8dee2aaSAndroid Build Coastguard Worker              sdc->height() == scaledDstBoundsI.height());
2419*c8dee2aaSAndroid Build Coastguard Worker     // We rounded out the integer scaled dst bounds. Select the fractional dst bounds from the
2420*c8dee2aaSAndroid Build Coastguard Worker     // integer dimension blurred result when we scale back up. This also accounts for the padding
2421*c8dee2aaSAndroid Build Coastguard Worker     // added to 'scaledDstBoundsI' when sampling from the blurred result.
2422*c8dee2aaSAndroid Build Coastguard Worker     scaledDstBounds.offset(-scaledDstBoundsI.left(), -scaledDstBoundsI.top());
2423*c8dee2aaSAndroid Build Coastguard Worker     return reexpand(rContext,
2424*c8dee2aaSAndroid Build Coastguard Worker                     std::move(sdc),
2425*c8dee2aaSAndroid Build Coastguard Worker                     scaledDstBounds,
2426*c8dee2aaSAndroid Build Coastguard Worker                     dstBounds.size(),
2427*c8dee2aaSAndroid Build Coastguard Worker                     std::move(colorSpace),
2428*c8dee2aaSAndroid Build Coastguard Worker                     fit);
2429*c8dee2aaSAndroid Build Coastguard Worker }
2430*c8dee2aaSAndroid Build Coastguard Worker 
2431*c8dee2aaSAndroid Build Coastguard Worker }  // namespace GrBlurUtils
2432