blob: 66861ec07fe53682c07cfe7cf22305f7971463be [file] [log] [blame]
robertphillipsccb1b572015-05-27 11:02:55 -07001/*
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"
bsalomon76228632015-05-29 08:02:10 -070010#include "GrCaps.h"
robertphillipsccb1b572015-05-27 11:02:55 -070011#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 "SkGr.h"
18#include "SkMaskFilter.h"
19#include "SkPaint.h"
20
21static 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.
28static bool draw_mask(GrDrawContext* drawContext,
29 GrRenderTarget* rt,
30 const GrClip& clip,
31 const SkMatrix& viewMatrix,
32 const SkRect& maskRect,
33 GrPaint* grp,
34 GrTexture* mask) {
35 SkMatrix matrix;
36 matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop);
37 matrix.postIDiv(mask->width(), mask->height());
38
39 grp->addCoverageProcessor(GrSimpleTextureEffect::Create(mask, matrix,
40 kDevice_GrCoordSet))->unref();
41
42 SkMatrix inverse;
43 if (!viewMatrix.invert(&inverse)) {
44 return false;
45 }
46 drawContext->drawNonAARectWithLocalMatrix(rt, clip, *grp, SkMatrix::I(), maskRect, inverse);
47 return true;
48}
49
50static bool draw_with_mask_filter(GrDrawContext* drawContext,
51 GrTextureProvider* textureProvider,
52 GrRenderTarget* rt,
53 const GrClip& clipData,
54 const SkMatrix& viewMatrix,
55 const SkPath& devPath,
56 SkMaskFilter* filter,
57 const SkIRect& clipBounds,
58 GrPaint* grp,
59 SkPaint::Style style) {
60 SkMask srcM, dstM;
61
62 if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
63 SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {
64 return false;
65 }
66 SkAutoMaskFreeImage autoSrc(srcM.fImage);
67
68 if (!filter->filterMask(&dstM, srcM, viewMatrix, NULL)) {
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->refScratchTexture(
86 desc, GrTextureProvider::kApprox_ScratchTexMatch));
87 if (!texture) {
88 return false;
89 }
90 texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
91 dstM.fImage, dstM.fRowBytes);
92
93 SkRect maskRect = SkRect::Make(dstM.fBounds);
94
95 return draw_mask(drawContext, rt, clipData, viewMatrix, maskRect, grp, texture);
96}
97
98// Create a mask of 'devPath' and place the result in 'mask'.
99static GrTexture* create_mask_GPU(GrContext* context,
100 const SkRect& maskRect,
101 const SkPath& devPath,
102 const GrStrokeInfo& strokeInfo,
103 bool doAA,
104 int sampleCnt) {
105 GrSurfaceDesc desc;
106 desc.fFlags = kRenderTarget_GrSurfaceFlag;
107 desc.fWidth = SkScalarCeilToInt(maskRect.width());
108 desc.fHeight = SkScalarCeilToInt(maskRect.height());
109 desc.fSampleCnt = doAA ? sampleCnt : 0;
110 // We actually only need A8, but it often isn't supported as a
111 // render target so default to RGBA_8888
112 desc.fConfig = kRGBA_8888_GrPixelConfig;
113
bsalomon76228632015-05-29 08:02:10 -0700114 if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)) {
robertphillipsccb1b572015-05-27 11:02:55 -0700115 desc.fConfig = kAlpha_8_GrPixelConfig;
116 }
117
118 GrTexture* mask = context->textureProvider()->refScratchTexture(
119 desc, GrTextureProvider::kApprox_ScratchTexMatch);
120 if (NULL == mask) {
121 return NULL;
122 }
123
124 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
125
126 GrDrawContext* drawContext = context->drawContext();
127 if (!drawContext) {
128 return NULL;
129 }
130
131 drawContext->clear(mask->asRenderTarget(), NULL, 0x0, true);
132
133 GrPaint tempPaint;
134 tempPaint.setAntiAlias(doAA);
135 tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
136
137 // setup new clip
138 GrClip clip(clipRect);
139
140 // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint.
141 SkMatrix translate;
142 translate.setTranslate(-maskRect.fLeft, -maskRect.fTop);
143 drawContext->drawPath(mask->asRenderTarget(), clip, tempPaint, translate, devPath, strokeInfo);
144 return mask;
145}
146
147void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
148 GrDrawContext* drawContext,
149 GrRenderTarget* renderTarget,
150 const GrClip& clip,
151 const SkPath& origSrcPath,
152 const SkPaint& paint,
153 const SkMatrix& origViewMatrix,
154 const SkMatrix* prePathMatrix,
155 const SkIRect& clipBounds,
156 bool pathIsMutable) {
157 SkASSERT(!pathIsMutable || origSrcPath.isVolatile());
158
159 GrStrokeInfo strokeInfo(paint);
160
161 // If we have a prematrix, apply it to the path, optimizing for the case
162 // where the original path can in fact be modified in place (even though
163 // its parameter type is const).
164 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
165 SkTLazy<SkPath> tmpPath;
166 SkTLazy<SkPath> effectPath;
167 SkPathEffect* pathEffect = paint.getPathEffect();
168
169 SkMatrix viewMatrix = origViewMatrix;
170
171 if (prePathMatrix) {
172 // stroking, path effects, and blurs are supposed to be applied *after* the prePathMatrix.
173 // The pre-path-matrix also should not affect shading.
174 if (NULL == paint.getMaskFilter() && NULL == pathEffect && NULL == paint.getShader() &&
175 (strokeInfo.isFillStyle() || strokeInfo.isHairlineStyle())) {
176 viewMatrix.preConcat(*prePathMatrix);
177 } else {
178 SkPath* result = pathPtr;
179
180 if (!pathIsMutable) {
181 result = tmpPath.init();
182 result->setIsVolatile(true);
183 pathIsMutable = true;
184 }
185 // should I push prePathMatrix on our MV stack temporarily, instead
186 // of applying it here? See SkDraw.cpp
187 pathPtr->transform(*prePathMatrix, result);
188 pathPtr = result;
189 }
190 }
191 // at this point we're done with prePathMatrix
192 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
193
194 GrPaint grPaint;
195 if (!SkPaint2GrPaint(context, renderTarget, paint, viewMatrix, true, &grPaint)) {
196 return;
197 }
198
199 const SkRect* cullRect = NULL; // TODO: what is our bounds?
200 if (!strokeInfo.isDashed() && pathEffect && pathEffect->filterPath(effectPath.init(), *pathPtr,
201 &strokeInfo, cullRect)) {
202 pathPtr = effectPath.get();
203 pathIsMutable = true;
204 }
205
206 if (paint.getMaskFilter()) {
207 if (!strokeInfo.isHairlineStyle()) {
208 SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init();
209 if (strokeInfo.isDashed()) {
210 if (pathEffect->filterPath(strokedPath, *pathPtr, &strokeInfo, cullRect)) {
211 pathPtr = strokedPath;
212 pathIsMutable = true;
213 }
214 strokeInfo.removeDash();
215 }
216 if (strokeInfo.applyToPath(strokedPath, *pathPtr)) {
217 pathPtr = strokedPath;
218 pathIsMutable = true;
219 strokeInfo.setFillStyle();
220 }
221 }
222
223 // avoid possibly allocating a new path in transform if we can
224 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath.init();
225 if (!pathIsMutable) {
226 devPathPtr->setIsVolatile(true);
227 }
228
229 // transform the path into device space
230 pathPtr->transform(viewMatrix, devPathPtr);
231
232 SkRect maskRect;
233 if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(),
234 clipBounds,
235 viewMatrix,
236 &maskRect)) {
237 SkIRect finalIRect;
238 maskRect.roundOut(&finalIRect);
239 if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
240 // clipped out
241 return;
242 }
243
244 if (paint.getMaskFilter()->directFilterMaskGPU(context,
245 renderTarget,
246 &grPaint,
247 clip,
248 viewMatrix,
249 strokeInfo,
250 *devPathPtr)) {
251 // the mask filter was able to draw itself directly, so there's nothing
252 // left to do.
253 return;
254 }
255
256
257 SkAutoTUnref<GrTexture> mask(create_mask_GPU(context,
258 maskRect,
259 *devPathPtr,
260 strokeInfo,
261 grPaint.isAntiAlias(),
262 renderTarget->numSamples()));
263 if (mask) {
264 GrTexture* filtered;
265
266 if (paint.getMaskFilter()->filterMaskGPU(mask, viewMatrix, maskRect,
267 &filtered, true)) {
268 // filterMaskGPU gives us ownership of a ref to the result
269 SkAutoTUnref<GrTexture> atu(filtered);
270 if (draw_mask(drawContext,
271 renderTarget,
272 clip,
273 viewMatrix,
274 maskRect,
275 &grPaint,
276 filtered)) {
277 // This path is completely drawn
278 return;
279 }
280 }
281 }
282 }
283
284 // draw the mask on the CPU - this is a fallthrough path in case the
285 // GPU path fails
286 SkPaint::Style style = strokeInfo.isHairlineStyle() ? SkPaint::kStroke_Style :
287 SkPaint::kFill_Style;
288 draw_with_mask_filter(drawContext, context->textureProvider(), renderTarget,
289 clip, viewMatrix, *devPathPtr,
290 paint.getMaskFilter(), clipBounds, &grPaint, style);
291 return;
292 }
293
294 drawContext->drawPath(renderTarget, clip, grPaint, viewMatrix, *pathPtr, strokeInfo);
295}
296