static void addPolyCurveToPath(const TTPOLYCURVE* polyCurve, SkPath* path) { switch (polyCurve->wType) { case TT_PRIM_LINE: for (WORD i = 0; i < polyCurve->cpfx; i++) { path->lineTo(FIXEDToSkScalar(polyCurve->apfx[i].x), -FIXEDToSkScalar(polyCurve->apfx[i].y)); } break; case TT_PRIM_QSPLINE: // FIXME: doesn't this duplicate points if we do the loop > once? for (WORD i = 0; i < polyCurve->cpfx - 1; i++) { SkScalar bx = FIXEDToSkScalar(polyCurve->apfx[i].x); SkScalar by = FIXEDToSkScalar(polyCurve->apfx[i].y); SkScalar cx = FIXEDToSkScalar(polyCurve->apfx[i + 1].x); SkScalar cy = FIXEDToSkScalar(polyCurve->apfx[i + 1].y); if (i < polyCurve->cpfx - 2) { // We're not the last point, compute C. cx = SkScalarAve(bx, cx); cy = SkScalarAve(by, cy); } // Need to flip the y coordinates since the font's coordinate system is // flipped from ours vertically. path->quadTo(bx, -by, cx, -cy); } break; case TT_PRIM_CSPLINE: // FIXME break; } }
/* TODO Need differentially more subdivisions when the follow-path is curvy. Not sure how to determine that, but we need it. I guess a cheap answer is let the caller tell us, but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out. */ static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas, SkScalar dist) { SkPath::Iter iter(src, false); SkPoint srcP[4], dstP[3]; SkPath::Verb verb; while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) { switch (verb) { case SkPath::kMove_Verb: morphpoints(dstP, srcP, 1, meas, dist); dst->moveTo(dstP[0]); break; case SkPath::kLine_Verb: srcP[2] = srcP[1]; srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX), SkScalarAve(srcP[0].fY, srcP[2].fY)); // fall through to quad case SkPath::kQuad_Verb: morphpoints(dstP, &srcP[1], 2, meas, dist); dst->quadTo(dstP[0], dstP[1]); break; case SkPath::kCubic_Verb: morphpoints(dstP, &srcP[1], 3, meas, dist); dst->cubicTo(dstP[0], dstP[1], dstP[2]); break; case SkPath::kClose_Verb: dst->close(); break; default: SkDEBUGFAIL("unknown verb"); break; } } }
static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) { SkPoint center; center.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); return SkGradientShader::CreateRadial(center, center.fX, data.fColors, data.fPos, data.fCount, tm); }
static sk_sp<SkShader> make_radial_gradient(const SkPoint pts[2], const SkMatrix& localMatrix) { SkPoint center; center.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); float radius = (center - pts[0]).length(); return SkGradientShader::MakeRadial(center, radius, gColors, nullptr, SK_ARRAY_COUNT(gColors), SkTileMode::kClamp, 0, &localMatrix); }
static sk_sp<SkShader> MakeSweep(const SkPoint pts[2], const GradData& data, SkShader::TileMode, const SkMatrix& localMatrix) { SkPoint center; center.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount, 0, &localMatrix); }
static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm, SkUnitMapper* mapper) { SkPoint center; center.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount, mapper); }
static SkShader* make_radial_gradient(const SkPoint pts[2], const SkMatrix& localMatrix) { SkPoint center; center.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); float radius = (center - pts[0]).length(); return SkGradientShader::CreateRadial(center, radius, gColors, NULL, SK_ARRAY_COUNT(gColors), SkShader::kClamp_TileMode, 0, &localMatrix); }
static sk_sp<SkShader> MakeRadial4f(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm, const SkMatrix& localMatrix) { SkPoint center; center.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); auto srgb = SkColorSpace::MakeSRGBLinear(); return SkGradientShader::MakeRadial(center, center.fX, data.fColors4f, srgb, data.fPos, data.fCount, tm, 0, &localMatrix); }
static SkShader* Make2ConicalConcentric(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) { SkPoint center; center.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); return SkGradientShader::CreateTwoPointConical( center, (pts[1].fX - pts[0].fX) / 7, center, (pts[1].fX - pts[0].fX) / 2, data.fColors, data.fPos, data.fCount, tm); }
static sk_sp<SkShader> Make2Conical(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) { SkPoint center0, center1; center0.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7, center0, (pts[1].fX - pts[0].fX) / 2, data.fColors, data.fPos, data.fCount, tm); }
SkPoint SkBoundaryPatch::eval(SkScalar unitU, SkScalar unitV) { SkBoundary* b = fBoundary; SkPoint u = SkPointInterp(b->eval(SkBoundary::kLeft, SK_Scalar1 - unitV), b->eval(SkBoundary::kRight, unitV), unitU); SkPoint v = SkPointInterp(b->eval(SkBoundary::kTop, unitU), b->eval(SkBoundary::kBottom, SK_Scalar1 - unitU), unitV); return SkMakePoint(SkScalarAve(u.fX, v.fX), SkScalarAve(u.fY, v.fY)); }
static sk_sp<SkShader> Make2ConicalTouchY(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm, const SkMatrix& localMatrix) { SkPoint center0, center1; SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7; SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; center1.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); center0.set(center1.fX, center1.fY + radius1 - radius0); return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors, data.fPos, data.fCount, tm, 0, &localMatrix); }
static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm, SkUnitMapper* mapper) { SkPoint center0, center1; center0.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); return SkGradientShader::CreateTwoPointRadial( center1, (pts[1].fX - pts[0].fX) / 7, center0, (pts[1].fX - pts[0].fX) / 2, data.fColors, data.fPos, data.fCount, tm, mapper); }
static SkShader* Make2ConicalInsideCenter(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm, const SkMatrix& localMatrix) { SkPoint center0, center1; center0.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); return SkGradientShader::CreateTwoPointConical(center0, (pts[1].fX - pts[0].fX) / 7, center0, (pts[1].fX - pts[0].fX) / 2, data.fColors, data.fPos, data.fCount, tm, 0, &localMatrix); }
static sk_sp<SkShader> Make2Radial4f(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm, const SkMatrix& localMatrix) { SkPoint center0, center1; center0.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3) / 5), SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1) / 4)); auto srgb = SkColorSpace::MakeSRGBLinear(); return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7, center0, (pts[1].fX - pts[0].fX) / 2, data.fColors4f, srgb, data.fPos, data.fCount, tm, 0, &localMatrix); }
static SkShader* Make2ConicalTouchX(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm, const SkMatrix& localMatrix) { SkPoint center0, center1; SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 7); SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3); center1.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); center0.set(center1.fX - radius1 + radius0, center1.fY); return SkGradientShader::CreateTwoPointConical(center0, radius0, center1, radius1, data.fColors, data.fPos, data.fCount, tm, 0, &localMatrix); }
static SkShader* Make2ConicalZeroRadEdgeY(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm, const SkMatrix& localMatrix) { SkPoint center0, center1; SkScalar radius0 = 0.f; SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; center1.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); center0.set(center1.fX, center1.fY + radius1); return SkGradientShader::CreateTwoPointConical(center0, radius0, center1, radius1, data.fColors, data.fPos, data.fCount, tm, 0, &localMatrix); }
/* Given 4 cubic points (either Xs or Ys), and a target X or Y, compute the t value such that cubic(t) = target */ static bool chopMonoCubicAt(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3, SkScalar target, SkScalar* t) { // SkASSERT(c0 <= c1 && c1 <= c2 && c2 <= c3); SkASSERT(c0 < target && target < c3); SkScalar D = c0 - target; SkScalar A = c3 + 3*(c1 - c2) - c0; SkScalar B = 3*(c2 - c1 - c1 + c0); SkScalar C = 3*(c1 - c0); const SkScalar TOLERANCE = SK_Scalar1 / 4096; SkScalar minT = 0; SkScalar maxT = SK_Scalar1; SkScalar mid; int i; for (i = 0; i < 16; i++) { mid = SkScalarAve(minT, maxT); SkScalar delta = eval_cubic_coeff(A, B, C, D, mid); if (delta < 0) { minT = mid; delta = -delta; } else { maxT = mid; } if (delta < TOLERANCE) { break; } } *t = mid; // SkDebugf("-- evalCubicAt %d delta %g\n", i, eval_cubic_coeff(A, B, C, D, *t)); return true; }
/* TODO Need differentially more subdivisions when the follow-path is curvy. Not sure how to determine that, but we need it. I guess a cheap answer is let the caller tell us, but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out. */ void TextArt::EnvelopeWarp::morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas, const SkMatrix& matrix) { SkPath::Iter iter(src, false); SkPoint srcP[4], dstP[3]; SkPath::Verb verb; while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) { switch (verb) { case SkPath::kMove_Verb: morphpoints(dstP, srcP, 1, meas, matrix); dst->moveTo(dstP[0]); break; case SkPath::kLine_Verb: /* morphpoints(dstP, &srcP[1], 1, meas, matrix); dst->lineTo(dstP[0]); break; */ // turn lines into quads to look bendy srcP[0].fX = SkScalarAve(srcP[0].fX, srcP[1].fX); srcP[0].fY = SkScalarAve(srcP[0].fY, srcP[1].fY); morphpoints(dstP, srcP, 2, meas, matrix); dst->quadTo(dstP[0], dstP[1]); break; case SkPath::kQuad_Verb: morphpoints(dstP, &srcP[1], 2, meas, matrix); dst->quadTo(dstP[0], dstP[1]); break; case SkPath::kCubic_Verb: morphpoints(dstP, &srcP[1], 3, meas, matrix); dst->cubicTo(dstP[0], dstP[1], dstP[2]); break; case SkPath::kClose_Verb: dst->close(); break; default: SkDEBUGFAIL("unknown verb"); break; } } }
// return X coordinate of intersection with horizontal line at Y static SkScalar sect_with_horizontal(const SkPoint src[2], SkScalar Y) { SkScalar dy = src[1].fY - src[0].fY; if (SkScalarNearlyZero(dy)) { return SkScalarAve(src[0].fX, src[1].fX); } else { return src[0].fX + SkScalarMulDiv(Y - src[0].fY, src[1].fX - src[0].fX, dy); } }
// return Y coordinate of intersection with vertical line at X static SkScalar sect_with_vertical(const SkPoint src[2], SkScalar X) { SkScalar dx = src[1].fX - src[0].fX; if (SkScalarNearlyZero(dx)) { return SkScalarAve(src[0].fY, src[1].fY); } else { return src[0].fY + SkScalarMulDiv(X - src[0].fX, src[1].fY - src[0].fY, dx); } }
/* * Spits out a dummy gradient to test blur with shader on paint */ static sk_sp<SkShader> MakeRadial() { SkPoint pts[2] = { { 0, 0 }, { SkIntToScalar(100), SkIntToScalar(100) } }; SkShader::TileMode tm = SkShader::kClamp_TileMode; const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, }; const SkScalar pos[] = { SK_Scalar1/4, SK_Scalar1*3/4 }; SkMatrix scale; scale.setScale(0.5f, 0.5f); scale.postTranslate(5.f, 5.f); SkPoint center0, center1; center0.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7, center0, (pts[1].fX - pts[0].fX) / 2, colors, pos, SK_ARRAY_COUNT(colors), tm, 0, &scale); }
// return Y coordinate of intersection with vertical line at X static SkScalar sect_with_vertical(const SkPoint src[2], SkScalar X) { SkScalar dx = src[1].fX - src[0].fX; if (SkScalarNearlyZero(dx)) { return SkScalarAve(src[0].fY, src[1].fY); } else { // need the extra precision so we don't compute a value that exceeds // our original limits double X0 = src[0].fX; double Y0 = src[0].fY; double X1 = src[1].fX; double Y1 = src[1].fY; double result = Y0 + ((double)X - X0) * (Y1 - Y0) / (X1 - X0); return (float)result; } }
void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]) { SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX); SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY); SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX); SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY); dst[0] = src[0]; dst[1].set(x01, y01); dst[2].set(SkScalarAve(x01, x12), SkScalarAve(y01, y12)); dst[3].set(x12, y12); dst[4] = src[2]; }
// return X coordinate of intersection with horizontal line at Y static SkScalar sect_with_horizontal(const SkPoint src[2], SkScalar Y) { SkScalar dy = src[1].fY - src[0].fY; if (SkScalarNearlyZero(dy)) { return SkScalarAve(src[0].fX, src[1].fX); } else { // need the extra precision so we don't compute a value that exceeds // our original limits double X0 = src[0].fX; double Y0 = src[0].fY; double X1 = src[1].fX; double Y1 = src[1].fY; double result = X0 + ((double)Y - Y0) * (X1 - X0) / (Y1 - Y0); // The computed X value might still exceed [X0..X1] due to quantum flux // when the doubles were added and subtracted, so we have to pin the // answer :( return (float)pin_unsorted(result, X0, X1); } }
// return X coordinate of intersection with horizontal line at Y static SkScalar sect_with_horizontal(const SkPoint src[2], SkScalar Y) { SkScalar dy = src[1].fY - src[0].fY; if (SkScalarNearlyZero(dy)) { return SkScalarAve(src[0].fX, src[1].fX); } else { #ifdef SK_SCALAR_IS_FLOAT // need the extra precision so we don't compute a value that exceeds // our original limits double X0 = src[0].fX; double Y0 = src[0].fY; double X1 = src[1].fX; double Y1 = src[1].fY; double result = X0 + ((double)Y - Y0) * (X1 - X0) / (Y1 - Y0); return (float)result; #else return src[0].fX + SkScalarMulDiv(Y - src[0].fY, src[1].fX - src[0].fX, dy); #endif } }
static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y) { SkScalar y0 = pts[0].fY; SkScalar y2 = pts[2].fY; int dir = 1; if (y0 > y2) { SkTSwap(y0, y2); dir = -1; } if (y < y0 || y >= y2) { return 0; } // bounds check on X (not required, but maybe faster) #if 0 if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) { return 0; } #endif SkScalar roots[2]; int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY, 2 * (pts[1].fY - pts[0].fY), pts[0].fY - y, roots); SkASSERT(n <= 1); SkScalar xt; if (0 == n) { SkScalar mid = SkScalarAve(y0, y2); // Need [0] and [2] if dir == 1 // and [2] and [0] if dir == -1 xt = y < mid ? pts[1 - dir].fX : pts[dir - 1].fX; } else { SkScalar t = roots[0]; SkScalar C = pts[0].fX; SkScalar A = pts[2].fX - 2 * pts[1].fX + C; SkScalar B = 2 * (pts[1].fX - C); xt = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C); } return xt < x ? dir : 0; }
void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt, SkVector* tangent) { SkASSERT(src); if (pt) { SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX); SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY); SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX); SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY); pt->set(SkScalarAve(x01, x12), SkScalarAve(y01, y12)); } if (tangent) { tangent->set(eval_quad_derivative_at_half(&src[0].fX), eval_quad_derivative_at_half(&src[0].fY)); } }
uint32_t GrPathUtils::generateQuadraticPoints(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, SkScalar tolSqd, SkPoint** points, uint32_t pointsLeft) { if (pointsLeft < 2 || (p1.distanceToLineSegmentBetweenSqd(p0, p2)) < tolSqd) { (*points)[0] = p2; *points += 1; return 1; } SkPoint q[] = { { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) }, { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) }, }; SkPoint r = { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) }; pointsLeft >>= 1; uint32_t a = generateQuadraticPoints(p0, q[0], r, tolSqd, points, pointsLeft); uint32_t b = generateQuadraticPoints(r, q[1], p2, tolSqd, points, pointsLeft); return a + b; }
// midPt sets the first argument to be the midpoint of the other two // it is used by quadApprox static inline void midPt(SkPoint& dest,const SkPoint& a,const SkPoint& b) { dest.set(SkScalarAve(a.fX, b.fX),SkScalarAve(a.fY, b.fY)); }