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 #ifdef SK_DEBUG |
234 static inline bool contains_inclusive(const SkRect& rect, const SkPoint& p) { | 235 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 return p.fX >= rect.fLeft && p.fX <= rect.fRight && p.fY >= rect.fTop && p.f Y <= rect.fBottom; |
236 } | 237 } |
237 #endif | 238 #endif |
238 | 239 |
239 static inline void add_quad_segment(const SkPoint pts[3], | 240 static inline void add_quad_segment(const SkPoint pts[3], |
240 SegmentArray* segments, | 241 SegmentArray* segments) { |
241 SkRect* devBounds) { | |
242 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) { | 242 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) { |
243 if (pts[0] != pts[2]) { | 243 if (pts[0] != pts[2]) { |
244 add_line_to_segment(pts[2], segments, devBounds); | 244 add_line_to_segment(pts[2], segments); |
245 } | 245 } |
246 } else { | 246 } else { |
247 segments->push_back(); | 247 segments->push_back(); |
248 segments->back().fType = Segment::kQuad; | 248 segments->back().fType = Segment::kQuad; |
249 segments->back().fPts[0] = pts[1]; | 249 segments->back().fPts[0] = pts[1]; |
250 segments->back().fPts[1] = pts[2]; | 250 segments->back().fPts[1] = pts[2]; |
251 SkASSERT(contains_inclusive(*devBounds, pts[0])); | |
252 devBounds->growToInclude(pts + 1, 2); | |
253 } | 251 } |
254 } | 252 } |
255 | 253 |
256 static inline void add_cubic_segments(const SkPoint pts[4], | 254 static inline void add_cubic_segments(const SkPoint pts[4], |
257 SkPath::Direction dir, | 255 SkPath::Direction dir, |
258 SegmentArray* segments, | 256 SegmentArray* segments) { |
259 SkRect* devBounds) { | |
260 SkSTArray<15, SkPoint, true> quads; | 257 SkSTArray<15, SkPoint, true> quads; |
261 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads); | 258 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads); |
262 int count = quads.count(); | 259 int count = quads.count(); |
263 for (int q = 0; q < count; q += 3) { | 260 for (int q = 0; q < count; q += 3) { |
264 add_quad_segment(&quads[q], segments, devBounds); | 261 add_quad_segment(&quads[q], segments); |
265 } | 262 } |
266 } | 263 } |
267 | 264 |
268 static bool get_segments(const SkPath& path, | 265 static bool get_segments(const SkPath& path, |
269 const SkMatrix& m, | 266 const SkMatrix& m, |
270 SegmentArray* segments, | 267 SegmentArray* segments, |
271 SkPoint* fanPt, | 268 SkPoint* fanPt, |
272 int* vCount, | 269 int* vCount, |
273 int* iCount, | 270 int* iCount) { |
274 SkRect* devBounds) { | |
275 SkPath::Iter iter(path, true); | 271 SkPath::Iter iter(path, true); |
276 // This renderer over-emphasizes very thin path regions. We use the distance | 272 // 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 | 273 // 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 | 274 // 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 | 275 // 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 | 276 // 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 | 277 // line paths. We detect paths that are very close to a line (zero area) and |
282 // draw nothing. | 278 // draw nothing. |
283 DegenerateTestData degenerateData; | 279 DegenerateTestData degenerateData; |
284 SkPath::Direction dir; | 280 SkPath::Direction dir; |
285 // get_direction can fail for some degenerate paths. | 281 // get_direction can fail for some degenerate paths. |
286 if (!get_direction(path, m, &dir)) { | 282 if (!get_direction(path, m, &dir)) { |
287 return false; | 283 return false; |
288 } | 284 } |
289 | 285 |
290 for (;;) { | 286 for (;;) { |
291 SkPoint pts[4]; | 287 SkPoint pts[4]; |
292 SkPath::Verb verb = iter.next(pts); | 288 SkPath::Verb verb = iter.next(pts); |
293 switch (verb) { | 289 switch (verb) { |
294 case SkPath::kMove_Verb: | 290 case SkPath::kMove_Verb: |
295 m.mapPoints(pts, 1); | 291 m.mapPoints(pts, 1); |
296 update_degenerate_test(°enerateData, pts[0]); | 292 update_degenerate_test(°enerateData, pts[0]); |
297 devBounds->set(pts->fX, pts->fY, pts->fX, pts->fY); | |
298 break; | 293 break; |
299 case SkPath::kLine_Verb: { | 294 case SkPath::kLine_Verb: { |
300 m.mapPoints(&pts[1], 1); | 295 m.mapPoints(&pts[1], 1); |
301 update_degenerate_test(°enerateData, pts[1]); | 296 update_degenerate_test(°enerateData, pts[1]); |
302 add_line_to_segment(pts[1], segments, devBounds); | 297 add_line_to_segment(pts[1], segments); |
303 break; | 298 break; |
304 } | 299 } |
305 case SkPath::kQuad_Verb: | 300 case SkPath::kQuad_Verb: |
306 m.mapPoints(pts, 3); | 301 m.mapPoints(pts, 3); |
307 update_degenerate_test(°enerateData, pts[1]); | 302 update_degenerate_test(°enerateData, pts[1]); |
308 update_degenerate_test(°enerateData, pts[2]); | 303 update_degenerate_test(°enerateData, pts[2]); |
309 add_quad_segment(pts, segments, devBounds); | 304 add_quad_segment(pts, segments); |
310 break; | 305 break; |
311 case SkPath::kConic_Verb: { | 306 case SkPath::kConic_Verb: { |
312 m.mapPoints(pts, 3); | 307 m.mapPoints(pts, 3); |
313 SkScalar weight = iter.conicWeight(); | 308 SkScalar weight = iter.conicWeight(); |
314 SkAutoConicToQuads converter; | 309 SkAutoConicToQuads converter; |
315 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5 f); | 310 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5 f); |
316 for (int i = 0; i < converter.countQuads(); ++i) { | 311 for (int i = 0; i < converter.countQuads(); ++i) { |
317 update_degenerate_test(°enerateData, quadPts[2*i + 1]); | 312 update_degenerate_test(°enerateData, quadPts[2*i + 1]); |
318 update_degenerate_test(°enerateData, quadPts[2*i + 2]); | 313 update_degenerate_test(°enerateData, quadPts[2*i + 2]); |
319 add_quad_segment(quadPts + 2*i, segments, devBounds); | 314 add_quad_segment(quadPts + 2*i, segments); |
320 } | 315 } |
321 break; | 316 break; |
322 } | 317 } |
323 case SkPath::kCubic_Verb: { | 318 case SkPath::kCubic_Verb: { |
324 m.mapPoints(pts, 4); | 319 m.mapPoints(pts, 4); |
325 update_degenerate_test(°enerateData, pts[1]); | 320 update_degenerate_test(°enerateData, pts[1]); |
326 update_degenerate_test(°enerateData, pts[2]); | 321 update_degenerate_test(°enerateData, pts[2]); |
327 update_degenerate_test(°enerateData, pts[3]); | 322 update_degenerate_test(°enerateData, pts[3]); |
328 add_cubic_segments(pts, dir, segments, devBounds); | 323 add_cubic_segments(pts, dir, segments); |
329 break; | 324 break; |
330 }; | 325 }; |
331 case SkPath::kDone_Verb: | 326 case SkPath::kDone_Verb: |
332 if (degenerateData.isDegenerate()) { | 327 if (degenerateData.isDegenerate()) { |
333 return false; | 328 return false; |
334 } else { | 329 } else { |
335 compute_vectors(segments, fanPt, dir, vCount, iCount); | 330 compute_vectors(segments, fanPt, dir, vCount, iCount); |
336 return true; | 331 return true; |
337 } | 332 } |
338 default: | 333 default: |
(...skipping 357 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
696 bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget* target, | 691 bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget* target, |
697 const GrPipelineBuilder*, | 692 const GrPipelineBuilder*, |
698 const SkMatrix& viewMatrix, | 693 const SkMatrix& viewMatrix, |
699 const SkPath& path, | 694 const SkPath& path, |
700 const SkStrokeRec& stroke, | 695 const SkStrokeRec& stroke, |
701 bool antiAlias) const { | 696 bool antiAlias) const { |
702 return (target->caps()->shaderDerivativeSupport() && antiAlias && | 697 return (target->caps()->shaderDerivativeSupport() && antiAlias && |
703 stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex() ); | 698 stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex() ); |
704 } | 699 } |
705 | 700 |
701 class AAConvexPathBatch : public GrBatch { | |
702 public: | |
703 struct Geometry { | |
704 GrColor fColor; | |
705 SkMatrix fViewMatrix; | |
706 SkPath fPath; | |
707 SkDEBUGCODE(SkRect fDevBounds;) | |
708 }; | |
709 | |
710 static GrBatch* Create(const Geometry& geometry) { | |
711 return SkNEW_ARGS(AAConvexPathBatch, (geometry)); | |
712 } | |
713 | |
714 const char* name() const SK_OVERRIDE { return "AAConvexBatch"; } | |
715 | |
716 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE { | |
717 // When this is called on a batch, there is only one geometry bundle | |
718 out->setKnownFourComponents(fGeoData[0].fColor); | |
719 } | |
720 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRID E { | |
721 out->setUnknownSingleComponent(); | |
722 } | |
723 | |
724 void initBatchOpt(const GrBatchOpt& batchOpt) { | |
725 fBatchOpt = batchOpt; | |
726 } | |
727 | |
728 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE { | |
729 // Handle any color overrides | |
730 if (init.fColorIgnored) { | |
731 fGeoData[0].fColor = GrColor_ILLEGAL; | |
732 } else if (GrColor_ILLEGAL != init.fOverrideColor) { | |
733 fGeoData[0].fColor = init.fOverrideColor; | |
734 } | |
735 | |
736 // setup batch properties | |
737 fBatch.fColorIgnored = init.fColorIgnored; | |
738 fBatch.fColor = fGeoData[0].fColor; | |
739 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; | |
740 fBatch.fCoverageIgnored = init.fCoverageIgnored; | |
741 } | |
742 | |
743 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline ) SK_OVERRIDE { | |
744 int instanceCount = fGeoData.count(); | |
745 for (int i = 0; i < instanceCount; i++) { | |
746 Geometry& args = fGeoData[i]; | |
747 | |
748 const SkMatrix* viewMatrix = &args.fViewMatrix; | |
749 SkMatrix invert; | |
750 if (!viewMatrix->invert(&invert)) { | |
751 continue; | |
752 } | |
753 | |
754 // We use the fact that SkPath::transform path does subdivision base d on | |
755 // perspective. Otherwise, we apply the view matrix when copying to the | |
756 // segment representation. | |
757 if (viewMatrix->hasPerspective()) { | |
758 args.fPath.transform(*viewMatrix); | |
759 viewMatrix = &SkMatrix::I(); | |
760 } | |
761 | |
762 int vertexCount; | |
763 int indexCount; | |
764 enum { | |
765 kPreallocSegmentCnt = 512 / sizeof(Segment), | |
766 kPreallocDrawCnt = 4, | |
767 }; | |
768 SkSTArray<kPreallocSegmentCnt, Segment, true> segments; | |
769 SkPoint fanPt; | |
770 | |
771 if (!get_segments(args.fPath, *viewMatrix, &segments, &fanPt, &verte xCount, | |
772 &indexCount)) { | |
773 continue; | |
774 } | |
775 | |
776 SkAutoTUnref<GrGeometryProcessor> quadProcessor(QuadEdgeEffect::Crea te(args.fColor, | |
777 invert)); | |
778 | |
779 batchTarget->initDraw(quadProcessor, pipeline); | |
780 fBatchesGenerated++; | |
781 | |
782 // TODO remove this when batch is everywhere | |
783 GrPipelineInfo init; | |
784 init.fColorIgnored = fBatch.fColorIgnored; | |
785 init.fOverrideColor = GrColor_ILLEGAL; | |
786 init.fCoverageIgnored = fBatch.fCoverageIgnored; | |
787 init.fUsesLocalCoords = this->usesLocalCoords(); | |
788 quadProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init); | |
789 | |
790 const GrVertexBuffer* vertexBuffer; | |
791 int firstVertex; | |
792 | |
793 size_t vertexStride = quadProcessor->getVertexStride(); | |
794 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride, | |
795 vertexCount, | |
796 &vertexBuffer, | |
797 &firstVertex); | |
798 | |
799 const GrIndexBuffer* indexBuffer; | |
800 int firstIndex; | |
801 | |
802 void *indices = batchTarget->indexPool()->makeSpace(indexCount, | |
803 &indexBuffer, | |
804 &firstIndex); | |
805 | |
806 QuadVertex* verts = reinterpret_cast<QuadVertex*>(vertices); | |
807 uint16_t* idxs = reinterpret_cast<uint16_t*>(indices); | |
808 | |
809 SkSTArray<kPreallocDrawCnt, Draw, true> draws; | |
810 create_vertices(segments, fanPt, &draws, verts, idxs); | |
811 | |
812 #ifdef SK_DEBUG | |
813 // Check devBounds | |
814 SkRect tolDevBounds = args.fDevBounds; | |
815 tolDevBounds.outset(SK_Scalar1 / 10000, SK_Scalar1 / 10000); | |
816 SkRect actualBounds; | |
817 actualBounds.set(verts[0].fPos, verts[1].fPos); | |
818 for (int i = 2; i < vertexCount; ++i) { | |
819 actualBounds.growToInclude(verts[i].fPos.fX, verts[i].fPos.fY); | |
820 } | |
821 SkASSERT(tolDevBounds.contains(actualBounds)); | |
822 #endif | |
823 | |
824 GrDrawTarget::DrawInfo info; | |
825 info.setVertexBuffer(vertexBuffer); | |
826 info.setIndexBuffer(indexBuffer); | |
827 info.setPrimitiveType(kTriangles_GrPrimitiveType); | |
828 info.setStartIndex(firstIndex); | |
829 | |
830 int vOffset = 0; | |
831 for (int i = 0; i < draws.count(); ++i) { | |
832 const Draw& draw = draws[i]; | |
833 info.setStartVertex(vOffset + firstVertex); | |
834 info.setVertexCount(draw.fVertexCnt); | |
835 info.setIndexCount(draw.fIndexCnt); | |
836 batchTarget->draw(info); | |
837 vOffset += draw.fVertexCnt; | |
838 } | |
839 } | |
840 } | |
841 | |
842 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } | |
843 | |
844 private: | |
845 AAConvexPathBatch(const Geometry& geometry) { | |
846 this->initClassID<AAConvexPathBatch>(); | |
847 fGeoData.push_back(geometry); | |
848 } | |
849 | |
850 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE { | |
851 AAConvexPathBatch* that = t->cast<AAConvexPathBatch>(); | |
852 | |
853 // TODO we can 'batch' with color because we draw each convex path separ ately | |
854 if (this->color() != that->color()) { | |
855 return false; | |
856 } | |
857 | |
858 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); | |
859 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->vi ewMatrix())) { | |
860 return false; | |
861 } | |
862 | |
863 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()) ; | |
864 return true; | |
865 } | |
866 | |
867 GrColor color() const { return fBatch.fColor; } | |
868 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } | |
869 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } | |
870 | |
871 struct BatchTracker { | |
872 GrColor fColor; | |
873 bool fUsesLocalCoords; | |
874 bool fColorIgnored; | |
875 bool fCoverageIgnored; | |
876 }; | |
877 | |
878 GrBatchOpt fBatchOpt; | |
879 BatchTracker fBatch; | |
880 SkSTArray<1, Geometry, true> fGeoData; | |
881 }; | |
882 | |
706 bool GrAAConvexPathRenderer::onDrawPath(GrDrawTarget* target, | 883 bool GrAAConvexPathRenderer::onDrawPath(GrDrawTarget* target, |
707 GrPipelineBuilder* pipelineBuilder, | 884 GrPipelineBuilder* pipelineBuilder, |
708 GrColor color, | 885 GrColor color, |
709 const SkMatrix& vm, | 886 const SkMatrix& vm, |
710 const SkPath& origPath, | 887 const SkPath& path, |
711 const SkStrokeRec&, | 888 const SkStrokeRec&, |
712 bool antiAlias) { | 889 bool antiAlias) { |
713 | 890 if (path.isEmpty()) { |
714 const SkPath* path = &origPath; | |
715 if (path->isEmpty()) { | |
716 return true; | 891 return true; |
717 } | 892 } |
718 | 893 |
719 SkMatrix viewMatrix = vm; | 894 // This outset was determined experimentally by running skps and gms. It pr obably could be a |
bsalomon
2015/02/03 18:09:41
yikes!!! That seems really dangerous. I think we n
| |
720 SkMatrix invert; | 895 // bit tighter |
721 if (!viewMatrix.invert(&invert)) { | 896 SkRect devRect = path.getBounds(); |
722 return false; | 897 devRect.outset(7, 7); |
723 } | 898 vm.mapRect(&devRect); |
724 | 899 |
725 // We use the fact that SkPath::transform path does subdivision based on | 900 AAConvexPathBatch::Geometry geometry; |
726 // perspective. Otherwise, we apply the view matrix when copying to the | 901 geometry.fColor = color; |
727 // segment representation. | 902 geometry.fViewMatrix = vm; |
728 SkPath tmpPath; | 903 geometry.fPath = path; |
729 if (viewMatrix.hasPerspective()) { | 904 SkDEBUGCODE(geometry.fDevBounds = devRect;) |
730 origPath.transform(viewMatrix, &tmpPath); | 905 |
731 path = &tmpPath; | 906 GrBatch* batch = AAConvexPathBatch::Create(geometry); |
732 viewMatrix = SkMatrix::I(); | 907 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 | 908 |
796 return true; | 909 return true; |
910 | |
797 } | 911 } |
OLD | NEW |