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