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 "GrBlurUtils.h"
9 #include "GrDrawContext.h"
10 #include "GrCaps.h"
11 #include "GrContext.h"
12 #include "effects/GrSimpleTextureEffect.h"
13 #include "GrStrokeInfo.h"
14 #include "GrTexture.h"
15 #include "GrTextureProvider.h"
16 #include "SkDraw.h"
17 #include "SkGrPriv.h"
18 #include "SkMaskFilter.h"
19 #include "SkPaint.h"
20 
clip_bounds_quick_reject(const SkIRect & clipBounds,const SkIRect & rect)21 static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& rect) {
22     return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBounds, rect);
23 }
24 
25 // Draw a mask using the supplied paint. Since the coverage/geometry
26 // is already burnt into the mask this boils down to a rect draw.
27 // Return true if the mask was successfully drawn.
draw_mask(GrDrawContext * drawContext,const GrClip & clip,const SkMatrix & viewMatrix,const SkRect & maskRect,GrPaint * grp,GrTexture * mask)28 static bool draw_mask(GrDrawContext* drawContext,
29                       const GrClip& clip,
30                       const SkMatrix& viewMatrix,
31                       const SkRect& maskRect,
32                       GrPaint* grp,
33                       GrTexture* mask) {
34     SkMatrix matrix;
35     matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop);
36     matrix.postIDiv(mask->width(), mask->height());
37 
38     grp->addCoverageFragmentProcessor(GrSimpleTextureEffect::Create(mask, matrix,
39                                                                     kDevice_GrCoordSet))->unref();
40 
41     SkMatrix inverse;
42     if (!viewMatrix.invert(&inverse)) {
43         return false;
44     }
45     drawContext->fillRectWithLocalMatrix(clip, *grp, SkMatrix::I(), maskRect, inverse);
46     return true;
47 }
48 
sw_draw_with_mask_filter(GrDrawContext * drawContext,GrTextureProvider * textureProvider,const GrClip & clipData,const SkMatrix & viewMatrix,const SkPath & devPath,const SkMaskFilter * filter,const SkIRect & clipBounds,GrPaint * grp,SkPaint::Style style)49 static bool sw_draw_with_mask_filter(GrDrawContext* drawContext,
50                                      GrTextureProvider* textureProvider,
51                                      const GrClip& clipData,
52                                      const SkMatrix& viewMatrix,
53                                      const SkPath& devPath,
54                                      const SkMaskFilter* filter,
55                                      const SkIRect& clipBounds,
56                                      GrPaint* grp,
57                                      SkPaint::Style style) {
58     SkMask  srcM, dstM;
59 
60     if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
61                             SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {
62         return false;
63     }
64     SkAutoMaskFreeImage autoSrc(srcM.fImage);
65 
66     if (!filter->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
67         return false;
68     }
69     // this will free-up dstM when we're done (allocated in filterMask())
70     SkAutoMaskFreeImage autoDst(dstM.fImage);
71 
72     if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) {
73         return false;
74     }
75 
76     // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
77     // the current clip (and identity matrix) and GrPaint settings
78     GrSurfaceDesc desc;
79     desc.fWidth = dstM.fBounds.width();
80     desc.fHeight = dstM.fBounds.height();
81     desc.fConfig = kAlpha_8_GrPixelConfig;
82 
83     SkAutoTUnref<GrTexture> texture(textureProvider->createApproxTexture(desc));
84     if (!texture) {
85         return false;
86     }
87     texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
88                                dstM.fImage, dstM.fRowBytes);
89 
90     SkRect maskRect = SkRect::Make(dstM.fBounds);
91 
92     return draw_mask(drawContext, clipData, viewMatrix, maskRect, grp, texture);
93 }
94 
95 // Create a mask of 'devPath' and place the result in 'mask'.
create_mask_GPU(GrContext * context,SkRect * maskRect,const SkPath & devPath,const GrStrokeInfo & strokeInfo,bool doAA,int sampleCnt)96 static GrTexture* create_mask_GPU(GrContext* context,
97                                   SkRect* maskRect,
98                                   const SkPath& devPath,
99                                   const GrStrokeInfo& strokeInfo,
100                                   bool doAA,
101                                   int sampleCnt) {
102     // This mask will ultimately be drawn as a non-AA rect (see draw_mask).
103     // Non-AA rects have a bad habit of snapping arbitrarily. Integerize here
104     // so the mask draws in a reproducible manner.
105     *maskRect = SkRect::Make(maskRect->roundOut());
106 
107     GrSurfaceDesc desc;
108     desc.fFlags = kRenderTarget_GrSurfaceFlag;
109     desc.fWidth = SkScalarCeilToInt(maskRect->width());
110     desc.fHeight = SkScalarCeilToInt(maskRect->height());
111     desc.fSampleCnt = doAA ? sampleCnt : 0;
112     // We actually only need A8, but it often isn't supported as a
113     // render target so default to RGBA_8888
114     desc.fConfig = kRGBA_8888_GrPixelConfig;
115 
116     if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)) {
117         desc.fConfig = kAlpha_8_GrPixelConfig;
118     }
119 
120     GrTexture* mask = context->textureProvider()->createApproxTexture(desc);
121     if (nullptr == mask) {
122         return nullptr;
123     }
124 
125     SkRect clipRect = SkRect::MakeWH(maskRect->width(), maskRect->height());
126 
127     SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(mask->asRenderTarget()));
128     if (!drawContext) {
129         return nullptr;
130     }
131 
132     drawContext->clear(nullptr, 0x0, true);
133 
134     GrPaint tempPaint;
135     tempPaint.setAntiAlias(doAA);
136     tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
137 
138     // setup new clip
139     GrClip clip(clipRect);
140 
141     // Draw the mask into maskTexture with the path's integerized top-left at
142     // the origin using tempPaint.
143     SkMatrix translate;
144     translate.setTranslate(-maskRect->fLeft, -maskRect->fTop);
145     drawContext->drawPath(clip, tempPaint, translate, devPath, strokeInfo);
146     return mask;
147 }
148 
draw_path_with_mask_filter(GrContext * context,GrDrawContext * drawContext,const GrClip & clip,GrPaint * paint,const SkMatrix & viewMatrix,const SkMaskFilter * maskFilter,const SkPathEffect * pathEffect,const GrStrokeInfo & origStrokeInfo,SkPath * pathPtr,bool pathIsMutable)149 static void draw_path_with_mask_filter(GrContext* context,
150                                        GrDrawContext* drawContext,
151                                        const GrClip& clip,
152                                        GrPaint* paint,
153                                        const SkMatrix& viewMatrix,
154                                        const SkMaskFilter* maskFilter,
155                                        const SkPathEffect* pathEffect,
156                                        const GrStrokeInfo& origStrokeInfo,
157                                        SkPath* pathPtr,
158                                        bool pathIsMutable) {
159     SkASSERT(maskFilter);
160 
161     SkIRect clipBounds;
162     clip.getConservativeBounds(drawContext->width(), drawContext->height(), &clipBounds);
163     SkTLazy<SkPath> tmpPath;
164     GrStrokeInfo strokeInfo(origStrokeInfo);
165 
166     static const SkRect* cullRect = nullptr;  // TODO: what is our bounds?
167 
168     SkASSERT(strokeInfo.isDashed() || !pathEffect);
169 
170     if (!strokeInfo.isHairlineStyle()) {
171         SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init();
172         if (strokeInfo.isDashed()) {
173             if (pathEffect->filterPath(strokedPath, *pathPtr, &strokeInfo, cullRect)) {
174                 pathPtr = strokedPath;
175                 pathPtr->setIsVolatile(true);
176                 pathIsMutable = true;
177             }
178             strokeInfo.removeDash();
179         }
180         if (strokeInfo.applyToPath(strokedPath, *pathPtr)) {
181             // Apply the stroke to the path if there is one
182             pathPtr = strokedPath;
183             pathPtr->setIsVolatile(true);
184             pathIsMutable = true;
185             strokeInfo.setFillStyle();
186         }
187     }
188 
189     // avoid possibly allocating a new path in transform if we can
190     SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath.init();
191     if (!pathIsMutable) {
192         devPathPtr->setIsVolatile(true);
193     }
194 
195     // transform the path into device space
196     pathPtr->transform(viewMatrix, devPathPtr);
197 
198     SkRect maskRect;
199     if (maskFilter->canFilterMaskGPU(SkRRect::MakeRect(devPathPtr->getBounds()),
200                                      clipBounds,
201                                      viewMatrix,
202                                      &maskRect)) {
203         SkIRect finalIRect;
204         maskRect.roundOut(&finalIRect);
205         if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
206             // clipped out
207             return;
208         }
209 
210         if (maskFilter->directFilterMaskGPU(context->textureProvider(),
211                                             drawContext,
212                                             paint,
213                                             clip,
214                                             viewMatrix,
215                                             strokeInfo,
216                                             *devPathPtr)) {
217             // the mask filter was able to draw itself directly, so there's nothing
218             // left to do.
219             return;
220         }
221 
222         SkAutoTUnref<GrTexture> mask(create_mask_GPU(context,
223                                                      &maskRect,
224                                                      *devPathPtr,
225                                                      strokeInfo,
226                                                      paint->isAntiAlias(),
227                                                      drawContext->numColorSamples()));
228         if (mask) {
229             GrTexture* filtered;
230 
231             if (maskFilter->filterMaskGPU(mask, viewMatrix, maskRect, &filtered, true)) {
232                 // filterMaskGPU gives us ownership of a ref to the result
233                 SkAutoTUnref<GrTexture> atu(filtered);
234                 if (draw_mask(drawContext, clip, viewMatrix, maskRect, paint, filtered)) {
235                     // This path is completely drawn
236                     return;
237                 }
238             }
239         }
240     }
241 
242     // draw the mask on the CPU - this is a fallthrough path in case the
243     // GPU path fails
244     SkPaint::Style style = strokeInfo.isHairlineStyle() ? SkPaint::kStroke_Style :
245                                                           SkPaint::kFill_Style;
246     sw_draw_with_mask_filter(drawContext, context->textureProvider(),
247                              clip, viewMatrix, *devPathPtr,
248                              maskFilter, clipBounds, paint, style);
249 }
250 
drawPathWithMaskFilter(GrContext * context,GrDrawContext * drawContext,const GrClip & clip,const SkPath & origPath,GrPaint * paint,const SkMatrix & viewMatrix,const SkMaskFilter * mf,const SkPathEffect * pathEffect,const GrStrokeInfo & origStrokeInfo,bool pathIsMutable)251 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
252                                          GrDrawContext* drawContext,
253                                          const GrClip& clip,
254                                          const SkPath& origPath,
255                                          GrPaint* paint,
256                                          const SkMatrix& viewMatrix,
257                                          const SkMaskFilter* mf,
258                                          const SkPathEffect* pathEffect,
259                                          const GrStrokeInfo& origStrokeInfo,
260                                          bool pathIsMutable) {
261     SkPath* pathPtr = const_cast<SkPath*>(&origPath);
262 
263     SkTLazy<SkPath> tmpPath;
264     GrStrokeInfo strokeInfo(origStrokeInfo);
265 
266     if (!strokeInfo.isDashed() && pathEffect && pathEffect->filterPath(tmpPath.init(), *pathPtr,
267                                                                        &strokeInfo, nullptr)) {
268         pathPtr = tmpPath.get();
269         pathPtr->setIsVolatile(true);
270         pathIsMutable = true;
271         pathEffect = nullptr;
272     }
273 
274     draw_path_with_mask_filter(context, drawContext, clip, paint, viewMatrix, mf, pathEffect,
275                                strokeInfo, pathPtr, pathIsMutable);
276 }
277 
drawPathWithMaskFilter(GrContext * context,GrDrawContext * drawContext,const GrClip & clip,const SkPath & origSrcPath,const SkPaint & paint,const SkMatrix & origViewMatrix,const SkMatrix * prePathMatrix,const SkIRect & clipBounds,bool pathIsMutable)278 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
279                                          GrDrawContext* drawContext,
280                                          const GrClip& clip,
281                                          const SkPath& origSrcPath,
282                                          const SkPaint& paint,
283                                          const SkMatrix& origViewMatrix,
284                                          const SkMatrix* prePathMatrix,
285                                          const SkIRect& clipBounds,
286                                          bool pathIsMutable) {
287     SkASSERT(!pathIsMutable || origSrcPath.isVolatile());
288 
289     GrStrokeInfo strokeInfo(paint);
290     // comment out the line below to determine if it is the reason that the chrome mac perf bot
291     // has begun crashing
292     // strokeInfo.setResScale(SkDraw::ComputeResScaleForStroking(origViewMatrix));
293 
294     // If we have a prematrix, apply it to the path, optimizing for the case
295     // where the original path can in fact be modified in place (even though
296     // its parameter type is const).
297     SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
298     SkTLazy<SkPath> tmpPath;
299     SkTLazy<SkPath> effectPath;
300     SkPathEffect* pathEffect = paint.getPathEffect();
301 
302     SkMatrix viewMatrix = origViewMatrix;
303 
304     if (prePathMatrix) {
305         // stroking, path effects, and blurs are supposed to be applied *after* the prePathMatrix.
306         // The pre-path-matrix also should not affect shading.
307         if (!paint.getMaskFilter() && !pathEffect && !paint.getShader() &&
308             (strokeInfo.isFillStyle() || strokeInfo.isHairlineStyle())) {
309             viewMatrix.preConcat(*prePathMatrix);
310         } else {
311             SkPath* result = pathPtr;
312 
313             if (!pathIsMutable) {
314                 result = tmpPath.init();
315                 result->setIsVolatile(true);
316                 pathIsMutable = true;
317             }
318             // should I push prePathMatrix on our MV stack temporarily, instead
319             // of applying it here? See SkDraw.cpp
320             pathPtr->transform(*prePathMatrix, result);
321             pathPtr = result;
322         }
323     }
324     // at this point we're done with prePathMatrix
325     SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
326 
327     SkTLazy<SkPath> tmpPath2;
328 
329     if (!strokeInfo.isDashed() && pathEffect &&
330         pathEffect->filterPath(tmpPath2.init(), *pathPtr, &strokeInfo, nullptr)) {
331         pathPtr = tmpPath2.get();
332         pathPtr->setIsVolatile(true);
333         pathIsMutable = true;
334         pathEffect = nullptr;
335     }
336 
337     GrPaint grPaint;
338     if (!SkPaintToGrPaint(context, paint, viewMatrix, &grPaint)) {
339         return;
340     }
341 
342     if (paint.getMaskFilter()) {
343         draw_path_with_mask_filter(context, drawContext, clip, &grPaint, viewMatrix,
344                                    paint.getMaskFilter(), pathEffect, strokeInfo,
345                                    pathPtr, pathIsMutable);
346     } else {
347         drawContext->drawPath(clip, grPaint, viewMatrix, *pathPtr, strokeInfo);
348     }
349 }
350 
351