2 * Copyright 2015 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "GrBlurUtils.h"
9 #include "GrDrawContext.h"
11 #include "GrContext.h"
12 #include "GrFixedClip.h"
13 #include "effects/GrSimpleTextureEffect.h"
15 #include "GrTexture.h"
16 #include "GrTextureProvider.h"
19 #include "SkMaskFilter.h"
23 static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& rect) {
24 return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBounds, rect);
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.
30 static bool draw_mask(GrDrawContext* drawContext,
32 const SkMatrix& viewMatrix,
33 const SkIRect& maskRect,
37 matrix.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
38 matrix.postIDiv(mask->width(), mask->height());
40 grp->addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(mask, nullptr, matrix,
44 if (!viewMatrix.invert(&inverse)) {
47 drawContext->fillRectWithLocalMatrix(clip, *grp, SkMatrix::I(),
48 SkRect::Make(maskRect), inverse);
52 static 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,
60 SkStrokeRec::InitStyle fillOrHairline) {
62 if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
63 SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) {
66 SkAutoMaskFreeImage autoSrc(srcM.fImage);
68 if (!filter->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
71 // this will free-up dstM when we're done (allocated in filterMask())
72 SkAutoMaskFreeImage autoDst(dstM.fImage);
74 if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) {
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
81 desc.fWidth = dstM.fBounds.width();
82 desc.fHeight = dstM.fBounds.height();
83 desc.fConfig = kAlpha_8_GrPixelConfig;
85 SkAutoTUnref<GrTexture> texture(textureProvider->createApproxTexture(desc));
89 texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
90 dstM.fImage, dstM.fRowBytes);
92 return draw_mask(drawContext, clipData, viewMatrix, dstM.fBounds, grp, texture);
95 // Create a mask of 'devPath' and place the result in 'mask'.
96 static sk_sp<GrTexture> create_mask_GPU(GrContext* context,
97 const SkIRect& maskRect,
98 const SkPath& devPath,
99 SkStrokeRec::InitStyle fillOrHairline,
103 // Don't need MSAA if mask isn't AA
107 sk_sp<GrDrawContext> drawContext(context->makeDrawContextWithFallback(SkBackingFit::kApprox,
110 kAlpha_8_GrPixelConfig,
117 drawContext->clear(nullptr, 0x0, true);
120 tempPaint.setAntiAlias(doAA);
121 tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
124 const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height());
125 GrFixedClip clip(clipRect);
127 // Draw the mask into maskTexture with the path's integerized top-left at
128 // the origin using tempPaint.
130 translate.setTranslate(-SkIntToScalar(maskRect.fLeft), -SkIntToScalar(maskRect.fTop));
131 drawContext->drawPath(clip, tempPaint, translate, devPath, GrStyle(fillOrHairline));
132 return drawContext->asTexture();;
135 static void draw_path_with_mask_filter(GrContext* context,
136 GrDrawContext* drawContext,
139 const SkMatrix& viewMatrix,
140 const SkMaskFilter* maskFilter,
141 const GrStyle& style,
143 bool pathIsMutable) {
144 SkASSERT(maskFilter);
147 clip.getConservativeBounds(drawContext->width(), drawContext->height(), &clipBounds);
148 SkTLazy<SkPath> tmpPath;
149 SkStrokeRec::InitStyle fillOrHairline;
151 // We just fully apply the style here.
152 if (style.applies()) {
153 SkScalar scale = GrStyle::MatrixToScaleFactor(viewMatrix);
154 if (0 == scale || !style.applyToPath(tmpPath.init(), &fillOrHairline, *path, scale)) {
157 pathIsMutable = true;
158 path = tmpPath.get();
159 } else if (style.isSimpleHairline()) {
160 fillOrHairline = SkStrokeRec::kHairline_InitStyle;
162 SkASSERT(style.isSimpleFill());
163 fillOrHairline = SkStrokeRec::kFill_InitStyle;
166 // transform the path into device space
167 if (!viewMatrix.isIdentity()) {
170 result = const_cast<SkPath*>(path);
172 if (!tmpPath.isValid()) {
175 result = tmpPath.get();
177 path->transform(viewMatrix, result);
179 result->setIsVolatile(true);
180 pathIsMutable = true;
184 if (maskFilter->canFilterMaskGPU(SkRRect::MakeRect(path->getBounds()),
188 // This mask will ultimately be drawn as a non-AA rect (see draw_mask).
189 // Non-AA rects have a bad habit of snapping arbitrarily. Integerize here
190 // so the mask draws in a reproducible manner.
192 maskRect.roundOut(&finalIRect);
193 if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
198 if (maskFilter->directFilterMaskGPU(context->textureProvider(),
203 SkStrokeRec(fillOrHairline),
205 // the mask filter was able to draw itself directly, so there's nothing
210 sk_sp<GrTexture> mask(create_mask_GPU(context,
214 paint->isAntiAlias(),
215 drawContext->numColorSamples()));
219 if (maskFilter->filterMaskGPU(mask.get(), viewMatrix, finalIRect, &filtered)) {
220 // filterMaskGPU gives us ownership of a ref to the result
221 SkAutoTUnref<GrTexture> atu(filtered);
222 if (draw_mask(drawContext, clip, viewMatrix, finalIRect, paint, filtered)) {
223 // This path is completely drawn
230 sw_draw_with_mask_filter(drawContext, context->textureProvider(),
231 clip, viewMatrix, *path,
232 maskFilter, clipBounds, paint, fillOrHairline);
235 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
236 GrDrawContext* drawContext,
240 const SkMatrix& viewMatrix,
241 const SkMaskFilter* mf,
242 const GrStyle& style,
243 bool pathIsMutable) {
244 draw_path_with_mask_filter(context, drawContext, clip, paint, viewMatrix, mf,
245 style, &path, pathIsMutable);
248 void GrBlurUtils::drawPathWithMaskFilter(GrContext* context,
249 GrDrawContext* drawContext,
251 const SkPath& origPath,
252 const SkPaint& paint,
253 const SkMatrix& origViewMatrix,
254 const SkMatrix* prePathMatrix,
255 const SkIRect& clipBounds,
256 bool pathIsMutable) {
257 SkASSERT(!pathIsMutable || origPath.isVolatile());
259 GrStyle style(paint);
260 // If we have a prematrix, apply it to the path, optimizing for the case
261 // where the original path can in fact be modified in place (even though
262 // its parameter type is const).
264 const SkPath* path = &origPath;
265 SkTLazy<SkPath> tmpPath;
267 SkMatrix viewMatrix = origViewMatrix;
270 // Styling, blurs, and shading are supposed to be applied *after* the prePathMatrix.
271 if (!paint.getMaskFilter() && !paint.getShader() && !style.applies()) {
272 viewMatrix.preConcat(*prePathMatrix);
274 SkPath* result = pathIsMutable ? const_cast<SkPath*>(path) : tmpPath.init();
275 pathIsMutable = true;
276 path->transform(*prePathMatrix, result);
278 result->setIsVolatile(true);
281 // at this point we're done with prePathMatrix
282 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
285 if (!SkPaintToGrPaint(context, drawContext, paint, viewMatrix, &grPaint)) {
289 if (paint.getMaskFilter()) {
290 draw_path_with_mask_filter(context, drawContext, clip, &grPaint, viewMatrix,
291 paint.getMaskFilter(), style,
292 path, pathIsMutable);
294 drawContext->drawPath(clip, grPaint, viewMatrix, *path, style);