GrFragmentProcessor* GrConvexPolyEffect::Create(GrPrimitiveEdgeType type, const SkPath& path, const SkVector* offset) { if (kHairlineAA_GrProcessorEdgeType == type) { return NULL; } if (path.getSegmentMasks() != SkPath::kLine_SegmentMask || !path.isConvex()) { return NULL; } if (path.countPoints() > kMaxEdges) { return NULL; } SkPoint pts[kMaxEdges]; SkScalar edges[3 * kMaxEdges]; SkPath::Direction dir; SkAssertResult(path.cheapComputeDirection(&dir)); SkVector t; if (NULL == offset) { t.set(0, 0); } else { t = *offset; } int count = path.getPoints(pts, kMaxEdges); int n = 0; for (int lastPt = count - 1, i = 0; i < count; lastPt = i++) { if (pts[lastPt] != pts[i]) { SkVector v = pts[i] - pts[lastPt]; v.normalize(); if (SkPath::kCCW_Direction == dir) { edges[3 * n] = v.fY; edges[3 * n + 1] = -v.fX; } else { edges[3 * n] = -v.fY; edges[3 * n + 1] = v.fX; } SkPoint p = pts[i] + t; edges[3 * n + 2] = -(edges[3 * n] * p.fX + edges[3 * n + 1] * p.fY); ++n; } } if (path.isInverseFillType()) { type = GrInvertProcessorEdgeType(type); } return Create(type, n, edges); }
static void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice, const SkMatrix* toSrc, BezierVertex verts[kQuadNumVertices]) { SkASSERT(!toDevice == !toSrc); // original quad is specified by tri a,b,c SkPoint a = qpts[0]; SkPoint b = qpts[1]; SkPoint c = qpts[2]; if (toDevice) { toDevice->mapPoints(&a, 1); toDevice->mapPoints(&b, 1); toDevice->mapPoints(&c, 1); } // make a new poly where we replace a and c by a 1-pixel wide edges orthog // to edges ab and bc: // // before | after // | b0 // b | // | // | a0 c0 // a c | a1 c1 // // edges a0->b0 and b0->c0 are parallel to original edges a->b and b->c, // respectively. BezierVertex& a0 = verts[0]; BezierVertex& a1 = verts[1]; BezierVertex& b0 = verts[2]; BezierVertex& c0 = verts[3]; BezierVertex& c1 = verts[4]; SkVector ab = b; ab -= a; SkVector ac = c; ac -= a; SkVector cb = b; cb -= c; // We should have already handled degenerates SkASSERT(ab.length() > 0 && cb.length() > 0); ab.normalize(); SkVector abN; abN.setOrthog(ab, SkVector::kLeft_Side); if (abN.dot(ac) > 0) { abN.negate(); } cb.normalize(); SkVector cbN; cbN.setOrthog(cb, SkVector::kLeft_Side); if (cbN.dot(ac) < 0) { cbN.negate(); } a0.fPos = a; a0.fPos += abN; a1.fPos = a; a1.fPos -= abN; c0.fPos = c; c0.fPos += cbN; c1.fPos = c; c1.fPos -= cbN; intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos); if (toSrc) { toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(BezierVertex), kQuadNumVertices); } }
void SkPathStroker::cubic_to(const SkPoint pts[4], const SkVector& normalAB, const SkVector& unitNormalAB, SkVector* normalCD, SkVector* unitNormalCD, int subDivide) { SkVector ab = pts[1] - pts[0]; SkVector cd = pts[3] - pts[2]; SkVector normalBC, unitNormalBC; bool degenerateAB = degenerate_vector(ab); bool degenerateCD = degenerate_vector(cd); if (degenerateAB && degenerateCD) { DRAW_LINE: this->line_to(pts[3], normalAB); *normalCD = normalAB; *unitNormalCD = unitNormalAB; return; } if (degenerateAB) { ab = pts[2] - pts[0]; degenerateAB = degenerate_vector(ab); } if (degenerateCD) { cd = pts[3] - pts[1]; degenerateCD = degenerate_vector(cd); } if (degenerateAB || degenerateCD) { goto DRAW_LINE; } SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD)); bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius, &normalBC, &unitNormalBC); if (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) || normals_too_curvy(unitNormalBC, *unitNormalCD)) { // subdivide if we can if (--subDivide < 0) { goto DRAW_LINE; } SkPoint tmp[7]; SkVector norm, unit, dummy, unitDummy; SkChopCubicAtHalf(pts, tmp); this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide); // we use dummys since we already have a valid (and more accurate) // normals for CD this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide); } else { SkVector normalB, normalC; // need normals to inset/outset the off-curve pts B and C if (0) { // this is normal to the line between our adjacent pts normalB = pts[2] - pts[0]; normalB.rotateCCW(); SkAssertResult(normalB.setLength(fRadius)); normalC = pts[3] - pts[1]; normalC.rotateCCW(); SkAssertResult(normalC.setLength(fRadius)); } else { // miter-join SkVector unitBC = pts[2] - pts[1]; unitBC.normalize(); unitBC.rotateCCW(); normalB = unitNormalAB + unitBC; normalC = *unitNormalCD + unitBC; SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC); SkAssertResult(normalB.setLength(SkScalarDiv(fRadius, SkScalarSqrt((SK_Scalar1 + dot)/2)))); dot = SkPoint::DotProduct(*unitNormalCD, unitBC); SkAssertResult(normalC.setLength(SkScalarDiv(fRadius, SkScalarSqrt((SK_Scalar1 + dot)/2)))); } fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, pts[2].fX + normalC.fX, pts[2].fY + normalC.fY, pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY); fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, pts[2].fX - normalC.fX, pts[2].fY - normalC.fY, pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY); } }
static void create_vertices(const SegmentArray& segments, const SkPoint& fanPt, DrawArray* draws, QuadVertex* verts, uint16_t* idxs) { Draw* draw = &draws->push_back(); // alias just to make vert/index assignments easier to read. int* v = &draw->fVertexCnt; int* i = &draw->fIndexCnt; int count = segments.count(); for (int a = 0; a < count; ++a) { const Segment& sega = segments[a]; int b = (a + 1) % count; const Segment& segb = segments[b]; // Check whether adding the verts for this segment to the current draw would cause index // values to overflow. int vCount = 4; if (Segment::kLine == segb.fType) { vCount += 5; } else { vCount += 6; } if (draw->fVertexCnt + vCount > (1 << 16)) { verts += *v; idxs += *i; draw = &draws->push_back(); v = &draw->fVertexCnt; i = &draw->fIndexCnt; } // FIXME: These tris are inset in the 1 unit arc around the corner verts[*v + 0].fPos = sega.endPt(); verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm(); verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid; verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0]; verts[*v + 0].fUV.set(0,0); verts[*v + 1].fUV.set(0,-SK_Scalar1); verts[*v + 2].fUV.set(0,-SK_Scalar1); verts[*v + 3].fUV.set(0,-SK_Scalar1); verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1; verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1; verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1; verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1; idxs[*i + 0] = *v + 0; idxs[*i + 1] = *v + 2; idxs[*i + 2] = *v + 1; idxs[*i + 3] = *v + 0; idxs[*i + 4] = *v + 3; idxs[*i + 5] = *v + 2; *v += 4; *i += 6; if (Segment::kLine == segb.fType) { verts[*v + 0].fPos = fanPt; verts[*v + 1].fPos = sega.endPt(); verts[*v + 2].fPos = segb.fPts[0]; verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0]; verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0]; // we draw the line edge as a degenerate quad (u is 0, v is the // signed distance to the edge) SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos, verts[*v + 2].fPos); verts[*v + 0].fUV.set(0, dist); verts[*v + 1].fUV.set(0, 0); verts[*v + 2].fUV.set(0, 0); verts[*v + 3].fUV.set(0, -SK_Scalar1); verts[*v + 4].fUV.set(0, -SK_Scalar1); verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1; verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1; verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1; verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1; verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1; idxs[*i + 0] = *v + 0; idxs[*i + 1] = *v + 2; idxs[*i + 2] = *v + 1; idxs[*i + 3] = *v + 3; idxs[*i + 4] = *v + 1; idxs[*i + 5] = *v + 2; idxs[*i + 6] = *v + 4; idxs[*i + 7] = *v + 3; idxs[*i + 8] = *v + 2; *v += 5; *i += 9; } else { SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]}; SkVector midVec = segb.fNorms[0] + segb.fNorms[1]; midVec.normalize(); verts[*v + 0].fPos = fanPt; verts[*v + 1].fPos = qpts[0]; verts[*v + 2].fPos = qpts[2]; verts[*v + 3].fPos = qpts[0] + segb.fNorms[0]; verts[*v + 4].fPos = qpts[2] + segb.fNorms[1]; verts[*v + 5].fPos = qpts[1] + midVec; SkScalar c = segb.fNorms[0].dot(qpts[0]); verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c; verts[*v + 1].fD0 = 0.f; verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c; verts[*v + 3].fD0 = -SK_ScalarMax/100; verts[*v + 4].fD0 = -SK_ScalarMax/100; verts[*v + 5].fD0 = -SK_ScalarMax/100; c = segb.fNorms[1].dot(qpts[2]); verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c; verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c; verts[*v + 2].fD1 = 0.f; verts[*v + 3].fD1 = -SK_ScalarMax/100; verts[*v + 4].fD1 = -SK_ScalarMax/100; verts[*v + 5].fD1 = -SK_ScalarMax/100; GrPathUtils::QuadUVMatrix toUV(qpts); toUV.apply<6, sizeof(QuadVertex), sizeof(SkPoint)>(verts + *v); idxs[*i + 0] = *v + 3; idxs[*i + 1] = *v + 1; idxs[*i + 2] = *v + 2; idxs[*i + 3] = *v + 4; idxs[*i + 4] = *v + 3; idxs[*i + 5] = *v + 2; idxs[*i + 6] = *v + 5; idxs[*i + 7] = *v + 3; idxs[*i + 8] = *v + 4; idxs[*i + 9] = *v + 0; idxs[*i + 10] = *v + 2; idxs[*i + 11] = *v + 1; *v += 6; *i += 12; } } }
GrFragmentProcessor* GrConvexPolyEffect::Create(GrPrimitiveEdgeType type, const SkPath& path, const SkVector* offset) { if (kHairlineAA_GrProcessorEdgeType == type) { return nullptr; } if (path.getSegmentMasks() != SkPath::kLine_SegmentMask || !path.isConvex()) { return nullptr; } SkPathPriv::FirstDirection dir; // The only way this should fail is if the clip is effectively a infinitely thin line. In that // case nothing is inside the clip. It'd be nice to detect this at a higher level and either // skip the draw or omit the clip element. if (!SkPathPriv::CheapComputeFirstDirection(path, &dir)) { if (GrProcessorEdgeTypeIsInverseFill(type)) { return GrConstColorProcessor::Create(0xFFFFFFFF, GrConstColorProcessor::kModulateRGBA_InputMode); } return GrConstColorProcessor::Create(0, GrConstColorProcessor::kIgnore_InputMode); } SkVector t; if (nullptr == offset) { t.set(0, 0); } else { t = *offset; } SkScalar edges[3 * kMaxEdges]; SkPoint pts[4]; SkPath::Verb verb; SkPath::Iter iter(path, true); // SkPath considers itself convex so long as there is a convex contour within it, // regardless of any degenerate contours such as a string of moveTos before it. // Iterate here to consume any degenerate contours and only process the points // on the actual convex contour. int n = 0; while ((verb = iter.next(pts, true, true)) != SkPath::kDone_Verb) { switch (verb) { case SkPath::kMove_Verb: SkASSERT(n == 0); case SkPath::kClose_Verb: break; case SkPath::kLine_Verb: { if (n >= kMaxEdges) { return nullptr; } SkVector v = pts[1] - pts[0]; v.normalize(); if (SkPathPriv::kCCW_FirstDirection == dir) { edges[3 * n] = v.fY; edges[3 * n + 1] = -v.fX; } else { edges[3 * n] = -v.fY; edges[3 * n + 1] = v.fX; } SkPoint p = pts[1] + t; edges[3 * n + 2] = -(edges[3 * n] * p.fX + edges[3 * n + 1] * p.fY); ++n; break; } default: return nullptr; } } if (path.isInverseFillType()) { type = GrInvertProcessorEdgeType(type); } return Create(type, n, edges); }