OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "GrTessellatingPathRenderer.h" | 8 #include "GrTessellatingPathRenderer.h" |
9 | 9 |
10 #include "GrBatchFlushState.h" | 10 #include "GrBatchFlushState.h" |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
98 }; | 98 }; |
99 | 99 |
100 } // namespace | 100 } // namespace |
101 | 101 |
102 GrTessellatingPathRenderer::GrTessellatingPathRenderer() { | 102 GrTessellatingPathRenderer::GrTessellatingPathRenderer() { |
103 } | 103 } |
104 | 104 |
105 bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) cons
t { | 105 bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) cons
t { |
106 // This path renderer can draw all fill styles, all stroke styles except hai
rlines, but does | 106 // This path renderer can draw all fill styles, all stroke styles except hai
rlines, but does |
107 // not do antialiasing. It can do convex and concave paths, but we'll leave
the convex ones to | 107 // not do antialiasing. It can do convex and concave paths, but we'll leave
the convex ones to |
108 // simpler algorithms. Similary, we skip the non-hairlines that can be treat
ed as hairline. | 108 // simpler algorithms. |
109 // An arbitrary path effect could produce a hairline result so we pass on th
ose. | 109 return !IsStrokeHairlineOrEquivalent(*args.fStroke, *args.fViewMatrix, nullp
tr) && |
110 return !IsStrokeHairlineOrEquivalent(*args.fStyle, *args.fViewMatrix, nullpt
r) && | 110 !args.fAntiAlias && !args.fPath->isConvex(); |
111 !args.fStyle->hasNonDashPathEffect() && !args.fAntiAlias && !args.fPa
th->isConvex(); | |
112 } | 111 } |
113 | 112 |
114 class TessellatingPathBatch : public GrVertexBatch { | 113 class TessellatingPathBatch : public GrVertexBatch { |
115 public: | 114 public: |
116 DEFINE_BATCH_CLASS_ID | 115 DEFINE_BATCH_CLASS_ID |
117 | 116 |
118 static GrDrawBatch* Create(const GrColor& color, | 117 static GrDrawBatch* Create(const GrColor& color, |
119 const SkPath& path, | 118 const SkPath& path, |
120 const GrStyle& style, | 119 const GrStrokeInfo& stroke, |
121 const SkMatrix& viewMatrix, | 120 const SkMatrix& viewMatrix, |
122 SkRect clipBounds) { | 121 SkRect clipBounds) { |
123 return new TessellatingPathBatch(color, path, style, viewMatrix, clipBou
nds); | 122 return new TessellatingPathBatch(color, path, stroke, viewMatrix, clipBo
unds); |
124 } | 123 } |
125 | 124 |
126 const char* name() const override { return "TessellatingPathBatch"; } | 125 const char* name() const override { return "TessellatingPathBatch"; } |
127 | 126 |
128 void computePipelineOptimizations(GrInitInvariantOutput* color, | 127 void computePipelineOptimizations(GrInitInvariantOutput* color, |
129 GrInitInvariantOutput* coverage, | 128 GrInitInvariantOutput* coverage, |
130 GrBatchToXPOverrides* overrides) const ove
rride { | 129 GrBatchToXPOverrides* overrides) const ove
rride { |
131 color->setKnownFourComponents(fColor); | 130 color->setKnownFourComponents(fColor); |
132 coverage->setUnknownSingleComponent(); | 131 coverage->setUnknownSingleComponent(); |
133 } | 132 } |
134 | 133 |
135 private: | 134 private: |
136 void initBatchTracker(const GrXPOverridesForBatch& overrides) override { | 135 void initBatchTracker(const GrXPOverridesForBatch& overrides) override { |
137 // Handle any color overrides | 136 // Handle any color overrides |
138 if (!overrides.readsColor()) { | 137 if (!overrides.readsColor()) { |
139 fColor = GrColor_ILLEGAL; | 138 fColor = GrColor_ILLEGAL; |
140 } | 139 } |
141 overrides.getOverrideColorIfSet(&fColor); | 140 overrides.getOverrideColorIfSet(&fColor); |
142 fPipelineInfo = overrides; | 141 fPipelineInfo = overrides; |
143 } | 142 } |
144 | 143 |
145 void draw(Target* target, const GrGeometryProcessor* gp) const { | 144 void draw(Target* target, const GrGeometryProcessor* gp) const { |
146 GrResourceProvider* rp = target->resourceProvider(); | |
147 SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance; | |
148 SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMat
rix, | |
149 fPath.getBounds()); | |
150 | |
151 SkScalar styleScale = SK_Scalar1; | |
152 if (fStyle.applies()) { | |
153 styleScale = GrStyle::MatrixToScaleFactor(fViewMatrix); | |
154 } | |
155 | |
156 // construct a cache key from the path's genID and the view matrix | 145 // construct a cache key from the path's genID and the view matrix |
157 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain()
; | 146 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain()
; |
158 GrUniqueKey key; | 147 GrUniqueKey key; |
159 int clipBoundsCnt = | 148 int clipBoundsSize32 = |
160 fPath.isInverseFillType() ? sizeof(fClipBounds) / sizeof(uint32_t) :
0; | 149 fPath.isInverseFillType() ? sizeof(fClipBounds) / sizeof(uint32_t) :
0; |
161 int styleDataCnt = GrStyle::KeySize(fStyle, GrStyle::Apply::kPathEffectA
ndStrokeRec); | 150 int strokeDataSize32 = fStroke.computeUniqueKeyFragmentData32Cnt(); |
162 if (styleDataCnt >= 0) { | 151 GrUniqueKey::Builder builder(&key, kDomain, 2 + clipBoundsSize32 + strok
eDataSize32); |
163 GrUniqueKey::Builder builder(&key, kDomain, 2 + clipBoundsCnt + styl
eDataCnt); | 152 builder[0] = fPath.getGenerationID(); |
164 builder[0] = fPath.getGenerationID(); | 153 builder[1] = fPath.getFillType(); |
165 builder[1] = fPath.getFillType(); | 154 // For inverse fills, the tessellation is dependent on clip bounds. |
166 // For inverse fills, the tessellation is dependent on clip bounds. | 155 if (fPath.isInverseFillType()) { |
167 if (fPath.isInverseFillType()) { | 156 memcpy(&builder[2], &fClipBounds, sizeof(fClipBounds)); |
168 memcpy(&builder[2], &fClipBounds, sizeof(fClipBounds)); | 157 } |
169 } | 158 fStroke.asUniqueKeyFragment(&builder[2 + clipBoundsSize32]); |
170 if (styleDataCnt) { | 159 builder.finish(); |
171 GrStyle::WriteKey(&builder[2 + clipBoundsCnt], fStyle, | 160 GrResourceProvider* rp = target->resourceProvider(); |
172 GrStyle::Apply::kPathEffectAndStrokeRec, style
Scale); | 161 SkAutoTUnref<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey<GrB
uffer>(key)); |
173 } | 162 int actualCount; |
174 builder.finish(); | 163 SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance; |
175 SkAutoTUnref<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey
<GrBuffer>(key)); | 164 SkScalar tol = GrPathUtils::scaleToleranceToSrc( |
176 int actualCount; | 165 screenSpaceTol, fViewMatrix, fPath.getBounds()); |
177 if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) { | 166 if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) { |
178 this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actu
alCount); | 167 this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCo
unt); |
179 return; | 168 return; |
180 } | |
181 } | 169 } |
182 | 170 |
183 SkPath path; | 171 SkPath path; |
184 if (fStyle.applies()) { | 172 GrStrokeInfo stroke(fStroke); |
185 SkStrokeRec::InitStyle fill; | 173 if (stroke.isDashed()) { |
186 SkAssertResult(fStyle.applyToPath(&path, &fill, fPath, styleScale)); | 174 if (!stroke.applyDashToPath(&path, &stroke, fPath)) { |
187 SkASSERT(SkStrokeRec::kFill_InitStyle == fill); | 175 return; |
| 176 } |
188 } else { | 177 } else { |
189 path = fPath; | 178 path = fPath; |
190 } | 179 } |
| 180 if (!stroke.isFillStyle()) { |
| 181 stroke.setResScale(SkScalarAbs(fViewMatrix.getMaxScale())); |
| 182 if (!stroke.applyToPath(&path, path)) { |
| 183 return; |
| 184 } |
| 185 stroke.setFillStyle(); |
| 186 } |
191 bool isLinear; | 187 bool isLinear; |
192 bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags(
); | 188 bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags(
); |
193 StaticVertexAllocator allocator(rp, canMapVB); | 189 StaticVertexAllocator allocator(rp, canMapVB); |
194 int count = GrTessellator::PathToTriangles(path, tol, fClipBounds, &allo
cator, &isLinear); | 190 int count = GrTessellator::PathToTriangles(path, tol, fClipBounds, &allo
cator, &isLinear); |
195 if (count == 0) { | 191 if (count == 0) { |
196 return; | 192 return; |
197 } | 193 } |
198 this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count); | 194 this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count); |
199 if (!fPath.isVolatile() && styleDataCnt >= 0) { | 195 if (!fPath.isVolatile()) { |
200 TessInfo info; | 196 TessInfo info; |
201 info.fTolerance = isLinear ? 0 : tol; | 197 info.fTolerance = isLinear ? 0 : tol; |
202 info.fCount = count; | 198 info.fCount = count; |
203 SkAutoTUnref<SkData> data(SkData::NewWithCopy(&info, sizeof(info))); | 199 SkAutoTUnref<SkData> data(SkData::NewWithCopy(&info, sizeof(info))); |
204 key.setCustomData(data.get()); | 200 key.setCustomData(data.get()); |
205 rp->assignUniqueKeyToResource(key, allocator.vertexBuffer()); | 201 rp->assignUniqueKeyToResource(key, allocator.vertexBuffer()); |
206 SkPathPriv::AddGenIDChangeListener(fPath, new PathInvalidator(key)); | 202 SkPathPriv::AddGenIDChangeListener(fPath, new PathInvalidator(key)); |
207 } | 203 } |
208 } | 204 } |
209 | 205 |
(...skipping 27 matching lines...) Expand all Loading... |
237 : kTriangles_GrPri
mitiveType; | 233 : kTriangles_GrPri
mitiveType; |
238 GrMesh mesh; | 234 GrMesh mesh; |
239 mesh.init(primitiveType, vb, firstVertex, count); | 235 mesh.init(primitiveType, vb, firstVertex, count); |
240 target->draw(gp, mesh); | 236 target->draw(gp, mesh); |
241 } | 237 } |
242 | 238 |
243 bool onCombineIfPossible(GrBatch*, const GrCaps&) override { return false; } | 239 bool onCombineIfPossible(GrBatch*, const GrCaps&) override { return false; } |
244 | 240 |
245 TessellatingPathBatch(const GrColor& color, | 241 TessellatingPathBatch(const GrColor& color, |
246 const SkPath& path, | 242 const SkPath& path, |
247 const GrStyle& style, | 243 const GrStrokeInfo& stroke, |
248 const SkMatrix& viewMatrix, | 244 const SkMatrix& viewMatrix, |
249 const SkRect& clipBounds) | 245 const SkRect& clipBounds) |
250 : INHERITED(ClassID()) | 246 : INHERITED(ClassID()) |
251 , fColor(color) | 247 , fColor(color) |
252 , fPath(path) | 248 , fPath(path) |
253 , fStyle(style) | 249 , fStroke(stroke) |
254 , fViewMatrix(viewMatrix) { | 250 , fViewMatrix(viewMatrix) { |
255 const SkRect& pathBounds = path.getBounds(); | 251 const SkRect& pathBounds = path.getBounds(); |
256 fClipBounds = clipBounds; | 252 fClipBounds = clipBounds; |
257 // Because the clip bounds are used to add a contour for inverse fills,
they must also | 253 // Because the clip bounds are used to add a contour for inverse fills,
they must also |
258 // include the path bounds. | 254 // include the path bounds. |
259 fClipBounds.join(pathBounds); | 255 fClipBounds.join(pathBounds); |
260 if (path.isInverseFillType()) { | 256 if (path.isInverseFillType()) { |
261 fBounds = fClipBounds; | 257 fBounds = fClipBounds; |
262 } else { | 258 } else { |
263 fBounds = path.getBounds(); | 259 fBounds = path.getBounds(); |
264 } | 260 } |
265 style.adjustBounds(&fBounds, fBounds); | 261 SkScalar radius = stroke.getInflationRadius(); |
| 262 fBounds.outset(radius, radius); |
266 viewMatrix.mapRect(&fBounds); | 263 viewMatrix.mapRect(&fBounds); |
267 } | 264 } |
268 | 265 |
269 GrColor fColor; | 266 GrColor fColor; |
270 SkPath fPath; | 267 SkPath fPath; |
271 GrStyle fStyle; | 268 GrStrokeInfo fStroke; |
272 SkMatrix fViewMatrix; | 269 SkMatrix fViewMatrix; |
273 SkRect fClipBounds; // in source space | 270 SkRect fClipBounds; // in source space |
274 GrXPOverridesForBatch fPipelineInfo; | 271 GrXPOverridesForBatch fPipelineInfo; |
275 | 272 |
276 typedef GrVertexBatch INHERITED; | 273 typedef GrVertexBatch INHERITED; |
277 }; | 274 }; |
278 | 275 |
279 bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) { | 276 bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) { |
280 GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), | 277 GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), |
281 "GrTessellatingPathRenderer::onDrawPath"); | 278 "GrTessellatingPathRenderer::onDrawPath"); |
282 SkASSERT(!args.fAntiAlias); | 279 SkASSERT(!args.fAntiAlias); |
283 const GrRenderTarget* rt = args.fPipelineBuilder->getRenderTarget(); | 280 const GrRenderTarget* rt = args.fPipelineBuilder->getRenderTarget(); |
284 if (nullptr == rt) { | 281 if (nullptr == rt) { |
285 return false; | 282 return false; |
286 } | 283 } |
287 | 284 |
288 SkIRect clipBoundsI; | 285 SkIRect clipBoundsI; |
289 args.fPipelineBuilder->clip().getConservativeBounds(rt->width(), rt->height(
), &clipBoundsI); | 286 args.fPipelineBuilder->clip().getConservativeBounds(rt->width(), rt->height(
), &clipBoundsI); |
290 SkRect clipBounds = SkRect::Make(clipBoundsI); | 287 SkRect clipBounds = SkRect::Make(clipBoundsI); |
291 SkMatrix vmi; | 288 SkMatrix vmi; |
292 if (!args.fViewMatrix->invert(&vmi)) { | 289 if (!args.fViewMatrix->invert(&vmi)) { |
293 return false; | 290 return false; |
294 } | 291 } |
295 vmi.mapRect(&clipBounds); | 292 vmi.mapRect(&clipBounds); |
296 SkAutoTUnref<GrDrawBatch> batch(TessellatingPathBatch::Create(args.fColor, *
args.fPath, | 293 SkAutoTUnref<GrDrawBatch> batch(TessellatingPathBatch::Create(args.fColor, *
args.fPath, |
297 *args.fStyle,
*args.fViewMatrix, | 294 *args.fStroke,
*args.fViewMatrix, |
298 clipBounds)); | 295 clipBounds)); |
299 args.fTarget->drawBatch(*args.fPipelineBuilder, batch); | 296 args.fTarget->drawBatch(*args.fPipelineBuilder, batch); |
300 | 297 |
301 return true; | 298 return true; |
302 } | 299 } |
303 | 300 |
304 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 301 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
305 | 302 |
306 #ifdef GR_TEST_UTILS | 303 #ifdef GR_TEST_UTILS |
307 | 304 |
308 DRAW_BATCH_TEST_DEFINE(TesselatingPathBatch) { | 305 DRAW_BATCH_TEST_DEFINE(TesselatingPathBatch) { |
309 GrColor color = GrRandomColor(random); | 306 GrColor color = GrRandomColor(random); |
310 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); | 307 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); |
311 SkPath path = GrTest::TestPath(random); | 308 SkPath path = GrTest::TestPath(random); |
312 SkRect clipBounds = GrTest::TestRect(random); | 309 SkRect clipBounds = GrTest::TestRect(random); |
313 SkMatrix vmi; | 310 SkMatrix vmi; |
314 bool result = viewMatrix.invert(&vmi); | 311 bool result = viewMatrix.invert(&vmi); |
315 if (!result) { | 312 if (!result) { |
316 SkFAIL("Cannot invert matrix\n"); | 313 SkFAIL("Cannot invert matrix\n"); |
317 } | 314 } |
318 vmi.mapRect(&clipBounds); | 315 vmi.mapRect(&clipBounds); |
319 GrStyle style; | 316 GrStrokeInfo strokeInfo = GrTest::TestStrokeInfo(random); |
320 do { | 317 return TessellatingPathBatch::Create(color, path, strokeInfo, viewMatrix, cl
ipBounds); |
321 GrTest::TestStyle(random, &style); | |
322 } while (style.strokeRec().isHairlineStyle()); | |
323 return TessellatingPathBatch::Create(color, path, style, viewMatrix, clipBou
nds); | |
324 } | 318 } |
325 | 319 |
326 #endif | 320 #endif |
OLD | NEW |