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