blob: a96d92e764cd0e2104e187c0f0f86816abde7424 [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
bsalomonac856c92015-08-27 06:30:17 -070039 grp->addCoverageFragmentProcessor(GrSimpleTextureEffect::Create(grp->getProcessorDataManager(),
joshualitt5f10b5c2015-07-09 10:24:35 -070040 mask, matrix,
robertphillipsccb1b572015-05-27 11:02:55 -070041 kDevice_GrCoordSet))->unref();
42
43 SkMatrix inverse;
44 if (!viewMatrix.invert(&inverse)) {
45 return false;
46 }
47 drawContext->drawNonAARectWithLocalMatrix(rt, clip, *grp, SkMatrix::I(), maskRect, inverse);
48 return true;
49}
50
51static bool draw_with_mask_filter(GrDrawContext* drawContext,
52 GrTextureProvider* textureProvider,
53 GrRenderTarget* rt,
54 const GrClip& clipData,
55 const SkMatrix& viewMatrix,
56 const SkPath& devPath,
57 SkMaskFilter* filter,
58 const SkIRect& clipBounds,
59 GrPaint* grp,
60 SkPaint::Style style) {
61 SkMask srcM, dstM;
62
63 if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
64 SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {
65 return false;
66 }
67 SkAutoMaskFreeImage autoSrc(srcM.fImage);
68
halcanary96fcdcc2015-08-27 07:41:13 -070069 if (!filter->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
robertphillipsccb1b572015-05-27 11:02:55 -070070 return false;
71 }
72 // this will free-up dstM when we're done (allocated in filterMask())
73 SkAutoMaskFreeImage autoDst(dstM.fImage);
74
75 if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) {
76 return false;
77 }
78
79 // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
80 // the current clip (and identity matrix) and GrPaint settings
81 GrSurfaceDesc desc;
82 desc.fWidth = dstM.fBounds.width();
83 desc.fHeight = dstM.fBounds.height();
84 desc.fConfig = kAlpha_8_GrPixelConfig;
85
bsalomoneae62002015-07-31 13:59:30 -070086 SkAutoTUnref<GrTexture> texture(textureProvider->createApproxTexture(desc));
robertphillipsccb1b572015-05-27 11:02:55 -070087 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,
robertphillips3833daa2015-09-14 11:18:13 -0700100 SkRect* maskRect,
robertphillipsccb1b572015-05-27 11:02:55 -0700101 const SkPath& devPath,
102 const GrStrokeInfo& strokeInfo,
103 bool doAA,
104 int sampleCnt) {
robertphillips3833daa2015-09-14 11:18:13 -0700105 // This mask will ultimately be drawn as a non-AA rect (see draw_mask).
106 // Non-AA rects have a bad habit of snapping arbitrarily. Integerize here
107 // so the mask draws in a reproducible manner.
108 *maskRect = SkRect::Make(maskRect->roundOut());
109
robertphillipsccb1b572015-05-27 11:02:55 -0700110 GrSurfaceDesc desc;
111 desc.fFlags = kRenderTarget_GrSurfaceFlag;
robertphillips3833daa2015-09-14 11:18:13 -0700112 desc.fWidth = SkScalarCeilToInt(maskRect->width());
113 desc.fHeight = SkScalarCeilToInt(maskRect->height());
robertphillipsccb1b572015-05-27 11:02:55 -0700114 desc.fSampleCnt = doAA ? sampleCnt : 0;
115 // We actually only need A8, but it often isn't supported as a
116 // render target so default to RGBA_8888
117 desc.fConfig = kRGBA_8888_GrPixelConfig;
118
bsalomon76228632015-05-29 08:02:10 -0700119 if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)) {
robertphillipsccb1b572015-05-27 11:02:55 -0700120 desc.fConfig = kAlpha_8_GrPixelConfig;
121 }
122
bsalomoneae62002015-07-31 13:59:30 -0700123 GrTexture* mask = context->textureProvider()->createApproxTexture(desc);
halcanary96fcdcc2015-08-27 07:41:13 -0700124 if (nullptr == mask) {
125 return nullptr;
robertphillipsccb1b572015-05-27 11:02:55 -0700126 }
127
robertphillips3833daa2015-09-14 11:18:13 -0700128 SkRect clipRect = SkRect::MakeWH(maskRect->width(), maskRect->height());
robertphillipsccb1b572015-05-27 11:02:55 -0700129
robertphillipsc9a37062015-09-01 08:34:28 -0700130 SkAutoTUnref<GrDrawContext> drawContext(context->drawContext());
robertphillipsccb1b572015-05-27 11:02:55 -0700131 if (!drawContext) {
halcanary96fcdcc2015-08-27 07:41:13 -0700132 return nullptr;
robertphillipsccb1b572015-05-27 11:02:55 -0700133 }
134
halcanary96fcdcc2015-08-27 07:41:13 -0700135 drawContext->clear(mask->asRenderTarget(), nullptr, 0x0, true);
robertphillipsccb1b572015-05-27 11:02:55 -0700136
137 GrPaint tempPaint;
138 tempPaint.setAntiAlias(doAA);
139 tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
140
141 // setup new clip
142 GrClip clip(clipRect);
143
robertphillips3833daa2015-09-14 11:18:13 -0700144 // Draw the mask into maskTexture with the path's integerized top-left at
145 // the origin using tempPaint.
robertphillipsccb1b572015-05-27 11:02:55 -0700146 SkMatrix translate;
robertphillips3833daa2015-09-14 11:18:13 -0700147 translate.setTranslate(-maskRect->fLeft, -maskRect->fTop);
robertphillipsccb1b572015-05-27 11:02:55 -0700148 drawContext->drawPath(mask->asRenderTarget(), clip, tempPaint, translate, devPath, strokeInfo);
149 return mask;
150}
151
152void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
153 GrDrawContext* drawContext,
154 GrRenderTarget* renderTarget,
155 const GrClip& clip,
156 const SkPath& origSrcPath,
157 const SkPaint& paint,
158 const SkMatrix& origViewMatrix,
159 const SkMatrix* prePathMatrix,
160 const SkIRect& clipBounds,
161 bool pathIsMutable) {
162 SkASSERT(!pathIsMutable || origSrcPath.isVolatile());
163
164 GrStrokeInfo strokeInfo(paint);
165
166 // If we have a prematrix, apply it to the path, optimizing for the case
167 // where the original path can in fact be modified in place (even though
168 // its parameter type is const).
169 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
170 SkTLazy<SkPath> tmpPath;
171 SkTLazy<SkPath> effectPath;
172 SkPathEffect* pathEffect = paint.getPathEffect();
173
174 SkMatrix viewMatrix = origViewMatrix;
175
176 if (prePathMatrix) {
177 // stroking, path effects, and blurs are supposed to be applied *after* the prePathMatrix.
178 // The pre-path-matrix also should not affect shading.
halcanary96fcdcc2015-08-27 07:41:13 -0700179 if (nullptr == paint.getMaskFilter() && nullptr == pathEffect && nullptr == paint.getShader() &&
robertphillipsccb1b572015-05-27 11:02:55 -0700180 (strokeInfo.isFillStyle() || strokeInfo.isHairlineStyle())) {
181 viewMatrix.preConcat(*prePathMatrix);
182 } else {
183 SkPath* result = pathPtr;
184
185 if (!pathIsMutable) {
186 result = tmpPath.init();
187 result->setIsVolatile(true);
188 pathIsMutable = true;
189 }
190 // should I push prePathMatrix on our MV stack temporarily, instead
191 // of applying it here? See SkDraw.cpp
192 pathPtr->transform(*prePathMatrix, result);
193 pathPtr = result;
194 }
195 }
196 // at this point we're done with prePathMatrix
197 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
198
199 GrPaint grPaint;
200 if (!SkPaint2GrPaint(context, renderTarget, paint, viewMatrix, true, &grPaint)) {
201 return;
202 }
203
halcanary96fcdcc2015-08-27 07:41:13 -0700204 const SkRect* cullRect = nullptr; // TODO: what is our bounds?
robertphillipsccb1b572015-05-27 11:02:55 -0700205 if (!strokeInfo.isDashed() && pathEffect && pathEffect->filterPath(effectPath.init(), *pathPtr,
206 &strokeInfo, cullRect)) {
207 pathPtr = effectPath.get();
208 pathIsMutable = true;
209 }
210
211 if (paint.getMaskFilter()) {
212 if (!strokeInfo.isHairlineStyle()) {
213 SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init();
214 if (strokeInfo.isDashed()) {
215 if (pathEffect->filterPath(strokedPath, *pathPtr, &strokeInfo, cullRect)) {
216 pathPtr = strokedPath;
217 pathIsMutable = true;
218 }
219 strokeInfo.removeDash();
220 }
221 if (strokeInfo.applyToPath(strokedPath, *pathPtr)) {
222 pathPtr = strokedPath;
223 pathIsMutable = true;
224 strokeInfo.setFillStyle();
225 }
226 }
227
228 // avoid possibly allocating a new path in transform if we can
229 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath.init();
230 if (!pathIsMutable) {
231 devPathPtr->setIsVolatile(true);
232 }
233
234 // transform the path into device space
235 pathPtr->transform(viewMatrix, devPathPtr);
236
237 SkRect maskRect;
238 if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(),
239 clipBounds,
240 viewMatrix,
241 &maskRect)) {
242 SkIRect finalIRect;
243 maskRect.roundOut(&finalIRect);
244 if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
245 // clipped out
246 return;
247 }
248
robertphillipsff0ca5e2015-07-22 11:54:44 -0700249 if (paint.getMaskFilter()->directFilterMaskGPU(context->textureProvider(),
250 drawContext,
robertphillipsccb1b572015-05-27 11:02:55 -0700251 renderTarget,
252 &grPaint,
253 clip,
254 viewMatrix,
255 strokeInfo,
256 *devPathPtr)) {
257 // the mask filter was able to draw itself directly, so there's nothing
258 // left to do.
259 return;
260 }
261
robertphillipsccb1b572015-05-27 11:02:55 -0700262 SkAutoTUnref<GrTexture> mask(create_mask_GPU(context,
robertphillips3833daa2015-09-14 11:18:13 -0700263 &maskRect,
robertphillipsccb1b572015-05-27 11:02:55 -0700264 *devPathPtr,
265 strokeInfo,
266 grPaint.isAntiAlias(),
vbuzinovdded6962015-06-12 08:59:45 -0700267 renderTarget->numColorSamples()));
robertphillipsccb1b572015-05-27 11:02:55 -0700268 if (mask) {
269 GrTexture* filtered;
270
271 if (paint.getMaskFilter()->filterMaskGPU(mask, viewMatrix, maskRect,
272 &filtered, true)) {
273 // filterMaskGPU gives us ownership of a ref to the result
274 SkAutoTUnref<GrTexture> atu(filtered);
275 if (draw_mask(drawContext,
276 renderTarget,
277 clip,
278 viewMatrix,
279 maskRect,
280 &grPaint,
281 filtered)) {
282 // This path is completely drawn
283 return;
284 }
285 }
286 }
287 }
288
289 // draw the mask on the CPU - this is a fallthrough path in case the
290 // GPU path fails
291 SkPaint::Style style = strokeInfo.isHairlineStyle() ? SkPaint::kStroke_Style :
292 SkPaint::kFill_Style;
293 draw_with_mask_filter(drawContext, context->textureProvider(), renderTarget,
294 clip, viewMatrix, *devPathPtr,
295 paint.getMaskFilter(), clipBounds, &grPaint, style);
296 return;
297 }
298
299 drawContext->drawPath(renderTarget, clip, grPaint, viewMatrix, *pathPtr, strokeInfo);
300}
301