blob: c6cff60b342377b1bd7c82003ee7e048086ac823 [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,
robertphillipsccb1b572015-05-27 11:02:55 -070029 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
bsalomon4a339522015-10-06 08:40:50 -070038 grp->addCoverageFragmentProcessor(GrSimpleTextureEffect::Create(mask, matrix,
39 kDevice_GrCoordSet))->unref();
robertphillipsccb1b572015-05-27 11:02:55 -070040
41 SkMatrix inverse;
42 if (!viewMatrix.invert(&inverse)) {
43 return false;
44 }
bsalomona2e69fc2015-11-05 10:41:43 -080045 drawContext->fillRectWithLocalMatrix(clip, *grp, SkMatrix::I(), maskRect, inverse);
robertphillipsccb1b572015-05-27 11:02:55 -070046 return true;
47}
48
49static bool draw_with_mask_filter(GrDrawContext* drawContext,
50 GrTextureProvider* textureProvider,
robertphillipsccb1b572015-05-27 11:02:55 -070051 const GrClip& clipData,
52 const SkMatrix& viewMatrix,
53 const SkPath& devPath,
bsalomonc55271f2015-11-09 11:55:57 -080054 const SkMaskFilter* filter,
robertphillipsccb1b572015-05-27 11:02:55 -070055 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
halcanary96fcdcc2015-08-27 07:41:13 -070066 if (!filter->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
robertphillipsccb1b572015-05-27 11:02:55 -070067 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
bsalomoneae62002015-07-31 13:59:30 -070083 SkAutoTUnref<GrTexture> texture(textureProvider->createApproxTexture(desc));
robertphillipsccb1b572015-05-27 11:02:55 -070084 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
robertphillips2e1e51f2015-10-15 08:01:48 -070092 return draw_mask(drawContext, clipData, viewMatrix, maskRect, grp, texture);
robertphillipsccb1b572015-05-27 11:02:55 -070093}
94
95// Create a mask of 'devPath' and place the result in 'mask'.
96static GrTexture* create_mask_GPU(GrContext* context,
robertphillips3833daa2015-09-14 11:18:13 -070097 SkRect* maskRect,
robertphillipsccb1b572015-05-27 11:02:55 -070098 const SkPath& devPath,
99 const GrStrokeInfo& strokeInfo,
100 bool doAA,
101 int sampleCnt) {
robertphillips3833daa2015-09-14 11:18:13 -0700102 // 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
robertphillipsccb1b572015-05-27 11:02:55 -0700107 GrSurfaceDesc desc;
108 desc.fFlags = kRenderTarget_GrSurfaceFlag;
robertphillips3833daa2015-09-14 11:18:13 -0700109 desc.fWidth = SkScalarCeilToInt(maskRect->width());
110 desc.fHeight = SkScalarCeilToInt(maskRect->height());
robertphillipsccb1b572015-05-27 11:02:55 -0700111 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
bsalomon76228632015-05-29 08:02:10 -0700116 if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)) {
robertphillipsccb1b572015-05-27 11:02:55 -0700117 desc.fConfig = kAlpha_8_GrPixelConfig;
118 }
119
bsalomoneae62002015-07-31 13:59:30 -0700120 GrTexture* mask = context->textureProvider()->createApproxTexture(desc);
halcanary96fcdcc2015-08-27 07:41:13 -0700121 if (nullptr == mask) {
122 return nullptr;
robertphillipsccb1b572015-05-27 11:02:55 -0700123 }
124
robertphillips3833daa2015-09-14 11:18:13 -0700125 SkRect clipRect = SkRect::MakeWH(maskRect->width(), maskRect->height());
robertphillipsccb1b572015-05-27 11:02:55 -0700126
robertphillips2e1e51f2015-10-15 08:01:48 -0700127 SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(mask->asRenderTarget()));
robertphillipsccb1b572015-05-27 11:02:55 -0700128 if (!drawContext) {
halcanary96fcdcc2015-08-27 07:41:13 -0700129 return nullptr;
robertphillipsccb1b572015-05-27 11:02:55 -0700130 }
131
robertphillips2e1e51f2015-10-15 08:01:48 -0700132 drawContext->clear(nullptr, 0x0, true);
robertphillipsccb1b572015-05-27 11:02:55 -0700133
134 GrPaint tempPaint;
135 tempPaint.setAntiAlias(doAA);
136 tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
137
138 // setup new clip
139 GrClip clip(clipRect);
140
robertphillips3833daa2015-09-14 11:18:13 -0700141 // Draw the mask into maskTexture with the path's integerized top-left at
142 // the origin using tempPaint.
robertphillipsccb1b572015-05-27 11:02:55 -0700143 SkMatrix translate;
robertphillips3833daa2015-09-14 11:18:13 -0700144 translate.setTranslate(-maskRect->fLeft, -maskRect->fTop);
robertphillips2e1e51f2015-10-15 08:01:48 -0700145 drawContext->drawPath(clip, tempPaint, translate, devPath, strokeInfo);
robertphillipsccb1b572015-05-27 11:02:55 -0700146 return mask;
147}
148
bsalomonc55271f2015-11-09 11:55:57 -0800149static void draw_path_with_mask_filter(GrContext* context,
150 GrDrawContext* drawContext,
151 GrRenderTarget* renderTarget,
152 const GrClip& clip,
153 GrPaint* paint,
154 const SkMatrix& viewMatrix,
155 const SkMaskFilter* maskFilter,
156 const SkPathEffect* pathEffect,
157 const GrStrokeInfo& origStrokeInfo,
158 SkPath* pathPtr,
159 bool pathIsMutable) {
160 SkASSERT(maskFilter);
161
162 SkIRect clipBounds;
163 clip.getConservativeBounds(renderTarget, &clipBounds);
164 SkTLazy<SkPath> tmpPath;
165 GrStrokeInfo strokeInfo(origStrokeInfo);
166
167 static const SkRect* cullRect = nullptr; // TODO: what is our bounds?
168
169 if (!strokeInfo.isDashed() && pathEffect && pathEffect->filterPath(tmpPath.init(), *pathPtr,
170 &strokeInfo, cullRect)) {
171 pathPtr = tmpPath.get();
172 pathPtr->setIsVolatile(true);
173 pathIsMutable = true;
174 }
175 if (!strokeInfo.isHairlineStyle()) {
176 SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init();
177 if (strokeInfo.isDashed()) {
178 if (pathEffect->filterPath(strokedPath, *pathPtr, &strokeInfo, cullRect)) {
179 pathPtr = strokedPath;
180 pathPtr->setIsVolatile(true);
181 pathIsMutable = true;
182 }
183 strokeInfo.removeDash();
184 }
185 if (strokeInfo.applyToPath(strokedPath, *pathPtr)) {
186 pathPtr = strokedPath;
187 pathPtr->setIsVolatile(true);
188 pathIsMutable = true;
189 strokeInfo.setFillStyle();
190 }
191 }
192
193 // avoid possibly allocating a new path in transform if we can
194 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath.init();
195 if (!pathIsMutable) {
196 devPathPtr->setIsVolatile(true);
197 }
198
199 // transform the path into device space
200 pathPtr->transform(viewMatrix, devPathPtr);
201
202 SkRect maskRect;
203 if (maskFilter->canFilterMaskGPU(SkRRect::MakeRect(devPathPtr->getBounds()),
204 clipBounds,
205 viewMatrix,
206 &maskRect)) {
207 SkIRect finalIRect;
208 maskRect.roundOut(&finalIRect);
209 if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
210 // clipped out
211 return;
212 }
213
214 if (maskFilter->directFilterMaskGPU(context->textureProvider(),
215 drawContext,
216 paint,
217 clip,
218 viewMatrix,
219 strokeInfo,
220 *devPathPtr)) {
221 // the mask filter was able to draw itself directly, so there's nothing
222 // left to do.
223 return;
224 }
225
226 SkAutoTUnref<GrTexture> mask(create_mask_GPU(context,
227 &maskRect,
228 *devPathPtr,
229 strokeInfo,
230 paint->isAntiAlias(),
231 renderTarget->numColorSamples()));
232 if (mask) {
233 GrTexture* filtered;
234
235 if (maskFilter->filterMaskGPU(mask, viewMatrix, maskRect, &filtered, true)) {
236 // filterMaskGPU gives us ownership of a ref to the result
237 SkAutoTUnref<GrTexture> atu(filtered);
238 if (draw_mask(drawContext, clip, viewMatrix, maskRect, paint, filtered)) {
239 // This path is completely drawn
240 return;
241 }
242 }
243 }
244 }
245
246 // draw the mask on the CPU - this is a fallthrough path in case the
247 // GPU path fails
248 SkPaint::Style style = strokeInfo.isHairlineStyle() ? SkPaint::kStroke_Style :
249 SkPaint::kFill_Style;
250 draw_with_mask_filter(drawContext, context->textureProvider(),
251 clip, viewMatrix, *devPathPtr,
252 maskFilter, clipBounds, paint, style);
253}
254
255void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
256 GrDrawContext* drawContext,
257 GrRenderTarget* rt,
258 const GrClip& clip,
259 const SkPath& origPath,
260 GrPaint* paint,
261 const SkMatrix& viewMatrix,
262 const SkMaskFilter* mf,
263 const SkPathEffect* pe,
264 const GrStrokeInfo& strokeInfo) {
265 SkPath* path = const_cast<SkPath*>(&origPath);
266 draw_path_with_mask_filter(context, drawContext, rt, clip, paint, viewMatrix, mf, pe,
267 strokeInfo, path, false);
268}
269
robertphillipsccb1b572015-05-27 11:02:55 -0700270void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
271 GrDrawContext* drawContext,
272 GrRenderTarget* renderTarget,
273 const GrClip& clip,
274 const SkPath& origSrcPath,
275 const SkPaint& paint,
276 const SkMatrix& origViewMatrix,
277 const SkMatrix* prePathMatrix,
278 const SkIRect& clipBounds,
279 bool pathIsMutable) {
280 SkASSERT(!pathIsMutable || origSrcPath.isVolatile());
281
282 GrStrokeInfo strokeInfo(paint);
283
284 // If we have a prematrix, apply it to the path, optimizing for the case
285 // where the original path can in fact be modified in place (even though
286 // its parameter type is const).
287 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
288 SkTLazy<SkPath> tmpPath;
289 SkTLazy<SkPath> effectPath;
290 SkPathEffect* pathEffect = paint.getPathEffect();
291
292 SkMatrix viewMatrix = origViewMatrix;
293
294 if (prePathMatrix) {
295 // stroking, path effects, and blurs are supposed to be applied *after* the prePathMatrix.
296 // The pre-path-matrix also should not affect shading.
bsalomonc55271f2015-11-09 11:55:57 -0800297 if (!paint.getMaskFilter() && !pathEffect && !paint.getShader() &&
robertphillipsccb1b572015-05-27 11:02:55 -0700298 (strokeInfo.isFillStyle() || strokeInfo.isHairlineStyle())) {
299 viewMatrix.preConcat(*prePathMatrix);
300 } else {
301 SkPath* result = pathPtr;
302
303 if (!pathIsMutable) {
304 result = tmpPath.init();
305 result->setIsVolatile(true);
306 pathIsMutable = true;
307 }
308 // should I push prePathMatrix on our MV stack temporarily, instead
309 // of applying it here? See SkDraw.cpp
310 pathPtr->transform(*prePathMatrix, result);
311 pathPtr = result;
312 }
313 }
314 // at this point we're done with prePathMatrix
315 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
316
317 GrPaint grPaint;
bsalomonf1b7a1d2015-09-28 06:26:28 -0700318 if (!SkPaintToGrPaint(context, paint, viewMatrix, &grPaint)) {
robertphillipsccb1b572015-05-27 11:02:55 -0700319 return;
320 }
321
robertphillipsccb1b572015-05-27 11:02:55 -0700322 if (paint.getMaskFilter()) {
bsalomonc55271f2015-11-09 11:55:57 -0800323 draw_path_with_mask_filter(context, drawContext, renderTarget, clip, &grPaint, viewMatrix,
324 paint.getMaskFilter(), paint.getPathEffect(), strokeInfo,
325 pathPtr, pathIsMutable);
326 } else {
327 SkTLazy<SkPath> tmpPath2;
328 if (!strokeInfo.isDashed() && pathEffect &&
329 pathEffect->filterPath(tmpPath2.init(), *pathPtr, &strokeInfo, nullptr)) {
330 pathPtr = tmpPath2.get();
331 pathPtr->setIsVolatile(true);
332 pathIsMutable = true;
robertphillipsccb1b572015-05-27 11:02:55 -0700333 }
bsalomonc55271f2015-11-09 11:55:57 -0800334 drawContext->drawPath(clip, grPaint, viewMatrix, *pathPtr, strokeInfo);
robertphillipsccb1b572015-05-27 11:02:55 -0700335 }
robertphillipsccb1b572015-05-27 11:02:55 -0700336}
337