OLD | NEW |
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2012 Google Inc. | 3 * Copyright 2012 Google Inc. |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 #include "GrAAConvexPathRenderer.h" | 9 #include "GrAAConvexPathRenderer.h" |
10 | 10 |
| 11 #include "GrBatch.h" |
| 12 #include "GrBatchTarget.h" |
| 13 #include "GrBufferAllocPool.h" |
11 #include "GrContext.h" | 14 #include "GrContext.h" |
12 #include "GrDrawTargetCaps.h" | 15 #include "GrDrawTargetCaps.h" |
13 #include "GrGeometryProcessor.h" | 16 #include "GrGeometryProcessor.h" |
14 #include "GrInvariantOutput.h" | 17 #include "GrInvariantOutput.h" |
15 #include "GrPathUtils.h" | 18 #include "GrPathUtils.h" |
16 #include "GrProcessor.h" | 19 #include "GrProcessor.h" |
17 #include "GrPipelineBuilder.h" | 20 #include "GrPipelineBuilder.h" |
18 #include "SkGeometry.h" | 21 #include "SkGeometry.h" |
19 #include "SkString.h" | 22 #include "SkString.h" |
20 #include "SkStrokeRec.h" | 23 #include "SkStrokeRec.h" |
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
215 SkASSERT(!m.hasPerspective()); | 218 SkASSERT(!m.hasPerspective()); |
216 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMS
caleY)) - | 219 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMS
caleY)) - |
217 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSk
ewY)); | 220 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSk
ewY)); |
218 if (det2x2 < 0) { | 221 if (det2x2 < 0) { |
219 *dir = SkPath::OppositeDirection(*dir); | 222 *dir = SkPath::OppositeDirection(*dir); |
220 } | 223 } |
221 return true; | 224 return true; |
222 } | 225 } |
223 | 226 |
224 static inline void add_line_to_segment(const SkPoint& pt, | 227 static inline void add_line_to_segment(const SkPoint& pt, |
225 SegmentArray* segments, | 228 SegmentArray* segments) { |
226 SkRect* devBounds) { | |
227 segments->push_back(); | 229 segments->push_back(); |
228 segments->back().fType = Segment::kLine; | 230 segments->back().fType = Segment::kLine; |
229 segments->back().fPts[0] = pt; | 231 segments->back().fPts[0] = pt; |
230 devBounds->growToInclude(pt.fX, pt.fY); | |
231 } | 232 } |
232 | 233 |
233 #ifdef SK_DEBUG | |
234 static inline bool contains_inclusive(const SkRect& rect, const SkPoint& p) { | |
235 return p.fX >= rect.fLeft && p.fX <= rect.fRight && p.fY >= rect.fTop && p.f
Y <= rect.fBottom; | |
236 } | |
237 #endif | |
238 | |
239 static inline void add_quad_segment(const SkPoint pts[3], | 234 static inline void add_quad_segment(const SkPoint pts[3], |
240 SegmentArray* segments, | 235 SegmentArray* segments) { |
241 SkRect* devBounds) { | |
242 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2])
< kCloseSqd) { | 236 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2])
< kCloseSqd) { |
243 if (pts[0] != pts[2]) { | 237 if (pts[0] != pts[2]) { |
244 add_line_to_segment(pts[2], segments, devBounds); | 238 add_line_to_segment(pts[2], segments); |
245 } | 239 } |
246 } else { | 240 } else { |
247 segments->push_back(); | 241 segments->push_back(); |
248 segments->back().fType = Segment::kQuad; | 242 segments->back().fType = Segment::kQuad; |
249 segments->back().fPts[0] = pts[1]; | 243 segments->back().fPts[0] = pts[1]; |
250 segments->back().fPts[1] = pts[2]; | 244 segments->back().fPts[1] = pts[2]; |
251 SkASSERT(contains_inclusive(*devBounds, pts[0])); | |
252 devBounds->growToInclude(pts + 1, 2); | |
253 } | 245 } |
254 } | 246 } |
255 | 247 |
256 static inline void add_cubic_segments(const SkPoint pts[4], | 248 static inline void add_cubic_segments(const SkPoint pts[4], |
257 SkPath::Direction dir, | 249 SkPath::Direction dir, |
258 SegmentArray* segments, | 250 SegmentArray* segments) { |
259 SkRect* devBounds) { | |
260 SkSTArray<15, SkPoint, true> quads; | 251 SkSTArray<15, SkPoint, true> quads; |
261 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads); | 252 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads); |
262 int count = quads.count(); | 253 int count = quads.count(); |
263 for (int q = 0; q < count; q += 3) { | 254 for (int q = 0; q < count; q += 3) { |
264 add_quad_segment(&quads[q], segments, devBounds); | 255 add_quad_segment(&quads[q], segments); |
265 } | 256 } |
266 } | 257 } |
267 | 258 |
268 static bool get_segments(const SkPath& path, | 259 static bool get_segments(const SkPath& path, |
269 const SkMatrix& m, | 260 const SkMatrix& m, |
270 SegmentArray* segments, | 261 SegmentArray* segments, |
271 SkPoint* fanPt, | 262 SkPoint* fanPt, |
272 int* vCount, | 263 int* vCount, |
273 int* iCount, | 264 int* iCount) { |
274 SkRect* devBounds) { | |
275 SkPath::Iter iter(path, true); | 265 SkPath::Iter iter(path, true); |
276 // This renderer over-emphasizes very thin path regions. We use the distance | 266 // This renderer over-emphasizes very thin path regions. We use the distance |
277 // to the path from the sample to compute coverage. Every pixel intersected | 267 // to the path from the sample to compute coverage. Every pixel intersected |
278 // by the path will be hit and the maximum distance is sqrt(2)/2. We don't | 268 // by the path will be hit and the maximum distance is sqrt(2)/2. We don't |
279 // notice that the sample may be close to a very thin area of the path and | 269 // notice that the sample may be close to a very thin area of the path and |
280 // thus should be very light. This is particularly egregious for degenerate | 270 // thus should be very light. This is particularly egregious for degenerate |
281 // line paths. We detect paths that are very close to a line (zero area) and | 271 // line paths. We detect paths that are very close to a line (zero area) and |
282 // draw nothing. | 272 // draw nothing. |
283 DegenerateTestData degenerateData; | 273 DegenerateTestData degenerateData; |
284 SkPath::Direction dir; | 274 SkPath::Direction dir; |
285 // get_direction can fail for some degenerate paths. | 275 // get_direction can fail for some degenerate paths. |
286 if (!get_direction(path, m, &dir)) { | 276 if (!get_direction(path, m, &dir)) { |
287 return false; | 277 return false; |
288 } | 278 } |
289 | 279 |
290 for (;;) { | 280 for (;;) { |
291 SkPoint pts[4]; | 281 SkPoint pts[4]; |
292 SkPath::Verb verb = iter.next(pts); | 282 SkPath::Verb verb = iter.next(pts); |
293 switch (verb) { | 283 switch (verb) { |
294 case SkPath::kMove_Verb: | 284 case SkPath::kMove_Verb: |
295 m.mapPoints(pts, 1); | 285 m.mapPoints(pts, 1); |
296 update_degenerate_test(°enerateData, pts[0]); | 286 update_degenerate_test(°enerateData, pts[0]); |
297 devBounds->set(pts->fX, pts->fY, pts->fX, pts->fY); | |
298 break; | 287 break; |
299 case SkPath::kLine_Verb: { | 288 case SkPath::kLine_Verb: { |
300 m.mapPoints(&pts[1], 1); | 289 m.mapPoints(&pts[1], 1); |
301 update_degenerate_test(°enerateData, pts[1]); | 290 update_degenerate_test(°enerateData, pts[1]); |
302 add_line_to_segment(pts[1], segments, devBounds); | 291 add_line_to_segment(pts[1], segments); |
303 break; | 292 break; |
304 } | 293 } |
305 case SkPath::kQuad_Verb: | 294 case SkPath::kQuad_Verb: |
306 m.mapPoints(pts, 3); | 295 m.mapPoints(pts, 3); |
307 update_degenerate_test(°enerateData, pts[1]); | 296 update_degenerate_test(°enerateData, pts[1]); |
308 update_degenerate_test(°enerateData, pts[2]); | 297 update_degenerate_test(°enerateData, pts[2]); |
309 add_quad_segment(pts, segments, devBounds); | 298 add_quad_segment(pts, segments); |
310 break; | 299 break; |
311 case SkPath::kConic_Verb: { | 300 case SkPath::kConic_Verb: { |
312 m.mapPoints(pts, 3); | 301 m.mapPoints(pts, 3); |
313 SkScalar weight = iter.conicWeight(); | 302 SkScalar weight = iter.conicWeight(); |
314 SkAutoConicToQuads converter; | 303 SkAutoConicToQuads converter; |
315 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5
f); | 304 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5
f); |
316 for (int i = 0; i < converter.countQuads(); ++i) { | 305 for (int i = 0; i < converter.countQuads(); ++i) { |
317 update_degenerate_test(°enerateData, quadPts[2*i + 1]); | 306 update_degenerate_test(°enerateData, quadPts[2*i + 1]); |
318 update_degenerate_test(°enerateData, quadPts[2*i + 2]); | 307 update_degenerate_test(°enerateData, quadPts[2*i + 2]); |
319 add_quad_segment(quadPts + 2*i, segments, devBounds); | 308 add_quad_segment(quadPts + 2*i, segments); |
320 } | 309 } |
321 break; | 310 break; |
322 } | 311 } |
323 case SkPath::kCubic_Verb: { | 312 case SkPath::kCubic_Verb: { |
324 m.mapPoints(pts, 4); | 313 m.mapPoints(pts, 4); |
325 update_degenerate_test(°enerateData, pts[1]); | 314 update_degenerate_test(°enerateData, pts[1]); |
326 update_degenerate_test(°enerateData, pts[2]); | 315 update_degenerate_test(°enerateData, pts[2]); |
327 update_degenerate_test(°enerateData, pts[3]); | 316 update_degenerate_test(°enerateData, pts[3]); |
328 add_cubic_segments(pts, dir, segments, devBounds); | 317 add_cubic_segments(pts, dir, segments); |
329 break; | 318 break; |
330 }; | 319 }; |
331 case SkPath::kDone_Verb: | 320 case SkPath::kDone_Verb: |
332 if (degenerateData.isDegenerate()) { | 321 if (degenerateData.isDegenerate()) { |
333 return false; | 322 return false; |
334 } else { | 323 } else { |
335 compute_vectors(segments, fanPt, dir, vCount, iCount); | 324 compute_vectors(segments, fanPt, dir, vCount, iCount); |
336 return true; | 325 return true; |
337 } | 326 } |
338 default: | 327 default: |
(...skipping 357 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
696 bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget* target, | 685 bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget* target, |
697 const GrPipelineBuilder*, | 686 const GrPipelineBuilder*, |
698 const SkMatrix& viewMatrix, | 687 const SkMatrix& viewMatrix, |
699 const SkPath& path, | 688 const SkPath& path, |
700 const SkStrokeRec& stroke, | 689 const SkStrokeRec& stroke, |
701 bool antiAlias) const { | 690 bool antiAlias) const { |
702 return (target->caps()->shaderDerivativeSupport() && antiAlias && | 691 return (target->caps()->shaderDerivativeSupport() && antiAlias && |
703 stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex()
); | 692 stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex()
); |
704 } | 693 } |
705 | 694 |
| 695 class AAConvexPathBatch : public GrBatch { |
| 696 public: |
| 697 struct Geometry { |
| 698 GrColor fColor; |
| 699 SkMatrix fViewMatrix; |
| 700 SkPath fPath; |
| 701 SkDEBUGCODE(SkRect fDevBounds;) |
| 702 }; |
| 703 |
| 704 static GrBatch* Create(const Geometry& geometry) { |
| 705 return SkNEW_ARGS(AAConvexPathBatch, (geometry)); |
| 706 } |
| 707 |
| 708 const char* name() const SK_OVERRIDE { return "AAConvexBatch"; } |
| 709 |
| 710 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE { |
| 711 // When this is called on a batch, there is only one geometry bundle |
| 712 out->setKnownFourComponents(fGeoData[0].fColor); |
| 713 } |
| 714 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRID
E { |
| 715 out->setUnknownSingleComponent(); |
| 716 } |
| 717 |
| 718 void initBatchOpt(const GrBatchOpt& batchOpt) { |
| 719 fBatchOpt = batchOpt; |
| 720 } |
| 721 |
| 722 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE { |
| 723 // Handle any color overrides |
| 724 if (init.fColorIgnored) { |
| 725 fGeoData[0].fColor = GrColor_ILLEGAL; |
| 726 } else if (GrColor_ILLEGAL != init.fOverrideColor) { |
| 727 fGeoData[0].fColor = init.fOverrideColor; |
| 728 } |
| 729 |
| 730 // setup batch properties |
| 731 fBatch.fColorIgnored = init.fColorIgnored; |
| 732 fBatch.fColor = fGeoData[0].fColor; |
| 733 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; |
| 734 fBatch.fCoverageIgnored = init.fCoverageIgnored; |
| 735 } |
| 736 |
| 737 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline
) SK_OVERRIDE { |
| 738 int instanceCount = fGeoData.count(); |
| 739 |
| 740 SkMatrix invert; |
| 741 if (!this->viewMatrix().invert(&invert)) { |
| 742 SkDebugf("Could not invert viewmatrix\n"); |
| 743 return; |
| 744 } |
| 745 |
| 746 // Setup GrGeometryProcessor |
| 747 SkAutoTUnref<GrGeometryProcessor> quadProcessor(QuadEdgeEffect::Create(t
his->color(), |
| 748 i
nvert)); |
| 749 |
| 750 batchTarget->initDraw(quadProcessor, pipeline); |
| 751 |
| 752 // TODO remove this when batch is everywhere |
| 753 GrPipelineInfo init; |
| 754 init.fColorIgnored = fBatch.fColorIgnored; |
| 755 init.fOverrideColor = GrColor_ILLEGAL; |
| 756 init.fCoverageIgnored = fBatch.fCoverageIgnored; |
| 757 init.fUsesLocalCoords = this->usesLocalCoords(); |
| 758 quadProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init
); |
| 759 |
| 760 // TODO generate all segments for all paths and use one vertex buffer |
| 761 for (int i = 0; i < instanceCount; i++) { |
| 762 Geometry& args = fGeoData[i]; |
| 763 |
| 764 // We use the fact that SkPath::transform path does subdivision base
d on |
| 765 // perspective. Otherwise, we apply the view matrix when copying to
the |
| 766 // segment representation. |
| 767 const SkMatrix* viewMatrix = &args.fViewMatrix; |
| 768 if (viewMatrix->hasPerspective()) { |
| 769 args.fPath.transform(*viewMatrix); |
| 770 viewMatrix = &SkMatrix::I(); |
| 771 } |
| 772 |
| 773 int vertexCount; |
| 774 int indexCount; |
| 775 enum { |
| 776 kPreallocSegmentCnt = 512 / sizeof(Segment), |
| 777 kPreallocDrawCnt = 4, |
| 778 }; |
| 779 SkSTArray<kPreallocSegmentCnt, Segment, true> segments; |
| 780 SkPoint fanPt; |
| 781 |
| 782 if (!get_segments(args.fPath, *viewMatrix, &segments, &fanPt, &verte
xCount, |
| 783 &indexCount)) { |
| 784 continue; |
| 785 } |
| 786 |
| 787 const GrVertexBuffer* vertexBuffer; |
| 788 int firstVertex; |
| 789 |
| 790 size_t vertexStride = quadProcessor->getVertexStride(); |
| 791 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride, |
| 792 vertexCount, |
| 793 &vertexBuffer, |
| 794 &firstVertex); |
| 795 |
| 796 const GrIndexBuffer* indexBuffer; |
| 797 int firstIndex; |
| 798 |
| 799 void *indices = batchTarget->indexPool()->makeSpace(indexCount, |
| 800 &indexBuffer, |
| 801 &firstIndex); |
| 802 |
| 803 QuadVertex* verts = reinterpret_cast<QuadVertex*>(vertices); |
| 804 uint16_t* idxs = reinterpret_cast<uint16_t*>(indices); |
| 805 |
| 806 SkSTArray<kPreallocDrawCnt, Draw, true> draws; |
| 807 create_vertices(segments, fanPt, &draws, verts, idxs); |
| 808 |
| 809 #ifdef SK_DEBUG |
| 810 // Check devBounds |
| 811 SkRect actualBounds; |
| 812 actualBounds.set(verts[0].fPos, verts[1].fPos); |
| 813 for (int i = 2; i < vertexCount; ++i) { |
| 814 actualBounds.growToInclude(verts[i].fPos.fX, verts[i].fPos.fY); |
| 815 } |
| 816 SkASSERT(args.fDevBounds.contains(actualBounds)); |
| 817 #endif |
| 818 |
| 819 GrDrawTarget::DrawInfo info; |
| 820 info.setVertexBuffer(vertexBuffer); |
| 821 info.setIndexBuffer(indexBuffer); |
| 822 info.setPrimitiveType(kTriangles_GrPrimitiveType); |
| 823 info.setStartIndex(firstIndex); |
| 824 |
| 825 int vOffset = 0; |
| 826 for (int i = 0; i < draws.count(); ++i) { |
| 827 const Draw& draw = draws[i]; |
| 828 info.setStartVertex(vOffset + firstVertex); |
| 829 info.setVertexCount(draw.fVertexCnt); |
| 830 info.setIndexCount(draw.fIndexCnt); |
| 831 batchTarget->draw(info); |
| 832 vOffset += draw.fVertexCnt; |
| 833 } |
| 834 } |
| 835 } |
| 836 |
| 837 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } |
| 838 |
| 839 private: |
| 840 AAConvexPathBatch(const Geometry& geometry) { |
| 841 this->initClassID<AAConvexPathBatch>(); |
| 842 fGeoData.push_back(geometry); |
| 843 } |
| 844 |
| 845 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE { |
| 846 AAConvexPathBatch* that = t->cast<AAConvexPathBatch>(); |
| 847 |
| 848 if (this->color() != that->color()) { |
| 849 return false; |
| 850 } |
| 851 |
| 852 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); |
| 853 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->vi
ewMatrix())) { |
| 854 return false; |
| 855 } |
| 856 |
| 857 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin())
; |
| 858 return true; |
| 859 } |
| 860 |
| 861 GrColor color() const { return fBatch.fColor; } |
| 862 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } |
| 863 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } |
| 864 |
| 865 struct BatchTracker { |
| 866 GrColor fColor; |
| 867 bool fUsesLocalCoords; |
| 868 bool fColorIgnored; |
| 869 bool fCoverageIgnored; |
| 870 }; |
| 871 |
| 872 GrBatchOpt fBatchOpt; |
| 873 BatchTracker fBatch; |
| 874 SkSTArray<1, Geometry, true> fGeoData; |
| 875 }; |
| 876 |
706 bool GrAAConvexPathRenderer::onDrawPath(GrDrawTarget* target, | 877 bool GrAAConvexPathRenderer::onDrawPath(GrDrawTarget* target, |
707 GrPipelineBuilder* pipelineBuilder, | 878 GrPipelineBuilder* pipelineBuilder, |
708 GrColor color, | 879 GrColor color, |
709 const SkMatrix& vm, | 880 const SkMatrix& vm, |
710 const SkPath& origPath, | 881 const SkPath& path, |
711 const SkStrokeRec&, | 882 const SkStrokeRec&, |
712 bool antiAlias) { | 883 bool antiAlias) { |
713 | 884 if (path.isEmpty()) { |
714 const SkPath* path = &origPath; | |
715 if (path->isEmpty()) { | |
716 return true; | 885 return true; |
717 } | 886 } |
718 | 887 |
719 SkMatrix viewMatrix = vm; | 888 // We outset our vertices one pixel and add one more pixel for precision. |
720 SkMatrix invert; | 889 // TODO create tighter bounds when we start reordering. |
721 if (!viewMatrix.invert(&invert)) { | 890 SkRect devRect = path.getBounds(); |
722 return false; | 891 vm.mapRect(&devRect); |
723 } | 892 devRect.outset(2, 2); |
724 | 893 |
725 // We use the fact that SkPath::transform path does subdivision based on | 894 AAConvexPathBatch::Geometry geometry; |
726 // perspective. Otherwise, we apply the view matrix when copying to the | 895 geometry.fColor = color; |
727 // segment representation. | 896 geometry.fViewMatrix = vm; |
728 SkPath tmpPath; | 897 geometry.fPath = path; |
729 if (viewMatrix.hasPerspective()) { | 898 SkDEBUGCODE(geometry.fDevBounds = devRect;) |
730 origPath.transform(viewMatrix, &tmpPath); | 899 |
731 path = &tmpPath; | 900 SkAutoTUnref<GrBatch> batch(AAConvexPathBatch::Create(geometry)); |
732 viewMatrix = SkMatrix::I(); | 901 target->drawBatch(pipelineBuilder, batch, &devRect); |
733 } | |
734 | |
735 QuadVertex *verts; | |
736 uint16_t* idxs; | |
737 | |
738 int vCount; | |
739 int iCount; | |
740 enum { | |
741 kPreallocSegmentCnt = 512 / sizeof(Segment), | |
742 kPreallocDrawCnt = 4, | |
743 }; | |
744 SkSTArray<kPreallocSegmentCnt, Segment, true> segments; | |
745 SkPoint fanPt; | |
746 | |
747 // We can't simply use the path bounds because we may degenerate cubics to q
uads which produces | |
748 // new control points outside the original convex hull. | |
749 SkRect devBounds; | |
750 if (!get_segments(*path, viewMatrix, &segments, &fanPt, &vCount, &iCount, &d
evBounds)) { | |
751 return false; | |
752 } | |
753 | |
754 // Our computed verts should all be within one pixel of the segment control
points. | |
755 devBounds.outset(SK_Scalar1, SK_Scalar1); | |
756 | |
757 SkAutoTUnref<GrGeometryProcessor> quadProcessor(QuadEdgeEffect::Create(color
, invert)); | |
758 | |
759 GrDrawTarget::AutoReleaseGeometry arg(target, vCount, quadProcessor->getVert
exStride(), iCount); | |
760 SkASSERT(quadProcessor->getVertexStride() == sizeof(QuadVertex)); | |
761 if (!arg.succeeded()) { | |
762 return false; | |
763 } | |
764 verts = reinterpret_cast<QuadVertex*>(arg.vertices()); | |
765 idxs = reinterpret_cast<uint16_t*>(arg.indices()); | |
766 | |
767 SkSTArray<kPreallocDrawCnt, Draw, true> draws; | |
768 create_vertices(segments, fanPt, &draws, verts, idxs); | |
769 | |
770 // Check devBounds | |
771 #ifdef SK_DEBUG | |
772 SkRect tolDevBounds = devBounds; | |
773 tolDevBounds.outset(SK_Scalar1 / 10000, SK_Scalar1 / 10000); | |
774 SkRect actualBounds; | |
775 actualBounds.set(verts[0].fPos, verts[1].fPos); | |
776 for (int i = 2; i < vCount; ++i) { | |
777 actualBounds.growToInclude(verts[i].fPos.fX, verts[i].fPos.fY); | |
778 } | |
779 SkASSERT(tolDevBounds.contains(actualBounds)); | |
780 #endif | |
781 | |
782 int vOffset = 0; | |
783 for (int i = 0; i < draws.count(); ++i) { | |
784 const Draw& draw = draws[i]; | |
785 target->drawIndexed(pipelineBuilder, | |
786 quadProcessor, | |
787 kTriangles_GrPrimitiveType, | |
788 vOffset, // start vertex | |
789 0, // start index | |
790 draw.fVertexCnt, | |
791 draw.fIndexCnt, | |
792 &devBounds); | |
793 vOffset += draw.fVertexCnt; | |
794 } | |
795 | 902 |
796 return true; | 903 return true; |
| 904 |
797 } | 905 } |
OLD | NEW |