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