blob: 3d4c00d841428aa4779b126c0536b9fd8a24db57 [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,
54 SkMaskFilter* filter,
55 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
149void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
150 GrDrawContext* drawContext,
151 GrRenderTarget* renderTarget,
152 const GrClip& clip,
153 const SkPath& origSrcPath,
154 const SkPaint& paint,
155 const SkMatrix& origViewMatrix,
156 const SkMatrix* prePathMatrix,
157 const SkIRect& clipBounds,
158 bool pathIsMutable) {
159 SkASSERT(!pathIsMutable || origSrcPath.isVolatile());
160
161 GrStrokeInfo strokeInfo(paint);
162
163 // If we have a prematrix, apply it to the path, optimizing for the case
164 // where the original path can in fact be modified in place (even though
165 // its parameter type is const).
166 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
167 SkTLazy<SkPath> tmpPath;
168 SkTLazy<SkPath> effectPath;
169 SkPathEffect* pathEffect = paint.getPathEffect();
170
171 SkMatrix viewMatrix = origViewMatrix;
172
173 if (prePathMatrix) {
174 // stroking, path effects, and blurs are supposed to be applied *after* the prePathMatrix.
175 // The pre-path-matrix also should not affect shading.
halcanary96fcdcc2015-08-27 07:41:13 -0700176 if (nullptr == paint.getMaskFilter() && nullptr == pathEffect && nullptr == paint.getShader() &&
robertphillipsccb1b572015-05-27 11:02:55 -0700177 (strokeInfo.isFillStyle() || strokeInfo.isHairlineStyle())) {
178 viewMatrix.preConcat(*prePathMatrix);
179 } else {
180 SkPath* result = pathPtr;
181
182 if (!pathIsMutable) {
183 result = tmpPath.init();
184 result->setIsVolatile(true);
185 pathIsMutable = true;
186 }
187 // should I push prePathMatrix on our MV stack temporarily, instead
188 // of applying it here? See SkDraw.cpp
189 pathPtr->transform(*prePathMatrix, result);
190 pathPtr = result;
191 }
192 }
193 // at this point we're done with prePathMatrix
194 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
195
196 GrPaint grPaint;
bsalomonf1b7a1d2015-09-28 06:26:28 -0700197 if (!SkPaintToGrPaint(context, paint, viewMatrix, &grPaint)) {
robertphillipsccb1b572015-05-27 11:02:55 -0700198 return;
199 }
200
halcanary96fcdcc2015-08-27 07:41:13 -0700201 const SkRect* cullRect = nullptr; // TODO: what is our bounds?
robertphillipsccb1b572015-05-27 11:02:55 -0700202 if (!strokeInfo.isDashed() && pathEffect && pathEffect->filterPath(effectPath.init(), *pathPtr,
203 &strokeInfo, cullRect)) {
204 pathPtr = effectPath.get();
205 pathIsMutable = true;
206 }
207
208 if (paint.getMaskFilter()) {
209 if (!strokeInfo.isHairlineStyle()) {
210 SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init();
211 if (strokeInfo.isDashed()) {
212 if (pathEffect->filterPath(strokedPath, *pathPtr, &strokeInfo, cullRect)) {
213 pathPtr = strokedPath;
214 pathIsMutable = true;
215 }
216 strokeInfo.removeDash();
217 }
218 if (strokeInfo.applyToPath(strokedPath, *pathPtr)) {
219 pathPtr = strokedPath;
220 pathIsMutable = true;
221 strokeInfo.setFillStyle();
222 }
223 }
224
225 // avoid possibly allocating a new path in transform if we can
226 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath.init();
227 if (!pathIsMutable) {
228 devPathPtr->setIsVolatile(true);
229 }
230
231 // transform the path into device space
232 pathPtr->transform(viewMatrix, devPathPtr);
233
234 SkRect maskRect;
robertphillips30c4cae2015-09-15 10:20:55 -0700235 if (paint.getMaskFilter()->canFilterMaskGPU(SkRRect::MakeRect(devPathPtr->getBounds()),
robertphillipsccb1b572015-05-27 11:02:55 -0700236 clipBounds,
237 viewMatrix,
238 &maskRect)) {
239 SkIRect finalIRect;
240 maskRect.roundOut(&finalIRect);
241 if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
242 // clipped out
243 return;
244 }
245
robertphillipsff0ca5e2015-07-22 11:54:44 -0700246 if (paint.getMaskFilter()->directFilterMaskGPU(context->textureProvider(),
247 drawContext,
robertphillipsccb1b572015-05-27 11:02:55 -0700248 &grPaint,
249 clip,
250 viewMatrix,
251 strokeInfo,
252 *devPathPtr)) {
253 // the mask filter was able to draw itself directly, so there's nothing
254 // left to do.
255 return;
256 }
257
robertphillipsccb1b572015-05-27 11:02:55 -0700258 SkAutoTUnref<GrTexture> mask(create_mask_GPU(context,
robertphillips3833daa2015-09-14 11:18:13 -0700259 &maskRect,
robertphillipsccb1b572015-05-27 11:02:55 -0700260 *devPathPtr,
261 strokeInfo,
262 grPaint.isAntiAlias(),
vbuzinovdded6962015-06-12 08:59:45 -0700263 renderTarget->numColorSamples()));
robertphillipsccb1b572015-05-27 11:02:55 -0700264 if (mask) {
265 GrTexture* filtered;
266
267 if (paint.getMaskFilter()->filterMaskGPU(mask, viewMatrix, maskRect,
268 &filtered, true)) {
269 // filterMaskGPU gives us ownership of a ref to the result
270 SkAutoTUnref<GrTexture> atu(filtered);
271 if (draw_mask(drawContext,
robertphillipsccb1b572015-05-27 11:02:55 -0700272 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;
robertphillips2e1e51f2015-10-15 08:01:48 -0700288 draw_with_mask_filter(drawContext, context->textureProvider(),
robertphillipsccb1b572015-05-27 11:02:55 -0700289 clip, viewMatrix, *devPathPtr,
290 paint.getMaskFilter(), clipBounds, &grPaint, style);
291 return;
292 }
293
robertphillips2e1e51f2015-10-15 08:01:48 -0700294 drawContext->drawPath(clip, grPaint, viewMatrix, *pathPtr, strokeInfo);
robertphillipsccb1b572015-05-27 11:02:55 -0700295}
296