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