• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/ganesh/GrBlurUtils.h"
9 
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkColorSpace.h"
12 #include "include/core/SkPaint.h"
13 #include "include/gpu/GrDirectContext.h"
14 #include "include/gpu/GrRecordingContext.h"
15 #include "src/base/SkTLazy.h"
16 #include "src/core/SkDraw.h"
17 #include "src/core/SkMaskFilterBase.h"
18 #include "src/core/SkMatrixProvider.h"
19 #include "src/gpu/ganesh/GrCaps.h"
20 #include "src/gpu/ganesh/GrDirectContextPriv.h"
21 #include "src/gpu/ganesh/GrFixedClip.h"
22 #include "src/gpu/ganesh/GrProxyProvider.h"
23 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
24 #include "src/gpu/ganesh/GrResourceProvider.h"
25 #include "src/gpu/ganesh/GrStyle.h"
26 #include "src/gpu/ganesh/GrTextureProxy.h"
27 #include "src/gpu/ganesh/GrThreadSafeCache.h"
28 #include "src/gpu/ganesh/GrUtil.h"
29 #include "src/gpu/ganesh/SkGr.h"
30 #include "src/gpu/ganesh/SurfaceDrawContext.h"
31 #include "src/gpu/ganesh/effects/GrTextureEffect.h"
32 #include "src/gpu/ganesh/geometry/GrStyledShape.h"
33 
clip_bounds_quick_reject(const SkIRect & clipBounds,const SkIRect & rect)34 static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& rect) {
35     return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBounds, rect);
36 }
37 
38 static constexpr auto kMaskOrigin = kTopLeft_GrSurfaceOrigin;
39 
40 // Draw a mask using the supplied paint. Since the coverage/geometry
41 // is already burnt into the mask this boils down to a rect draw.
42 // Return true if the mask was successfully drawn.
draw_mask(skgpu::v1::SurfaceDrawContext * sdc,const GrClip * clip,const SkMatrix & viewMatrix,const SkIRect & maskBounds,GrPaint && paint,GrSurfaceProxyView mask)43 static bool draw_mask(skgpu::v1::SurfaceDrawContext* sdc,
44                       const GrClip* clip,
45                       const SkMatrix& viewMatrix,
46                       const SkIRect& maskBounds,
47                       GrPaint&& paint,
48                       GrSurfaceProxyView mask) {
49     SkMatrix inverse;
50     if (!viewMatrix.invert(&inverse)) {
51         return false;
52     }
53 
54     mask.concatSwizzle(skgpu::Swizzle("aaaa"));
55 
56     SkMatrix matrix = SkMatrix::Translate(-SkIntToScalar(maskBounds.fLeft),
57                                           -SkIntToScalar(maskBounds.fTop));
58     matrix.preConcat(viewMatrix);
59     paint.setCoverageFragmentProcessor(
60             GrTextureEffect::Make(std::move(mask), kUnknown_SkAlphaType, matrix));
61 
62     sdc->fillPixelsWithLocalMatrix(clip, std::move(paint), maskBounds, inverse);
63     return true;
64 }
65 
mask_release_proc(void * addr,void *)66 static void mask_release_proc(void* addr, void* /*context*/) {
67     SkMask::FreeImage(addr);
68 }
69 
70 // This stores the mapping from an unclipped, integerized, device-space, shape bounds to
71 // the filtered mask's draw rect.
72 struct DrawRectData {
73     SkIVector fOffset;
74     SkISize   fSize;
75 };
76 
create_data(const SkIRect & drawRect,const SkIRect & origDevBounds)77 static sk_sp<SkData> create_data(const SkIRect& drawRect, const SkIRect& origDevBounds) {
78 
79     DrawRectData drawRectData { {drawRect.fLeft - origDevBounds.fLeft,
80                                  drawRect.fTop - origDevBounds.fTop},
81                                 drawRect.size() };
82 
83     return SkData::MakeWithCopy(&drawRectData, sizeof(drawRectData));
84 }
85 
extract_draw_rect_from_data(SkData * data,const SkIRect & origDevBounds)86 static SkIRect extract_draw_rect_from_data(SkData* data, const SkIRect& origDevBounds) {
87     auto drawRectData = static_cast<const DrawRectData*>(data->data());
88 
89     return SkIRect::MakeXYWH(origDevBounds.fLeft + drawRectData->fOffset.fX,
90                              origDevBounds.fTop + drawRectData->fOffset.fY,
91                              drawRectData->fSize.fWidth,
92                              drawRectData->fSize.fHeight);
93 }
94 
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)95 static GrSurfaceProxyView sw_create_filtered_mask(GrRecordingContext* rContext,
96                                                   const SkMatrix& viewMatrix,
97                                                   const GrStyledShape& shape,
98                                                   const SkMaskFilter* filter,
99                                                   const SkIRect& unclippedDevShapeBounds,
100                                                   const SkIRect& clipBounds,
101                                                   SkIRect* drawRect,
102                                                   skgpu::UniqueKey* key) {
103     SkASSERT(filter);
104     SkASSERT(!shape.style().applies());
105 
106     auto threadSafeCache = rContext->priv().threadSafeCache();
107 
108     GrSurfaceProxyView filteredMaskView;
109     sk_sp<SkData> data;
110 
111     if (key->isValid()) {
112         std::tie(filteredMaskView, data) = threadSafeCache->findWithData(*key);
113     }
114 
115     if (filteredMaskView) {
116         SkASSERT(data);
117         SkASSERT(kMaskOrigin == filteredMaskView.origin());
118 
119         *drawRect = extract_draw_rect_from_data(data.get(), unclippedDevShapeBounds);
120     } else {
121         SkStrokeRec::InitStyle fillOrHairline = shape.style().isSimpleHairline()
122                                                         ? SkStrokeRec::kHairline_InitStyle
123                                                         : SkStrokeRec::kFill_InitStyle;
124 
125         // TODO: it seems like we could create an SkDraw here and set its fMatrix field rather
126         // than explicitly transforming the path to device space.
127         SkPath devPath;
128 
129         shape.asPath(&devPath);
130 
131         devPath.transform(viewMatrix);
132 
133         SkMask srcM, dstM;
134         if (!SkDraw::DrawToMask(devPath, clipBounds, filter, &viewMatrix, &srcM,
135                                 SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) {
136             return {};
137         }
138         SkAutoMaskFreeImage autoSrc(srcM.fImage);
139 
140         SkASSERT(SkMask::kA8_Format == srcM.fFormat);
141 
142         if (!as_MFB(filter)->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
143             return {};
144         }
145         // this will free-up dstM when we're done (allocated in filterMask())
146         SkAutoMaskFreeImage autoDst(dstM.fImage);
147 
148         if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) {
149             return {};
150         }
151 
152         // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
153         // the current clip (and identity matrix) and GrPaint settings
154         SkBitmap bm;
155         if (!bm.installPixels(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()),
156                               autoDst.release(), dstM.fRowBytes, mask_release_proc, nullptr)) {
157             return {};
158         }
159         bm.setImmutable();
160 
161         std::tie(filteredMaskView, std::ignore) = GrMakeUncachedBitmapProxyView(
162                 rContext,
163                 bm,
164                 GrMipmapped::kNo,
165                 SkBackingFit::kApprox);
166         if (!filteredMaskView) {
167             return {};
168         }
169 
170         SkASSERT(kMaskOrigin == filteredMaskView.origin());
171 
172         *drawRect = dstM.fBounds;
173 
174         if (key->isValid()) {
175             key->setCustomData(create_data(*drawRect, unclippedDevShapeBounds));
176             std::tie(filteredMaskView, data) = threadSafeCache->addWithData(*key, filteredMaskView);
177             // If we got a different view back from 'addWithData' it could have a different drawRect
178             *drawRect = extract_draw_rect_from_data(data.get(), unclippedDevShapeBounds);
179         }
180     }
181 
182     return filteredMaskView;
183 }
184 
185 // 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)186 static std::unique_ptr<skgpu::v1::SurfaceDrawContext> create_mask_GPU(
187         GrRecordingContext* rContext,
188         const SkIRect& maskRect,
189         const SkMatrix& origViewMatrix,
190         const GrStyledShape& shape,
191         int sampleCnt) {
192     // We cache blur masks. Use default surface props here so we can use the same cached mask
193     // regardless of the final dst surface.
194     SkSurfaceProps defaultSurfaceProps;
195 
196     // Use GrResourceProvider::MakeApprox to implement our own approximate size matching, but demand
197     // a "SkBackingFit::kExact" size match on the actual render target. We do this because the
198     // filter will reach outside the src bounds, so we need to pre-clear these values to ensure a
199     // "decal" sampling effect (i.e., ensure reads outside the src bounds return alpha=0).
200     //
201     // FIXME: Reads outside the left and top edges will actually clamp to the edge pixel. And in the
202     // event that MakeApprox does not change the size, reads outside the right and/or bottom will do
203     // the same. We should offset our filter within the render target and expand the size as needed
204     // to guarantee at least 1px of padding on all sides.
205     auto approxSize = GrResourceProvider::MakeApprox(maskRect.size());
206     auto sdc = skgpu::v1::SurfaceDrawContext::MakeWithFallback(rContext,
207                                                                GrColorType::kAlpha_8,
208                                                                nullptr,
209                                                                SkBackingFit::kExact,
210                                                                approxSize,
211                                                                defaultSurfaceProps,
212                                                                sampleCnt,
213                                                                GrMipmapped::kNo,
214                                                                GrProtected::kNo,
215                                                                kMaskOrigin);
216     if (!sdc) {
217         return nullptr;
218     }
219 
220     sdc->clear(SK_PMColor4fTRANSPARENT);
221 
222     GrPaint maskPaint;
223     maskPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
224 
225     // setup new clip
226     GrFixedClip clip(sdc->dimensions(), SkIRect::MakeWH(maskRect.width(), maskRect.height()));
227 
228     // Draw the mask into maskTexture with the path's integerized top-left at the origin using
229     // maskPaint.
230     SkMatrix viewMatrix = origViewMatrix;
231     viewMatrix.postTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
232     sdc->drawShape(&clip, std::move(maskPaint), GrAA::kYes, viewMatrix, GrStyledShape(shape));
233     return sdc;
234 }
235 
get_unclipped_shape_dev_bounds(const GrStyledShape & shape,const SkMatrix & matrix,SkIRect * devBounds)236 static bool get_unclipped_shape_dev_bounds(const GrStyledShape& shape, const SkMatrix& matrix,
237                                            SkIRect* devBounds) {
238     SkRect shapeDevBounds;
239     if (shape.inverseFilled()) {
240         shapeDevBounds = {SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity,
241                           SK_ScalarInfinity, SK_ScalarInfinity};
242     } else {
243         SkRect shapeBounds = shape.styledBounds();
244         if (shapeBounds.isEmpty()) {
245             return false;
246         }
247         matrix.mapRect(&shapeDevBounds, shapeBounds);
248     }
249     // Even though these are "unclipped" bounds we still clip to the int32_t range.
250     // This is the largest int32_t that is representable exactly as a float. The next 63 larger ints
251     // would round down to this value when cast to a float, but who really cares.
252     // INT32_MIN is exactly representable.
253     static constexpr int32_t kMaxInt = 2147483520;
254     if (!shapeDevBounds.intersect(SkRect::MakeLTRB(INT32_MIN, INT32_MIN, kMaxInt, kMaxInt))) {
255         return false;
256     }
257     // Make sure that the resulting SkIRect can have representable width and height
258     if (SkScalarRoundToInt(shapeDevBounds.width()) > kMaxInt ||
259         SkScalarRoundToInt(shapeDevBounds.height()) > kMaxInt) {
260         return false;
261     }
262     shapeDevBounds.roundOut(devBounds);
263     return true;
264 }
265 
266 // Gets the shape bounds, the clip bounds, and the intersection (if any). Returns false if there
267 // is no intersection.
get_shape_and_clip_bounds(skgpu::v1::SurfaceDrawContext * sdc,const GrClip * clip,const GrStyledShape & shape,const SkMatrix & matrix,SkIRect * unclippedDevShapeBounds,SkIRect * devClipBounds)268 static bool get_shape_and_clip_bounds(skgpu::v1::SurfaceDrawContext* sdc,
269                                       const GrClip* clip,
270                                       const GrStyledShape& shape,
271                                       const SkMatrix& matrix,
272                                       SkIRect* unclippedDevShapeBounds,
273                                       SkIRect* devClipBounds) {
274     // compute bounds as intersection of rt size, clip, and path
275     *devClipBounds = clip ? clip->getConservativeBounds()
276                           : SkIRect::MakeWH(sdc->width(), sdc->height());
277 
278     if (!get_unclipped_shape_dev_bounds(shape, matrix, unclippedDevShapeBounds)) {
279         *unclippedDevShapeBounds = SkIRect::MakeEmpty();
280         return false;
281     }
282 
283     return true;
284 }
285 
286 // The key and clip-bounds are computed together because the caching decision can impact the
287 // clip-bound - since we only cache un-clipped masks the clip can be removed entirely.
288 // 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)289 static bool compute_key_and_clip_bounds(skgpu::UniqueKey* maskKey,
290                                         SkIRect* boundsForClip,
291                                         const GrCaps* caps,
292                                         const SkMatrix& viewMatrix,
293                                         bool inverseFilled,
294                                         const SkMaskFilterBase* maskFilter,
295                                         const GrStyledShape& shape,
296                                         const SkIRect& unclippedDevShapeBounds,
297                                         const SkIRect& devClipBounds) {
298     *boundsForClip = devClipBounds;
299 
300 #ifndef SK_DISABLE_MASKFILTERED_MASK_CACHING
301     // To prevent overloading the cache with entries during animations we limit the cache of masks
302     // to cases where the matrix preserves axis alignment.
303     bool useCache = !inverseFilled && viewMatrix.preservesAxisAlignment() &&
304                     shape.hasUnstyledKey() && as_MFB(maskFilter)->asABlur(nullptr);
305 
306     if (useCache) {
307         SkIRect clippedMaskRect, unClippedMaskRect;
308         maskFilter->canFilterMaskGPU(shape, unclippedDevShapeBounds, devClipBounds,
309                                      viewMatrix, &clippedMaskRect);
310         maskFilter->canFilterMaskGPU(shape, unclippedDevShapeBounds, unclippedDevShapeBounds,
311                                      viewMatrix, &unClippedMaskRect);
312         if (clippedMaskRect.isEmpty()) {
313             return false;
314         }
315 
316         // Use the cache only if >50% of the filtered mask is visible.
317         int unclippedWidth = unClippedMaskRect.width();
318         int unclippedHeight = unClippedMaskRect.height();
319         int64_t unclippedArea = sk_64_mul(unclippedWidth, unclippedHeight);
320         int64_t clippedArea = sk_64_mul(clippedMaskRect.width(), clippedMaskRect.height());
321         int maxTextureSize = caps->maxTextureSize();
322         if (unclippedArea > 2 * clippedArea || unclippedWidth > maxTextureSize ||
323             unclippedHeight > maxTextureSize) {
324             useCache = false;
325         } else {
326             // Make the clip not affect the mask
327             *boundsForClip = unclippedDevShapeBounds;
328         }
329     }
330 
331     if (useCache) {
332         static const skgpu::UniqueKey::Domain kDomain = skgpu::UniqueKey::GenerateDomain();
333         skgpu::UniqueKey::Builder builder(maskKey, kDomain, 5 + 2 + shape.unstyledKeySize(),
334                                      "Mask Filtered Masks");
335 
336         // We require the upper left 2x2 of the matrix to match exactly for a cache hit.
337         SkScalar sx = viewMatrix.get(SkMatrix::kMScaleX);
338         SkScalar sy = viewMatrix.get(SkMatrix::kMScaleY);
339         SkScalar kx = viewMatrix.get(SkMatrix::kMSkewX);
340         SkScalar ky = viewMatrix.get(SkMatrix::kMSkewY);
341         SkScalar tx = viewMatrix.get(SkMatrix::kMTransX);
342         SkScalar ty = viewMatrix.get(SkMatrix::kMTransY);
343         // Allow 8 bits each in x and y of subpixel positioning. But, note that we're allowing
344         // reuse for integer translations.
345         SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00;
346         SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00;
347 
348         builder[0] = SkFloat2Bits(sx);
349         builder[1] = SkFloat2Bits(sy);
350         builder[2] = SkFloat2Bits(kx);
351         builder[3] = SkFloat2Bits(ky);
352         // Distinguish between hairline and filled paths. For hairlines, we also need to include
353         // the cap. (SW grows hairlines by 0.5 pixel with round and square caps). Note that
354         // stroke-and-fill of hairlines is turned into pure fill by SkStrokeRec, so this covers
355         // all cases we might see.
356         uint32_t styleBits = shape.style().isSimpleHairline()
357                                     ? ((shape.style().strokeRec().getCap() << 1) | 1)
358                                     : 0;
359         builder[4] = fracX | (fracY >> 8) | (styleBits << 16);
360 
361         SkMaskFilterBase::BlurRec rec;
362         SkAssertResult(as_MFB(maskFilter)->asABlur(&rec));
363 
364         builder[5] = rec.fStyle;  // TODO: we could put this with the other style bits
365         builder[6] = SkFloat2Bits(rec.fSigma);
366         shape.writeUnstyledKey(&builder[7]);
367     }
368 #endif
369 
370     return true;
371 }
372 
hw_create_filtered_mask(GrDirectContext * dContext,skgpu::v1::SurfaceDrawContext * sdc,const SkMatrix & viewMatrix,const GrStyledShape & shape,const SkMaskFilterBase * filter,const SkIRect & unclippedDevShapeBounds,const SkIRect & clipBounds,SkIRect * maskRect,skgpu::UniqueKey * key)373 static GrSurfaceProxyView hw_create_filtered_mask(GrDirectContext* dContext,
374                                                   skgpu::v1::SurfaceDrawContext* sdc,
375                                                   const SkMatrix& viewMatrix,
376                                                   const GrStyledShape& shape,
377                                                   const SkMaskFilterBase* filter,
378                                                   const SkIRect& unclippedDevShapeBounds,
379                                                   const SkIRect& clipBounds,
380                                                   SkIRect* maskRect,
381                                                   skgpu::UniqueKey* key) {
382     if (!filter->canFilterMaskGPU(shape,
383                                   unclippedDevShapeBounds,
384                                   clipBounds,
385                                   viewMatrix,
386                                   maskRect)) {
387         return {};
388     }
389 
390     if (clip_bounds_quick_reject(clipBounds, *maskRect)) {
391         // clipped out
392         return {};
393     }
394 
395     auto threadSafeCache = dContext->priv().threadSafeCache();
396 
397     GrSurfaceProxyView lazyView;
398     sk_sp<GrThreadSafeCache::Trampoline> trampoline;
399 
400     if (key->isValid()) {
401         // In this case, we want GPU-filtered masks to have priority over SW-generated ones so
402         // we pre-emptively add a lazy-view to the cache and fill it in later.
403         std::tie(lazyView, trampoline) = GrThreadSafeCache::CreateLazyView(
404                 dContext, GrColorType::kAlpha_8, maskRect->size(),
405                 kMaskOrigin, SkBackingFit::kApprox);
406         if (!lazyView) {
407             return {}; // fall back to a SW-created mask - 'create_mask_GPU' probably won't succeed
408         }
409 
410         key->setCustomData(create_data(*maskRect, unclippedDevShapeBounds));
411         auto [cachedView, data] = threadSafeCache->findOrAddWithData(*key, lazyView);
412         if (cachedView != lazyView) {
413             // In this case, the gpu-thread lost out to a recording thread - use its result.
414             SkASSERT(data);
415             SkASSERT(cachedView.asTextureProxy());
416             SkASSERT(cachedView.origin() == kMaskOrigin);
417 
418             *maskRect = extract_draw_rect_from_data(data.get(), unclippedDevShapeBounds);
419             return cachedView;
420         }
421     }
422 
423     std::unique_ptr<skgpu::v1::SurfaceDrawContext> maskSDC(create_mask_GPU(dContext,
424                                                                            *maskRect,
425                                                                            viewMatrix,
426                                                                            shape,
427                                                                            sdc->numSamples()));
428     if (!maskSDC) {
429         if (key->isValid()) {
430             // It is very unlikely that 'create_mask_GPU' will fail after 'CreateLazyView'
431             // succeeded but, if it does, remove the lazy-view from the cache and fallback to
432             // a SW-created mask. Note that any recording threads that glommed onto the
433             // lazy-view will have to, later, drop those draws.
434             threadSafeCache->remove(*key);
435         }
436         return {};
437     }
438 
439     auto filteredMaskView = filter->filterMaskGPU(dContext,
440                                                   maskSDC->readSurfaceView(),
441                                                   maskSDC->colorInfo().colorType(),
442                                                   maskSDC->colorInfo().alphaType(),
443                                                   viewMatrix,
444                                                   *maskRect);
445     if (!filteredMaskView) {
446         if (key->isValid()) {
447             // Remove the lazy-view from the cache and fallback to a SW-created mask. Note that
448             // any recording threads that glommed onto the lazy-view will have to, later, drop
449             // those draws.
450             threadSafeCache->remove(*key);
451         }
452         return {};
453     }
454 
455     if (key->isValid()) {
456         SkASSERT(filteredMaskView.dimensions() == lazyView.dimensions());
457         SkASSERT(filteredMaskView.swizzle() == lazyView.swizzle());
458         SkASSERT(filteredMaskView.origin() == lazyView.origin());
459 
460         trampoline->fProxy = filteredMaskView.asTextureProxyRef();
461         return lazyView;
462     }
463 
464     return filteredMaskView;
465 }
466 
draw_shape_with_mask_filter(GrRecordingContext * rContext,skgpu::v1::SurfaceDrawContext * sdc,const GrClip * clip,GrPaint && paint,const SkMatrix & viewMatrix,const SkMaskFilterBase * maskFilter,const GrStyledShape & origShape)467 static void draw_shape_with_mask_filter(GrRecordingContext* rContext,
468                                         skgpu::v1::SurfaceDrawContext* sdc,
469                                         const GrClip* clip,
470                                         GrPaint&& paint,
471                                         const SkMatrix& viewMatrix,
472                                         const SkMaskFilterBase* maskFilter,
473                                         const GrStyledShape& origShape) {
474     SkASSERT(maskFilter);
475 
476     const GrStyledShape* shape = &origShape;
477     SkTLazy<GrStyledShape> tmpShape;
478 
479     if (origShape.style().applies()) {
480         SkScalar styleScale =  GrStyle::MatrixToScaleFactor(viewMatrix);
481         if (styleScale == 0) {
482             return;
483         }
484 
485         tmpShape.init(origShape.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, styleScale));
486         if (tmpShape->isEmpty()) {
487             return;
488         }
489 
490         shape = tmpShape.get();
491     }
492 
493     if (maskFilter->directFilterMaskGPU(rContext, sdc, std::move(paint), clip,
494                                         viewMatrix, *shape)) {
495         // the mask filter was able to draw itself directly, so there's nothing
496         // left to do.
497         return;
498     }
499     assert_alive(paint);
500 
501     // If the path is hairline, ignore inverse fill.
502     bool inverseFilled = shape->inverseFilled() &&
503                          !GrIsStrokeHairlineOrEquivalent(shape->style(), viewMatrix, nullptr);
504 
505     SkIRect unclippedDevShapeBounds, devClipBounds;
506     if (!get_shape_and_clip_bounds(sdc, clip, *shape, viewMatrix,
507                                    &unclippedDevShapeBounds, &devClipBounds)) {
508         // TODO: just cons up an opaque mask here
509         if (!inverseFilled) {
510             return;
511         }
512     }
513 
514     skgpu::UniqueKey maskKey;
515     SkIRect boundsForClip;
516     if (!compute_key_and_clip_bounds(&maskKey, &boundsForClip,
517                                      sdc->caps(),
518                                      viewMatrix, inverseFilled,
519                                      maskFilter, *shape,
520                                      unclippedDevShapeBounds,
521                                      devClipBounds)) {
522         return; // 'shape' was entirely clipped out
523     }
524 
525     GrSurfaceProxyView filteredMaskView;
526     SkIRect maskRect;
527 
528     if (auto dContext = rContext->asDirectContext()) {
529         filteredMaskView = hw_create_filtered_mask(dContext, sdc,
530                                                    viewMatrix, *shape, maskFilter,
531                                                    unclippedDevShapeBounds, boundsForClip,
532                                                    &maskRect, &maskKey);
533         if (filteredMaskView) {
534             if (draw_mask(sdc, clip, viewMatrix, maskRect, std::move(paint),
535                           std::move(filteredMaskView))) {
536                 // This path is completely drawn
537                 return;
538             }
539             assert_alive(paint);
540         }
541     }
542 
543     // Either HW mask rendering failed or we're in a DDL recording thread
544     filteredMaskView = sw_create_filtered_mask(rContext,
545                                                viewMatrix, *shape, maskFilter,
546                                                unclippedDevShapeBounds, boundsForClip,
547                                                &maskRect, &maskKey);
548     if (filteredMaskView) {
549         if (draw_mask(sdc, clip, viewMatrix, maskRect, std::move(paint),
550                       std::move(filteredMaskView))) {
551             return;
552         }
553         assert_alive(paint);
554     }
555 }
556 
drawShapeWithMaskFilter(GrRecordingContext * rContext,skgpu::v1::SurfaceDrawContext * sdc,const GrClip * clip,const GrStyledShape & shape,GrPaint && paint,const SkMatrix & viewMatrix,const SkMaskFilter * mf)557 void GrBlurUtils::drawShapeWithMaskFilter(GrRecordingContext* rContext,
558                                           skgpu::v1::SurfaceDrawContext* sdc,
559                                           const GrClip* clip,
560                                           const GrStyledShape& shape,
561                                           GrPaint&& paint,
562                                           const SkMatrix& viewMatrix,
563                                           const SkMaskFilter* mf) {
564     draw_shape_with_mask_filter(rContext, sdc, clip, std::move(paint),
565                                 viewMatrix, as_MFB(mf), shape);
566 }
567 
drawShapeWithMaskFilter(GrRecordingContext * rContext,skgpu::v1::SurfaceDrawContext * sdc,const GrClip * clip,const SkPaint & paint,const SkMatrixProvider & matrixProvider,const GrStyledShape & shape)568 void GrBlurUtils::drawShapeWithMaskFilter(GrRecordingContext* rContext,
569                                           skgpu::v1::SurfaceDrawContext* sdc,
570                                           const GrClip* clip,
571                                           const SkPaint& paint,
572                                           const SkMatrixProvider& matrixProvider,
573                                           const GrStyledShape& shape) {
574     if (rContext->abandoned()) {
575         return;
576     }
577 
578     GrPaint grPaint;
579     if (!SkPaintToGrPaint(rContext,
580                           sdc->colorInfo(),
581                           paint,
582                           matrixProvider.localToDevice(),
583                           sdc->surfaceProps(),
584                           &grPaint)) {
585         return;
586     }
587 
588     const SkMatrix& viewMatrix(matrixProvider.localToDevice());
589     SkMaskFilterBase* mf = as_MFB(paint.getMaskFilter());
590     if (mf && !mf->hasFragmentProcessor()) {
591         // The MaskFilter wasn't already handled in SkPaintToGrPaint
592         draw_shape_with_mask_filter(rContext, sdc, clip, std::move(grPaint), viewMatrix, mf, shape);
593     } else {
594         sdc->drawShape(clip, std::move(grPaint), sdc->chooseAA(paint), viewMatrix,
595                        GrStyledShape(shape));
596     }
597 }
598