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