1 /*
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 "GrRenderTargetContext.h"
10 #include "GrCaps.h"
11 #include "GrContext.h"
12 #include "GrContextPriv.h"
13 #include "GrFixedClip.h"
14 #include "GrRenderTargetContextPriv.h"
15 #include "effects/GrSimpleTextureEffect.h"
16 #include "GrStyle.h"
17 #include "GrTextureProxy.h"
18 #include "SkDraw.h"
19 #include "SkGr.h"
20 #include "SkMaskFilterBase.h"
21 #include "SkPaint.h"
22 #include "SkTLazy.h"
23
clip_bounds_quick_reject(const SkIRect & clipBounds,const SkIRect & rect)24 static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& rect) {
25 return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBounds, rect);
26 }
27
28 // Draw a mask using the supplied paint. Since the coverage/geometry
29 // is already burnt into the mask this boils down to a rect draw.
30 // Return true if the mask was successfully drawn.
draw_mask(GrRenderTargetContext * renderTargetContext,const GrClip & clip,const SkMatrix & viewMatrix,const SkIRect & maskRect,GrPaint && paint,sk_sp<GrTextureProxy> mask)31 static bool draw_mask(GrRenderTargetContext* renderTargetContext,
32 const GrClip& clip,
33 const SkMatrix& viewMatrix,
34 const SkIRect& maskRect,
35 GrPaint&& paint,
36 sk_sp<GrTextureProxy> mask) {
37 SkMatrix inverse;
38 if (!viewMatrix.invert(&inverse)) {
39 return false;
40 }
41
42 SkMatrix matrix = SkMatrix::MakeTrans(-SkIntToScalar(maskRect.fLeft),
43 -SkIntToScalar(maskRect.fTop));
44 matrix.preConcat(viewMatrix);
45 paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(std::move(mask), matrix));
46
47 renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
48 SkRect::Make(maskRect), inverse);
49 return true;
50 }
51
sw_draw_with_mask_filter(GrContext * context,GrRenderTargetContext * renderTargetContext,const GrClip & clipData,const SkMatrix & viewMatrix,const SkPath & devPath,const SkMaskFilter * filter,const SkIRect & clipBounds,GrPaint && paint,SkStrokeRec::InitStyle fillOrHairline)52 static bool sw_draw_with_mask_filter(GrContext* context,
53 GrRenderTargetContext* renderTargetContext,
54 const GrClip& clipData,
55 const SkMatrix& viewMatrix,
56 const SkPath& devPath,
57 const SkMaskFilter* filter,
58 const SkIRect& clipBounds,
59 GrPaint&& paint,
60 SkStrokeRec::InitStyle fillOrHairline) {
61 SkMask srcM, dstM;
62 if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
63 SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) {
64 return false;
65 }
66 SkAutoMaskFreeImage autoSrc(srcM.fImage);
67
68 if (!as_MFB(filter)->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
69 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.fOrigin = kTopLeft_GrSurfaceOrigin;
82 desc.fWidth = dstM.fBounds.width();
83 desc.fHeight = dstM.fBounds.height();
84 desc.fConfig = kAlpha_8_GrPixelConfig;
85
86 sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeDeferredSurfaceContext(
87 desc,
88 GrMipMapped::kNo,
89 SkBackingFit::kApprox,
90 SkBudgeted::kYes);
91 if (!sContext) {
92 return false;
93 }
94
95 SkImageInfo ii = SkImageInfo::MakeA8(desc.fWidth, desc.fHeight);
96 if (!sContext->writePixels(ii, dstM.fImage, dstM.fRowBytes, 0, 0)) {
97 return false;
98 }
99
100 return draw_mask(renderTargetContext, clipData, viewMatrix,
101 dstM.fBounds, std::move(paint), sContext->asTextureProxyRef());
102 }
103
104 // Create a mask of 'devPath' and place the result in 'mask'.
create_mask_GPU(GrContext * context,const SkIRect & maskRect,const SkPath & devPath,SkStrokeRec::InitStyle fillOrHairline,GrAA aa,int sampleCnt)105 static sk_sp<GrTextureProxy> create_mask_GPU(GrContext* context,
106 const SkIRect& maskRect,
107 const SkPath& devPath,
108 SkStrokeRec::InitStyle fillOrHairline,
109 GrAA aa,
110 int sampleCnt) {
111 if (GrAA::kNo == aa) {
112 // Don't need MSAA if mask isn't AA
113 sampleCnt = 1;
114 }
115
116 sk_sp<GrRenderTargetContext> rtContext(context->makeDeferredRenderTargetContextWithFallback(
117 SkBackingFit::kApprox, maskRect.width(), maskRect.height(), kAlpha_8_GrPixelConfig, nullptr,
118 sampleCnt));
119 if (!rtContext) {
120 return nullptr;
121 }
122
123 rtContext->priv().absClear(nullptr, 0x0);
124
125 GrPaint maskPaint;
126 maskPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
127
128 // setup new clip
129 const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height());
130 GrFixedClip clip(clipRect);
131
132 // Draw the mask into maskTexture with the path's integerized top-left at
133 // the origin using maskPaint.
134 SkMatrix translate;
135 translate.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
136 rtContext->drawPath(clip, std::move(maskPaint), aa, translate, devPath,
137 GrStyle(fillOrHairline));
138 return rtContext->asTextureProxyRef();
139 }
140
draw_path_with_mask_filter(GrContext * context,GrRenderTargetContext * renderTargetContext,const GrClip & clip,GrPaint && paint,GrAA aa,const SkMatrix & viewMatrix,const SkMaskFilterBase * maskFilter,const GrStyle & style,const SkPath * path,bool pathIsMutable)141 static void draw_path_with_mask_filter(GrContext* context,
142 GrRenderTargetContext* renderTargetContext,
143 const GrClip& clip,
144 GrPaint&& paint,
145 GrAA aa,
146 const SkMatrix& viewMatrix,
147 const SkMaskFilterBase* maskFilter,
148 const GrStyle& style,
149 const SkPath* path,
150 bool pathIsMutable) {
151 SkASSERT(maskFilter);
152
153 SkIRect clipBounds;
154 clip.getConservativeBounds(renderTargetContext->width(),
155 renderTargetContext->height(),
156 &clipBounds);
157 SkTLazy<SkPath> tmpPath;
158 SkStrokeRec::InitStyle fillOrHairline;
159
160 // We just fully apply the style here.
161 if (style.applies()) {
162 SkScalar scale = GrStyle::MatrixToScaleFactor(viewMatrix);
163 if (0 == scale || !style.applyToPath(tmpPath.init(), &fillOrHairline, *path, scale)) {
164 return;
165 }
166 pathIsMutable = true;
167 path = tmpPath.get();
168 } else if (style.isSimpleHairline()) {
169 fillOrHairline = SkStrokeRec::kHairline_InitStyle;
170 } else {
171 SkASSERT(style.isSimpleFill());
172 fillOrHairline = SkStrokeRec::kFill_InitStyle;
173 }
174
175 // transform the path into device space
176 if (!viewMatrix.isIdentity()) {
177 SkPath* result;
178 if (pathIsMutable) {
179 result = const_cast<SkPath*>(path);
180 } else {
181 if (!tmpPath.isValid()) {
182 tmpPath.init();
183 }
184 result = tmpPath.get();
185 }
186 path->transform(viewMatrix, result);
187 path = result;
188 result->setIsVolatile(true);
189 pathIsMutable = true;
190 }
191
192 SkRect maskRect;
193 if (maskFilter->canFilterMaskGPU(SkRRect::MakeRect(path->getBounds()),
194 clipBounds,
195 viewMatrix,
196 &maskRect)) {
197 // This mask will ultimately be drawn as a non-AA rect (see draw_mask).
198 // Non-AA rects have a bad habit of snapping arbitrarily. Integerize here
199 // so the mask draws in a reproducible manner.
200 SkIRect finalIRect;
201 maskRect.roundOut(&finalIRect);
202 if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
203 // clipped out
204 return;
205 }
206
207 if (maskFilter->directFilterMaskGPU(context,
208 renderTargetContext,
209 std::move(paint),
210 clip,
211 viewMatrix,
212 SkStrokeRec(fillOrHairline),
213 *path)) {
214 // the mask filter was able to draw itself directly, so there's nothing
215 // left to do.
216 return;
217 }
218
219 sk_sp<GrTextureProxy> maskProxy(create_mask_GPU(context,
220 finalIRect,
221 *path,
222 fillOrHairline,
223 aa,
224 renderTargetContext->numColorSamples()));
225 if (maskProxy) {
226 sk_sp<GrTextureProxy> filtered = maskFilter->filterMaskGPU(context,
227 std::move(maskProxy),
228 viewMatrix,
229 finalIRect);
230 if (filtered) {
231 if (draw_mask(renderTargetContext, clip, viewMatrix,
232 finalIRect, std::move(paint), std::move(filtered))) {
233 // This path is completely drawn
234 return;
235 }
236 }
237 }
238 }
239
240 sw_draw_with_mask_filter(context, renderTargetContext, clip, viewMatrix, *path, maskFilter,
241 clipBounds, std::move(paint), fillOrHairline);
242 }
243
drawPathWithMaskFilter(GrContext * context,GrRenderTargetContext * renderTargetContext,const GrClip & clip,const SkPath & path,GrPaint && paint,GrAA aa,const SkMatrix & viewMatrix,const SkMaskFilter * mf,const GrStyle & style,bool pathIsMutable)244 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
245 GrRenderTargetContext* renderTargetContext,
246 const GrClip& clip,
247 const SkPath& path,
248 GrPaint&& paint,
249 GrAA aa,
250 const SkMatrix& viewMatrix,
251 const SkMaskFilter* mf,
252 const GrStyle& style,
253 bool pathIsMutable) {
254 draw_path_with_mask_filter(context, renderTargetContext, clip, std::move(paint), aa, viewMatrix,
255 as_MFB(mf), style, &path, pathIsMutable);
256 }
257
drawPathWithMaskFilter(GrContext * context,GrRenderTargetContext * renderTargetContext,const GrClip & clip,const SkPath & origPath,const SkPaint & paint,const SkMatrix & origViewMatrix,const SkMatrix * prePathMatrix,const SkIRect & clipBounds,bool pathIsMutable)258 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
259 GrRenderTargetContext* renderTargetContext,
260 const GrClip& clip,
261 const SkPath& origPath,
262 const SkPaint& paint,
263 const SkMatrix& origViewMatrix,
264 const SkMatrix* prePathMatrix,
265 const SkIRect& clipBounds,
266 bool pathIsMutable) {
267 SkASSERT(!pathIsMutable || origPath.isVolatile());
268
269 GrStyle style(paint);
270 // If we have a prematrix, apply it to the path, optimizing for the case
271 // where the original path can in fact be modified in place (even though
272 // its parameter type is const).
273
274 const SkPath* path = &origPath;
275 SkTLazy<SkPath> tmpPath;
276
277 SkMatrix viewMatrix = origViewMatrix;
278
279 if (prePathMatrix) {
280 // Styling, blurs, and shading are supposed to be applied *after* the prePathMatrix.
281 if (!paint.getMaskFilter() && !paint.getShader() && !style.applies()) {
282 viewMatrix.preConcat(*prePathMatrix);
283 } else {
284 SkPath* result = pathIsMutable ? const_cast<SkPath*>(path) : tmpPath.init();
285 pathIsMutable = true;
286 path->transform(*prePathMatrix, result);
287 path = result;
288 result->setIsVolatile(true);
289 }
290 }
291 // at this point we're done with prePathMatrix
292 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
293
294 GrPaint grPaint;
295 if (!SkPaintToGrPaint(context, renderTargetContext->colorSpaceInfo(), paint, viewMatrix,
296 &grPaint)) {
297 return;
298 }
299 GrAA aa = GrAA(paint.isAntiAlias());
300 SkMaskFilterBase* mf = as_MFB(paint.getMaskFilter());
301 if (mf && !mf->hasFragmentProcessor()) {
302 // The MaskFilter wasn't already handled in SkPaintToGrPaint
303 draw_path_with_mask_filter(context, renderTargetContext, clip, std::move(grPaint), aa,
304 viewMatrix, mf, style, path, pathIsMutable);
305 } else {
306 renderTargetContext->drawPath(clip, std::move(grPaint), aa, viewMatrix, *path, style);
307 }
308 }
309