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 "GrFixedClip.h"
13 #include "effects/GrSimpleTextureEffect.h"
14 #include "GrStyle.h"
15 #include "GrTexture.h"
16 #include "GrTextureProvider.h"
17 #include "SkDraw.h"
18 #include "SkGrPriv.h"
19 #include "SkMaskFilter.h"
20 #include "SkPaint.h"
21 #include "SkTLazy.h"
22 
clip_bounds_quick_reject(const SkIRect & clipBounds,const SkIRect & rect)23 static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& rect) {
24     return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBounds, rect);
25 }
26 
27 // Draw a mask using the supplied paint. Since the coverage/geometry
28 // is already burnt into the mask this boils down to a rect draw.
29 // Return true if the mask was successfully drawn.
draw_mask(GrDrawContext * drawContext,const GrClip & clip,const SkMatrix & viewMatrix,const SkIRect & maskRect,GrPaint * grp,GrTexture * mask)30 static bool draw_mask(GrDrawContext* drawContext,
31                       const GrClip& clip,
32                       const SkMatrix& viewMatrix,
33                       const SkIRect& maskRect,
34                       GrPaint* grp,
35                       GrTexture* mask) {
36     SkMatrix matrix;
37     matrix.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
38     matrix.postIDiv(mask->width(), mask->height());
39     matrix.preConcat(viewMatrix);
40     grp->addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(mask, nullptr, matrix));
41 
42     SkMatrix inverse;
43     if (!viewMatrix.invert(&inverse)) {
44         return false;
45     }
46     drawContext->fillRectWithLocalMatrix(clip, *grp, SkMatrix::I(), SkRect::Make(maskRect),
47                                          inverse);
48     return true;
49 }
50 
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,SkStrokeRec::InitStyle fillOrHairline)51 static bool sw_draw_with_mask_filter(GrDrawContext* drawContext,
52                                      GrTextureProvider* textureProvider,
53                                      const GrClip& clipData,
54                                      const SkMatrix& viewMatrix,
55                                      const SkPath& devPath,
56                                      const SkMaskFilter* filter,
57                                      const SkIRect& clipBounds,
58                                      GrPaint* grp,
59                                      SkStrokeRec::InitStyle fillOrHairline) {
60     SkMask  srcM, dstM;
61     if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
62                             SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) {
63         return false;
64     }
65     SkAutoMaskFreeImage autoSrc(srcM.fImage);
66 
67     if (!filter->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
68         return false;
69     }
70     // this will free-up dstM when we're done (allocated in filterMask())
71     SkAutoMaskFreeImage autoDst(dstM.fImage);
72 
73     if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) {
74         return false;
75     }
76 
77     // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
78     // the current clip (and identity matrix) and GrPaint settings
79     GrSurfaceDesc desc;
80     desc.fWidth = dstM.fBounds.width();
81     desc.fHeight = dstM.fBounds.height();
82     desc.fConfig = kAlpha_8_GrPixelConfig;
83 
84     SkAutoTUnref<GrTexture> texture(textureProvider->createApproxTexture(desc));
85     if (!texture) {
86         return false;
87     }
88     texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
89                                dstM.fImage, dstM.fRowBytes);
90 
91     return draw_mask(drawContext, clipData, viewMatrix, dstM.fBounds, grp, texture);
92 }
93 
94 // Create a mask of 'devPath' and place the result in 'mask'.
create_mask_GPU(GrContext * context,const SkIRect & maskRect,const SkPath & devPath,SkStrokeRec::InitStyle fillOrHairline,bool doAA,int sampleCnt)95 static sk_sp<GrTexture> create_mask_GPU(GrContext* context,
96                                         const SkIRect& maskRect,
97                                         const SkPath& devPath,
98                                         SkStrokeRec::InitStyle fillOrHairline,
99                                         bool doAA,
100                                         int sampleCnt) {
101     if (!doAA) {
102         // Don't need MSAA if mask isn't AA
103         sampleCnt = 0;
104     }
105 
106     sk_sp<GrDrawContext> drawContext(context->makeDrawContextWithFallback(SkBackingFit::kApprox,
107                                                                           maskRect.width(),
108                                                                           maskRect.height(),
109                                                                           kAlpha_8_GrPixelConfig,
110                                                                           nullptr,
111                                                                           sampleCnt));
112     if (!drawContext) {
113         return nullptr;
114     }
115 
116     drawContext->clear(nullptr, 0x0, true);
117 
118     GrPaint tempPaint;
119     tempPaint.setAntiAlias(doAA);
120     tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
121 
122     // setup new clip
123     const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height());
124     GrFixedClip clip(clipRect);
125 
126     // Draw the mask into maskTexture with the path's integerized top-left at
127     // the origin using tempPaint.
128     SkMatrix translate;
129     translate.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
130     drawContext->drawPath(clip, tempPaint, translate, devPath, GrStyle(fillOrHairline));
131     return drawContext->asTexture();;
132 }
133 
draw_path_with_mask_filter(GrContext * context,GrDrawContext * drawContext,const GrClip & clip,GrPaint * paint,const SkMatrix & viewMatrix,const SkMaskFilter * maskFilter,const GrStyle & style,const SkPath * path,bool pathIsMutable)134 static void draw_path_with_mask_filter(GrContext* context,
135                                        GrDrawContext* drawContext,
136                                        const GrClip& clip,
137                                        GrPaint* paint,
138                                        const SkMatrix& viewMatrix,
139                                        const SkMaskFilter* maskFilter,
140                                        const GrStyle& style,
141                                        const SkPath* path,
142                                        bool pathIsMutable) {
143     SkASSERT(maskFilter);
144 
145     SkIRect clipBounds;
146     clip.getConservativeBounds(drawContext->width(), drawContext->height(), &clipBounds);
147     SkTLazy<SkPath> tmpPath;
148     SkStrokeRec::InitStyle fillOrHairline;
149 
150     // We just fully apply the style here.
151     if (style.applies()) {
152         SkScalar scale = GrStyle::MatrixToScaleFactor(viewMatrix);
153         if (0 == scale || !style.applyToPath(tmpPath.init(), &fillOrHairline, *path, scale)) {
154             return;
155         }
156         pathIsMutable = true;
157         path = tmpPath.get();
158     } else if (style.isSimpleHairline()) {
159         fillOrHairline = SkStrokeRec::kHairline_InitStyle;
160     } else {
161         SkASSERT(style.isSimpleFill());
162         fillOrHairline = SkStrokeRec::kFill_InitStyle;
163     }
164 
165     // transform the path into device space
166     if (!viewMatrix.isIdentity()) {
167         SkPath* result;
168         if (pathIsMutable) {
169             result = const_cast<SkPath*>(path);
170         } else {
171             if (!tmpPath.isValid()) {
172                 tmpPath.init();
173             }
174             result = tmpPath.get();
175         }
176         path->transform(viewMatrix, result);
177         path = result;
178         result->setIsVolatile(true);
179         pathIsMutable = true;
180     }
181 
182     SkRect maskRect;
183     if (maskFilter->canFilterMaskGPU(SkRRect::MakeRect(path->getBounds()),
184                                      clipBounds,
185                                      viewMatrix,
186                                      &maskRect)) {
187         // This mask will ultimately be drawn as a non-AA rect (see draw_mask).
188         // Non-AA rects have a bad habit of snapping arbitrarily. Integerize here
189         // so the mask draws in a reproducible manner.
190         SkIRect finalIRect;
191         maskRect.roundOut(&finalIRect);
192         if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
193             // clipped out
194             return;
195         }
196 
197         if (maskFilter->directFilterMaskGPU(context->textureProvider(),
198                                             drawContext,
199                                             paint,
200                                             clip,
201                                             viewMatrix,
202                                             SkStrokeRec(fillOrHairline),
203                                             *path)) {
204             // the mask filter was able to draw itself directly, so there's nothing
205             // left to do.
206             return;
207         }
208 
209         sk_sp<GrTexture> mask(create_mask_GPU(context,
210                                               finalIRect,
211                                               *path,
212                                               fillOrHairline,
213                                               paint->isAntiAlias(),
214                                               drawContext->numColorSamples()));
215         if (mask) {
216             GrTexture* filtered;
217 
218             if (maskFilter->filterMaskGPU(mask.get(), viewMatrix, finalIRect, &filtered)) {
219                 // filterMaskGPU gives us ownership of a ref to the result
220                 SkAutoTUnref<GrTexture> atu(filtered);
221                 if (draw_mask(drawContext, clip, viewMatrix, finalIRect, paint, filtered)) {
222                     // This path is completely drawn
223                     return;
224                 }
225             }
226         }
227     }
228 
229     sw_draw_with_mask_filter(drawContext, context->textureProvider(),
230                              clip, viewMatrix, *path,
231                              maskFilter, clipBounds, paint, fillOrHairline);
232 }
233 
drawPathWithMaskFilter(GrContext * context,GrDrawContext * drawContext,const GrClip & clip,const SkPath & path,GrPaint * paint,const SkMatrix & viewMatrix,const SkMaskFilter * mf,const GrStyle & style,bool pathIsMutable)234 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
235                                          GrDrawContext* drawContext,
236                                          const GrClip& clip,
237                                          const SkPath& path,
238                                          GrPaint* paint,
239                                          const SkMatrix& viewMatrix,
240                                          const SkMaskFilter* mf,
241                                          const GrStyle& style,
242                                          bool pathIsMutable) {
243     draw_path_with_mask_filter(context, drawContext, clip, paint, viewMatrix, mf,
244                                style, &path, pathIsMutable);
245 }
246 
drawPathWithMaskFilter(GrContext * context,GrDrawContext * drawContext,const GrClip & clip,const SkPath & origPath,const SkPaint & paint,const SkMatrix & origViewMatrix,const SkMatrix * prePathMatrix,const SkIRect & clipBounds,bool pathIsMutable)247 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
248                                          GrDrawContext* drawContext,
249                                          const GrClip& clip,
250                                          const SkPath& origPath,
251                                          const SkPaint& paint,
252                                          const SkMatrix& origViewMatrix,
253                                          const SkMatrix* prePathMatrix,
254                                          const SkIRect& clipBounds,
255                                          bool pathIsMutable) {
256     SkASSERT(!pathIsMutable || origPath.isVolatile());
257 
258     GrStyle style(paint);
259     // If we have a prematrix, apply it to the path, optimizing for the case
260     // where the original path can in fact be modified in place (even though
261     // its parameter type is const).
262 
263     const SkPath* path = &origPath;
264     SkTLazy<SkPath> tmpPath;
265 
266     SkMatrix viewMatrix = origViewMatrix;
267 
268     if (prePathMatrix) {
269         // Styling, blurs, and shading are supposed to be applied *after* the prePathMatrix.
270         if (!paint.getMaskFilter() && !paint.getShader() && !style.applies()) {
271             viewMatrix.preConcat(*prePathMatrix);
272         } else {
273             SkPath* result = pathIsMutable ? const_cast<SkPath*>(path) : tmpPath.init();
274             pathIsMutable = true;
275             path->transform(*prePathMatrix, result);
276             path = result;
277             result->setIsVolatile(true);
278         }
279     }
280     // at this point we're done with prePathMatrix
281     SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
282 
283     GrPaint grPaint;
284     if (!SkPaintToGrPaint(context, drawContext, paint, viewMatrix, &grPaint)) {
285         return;
286     }
287 
288     if (paint.getMaskFilter()) {
289         draw_path_with_mask_filter(context, drawContext, clip, &grPaint, viewMatrix,
290                                    paint.getMaskFilter(), style,
291                                    path, pathIsMutable);
292     } else {
293         drawContext->drawPath(clip, grPaint, viewMatrix, *path, style);
294     }
295 }
296