1/*
2 * Copyright 2012 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 "GrSoftwarePathRenderer.h"
9#include "GrAuditTrail.h"
10#include "GrClip.h"
11#include "GrGpuResourcePriv.h"
12#include "GrPipelineBuilder.h"
13#include "GrResourceProvider.h"
14#include "GrSWMaskHelper.h"
15#include "ops/GrRectOpFactory.h"
16
17////////////////////////////////////////////////////////////////////////////////
18bool GrSoftwarePathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
19    // Pass on any style that applies. The caller will apply the style if a suitable renderer is
20    // not found and try again with the new GrShape.
21    return !args.fShape->style().applies() && SkToBool(fResourceProvider) &&
22           (args.fAAType == GrAAType::kCoverage || args.fAAType == GrAAType::kNone);
23}
24
25////////////////////////////////////////////////////////////////////////////////
26static bool get_unclipped_shape_dev_bounds(const GrShape& shape, const SkMatrix& matrix,
27                                           SkIRect* devBounds) {
28    SkRect shapeBounds = shape.styledBounds();
29    if (shapeBounds.isEmpty()) {
30        return false;
31    }
32    SkRect shapeDevBounds;
33    matrix.mapRect(&shapeDevBounds, shapeBounds);
34    // Even though these are "unclipped" bounds we still clip to the int32_t range.
35    // This is the largest int32_t that is representable exactly as a float. The next 63 larger ints
36    // would round down to this value when cast to a float, but who really cares.
37    // INT32_MIN is exactly representable.
38    static constexpr int32_t kMaxInt = 2147483520;
39    if (!shapeDevBounds.intersect(SkRect::MakeLTRB(INT32_MIN, INT32_MIN, kMaxInt, kMaxInt))) {
40        return false;
41    }
42    shapeDevBounds.roundOut(devBounds);
43    return true;
44}
45
46// Gets the shape bounds, the clip bounds, and the intersection (if any). Returns false if there
47// is no intersection.
48static bool get_shape_and_clip_bounds(int width, int height,
49                                      const GrClip& clip,
50                                      const GrShape& shape,
51                                      const SkMatrix& matrix,
52                                      SkIRect* unclippedDevShapeBounds,
53                                      SkIRect* clippedDevShapeBounds,
54                                      SkIRect* devClipBounds) {
55    // compute bounds as intersection of rt size, clip, and path
56    clip.getConservativeBounds(width, height, devClipBounds);
57
58    if (!get_unclipped_shape_dev_bounds(shape, matrix, unclippedDevShapeBounds)) {
59        *unclippedDevShapeBounds = SkIRect::EmptyIRect();
60        *clippedDevShapeBounds = SkIRect::EmptyIRect();
61        return false;
62    }
63    if (!clippedDevShapeBounds->intersect(*devClipBounds, *unclippedDevShapeBounds)) {
64        *clippedDevShapeBounds = SkIRect::EmptyIRect();
65        return false;
66    }
67    return true;
68}
69
70////////////////////////////////////////////////////////////////////////////////
71
72void GrSoftwarePathRenderer::DrawNonAARect(GrRenderTargetContext* renderTargetContext,
73                                           GrPaint&& paint,
74                                           const GrUserStencilSettings& userStencilSettings,
75                                           const GrClip& clip,
76                                           const SkMatrix& viewMatrix,
77                                           const SkRect& rect,
78                                           const SkMatrix& localMatrix) {
79    std::unique_ptr<GrMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill(paint.getColor(), viewMatrix,
80                                                                    rect, nullptr, &localMatrix));
81
82    GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
83    pipelineBuilder.setUserStencil(&userStencilSettings);
84    renderTargetContext->addMeshDrawOp(pipelineBuilder, clip, std::move(op));
85}
86
87void GrSoftwarePathRenderer::DrawAroundInvPath(GrRenderTargetContext* renderTargetContext,
88                                               GrPaint&& paint,
89                                               const GrUserStencilSettings& userStencilSettings,
90                                               const GrClip& clip,
91                                               const SkMatrix& viewMatrix,
92                                               const SkIRect& devClipBounds,
93                                               const SkIRect& devPathBounds) {
94    SkMatrix invert;
95    if (!viewMatrix.invert(&invert)) {
96        return;
97    }
98
99    SkRect rect;
100    if (devClipBounds.fTop < devPathBounds.fTop) {
101        rect.iset(devClipBounds.fLeft, devClipBounds.fTop,
102                  devClipBounds.fRight, devPathBounds.fTop);
103        DrawNonAARect(renderTargetContext, GrPaint(paint), userStencilSettings, clip, SkMatrix::I(),
104                      rect, invert);
105    }
106    if (devClipBounds.fLeft < devPathBounds.fLeft) {
107        rect.iset(devClipBounds.fLeft, devPathBounds.fTop,
108                  devPathBounds.fLeft, devPathBounds.fBottom);
109        DrawNonAARect(renderTargetContext, GrPaint(paint), userStencilSettings, clip, SkMatrix::I(),
110                      rect, invert);
111    }
112    if (devClipBounds.fRight > devPathBounds.fRight) {
113        rect.iset(devPathBounds.fRight, devPathBounds.fTop,
114                  devClipBounds.fRight, devPathBounds.fBottom);
115        DrawNonAARect(renderTargetContext, GrPaint(paint), userStencilSettings, clip, SkMatrix::I(),
116                      rect, invert);
117    }
118    if (devClipBounds.fBottom > devPathBounds.fBottom) {
119        rect.iset(devClipBounds.fLeft, devPathBounds.fBottom,
120                  devClipBounds.fRight, devClipBounds.fBottom);
121        DrawNonAARect(renderTargetContext, std::move(paint), userStencilSettings, clip,
122                      SkMatrix::I(), rect, invert);
123    }
124}
125
126////////////////////////////////////////////////////////////////////////////////
127// return true on success; false on failure
128bool GrSoftwarePathRenderer::onDrawPath(const DrawPathArgs& args) {
129    GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
130                              "GrSoftwarePathRenderer::onDrawPath");
131    if (!fResourceProvider) {
132        return false;
133    }
134
135    // We really need to know if the shape will be inverse filled or not
136    bool inverseFilled = false;
137    SkTLazy<GrShape> tmpShape;
138    SkASSERT(!args.fShape->style().applies());
139    inverseFilled = args.fShape->inverseFilled();
140
141    SkIRect unclippedDevShapeBounds, clippedDevShapeBounds, devClipBounds;
142    // To prevent overloading the cache with entries during animations we limit the cache of masks
143    // to cases where the matrix preserves axis alignment.
144    bool useCache = fAllowCaching && !inverseFilled && args.fViewMatrix->preservesAxisAlignment() &&
145                    args.fShape->hasUnstyledKey() && GrAAType::kCoverage == args.fAAType;
146
147    if (!get_shape_and_clip_bounds(args.fRenderTargetContext->width(),
148                                   args.fRenderTargetContext->height(),
149                                   *args.fClip, *args.fShape,
150                                   *args.fViewMatrix, &unclippedDevShapeBounds,
151                                   &clippedDevShapeBounds,
152                                   &devClipBounds)) {
153        if (inverseFilled) {
154            DrawAroundInvPath(args.fRenderTargetContext, std::move(args.fPaint),
155                              *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix,
156                              devClipBounds, unclippedDevShapeBounds);
157        }
158        return true;
159    }
160
161    const SkIRect* boundsForMask = &clippedDevShapeBounds;
162    if (useCache) {
163        // Use the cache only if >50% of the path is visible.
164        int unclippedWidth = unclippedDevShapeBounds.width();
165        int unclippedHeight = unclippedDevShapeBounds.height();
166        int unclippedArea = unclippedWidth * unclippedHeight;
167        int clippedArea = clippedDevShapeBounds.width() * clippedDevShapeBounds.height();
168        int maxTextureSize = args.fRenderTargetContext->caps()->maxTextureSize();
169        if (unclippedArea > 2 * clippedArea || unclippedWidth > maxTextureSize ||
170            unclippedHeight > maxTextureSize) {
171            useCache = false;
172        } else {
173            boundsForMask = &unclippedDevShapeBounds;
174        }
175    }
176
177    GrUniqueKey maskKey;
178    struct KeyData {
179        SkScalar fFractionalTranslateX;
180        SkScalar fFractionalTranslateY;
181    };
182
183    if (useCache) {
184        // We require the upper left 2x2 of the matrix to match exactly for a cache hit.
185        SkScalar sx = args.fViewMatrix->get(SkMatrix::kMScaleX);
186        SkScalar sy = args.fViewMatrix->get(SkMatrix::kMScaleY);
187        SkScalar kx = args.fViewMatrix->get(SkMatrix::kMSkewX);
188        SkScalar ky = args.fViewMatrix->get(SkMatrix::kMSkewY);
189        SkScalar tx = args.fViewMatrix->get(SkMatrix::kMTransX);
190        SkScalar ty = args.fViewMatrix->get(SkMatrix::kMTransY);
191        // Allow 8 bits each in x and y of subpixel positioning.
192        SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00;
193        SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00;
194        static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
195        GrUniqueKey::Builder builder(&maskKey, kDomain, 5 + args.fShape->unstyledKeySize());
196        builder[0] = SkFloat2Bits(sx);
197        builder[1] = SkFloat2Bits(sy);
198        builder[2] = SkFloat2Bits(kx);
199        builder[3] = SkFloat2Bits(ky);
200        builder[4] = fracX | (fracY >> 8);
201        args.fShape->writeUnstyledKey(&builder[5]);
202        // FIXME: Doesn't the key need to consider whether we're using AA or not? In practice that
203        // should always be true, though.
204    }
205
206    sk_sp<GrTextureProxy> proxy;
207    if (useCache) {
208        proxy = fResourceProvider->findProxyByUniqueKey(maskKey);
209    }
210    if (!proxy) {
211        SkBackingFit fit = useCache ? SkBackingFit::kExact : SkBackingFit::kApprox;
212        GrAA aa = GrAAType::kCoverage == args.fAAType ? GrAA::kYes : GrAA::kNo;
213        proxy = GrSWMaskHelper::DrawShapeMaskToTexture(args.fContext, *args.fShape,
214                                                       *boundsForMask, aa,
215                                                       fit, args.fViewMatrix);
216        if (!proxy) {
217            return false;
218        }
219        if (useCache) {
220            fResourceProvider->assignUniqueKeyToProxy(maskKey, proxy.get());
221        }
222    }
223    if (inverseFilled) {
224        DrawAroundInvPath(args.fRenderTargetContext, GrPaint(args.fPaint),
225                          *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix, devClipBounds,
226                          unclippedDevShapeBounds);
227    }
228    GrSWMaskHelper::DrawToTargetWithShapeMask(
229            std::move(proxy), args.fRenderTargetContext, std::move(args.fPaint),
230            *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix,
231            SkIPoint{boundsForMask->fLeft, boundsForMask->fTop}, *boundsForMask);
232
233    return true;
234}
235