• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/GrBlurUtils.h"
9 
10 #include "include/private/GrRecordingContext.h"
11 #include "src/gpu/GrCaps.h"
12 #include "src/gpu/GrFixedClip.h"
13 #include "src/gpu/GrProxyProvider.h"
14 #include "src/gpu/GrRecordingContextPriv.h"
15 #include "src/gpu/GrRenderTargetContext.h"
16 #include "src/gpu/GrRenderTargetContextPriv.h"
17 #include "src/gpu/GrSoftwarePathRenderer.h"
18 #include "src/gpu/GrStyle.h"
19 #include "src/gpu/GrTextureProxy.h"
20 #include "src/gpu/effects/generated/GrSimpleTextureEffect.h"
21 #include "src/gpu/geometry/GrShape.h"
22 
23 #include "include/core/SkPaint.h"
24 #include "src/core/SkDraw.h"
25 #include "src/core/SkMaskFilterBase.h"
26 #include "src/core/SkTLazy.h"
27 #include "src/gpu/SkGr.h"
28 
clip_bounds_quick_reject(const SkIRect & clipBounds,const SkIRect & rect)29 static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& rect) {
30     return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBounds, rect);
31 }
32 
33 // Draw a mask using the supplied paint. Since the coverage/geometry
34 // is already burnt into the mask this boils down to a rect draw.
35 // Return true if the mask was successfully drawn.
draw_mask(GrRenderTargetContext * renderTargetContext,const GrClip & clip,const SkMatrix & viewMatrix,const SkIRect & maskRect,GrPaint && paint,sk_sp<GrTextureProxy> mask)36 static bool draw_mask(GrRenderTargetContext* renderTargetContext,
37                       const GrClip& clip,
38                       const SkMatrix& viewMatrix,
39                       const SkIRect& maskRect,
40                       GrPaint&& paint,
41                       sk_sp<GrTextureProxy> mask) {
42     SkMatrix inverse;
43     if (!viewMatrix.invert(&inverse)) {
44         return false;
45     }
46 
47     SkMatrix matrix = SkMatrix::MakeTrans(-SkIntToScalar(maskRect.fLeft),
48                                           -SkIntToScalar(maskRect.fTop));
49     matrix.preConcat(viewMatrix);
50     paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(std::move(mask), matrix));
51 
52     renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
53                                                  SkRect::Make(maskRect), inverse);
54     return true;
55 }
56 
mask_release_proc(void * addr,void *)57 static void mask_release_proc(void* addr, void* /*context*/) {
58     SkMask::FreeImage(addr);
59 }
60 
sw_draw_with_mask_filter(GrRecordingContext * context,GrRenderTargetContext * renderTargetContext,const GrClip & clipData,const SkMatrix & viewMatrix,const GrShape & shape,const SkMaskFilter * filter,const SkIRect & clipBounds,GrPaint && paint,const GrUniqueKey & key)61 static bool sw_draw_with_mask_filter(GrRecordingContext* context,
62                                      GrRenderTargetContext* renderTargetContext,
63                                      const GrClip& clipData,
64                                      const SkMatrix& viewMatrix,
65                                      const GrShape& shape,
66                                      const SkMaskFilter* filter,
67                                      const SkIRect& clipBounds,
68                                      GrPaint&& paint,
69                                      const GrUniqueKey& key) {
70     SkASSERT(filter);
71     SkASSERT(!shape.style().applies());
72 
73     auto proxyProvider = context->priv().proxyProvider();
74 
75     sk_sp<GrTextureProxy> filteredMask;
76 
77     SkStrokeRec::InitStyle fillOrHairline = shape.style().isSimpleHairline()
78                                                     ? SkStrokeRec::kHairline_InitStyle
79                                                     : SkStrokeRec::kFill_InitStyle;
80 
81     if (key.isValid()) {
82         // TODO: this cache look up is duplicated in draw_shape_with_mask_filter for gpu
83         filteredMask = proxyProvider->findOrCreateProxyByUniqueKey(key, GrColorType::kAlpha_8,
84                                                                    kTopLeft_GrSurfaceOrigin);
85     }
86 
87     SkIRect drawRect;
88     if (filteredMask) {
89         SkRect devBounds = shape.bounds();
90         viewMatrix.mapRect(&devBounds);
91 
92         // Here we need to recompute the destination bounds in order to draw the mask correctly
93         SkMask srcM, dstM;
94         if (!SkDraw::ComputeMaskBounds(devBounds, &clipBounds, filter, &viewMatrix,
95                                        &srcM.fBounds)) {
96             return false;
97         }
98 
99         srcM.fFormat = SkMask::kA8_Format;
100 
101         if (!as_MFB(filter)->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
102             return false;
103         }
104 
105         // Unfortunately, we cannot double check that the computed bounds (i.e., dstM.fBounds)
106         // match the stored bounds of the mask bc the proxy may have been recreated and,
107         // when it is recreated, it just gets the bounds of the underlying GrTexture (which
108         // might be a loose fit).
109         drawRect = dstM.fBounds;
110     } else {
111         // TODO: it seems like we could create an SkDraw here and set its fMatrix field rather
112         // than explicitly transforming the path to device space.
113         SkPath devPath;
114 
115         shape.asPath(&devPath);
116 
117         devPath.transform(viewMatrix);
118 
119         SkMask srcM, dstM;
120         if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
121                                 SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) {
122             return false;
123         }
124         SkAutoMaskFreeImage autoSrc(srcM.fImage);
125 
126         SkASSERT(SkMask::kA8_Format == srcM.fFormat);
127 
128         if (!as_MFB(filter)->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
129             return false;
130         }
131         // this will free-up dstM when we're done (allocated in filterMask())
132         SkAutoMaskFreeImage autoDst(dstM.fImage);
133 
134         if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) {
135             return false;
136         }
137 
138         // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
139         // the current clip (and identity matrix) and GrPaint settings
140         SkBitmap bm;
141         if (!bm.installPixels(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()),
142                               autoDst.release(), dstM.fRowBytes, mask_release_proc, nullptr)) {
143             return false;
144         }
145         bm.setImmutable();
146 
147         sk_sp<SkImage> image = SkImage::MakeFromBitmap(bm);
148         if (!image) {
149             return false;
150         }
151 
152         filteredMask = proxyProvider->createTextureProxy(std::move(image), 1, SkBudgeted::kYes,
153                                                          SkBackingFit::kApprox);
154         if (!filteredMask) {
155             return false;
156         }
157 
158         SkASSERT(kTopLeft_GrSurfaceOrigin == filteredMask->origin());
159 
160         drawRect = dstM.fBounds;
161 
162         if (key.isValid()) {
163             proxyProvider->assignUniqueKeyToProxy(key, filteredMask.get());
164         }
165     }
166 
167     return draw_mask(renderTargetContext, clipData, viewMatrix, drawRect,
168                      std::move(paint), std::move(filteredMask));
169 }
170 
171 // Create a mask of 'shape' and place the result in 'mask'.
create_mask_GPU(GrRecordingContext * context,const SkIRect & maskRect,const SkMatrix & origViewMatrix,const GrShape & shape,int sampleCnt)172 static sk_sp<GrTextureProxy> create_mask_GPU(GrRecordingContext* context,
173                                              const SkIRect& maskRect,
174                                              const SkMatrix& origViewMatrix,
175                                              const GrShape& shape,
176                                              int sampleCnt) {
177     sk_sp<GrRenderTargetContext> rtContext(
178             context->priv().makeDeferredRenderTargetContextWithFallback(
179                     SkBackingFit::kApprox, maskRect.width(), maskRect.height(),
180                     GrColorType::kAlpha_8, nullptr, sampleCnt, GrMipMapped::kNo,
181                     kTopLeft_GrSurfaceOrigin));
182     if (!rtContext) {
183         return nullptr;
184     }
185 
186     rtContext->priv().absClear(nullptr, SK_PMColor4fTRANSPARENT);
187 
188     GrPaint maskPaint;
189     maskPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
190 
191     // setup new clip
192     const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height());
193     GrFixedClip clip(clipRect);
194 
195     // Draw the mask into maskTexture with the path's integerized top-left at
196     // the origin using maskPaint.
197     SkMatrix viewMatrix = origViewMatrix;
198     viewMatrix.postTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
199     rtContext->drawShape(clip, std::move(maskPaint), GrAA::kYes, viewMatrix, shape);
200     return rtContext->asTextureProxyRef();
201 }
202 
get_unclipped_shape_dev_bounds(const GrShape & shape,const SkMatrix & matrix,SkIRect * devBounds)203 static bool get_unclipped_shape_dev_bounds(const GrShape& shape, const SkMatrix& matrix,
204                                            SkIRect* devBounds) {
205     SkRect shapeBounds = shape.styledBounds();
206     if (shapeBounds.isEmpty()) {
207         return false;
208     }
209     SkRect shapeDevBounds;
210     matrix.mapRect(&shapeDevBounds, shapeBounds);
211     // Even though these are "unclipped" bounds we still clip to the int32_t range.
212     // This is the largest int32_t that is representable exactly as a float. The next 63 larger ints
213     // would round down to this value when cast to a float, but who really cares.
214     // INT32_MIN is exactly representable.
215     static constexpr int32_t kMaxInt = 2147483520;
216     if (!shapeDevBounds.intersect(SkRect::MakeLTRB(INT32_MIN, INT32_MIN, kMaxInt, kMaxInt))) {
217         return false;
218     }
219     // Make sure that the resulting SkIRect can have representable width and height
220     if (SkScalarRoundToInt(shapeDevBounds.width()) > kMaxInt ||
221         SkScalarRoundToInt(shapeDevBounds.height()) > kMaxInt) {
222         return false;
223     }
224     shapeDevBounds.roundOut(devBounds);
225     return true;
226 }
227 
228 // Gets the shape bounds, the clip bounds, and the intersection (if any). Returns false if there
229 // is no intersection.
get_shape_and_clip_bounds(GrRenderTargetContext * renderTargetContext,const GrClip & clip,const GrShape & shape,const SkMatrix & matrix,SkIRect * unclippedDevShapeBounds,SkIRect * devClipBounds)230 static bool get_shape_and_clip_bounds(GrRenderTargetContext* renderTargetContext,
231                                       const GrClip& clip,
232                                       const GrShape& shape,
233                                       const SkMatrix& matrix,
234                                       SkIRect* unclippedDevShapeBounds,
235                                       SkIRect* devClipBounds) {
236     // compute bounds as intersection of rt size, clip, and path
237     clip.getConservativeBounds(renderTargetContext->width(),
238                                renderTargetContext->height(),
239                                devClipBounds);
240 
241     if (!get_unclipped_shape_dev_bounds(shape, matrix, unclippedDevShapeBounds)) {
242         *unclippedDevShapeBounds = SkIRect::EmptyIRect();
243         return false;
244     }
245 
246     return true;
247 }
248 
draw_shape_with_mask_filter(GrRecordingContext * context,GrRenderTargetContext * renderTargetContext,const GrClip & clip,GrPaint && paint,const SkMatrix & viewMatrix,const SkMaskFilterBase * maskFilter,const GrShape & origShape)249 static void draw_shape_with_mask_filter(GrRecordingContext* context,
250                                         GrRenderTargetContext* renderTargetContext,
251                                         const GrClip& clip,
252                                         GrPaint&& paint,
253                                         const SkMatrix& viewMatrix,
254                                         const SkMaskFilterBase* maskFilter,
255                                         const GrShape& origShape) {
256     SkASSERT(maskFilter);
257 
258     const GrShape* shape = &origShape;
259     SkTLazy<GrShape> tmpShape;
260 
261     if (origShape.style().applies()) {
262         SkScalar styleScale =  GrStyle::MatrixToScaleFactor(viewMatrix);
263         if (0 == styleScale) {
264             return;
265         }
266 
267         tmpShape.init(origShape.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, styleScale));
268         if (tmpShape.get()->isEmpty()) {
269             return;
270         }
271 
272         shape = tmpShape.get();
273     }
274 
275     if (maskFilter->directFilterMaskGPU(context,
276                                         renderTargetContext,
277                                         std::move(paint),
278                                         clip,
279                                         viewMatrix,
280                                         *shape)) {
281         // the mask filter was able to draw itself directly, so there's nothing
282         // left to do.
283         return;
284     }
285     assert_alive(paint);
286 
287     // If the path is hairline, ignore inverse fill.
288     bool inverseFilled = shape->inverseFilled() &&
289                          !GrPathRenderer::IsStrokeHairlineOrEquivalent(shape->style(),
290                                                                        viewMatrix, nullptr);
291 
292     SkIRect unclippedDevShapeBounds, devClipBounds;
293     if (!get_shape_and_clip_bounds(renderTargetContext, clip, *shape, viewMatrix,
294                                    &unclippedDevShapeBounds,
295                                    &devClipBounds)) {
296         // TODO: just cons up an opaque mask here
297         if (!inverseFilled) {
298             return;
299         }
300     }
301 
302     // To prevent overloading the cache with entries during animations we limit the cache of masks
303     // to cases where the matrix preserves axis alignment.
304 #ifdef SK_DISABLE_MASKFILTERED_MASK_CACHING
305     bool useCache = false;
306 #else
307     bool useCache = !inverseFilled && viewMatrix.preservesAxisAlignment() &&
308                     shape->hasUnstyledKey() && as_MFB(maskFilter)->asABlur(nullptr);
309 #endif
310 
311     const SkIRect* boundsForClip = &devClipBounds;
312     if (useCache) {
313         SkIRect clippedMaskRect, unClippedMaskRect;
314         maskFilter->canFilterMaskGPU(*shape, unclippedDevShapeBounds, devClipBounds,
315                                      viewMatrix, &clippedMaskRect);
316         maskFilter->canFilterMaskGPU(*shape, unclippedDevShapeBounds, unclippedDevShapeBounds,
317                                      viewMatrix, &unClippedMaskRect);
318         if (clippedMaskRect.isEmpty()) {
319             return;
320         }
321 
322         // Use the cache only if >50% of the filtered mask is visible.
323         int unclippedWidth = unClippedMaskRect.width();
324         int unclippedHeight = unClippedMaskRect.height();
325         int64_t unclippedArea = sk_64_mul(unclippedWidth, unclippedHeight);
326         int64_t clippedArea = sk_64_mul(clippedMaskRect.width(), clippedMaskRect.height());
327         int maxTextureSize = renderTargetContext->caps()->maxTextureSize();
328         if (unclippedArea > 2 * clippedArea || unclippedWidth > maxTextureSize ||
329             unclippedHeight > maxTextureSize) {
330             useCache = false;
331         } else {
332             // Make the clip not affect the mask
333             boundsForClip = &unclippedDevShapeBounds;
334         }
335     }
336 
337     GrUniqueKey maskKey;
338     if (useCache) {
339         static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
340         GrUniqueKey::Builder builder(&maskKey, kDomain, 5 + 2 + shape->unstyledKeySize(),
341                                      "Mask Filtered Masks");
342 
343         // We require the upper left 2x2 of the matrix to match exactly for a cache hit.
344         SkScalar sx = viewMatrix.get(SkMatrix::kMScaleX);
345         SkScalar sy = viewMatrix.get(SkMatrix::kMScaleY);
346         SkScalar kx = viewMatrix.get(SkMatrix::kMSkewX);
347         SkScalar ky = viewMatrix.get(SkMatrix::kMSkewY);
348         SkScalar tx = viewMatrix.get(SkMatrix::kMTransX);
349         SkScalar ty = viewMatrix.get(SkMatrix::kMTransY);
350         // Allow 8 bits each in x and y of subpixel positioning.
351         SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00;
352         SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00;
353 
354         builder[0] = SkFloat2Bits(sx);
355         builder[1] = SkFloat2Bits(sy);
356         builder[2] = SkFloat2Bits(kx);
357         builder[3] = SkFloat2Bits(ky);
358         // Distinguish between hairline and filled paths. For hairlines, we also need to include
359         // the cap. (SW grows hairlines by 0.5 pixel with round and square caps). Note that
360         // stroke-and-fill of hairlines is turned into pure fill by SkStrokeRec, so this covers
361         // all cases we might see.
362         uint32_t styleBits = shape->style().isSimpleHairline()
363                                     ? ((shape->style().strokeRec().getCap() << 1) | 1)
364                                     : 0;
365         builder[4] = fracX | (fracY >> 8) | (styleBits << 16);
366 
367         SkMaskFilterBase::BlurRec rec;
368         SkAssertResult(as_MFB(maskFilter)->asABlur(&rec));
369 
370         builder[5] = rec.fStyle;  // TODO: we could put this with the other style bits
371         builder[6] = SkFloat2Bits(rec.fSigma);
372         shape->writeUnstyledKey(&builder[7]);
373     }
374 
375     SkIRect maskRect;
376     if (maskFilter->canFilterMaskGPU(*shape,
377                                      unclippedDevShapeBounds,
378                                      *boundsForClip,
379                                      viewMatrix,
380                                      &maskRect)) {
381         if (clip_bounds_quick_reject(*boundsForClip, maskRect)) {
382             // clipped out
383             return;
384         }
385 
386         sk_sp<GrTextureProxy> filteredMask;
387 
388         GrProxyProvider* proxyProvider = context->priv().proxyProvider();
389 
390         if (maskKey.isValid()) {
391             // TODO: this cache look up is duplicated in sw_draw_with_mask_filter for raster
392             filteredMask = proxyProvider->findOrCreateProxyByUniqueKey(
393                     maskKey, GrColorType::kAlpha_8, kTopLeft_GrSurfaceOrigin);
394         }
395 
396         if (!filteredMask) {
397             sk_sp<GrTextureProxy> maskProxy(create_mask_GPU(
398                                                         context,
399                                                         maskRect,
400                                                         viewMatrix,
401                                                         *shape,
402                                                         renderTargetContext->numSamples()));
403             if (maskProxy) {
404                 filteredMask = maskFilter->filterMaskGPU(context,
405                                                          std::move(maskProxy),
406                                                          viewMatrix,
407                                                          maskRect);
408                 SkASSERT(kTopLeft_GrSurfaceOrigin == filteredMask->origin());
409 
410                 if (filteredMask && maskKey.isValid()) {
411                     proxyProvider->assignUniqueKeyToProxy(maskKey, filteredMask.get());
412                 }
413             }
414         }
415 
416         if (filteredMask) {
417             if (draw_mask(renderTargetContext, clip, viewMatrix,
418                           maskRect, std::move(paint), std::move(filteredMask))) {
419                 // This path is completely drawn
420                 return;
421             }
422             assert_alive(paint);
423         }
424     }
425 
426     sw_draw_with_mask_filter(context, renderTargetContext, clip, viewMatrix, *shape,
427                              maskFilter, *boundsForClip, std::move(paint), maskKey);
428 }
429 
drawShapeWithMaskFilter(GrRecordingContext * context,GrRenderTargetContext * renderTargetContext,const GrClip & clip,const GrShape & shape,GrPaint && paint,const SkMatrix & viewMatrix,const SkMaskFilter * mf)430 void GrBlurUtils::drawShapeWithMaskFilter(GrRecordingContext* context,
431                                           GrRenderTargetContext* renderTargetContext,
432                                           const GrClip& clip,
433                                           const GrShape& shape,
434                                           GrPaint&& paint,
435                                           const SkMatrix& viewMatrix,
436                                           const SkMaskFilter* mf) {
437     draw_shape_with_mask_filter(context, renderTargetContext, clip, std::move(paint),
438                                 viewMatrix, as_MFB(mf), shape);
439 }
440 
drawShapeWithMaskFilter(GrRecordingContext * context,GrRenderTargetContext * renderTargetContext,const GrClip & clip,const SkPaint & paint,const SkMatrix & viewMatrix,const GrShape & shape)441 void GrBlurUtils::drawShapeWithMaskFilter(GrRecordingContext* context,
442                                           GrRenderTargetContext* renderTargetContext,
443                                           const GrClip& clip,
444                                           const SkPaint& paint,
445                                           const SkMatrix& viewMatrix,
446                                           const GrShape& shape) {
447     if (context->priv().abandoned()) {
448         return;
449     }
450 
451     GrPaint grPaint;
452     if (!SkPaintToGrPaint(context, renderTargetContext->colorSpaceInfo(), paint, viewMatrix,
453                           &grPaint)) {
454         return;
455     }
456 
457     SkMaskFilterBase* mf = as_MFB(paint.getMaskFilter());
458     if (mf && !mf->hasFragmentProcessor()) {
459         // The MaskFilter wasn't already handled in SkPaintToGrPaint
460         draw_shape_with_mask_filter(context, renderTargetContext, clip, std::move(grPaint),
461                                     viewMatrix, mf, shape);
462     } else {
463         GrAA aa = GrAA(paint.isAntiAlias());
464         renderTargetContext->drawShape(clip, std::move(grPaint), aa, viewMatrix, shape);
465     }
466 }
467