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