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
24static 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.
31static 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
52static 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'.
105static 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
141static 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
244void 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
258void 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