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