blob: 8a8ab789bfa9f4e80a6c9a1e8b9018b07db9265f [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
robertphillips0e7029e2015-11-30 05:45:06 -080049static 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) {
robertphillipsccb1b572015-05-27 11:02:55 -070058 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,
bsalomonc55271f2015-11-09 11:55:57 -0800151 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;
robertphillips7bceedc2015-12-01 12:51:26 -0800162 clip.getConservativeBounds(drawContext->width(), drawContext->height(), &clipBounds);
bsalomonc55271f2015-11-09 11:55:57 -0800163 SkTLazy<SkPath> tmpPath;
164 GrStrokeInfo strokeInfo(origStrokeInfo);
165
166 static const SkRect* cullRect = nullptr; // TODO: what is our bounds?
167
robertphillips0e7029e2015-11-30 05:45:06 -0800168 SkASSERT(strokeInfo.isDashed() || !pathEffect);
169
bsalomonc55271f2015-11-09 11:55:57 -0800170 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)) {
robertphillips0e7029e2015-11-30 05:45:06 -0800181 // Apply the stroke to the path if there is one
bsalomonc55271f2015-11-09 11:55:57 -0800182 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(),
robertphillips7bceedc2015-12-01 12:51:26 -0800227 drawContext->numColorSamples()));
bsalomonc55271f2015-11-09 11:55:57 -0800228 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;
robertphillips0e7029e2015-11-30 05:45:06 -0800246 sw_draw_with_mask_filter(drawContext, context->textureProvider(),
247 clip, viewMatrix, *devPathPtr,
248 maskFilter, clipBounds, paint, style);
bsalomonc55271f2015-11-09 11:55:57 -0800249}
250
251void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
252 GrDrawContext* drawContext,
bsalomonc55271f2015-11-09 11:55:57 -0800253 const GrClip& clip,
254 const SkPath& origPath,
255 GrPaint* paint,
256 const SkMatrix& viewMatrix,
257 const SkMaskFilter* mf,
robertphillips0e7029e2015-11-30 05:45:06 -0800258 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
robertphillips7bceedc2015-12-01 12:51:26 -0800274 draw_path_with_mask_filter(context, drawContext, clip, paint, viewMatrix, mf, pathEffect,
robertphillips0e7029e2015-11-30 05:45:06 -0800275 strokeInfo, pathPtr, pathIsMutable);
bsalomonc55271f2015-11-09 11:55:57 -0800276}
277
robertphillipsccb1b572015-05-27 11:02:55 -0700278void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
279 GrDrawContext* drawContext,
robertphillipsccb1b572015-05-27 11:02:55 -0700280 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);
caryclark1a7eb262016-01-21 07:07:02 -0800290 strokeInfo.setResScale(SkDraw::ComputeResScaleForStroking(origViewMatrix));
robertphillipsccb1b572015-05-27 11:02:55 -0700291
292 // If we have a prematrix, apply it to the path, optimizing for the case
293 // where the original path can in fact be modified in place (even though
294 // its parameter type is const).
295 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
296 SkTLazy<SkPath> tmpPath;
297 SkTLazy<SkPath> effectPath;
298 SkPathEffect* pathEffect = paint.getPathEffect();
299
300 SkMatrix viewMatrix = origViewMatrix;
301
302 if (prePathMatrix) {
303 // stroking, path effects, and blurs are supposed to be applied *after* the prePathMatrix.
304 // The pre-path-matrix also should not affect shading.
bsalomonc55271f2015-11-09 11:55:57 -0800305 if (!paint.getMaskFilter() && !pathEffect && !paint.getShader() &&
robertphillipsccb1b572015-05-27 11:02:55 -0700306 (strokeInfo.isFillStyle() || strokeInfo.isHairlineStyle())) {
307 viewMatrix.preConcat(*prePathMatrix);
308 } else {
309 SkPath* result = pathPtr;
310
311 if (!pathIsMutable) {
312 result = tmpPath.init();
313 result->setIsVolatile(true);
314 pathIsMutable = true;
315 }
316 // should I push prePathMatrix on our MV stack temporarily, instead
317 // of applying it here? See SkDraw.cpp
318 pathPtr->transform(*prePathMatrix, result);
319 pathPtr = result;
320 }
321 }
322 // at this point we're done with prePathMatrix
323 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
324
robertphillips0e7029e2015-11-30 05:45:06 -0800325 SkTLazy<SkPath> tmpPath2;
326
327 if (!strokeInfo.isDashed() && pathEffect &&
328 pathEffect->filterPath(tmpPath2.init(), *pathPtr, &strokeInfo, nullptr)) {
329 pathPtr = tmpPath2.get();
330 pathPtr->setIsVolatile(true);
331 pathIsMutable = true;
332 pathEffect = nullptr;
333 }
334
robertphillipsccb1b572015-05-27 11:02:55 -0700335 GrPaint grPaint;
bsalomonf1b7a1d2015-09-28 06:26:28 -0700336 if (!SkPaintToGrPaint(context, paint, viewMatrix, &grPaint)) {
robertphillipsccb1b572015-05-27 11:02:55 -0700337 return;
338 }
339
robertphillipsccb1b572015-05-27 11:02:55 -0700340 if (paint.getMaskFilter()) {
robertphillips7bceedc2015-12-01 12:51:26 -0800341 draw_path_with_mask_filter(context, drawContext, clip, &grPaint, viewMatrix,
robertphillips0e7029e2015-11-30 05:45:06 -0800342 paint.getMaskFilter(), pathEffect, strokeInfo,
bsalomonc55271f2015-11-09 11:55:57 -0800343 pathPtr, pathIsMutable);
344 } else {
bsalomonc55271f2015-11-09 11:55:57 -0800345 drawContext->drawPath(clip, grPaint, viewMatrix, *pathPtr, strokeInfo);
robertphillipsccb1b572015-05-27 11:02:55 -0700346 }
robertphillipsccb1b572015-05-27 11:02:55 -0700347}
348