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 "effects/GrSimpleTextureEffect.h"
13 #include "GrStrokeInfo.h"
14 #include "GrTexture.h"
15 #include "GrTextureProvider.h"
16 #include "SkDraw.h"
17 #include "SkGrPriv.h"
18 #include "SkMaskFilter.h"
19 #include "SkPaint.h"
20
clip_bounds_quick_reject(const SkIRect & clipBounds,const SkIRect & rect)21 static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& rect) {
22 return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBounds, rect);
23 }
24
25 // Draw a mask using the supplied paint. Since the coverage/geometry
26 // is already burnt into the mask this boils down to a rect draw.
27 // Return true if the mask was successfully drawn.
draw_mask(GrDrawContext * drawContext,const GrClip & clip,const SkMatrix & viewMatrix,const SkRect & maskRect,GrPaint * grp,GrTexture * mask)28 static bool draw_mask(GrDrawContext* drawContext,
29 const GrClip& clip,
30 const SkMatrix& viewMatrix,
31 const SkRect& maskRect,
32 GrPaint* grp,
33 GrTexture* mask) {
34 SkMatrix matrix;
35 matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop);
36 matrix.postIDiv(mask->width(), mask->height());
37
38 grp->addCoverageFragmentProcessor(GrSimpleTextureEffect::Create(mask, matrix,
39 kDevice_GrCoordSet))->unref();
40
41 SkMatrix inverse;
42 if (!viewMatrix.invert(&inverse)) {
43 return false;
44 }
45 drawContext->fillRectWithLocalMatrix(clip, *grp, SkMatrix::I(), maskRect, inverse);
46 return true;
47 }
48
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,SkPaint::Style style)49 static bool sw_draw_with_mask_filter(GrDrawContext* drawContext,
50 GrTextureProvider* textureProvider,
51 const GrClip& clipData,
52 const SkMatrix& viewMatrix,
53 const SkPath& devPath,
54 const SkMaskFilter* filter,
55 const SkIRect& clipBounds,
56 GrPaint* grp,
57 SkPaint::Style style) {
58 SkMask srcM, dstM;
59
60 if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
61 SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {
62 return false;
63 }
64 SkAutoMaskFreeImage autoSrc(srcM.fImage);
65
66 if (!filter->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
67 return false;
68 }
69 // this will free-up dstM when we're done (allocated in filterMask())
70 SkAutoMaskFreeImage autoDst(dstM.fImage);
71
72 if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) {
73 return false;
74 }
75
76 // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
77 // the current clip (and identity matrix) and GrPaint settings
78 GrSurfaceDesc desc;
79 desc.fWidth = dstM.fBounds.width();
80 desc.fHeight = dstM.fBounds.height();
81 desc.fConfig = kAlpha_8_GrPixelConfig;
82
83 SkAutoTUnref<GrTexture> texture(textureProvider->createApproxTexture(desc));
84 if (!texture) {
85 return false;
86 }
87 texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
88 dstM.fImage, dstM.fRowBytes);
89
90 SkRect maskRect = SkRect::Make(dstM.fBounds);
91
92 return draw_mask(drawContext, clipData, viewMatrix, maskRect, grp, texture);
93 }
94
95 // Create a mask of 'devPath' and place the result in 'mask'.
create_mask_GPU(GrContext * context,SkRect * maskRect,const SkPath & devPath,const GrStrokeInfo & strokeInfo,bool doAA,int sampleCnt)96 static GrTexture* create_mask_GPU(GrContext* context,
97 SkRect* maskRect,
98 const SkPath& devPath,
99 const GrStrokeInfo& strokeInfo,
100 bool doAA,
101 int sampleCnt) {
102 // This mask will ultimately be drawn as a non-AA rect (see draw_mask).
103 // Non-AA rects have a bad habit of snapping arbitrarily. Integerize here
104 // so the mask draws in a reproducible manner.
105 *maskRect = SkRect::Make(maskRect->roundOut());
106
107 GrSurfaceDesc desc;
108 desc.fFlags = kRenderTarget_GrSurfaceFlag;
109 desc.fWidth = SkScalarCeilToInt(maskRect->width());
110 desc.fHeight = SkScalarCeilToInt(maskRect->height());
111 desc.fSampleCnt = doAA ? sampleCnt : 0;
112 // We actually only need A8, but it often isn't supported as a
113 // render target so default to RGBA_8888
114 desc.fConfig = kRGBA_8888_GrPixelConfig;
115
116 if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)) {
117 desc.fConfig = kAlpha_8_GrPixelConfig;
118 }
119
120 GrTexture* mask = context->textureProvider()->createApproxTexture(desc);
121 if (nullptr == mask) {
122 return nullptr;
123 }
124
125 SkRect clipRect = SkRect::MakeWH(maskRect->width(), maskRect->height());
126
127 SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(mask->asRenderTarget()));
128 if (!drawContext) {
129 return nullptr;
130 }
131
132 drawContext->clear(nullptr, 0x0, true);
133
134 GrPaint tempPaint;
135 tempPaint.setAntiAlias(doAA);
136 tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
137
138 // setup new clip
139 GrClip clip(clipRect);
140
141 // Draw the mask into maskTexture with the path's integerized top-left at
142 // the origin using tempPaint.
143 SkMatrix translate;
144 translate.setTranslate(-maskRect->fLeft, -maskRect->fTop);
145 drawContext->drawPath(clip, tempPaint, translate, devPath, strokeInfo);
146 return mask;
147 }
148
draw_path_with_mask_filter(GrContext * context,GrDrawContext * drawContext,const GrClip & clip,GrPaint * paint,const SkMatrix & viewMatrix,const SkMaskFilter * maskFilter,const SkPathEffect * pathEffect,const GrStrokeInfo & origStrokeInfo,SkPath * pathPtr,bool pathIsMutable)149 static void draw_path_with_mask_filter(GrContext* context,
150 GrDrawContext* drawContext,
151 const GrClip& clip,
152 GrPaint* paint,
153 const SkMatrix& viewMatrix,
154 const SkMaskFilter* maskFilter,
155 const SkPathEffect* pathEffect,
156 const GrStrokeInfo& origStrokeInfo,
157 SkPath* pathPtr,
158 bool pathIsMutable) {
159 SkASSERT(maskFilter);
160
161 SkIRect clipBounds;
162 clip.getConservativeBounds(drawContext->width(), drawContext->height(), &clipBounds);
163 SkTLazy<SkPath> tmpPath;
164 GrStrokeInfo strokeInfo(origStrokeInfo);
165
166 static const SkRect* cullRect = nullptr; // TODO: what is our bounds?
167
168 SkASSERT(strokeInfo.isDashed() || !pathEffect);
169
170 if (!strokeInfo.isHairlineStyle()) {
171 SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init();
172 if (strokeInfo.isDashed()) {
173 if (pathEffect->filterPath(strokedPath, *pathPtr, &strokeInfo, cullRect)) {
174 pathPtr = strokedPath;
175 pathPtr->setIsVolatile(true);
176 pathIsMutable = true;
177 }
178 strokeInfo.removeDash();
179 }
180 if (strokeInfo.applyToPath(strokedPath, *pathPtr)) {
181 // Apply the stroke to the path if there is one
182 pathPtr = strokedPath;
183 pathPtr->setIsVolatile(true);
184 pathIsMutable = true;
185 strokeInfo.setFillStyle();
186 }
187 }
188
189 // avoid possibly allocating a new path in transform if we can
190 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath.init();
191 if (!pathIsMutable) {
192 devPathPtr->setIsVolatile(true);
193 }
194
195 // transform the path into device space
196 pathPtr->transform(viewMatrix, devPathPtr);
197
198 SkRect maskRect;
199 if (maskFilter->canFilterMaskGPU(SkRRect::MakeRect(devPathPtr->getBounds()),
200 clipBounds,
201 viewMatrix,
202 &maskRect)) {
203 SkIRect finalIRect;
204 maskRect.roundOut(&finalIRect);
205 if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
206 // clipped out
207 return;
208 }
209
210 if (maskFilter->directFilterMaskGPU(context->textureProvider(),
211 drawContext,
212 paint,
213 clip,
214 viewMatrix,
215 strokeInfo,
216 *devPathPtr)) {
217 // the mask filter was able to draw itself directly, so there's nothing
218 // left to do.
219 return;
220 }
221
222 SkAutoTUnref<GrTexture> mask(create_mask_GPU(context,
223 &maskRect,
224 *devPathPtr,
225 strokeInfo,
226 paint->isAntiAlias(),
227 drawContext->numColorSamples()));
228 if (mask) {
229 GrTexture* filtered;
230
231 if (maskFilter->filterMaskGPU(mask, viewMatrix, maskRect, &filtered, true)) {
232 // filterMaskGPU gives us ownership of a ref to the result
233 SkAutoTUnref<GrTexture> atu(filtered);
234 if (draw_mask(drawContext, clip, viewMatrix, maskRect, paint, filtered)) {
235 // This path is completely drawn
236 return;
237 }
238 }
239 }
240 }
241
242 // draw the mask on the CPU - this is a fallthrough path in case the
243 // GPU path fails
244 SkPaint::Style style = strokeInfo.isHairlineStyle() ? SkPaint::kStroke_Style :
245 SkPaint::kFill_Style;
246 sw_draw_with_mask_filter(drawContext, context->textureProvider(),
247 clip, viewMatrix, *devPathPtr,
248 maskFilter, clipBounds, paint, style);
249 }
250
drawPathWithMaskFilter(GrContext * context,GrDrawContext * drawContext,const GrClip & clip,const SkPath & origPath,GrPaint * paint,const SkMatrix & viewMatrix,const SkMaskFilter * mf,const SkPathEffect * pathEffect,const GrStrokeInfo & origStrokeInfo,bool pathIsMutable)251 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
252 GrDrawContext* drawContext,
253 const GrClip& clip,
254 const SkPath& origPath,
255 GrPaint* paint,
256 const SkMatrix& viewMatrix,
257 const SkMaskFilter* mf,
258 const SkPathEffect* pathEffect,
259 const GrStrokeInfo& origStrokeInfo,
260 bool pathIsMutable) {
261 SkPath* pathPtr = const_cast<SkPath*>(&origPath);
262
263 SkTLazy<SkPath> tmpPath;
264 GrStrokeInfo strokeInfo(origStrokeInfo);
265
266 if (!strokeInfo.isDashed() && pathEffect && pathEffect->filterPath(tmpPath.init(), *pathPtr,
267 &strokeInfo, nullptr)) {
268 pathPtr = tmpPath.get();
269 pathPtr->setIsVolatile(true);
270 pathIsMutable = true;
271 pathEffect = nullptr;
272 }
273
274 draw_path_with_mask_filter(context, drawContext, clip, paint, viewMatrix, mf, pathEffect,
275 strokeInfo, pathPtr, pathIsMutable);
276 }
277
drawPathWithMaskFilter(GrContext * context,GrDrawContext * drawContext,const GrClip & clip,const SkPath & origSrcPath,const SkPaint & paint,const SkMatrix & origViewMatrix,const SkMatrix * prePathMatrix,const SkIRect & clipBounds,bool pathIsMutable)278 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
279 GrDrawContext* drawContext,
280 const GrClip& clip,
281 const SkPath& origSrcPath,
282 const SkPaint& paint,
283 const SkMatrix& origViewMatrix,
284 const SkMatrix* prePathMatrix,
285 const SkIRect& clipBounds,
286 bool pathIsMutable) {
287 SkASSERT(!pathIsMutable || origSrcPath.isVolatile());
288
289 GrStrokeInfo strokeInfo(paint);
290 // comment out the line below to determine if it is the reason that the chrome mac perf bot
291 // has begun crashing
292 // strokeInfo.setResScale(SkDraw::ComputeResScaleForStroking(origViewMatrix));
293
294 // If we have a prematrix, apply it to the path, optimizing for the case
295 // where the original path can in fact be modified in place (even though
296 // its parameter type is const).
297 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
298 SkTLazy<SkPath> tmpPath;
299 SkTLazy<SkPath> effectPath;
300 SkPathEffect* pathEffect = paint.getPathEffect();
301
302 SkMatrix viewMatrix = origViewMatrix;
303
304 if (prePathMatrix) {
305 // stroking, path effects, and blurs are supposed to be applied *after* the prePathMatrix.
306 // The pre-path-matrix also should not affect shading.
307 if (!paint.getMaskFilter() && !pathEffect && !paint.getShader() &&
308 (strokeInfo.isFillStyle() || strokeInfo.isHairlineStyle())) {
309 viewMatrix.preConcat(*prePathMatrix);
310 } else {
311 SkPath* result = pathPtr;
312
313 if (!pathIsMutable) {
314 result = tmpPath.init();
315 result->setIsVolatile(true);
316 pathIsMutable = true;
317 }
318 // should I push prePathMatrix on our MV stack temporarily, instead
319 // of applying it here? See SkDraw.cpp
320 pathPtr->transform(*prePathMatrix, result);
321 pathPtr = result;
322 }
323 }
324 // at this point we're done with prePathMatrix
325 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
326
327 SkTLazy<SkPath> tmpPath2;
328
329 if (!strokeInfo.isDashed() && pathEffect &&
330 pathEffect->filterPath(tmpPath2.init(), *pathPtr, &strokeInfo, nullptr)) {
331 pathPtr = tmpPath2.get();
332 pathPtr->setIsVolatile(true);
333 pathIsMutable = true;
334 pathEffect = nullptr;
335 }
336
337 GrPaint grPaint;
338 if (!SkPaintToGrPaint(context, paint, viewMatrix, &grPaint)) {
339 return;
340 }
341
342 if (paint.getMaskFilter()) {
343 draw_path_with_mask_filter(context, drawContext, clip, &grPaint, viewMatrix,
344 paint.getMaskFilter(), pathEffect, strokeInfo,
345 pathPtr, pathIsMutable);
346 } else {
347 drawContext->drawPath(clip, grPaint, viewMatrix, *pathPtr, strokeInfo);
348 }
349 }
350
351