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