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