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