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