blob: 2b7540da0abc6d40829aa3ee7890995022527d3e [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"
csmartdalton02fa32c2016-08-19 13:29:27 -070012#include "GrFixedClip.h"
robertphillipsccb1b572015-05-27 11:02:55 -070013#include "effects/GrSimpleTextureEffect.h"
bsalomon6663acf2016-05-10 09:14:17 -070014#include "GrStyle.h"
robertphillipsccb1b572015-05-27 11:02:55 -070015#include "GrTexture.h"
16#include "GrTextureProvider.h"
17#include "SkDraw.h"
bsalomonf1b7a1d2015-09-28 06:26:28 -070018#include "SkGrPriv.h"
robertphillipsccb1b572015-05-27 11:02:55 -070019#include "SkMaskFilter.h"
20#include "SkPaint.h"
csmartdaltonc6f411e2016-08-05 22:32:12 -070021#include "SkTLazy.h"
robertphillipsccb1b572015-05-27 11:02:55 -070022
23static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& rect) {
24 return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBounds, rect);
25}
26
27// Draw a mask using the supplied paint. Since the coverage/geometry
28// is already burnt into the mask this boils down to a rect draw.
29// Return true if the mask was successfully drawn.
30static bool draw_mask(GrDrawContext* drawContext,
robertphillipsccb1b572015-05-27 11:02:55 -070031 const GrClip& clip,
32 const SkMatrix& viewMatrix,
robertphillipsf054b172016-05-13 05:06:19 -070033 const SkIRect& maskRect,
robertphillipsccb1b572015-05-27 11:02:55 -070034 GrPaint* grp,
35 GrTexture* mask) {
36 SkMatrix matrix;
robertphillipsf054b172016-05-13 05:06:19 -070037 matrix.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
robertphillipsccb1b572015-05-27 11:02:55 -070038 matrix.postIDiv(mask->width(), mask->height());
39
brianosman54f30c12016-07-18 10:53:52 -070040 grp->addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(mask, nullptr, matrix,
bungeman06ca8ec2016-06-09 08:01:03 -070041 kDevice_GrCoordSet));
robertphillipsccb1b572015-05-27 11:02:55 -070042
43 SkMatrix inverse;
44 if (!viewMatrix.invert(&inverse)) {
45 return false;
46 }
robertphillipsf054b172016-05-13 05:06:19 -070047 drawContext->fillRectWithLocalMatrix(clip, *grp, SkMatrix::I(),
48 SkRect::Make(maskRect), inverse);
robertphillipsccb1b572015-05-27 11:02:55 -070049 return true;
50}
51
robertphillips0e7029e2015-11-30 05:45:06 -080052static bool sw_draw_with_mask_filter(GrDrawContext* drawContext,
53 GrTextureProvider* textureProvider,
54 const GrClip& clipData,
55 const SkMatrix& viewMatrix,
56 const SkPath& devPath,
57 const SkMaskFilter* filter,
58 const SkIRect& clipBounds,
59 GrPaint* grp,
bsalomon6663acf2016-05-10 09:14:17 -070060 SkStrokeRec::InitStyle fillOrHairline) {
robertphillipsccb1b572015-05-27 11:02:55 -070061 SkMask srcM, dstM;
robertphillipsccb1b572015-05-27 11:02:55 -070062 if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
bsalomon6663acf2016-05-10 09:14:17 -070063 SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) {
robertphillipsccb1b572015-05-27 11:02:55 -070064 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
robertphillipsf054b172016-05-13 05:06:19 -070092 return draw_mask(drawContext, clipData, viewMatrix, dstM.fBounds, grp, texture);
robertphillipsccb1b572015-05-27 11:02:55 -070093}
94
95// Create a mask of 'devPath' and place the result in 'mask'.
robertphillipsd4c741e2016-04-28 09:55:15 -070096static sk_sp<GrTexture> create_mask_GPU(GrContext* context,
robertphillipsf054b172016-05-13 05:06:19 -070097 const SkIRect& maskRect,
robertphillipsd4c741e2016-04-28 09:55:15 -070098 const SkPath& devPath,
bsalomon6663acf2016-05-10 09:14:17 -070099 SkStrokeRec::InitStyle fillOrHairline,
robertphillipsd4c741e2016-04-28 09:55:15 -0700100 bool doAA,
101 int sampleCnt) {
robertphillipsd4c741e2016-04-28 09:55:15 -0700102 if (!doAA) {
103 // Don't need MSAA if mask isn't AA
104 sampleCnt = 0;
105 }
106
robertphillipsccb1b572015-05-27 11:02:55 -0700107 // We actually only need A8, but it often isn't supported as a
108 // render target so default to RGBA_8888
robertphillipsd4c741e2016-04-28 09:55:15 -0700109 GrPixelConfig config = kRGBA_8888_GrPixelConfig;
110 if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, sampleCnt > 0)) {
111 config = kAlpha_8_GrPixelConfig;
robertphillipsccb1b572015-05-27 11:02:55 -0700112 }
113
robertphillips6738c702016-07-27 12:13:51 -0700114 sk_sp<GrDrawContext> drawContext(context->makeDrawContext(SkBackingFit::kApprox,
115 maskRect.width(),
116 maskRect.height(),
117 config,
118 nullptr,
119 sampleCnt));
robertphillipsccb1b572015-05-27 11:02:55 -0700120 if (!drawContext) {
halcanary96fcdcc2015-08-27 07:41:13 -0700121 return nullptr;
robertphillipsccb1b572015-05-27 11:02:55 -0700122 }
123
robertphillips2e1e51f2015-10-15 08:01:48 -0700124 drawContext->clear(nullptr, 0x0, true);
robertphillipsccb1b572015-05-27 11:02:55 -0700125
126 GrPaint tempPaint;
127 tempPaint.setAntiAlias(doAA);
128 tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
129
130 // setup new clip
cdalton846c0512016-05-13 10:25:00 -0700131 const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height());
132 GrFixedClip clip(clipRect);
robertphillipsccb1b572015-05-27 11:02:55 -0700133
robertphillips3833daa2015-09-14 11:18:13 -0700134 // Draw the mask into maskTexture with the path's integerized top-left at
135 // the origin using tempPaint.
robertphillipsccb1b572015-05-27 11:02:55 -0700136 SkMatrix translate;
robertphillipsf054b172016-05-13 05:06:19 -0700137 translate.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
bsalomon6663acf2016-05-10 09:14:17 -0700138 drawContext->drawPath(clip, tempPaint, translate, devPath, GrStyle(fillOrHairline));
robertphillipsd4c741e2016-04-28 09:55:15 -0700139 return drawContext->asTexture();;
robertphillipsccb1b572015-05-27 11:02:55 -0700140}
141
bsalomonc55271f2015-11-09 11:55:57 -0800142static void draw_path_with_mask_filter(GrContext* context,
143 GrDrawContext* drawContext,
bsalomonc55271f2015-11-09 11:55:57 -0800144 const GrClip& clip,
145 GrPaint* paint,
146 const SkMatrix& viewMatrix,
147 const SkMaskFilter* maskFilter,
bsalomon6663acf2016-05-10 09:14:17 -0700148 const GrStyle& style,
149 const SkPath* path,
bsalomonc55271f2015-11-09 11:55:57 -0800150 bool pathIsMutable) {
151 SkASSERT(maskFilter);
152
153 SkIRect clipBounds;
robertphillips7bceedc2015-12-01 12:51:26 -0800154 clip.getConservativeBounds(drawContext->width(), drawContext->height(), &clipBounds);
bsalomonc55271f2015-11-09 11:55:57 -0800155 SkTLazy<SkPath> tmpPath;
bsalomon6663acf2016-05-10 09:14:17 -0700156 SkStrokeRec::InitStyle fillOrHairline;
bsalomonc55271f2015-11-09 11:55:57 -0800157
bsalomon6663acf2016-05-10 09:14:17 -0700158 // We just fully apply the style here.
159 if (style.applies()) {
senorblancob6a40b82016-08-19 08:07:22 -0700160 SkScalar scale = GrStyle::MatrixToScaleFactor(viewMatrix);
161 if (0 == scale || !style.applyToPath(tmpPath.init(), &fillOrHairline, *path, scale)) {
bsalomon6663acf2016-05-10 09:14:17 -0700162 return;
163 }
164 pathIsMutable = true;
165 path = tmpPath.get();
166 } else if (style.isSimpleHairline()) {
167 fillOrHairline = SkStrokeRec::kHairline_InitStyle;
bsalomon055e1922016-05-06 07:22:58 -0700168 } else {
bsalomon6663acf2016-05-10 09:14:17 -0700169 SkASSERT(style.isSimpleFill());
170 fillOrHairline = SkStrokeRec::kFill_InitStyle;
bsalomonc55271f2015-11-09 11:55:57 -0800171 }
172
173 // transform the path into device space
bsalomon6663acf2016-05-10 09:14:17 -0700174 if (!viewMatrix.isIdentity()) {
175 SkPath* result;
176 if (pathIsMutable) {
177 result = const_cast<SkPath*>(path);
178 } else {
179 if (!tmpPath.isValid()) {
180 tmpPath.init();
181 }
182 result = tmpPath.get();
183 }
184 path->transform(viewMatrix, result);
185 path = result;
186 result->setIsVolatile(true);
187 pathIsMutable = true;
188 }
bsalomonc55271f2015-11-09 11:55:57 -0800189
190 SkRect maskRect;
bsalomon6663acf2016-05-10 09:14:17 -0700191 if (maskFilter->canFilterMaskGPU(SkRRect::MakeRect(path->getBounds()),
bsalomonc55271f2015-11-09 11:55:57 -0800192 clipBounds,
193 viewMatrix,
194 &maskRect)) {
robertphillipsf054b172016-05-13 05:06:19 -0700195 // This mask will ultimately be drawn as a non-AA rect (see draw_mask).
196 // Non-AA rects have a bad habit of snapping arbitrarily. Integerize here
197 // so the mask draws in a reproducible manner.
bsalomonc55271f2015-11-09 11:55:57 -0800198 SkIRect finalIRect;
199 maskRect.roundOut(&finalIRect);
200 if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
201 // clipped out
202 return;
203 }
204
205 if (maskFilter->directFilterMaskGPU(context->textureProvider(),
206 drawContext,
207 paint,
208 clip,
209 viewMatrix,
bsalomon6663acf2016-05-10 09:14:17 -0700210 SkStrokeRec(fillOrHairline),
211 *path)) {
bsalomonc55271f2015-11-09 11:55:57 -0800212 // the mask filter was able to draw itself directly, so there's nothing
213 // left to do.
214 return;
215 }
216
robertphillipsd4c741e2016-04-28 09:55:15 -0700217 sk_sp<GrTexture> mask(create_mask_GPU(context,
robertphillipsf054b172016-05-13 05:06:19 -0700218 finalIRect,
bsalomon6663acf2016-05-10 09:14:17 -0700219 *path,
220 fillOrHairline,
robertphillipsd4c741e2016-04-28 09:55:15 -0700221 paint->isAntiAlias(),
222 drawContext->numColorSamples()));
bsalomonc55271f2015-11-09 11:55:57 -0800223 if (mask) {
224 GrTexture* filtered;
225
robertphillips8bad3ac2016-06-27 11:11:05 -0700226 if (maskFilter->filterMaskGPU(mask.get(), viewMatrix, finalIRect, &filtered)) {
bsalomonc55271f2015-11-09 11:55:57 -0800227 // filterMaskGPU gives us ownership of a ref to the result
228 SkAutoTUnref<GrTexture> atu(filtered);
robertphillipsf054b172016-05-13 05:06:19 -0700229 if (draw_mask(drawContext, clip, viewMatrix, finalIRect, paint, filtered)) {
bsalomonc55271f2015-11-09 11:55:57 -0800230 // This path is completely drawn
231 return;
232 }
233 }
234 }
235 }
236
robertphillips0e7029e2015-11-30 05:45:06 -0800237 sw_draw_with_mask_filter(drawContext, context->textureProvider(),
bsalomon6663acf2016-05-10 09:14:17 -0700238 clip, viewMatrix, *path,
239 maskFilter, clipBounds, paint, fillOrHairline);
240}
241
242void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
243 GrDrawContext* drawContext,
244 const GrClip& clip,
245 const SkPath& path,
246 GrPaint* paint,
247 const SkMatrix& viewMatrix,
248 const SkMaskFilter* mf,
249 const GrStyle& style,
250 bool pathIsMutable) {
251 draw_path_with_mask_filter(context, drawContext, clip, paint, viewMatrix, mf,
252 style, &path, pathIsMutable);
bsalomonc55271f2015-11-09 11:55:57 -0800253}
254
255void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
256 GrDrawContext* drawContext,
bsalomonc55271f2015-11-09 11:55:57 -0800257 const GrClip& clip,
258 const SkPath& origPath,
robertphillipsccb1b572015-05-27 11:02:55 -0700259 const SkPaint& paint,
260 const SkMatrix& origViewMatrix,
261 const SkMatrix* prePathMatrix,
262 const SkIRect& clipBounds,
263 bool pathIsMutable) {
bsalomon6663acf2016-05-10 09:14:17 -0700264 SkASSERT(!pathIsMutable || origPath.isVolatile());
robertphillipsccb1b572015-05-27 11:02:55 -0700265
bsalomon6663acf2016-05-10 09:14:17 -0700266 GrStyle style(paint);
robertphillipsccb1b572015-05-27 11:02:55 -0700267 // If we have a prematrix, apply it to the path, optimizing for the case
268 // where the original path can in fact be modified in place (even though
269 // its parameter type is const).
bsalomon6663acf2016-05-10 09:14:17 -0700270
271 const SkPath* path = &origPath;
robertphillipsccb1b572015-05-27 11:02:55 -0700272 SkTLazy<SkPath> tmpPath;
robertphillipsccb1b572015-05-27 11:02:55 -0700273
274 SkMatrix viewMatrix = origViewMatrix;
275
276 if (prePathMatrix) {
bsalomon6663acf2016-05-10 09:14:17 -0700277 // Styling, blurs, and shading are supposed to be applied *after* the prePathMatrix.
278 if (!paint.getMaskFilter() && !paint.getShader() && !style.applies()) {
robertphillipsccb1b572015-05-27 11:02:55 -0700279 viewMatrix.preConcat(*prePathMatrix);
280 } else {
bsalomon6663acf2016-05-10 09:14:17 -0700281 SkPath* result = pathIsMutable ? const_cast<SkPath*>(path) : tmpPath.init();
282 pathIsMutable = true;
283 path->transform(*prePathMatrix, result);
284 path = result;
285 result->setIsVolatile(true);
robertphillipsccb1b572015-05-27 11:02:55 -0700286 }
287 }
288 // at this point we're done with prePathMatrix
289 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
290
291 GrPaint grPaint;
brianosman8fe485b2016-07-25 12:31:51 -0700292 if (!SkPaintToGrPaint(context, drawContext, paint, viewMatrix, &grPaint)) {
robertphillipsccb1b572015-05-27 11:02:55 -0700293 return;
294 }
295
robertphillipsccb1b572015-05-27 11:02:55 -0700296 if (paint.getMaskFilter()) {
robertphillips7bceedc2015-12-01 12:51:26 -0800297 draw_path_with_mask_filter(context, drawContext, clip, &grPaint, viewMatrix,
bsalomon6663acf2016-05-10 09:14:17 -0700298 paint.getMaskFilter(), style,
299 path, pathIsMutable);
bsalomonc55271f2015-11-09 11:55:57 -0800300 } else {
bsalomon6663acf2016-05-10 09:14:17 -0700301 drawContext->drawPath(clip, grPaint, viewMatrix, *path, style);
robertphillipsccb1b572015-05-27 11:02:55 -0700302 }
robertphillipsccb1b572015-05-27 11:02:55 -0700303}