OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2011 Google Inc. | 2 * Copyright 2011 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 "GrAAHairLinePathRenderer.h" | 8 #include "GrAAHairLinePathRenderer.h" |
9 | 9 |
10 #include "GrBatch.h" | |
11 #include "GrBatchTarget.h" | |
12 #include "GrBufferAllocPool.h" | |
10 #include "GrContext.h" | 13 #include "GrContext.h" |
11 #include "GrDefaultGeoProcFactory.h" | 14 #include "GrDefaultGeoProcFactory.h" |
12 #include "GrDrawTargetCaps.h" | 15 #include "GrDrawTargetCaps.h" |
13 #include "GrGpu.h" | 16 #include "GrGpu.h" |
14 #include "GrIndexBuffer.h" | 17 #include "GrIndexBuffer.h" |
15 #include "GrPathUtils.h" | 18 #include "GrPathUtils.h" |
16 #include "GrPipelineBuilder.h" | 19 #include "GrPipelineBuilder.h" |
17 #include "GrProcessor.h" | 20 #include "GrProcessor.h" |
18 #include "SkGeometry.h" | 21 #include "SkGeometry.h" |
19 #include "SkStroke.h" | 22 #include "SkStroke.h" |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
246 | 249 |
247 /** | 250 /** |
248 * Generates the lines and quads to be rendered. Lines are always recorded in | 251 * Generates the lines and quads to be rendered. Lines are always recorded in |
249 * device space. We will do a device space bloat to account for the 1pixel | 252 * device space. We will do a device space bloat to account for the 1pixel |
250 * thickness. | 253 * thickness. |
251 * Quads are recorded in device space unless m contains | 254 * Quads are recorded in device space unless m contains |
252 * perspective, then in they are in src space. We do this because we will | 255 * perspective, then in they are in src space. We do this because we will |
253 * subdivide large quads to reduce over-fill. This subdivision has to be | 256 * subdivide large quads to reduce over-fill. This subdivision has to be |
254 * performed before applying the perspective matrix. | 257 * performed before applying the perspective matrix. |
255 */ | 258 */ |
256 int generate_lines_and_quads(const SkPath& path, | 259 int generate_lines_and_quads(const SkPath& path, |
bsalomon
2015/01/26 19:07:47
let's rename this, generate is becoming overloaded
| |
257 const SkMatrix& m, | 260 const SkMatrix& m, |
258 const SkIRect& devClipBounds, | 261 const SkIRect& devClipBounds, |
259 GrAAHairLinePathRenderer::PtArray* lines, | 262 GrAAHairLinePathRenderer::PtArray* lines, |
260 GrAAHairLinePathRenderer::PtArray* quads, | 263 GrAAHairLinePathRenderer::PtArray* quads, |
261 GrAAHairLinePathRenderer::PtArray* conics, | 264 GrAAHairLinePathRenderer::PtArray* conics, |
262 GrAAHairLinePathRenderer::IntArray* quadSubdivCnts, | 265 GrAAHairLinePathRenderer::IntArray* quadSubdivCnts, |
263 GrAAHairLinePathRenderer::FloatArray* conicWeights) { | 266 GrAAHairLinePathRenderer::FloatArray* conicWeights) { |
264 SkPath::Iter iter(path, false); | 267 SkPath::Iter iter(path, false); |
265 | 268 |
266 int totalQuadCount = 0; | 269 int totalQuadCount = 0; |
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
462 result->fY = SkScalarMul(result->fY, wInv); | 465 result->fY = SkScalarMul(result->fY, wInv); |
463 } | 466 } |
464 | 467 |
465 void set_uv_quad(const SkPoint qpts[3], BezierVertex verts[kQuadNumVertices]) { | 468 void set_uv_quad(const SkPoint qpts[3], BezierVertex verts[kQuadNumVertices]) { |
466 // this should be in the src space, not dev coords, when we have perspective | 469 // this should be in the src space, not dev coords, when we have perspective |
467 GrPathUtils::QuadUVMatrix DevToUV(qpts); | 470 GrPathUtils::QuadUVMatrix DevToUV(qpts); |
468 DevToUV.apply<kQuadNumVertices, sizeof(BezierVertex), sizeof(SkPoint)>(verts ); | 471 DevToUV.apply<kQuadNumVertices, sizeof(BezierVertex), sizeof(SkPoint)>(verts ); |
469 } | 472 } |
470 | 473 |
471 void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice, | 474 void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice, |
472 const SkMatrix* toSrc, BezierVertex verts[kQuadNumVertices], | 475 const SkMatrix* toSrc, BezierVertex verts[kQuadNumVertices]) { |
473 SkRect* devBounds) { | |
474 SkASSERT(!toDevice == !toSrc); | 476 SkASSERT(!toDevice == !toSrc); |
475 // original quad is specified by tri a,b,c | 477 // original quad is specified by tri a,b,c |
476 SkPoint a = qpts[0]; | 478 SkPoint a = qpts[0]; |
477 SkPoint b = qpts[1]; | 479 SkPoint b = qpts[1]; |
478 SkPoint c = qpts[2]; | 480 SkPoint c = qpts[2]; |
479 | 481 |
480 if (toDevice) { | 482 if (toDevice) { |
481 toDevice->mapPoints(&a, 1); | 483 toDevice->mapPoints(&a, 1); |
482 toDevice->mapPoints(&b, 1); | 484 toDevice->mapPoints(&b, 1); |
483 toDevice->mapPoints(&c, 1); | 485 toDevice->mapPoints(&c, 1); |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
528 a0.fPos += abN; | 530 a0.fPos += abN; |
529 a1.fPos = a; | 531 a1.fPos = a; |
530 a1.fPos -= abN; | 532 a1.fPos -= abN; |
531 | 533 |
532 c0.fPos = c; | 534 c0.fPos = c; |
533 c0.fPos += cbN; | 535 c0.fPos += cbN; |
534 c1.fPos = c; | 536 c1.fPos = c; |
535 c1.fPos -= cbN; | 537 c1.fPos -= cbN; |
536 | 538 |
537 intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos); | 539 intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos); |
538 devBounds->growToInclude(&verts[0].fPos, sizeof(BezierVertex), kQuadNumVerti ces); | |
539 | 540 |
540 if (toSrc) { | 541 if (toSrc) { |
541 toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(BezierVertex), kQuadNu mVertices); | 542 toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(BezierVertex), kQuadNu mVertices); |
542 } | 543 } |
543 } | 544 } |
544 | 545 |
545 // Equations based off of Loop-Blinn Quadratic GPU Rendering | 546 // Equations based off of Loop-Blinn Quadratic GPU Rendering |
546 // Input Parametric: | 547 // Input Parametric: |
547 // P(t) = (P0*(1-t)^2 + 2*w*P1*t*(1-t) + P2*t^2) / (1-t)^2 + 2*w*t*(1-t) + t^2) | 548 // P(t) = (P0*(1-t)^2 + 2*w*P1*t*(1-t) + P2*t^2) / (1-t)^2 + 2*w*t*(1-t) + t^2) |
548 // Output Implicit: | 549 // Output Implicit: |
(...skipping 11 matching lines...) Expand all Loading... | |
560 verts[i].fConic.fK = pnt.fX * klm[0] + pnt.fY * klm[1] + klm[2]; | 561 verts[i].fConic.fK = pnt.fX * klm[0] + pnt.fY * klm[1] + klm[2]; |
561 verts[i].fConic.fL = pnt.fX * klm[3] + pnt.fY * klm[4] + klm[5]; | 562 verts[i].fConic.fL = pnt.fX * klm[3] + pnt.fY * klm[4] + klm[5]; |
562 verts[i].fConic.fM = pnt.fX * klm[6] + pnt.fY * klm[7] + klm[8]; | 563 verts[i].fConic.fM = pnt.fX * klm[6] + pnt.fY * klm[7] + klm[8]; |
563 } | 564 } |
564 } | 565 } |
565 | 566 |
566 void add_conics(const SkPoint p[3], | 567 void add_conics(const SkPoint p[3], |
567 const SkScalar weight, | 568 const SkScalar weight, |
568 const SkMatrix* toDevice, | 569 const SkMatrix* toDevice, |
569 const SkMatrix* toSrc, | 570 const SkMatrix* toSrc, |
570 BezierVertex** vert, | 571 BezierVertex** vert) { |
571 SkRect* devBounds) { | 572 bloat_quad(p, toDevice, toSrc, *vert); |
572 bloat_quad(p, toDevice, toSrc, *vert, devBounds); | |
573 set_conic_coeffs(p, *vert, weight); | 573 set_conic_coeffs(p, *vert, weight); |
574 *vert += kQuadNumVertices; | 574 *vert += kQuadNumVertices; |
575 } | 575 } |
576 | 576 |
577 void add_quads(const SkPoint p[3], | 577 void add_quads(const SkPoint p[3], |
578 int subdiv, | 578 int subdiv, |
579 const SkMatrix* toDevice, | 579 const SkMatrix* toDevice, |
580 const SkMatrix* toSrc, | 580 const SkMatrix* toSrc, |
581 BezierVertex** vert, | 581 BezierVertex** vert) { |
582 SkRect* devBounds) { | |
583 SkASSERT(subdiv >= 0); | 582 SkASSERT(subdiv >= 0); |
584 if (subdiv) { | 583 if (subdiv) { |
585 SkPoint newP[5]; | 584 SkPoint newP[5]; |
586 SkChopQuadAtHalf(p, newP); | 585 SkChopQuadAtHalf(p, newP); |
587 add_quads(newP + 0, subdiv-1, toDevice, toSrc, vert, devBounds); | 586 add_quads(newP + 0, subdiv-1, toDevice, toSrc, vert); |
588 add_quads(newP + 2, subdiv-1, toDevice, toSrc, vert, devBounds); | 587 add_quads(newP + 2, subdiv-1, toDevice, toSrc, vert); |
589 } else { | 588 } else { |
590 bloat_quad(p, toDevice, toSrc, *vert, devBounds); | 589 bloat_quad(p, toDevice, toSrc, *vert); |
591 set_uv_quad(p, *vert); | 590 set_uv_quad(p, *vert); |
592 *vert += kQuadNumVertices; | 591 *vert += kQuadNumVertices; |
593 } | 592 } |
594 } | 593 } |
595 | 594 |
596 void add_line(const SkPoint p[2], | 595 void add_line(const SkPoint p[2], |
597 const SkMatrix* toSrc, | 596 const SkMatrix* toSrc, |
598 uint8_t coverage, | 597 uint8_t coverage, |
599 LineVertex** vert) { | 598 LineVertex** vert) { |
600 const SkPoint& a = p[0]; | 599 const SkPoint& a = p[0]; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
635 } | 634 } |
636 } | 635 } |
637 | 636 |
638 *vert += kLineSegNumVertices; | 637 *vert += kLineSegNumVertices; |
639 } | 638 } |
640 | 639 |
641 } | 640 } |
642 | 641 |
643 /////////////////////////////////////////////////////////////////////////////// | 642 /////////////////////////////////////////////////////////////////////////////// |
644 | 643 |
645 bool GrAAHairLinePathRenderer::createLineGeom(GrDrawTarget* target, | |
646 GrPipelineBuilder* pipelineBuilder , | |
647 const SkMatrix& viewMatrix, | |
648 uint8_t coverage, | |
649 size_t vertexStride, | |
650 GrDrawTarget::AutoReleaseGeometry* arg, | |
651 SkRect* devBounds, | |
652 const SkPath& path, | |
653 const PtArray& lines, | |
654 int lineCnt) { | |
655 int vertCnt = kLineSegNumVertices * lineCnt; | |
656 | |
657 SkASSERT(vertexStride == sizeof(LineVertex)); | |
658 if (!arg->set(target, vertCnt, vertexStride, 0)) { | |
659 return false; | |
660 } | |
661 | |
662 LineVertex* verts = reinterpret_cast<LineVertex*>(arg->vertices()); | |
663 | |
664 const SkMatrix* toSrc = NULL; | |
665 SkMatrix ivm; | |
666 | |
667 if (viewMatrix.hasPerspective()) { | |
668 if (viewMatrix.invert(&ivm)) { | |
669 toSrc = &ivm; | |
670 } | |
671 } | |
672 devBounds->set(lines.begin(), lines.count()); | |
673 for (int i = 0; i < lineCnt; ++i) { | |
674 add_line(&lines[2*i], toSrc, coverage, &verts); | |
675 } | |
676 // All the verts computed by add_line are within sqrt(1^2 + 0.5^2) of the en d points. | |
677 static const SkScalar kSqrtOfOneAndAQuarter = 1.118f; | |
678 // Add a little extra to account for vector normalization precision. | |
679 static const SkScalar kOutset = kSqrtOfOneAndAQuarter + SK_Scalar1 / 20; | |
680 devBounds->outset(kOutset, kOutset); | |
681 | |
682 return true; | |
683 } | |
684 | |
685 bool GrAAHairLinePathRenderer::createBezierGeom(GrDrawTarget* target, | |
686 GrPipelineBuilder* pipelineBuild er, | |
687 const SkMatrix& viewMatrix, | |
688 GrDrawTarget::AutoReleaseGeometr y* arg, | |
689 SkRect* devBounds, | |
690 const SkPath& path, | |
691 const PtArray& quads, | |
692 int quadCnt, | |
693 const PtArray& conics, | |
694 int conicCnt, | |
695 const IntArray& qSubdivs, | |
696 const FloatArray& cWeights, | |
697 size_t vertexStride) { | |
698 int vertCnt = kQuadNumVertices * quadCnt + kQuadNumVertices * conicCnt; | |
699 | |
700 if (!arg->set(target, vertCnt, vertexStride, 0)) { | |
701 return false; | |
702 } | |
703 | |
704 BezierVertex* verts = reinterpret_cast<BezierVertex*>(arg->vertices()); | |
705 | |
706 const SkMatrix* toDevice = NULL; | |
707 const SkMatrix* toSrc = NULL; | |
708 SkMatrix ivm; | |
709 | |
710 if (viewMatrix.hasPerspective()) { | |
711 if (viewMatrix.invert(&ivm)) { | |
712 toDevice = &viewMatrix; | |
713 toSrc = &ivm; | |
714 } | |
715 } | |
716 | |
717 // Seed the dev bounds with some pts known to be inside. Each quad and conic grows the bounding | |
718 // box to include its vertices. | |
719 SkPoint seedPts[2]; | |
720 if (quadCnt) { | |
721 seedPts[0] = quads[0]; | |
722 seedPts[1] = quads[2]; | |
723 } else if (conicCnt) { | |
724 seedPts[0] = conics[0]; | |
725 seedPts[1] = conics[2]; | |
726 } | |
727 if (toDevice) { | |
728 toDevice->mapPoints(seedPts, 2); | |
729 } | |
730 devBounds->set(seedPts[0], seedPts[1]); | |
731 | |
732 int unsubdivQuadCnt = quads.count() / 3; | |
733 for (int i = 0; i < unsubdivQuadCnt; ++i) { | |
734 SkASSERT(qSubdivs[i] >= 0); | |
735 add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts, devBounds); | |
736 } | |
737 | |
738 // Start Conics | |
739 for (int i = 0; i < conicCnt; ++i) { | |
740 add_conics(&conics[3*i], cWeights[i], toDevice, toSrc, &verts, devBounds ); | |
741 } | |
742 return true; | |
743 } | |
744 | |
745 bool GrAAHairLinePathRenderer::canDrawPath(const GrDrawTarget* target, | 644 bool GrAAHairLinePathRenderer::canDrawPath(const GrDrawTarget* target, |
746 const GrPipelineBuilder* pipelineBuil der, | 645 const GrPipelineBuilder* pipelineBuil der, |
747 const SkMatrix& viewMatrix, | 646 const SkMatrix& viewMatrix, |
748 const SkPath& path, | 647 const SkPath& path, |
749 const SkStrokeRec& stroke, | 648 const SkStrokeRec& stroke, |
750 bool antiAlias) const { | 649 bool antiAlias) const { |
751 if (!antiAlias) { | 650 if (!antiAlias) { |
752 return false; | 651 return false; |
753 } | 652 } |
754 | 653 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
793 actualBounds.growToInclude(pos.fX, pos.fY); | 692 actualBounds.growToInclude(pos.fX, pos.fY); |
794 } | 693 } |
795 } | 694 } |
796 if (!first) { | 695 if (!first) { |
797 return tolDevBounds.contains(actualBounds); | 696 return tolDevBounds.contains(actualBounds); |
798 } | 697 } |
799 | 698 |
800 return true; | 699 return true; |
801 } | 700 } |
802 | 701 |
702 class AAHairlineBatch : public GrBatch { | |
703 public: | |
704 struct Geometry { | |
705 GrColor fColor; | |
706 uint8_t fCoverage; | |
707 SkMatrix fViewMatrix; | |
708 SkPath fPath; | |
709 SkDEBUGCODE(SkRect fDevBounds;) | |
710 SkIRect fDevClipBounds; | |
711 }; | |
712 | |
713 static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* linesI ndexBuffer, | |
bsalomon
2015/01/26 19:07:48
I think we should come up with a way for these to
| |
714 const GrIndexBuffer* quadsIndexBuffer) { | |
715 return SkNEW_ARGS(AAHairlineBatch, (geometry, linesIndexBuffer, quadsInd exBuffer)); | |
716 } | |
717 | |
718 const char* name() const SK_OVERRIDE { return "AAHairlineBatch"; } | |
719 | |
720 void getInvariantOutputColor(GrInitInvariantOutput* out, | |
721 const GrBatchOpt& batchOpt) const SK_OVERRIDE { | |
722 // When this is called on a batch, there is only one geometry bundle | |
723 out->setKnownFourComponents(fGeoData[0].fColor); | |
724 } | |
725 void getInvariantOutputCoverage(GrInitInvariantOutput* out, | |
726 const GrBatchOpt& batchOpt) const SK_OVERRID E { | |
727 out->setUnknownSingleComponent(); | |
728 } | |
729 | |
730 void initBatchOpt(const GrBatchOpt& batchOpt) { | |
731 fBatchOpt = batchOpt; | |
732 } | |
733 | |
734 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE { | |
735 // Handle any color overrides | |
736 if (init.fColorIgnored) { | |
737 fGeoData[0].fColor = GrColor_ILLEGAL; | |
738 } else if (GrColor_ILLEGAL != init.fOverrideColor) { | |
739 fGeoData[0].fColor = init.fOverrideColor; | |
740 } | |
741 | |
742 // setup batch properties | |
743 fBatch.fColorIgnored = init.fColorIgnored; | |
744 fBatch.fColor = fGeoData[0].fColor; | |
745 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; | |
746 fBatch.fCoverageIgnored = init.fCoverageIgnored; | |
747 SkDEBUGCODE(fBatch.fDevBounds = fGeoData[0].fDevBounds;) | |
748 } | |
749 | |
750 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE { | |
bsalomon
2015/01/26 19:07:48
private?
| |
751 AAHairlineBatch* that = t->cast<AAHairlineBatch>(); | |
752 | |
753 // We go to identity if we don't have perspective | |
754 if (this->viewMatrix().hasPerspective() && | |
755 !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { | |
756 return false; | |
757 } | |
758 | |
759 // TODO we can actually batch hairlines if they are the same color in a kind of bulk method | |
760 // but we haven't implemented this yet | |
761 // TODO investigate going to vertex color and coverage? | |
762 if (this->coverage() != that->coverage()) { | |
763 return false; | |
764 } | |
765 | |
766 if (this->color() != that->color()) { | |
767 return false; | |
768 } | |
769 | |
770 if (this->devClipBounds() != this->devClipBounds()) { | |
771 return false; | |
772 } | |
773 | |
774 if (this->usesLocalCoords() != that->usesLocalCoords()) { | |
775 return false; | |
776 } | |
777 | |
778 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->vi ewMatrix())) { | |
779 return false; | |
780 } | |
781 | |
782 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()) ; | |
783 return true; | |
784 } | |
785 | |
786 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline ) SK_OVERRIDE { | |
787 int instanceCount = fGeoData.count(); | |
788 for (int i = 0; i < instanceCount; i++) { | |
789 const Geometry& args = fGeoData[i]; | |
790 | |
791 // createGeom transforms the geometry to device space when the matri x does not have | |
792 // perspective. | |
793 SkMatrix vm = args.fViewMatrix; | |
794 SkMatrix invert = SkMatrix::I(); | |
795 if (!args.fViewMatrix.hasPerspective()) { | |
796 vm = SkMatrix::I(); | |
797 if (!args.fViewMatrix.invert(&invert)) { | |
798 return; | |
799 } | |
800 } | |
801 | |
802 int lineCount; | |
803 int quadCount; | |
804 int conicCount; | |
805 PREALLOC_PTARRAY(128) lines; | |
806 PREALLOC_PTARRAY(128) quads; | |
807 PREALLOC_PTARRAY(128) conics; | |
808 IntArray qSubdivs; | |
809 FloatArray cWeights; | |
810 quadCount = generate_lines_and_quads(args.fPath, args.fViewMatrix, a rgs.fDevClipBounds, | |
811 &lines, &quads, &conics, &qSubd ivs, &cWeights); | |
812 | |
813 lineCount = lines.count() / 2; | |
814 conicCount = conics.count() / 3; | |
815 | |
816 // do lines first | |
817 if (lineCount) { | |
818 this->generateLines(batchTarget, pipeline, args, vm, invert, lin es, lineCount); | |
819 } | |
820 | |
821 if (quadCount || conicCount) { | |
822 this->generateQuadsAndConics(batchTarget, | |
823 pipeline, | |
824 args, | |
825 quads, | |
826 quadCount, | |
827 conics, | |
828 conicCount, | |
829 qSubdivs, | |
830 cWeights, | |
831 vm, | |
832 invert); | |
833 } | |
834 } | |
835 } | |
836 | |
837 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } | |
838 | |
839 private: | |
840 typedef SkTArray<SkPoint, true> PtArray; | |
841 typedef SkTArray<int, true> IntArray; | |
842 typedef SkTArray<float, true> FloatArray; | |
843 | |
844 AAHairlineBatch(const Geometry& geometry, const GrIndexBuffer* linesIndexBuf fer, | |
845 const GrIndexBuffer* quadsIndexBuffer) | |
846 : fLinesIndexBuffer(linesIndexBuffer) | |
847 , fQuadsIndexBuffer(quadsIndexBuffer) { | |
848 SkASSERT(linesIndexBuffer && quadsIndexBuffer); | |
849 this->initClassID<AAHairlineBatch>(); | |
850 fGeoData.push_back(geometry); | |
851 } | |
852 | |
853 GrColor color() const { return fBatch.fColor; } | |
854 uint8_t coverage() const { return fBatch.fCoverage; } | |
855 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } | |
856 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } | |
857 const SkIRect& devClipBounds() const { return fBatch.fDevClipBounds; } | |
858 | |
859 void generateLines(GrBatchTarget* batchTarget, | |
860 const GrPipeline* pipeline, | |
861 const Geometry& args, | |
862 const SkMatrix& viewMatrix, | |
863 const SkMatrix& invert, | |
864 const PtArray& lines, | |
865 int lineCnt) { | |
866 uint32_t gpFlags = GrDefaultGeoProcFactory::kPosition_GPType | | |
867 GrDefaultGeoProcFactory::kCoverage_GPType; | |
868 SkAutoTUnref<const GrGeometryProcessor> gp(GrDefaultGeoProcFactory::Crea te(gpFlags, | |
869 args.fColor, | |
870 viewMatrix, | |
871 invert, | |
872 false, | |
873 args.fCoverage)); | |
874 | |
875 batchTarget->initDraw(gp, pipeline); | |
876 | |
877 // TODO remove this when batch is everywhere | |
878 GrPipelineInfo init; | |
879 init.fColorIgnored = fBatch.fColorIgnored; | |
880 init.fOverrideColor = GrColor_ILLEGAL; | |
881 init.fCoverageIgnored = fBatch.fCoverageIgnored; | |
882 init.fUsesLocalCoords = this->usesLocalCoords(); | |
883 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); | |
884 | |
885 const GrVertexBuffer* vertexBuffer; | |
886 int firstVertex; | |
887 | |
888 size_t vertexStride = gp->getVertexStride(); | |
889 int vertexCount = kLineSegNumVertices * lineCnt; | |
890 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride, | |
891 vertexCount, | |
892 &vertexBuffer, | |
893 &firstVertex); | |
894 | |
895 SkASSERT(gp->getVertexStride() == sizeof(LineVertex)); | |
896 | |
897 // generate lines | |
898 const SkMatrix* toSrc = NULL; | |
899 if (args.fViewMatrix.hasPerspective()) { | |
900 SkMatrix perspectiveInvert; | |
901 if (!args.fViewMatrix.invert(&perspectiveInvert)) { | |
902 return; | |
903 } | |
904 toSrc = &perspectiveInvert; | |
905 } | |
906 | |
907 LineVertex* verts = reinterpret_cast<LineVertex*>(vertices); | |
908 for (int i = 0; i < lineCnt; ++i) { | |
909 add_line(&lines[2*i], toSrc, args.fCoverage, &verts); | |
910 } | |
911 | |
912 // Check devBounds | |
913 SkASSERT(check_bounds<LineVertex>(viewMatrix.hasPerspective() ? viewMatr ix : SkMatrix::I(), | |
914 args.fDevBounds, | |
915 vertices, | |
916 kLineSegNumVertices * lineCnt)); | |
917 | |
918 { | |
919 GrDrawTarget::DrawInfo info; | |
920 info.setVertexBuffer(vertexBuffer); | |
921 info.setIndexBuffer(fLinesIndexBuffer); | |
922 info.setPrimitiveType(kTriangles_GrPrimitiveType); | |
923 info.setStartIndex(0); | |
924 | |
925 int lines = 0; | |
926 while (lines < lineCnt) { | |
927 int n = SkTMin(lineCnt - lines, kLineSegsNumInIdxBuffer); | |
928 | |
929 info.setStartVertex(kLineSegNumVertices*lines + firstVertex); | |
930 info.setVertexCount(kLineSegNumVertices*n); | |
931 info.setIndexCount(kIdxsPerLineSeg*n); | |
932 batchTarget->draw(info); | |
933 | |
934 lines += n; | |
935 } | |
936 } | |
937 } | |
938 | |
939 void generateQuadsAndConics(GrBatchTarget* batchTarget, | |
940 const GrPipeline* pipeline, | |
941 const Geometry& args, | |
942 const PREALLOC_PTARRAY(128)& quads, | |
943 int quadCount, | |
944 const PREALLOC_PTARRAY(128)& conics, | |
945 int conicCount, | |
946 const IntArray& qSubdivs, | |
947 const FloatArray& cWeights, | |
948 const SkMatrix& vm, | |
949 const SkMatrix& invert) { | |
950 const GrVertexBuffer* vertexBuffer; | |
951 int firstVertex; | |
952 | |
953 size_t vertexStride = sizeof(BezierVertex); | |
954 int vertexCount = kQuadNumVertices * quadCount + kQuadNumVertices * coni cCount; | |
955 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride, | |
956 vertexCount, | |
957 &vertexBuffer, | |
958 &firstVertex); | |
959 | |
960 if (!this->createBezierGeom(vertices, | |
961 args.fViewMatrix, | |
962 args.fPath, | |
963 quads, | |
964 quadCount, | |
965 conics, | |
966 conicCount, | |
967 qSubdivs, | |
968 cWeights, | |
969 vertexStride)) { | |
970 SkDebugf("Couldn't create bezier geometry\n"); | |
971 return; | |
972 } | |
973 | |
974 // Check devBounds | |
975 SkASSERT(check_bounds<BezierVertex>(vm, | |
976 args.fDevBounds, | |
977 vertices, | |
978 kQuadNumVertices * quadCount + | |
979 kQuadNumVertices * conicCount)); | |
980 | |
981 if (quadCount > 0) { | |
982 SkAutoTUnref<GrGeometryProcessor> hairQuadProcessor( | |
983 GrQuadEffect::Create(args.fColor, | |
984 vm, | |
985 kHairlineAA_GrProcessorEdgeType, | |
986 batchTarget->caps(), | |
987 invert, | |
988 args.fCoverage)); | |
989 | |
990 batchTarget->initDraw(hairQuadProcessor, pipeline); | |
991 | |
992 // TODO remove this when batch is everywhere | |
993 GrPipelineInfo init; | |
994 init.fColorIgnored = fBatch.fColorIgnored; | |
995 init.fOverrideColor = GrColor_ILLEGAL; | |
996 init.fCoverageIgnored = fBatch.fCoverageIgnored; | |
997 init.fUsesLocalCoords = this->usesLocalCoords(); | |
998 hairQuadProcessor->initBatchTracker(batchTarget->currentBatchTracker (), init); | |
999 | |
1000 this->drawBeziers(batchTarget, | |
1001 hairQuadProcessor, | |
1002 pipeline, | |
1003 vertexBuffer, | |
1004 firstVertex, | |
1005 quadCount); | |
1006 } | |
1007 | |
1008 if (conicCount > 0) { | |
1009 SkAutoTUnref<GrGeometryProcessor> hairConicProcessor( | |
1010 GrConicEffect::Create(args.fColor, | |
1011 vm, | |
1012 kHairlineAA_GrProcessorEdgeType, | |
1013 batchTarget->caps(), | |
1014 invert, | |
1015 args.fCoverage)); | |
1016 | |
1017 batchTarget->initDraw(hairConicProcessor, pipeline); | |
1018 | |
1019 // TODO remove this when batch is everywhere | |
1020 GrPipelineInfo init; | |
1021 init.fColorIgnored = fBatch.fColorIgnored; | |
1022 init.fOverrideColor = GrColor_ILLEGAL; | |
1023 init.fCoverageIgnored = fBatch.fCoverageIgnored; | |
1024 init.fUsesLocalCoords = this->usesLocalCoords(); | |
1025 hairConicProcessor->initBatchTracker(batchTarget->currentBatchTracke r(), init); | |
1026 | |
1027 this->drawConics(batchTarget, | |
1028 hairConicProcessor, | |
1029 pipeline, | |
1030 vertexBuffer, | |
1031 firstVertex, | |
1032 conicCount, | |
1033 quadCount); | |
1034 } | |
1035 } | |
1036 | |
1037 bool createBezierGeom(void* vertices, | |
1038 const SkMatrix& viewMatrix, | |
1039 const SkPath& path, | |
1040 const PtArray& quads, | |
1041 int quadCnt, | |
1042 const PtArray& conics, | |
1043 int conicCnt, | |
1044 const IntArray& qSubdivs, | |
1045 const FloatArray& cWeights, | |
1046 size_t vertexStride) { | |
1047 BezierVertex* verts = reinterpret_cast<BezierVertex*>(vertices); | |
1048 | |
1049 const SkMatrix* toDevice = NULL; | |
1050 const SkMatrix* toSrc = NULL; | |
1051 SkMatrix ivm; | |
1052 | |
1053 if (viewMatrix.hasPerspective()) { | |
1054 if (viewMatrix.invert(&ivm)) { | |
1055 toDevice = &viewMatrix; | |
1056 toSrc = &ivm; | |
1057 } | |
1058 } | |
1059 | |
1060 int unsubdivQuadCnt = quads.count() / 3; | |
1061 for (int i = 0; i < unsubdivQuadCnt; ++i) { | |
1062 SkASSERT(qSubdivs[i] >= 0); | |
1063 add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts); | |
1064 } | |
1065 | |
1066 // Start Conics | |
1067 for (int i = 0; i < conicCnt; ++i) { | |
1068 add_conics(&conics[3*i], cWeights[i], toDevice, toSrc, &verts); | |
1069 } | |
1070 return true; | |
1071 } | |
1072 | |
1073 void drawBeziers(GrBatchTarget* batchTarget, | |
1074 const GrGeometryProcessor* hairQuadProcessor, | |
1075 const GrPipeline* pipeline, | |
1076 const GrVertexBuffer* vertexBuffer, | |
1077 int firstVertex, | |
1078 int quadCount) { | |
1079 GrDrawTarget::DrawInfo info; | |
1080 info.setVertexBuffer(vertexBuffer); | |
1081 info.setIndexBuffer(fQuadsIndexBuffer); | |
1082 info.setPrimitiveType(kTriangles_GrPrimitiveType); | |
1083 info.setStartIndex(0); | |
1084 | |
1085 int quads = 0; | |
1086 while (quads < quadCount) { | |
1087 int n = SkTMin(quadCount - quads, kQuadsNumInIdxBuffer); | |
1088 | |
1089 info.setStartVertex(kQuadNumVertices*quads + firstVertex); | |
1090 info.setVertexCount(kQuadNumVertices*n); | |
1091 info.setIndexCount(kIdxsPerQuad*n); | |
1092 batchTarget->draw(info); | |
1093 | |
1094 quads += n; | |
1095 } | |
1096 } | |
1097 | |
1098 void drawConics(GrBatchTarget* batchTarget, | |
1099 const GrGeometryProcessor* hairConicProcessor, | |
1100 const GrPipeline* pipeline, | |
1101 const GrVertexBuffer* vertexBuffer, | |
1102 int firstVertex, | |
1103 int conicCount, | |
1104 int quadCount) { | |
1105 GrDrawTarget::DrawInfo info; | |
1106 info.setVertexBuffer(vertexBuffer); | |
1107 info.setIndexBuffer(fQuadsIndexBuffer); | |
1108 info.setPrimitiveType(kTriangles_GrPrimitiveType); | |
1109 info.setStartIndex(0); | |
1110 | |
1111 int conics = 0; | |
1112 while (conics < conicCount) { | |
1113 int n = SkTMin(conicCount - conics, kQuadsNumInIdxBuffer); | |
1114 | |
1115 info.setStartVertex(kQuadNumVertices*(quadCount + conics) + firstVer tex); | |
1116 info.setVertexCount(kQuadNumVertices*n); | |
1117 info.setIndexCount(kIdxsPerQuad*n); | |
1118 batchTarget->draw(info); | |
1119 | |
1120 conics += n; | |
1121 } | |
1122 } | |
1123 | |
1124 struct BatchTracker { | |
1125 GrColor fColor; | |
1126 uint8_t fCoverage; | |
1127 SkRect fDevBounds; | |
1128 SkIRect fDevClipBounds; | |
1129 bool fUsesLocalCoords; | |
1130 bool fColorIgnored; | |
1131 bool fCoverageIgnored; | |
1132 }; | |
1133 | |
1134 GrBatchOpt fBatchOpt; | |
1135 BatchTracker fBatch; | |
1136 SkSTArray<1, Geometry, true> fGeoData; | |
1137 const GrIndexBuffer* fLinesIndexBuffer; | |
1138 const GrIndexBuffer* fQuadsIndexBuffer; | |
1139 }; | |
1140 | |
803 bool GrAAHairLinePathRenderer::onDrawPath(GrDrawTarget* target, | 1141 bool GrAAHairLinePathRenderer::onDrawPath(GrDrawTarget* target, |
804 GrPipelineBuilder* pipelineBuilder, | 1142 GrPipelineBuilder* pipelineBuilder, |
805 GrColor color, | 1143 GrColor color, |
806 const SkMatrix& viewMatrix, | 1144 const SkMatrix& viewMatrix, |
807 const SkPath& path, | 1145 const SkPath& path, |
808 const SkStrokeRec& stroke, | 1146 const SkStrokeRec& stroke, |
809 bool antiAlias) { | 1147 bool) { |
810 SkScalar hairlineCoverage; | 1148 SkScalar hairlineCoverage; |
811 uint8_t newCoverage = 0xff; | 1149 uint8_t newCoverage = 0xff; |
812 if (IsStrokeHairlineOrEquivalent(stroke, viewMatrix, &hairlineCoverage)) { | 1150 if (IsStrokeHairlineOrEquivalent(stroke, viewMatrix, &hairlineCoverage)) { |
813 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff); | 1151 newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff); |
814 } | 1152 } |
815 | 1153 |
816 SkIRect devClipBounds; | 1154 SkIRect devClipBounds; |
817 target->getClip()->getConservativeBounds(pipelineBuilder->getRenderTarget(), &devClipBounds); | 1155 target->getClip()->getConservativeBounds(pipelineBuilder->getRenderTarget(), &devClipBounds); |
818 | 1156 |
819 int lineCnt; | 1157 // This outset was determined experimentally by running skps and gms. It pr obably could be a |
820 int quadCnt; | 1158 // bit tighter |
821 int conicCnt; | 1159 SkRect devRect = path.getBounds(); |
822 PREALLOC_PTARRAY(128) lines; | 1160 devRect.outset(3, 3); |
823 PREALLOC_PTARRAY(128) quads; | 1161 viewMatrix.mapRect(&devRect); |
824 PREALLOC_PTARRAY(128) conics; | 1162 |
825 IntArray qSubdivs; | 1163 // NOTE we don't currently do anything with the strokerec. |
bsalomon
2015/01/26 19:07:47
didn't we already respect it above (if IsStrokeHai
| |
826 FloatArray cWeights; | 1164 AAHairlineBatch::Geometry geometry; |
827 quadCnt = generate_lines_and_quads(path, viewMatrix, devClipBounds, | 1165 geometry.fColor = color; |
828 &lines, &quads, &conics, &qSubdivs, &cWei ghts); | 1166 geometry.fCoverage = newCoverage; |
829 lineCnt = lines.count() / 2; | 1167 geometry.fViewMatrix = viewMatrix; |
830 conicCnt = conics.count() / 3; | 1168 geometry.fPath = path; |
831 | 1169 SkDEBUGCODE(geometry.fDevBounds = devRect;) |
832 // createGeom transforms the geometry to device space when the matrix does n ot have | 1170 geometry.fDevClipBounds = devClipBounds; |
833 // perspective. | 1171 |
834 SkMatrix vm = viewMatrix; | 1172 GrBatch* batch = AAHairlineBatch::Create(geometry, fLinesIndexBuffer, fQuads IndexBuffer); |
835 SkMatrix invert = SkMatrix::I(); | 1173 target->drawBatch(pipelineBuilder, batch, &devRect); |
836 if (!viewMatrix.hasPerspective()) { | |
837 vm = SkMatrix::I(); | |
838 if (!viewMatrix.invert(&invert)) { | |
839 return false; | |
840 } | |
841 } | |
842 | |
843 // do lines first | |
844 if (lineCnt) { | |
845 GrDrawTarget::AutoReleaseGeometry arg; | |
846 SkRect devBounds; | |
847 | |
848 GrPipelineBuilder::AutoRestoreEffects are(pipelineBuilder); | |
849 uint32_t gpFlags = GrDefaultGeoProcFactory::kPosition_GPType | | |
850 GrDefaultGeoProcFactory::kCoverage_GPType; | |
851 SkAutoTUnref<const GrGeometryProcessor> gp(GrDefaultGeoProcFactory::Crea te(gpFlags, | |
852 color, | |
853 vm, | |
854 invert, | |
855 false, | |
856 newCoverage)); | |
857 | |
858 if (!this->createLineGeom(target, | |
859 pipelineBuilder, | |
860 viewMatrix, | |
861 newCoverage, | |
862 gp->getVertexStride(), | |
863 &arg, | |
864 &devBounds, | |
865 path, | |
866 lines, | |
867 lineCnt)) { | |
868 return false; | |
869 } | |
870 | |
871 // Check devBounds | |
872 SkASSERT(check_bounds<LineVertex>(viewMatrix.hasPerspective() ? viewMatr ix : SkMatrix::I(), | |
873 devBounds, | |
874 arg.vertices(), | |
875 kLineSegNumVertices * lineCnt)); | |
876 | |
877 { | |
878 target->setIndexSourceToBuffer(fLinesIndexBuffer); | |
879 int lines = 0; | |
880 while (lines < lineCnt) { | |
881 int n = SkTMin(lineCnt - lines, kLineSegsNumInIdxBuffer); | |
882 target->drawIndexed(pipelineBuilder, | |
883 gp, | |
884 kTriangles_GrPrimitiveType, | |
885 kLineSegNumVertices*lines, // startV | |
886 0, // startI | |
887 kLineSegNumVertices*n, // vCount | |
888 kIdxsPerLineSeg*n, // iCount | |
889 &devBounds); | |
890 lines += n; | |
891 } | |
892 } | |
893 } | |
894 | |
895 // then quadratics/conics | |
896 if (quadCnt || conicCnt) { | |
897 GrDrawTarget::AutoReleaseGeometry arg; | |
898 SkRect devBounds; | |
899 | |
900 if (!this->createBezierGeom(target, | |
901 pipelineBuilder, | |
902 viewMatrix, | |
903 &arg, | |
904 &devBounds, | |
905 path, | |
906 quads, | |
907 quadCnt, | |
908 conics, | |
909 conicCnt, | |
910 qSubdivs, | |
911 cWeights, | |
912 sizeof(BezierVertex))) { | |
913 return false; | |
914 } | |
915 | |
916 // Check devBounds | |
917 SkASSERT(check_bounds<BezierVertex>(viewMatrix.hasPerspective() ? viewMa trix : | |
918 SkMatr ix::I(), | |
919 devBounds, | |
920 arg.vertices(), | |
921 kQuadNumVertices * quadCnt + | |
922 kQuadNumVertices * conicCnt)); | |
923 | |
924 if (quadCnt > 0) { | |
925 SkAutoTUnref<GrGeometryProcessor> hairQuadProcessor( | |
926 GrQuadEffect::Create(color, | |
927 vm, | |
928 kHairlineAA_GrProcessorEdgeType, | |
929 *target->caps(), | |
930 invert, | |
931 newCoverage)); | |
932 SkASSERT(hairQuadProcessor); | |
933 GrPipelineBuilder::AutoRestoreEffects are(pipelineBuilder); | |
934 target->setIndexSourceToBuffer(fQuadsIndexBuffer); | |
935 | |
936 int quads = 0; | |
937 while (quads < quadCnt) { | |
938 int n = SkTMin(quadCnt - quads, kQuadsNumInIdxBuffer); | |
939 target->drawIndexed(pipelineBuilder, | |
940 hairQuadProcessor, | |
941 kTriangles_GrPrimitiveType, | |
942 kQuadNumVertices*quads, // sta rtV | |
943 0, // sta rtI | |
944 kQuadNumVertices*n, // vCo unt | |
945 kIdxsPerQuad*n, // iCo unt | |
946 &devBounds); | |
947 quads += n; | |
948 } | |
949 } | |
950 | |
951 if (conicCnt > 0) { | |
952 SkAutoTUnref<GrGeometryProcessor> hairConicProcessor( | |
953 GrConicEffect::Create(color, vm, kHairlineAA_GrProcessorEdge Type, | |
954 *target->caps(), invert, newCoverage)) ; | |
955 SkASSERT(hairConicProcessor); | |
956 GrPipelineBuilder::AutoRestoreEffects are(pipelineBuilder); | |
957 target->setIndexSourceToBuffer(fQuadsIndexBuffer); | |
958 | |
959 int conics = 0; | |
960 while (conics < conicCnt) { | |
961 int n = SkTMin(conicCnt - conics, kQuadsNumInIdxBuffer); | |
962 target->drawIndexed(pipelineBuilder, | |
963 hairConicProcessor, | |
964 kTriangles_GrPrimitiveType, | |
965 kQuadNumVertices*(quadCnt + conics), // sta rtV | |
966 0, // sta rtI | |
967 kQuadNumVertices*n, // vCo unt | |
968 kIdxsPerQuad*n, // iCo unt | |
969 &devBounds); | |
970 conics += n; | |
971 } | |
972 } | |
973 } | |
974 | |
975 target->resetIndexSource(); | |
976 | 1174 |
977 return true; | 1175 return true; |
978 } | 1176 } |
OLD | NEW |