• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 ////////////////////////////////////////////////////////////////////////////////
onCanDrawPath(const CanDrawPathArgs & args) const18 bool 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 ////////////////////////////////////////////////////////////////////////////////
get_unclipped_shape_dev_bounds(const GrShape & shape,const SkMatrix & matrix,SkIRect * devBounds)26 static 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.
get_shape_and_clip_bounds(int width,int height,const GrClip & clip,const GrShape & shape,const SkMatrix & matrix,SkIRect * unclippedDevShapeBounds,SkIRect * clippedDevShapeBounds,SkIRect * devClipBounds)48 static 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 
DrawNonAARect(GrRenderTargetContext * renderTargetContext,GrPaint && paint,const GrUserStencilSettings & userStencilSettings,const GrClip & clip,const SkMatrix & viewMatrix,const SkRect & rect,const SkMatrix & localMatrix)72 void 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 
DrawAroundInvPath(GrRenderTargetContext * renderTargetContext,GrPaint && paint,const GrUserStencilSettings & userStencilSettings,const GrClip & clip,const SkMatrix & viewMatrix,const SkIRect & devClipBounds,const SkIRect & devPathBounds)87 void 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
onDrawPath(const DrawPathArgs & args)128 bool 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