blob: a396c3340dc92580f226f42b30c2f4bf1dec1f5c [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"
bsalomonf1b7a1d2015-09-28 06:26:28 -070017#include "SkGrPriv.h"
robertphillipsccb1b572015-05-27 11:02:55 -070018#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
bsalomon4a339522015-10-06 08:40:50 -070039 grp->addCoverageFragmentProcessor(GrSimpleTextureEffect::Create(mask, matrix,
40 kDevice_GrCoordSet))->unref();
robertphillipsccb1b572015-05-27 11:02:55 -070041
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
halcanary96fcdcc2015-08-27 07:41:13 -070068 if (!filter->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
robertphillipsccb1b572015-05-27 11:02:55 -070069 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
bsalomoneae62002015-07-31 13:59:30 -070085 SkAutoTUnref<GrTexture> texture(textureProvider->createApproxTexture(desc));
robertphillipsccb1b572015-05-27 11:02:55 -070086 if (!texture) {
87 return false;
88 }
89 texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
90 dstM.fImage, dstM.fRowBytes);
91
92 SkRect maskRect = SkRect::Make(dstM.fBounds);
93
94 return draw_mask(drawContext, rt, clipData, viewMatrix, maskRect, grp, texture);
95}
96
97// Create a mask of 'devPath' and place the result in 'mask'.
98static GrTexture* create_mask_GPU(GrContext* context,
robertphillips3833daa2015-09-14 11:18:13 -070099 SkRect* maskRect,
robertphillipsccb1b572015-05-27 11:02:55 -0700100 const SkPath& devPath,
101 const GrStrokeInfo& strokeInfo,
102 bool doAA,
103 int sampleCnt) {
robertphillips3833daa2015-09-14 11:18:13 -0700104 // This mask will ultimately be drawn as a non-AA rect (see draw_mask).
105 // Non-AA rects have a bad habit of snapping arbitrarily. Integerize here
106 // so the mask draws in a reproducible manner.
107 *maskRect = SkRect::Make(maskRect->roundOut());
108
robertphillipsccb1b572015-05-27 11:02:55 -0700109 GrSurfaceDesc desc;
110 desc.fFlags = kRenderTarget_GrSurfaceFlag;
robertphillips3833daa2015-09-14 11:18:13 -0700111 desc.fWidth = SkScalarCeilToInt(maskRect->width());
112 desc.fHeight = SkScalarCeilToInt(maskRect->height());
robertphillipsccb1b572015-05-27 11:02:55 -0700113 desc.fSampleCnt = doAA ? sampleCnt : 0;
114 // We actually only need A8, but it often isn't supported as a
115 // render target so default to RGBA_8888
116 desc.fConfig = kRGBA_8888_GrPixelConfig;
117
bsalomon76228632015-05-29 08:02:10 -0700118 if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)) {
robertphillipsccb1b572015-05-27 11:02:55 -0700119 desc.fConfig = kAlpha_8_GrPixelConfig;
120 }
121
bsalomoneae62002015-07-31 13:59:30 -0700122 GrTexture* mask = context->textureProvider()->createApproxTexture(desc);
halcanary96fcdcc2015-08-27 07:41:13 -0700123 if (nullptr == mask) {
124 return nullptr;
robertphillipsccb1b572015-05-27 11:02:55 -0700125 }
126
robertphillips3833daa2015-09-14 11:18:13 -0700127 SkRect clipRect = SkRect::MakeWH(maskRect->width(), maskRect->height());
robertphillipsccb1b572015-05-27 11:02:55 -0700128
robertphillipsc9a37062015-09-01 08:34:28 -0700129 SkAutoTUnref<GrDrawContext> drawContext(context->drawContext());
robertphillipsccb1b572015-05-27 11:02:55 -0700130 if (!drawContext) {
halcanary96fcdcc2015-08-27 07:41:13 -0700131 return nullptr;
robertphillipsccb1b572015-05-27 11:02:55 -0700132 }
133
halcanary96fcdcc2015-08-27 07:41:13 -0700134 drawContext->clear(mask->asRenderTarget(), nullptr, 0x0, true);
robertphillipsccb1b572015-05-27 11:02:55 -0700135
136 GrPaint tempPaint;
137 tempPaint.setAntiAlias(doAA);
138 tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
139
140 // setup new clip
141 GrClip clip(clipRect);
142
robertphillips3833daa2015-09-14 11:18:13 -0700143 // Draw the mask into maskTexture with the path's integerized top-left at
144 // the origin using tempPaint.
robertphillipsccb1b572015-05-27 11:02:55 -0700145 SkMatrix translate;
robertphillips3833daa2015-09-14 11:18:13 -0700146 translate.setTranslate(-maskRect->fLeft, -maskRect->fTop);
robertphillipsccb1b572015-05-27 11:02:55 -0700147 drawContext->drawPath(mask->asRenderTarget(), clip, tempPaint, translate, devPath, strokeInfo);
148 return mask;
149}
150
151void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
152 GrDrawContext* drawContext,
153 GrRenderTarget* renderTarget,
154 const GrClip& clip,
155 const SkPath& origSrcPath,
156 const SkPaint& paint,
157 const SkMatrix& origViewMatrix,
158 const SkMatrix* prePathMatrix,
159 const SkIRect& clipBounds,
160 bool pathIsMutable) {
161 SkASSERT(!pathIsMutable || origSrcPath.isVolatile());
162
163 GrStrokeInfo strokeInfo(paint);
164
165 // If we have a prematrix, apply it to the path, optimizing for the case
166 // where the original path can in fact be modified in place (even though
167 // its parameter type is const).
168 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
169 SkTLazy<SkPath> tmpPath;
170 SkTLazy<SkPath> effectPath;
171 SkPathEffect* pathEffect = paint.getPathEffect();
172
173 SkMatrix viewMatrix = origViewMatrix;
174
175 if (prePathMatrix) {
176 // stroking, path effects, and blurs are supposed to be applied *after* the prePathMatrix.
177 // The pre-path-matrix also should not affect shading.
halcanary96fcdcc2015-08-27 07:41:13 -0700178 if (nullptr == paint.getMaskFilter() && nullptr == pathEffect && nullptr == paint.getShader() &&
robertphillipsccb1b572015-05-27 11:02:55 -0700179 (strokeInfo.isFillStyle() || strokeInfo.isHairlineStyle())) {
180 viewMatrix.preConcat(*prePathMatrix);
181 } else {
182 SkPath* result = pathPtr;
183
184 if (!pathIsMutable) {
185 result = tmpPath.init();
186 result->setIsVolatile(true);
187 pathIsMutable = true;
188 }
189 // should I push prePathMatrix on our MV stack temporarily, instead
190 // of applying it here? See SkDraw.cpp
191 pathPtr->transform(*prePathMatrix, result);
192 pathPtr = result;
193 }
194 }
195 // at this point we're done with prePathMatrix
196 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
197
198 GrPaint grPaint;
bsalomonf1b7a1d2015-09-28 06:26:28 -0700199 if (!SkPaintToGrPaint(context, paint, viewMatrix, &grPaint)) {
robertphillipsccb1b572015-05-27 11:02:55 -0700200 return;
201 }
202
halcanary96fcdcc2015-08-27 07:41:13 -0700203 const SkRect* cullRect = nullptr; // TODO: what is our bounds?
robertphillipsccb1b572015-05-27 11:02:55 -0700204 if (!strokeInfo.isDashed() && pathEffect && pathEffect->filterPath(effectPath.init(), *pathPtr,
205 &strokeInfo, cullRect)) {
206 pathPtr = effectPath.get();
207 pathIsMutable = true;
208 }
209
210 if (paint.getMaskFilter()) {
211 if (!strokeInfo.isHairlineStyle()) {
212 SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init();
213 if (strokeInfo.isDashed()) {
214 if (pathEffect->filterPath(strokedPath, *pathPtr, &strokeInfo, cullRect)) {
215 pathPtr = strokedPath;
216 pathIsMutable = true;
217 }
218 strokeInfo.removeDash();
219 }
220 if (strokeInfo.applyToPath(strokedPath, *pathPtr)) {
221 pathPtr = strokedPath;
222 pathIsMutable = true;
223 strokeInfo.setFillStyle();
224 }
225 }
226
227 // avoid possibly allocating a new path in transform if we can
228 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath.init();
229 if (!pathIsMutable) {
230 devPathPtr->setIsVolatile(true);
231 }
232
233 // transform the path into device space
234 pathPtr->transform(viewMatrix, devPathPtr);
235
236 SkRect maskRect;
robertphillips30c4cae2015-09-15 10:20:55 -0700237 if (paint.getMaskFilter()->canFilterMaskGPU(SkRRect::MakeRect(devPathPtr->getBounds()),
robertphillipsccb1b572015-05-27 11:02:55 -0700238 clipBounds,
239 viewMatrix,
240 &maskRect)) {
241 SkIRect finalIRect;
242 maskRect.roundOut(&finalIRect);
243 if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
244 // clipped out
245 return;
246 }
247
robertphillipsff0ca5e2015-07-22 11:54:44 -0700248 if (paint.getMaskFilter()->directFilterMaskGPU(context->textureProvider(),
249 drawContext,
robertphillipsccb1b572015-05-27 11:02:55 -0700250 renderTarget,
251 &grPaint,
252 clip,
253 viewMatrix,
254 strokeInfo,
255 *devPathPtr)) {
256 // the mask filter was able to draw itself directly, so there's nothing
257 // left to do.
258 return;
259 }
260
robertphillipsccb1b572015-05-27 11:02:55 -0700261 SkAutoTUnref<GrTexture> mask(create_mask_GPU(context,
robertphillips3833daa2015-09-14 11:18:13 -0700262 &maskRect,
robertphillipsccb1b572015-05-27 11:02:55 -0700263 *devPathPtr,
264 strokeInfo,
265 grPaint.isAntiAlias(),
vbuzinovdded6962015-06-12 08:59:45 -0700266 renderTarget->numColorSamples()));
robertphillipsccb1b572015-05-27 11:02:55 -0700267 if (mask) {
268 GrTexture* filtered;
269
270 if (paint.getMaskFilter()->filterMaskGPU(mask, viewMatrix, maskRect,
271 &filtered, true)) {
272 // filterMaskGPU gives us ownership of a ref to the result
273 SkAutoTUnref<GrTexture> atu(filtered);
274 if (draw_mask(drawContext,
275 renderTarget,
276 clip,
277 viewMatrix,
278 maskRect,
279 &grPaint,
280 filtered)) {
281 // This path is completely drawn
282 return;
283 }
284 }
285 }
286 }
287
288 // draw the mask on the CPU - this is a fallthrough path in case the
289 // GPU path fails
290 SkPaint::Style style = strokeInfo.isHairlineStyle() ? SkPaint::kStroke_Style :
291 SkPaint::kFill_Style;
292 draw_with_mask_filter(drawContext, context->textureProvider(), renderTarget,
293 clip, viewMatrix, *devPathPtr,
294 paint.getMaskFilter(), clipBounds, &grPaint, style);
295 return;
296 }
297
298 drawContext->drawPath(renderTarget, clip, grPaint, viewMatrix, *pathPtr, strokeInfo);
299}
300