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 "GrPipelineBuilder.h"
12 #include "GrGpuResourcePriv.h"
13 #include "GrSWMaskHelper.h"
14 #include "GrTextureProvider.h"
15 #include "batches/GrRectBatchFactory.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(fTexProvider);
22 }
23 
24 ////////////////////////////////////////////////////////////////////////////////
get_unclipped_shape_dev_bounds(const GrShape & shape,const SkMatrix & matrix,SkIRect * devBounds)25 static bool get_unclipped_shape_dev_bounds(const GrShape& shape, const SkMatrix& matrix,
26                                            SkIRect* devBounds) {
27     SkRect shapeBounds = shape.styledBounds();
28     if (shapeBounds.isEmpty()) {
29         return false;
30     }
31     SkRect shapeDevBounds;
32     matrix.mapRect(&shapeDevBounds, shapeBounds);
33     shapeDevBounds.roundOut(devBounds);
34     return true;
35 }
36 
37 // Gets the shape bounds, the clip bounds, and the intersection (if any). Returns false if there
38 // 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)39 static bool get_shape_and_clip_bounds(int width, int height,
40                                       const GrClip& clip,
41                                       const GrShape& shape,
42                                       const SkMatrix& matrix,
43                                       SkIRect* unclippedDevShapeBounds,
44                                       SkIRect* clippedDevShapeBounds,
45                                       SkIRect* devClipBounds) {
46     // compute bounds as intersection of rt size, clip, and path
47     clip.getConservativeBounds(width, height, devClipBounds);
48 
49     if (!get_unclipped_shape_dev_bounds(shape, matrix, unclippedDevShapeBounds)) {
50         *unclippedDevShapeBounds = SkIRect::EmptyIRect();
51         *clippedDevShapeBounds = SkIRect::EmptyIRect();
52         return false;
53     }
54     if (!clippedDevShapeBounds->intersect(*devClipBounds, *unclippedDevShapeBounds)) {
55         *clippedDevShapeBounds = SkIRect::EmptyIRect();
56         return false;
57     }
58     return true;
59 }
60 
61 ////////////////////////////////////////////////////////////////////////////////
62 
DrawNonAARect(GrDrawContext * drawContext,const GrPaint & paint,const GrUserStencilSettings & userStencilSettings,const GrClip & clip,const SkMatrix & viewMatrix,const SkRect & rect,const SkMatrix & localMatrix)63 void GrSoftwarePathRenderer::DrawNonAARect(GrDrawContext* drawContext,
64                                            const GrPaint& paint,
65                                            const GrUserStencilSettings& userStencilSettings,
66                                            const GrClip& clip,
67                                            const SkMatrix& viewMatrix,
68                                            const SkRect& rect,
69                                            const SkMatrix& localMatrix) {
70     SkAutoTUnref<GrDrawBatch> batch(GrRectBatchFactory::CreateNonAAFill(paint.getColor(),
71                                                                         viewMatrix, rect,
72                                                                         nullptr, &localMatrix));
73 
74     GrPipelineBuilder pipelineBuilder(paint, drawContext->mustUseHWAA(paint));
75     pipelineBuilder.setUserStencil(&userStencilSettings);
76 
77     drawContext->drawBatch(pipelineBuilder, clip, batch);
78 }
79 
DrawAroundInvPath(GrDrawContext * drawContext,const GrPaint & paint,const GrUserStencilSettings & userStencilSettings,const GrClip & clip,const SkMatrix & viewMatrix,const SkIRect & devClipBounds,const SkIRect & devPathBounds)80 void GrSoftwarePathRenderer::DrawAroundInvPath(GrDrawContext* drawContext,
81                                                const GrPaint& paint,
82                                                const GrUserStencilSettings& userStencilSettings,
83                                                const GrClip& clip,
84                                                const SkMatrix& viewMatrix,
85                                                const SkIRect& devClipBounds,
86                                                const SkIRect& devPathBounds) {
87     SkMatrix invert;
88     if (!viewMatrix.invert(&invert)) {
89         return;
90     }
91 
92     SkRect rect;
93     if (devClipBounds.fTop < devPathBounds.fTop) {
94         rect.iset(devClipBounds.fLeft, devClipBounds.fTop,
95                   devClipBounds.fRight, devPathBounds.fTop);
96         DrawNonAARect(drawContext, paint, userStencilSettings, clip,
97                       SkMatrix::I(), rect, invert);
98     }
99     if (devClipBounds.fLeft < devPathBounds.fLeft) {
100         rect.iset(devClipBounds.fLeft, devPathBounds.fTop,
101                   devPathBounds.fLeft, devPathBounds.fBottom);
102         DrawNonAARect(drawContext, paint, userStencilSettings, clip,
103                       SkMatrix::I(), rect, invert);
104     }
105     if (devClipBounds.fRight > devPathBounds.fRight) {
106         rect.iset(devPathBounds.fRight, devPathBounds.fTop,
107                   devClipBounds.fRight, devPathBounds.fBottom);
108         DrawNonAARect(drawContext, paint, userStencilSettings, clip,
109                       SkMatrix::I(), rect, invert);
110     }
111     if (devClipBounds.fBottom > devPathBounds.fBottom) {
112         rect.iset(devClipBounds.fLeft, devPathBounds.fBottom,
113                   devClipBounds.fRight, devClipBounds.fBottom);
114         DrawNonAARect(drawContext, paint, userStencilSettings, clip,
115                       SkMatrix::I(), rect, invert);
116     }
117 }
118 
119 ////////////////////////////////////////////////////////////////////////////////
120 // return true on success; false on failure
onDrawPath(const DrawPathArgs & args)121 bool GrSoftwarePathRenderer::onDrawPath(const DrawPathArgs& args) {
122     GR_AUDIT_TRAIL_AUTO_FRAME(args.fDrawContext->auditTrail(),
123                               "GrSoftwarePathRenderer::onDrawPath");
124     if (!fTexProvider) {
125         return false;
126     }
127 
128     // We really need to know if the shape will be inverse filled or not
129     bool inverseFilled = false;
130     SkTLazy<GrShape> tmpShape;
131     SkASSERT(!args.fShape->style().applies());
132     inverseFilled = args.fShape->inverseFilled();
133 
134     SkIRect unclippedDevShapeBounds, clippedDevShapeBounds, devClipBounds;
135     // To prevent overloading the cache with entries during animations we limit the cache of masks
136     // to cases where the matrix preserves axis alignment.
137     bool useCache = fAllowCaching && !inverseFilled && args.fViewMatrix->preservesAxisAlignment() &&
138                     args.fShape->hasUnstyledKey() && args.fAntiAlias;
139 
140     if (!get_shape_and_clip_bounds(args.fDrawContext->width(), args.fDrawContext->height(),
141                                    *args.fClip, *args.fShape,
142                                    *args.fViewMatrix, &unclippedDevShapeBounds,
143                                    &clippedDevShapeBounds,
144                                    &devClipBounds)) {
145         if (inverseFilled) {
146             DrawAroundInvPath(args.fDrawContext, *args.fPaint, *args.fUserStencilSettings,
147                               *args.fClip,
148                               *args.fViewMatrix, devClipBounds, unclippedDevShapeBounds);
149 
150         }
151         return true;
152     }
153 
154     const SkIRect* boundsForMask = &clippedDevShapeBounds;
155     if (useCache) {
156         // Use the cache only if >50% of the path is visible.
157         int unclippedWidth = unclippedDevShapeBounds.width();
158         int unclippedHeight = unclippedDevShapeBounds.height();
159         int unclippedArea = unclippedWidth * unclippedHeight;
160         int clippedArea = clippedDevShapeBounds.width() * clippedDevShapeBounds.height();
161         int maxTextureSize = args.fDrawContext->caps()->maxTextureSize();
162         if (unclippedArea > 2 * clippedArea || unclippedWidth > maxTextureSize ||
163             unclippedHeight > maxTextureSize) {
164             useCache = false;
165         } else {
166             boundsForMask = &unclippedDevShapeBounds;
167         }
168     }
169 
170     GrUniqueKey maskKey;
171     struct KeyData {
172         SkScalar fFractionalTranslateX;
173         SkScalar fFractionalTranslateY;
174     };
175 
176     if (useCache) {
177         // We require the upper left 2x2 of the matrix to match exactly for a cache hit.
178         SkScalar sx = args.fViewMatrix->get(SkMatrix::kMScaleX);
179         SkScalar sy = args.fViewMatrix->get(SkMatrix::kMScaleY);
180         SkScalar kx = args.fViewMatrix->get(SkMatrix::kMSkewX);
181         SkScalar ky = args.fViewMatrix->get(SkMatrix::kMSkewY);
182         SkScalar tx = args.fViewMatrix->get(SkMatrix::kMTransX);
183         SkScalar ty = args.fViewMatrix->get(SkMatrix::kMTransY);
184         // Allow 8 bits each in x and y of subpixel positioning.
185         SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00;
186         SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00;
187         static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
188         GrUniqueKey::Builder builder(&maskKey, kDomain, 5 + args.fShape->unstyledKeySize());
189         builder[0] = SkFloat2Bits(sx);
190         builder[1] = SkFloat2Bits(sy);
191         builder[2] = SkFloat2Bits(kx);
192         builder[3] = SkFloat2Bits(ky);
193         builder[4] = fracX | (fracY >> 8);
194         args.fShape->writeUnstyledKey(&builder[5]);
195     }
196 
197     sk_sp<GrTexture> texture;
198     if (useCache) {
199         texture.reset(args.fResourceProvider->findAndRefTextureByUniqueKey(maskKey));
200     }
201     if (!texture) {
202          GrSWMaskHelper::TextureType type = useCache ? GrSWMaskHelper::TextureType::kExactFit
203                                                      : GrSWMaskHelper::TextureType::kApproximateFit;
204          texture.reset(GrSWMaskHelper::DrawShapeMaskToTexture(fTexProvider, *args.fShape,
205                                                               *boundsForMask, args.fAntiAlias,
206                                                               type, args.fViewMatrix));
207          if (!texture) {
208              return false;
209          }
210          if (useCache) {
211              texture->resourcePriv().setUniqueKey(maskKey);
212          }
213     }
214 
215     GrSWMaskHelper::DrawToTargetWithShapeMask(texture.get(), args.fDrawContext, *args.fPaint,
216                                               *args.fUserStencilSettings,
217                                               *args.fClip, *args.fViewMatrix,
218                                               SkIPoint {boundsForMask->fLeft, boundsForMask->fTop},
219                                               *boundsForMask);
220 
221     if (inverseFilled) {
222         DrawAroundInvPath(args.fDrawContext, *args.fPaint, *args.fUserStencilSettings,
223                           *args.fClip,
224                           *args.fViewMatrix, devClipBounds, unclippedDevShapeBounds);
225     }
226 
227     return true;
228 }
229