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