blob: 1e85b87dcf85b96e79731e5f6eac35250753678b [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"
bsalomon6663acf2016-05-10 09:14:17 -070013#include "GrStyle.h"
robertphillipsccb1b572015-05-27 11:02:55 -070014#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,
robertphillipsf054b172016-05-13 05:06:19 -070031 const SkIRect& maskRect,
robertphillipsccb1b572015-05-27 11:02:55 -070032 GrPaint* grp,
33 GrTexture* mask) {
34 SkMatrix matrix;
robertphillipsf054b172016-05-13 05:06:19 -070035 matrix.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
robertphillipsccb1b572015-05-27 11:02:55 -070036 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 }
robertphillipsf054b172016-05-13 05:06:19 -070045 drawContext->fillRectWithLocalMatrix(clip, *grp, SkMatrix::I(),
46 SkRect::Make(maskRect), inverse);
robertphillipsccb1b572015-05-27 11:02:55 -070047 return true;
48}
49
robertphillips0e7029e2015-11-30 05:45:06 -080050static bool sw_draw_with_mask_filter(GrDrawContext* drawContext,
51 GrTextureProvider* textureProvider,
52 const GrClip& clipData,
53 const SkMatrix& viewMatrix,
54 const SkPath& devPath,
55 const SkMaskFilter* filter,
56 const SkIRect& clipBounds,
57 GrPaint* grp,
bsalomon6663acf2016-05-10 09:14:17 -070058 SkStrokeRec::InitStyle fillOrHairline) {
robertphillipsccb1b572015-05-27 11:02:55 -070059 SkMask srcM, dstM;
robertphillipsccb1b572015-05-27 11:02:55 -070060 if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
bsalomon6663acf2016-05-10 09:14:17 -070061 SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) {
robertphillipsccb1b572015-05-27 11:02:55 -070062 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
robertphillipsf054b172016-05-13 05:06:19 -070090 return draw_mask(drawContext, clipData, viewMatrix, dstM.fBounds, grp, texture);
robertphillipsccb1b572015-05-27 11:02:55 -070091}
92
93// Create a mask of 'devPath' and place the result in 'mask'.
robertphillipsd4c741e2016-04-28 09:55:15 -070094static sk_sp<GrTexture> create_mask_GPU(GrContext* context,
robertphillipsf054b172016-05-13 05:06:19 -070095 const SkIRect& maskRect,
robertphillipsd4c741e2016-04-28 09:55:15 -070096 const SkPath& devPath,
bsalomon6663acf2016-05-10 09:14:17 -070097 SkStrokeRec::InitStyle fillOrHairline,
robertphillipsd4c741e2016-04-28 09:55:15 -070098 bool doAA,
99 int sampleCnt) {
robertphillipsd4c741e2016-04-28 09:55:15 -0700100 if (!doAA) {
101 // Don't need MSAA if mask isn't AA
102 sampleCnt = 0;
103 }
104
robertphillipsccb1b572015-05-27 11:02:55 -0700105 // We actually only need A8, but it often isn't supported as a
106 // render target so default to RGBA_8888
robertphillipsd4c741e2016-04-28 09:55:15 -0700107 GrPixelConfig config = kRGBA_8888_GrPixelConfig;
108 if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, sampleCnt > 0)) {
109 config = kAlpha_8_GrPixelConfig;
robertphillipsccb1b572015-05-27 11:02:55 -0700110 }
111
robertphillips76948d42016-05-04 12:47:41 -0700112 sk_sp<GrDrawContext> drawContext(context->newDrawContext(SkBackingFit::kApprox,
robertphillipsf054b172016-05-13 05:06:19 -0700113 maskRect.width(),
114 maskRect.height(),
robertphillipsd4c741e2016-04-28 09:55:15 -0700115 config,
116 sampleCnt));
robertphillipsccb1b572015-05-27 11:02:55 -0700117 if (!drawContext) {
halcanary96fcdcc2015-08-27 07:41:13 -0700118 return nullptr;
robertphillipsccb1b572015-05-27 11:02:55 -0700119 }
120
robertphillips2e1e51f2015-10-15 08:01:48 -0700121 drawContext->clear(nullptr, 0x0, true);
robertphillipsccb1b572015-05-27 11:02:55 -0700122
123 GrPaint tempPaint;
124 tempPaint.setAntiAlias(doAA);
125 tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
126
127 // setup new clip
cdalton846c0512016-05-13 10:25:00 -0700128 const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height());
129 GrFixedClip clip(clipRect);
robertphillipsccb1b572015-05-27 11:02:55 -0700130
robertphillips3833daa2015-09-14 11:18:13 -0700131 // Draw the mask into maskTexture with the path's integerized top-left at
132 // the origin using tempPaint.
robertphillipsccb1b572015-05-27 11:02:55 -0700133 SkMatrix translate;
robertphillipsf054b172016-05-13 05:06:19 -0700134 translate.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
bsalomon6663acf2016-05-10 09:14:17 -0700135 drawContext->drawPath(clip, tempPaint, translate, devPath, GrStyle(fillOrHairline));
robertphillipsd4c741e2016-04-28 09:55:15 -0700136 return drawContext->asTexture();;
robertphillipsccb1b572015-05-27 11:02:55 -0700137}
138
bsalomonc55271f2015-11-09 11:55:57 -0800139static void draw_path_with_mask_filter(GrContext* context,
140 GrDrawContext* drawContext,
bsalomonc55271f2015-11-09 11:55:57 -0800141 const GrClip& clip,
142 GrPaint* paint,
143 const SkMatrix& viewMatrix,
144 const SkMaskFilter* maskFilter,
bsalomon6663acf2016-05-10 09:14:17 -0700145 const GrStyle& style,
146 const SkPath* path,
bsalomonc55271f2015-11-09 11:55:57 -0800147 bool pathIsMutable) {
148 SkASSERT(maskFilter);
149
150 SkIRect clipBounds;
robertphillips7bceedc2015-12-01 12:51:26 -0800151 clip.getConservativeBounds(drawContext->width(), drawContext->height(), &clipBounds);
bsalomonc55271f2015-11-09 11:55:57 -0800152 SkTLazy<SkPath> tmpPath;
bsalomon6663acf2016-05-10 09:14:17 -0700153 SkStrokeRec::InitStyle fillOrHairline;
bsalomonc55271f2015-11-09 11:55:57 -0800154
bsalomon6663acf2016-05-10 09:14:17 -0700155 // We just fully apply the style here.
156 if (style.applies()) {
157 if (!style.applyToPath(tmpPath.init(), &fillOrHairline, *path,
158 GrStyle::MatrixToScaleFactor(viewMatrix))) {
159 return;
160 }
161 pathIsMutable = true;
162 path = tmpPath.get();
163 } else if (style.isSimpleHairline()) {
164 fillOrHairline = SkStrokeRec::kHairline_InitStyle;
bsalomon055e1922016-05-06 07:22:58 -0700165 } else {
bsalomon6663acf2016-05-10 09:14:17 -0700166 SkASSERT(style.isSimpleFill());
167 fillOrHairline = SkStrokeRec::kFill_InitStyle;
bsalomonc55271f2015-11-09 11:55:57 -0800168 }
169
170 // transform the path into device space
bsalomon6663acf2016-05-10 09:14:17 -0700171 if (!viewMatrix.isIdentity()) {
172 SkPath* result;
173 if (pathIsMutable) {
174 result = const_cast<SkPath*>(path);
175 } else {
176 if (!tmpPath.isValid()) {
177 tmpPath.init();
178 }
179 result = tmpPath.get();
180 }
181 path->transform(viewMatrix, result);
182 path = result;
183 result->setIsVolatile(true);
184 pathIsMutable = true;
185 }
bsalomonc55271f2015-11-09 11:55:57 -0800186
187 SkRect maskRect;
bsalomon6663acf2016-05-10 09:14:17 -0700188 if (maskFilter->canFilterMaskGPU(SkRRect::MakeRect(path->getBounds()),
bsalomonc55271f2015-11-09 11:55:57 -0800189 clipBounds,
190 viewMatrix,
191 &maskRect)) {
robertphillipsf054b172016-05-13 05:06:19 -0700192 // This mask will ultimately be drawn as a non-AA rect (see draw_mask).
193 // Non-AA rects have a bad habit of snapping arbitrarily. Integerize here
194 // so the mask draws in a reproducible manner.
bsalomonc55271f2015-11-09 11:55:57 -0800195 SkIRect finalIRect;
196 maskRect.roundOut(&finalIRect);
197 if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
198 // clipped out
199 return;
200 }
201
202 if (maskFilter->directFilterMaskGPU(context->textureProvider(),
203 drawContext,
204 paint,
205 clip,
206 viewMatrix,
bsalomon6663acf2016-05-10 09:14:17 -0700207 SkStrokeRec(fillOrHairline),
208 *path)) {
bsalomonc55271f2015-11-09 11:55:57 -0800209 // the mask filter was able to draw itself directly, so there's nothing
210 // left to do.
211 return;
212 }
213
robertphillipsd4c741e2016-04-28 09:55:15 -0700214 sk_sp<GrTexture> mask(create_mask_GPU(context,
robertphillipsf054b172016-05-13 05:06:19 -0700215 finalIRect,
bsalomon6663acf2016-05-10 09:14:17 -0700216 *path,
217 fillOrHairline,
robertphillipsd4c741e2016-04-28 09:55:15 -0700218 paint->isAntiAlias(),
219 drawContext->numColorSamples()));
bsalomonc55271f2015-11-09 11:55:57 -0800220 if (mask) {
221 GrTexture* filtered;
222
robertphillipsf054b172016-05-13 05:06:19 -0700223 if (maskFilter->filterMaskGPU(mask.get(), viewMatrix, finalIRect, &filtered, true)) {
bsalomonc55271f2015-11-09 11:55:57 -0800224 // filterMaskGPU gives us ownership of a ref to the result
225 SkAutoTUnref<GrTexture> atu(filtered);
robertphillipsf054b172016-05-13 05:06:19 -0700226 if (draw_mask(drawContext, clip, viewMatrix, finalIRect, paint, filtered)) {
bsalomonc55271f2015-11-09 11:55:57 -0800227 // This path is completely drawn
228 return;
229 }
230 }
231 }
232 }
233
robertphillips0e7029e2015-11-30 05:45:06 -0800234 sw_draw_with_mask_filter(drawContext, context->textureProvider(),
bsalomon6663acf2016-05-10 09:14:17 -0700235 clip, viewMatrix, *path,
236 maskFilter, clipBounds, paint, fillOrHairline);
237}
238
239void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
240 GrDrawContext* drawContext,
241 const GrClip& clip,
242 const SkPath& path,
243 GrPaint* paint,
244 const SkMatrix& viewMatrix,
245 const SkMaskFilter* mf,
246 const GrStyle& style,
247 bool pathIsMutable) {
248 draw_path_with_mask_filter(context, drawContext, clip, paint, viewMatrix, mf,
249 style, &path, pathIsMutable);
bsalomonc55271f2015-11-09 11:55:57 -0800250}
251
252void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
253 GrDrawContext* drawContext,
bsalomonc55271f2015-11-09 11:55:57 -0800254 const GrClip& clip,
255 const SkPath& origPath,
robertphillipsccb1b572015-05-27 11:02:55 -0700256 const SkPaint& paint,
257 const SkMatrix& origViewMatrix,
258 const SkMatrix* prePathMatrix,
259 const SkIRect& clipBounds,
260 bool pathIsMutable) {
bsalomon6663acf2016-05-10 09:14:17 -0700261 SkASSERT(!pathIsMutable || origPath.isVolatile());
robertphillipsccb1b572015-05-27 11:02:55 -0700262
bsalomon6663acf2016-05-10 09:14:17 -0700263 GrStyle style(paint);
robertphillipsccb1b572015-05-27 11:02:55 -0700264 // If we have a prematrix, apply it to the path, optimizing for the case
265 // where the original path can in fact be modified in place (even though
266 // its parameter type is const).
bsalomon6663acf2016-05-10 09:14:17 -0700267
268 const SkPath* path = &origPath;
robertphillipsccb1b572015-05-27 11:02:55 -0700269 SkTLazy<SkPath> tmpPath;
robertphillipsccb1b572015-05-27 11:02:55 -0700270
271 SkMatrix viewMatrix = origViewMatrix;
272
273 if (prePathMatrix) {
bsalomon6663acf2016-05-10 09:14:17 -0700274 // Styling, blurs, and shading are supposed to be applied *after* the prePathMatrix.
275 if (!paint.getMaskFilter() && !paint.getShader() && !style.applies()) {
robertphillipsccb1b572015-05-27 11:02:55 -0700276 viewMatrix.preConcat(*prePathMatrix);
277 } else {
bsalomon6663acf2016-05-10 09:14:17 -0700278 SkPath* result = pathIsMutable ? const_cast<SkPath*>(path) : tmpPath.init();
279 pathIsMutable = true;
280 path->transform(*prePathMatrix, result);
281 path = result;
282 result->setIsVolatile(true);
robertphillipsccb1b572015-05-27 11:02:55 -0700283 }
284 }
285 // at this point we're done with prePathMatrix
286 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
287
288 GrPaint grPaint;
brianosmanb461d342016-04-13 13:10:14 -0700289 if (!SkPaintToGrPaint(context, paint, viewMatrix, drawContext->isGammaCorrect(),
brianosman898235c2016-04-06 07:38:23 -0700290 &grPaint)) {
robertphillipsccb1b572015-05-27 11:02:55 -0700291 return;
292 }
293
robertphillipsccb1b572015-05-27 11:02:55 -0700294 if (paint.getMaskFilter()) {
robertphillips7bceedc2015-12-01 12:51:26 -0800295 draw_path_with_mask_filter(context, drawContext, clip, &grPaint, viewMatrix,
bsalomon6663acf2016-05-10 09:14:17 -0700296 paint.getMaskFilter(), style,
297 path, pathIsMutable);
bsalomonc55271f2015-11-09 11:55:57 -0800298 } else {
bsalomon6663acf2016-05-10 09:14:17 -0700299 drawContext->drawPath(clip, grPaint, viewMatrix, *path, style);
robertphillipsccb1b572015-05-27 11:02:55 -0700300 }
robertphillipsccb1b572015-05-27 11:02:55 -0700301}