Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Macros | Functions
SkGeometry.cpp File Reference
#include "src/core/SkGeometry.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPoint3.h"
#include "include/core/SkRect.h"
#include "include/core/SkScalar.h"
#include "include/private/base/SkDebug.h"
#include "include/private/base/SkFloatingPoint.h"
#include "include/private/base/SkTPin.h"
#include "include/private/base/SkTo.h"
#include "src/base/SkBezierCurves.h"
#include "src/base/SkCubics.h"
#include "src/base/SkUtils.h"
#include "src/base/SkVx.h"
#include "src/core/SkPointPriv.h"
#include <algorithm>
#include <array>
#include <cmath>
#include <cstddef>
#include <cstdint>

Go to the source code of this file.

Macros

#define AS_QUAD_ERROR_SETUP
 
#define kMaxConicToQuadPOW2   5
 

Functions

int SkFindUnitQuadRoots (SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2])
 
void SkEvalQuadAt (const SkPoint src[3], SkScalar t, SkPoint *pt, SkVector *tangent)
 
SkPoint SkEvalQuadAt (const SkPoint src[3], SkScalar t)
 
SkVector SkEvalQuadTangentAt (const SkPoint src[3], SkScalar t)
 
static float2 interp (const float2 &v0, const float2 &v1, const float2 &t)
 
void SkChopQuadAt (const SkPoint src[3], SkPoint dst[5], SkScalar t)
 
void SkChopQuadAtHalf (const SkPoint src[3], SkPoint dst[5])
 
float SkMeasureAngleBetweenVectors (SkVector a, SkVector b)
 
SkVector SkFindBisector (SkVector a, SkVector b)
 
float SkFindQuadMidTangent (const SkPoint src[3])
 
int SkFindQuadExtrema (SkScalar a, SkScalar b, SkScalar c, SkScalar tValue[1])
 
static void flatten_double_quad_extrema (SkScalar coords[14])
 
int SkChopQuadAtYExtrema (const SkPoint src[3], SkPoint dst[5])
 
int SkChopQuadAtXExtrema (const SkPoint src[3], SkPoint dst[5])
 
SkScalar SkFindQuadMaxCurvature (const SkPoint src[3])
 
int SkChopQuadAtMaxCurvature (const SkPoint src[3], SkPoint dst[5])
 
void SkConvertQuadToCubic (const SkPoint src[3], SkPoint dst[4])
 
static SkVector eval_cubic_derivative (const SkPoint src[4], SkScalar t)
 
static SkVector eval_cubic_2ndDerivative (const SkPoint src[4], SkScalar t)
 
void SkEvalCubicAt (const SkPoint src[4], SkScalar t, SkPoint *loc, SkVector *tangent, SkVector *curvature)
 
int SkFindCubicExtrema (SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2])
 
template<int N, typename T >
static skvx::Vec< N, Tunchecked_mix (const skvx::Vec< N, T > &a, const skvx::Vec< N, T > &b, const skvx::Vec< N, T > &t)
 
void SkChopCubicAt (const SkPoint src[4], SkPoint dst[7], SkScalar t)
 
void SkChopCubicAt (const SkPoint src[4], SkPoint dst[10], float t0, float t1)
 
void SkChopCubicAt (const SkPoint src[4], SkPoint dst[], const SkScalar tValues[], int tCount)
 
void SkChopCubicAtHalf (const SkPoint src[4], SkPoint dst[7])
 
float SkMeasureNonInflectCubicRotation (const SkPoint pts[4])
 
static skvx::float4 fma (const skvx::float4 &f, float m, const skvx::float4 &a)
 
static float solve_quadratic_equation_for_midtangent (float a, float b, float c, float discr)
 
static float solve_quadratic_equation_for_midtangent (float a, float b, float c)
 
float SkFindCubicMidTangent (const SkPoint src[4])
 
static void flatten_double_cubic_extrema (SkScalar coords[14])
 
int SkChopCubicAtYExtrema (const SkPoint src[4], SkPoint dst[10])
 
int SkChopCubicAtXExtrema (const SkPoint src[4], SkPoint dst[10])
 
int SkFindCubicInflections (const SkPoint src[4], SkScalar tValues[2])
 
int SkChopCubicAtInflections (const SkPoint src[4], SkPoint dst[10])
 
static double calc_dot_cross_cubic (const SkPoint &p0, const SkPoint &p1, const SkPoint &p2)
 
static double previous_inverse_pow2 (double n)
 
static void write_cubic_inflection_roots (double t0, double s0, double t1, double s1, double *t, double *s)
 
SkCubicType SkClassifyCubic (const SkPoint P[4], double t[2], double s[2], double d[4])
 
template<typename T >
void bubble_sort (T array[], int count)
 
static int collaps_duplicates (SkScalar array[], int count)
 
static SkScalar SkScalarCubeRoot (SkScalar x)
 
static int solve_cubic_poly (const SkScalar coeff[4], SkScalar tValues[3])
 
static void formulate_F1DotF2 (const SkScalar src[], SkScalar coeff[4])
 
int SkFindCubicMaxCurvature (const SkPoint src[4], SkScalar tValues[3])
 
int SkChopCubicAtMaxCurvature (const SkPoint src[4], SkPoint dst[13], SkScalar tValues[3])
 
static SkScalar calc_cubic_precision (const SkPoint src[4])
 
static bool on_same_side (const SkPoint src[4], int testIndex, int lineIndex)
 
SkScalar SkFindCubicCusp (const SkPoint src[4])
 
static bool close_enough_to_zero (double x)
 
static bool first_axis_intersection (const double coefficients[8], bool yDirection, double axisIntercept, double *solution)
 
bool SkChopMonoCubicAtY (const SkPoint src[4], SkScalar y, SkPoint dst[7])
 
bool SkChopMonoCubicAtX (const SkPoint src[4], SkScalar x, SkPoint dst[7])
 
static void conic_deriv_coeff (const SkScalar src[], SkScalar w, SkScalar coeff[3])
 
static bool conic_find_extrema (const SkScalar src[], SkScalar w, SkScalar *t)
 
static void p3d_interp (const SkScalar src[7], SkScalar dst[7], SkScalar t)
 
static void ratquad_mapTo3D (const SkPoint src[3], SkScalar w, SkPoint3 dst[3])
 
static SkPoint project_down (const SkPoint3 &src)
 
static SkScalar subdivide_w_value (SkScalar w)
 
static bool between (SkScalar a, SkScalar b, SkScalar c)
 
static SkPointsubdivide (const SkConic &src, SkPoint pts[], int level)
 

Macro Definition Documentation

◆ AS_QUAD_ERROR_SETUP

#define AS_QUAD_ERROR_SETUP
Value:
SkScalar a = fW - 1; \
SkScalar k = a / (4 * (2 + a)); \
SkScalar x = k * (fPts[0].fX - 2 * fPts[1].fX + fPts[2].fX); \
SkScalar y = k * (fPts[0].fY - 2 * fPts[1].fY + fPts[2].fY);
SkPoint fPts[2]
float SkScalar
Definition extension.cpp:12
struct MyStruct a[10]
double y
double x
float fX
x-axis value
float fY
y-axis value

Definition at line 1469 of file SkGeometry.cpp.

1474 {
1476 err->set(x, y);
1477}
1478
1479bool SkConic::asQuadTol(SkScalar tol) const {
1481 return (x * x + y * y) <= tol * tol;
1482}
1483
1484// Limit the number of suggested quads to approximate a conic
1485#define kMaxConicToQuadPOW2 5
1486
1487int SkConic::computeQuadPOW2(SkScalar tol) const {
1488 if (tol < 0 || !SkIsFinite(tol) || !SkPointPriv::AreFinite(fPts, 3)) {
1489 return 0;
1490 }
1491
1493
1494 SkScalar error = SkScalarSqrt(x * x + y * y);
1495 int pow2;
1496 for (pow2 = 0; pow2 < kMaxConicToQuadPOW2; ++pow2) {
1497 if (error <= tol) {
1498 break;
1499 }
1500 error *= 0.25f;
1501 }
1502 // float version -- using ceil gives the same results as the above.
1503 if ((false)) {
1504 SkScalar err = SkScalarSqrt(x * x + y * y);
1505 if (err <= tol) {
1506 return 0;
1507 }
1508 SkScalar tol2 = tol * tol;
1509 if (tol2 == 0) {
1510 return kMaxConicToQuadPOW2;
1511 }
1512 SkScalar fpow2 = SkScalarLog2((x * x + y * y) / tol2) * 0.25f;
1513 int altPow2 = SkScalarCeilToInt(fpow2);
1514 if (altPow2 != pow2) {
1515 SkDebugf("pow2 %d altPow2 %d fbits %g err %g tol %g\n", pow2, altPow2, fpow2, err, tol);
1516 }
1517 pow2 = altPow2;
1518 }
1519 return pow2;
1520}
1521
1522// This was originally developed and tested for pathops: see SkOpTypes.h
1523// returns true if (a <= b <= c) || (a >= b >= c)
1524static bool between(SkScalar a, SkScalar b, SkScalar c) {
1525 return (a - b) * (c - b) <= 0;
1526}
1527
1528static SkPoint* subdivide(const SkConic& src, SkPoint pts[], int level) {
1529 SkASSERT(level >= 0);
1530
1531 if (0 == level) {
1532 memcpy(pts, &src.fPts[1], 2 * sizeof(SkPoint));
1533 return pts + 2;
1534 } else {
1535 SkConic dst[2];
1536 src.chop(dst);
1537 const SkScalar startY = src.fPts[0].fY;
1538 SkScalar endY = src.fPts[2].fY;
1539 if (between(startY, src.fPts[1].fY, endY)) {
1540 // If the input is monotonic and the output is not, the scan converter hangs.
1541 // Ensure that the chopped conics maintain their y-order.
1542 SkScalar midY = dst[0].fPts[2].fY;
1543 if (!between(startY, midY, endY)) {
1544 // If the computed midpoint is outside the ends, move it to the closer one.
1545 SkScalar closerY = SkTAbs(midY - startY) < SkTAbs(midY - endY) ? startY : endY;
1546 dst[0].fPts[2].fY = dst[1].fPts[0].fY = closerY;
1547 }
1548 if (!between(startY, dst[0].fPts[1].fY, dst[0].fPts[2].fY)) {
1549 // If the 1st control is not between the start and end, put it at the start.
1550 // This also reduces the quad to a line.
1551 dst[0].fPts[1].fY = startY;
1552 }
1553 if (!between(dst[1].fPts[0].fY, dst[1].fPts[1].fY, endY)) {
1554 // If the 2nd control is not between the start and end, put it at the end.
1555 // This also reduces the quad to a line.
1556 dst[1].fPts[1].fY = endY;
1557 }
1558 // Verify that all five points are in order.
1559 SkASSERT(between(startY, dst[0].fPts[1].fY, dst[0].fPts[2].fY));
1560 SkASSERT(between(dst[0].fPts[1].fY, dst[0].fPts[2].fY, dst[1].fPts[1].fY));
1561 SkASSERT(between(dst[0].fPts[2].fY, dst[1].fPts[1].fY, endY));
1562 }
1563 --level;
1564 pts = subdivide(dst[0], pts, level);
1565 return subdivide(dst[1], pts, level);
1566 }
1567}
1568
1569int SkConic::chopIntoQuadsPOW2(SkPoint pts[], int pow2) const {
1570 SkASSERT(pow2 >= 0);
1571 *pts = fPts[0];
1572 SkDEBUGCODE(SkPoint* endPts);
1573 if (pow2 == kMaxConicToQuadPOW2) { // If an extreme weight generates many quads ...
1574 SkConic dst[2];
1575 this->chop(dst);
1576 // check to see if the first chop generates a pair of lines
1577 if (SkPointPriv::EqualsWithinTolerance(dst[0].fPts[1], dst[0].fPts[2]) &&
1578 SkPointPriv::EqualsWithinTolerance(dst[1].fPts[0], dst[1].fPts[1])) {
1579 pts[1] = pts[2] = pts[3] = dst[0].fPts[1]; // set ctrl == end to make lines
1580 pts[4] = dst[1].fPts[2];
1581 pow2 = 1;
1582 SkDEBUGCODE(endPts = &pts[5]);
1583 goto commonFinitePtCheck;
1584 }
1585 }
1586 SkDEBUGCODE(endPts = ) subdivide(*this, pts + 1, pow2);
1587commonFinitePtCheck:
1588 const int quadCount = 1 << pow2;
1589 const int ptCount = 2 * quadCount + 1;
1590 SkASSERT(endPts - pts == ptCount);
1591 if (!SkPointPriv::AreFinite(pts, ptCount)) {
1592 // if we generated a non-finite, pin ourselves to the middle of the hull,
1593 // as our first and last are already on the first/last pts of the hull.
1594 for (int i = 1; i < ptCount - 1; ++i) {
1595 pts[i] = fPts[1];
1596 }
1597 }
1598 return 1 << pow2;
1599}
1600
1601float SkConic::findMidTangent() const {
1602 // Tangents point in the direction of increasing T, so tan0 and -tan1 both point toward the
1603 // midtangent. The bisector of tan0 and -tan1 is orthogonal to the midtangent:
1604 //
1605 // bisector dot midtangent = 0
1606 //
1607 SkVector tan0 = fPts[1] - fPts[0];
1608 SkVector tan1 = fPts[2] - fPts[1];
1609 SkVector bisector = SkFindBisector(tan0, -tan1);
1610
1611 // Start by finding the tangent function's power basis coefficients. These define a tangent
1612 // direction (scaled by some uniform value) as:
1613 // |T^2|
1614 // Tangent_Direction(T) = dx,dy = |A B C| * |T |
1615 // |. . .| |1 |
1616 //
1617 // The derivative of a conic has a cumbersome order-4 denominator. However, this isn't necessary
1618 // if we are only interested in a vector in the same *direction* as a given tangent line. Since
1619 // the denominator scales dx and dy uniformly, we can throw it out completely after evaluating
1620 // the derivative with the standard quotient rule. This leaves us with a simpler quadratic
1621 // function that we use to find a tangent.
1622 SkVector A = (fPts[2] - fPts[0]) * (fW - 1);
1623 SkVector B = (fPts[2] - fPts[0]) - (fPts[1] - fPts[0]) * (fW*2);
1624 SkVector C = (fPts[1] - fPts[0]) * fW;
1625
1626 // Now solve for "bisector dot midtangent = 0":
1627 //
1628 // |T^2|
1629 // bisector * |A B C| * |T | = 0
1630 // |. . .| |1 |
1631 //
1632 float a = bisector.dot(A);
1633 float b = bisector.dot(B);
1634 float c = bisector.dot(C);
1636}
1637
1638bool SkConic::findXExtrema(SkScalar* t) const {
1639 return conic_find_extrema(&fPts[0].fX, fW, t);
1640}
1641
1642bool SkConic::findYExtrema(SkScalar* t) const {
1643 return conic_find_extrema(&fPts[0].fY, fW, t);
1644}
1645
1646bool SkConic::chopAtXExtrema(SkConic dst[2]) const {
1647 SkScalar t;
1648 if (this->findXExtrema(&t)) {
1649 if (!this->chopAt(t, dst)) {
1650 // if chop can't return finite values, don't chop
1651 return false;
1652 }
1653 // now clean-up the middle, since we know t was meant to be at
1654 // an X-extrema
1655 SkScalar value = dst[0].fPts[2].fX;
1656 dst[0].fPts[1].fX = value;
1657 dst[1].fPts[0].fX = value;
1658 dst[1].fPts[1].fX = value;
1659 return true;
1660 }
1661 return false;
1662}
1663
1664bool SkConic::chopAtYExtrema(SkConic dst[2]) const {
1665 SkScalar t;
1666 if (this->findYExtrema(&t)) {
1667 if (!this->chopAt(t, dst)) {
1668 // if chop can't return finite values, don't chop
1669 return false;
1670 }
1671 // now clean-up the middle, since we know t was meant to be at
1672 // an Y-extrema
1673 SkScalar value = dst[0].fPts[2].fY;
1674 dst[0].fPts[1].fY = value;
1675 dst[1].fPts[0].fY = value;
1676 dst[1].fPts[1].fY = value;
1677 return true;
1678 }
1679 return false;
1680}
1681
1682void SkConic::computeTightBounds(SkRect* bounds) const {
1683 SkPoint pts[4];
1684 pts[0] = fPts[0];
1685 pts[1] = fPts[2];
1686 int count = 2;
1687
1688 SkScalar t;
1689 if (this->findXExtrema(&t)) {
1690 this->evalAt(t, &pts[count++]);
1691 }
1692 if (this->findYExtrema(&t)) {
1693 this->evalAt(t, &pts[count++]);
1694 }
1695 bounds->setBounds(pts, count);
1696}
1697
1698void SkConic::computeFastBounds(SkRect* bounds) const {
1699 bounds->setBounds(fPts, 3);
1700}
1701
1702#if 0 // unimplemented
1703bool SkConic::findMaxCurvature(SkScalar* t) const {
1704 // TODO: Implement me
1705 return false;
1706}
1707#endif
1708
1709SkScalar SkConic::TransformW(const SkPoint pts[3], SkScalar w, const SkMatrix& matrix) {
1710 if (!matrix.hasPerspective()) {
1711 return w;
1712 }
1713
1714 SkPoint3 src[3], dst[3];
1715
1716 ratquad_mapTo3D(pts, w, src);
1717
1718 matrix.mapHomogeneousPoints(dst, src, 3);
1719
1720 // w' = sqrt(w1*w1/w0*w2)
1721 // use doubles temporarily, to handle small numer/denom
1722 double w0 = dst[0].fZ;
1723 double w1 = dst[1].fZ;
1724 double w2 = dst[2].fZ;
1725 return sk_double_to_float(sqrt(sk_ieee_double_divide(w1 * w1, w0 * w2)));
1726}
1727
1728int SkConic::BuildUnitArc(const SkVector& uStart, const SkVector& uStop, SkRotationDirection dir,
1729 const SkMatrix* userMatrix, SkConic dst[kMaxConicsForArc]) {
1730 // rotate by x,y so that uStart is (1.0)
1731 SkScalar x = SkPoint::DotProduct(uStart, uStop);
1732 SkScalar y = SkPoint::CrossProduct(uStart, uStop);
1733
1734 SkScalar absY = SkScalarAbs(y);
1735
1736 // check for (effectively) coincident vectors
1737 // this can happen if our angle is nearly 0 or nearly 180 (y == 0)
1738 // ... we use the dot-prod to distinguish between 0 and 180 (x > 0)
1739 if (absY <= SK_ScalarNearlyZero && x > 0 && ((y >= 0 && kCW_SkRotationDirection == dir) ||
1740 (y <= 0 && kCCW_SkRotationDirection == dir))) {
1741 return 0;
1742 }
1743
1744 if (dir == kCCW_SkRotationDirection) {
1745 y = -y;
1746 }
1747
1748 // We decide to use 1-conic per quadrant of a circle. What quadrant does [xy] lie in?
1749 // 0 == [0 .. 90)
1750 // 1 == [90 ..180)
1751 // 2 == [180..270)
1752 // 3 == [270..360)
1753 //
1754 int quadrant = 0;
1755 if (0 == y) {
1756 quadrant = 2; // 180
1758 } else if (0 == x) {
1760 quadrant = y > 0 ? 1 : 3; // 90 : 270
1761 } else {
1762 if (y < 0) {
1763 quadrant += 2;
1764 }
1765 if ((x < 0) != (y < 0)) {
1766 quadrant += 1;
1767 }
1768 }
1769
1770 const SkPoint quadrantPts[] = {
1771 { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 }, { 0, -1 }, { 1, -1 }
1772 };
1773 const SkScalar quadrantWeight = SK_ScalarRoot2Over2;
1774
1775 int conicCount = quadrant;
1776 for (int i = 0; i < conicCount; ++i) {
1777 dst[i].set(&quadrantPts[i * 2], quadrantWeight);
1778 }
1779
1780 // Now compute any remaing (sub-90-degree) arc for the last conic
1781 const SkPoint finalP = { x, y };
1782 const SkPoint& lastQ = quadrantPts[quadrant * 2]; // will already be a unit-vector
1783 const SkScalar dot = SkVector::DotProduct(lastQ, finalP);
1784 if (SkIsNaN(dot)) {
1785 return 0;
1786 }
1787 SkASSERT(0 <= dot && dot <= SK_Scalar1 + SK_ScalarNearlyZero);
1788
1789 if (dot < 1) {
1790 SkVector offCurve = { lastQ.x() + x, lastQ.y() + y };
1791 // compute the bisector vector, and then rescale to be the off-curve point.
1792 // we compute its length from cos(theta/2) = length / 1, using half-angle identity we get
1793 // length = sqrt(2 / (1 + cos(theta)). We already have cos() when to computed the dot.
1794 // This is nice, since our computed weight is cos(theta/2) as well!
1795 //
1796 const SkScalar cosThetaOver2 = SkScalarSqrt((1 + dot) / 2);
1797 offCurve.setLength(SkScalarInvert(cosThetaOver2));
1798 if (!SkPointPriv::EqualsWithinTolerance(lastQ, offCurve)) {
1799 dst[conicCount].set(lastQ, offCurve, finalP, cosThetaOver2);
1800 conicCount += 1;
1801 }
1802 }
1803
1804 // now handle counter-clockwise and the initial unitStart rotation
1806 matrix.setSinCos(uStart.fY, uStart.fX);
1807 if (dir == kCCW_SkRotationDirection) {
1808 matrix.preScale(SK_Scalar1, -SK_Scalar1);
1809 }
1810 if (userMatrix) {
1811 matrix.postConcat(*userMatrix);
1812 }
1813 for (int i = 0; i < conicCount; ++i) {
1814 matrix.mapPoints(dst[i].fPts, 3);
1815 }
1816 return conicCount;
1817}
int count
#define SkASSERT(cond)
Definition SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
static constexpr float sk_double_to_float(double x)
static bool SkIsFinite(T x, Pack... values)
static bool SkIsNaN(T x)
static constexpr double sk_ieee_double_divide(double numer, double denom)
static float solve_quadratic_equation_for_midtangent(float a, float b, float c, float discr)
static bool conic_find_extrema(const SkScalar src[], SkScalar w, SkScalar *t)
static void ratquad_mapTo3D(const SkPoint src[3], SkScalar w, SkPoint3 dst[3])
#define kMaxConicToQuadPOW2
#define AS_QUAD_ERROR_SETUP
static bool between(SkScalar a, SkScalar b, SkScalar c)
static SkPoint * subdivide(const SkConic &src, SkPoint pts[], int level)
SkVector SkFindBisector(SkVector, SkVector)
SkRotationDirection
Definition SkGeometry.h:321
@ kCW_SkRotationDirection
Definition SkGeometry.h:322
@ kCCW_SkRotationDirection
Definition SkGeometry.h:323
#define SkScalarInvert(x)
Definition SkScalar.h:73
#define SK_Scalar1
Definition SkScalar.h:18
#define SkScalarCeilToInt(x)
Definition SkScalar.h:36
#define SK_ScalarNearlyZero
Definition SkScalar.h:99
#define SkScalarSqrt(x)
Definition SkScalar.h:42
#define SK_ScalarRoot2Over2
Definition SkScalar.h:23
#define SkScalarAbs(x)
Definition SkScalar.h:39
#define SkScalarLog2(x)
Definition SkScalar.h:53
static T SkTAbs(T value)
Definition SkTemplates.h:43
static bool AreFinite(const SkPoint array[], int count)
Definition SkPointPriv.h:22
static bool EqualsWithinTolerance(const SkPoint &p1, const SkPoint &p2)
Definition SkPointPriv.h:54
static bool b
const uint8_t uint32_t uint32_t GError ** error
uint8_t value
unsigned useCenter Optional< SkMatrix > matrix
Definition SkRecords.h:258
Optional< SkRect > bounds
Definition SkRecords.h:189
dst
Definition cp.py:12
SINT T dot(const Vec< N, T > &a, const Vec< N, T > &b)
Definition SkVx.h:964
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition SkVx.h:706
SkScalar w
bool findXExtrema(SkScalar *t) const
int SK_SPI chopIntoQuadsPOW2(SkPoint pts[], int pow2) const
bool chopAtYExtrema(SkConic dst[2]) const
void computeTightBounds(SkRect *bounds) const
int SK_SPI computeQuadPOW2(SkScalar tol) const
static SkScalar TransformW(const SkPoint[3], SkScalar w, const SkMatrix &)
SkScalar fW
Definition SkGeometry.h:337
bool findYExtrema(SkScalar *t) const
bool chopAtXExtrema(SkConic dst[2]) const
float findMidTangent() const
void evalAt(SkScalar t, SkPoint *pos, SkVector *tangent=nullptr) const
bool chopAt(SkScalar t, SkConic dst[2]) const
bool asQuadTol(SkScalar tol) const
void chop(SkConic dst[2]) const
SkPoint fPts[3]
Definition SkGeometry.h:336
static int BuildUnitArc(const SkVector &start, const SkVector &stop, SkRotationDirection, const SkMatrix *, SkConic conics[kMaxConicsForArc])
void computeFastBounds(SkRect *bounds) const
static float CrossProduct(const SkVector &a, const SkVector &b)
bool setLength(float length)
Definition SkPoint.cpp:30
static float DotProduct(const SkVector &a, const SkVector &b)
float dot(const SkVector &vec) const
constexpr float y() const
constexpr float x() const

◆ kMaxConicToQuadPOW2

#define kMaxConicToQuadPOW2   5

Definition at line 1486 of file SkGeometry.cpp.

Function Documentation

◆ between()

static bool between ( SkScalar  a,
SkScalar  b,
SkScalar  c 
)
static

Definition at line 1525 of file SkGeometry.cpp.

1525 {
1526 return (a - b) * (c - b) <= 0;
1527}

◆ bubble_sort()

template<typename T >
void bubble_sort ( T  array[],
int  count 
)

Definition at line 881 of file SkGeometry.cpp.

881 {
882 for (int i = count - 1; i > 0; --i)
883 for (int j = i; j > 0; --j)
884 if (array[j] < array[j-1])
885 {
886 T tmp(array[j]);
887 array[j] = array[j-1];
888 array[j-1] = tmp;
889 }
890}
#define T

◆ calc_cubic_precision()

static SkScalar calc_cubic_precision ( const SkPoint  src[4])
static

Definition at line 1091 of file SkGeometry.cpp.

1091 {
1092 return (SkPointPriv::DistanceToSqd(src[1], src[0]) + SkPointPriv::DistanceToSqd(src[2], src[1])
1093 + SkPointPriv::DistanceToSqd(src[3], src[2])) * 1e-8f;
1094}
static SkScalar DistanceToSqd(const SkPoint &pt, const SkPoint &a)
Definition SkPointPriv.h:48

◆ calc_dot_cross_cubic()

static double calc_dot_cross_cubic ( const SkPoint p0,
const SkPoint p1,
const SkPoint p2 
)
static

Definition at line 771 of file SkGeometry.cpp.

771 {
772 const double xComp = (double) p0.fX * ((double) p1.fY - (double) p2.fY);
773 const double yComp = (double) p0.fY * ((double) p2.fX - (double) p1.fX);
774 const double wComp = (double) p1.fX * (double) p2.fY - (double) p1.fY * (double) p2.fX;
775 return (xComp + yComp + wComp);
776}

◆ close_enough_to_zero()

static bool close_enough_to_zero ( double  x)
static

Definition at line 1152 of file SkGeometry.cpp.

1152 {
1153 return std::fabs(x) < 0.00001;
1154}

◆ collaps_duplicates()

static int collaps_duplicates ( SkScalar  array[],
int  count 
)
static

Given an array and count, remove all pair-wise duplicates from the array, keeping the existing sorting, and return the new count

Definition at line 896 of file SkGeometry.cpp.

896 {
897 for (int n = count; n > 1; --n) {
898 if (array[0] == array[1]) {
899 for (int i = 1; i < n; ++i) {
900 array[i - 1] = array[i];
901 }
902 count -= 1;
903 } else {
904 array += 1;
905 }
906 }
907 return count;
908}

◆ conic_deriv_coeff()

static void conic_deriv_coeff ( const SkScalar  src[],
SkScalar  w,
SkScalar  coeff[3] 
)
static

Definition at line 1251 of file SkGeometry.cpp.

1253 {
1254 const SkScalar P20 = src[4] - src[0];
1255 const SkScalar P10 = src[2] - src[0];
1256 const SkScalar wP10 = w * P10;
1257 coeff[0] = w * P20 - P20;
1258 coeff[1] = P20 - 2 * wP10;
1259 coeff[2] = wP10;
1260}

◆ conic_find_extrema()

static bool conic_find_extrema ( const SkScalar  src[],
SkScalar  w,
SkScalar t 
)
static

Definition at line 1262 of file SkGeometry.cpp.

1262 {
1263 SkScalar coeff[3];
1264 conic_deriv_coeff(src, w, coeff);
1265
1266 SkScalar tValues[2];
1267 int roots = SkFindUnitQuadRoots(coeff[0], coeff[1], coeff[2], tValues);
1268 SkASSERT(0 == roots || 1 == roots);
1269
1270 if (1 == roots) {
1271 *t = tValues[0];
1272 return true;
1273 }
1274 return false;
1275}
int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2])
static void conic_deriv_coeff(const SkScalar src[], SkScalar w, SkScalar coeff[3])

◆ eval_cubic_2ndDerivative()

static SkVector eval_cubic_2ndDerivative ( const SkPoint  src[4],
SkScalar  t 
)
static

Definition at line 407 of file SkGeometry.cpp.

407 {
408 float2 P0 = from_point(src[0]);
409 float2 P1 = from_point(src[1]);
410 float2 P2 = from_point(src[2]);
411 float2 P3 = from_point(src[3]);
412 float2 A = P3 + 3 * (P1 - P2) - P0;
413 float2 B = P2 - times_2(P1) + P0;
414
415 return to_vector(A * t + B);
416}
static skvx::float2 times_2(const skvx::float2 &value)
Definition SkGeometry.h:32
static skvx::float2 from_point(const SkPoint &point)
Definition SkGeometry.h:22

◆ eval_cubic_derivative()

static SkVector eval_cubic_derivative ( const SkPoint  src[4],
SkScalar  t 
)
static

Definition at line 394 of file SkGeometry.cpp.

394 {
395 SkQuadCoeff coeff;
396 float2 P0 = from_point(src[0]);
397 float2 P1 = from_point(src[1]);
398 float2 P2 = from_point(src[2]);
399 float2 P3 = from_point(src[3]);
400
401 coeff.fA = P3 + 3 * (P1 - P2) - P0;
402 coeff.fB = times_2(P2 - times_2(P1) + P0);
403 coeff.fC = P1 - P0;
404 return to_vector(coeff.eval(t));
405}

◆ first_axis_intersection()

static bool first_axis_intersection ( const double  coefficients[8],
bool  yDirection,
double  axisIntercept,
double *  solution 
)
static

Definition at line 1156 of file SkGeometry.cpp.

1157 {
1158 auto [A, B, C, D] = SkBezierCubic::ConvertToPolynomial(coefficients, yDirection);
1159 D -= axisIntercept;
1160 double roots[3] = {0, 0, 0};
1161 int count = SkCubics::RootsValidT(A, B, C, D, roots);
1162 if (count == 0) {
1163 return false;
1164 }
1165 // Verify that at least one of the roots is accurate.
1166 for (int i = 0; i < count; i++) {
1167 if (close_enough_to_zero(SkCubics::EvalAt(A, B, C, D, roots[i]))) {
1168 *solution = roots[i];
1169 return true;
1170 }
1171 }
1172 // None of the roots returned by our normal cubic solver were correct enough
1173 // (e.g. https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=55732)
1174 // So we need to fallback to a more accurate solution.
1176 if (count == 0) {
1177 return false;
1178 }
1179 for (int i = 0; i < count; i++) {
1180 if (close_enough_to_zero(SkCubics::EvalAt(A, B, C, D, roots[i]))) {
1181 *solution = roots[i];
1182 return true;
1183 }
1184 }
1185 return false;
1186}
static bool close_enough_to_zero(double x)
static std::array< double, 4 > ConvertToPolynomial(const double curve[8], bool yValues)
static int BinarySearchRootsValidT(double A, double B, double C, double D, double solution[3])
Definition SkCubics.cpp:208
static double EvalAt(double A, double B, double C, double D, double t)
Definition SkCubics.h:51
static int RootsValidT(double A, double B, double C, double D, double solution[3])
Definition SkCubics.cpp:127
#define C(TEST_CATEGORY)
Definition colrv1.cpp:247
#define B

◆ flatten_double_cubic_extrema()

static void flatten_double_cubic_extrema ( SkScalar  coords[14])
static

Definition at line 686 of file SkGeometry.cpp.

686 {
687 coords[4] = coords[8] = coords[6];
688}

◆ flatten_double_quad_extrema()

static void flatten_double_quad_extrema ( SkScalar  coords[14])
inlinestatic

Definition at line 272 of file SkGeometry.cpp.

272 {
273 coords[2] = coords[6] = coords[4];
274}

◆ fma()

static skvx::float4 fma ( const skvx::float4 f,
float  m,
const skvx::float4 a 
)
static

Definition at line 597 of file SkGeometry.cpp.

597 {
598 return skvx::fma(f, skvx::float4(m), a);
599}
SIN Vec< N, float > fma(const Vec< N, float > &x, const Vec< N, float > &y, const Vec< N, float > &z)
Definition SkVx.h:708

◆ formulate_F1DotF2()

static void formulate_F1DotF2 ( const SkScalar  src[],
SkScalar  coeff[4] 
)
static

Definition at line 1021 of file SkGeometry.cpp.

1021 {
1022 SkScalar a = src[2] - src[0];
1023 SkScalar b = src[4] - 2 * src[2] + src[0];
1024 SkScalar c = src[6] + 3 * (src[2] - src[4]) - src[0];
1025
1026 coeff[0] = c * c;
1027 coeff[1] = 3 * b * c;
1028 coeff[2] = 2 * b * b + c * a;
1029 coeff[3] = a * b;
1030}

◆ interp()

static float2 interp ( const float2 v0,
const float2 v1,
const float2 t 
)
inlinestatic

Definition at line 169 of file SkGeometry.cpp.

171 {
172 return v0 + (v1 - v0) * t;
173}

◆ on_same_side()

static bool on_same_side ( const SkPoint  src[4],
int  testIndex,
int  lineIndex 
)
static

Definition at line 1098 of file SkGeometry.cpp.

1098 {
1099 SkPoint origin = src[lineIndex];
1100 SkVector line = src[lineIndex + 1] - origin;
1101 SkScalar crosses[2];
1102 for (int index = 0; index < 2; ++index) {
1103 SkVector testLine = src[testIndex + index] - origin;
1104 crosses[index] = line.cross(testLine);
1105 }
1106 return crosses[0] * crosses[1] >= 0;
1107}

◆ p3d_interp()

static void p3d_interp ( const SkScalar  src[7],
SkScalar  dst[7],
SkScalar  t 
)
static

Definition at line 1278 of file SkGeometry.cpp.

1278 {
1279 SkScalar ab = SkScalarInterp(src[0], src[3], t);
1280 SkScalar bc = SkScalarInterp(src[3], src[6], t);
1281 dst[0] = ab;
1282 dst[3] = SkScalarInterp(ab, bc, t);
1283 dst[6] = bc;
1284}
static SkScalar SkScalarInterp(SkScalar A, SkScalar B, SkScalar t)
Definition SkScalar.h:131
Definition ab.py:1

◆ previous_inverse_pow2()

static double previous_inverse_pow2 ( double  n)
inlinestatic

Definition at line 782 of file SkGeometry.cpp.

782 {
783 uint64_t bits;
784 memcpy(&bits, &n, sizeof(double));
785 bits = ((1023llu*2 << 52) + ((1llu << 52) - 1)) - bits; // exp=-exp
786 bits &= (0x7ffllu) << 52; // mantissa=1.0, sign=0
787 memcpy(&n, &bits, sizeof(double));
788 return n;
789}

◆ project_down()

static SkPoint project_down ( const SkPoint3 src)
static

Definition at line 1292 of file SkGeometry.cpp.

1292 {
1293 return {src.fX / src.fZ, src.fY / src.fZ};
1294}

◆ ratquad_mapTo3D()

static void ratquad_mapTo3D ( const SkPoint  src[3],
SkScalar  w,
SkPoint3  dst[3] 
)
static

Definition at line 1286 of file SkGeometry.cpp.

1286 {
1287 dst[0].set(src[0].fX * 1, src[0].fY * 1, 1);
1288 dst[1].set(src[1].fX * w, src[1].fY * w, w);
1289 dst[2].set(src[2].fX * 1, src[2].fY * 1, 1);
1290}

◆ SkChopCubicAt() [1/3]

void SkChopCubicAt ( const SkPoint  src[4],
SkPoint  dst[10],
float  t0,
float  t1 
)

Given a src cubic bezier, chop it at the specified t0 and t1 values, where 0 <= t0 <= t1 <= 1, and return the three new cubics in dst: dst[0..3], dst[3..6], and dst[6..9]

Definition at line 504 of file SkGeometry.cpp.

504 {
505 SkASSERT(0 <= t0 && t0 <= t1 && t1 <= 1);
506
507 if (t1 == 1) {
508 SkChopCubicAt(src, dst, t0);
509 dst[7] = dst[8] = dst[9] = src[3];
510 return;
511 }
512
513 // Perform both chops in parallel using 4-lane SIMD.
514 float4 p00, p11, p22, p33, T;
515 p00.lo = p00.hi = sk_bit_cast<float2>(src[0]);
516 p11.lo = p11.hi = sk_bit_cast<float2>(src[1]);
517 p22.lo = p22.hi = sk_bit_cast<float2>(src[2]);
518 p33.lo = p33.hi = sk_bit_cast<float2>(src[3]);
519 T.lo = t0;
520 T.hi = t1;
521
522 float4 ab = unchecked_mix(p00, p11, T);
523 float4 bc = unchecked_mix(p11, p22, T);
524 float4 cd = unchecked_mix(p22, p33, T);
525 float4 abc = unchecked_mix(ab, bc, T);
526 float4 bcd = unchecked_mix(bc, cd, T);
527 float4 abcd = unchecked_mix(abc, bcd, T);
528 float4 middle = unchecked_mix(abc, bcd, skvx::shuffle<2,3,0,1>(T));
529
530 dst[0] = sk_bit_cast<SkPoint>(p00.lo);
531 dst[1] = sk_bit_cast<SkPoint>(ab.lo);
532 dst[2] = sk_bit_cast<SkPoint>(abc.lo);
533 dst[3] = sk_bit_cast<SkPoint>(abcd.lo);
534 middle.store(dst + 4);
535 dst[6] = sk_bit_cast<SkPoint>(abcd.hi);
536 dst[7] = sk_bit_cast<SkPoint>(bcd.hi);
537 dst[8] = sk_bit_cast<SkPoint>(cd.hi);
538 dst[9] = sk_bit_cast<SkPoint>(p33.hi);
539}
static skvx::Vec< N, T > unchecked_mix(const skvx::Vec< N, T > &a, const skvx::Vec< N, T > &b, const skvx::Vec< N, T > &t)
void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t)
SKVX_ALWAYS_INLINE void store(void *ptr) const
Definition SkVx.h:112
Vec< N/2, T > hi
Definition SkVx.h:117
Vec< N/2, T > lo
Definition SkVx.h:117

◆ SkChopCubicAt() [2/3]

void SkChopCubicAt ( const SkPoint  src[4],
SkPoint  dst[7],
SkScalar  t 
)

Given a src cubic bezier, chop it at the specified t value, where 0 <= t <= 1, and return the two new cubics in dst: dst[0..3] and dst[3..6]

Definition at line 473 of file SkGeometry.cpp.

473 {
474 SkASSERT(0 <= t && t <= 1);
475
476 if (t == 1) {
477 memcpy(dst, src, sizeof(SkPoint) * 4);
478 dst[4] = dst[5] = dst[6] = src[3];
479 return;
480 }
481
482 float2 p0 = sk_bit_cast<float2>(src[0]);
483 float2 p1 = sk_bit_cast<float2>(src[1]);
484 float2 p2 = sk_bit_cast<float2>(src[2]);
485 float2 p3 = sk_bit_cast<float2>(src[3]);
486 float2 T = t;
487
488 float2 ab = unchecked_mix(p0, p1, T);
489 float2 bc = unchecked_mix(p1, p2, T);
490 float2 cd = unchecked_mix(p2, p3, T);
491 float2 abc = unchecked_mix(ab, bc, T);
492 float2 bcd = unchecked_mix(bc, cd, T);
493 float2 abcd = unchecked_mix(abc, bcd, T);
494
495 dst[0] = sk_bit_cast<SkPoint>(p0);
496 dst[1] = sk_bit_cast<SkPoint>(ab);
497 dst[2] = sk_bit_cast<SkPoint>(abc);
498 dst[3] = sk_bit_cast<SkPoint>(abcd);
499 dst[4] = sk_bit_cast<SkPoint>(bcd);
500 dst[5] = sk_bit_cast<SkPoint>(cd);
501 dst[6] = sk_bit_cast<SkPoint>(p3);
502}

◆ SkChopCubicAt() [3/3]

void SkChopCubicAt ( const SkPoint  src[4],
SkPoint  dst[],
const SkScalar  t[],
int  t_count 
)

Given a src cubic bezier, chop it at the specified t values, where 0 <= t0 <= t1 <= ... <= 1, and return the new cubics in dst: dst[0..3],dst[3..6],...,dst[3*t_count..3*(t_count+1)]

Definition at line 541 of file SkGeometry.cpp.

542 {
543 SkASSERT(std::all_of(tValues, tValues + tCount, [](SkScalar t) { return t >= 0 && t <= 1; }));
544 SkASSERT(std::is_sorted(tValues, tValues + tCount));
545
546 if (dst) {
547 if (tCount == 0) { // nothing to chop
548 memcpy(dst, src, 4*sizeof(SkPoint));
549 } else {
550 int i = 0;
551 for (; i < tCount - 1; i += 2) {
552 // Do two chops at once.
553 float2 tt = float2::Load(tValues + i);
554 if (i != 0) {
555 float lastT = tValues[i - 1];
556 tt = skvx::pin((tt - lastT) / (1 - lastT), float2(0), float2(1));
557 }
558 SkChopCubicAt(src, dst, tt[0], tt[1]);
559 src = dst = dst + 6;
560 }
561 if (i < tCount) {
562 // Chop the final cubic if there was an odd number of chops.
563 SkASSERT(i + 1 == tCount);
564 float t = tValues[i];
565 if (i != 0) {
566 float lastT = tValues[i - 1];
567 t = SkTPin(sk_ieee_float_divide(t - lastT, 1 - lastT), 0.f, 1.f);
568 }
569 SkChopCubicAt(src, dst, t);
570 }
571 }
572 }
573}
static constexpr float sk_ieee_float_divide(float numer, float denom)
skvx::float2 float2
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition SkTPin.h:19
SINT Vec< N, T > pin(const Vec< N, T > &x, const Vec< N, T > &lo, const Vec< N, T > &hi)
Definition SkVx.h:655
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
Definition SkVx.h:109

◆ SkChopCubicAtHalf()

void SkChopCubicAtHalf ( const SkPoint  src[4],
SkPoint  dst[7] 
)

Given a src cubic bezier, chop it at the specified t == 1/2, The new cubics are returned in dst[0..3] and dst[3..6]

Definition at line 575 of file SkGeometry.cpp.

575 {
576 SkChopCubicAt(src, dst, 0.5f);
577}

◆ SkChopCubicAtInflections()

int SkChopCubicAtInflections ( const SkPoint  src[4],
SkPoint  dst[10] 
)

Return 1 for no chop, 2 for having chopped the cubic at a single inflection point, 3 for having chopped at 2 inflection points. dst will hold the resulting 1, 2, or 3 cubics.

Definition at line 755 of file SkGeometry.cpp.

755 {
756 SkScalar tValues[2];
757 int count = SkFindCubicInflections(src, tValues);
758
759 if (dst) {
760 if (count == 0) {
761 memcpy(dst, src, 4 * sizeof(SkPoint));
762 } else {
763 SkChopCubicAt(src, dst, tValues, count);
764 }
765 }
766 return count + 1;
767}
int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[2])

◆ SkChopCubicAtMaxCurvature()

int SkChopCubicAtMaxCurvature ( const SkPoint  src[4],
SkPoint  dst[13],
SkScalar  tValues[3] 
)

Definition at line 1060 of file SkGeometry.cpp.

1061 {
1062 SkScalar t_storage[3];
1063
1064 if (tValues == nullptr) {
1065 tValues = t_storage;
1066 }
1067
1068 SkScalar roots[3];
1069 int rootCount = SkFindCubicMaxCurvature(src, roots);
1070
1071 // Throw out values not inside 0..1.
1072 int count = 0;
1073 for (int i = 0; i < rootCount; ++i) {
1074 if (0 < roots[i] && roots[i] < 1) {
1075 tValues[count++] = roots[i];
1076 }
1077 }
1078
1079 if (dst) {
1080 if (count == 0) {
1081 memcpy(dst, src, 4 * sizeof(SkPoint));
1082 } else {
1083 SkChopCubicAt(src, dst, tValues, count);
1084 }
1085 }
1086 return count + 1;
1087}
int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3])

◆ SkChopCubicAtXExtrema()

int SkChopCubicAtXExtrema ( const SkPoint  src[4],
SkPoint  dst[10] 
)

Definition at line 714 of file SkGeometry.cpp.

714 {
715 SkScalar tValues[2];
716 int roots = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX,
717 src[3].fX, tValues);
718
719 SkChopCubicAt(src, dst, tValues, roots);
720 if (dst && roots > 0) {
721 // we do some cleanup to ensure our Y extrema are flat
723 if (roots == 2) {
725 }
726 }
727 return roots;
728}
static void flatten_double_cubic_extrema(SkScalar coords[14])
int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2])

◆ SkChopCubicAtYExtrema()

int SkChopCubicAtYExtrema ( const SkPoint  src[4],
SkPoint  dst[10] 
)

Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that the resulting beziers are monotonic in Y. This is called by the scan converter. Depending on what is returned, dst[] is treated as follows: 0 dst[0..3] is the original cubic 1 dst[0..3] and dst[3..6] are the two new cubics 2 dst[0..3], dst[3..6], dst[6..9] are the three new cubics If dst == null, it is ignored and only the count is returned.

Definition at line 698 of file SkGeometry.cpp.

698 {
699 SkScalar tValues[2];
700 int roots = SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY,
701 src[3].fY, tValues);
702
703 SkChopCubicAt(src, dst, tValues, roots);
704 if (dst && roots > 0) {
705 // we do some cleanup to ensure our Y extrema are flat
707 if (roots == 2) {
709 }
710 }
711 return roots;
712}

◆ SkChopMonoCubicAtX()

bool SkChopMonoCubicAtX ( const SkPoint  src[4],
SkScalar  x,
SkPoint  dst[7] 
)

Given a monotonically increasing or decreasing cubic bezier src, chop it where the X value is the specified value. The returned cubics will be in dst, sharing the middle point. That is, the first cubic is dst[0..3] and the second dst[3..6].

If the cubic provided is not monotone, it will be chopped at the first time the curve has the specified X value.

If the cubic never reaches the specified value, the function returns false.

Definition at line 1204 of file SkGeometry.cpp.

1204 {
1205 double coefficients[8] = {src[0].fX, src[0].fY, src[1].fX, src[1].fY,
1206 src[2].fX, src[2].fY, src[3].fX, src[3].fY};
1207 double solution = 0;
1208 if (first_axis_intersection(coefficients, false, x, &solution)) {
1209 double cubicPair[14];
1210 SkBezierCubic::Subdivide(coefficients, solution, cubicPair);
1211 for (int i = 0; i < 7; i ++) {
1212 dst[i].fX = sk_double_to_float(cubicPair[i*2]);
1213 dst[i].fY = sk_double_to_float(cubicPair[i*2 + 1]);
1214 }
1215 return true;
1216 }
1217 return false;
1218}
static bool first_axis_intersection(const double coefficients[8], bool yDirection, double axisIntercept, double *solution)
static void Subdivide(const double curve[8], double t, double twoCurves[14])

◆ SkChopMonoCubicAtY()

bool SkChopMonoCubicAtY ( const SkPoint  src[4],
SkScalar  y,
SkPoint  dst[7] 
)

Given a monotonically increasing or decreasing cubic bezier src, chop it where the Y value is the specified value. The returned cubics will be in dst, sharing the middle point. That is, the first cubic is dst[0..3] and the second dst[3..6].

If the cubic provided is not monotone, it will be chopped at the first time the curve has the specified Y value.

If the cubic never reaches the specified value, the function returns false.

Definition at line 1188 of file SkGeometry.cpp.

1188 {
1189 double coefficients[8] = {src[0].fX, src[0].fY, src[1].fX, src[1].fY,
1190 src[2].fX, src[2].fY, src[3].fX, src[3].fY};
1191 double solution = 0;
1192 if (first_axis_intersection(coefficients, true, y, &solution)) {
1193 double cubicPair[14];
1194 SkBezierCubic::Subdivide(coefficients, solution, cubicPair);
1195 for (int i = 0; i < 7; i ++) {
1196 dst[i].fX = sk_double_to_float(cubicPair[i*2]);
1197 dst[i].fY = sk_double_to_float(cubicPair[i*2 + 1]);
1198 }
1199 return true;
1200 }
1201 return false;
1202}

◆ SkChopQuadAt()

void SkChopQuadAt ( const SkPoint  src[3],
SkPoint  dst[5],
SkScalar  t 
)

Given a src quadratic bezier, chop it at the specified t value, where 0 < t < 1, and return the two new quadratics in dst: dst[0..2] and dst[2..4]

Definition at line 175 of file SkGeometry.cpp.

175 {
176 SkASSERT(t > 0 && t < SK_Scalar1);
177
178 float2 p0 = from_point(src[0]);
179 float2 p1 = from_point(src[1]);
180 float2 p2 = from_point(src[2]);
181 float2 tt(t);
182
183 float2 p01 = interp(p0, p1, tt);
184 float2 p12 = interp(p1, p2, tt);
185
186 dst[0] = to_point(p0);
187 dst[1] = to_point(p01);
188 dst[2] = to_point(interp(p01, p12, tt));
189 dst[3] = to_point(p12);
190 dst[4] = to_point(p2);
191}
static float2 interp(const float2 &v0, const float2 &v1, const float2 &t)
static SkPoint to_point(SkIPoint p)
Definition editor.cpp:75

◆ SkChopQuadAtHalf()

void SkChopQuadAtHalf ( const SkPoint  src[3],
SkPoint  dst[5] 
)

Given a src quadratic bezier, chop it at the specified t == 1/2, The new quads are returned in dst[0..2] and dst[2..4]

Definition at line 193 of file SkGeometry.cpp.

193 {
194 SkChopQuadAt(src, dst, 0.5f);
195}
void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t)

◆ SkChopQuadAtMaxCurvature()

int SkChopQuadAtMaxCurvature ( const SkPoint  src[3],
SkPoint  dst[5] 
)

Given 3 points on a quadratic bezier, divide it into 2 quadratics if the point of maximum curvature exists on the quad segment. Depending on what is returned, dst[] is treated as follows 1 dst[0..2] is the original quad 2 dst[0..2] and dst[2..4] are the two new quads If dst == null, it is ignored and only the count is returned.

Definition at line 367 of file SkGeometry.cpp.

367 {
369 if (t > 0 && t < 1) {
370 SkChopQuadAt(src, dst, t);
371 return 2;
372 } else {
373 memcpy(dst, src, 3 * sizeof(SkPoint));
374 return 1;
375 }
376}
SkScalar SkFindQuadMaxCurvature(const SkPoint src[3])

◆ SkChopQuadAtXExtrema()

int SkChopQuadAtXExtrema ( const SkPoint  src[3],
SkPoint  dst[5] 
)

Definition at line 307 of file SkGeometry.cpp.

307 {
308 SkASSERT(src);
309 SkASSERT(dst);
310
311 SkScalar a = src[0].fX;
312 SkScalar b = src[1].fX;
313 SkScalar c = src[2].fX;
314
315 if (is_not_monotonic(a, b, c)) {
316 SkScalar tValue;
317 if (valid_unit_divide(a - b, a - b - b + c, &tValue)) {
318 SkChopQuadAt(src, dst, tValue);
319 flatten_double_quad_extrema(&dst[0].fX);
320 return 1;
321 }
322 // if we get here, we need to force dst to be monotonic, even though
323 // we couldn't compute a unit_divide value (probably underflow).
324 b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
325 }
326 dst[0].set(a, src[0].fY);
327 dst[1].set(b, src[1].fY);
328 dst[2].set(c, src[2].fY);
329 return 0;
330}
static void flatten_double_quad_extrema(SkScalar coords[14])
static int valid_unit_divide(double numer, double denom, double *ratio)

◆ SkChopQuadAtYExtrema()

int SkChopQuadAtYExtrema ( const SkPoint  src[3],
SkPoint  dst[5] 
)

Given 3 points on a quadratic bezier, chop it into 1, 2 beziers such that the resulting beziers are monotonic in Y. This is called by the scan converter. Depending on what is returned, dst[] is treated as follows 0 dst[0..2] is the original quad 1 dst[0..2] and dst[2..4] are the two new quads

Definition at line 279 of file SkGeometry.cpp.

279 {
280 SkASSERT(src);
281 SkASSERT(dst);
282
283 SkScalar a = src[0].fY;
284 SkScalar b = src[1].fY;
285 SkScalar c = src[2].fY;
286
287 if (is_not_monotonic(a, b, c)) {
288 SkScalar tValue;
289 if (valid_unit_divide(a - b, a - b - b + c, &tValue)) {
290 SkChopQuadAt(src, dst, tValue);
291 flatten_double_quad_extrema(&dst[0].fY);
292 return 1;
293 }
294 // if we get here, we need to force dst to be monotonic, even though
295 // we couldn't compute a unit_divide value (probably underflow).
296 b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
297 }
298 dst[0].set(src[0].fX, a);
299 dst[1].set(src[1].fX, b);
300 dst[2].set(src[2].fX, c);
301 return 0;
302}

◆ SkClassifyCubic()

SkCubicType SkClassifyCubic ( const SkPoint  p[4],
double  t[2] = nullptr,
double  s[2] = nullptr,
double  d[4] = nullptr 
)

Returns the cubic classification.

t[],s[] are set to the two homogeneous parameter values at which points the lines L & M intersect with K, sorted from smallest to largest and oriented so positive values of the implicit are on the "left" side. For a serpentine curve they are the inflection points. For a loop they are the double point. For a local cusp, they are both equal and denote the cusp point. For a cusp at an infinite parameter value, one will be the local inflection point and the other +inf (t,s = 1,0). If the curve is degenerate (i.e. quadratic or linear) they are both set to a parameter value of +inf (t,s = 1,0).

d[] is filled with the cubic inflection function coefficients. See "Resolution Independent Curve Rendering using Programmable Graphics Hardware", 4.2 Curve Categorization:

If the input points contain infinities or NaN, the return values are undefined.

https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf

Definition at line 809 of file SkGeometry.cpp.

809 {
810 // Find the cubic's inflection function, I = [T^3 -3T^2 3T -1] dot D. (D0 will always be 0
811 // for integral cubics.)
812 //
813 // See "Resolution Independent Curve Rendering using Programmable Graphics Hardware",
814 // 4.2 Curve Categorization:
815 //
816 // https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
817 double A1 = calc_dot_cross_cubic(P[0], P[3], P[2]);
818 double A2 = calc_dot_cross_cubic(P[1], P[0], P[3]);
819 double A3 = calc_dot_cross_cubic(P[2], P[1], P[0]);
820
821 double D3 = 3 * A3;
822 double D2 = D3 - A2;
823 double D1 = D2 - A2 + A1;
824
825 // Shift the exponents in D so the largest magnitude falls somewhere in 1..2. This protects us
826 // from overflow down the road while solving for roots and KLM functionals.
827 double Dmax = std::max(std::max(fabs(D1), fabs(D2)), fabs(D3));
828 double norm = previous_inverse_pow2(Dmax);
829 D1 *= norm;
830 D2 *= norm;
831 D3 *= norm;
832
833 if (d) {
834 d[3] = D3;
835 d[2] = D2;
836 d[1] = D1;
837 d[0] = 0;
838 }
839
840 // Now use the inflection function to classify the cubic.
841 //
842 // See "Resolution Independent Curve Rendering using Programmable Graphics Hardware",
843 // 4.4 Integral Cubics:
844 //
845 // https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
846 if (0 != D1) {
847 double discr = 3*D2*D2 - 4*D1*D3;
848 if (discr > 0) { // Serpentine.
849 if (t && s) {
850 double q = 3*D2 + copysign(sqrt(3*discr), D2);
851 write_cubic_inflection_roots(q, 6*D1, 2*D3, q, t, s);
852 }
854 } else if (discr < 0) { // Loop.
855 if (t && s) {
856 double q = D2 + copysign(sqrt(-discr), D2);
857 write_cubic_inflection_roots(q, 2*D1, 2*(D2*D2 - D3*D1), D1*q, t, s);
858 }
859 return SkCubicType::kLoop;
860 } else { // Cusp.
861 if (t && s) {
862 write_cubic_inflection_roots(D2, 2*D1, D2, 2*D1, t, s);
863 }
865 }
866 } else {
867 if (0 != D2) { // Cusp at T=infinity.
868 if (t && s) {
869 write_cubic_inflection_roots(D3, 3*D2, 1, 0, t, s); // T1=infinity.
870 }
872 } else { // Degenerate.
873 if (t && s) {
874 write_cubic_inflection_roots(1, 0, 1, 0, t, s); // T0=T1=infinity.
875 }
877 }
878 }
879}
static double calc_dot_cross_cubic(const SkPoint &p0, const SkPoint &p1, const SkPoint &p2)
static double previous_inverse_pow2(double n)
static void write_cubic_inflection_roots(double t0, double s0, double t1, double s1, double *t, double *s)
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
struct MyStruct s

◆ SkConvertQuadToCubic()

void SkConvertQuadToCubic ( const SkPoint  src[3],
SkPoint  dst[4] 
)

Given 3 points on a quadratic bezier, use degree elevation to convert it into the cubic fitting the same curve. The new cubic curve is returned in dst[0..3].

Definition at line 378 of file SkGeometry.cpp.

378 {
379 float2 scale(SkDoubleToScalar(2.0 / 3.0));
380 float2 s0 = from_point(src[0]);
381 float2 s1 = from_point(src[1]);
382 float2 s2 = from_point(src[2]);
383
384 dst[0] = to_point(s0);
385 dst[1] = to_point(s0 + (s1 - s0) * scale);
386 dst[2] = to_point(s2 + (s1 - s2) * scale);
387 dst[3] = to_point(s2);
388}
#define SkDoubleToScalar(x)
Definition SkScalar.h:64
const Scalar scale

◆ SkEvalCubicAt()

void SkEvalCubicAt ( const SkPoint  src[4],
SkScalar  t,
SkPoint locOrNull,
SkVector tangentOrNull,
SkVector curvatureOrNull 
)

Set pt to the point on the src cubic specified by t. t must be 0 <= t <= 1.0

Definition at line 418 of file SkGeometry.cpp.

419 {
420 SkASSERT(src);
421 SkASSERT(t >= 0 && t <= SK_Scalar1);
422
423 if (loc) {
424 *loc = to_point(SkCubicCoeff(src).eval(t));
425 }
426 if (tangent) {
427 // The derivative equation returns a zero tangent vector when t is 0 or 1, and the
428 // adjacent control point is equal to the end point. In this case, use the
429 // next control point or the end points to compute the tangent.
430 if ((t == 0 && src[0] == src[1]) || (t == 1 && src[2] == src[3])) {
431 if (t == 0) {
432 *tangent = src[2] - src[0];
433 } else {
434 *tangent = src[3] - src[1];
435 }
436 if (!tangent->fX && !tangent->fY) {
437 *tangent = src[3] - src[0];
438 }
439 } else {
440 *tangent = eval_cubic_derivative(src, t);
441 }
442 }
443 if (curvature) {
444 *curvature = eval_cubic_2ndDerivative(src, t);
445 }
446}
static SkVector eval_cubic_2ndDerivative(const SkPoint src[4], SkScalar t)
static SkVector eval_cubic_derivative(const SkPoint src[4], SkScalar t)

◆ SkEvalQuadAt() [1/2]

SkPoint SkEvalQuadAt ( const SkPoint  src[3],
SkScalar  t 
)

Definition at line 144 of file SkGeometry.cpp.

144 {
145 return to_point(SkQuadCoeff(src).eval(t));
146}

◆ SkEvalQuadAt() [2/2]

void SkEvalQuadAt ( const SkPoint  src[3],
SkScalar  t,
SkPoint pt,
SkVector tangent = nullptr 
)

Set pt to the point on the src quadratic specified by t. t must be 0 <= t <= 1.0

Definition at line 132 of file SkGeometry.cpp.

132 {
133 SkASSERT(src);
134 SkASSERT(t >= 0 && t <= SK_Scalar1);
135
136 if (pt) {
137 *pt = SkEvalQuadAt(src, t);
138 }
139 if (tangent) {
140 *tangent = SkEvalQuadTangentAt(src, t);
141 }
142}
SkVector SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t)
void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint *pt, SkVector *tangent)

◆ SkEvalQuadTangentAt()

SkVector SkEvalQuadTangentAt ( const SkPoint  src[3],
SkScalar  t 
)

Definition at line 148 of file SkGeometry.cpp.

148 {
149 // The derivative equation is 2(b - a +(a - 2b +c)t). This returns a
150 // zero tangent vector when t is 0 or 1, and the control point is equal
151 // to the end point. In this case, use the quad end points to compute the tangent.
152 if ((t == 0 && src[0] == src[1]) || (t == 1 && src[1] == src[2])) {
153 return src[2] - src[0];
154 }
155 SkASSERT(src);
156 SkASSERT(t >= 0 && t <= SK_Scalar1);
157
158 float2 P0 = from_point(src[0]);
159 float2 P1 = from_point(src[1]);
160 float2 P2 = from_point(src[2]);
161
162 float2 B = P1 - P0;
163 float2 A = P2 - P1 - B;
164 float2 T = A * t + B;
165
166 return to_vector(T + T);
167}

◆ SkFindBisector()

SkVector SkFindBisector ( SkVector  a,
SkVector  b 
)

Returns a new, arbitrarily scaled vector that bisects the given vectors. The returned bisector will always point toward the interior of the provided vectors.

Definition at line 204 of file SkGeometry.cpp.

204 {
205 std::array<SkVector, 2> v;
206 if (a.dot(b) >= 0) {
207 // a,b are within +/-90 degrees apart.
208 v = {a, b};
209 } else if (a.cross(b) >= 0) {
210 // a,b are >90 degrees apart. Find the bisector of their interior normals instead. (Above 90
211 // degrees, the original vectors start cancelling each other out which eventually becomes
212 // unstable.)
213 v[0].set(-a.fY, +a.fX);
214 v[1].set(+b.fY, -b.fX);
215 } else {
216 // a,b are <-90 degrees apart. Find the bisector of their interior normals instead. (Below
217 // -90 degrees, the original vectors start cancelling each other out which eventually
218 // becomes unstable.)
219 v[0].set(+a.fY, -a.fX);
220 v[1].set(-b.fY, +b.fX);
221 }
222 // Return "normalize(v[0]) + normalize(v[1])".
223 skvx::float2 x0_x1{v[0].fX, v[1].fX};
224 skvx::float2 y0_y1{v[0].fY, v[1].fY};
225 auto invLengths = 1.0f / sqrt(x0_x1 * x0_x1 + y0_y1 * y0_y1);
226 x0_x1 *= invLengths;
227 y0_y1 *= invLengths;
228 return SkPoint{x0_x1[0] + x0_x1[1], y0_y1[0] + y0_y1[1]};
229}

◆ SkFindCubicCusp()

SkScalar SkFindCubicCusp ( const SkPoint  src[4])

Returns t value of cusp if cubic has one; returns -1 otherwise.

Definition at line 1112 of file SkGeometry.cpp.

1112 {
1113 // When the adjacent control point matches the end point, it behaves as if
1114 // the cubic has a cusp: there's a point of max curvature where the derivative
1115 // goes to zero. Ideally, this would be where t is zero or one, but math
1116 // error makes not so. It is not uncommon to create cubics this way; skip them.
1117 if (src[0] == src[1]) {
1118 return -1;
1119 }
1120 if (src[2] == src[3]) {
1121 return -1;
1122 }
1123 // Cubics only have a cusp if the line segments formed by the control and end points cross.
1124 // Detect crossing if line ends are on opposite sides of plane formed by the other line.
1125 if (on_same_side(src, 0, 2) || on_same_side(src, 2, 0)) {
1126 return -1;
1127 }
1128 // Cubics may have multiple points of maximum curvature, although at most only
1129 // one is a cusp.
1130 SkScalar maxCurvature[3];
1131 int roots = SkFindCubicMaxCurvature(src, maxCurvature);
1132 for (int index = 0; index < roots; ++index) {
1133 SkScalar testT = maxCurvature[index];
1134 if (0 >= testT || testT >= 1) { // no need to consider max curvature on the end
1135 continue;
1136 }
1137 // A cusp is at the max curvature, and also has a derivative close to zero.
1138 // Choose the 'close to zero' meaning by comparing the derivative length
1139 // with the overall cubic size.
1140 SkVector dPt = eval_cubic_derivative(src, testT);
1141 SkScalar dPtMagnitude = SkPointPriv::LengthSqd(dPt);
1142 SkScalar precision = calc_cubic_precision(src);
1143 if (dPtMagnitude < precision) {
1144 // All three max curvature t values may be close to the cusp;
1145 // return the first one.
1146 return testT;
1147 }
1148 }
1149 return -1;
1150}
static SkScalar calc_cubic_precision(const SkPoint src[4])
static bool on_same_side(const SkPoint src[4], int testIndex, int lineIndex)
static SkScalar LengthSqd(const SkPoint &pt)
Definition SkPointPriv.h:63

◆ SkFindCubicExtrema()

int SkFindCubicExtrema ( SkScalar  a,
SkScalar  b,
SkScalar  c,
SkScalar  d,
SkScalar  tValues[2] 
)

Cubic'(t) = At^2 + Bt + C, where A = 3(-a + 3(b - c) + d) B = 6(a - 2b + c) C = 3(b - a) Solve for t, keeping only those that fit betwee 0 < t < 1

Definition at line 454 of file SkGeometry.cpp.

455 {
456 // we divide A,B,C by 3 to simplify
457 SkScalar A = d - a + 3*(b - c);
458 SkScalar B = 2*(a - b - b + c);
459 SkScalar C = b - a;
460
461 return SkFindUnitQuadRoots(A, B, C, tValues);
462}

◆ SkFindCubicInflections()

int SkFindCubicInflections ( const SkPoint  src[4],
SkScalar  tValues[2] 
)

http://www.faculty.idc.ac.il/arik/quality/appendixA.html

Inflection means that curvature is zero. Curvature is [F' x F''] / [F'^3] So we solve F'x X F''y - F'y X F''y == 0 After some canceling of the cubic term, we get A = b - a B = c - 2b + a C = d - 3c + 3b - a (BxCy - ByCx)t^2 + (AxCy - AyCx)t + AxBy - AyBx == 0

Definition at line 741 of file SkGeometry.cpp.

741 {
742 SkScalar Ax = src[1].fX - src[0].fX;
743 SkScalar Ay = src[1].fY - src[0].fY;
744 SkScalar Bx = src[2].fX - 2 * src[1].fX + src[0].fX;
745 SkScalar By = src[2].fY - 2 * src[1].fY + src[0].fY;
746 SkScalar Cx = src[3].fX + 3 * (src[1].fX - src[2].fX) - src[0].fX;
747 SkScalar Cy = src[3].fY + 3 * (src[1].fY - src[2].fY) - src[0].fY;
748
749 return SkFindUnitQuadRoots(Bx*Cy - By*Cx,
750 Ax*Cy - Ay*Cx,
751 Ax*By - Ay*Bx,
752 tValues);
753}

◆ SkFindCubicMaxCurvature()

int SkFindCubicMaxCurvature ( const SkPoint  src[4],
SkScalar  tValues[3] 
)

Definition at line 1043 of file SkGeometry.cpp.

1043 {
1044 SkScalar coeffX[4], coeffY[4];
1045 int i;
1046
1047 formulate_F1DotF2(&src[0].fX, coeffX);
1048 formulate_F1DotF2(&src[0].fY, coeffY);
1049
1050 for (i = 0; i < 4; i++) {
1051 coeffX[i] += coeffY[i];
1052 }
1053
1054 int numRoots = solve_cubic_poly(coeffX, tValues);
1055 // now remove extrema where the curvature is zero (mins)
1056 // !!!! need a test for this !!!!
1057 return numRoots;
1058}
static void formulate_F1DotF2(const SkScalar src[], SkScalar coeff[4])
static int solve_cubic_poly(const SkScalar coeff[4], SkScalar tValues[3])

◆ SkFindCubicMidTangent()

float SkFindCubicMidTangent ( const SkPoint  src[4])

Given a src cubic bezier, returns the T value whose tangent angle is halfway between the tangents at p0 and p3.

Definition at line 620 of file SkGeometry.cpp.

620 {
621 // Tangents point in the direction of increasing T, so tan0 and -tan1 both point toward the
622 // midtangent. The bisector of tan0 and -tan1 is orthogonal to the midtangent:
623 //
624 // bisector dot midtangent == 0
625 //
626 SkVector tan0 = (src[0] == src[1]) ? src[2] - src[0] : src[1] - src[0];
627 SkVector tan1 = (src[2] == src[3]) ? src[3] - src[1] : src[3] - src[2];
628 SkVector bisector = SkFindBisector(tan0, -tan1);
629
630 // Find the T value at the midtangent. This is a simple quadratic equation:
631 //
632 // midtangent dot bisector == 0, or using a tangent matrix C' in power basis form:
633 //
634 // |C'x C'y|
635 // |T^2 T 1| * |. . | * |bisector.x| == 0
636 // |. . | |bisector.y|
637 //
638 // The coeffs for the quadratic equation we need to solve are therefore: C' * bisector
639 static const skvx::float4 kM[4] = {skvx::float4(-1, 2, -1, 0),
640 skvx::float4( 3, -4, 1, 0),
641 skvx::float4(-3, 2, 0, 0)};
642 auto C_x = fma(kM[0], src[0].fX,
643 fma(kM[1], src[1].fX,
644 fma(kM[2], src[2].fX, skvx::float4(src[3].fX, 0,0,0))));
645 auto C_y = fma(kM[0], src[0].fY,
646 fma(kM[1], src[1].fY,
647 fma(kM[2], src[2].fY, skvx::float4(src[3].fY, 0,0,0))));
648 auto coeffs = C_x * bisector.x() + C_y * bisector.y();
649
650 // Now solve the quadratic for T.
651 float T = 0;
652 float a=coeffs[0], b=coeffs[1], c=coeffs[2];
653 float discr = b*b - 4*a*c;
654 if (discr > 0) { // This will only be false if the curve is a line.
656 } else {
657 // This is a 0- or 360-degree flat line. It doesn't have single points of midtangent.
658 // (tangent == midtangent at every point on the curve except the cusp points.)
659 // Chop in between both cusps instead, if any. There can be up to two cusps on a flat line,
660 // both where the tangent is perpendicular to the starting tangent:
661 //
662 // tangent dot tan0 == 0
663 //
664 coeffs = C_x * tan0.x() + C_y * tan0.y();
665 a = coeffs[0];
666 b = coeffs[1];
667 if (a != 0) {
668 // We want the point in between both cusps. The midpoint of:
669 //
670 // (-b +/- sqrt(b^2 - 4*a*c)) / (2*a)
671 //
672 // Is equal to:
673 //
674 // -b / (2*a)
675 T = -b / (2*a);
676 }
677 if (!(T > 0 && T < 1)) { // Use "!(positive_logic)" so T=NaN will take this branch.
678 // Either the curve is a flat line with no rotation or FP precision failed us. Chop at
679 // .5.
680 T = .5;
681 }
682 return T;
683 }
684}
SkVector SkFindBisector(SkVector a, SkVector b)
static skvx::float4 fma(const skvx::float4 &f, float m, const skvx::float4 &a)
Vec< 4, float > float4
Definition SkVx.h:1146

◆ SkFindQuadExtrema()

int SkFindQuadExtrema ( SkScalar  a,
SkScalar  b,
SkScalar  c,
SkScalar  tValue[1] 
)

Quad'(t) = At + B, where A = 2(a - 2b + c) B = 2(b - a) Solve for t, only if it fits between 0 < t < 1

Definition at line 265 of file SkGeometry.cpp.

265 {
266 /* At + B == 0
267 t = -B / A
268 */
269 return valid_unit_divide(a - b, a - b - b + c, tValue);
270}

◆ SkFindQuadMaxCurvature()

SkScalar SkFindQuadMaxCurvature ( const SkPoint  src[3])

Given 3 points on a quadratic bezier, if the point of maximum curvature exists on the segment, returns the t value for this point along the curve. Otherwise it will return a value of 0.

Definition at line 344 of file SkGeometry.cpp.

344 {
345 SkScalar Ax = src[1].fX - src[0].fX;
346 SkScalar Ay = src[1].fY - src[0].fY;
347 SkScalar Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX;
348 SkScalar By = src[0].fY - src[1].fY - src[1].fY + src[2].fY;
349
350 SkScalar numer = -(Ax * Bx + Ay * By);
351 SkScalar denom = Bx * Bx + By * By;
352 if (denom < 0) {
353 numer = -numer;
354 denom = -denom;
355 }
356 if (numer <= 0) {
357 return 0;
358 }
359 if (numer >= denom) { // Also catches denom=0.
360 return 1;
361 }
362 SkScalar t = numer / denom;
363 SkASSERT((0 <= t && t < 1) || SkIsNaN(t));
364 return t;
365}

◆ SkFindQuadMidTangent()

float SkFindQuadMidTangent ( const SkPoint  src[3])

Given a src quadratic bezier, returns the T value whose tangent angle is halfway between the tangents at p0 and p3.

Definition at line 231 of file SkGeometry.cpp.

231 {
232 // Tangents point in the direction of increasing T, so tan0 and -tan1 both point toward the
233 // midtangent. The bisector of tan0 and -tan1 is orthogonal to the midtangent:
234 //
235 // n dot midtangent = 0
236 //
237 SkVector tan0 = src[1] - src[0];
238 SkVector tan1 = src[2] - src[1];
239 SkVector bisector = SkFindBisector(tan0, -tan1);
240
241 // The midtangent can be found where (F' dot bisector) = 0:
242 //
243 // 0 = (F'(T) dot bisector) = |2*T 1| * |p0 - 2*p1 + p2| * |bisector.x|
244 // |-2*p0 + 2*p1 | |bisector.y|
245 //
246 // = |2*T 1| * |tan1 - tan0| * |nx|
247 // |2*tan0 | |ny|
248 //
249 // = 2*T * ((tan1 - tan0) dot bisector) + (2*tan0 dot bisector)
250 //
251 // T = (tan0 dot bisector) / ((tan0 - tan1) dot bisector)
252 float T = sk_ieee_float_divide(tan0.dot(bisector), (tan0 - tan1).dot(bisector));
253 if (!(T > 0 && T < 1)) { // Use "!(positive_logic)" so T=nan will take this branch.
254 T = .5; // The quadratic was a line or near-line. Just chop at .5.
255 }
256
257 return T;
258}

◆ SkFindUnitQuadRoots()

int SkFindUnitQuadRoots ( SkScalar  A,
SkScalar  B,
SkScalar  C,
SkScalar  roots[2] 
)

From Numerical Recipes in C.

Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C]) x1 = Q / A x2 = C / Q

Definition at line 95 of file SkGeometry.cpp.

95 {
96 SkASSERT(roots);
97
98 if (A == 0) {
99 return return_check_zero(valid_unit_divide(-C, B, roots));
100 }
101
102 SkScalar* r = roots;
103
104 // use doubles so we don't overflow temporarily trying to compute R
105 double dr = (double)B * B - 4 * (double)A * C;
106 if (dr < 0) {
107 return return_check_zero(0);
108 }
109 dr = sqrt(dr);
111 if (!SkIsFinite(R)) {
112 return return_check_zero(0);
113 }
114
115 SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2;
116 r += valid_unit_divide(Q, A, r);
117 r += valid_unit_divide(C, Q, r);
118 if (r - roots == 2) {
119 if (roots[0] > roots[1]) {
120 using std::swap;
121 swap(roots[0], roots[1]);
122 } else if (roots[0] == roots[1]) { // nearly-equal?
123 r -= 1; // skip the double root
124 }
125 }
126 return return_check_zero((int)(r - roots));
127}
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition SkRefCnt.h:341
#define R(r)

◆ SkMeasureAngleBetweenVectors()

float SkMeasureAngleBetweenVectors ( SkVector  a,
SkVector  b 
)

Measures the angle between two vectors, in the range [0, pi].

Definition at line 197 of file SkGeometry.cpp.

197 {
198 float cosTheta = sk_ieee_float_divide(a.dot(b), sqrtf(a.dot(a) * b.dot(b)));
199 // Pin cosTheta such that if it is NaN (e.g., if a or b was 0), then we return acos(1) = 0.
200 cosTheta = std::max(std::min(1.f, cosTheta), -1.f);
201 return acosf(cosTheta);
202}

◆ SkMeasureNonInflectCubicRotation()

float SkMeasureNonInflectCubicRotation ( const SkPoint  pts[4])

Given a cubic curve with no inflection points, this method measures the rotation in radians.

Rotation is perhaps easiest described via a driving analogy: If you drive your car along the curve from p0 to p3, then by the time you arrive at p3, how many radians will your car have rotated? This is not quite the same as the vector inside the tangents at the endpoints, even without inflection, because the curve might rotate around the outside of the tangents (>= 180 degrees) or the inside (<= 180 degrees).

Cubics can have rotations in the range [0, 2*pi].

NOTE: The caller must either call SkChopCubicAtInflections or otherwise prove that the provided cubic has no inflection points prior to calling this method.

Definition at line 579 of file SkGeometry.cpp.

579 {
580 SkVector a = pts[1] - pts[0];
581 SkVector b = pts[2] - pts[1];
582 SkVector c = pts[3] - pts[2];
583 if (a.isZero()) {
585 }
586 if (b.isZero()) {
588 }
589 if (c.isZero()) {
591 }
592 // Postulate: When no points are colocated and there are no inflection points in T=0..1, the
593 // rotation is: 360 degrees, minus the angle [p0,p1,p2], minus the angle [p1,p2,p3].
595}
float SkMeasureAngleBetweenVectors(SkVector a, SkVector b)
#define SK_ScalarPI
Definition SkScalar.h:21
bool isZero() const

◆ SkScalarCubeRoot()

static SkScalar SkScalarCubeRoot ( SkScalar  x)
static

Definition at line 950 of file SkGeometry.cpp.

950 {
951 return SkScalarPow(x, 0.3333333f);
952}
#define SkScalarPow(b, e)
Definition SkScalar.h:43

◆ solve_cubic_poly()

static int solve_cubic_poly ( const SkScalar  coeff[4],
SkScalar  tValues[3] 
)
static

Definition at line 961 of file SkGeometry.cpp.

961 {
962 if (SkScalarNearlyZero(coeff[0])) { // we're just a quadratic
963 return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues);
964 }
965
966 SkScalar a, b, c, Q, R;
967
968 {
969 SkASSERT(coeff[0] != 0);
970
971 SkScalar inva = SkScalarInvert(coeff[0]);
972 a = coeff[1] * inva;
973 b = coeff[2] * inva;
974 c = coeff[3] * inva;
975 }
976 Q = (a*a - b*3) / 9;
977 R = (2*a*a*a - 9*a*b + 27*c) / 54;
978
979 SkScalar Q3 = Q * Q * Q;
980 SkScalar R2MinusQ3 = R * R - Q3;
981 SkScalar adiv3 = a / 3;
982
983 if (R2MinusQ3 < 0) { // we have 3 real roots
984 // the divide/root can, due to finite precisions, be slightly outside of -1...1
985 SkScalar theta = SkScalarACos(SkTPin(R / SkScalarSqrt(Q3), -1.0f, 1.0f));
986 SkScalar neg2RootQ = -2 * SkScalarSqrt(Q);
987
988 tValues[0] = SkTPin(neg2RootQ * SkScalarCos(theta/3) - adiv3, 0.0f, 1.0f);
989 tValues[1] = SkTPin(neg2RootQ * SkScalarCos((theta + 2*SK_ScalarPI)/3) - adiv3, 0.0f, 1.0f);
990 tValues[2] = SkTPin(neg2RootQ * SkScalarCos((theta - 2*SK_ScalarPI)/3) - adiv3, 0.0f, 1.0f);
991 SkDEBUGCODE(test_collaps_duplicates();)
992
993 // now sort the roots
994 bubble_sort(tValues, 3);
995 return collaps_duplicates(tValues, 3);
996 } else { // we have 1 real root
997 SkScalar A = SkScalarAbs(R) + SkScalarSqrt(R2MinusQ3);
999 if (R > 0) {
1000 A = -A;
1001 }
1002 if (A != 0) {
1003 A += Q / A;
1004 }
1005 tValues[0] = SkTPin(A - adiv3, 0.0f, 1.0f);
1006 return 1;
1007 }
1008}
static int collaps_duplicates(SkScalar array[], int count)
static SkScalar SkScalarCubeRoot(SkScalar x)
void bubble_sort(T array[], int count)
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:101
#define SkScalarCos(radians)
Definition SkScalar.h:46
#define SkScalarACos(val)
Definition SkScalar.h:49

◆ solve_quadratic_equation_for_midtangent() [1/2]

static float solve_quadratic_equation_for_midtangent ( float  a,
float  b,
float  c 
)
static

Definition at line 616 of file SkGeometry.cpp.

616 {
617 return solve_quadratic_equation_for_midtangent(a, b, c, b*b - 4*a*c);
618}

◆ solve_quadratic_equation_for_midtangent() [2/2]

static float solve_quadratic_equation_for_midtangent ( float  a,
float  b,
float  c,
float  discr 
)
static

Definition at line 602 of file SkGeometry.cpp.

602 {
603 // Quadratic formula from Numerical Recipes in C:
604 float q = -.5f * (b + copysignf(sqrtf(discr), b));
605 // The roots are q/a and c/q. Pick the midtangent closer to T=.5.
606 float _5qa = -.5f*q*a;
607 float T = fabsf(q*q + _5qa) < fabsf(a*c + _5qa) ? sk_ieee_float_divide(q,a)
609 if (!(T > 0 && T < 1)) { // Use "!(positive_logic)" so T=NaN will take this branch.
610 // Either the curve is a flat line with no rotation or FP precision failed us. Chop at .5.
611 T = .5;
612 }
613 return T;
614}

◆ subdivide()

static SkPoint * subdivide ( const SkConic src,
SkPoint  pts[],
int  level 
)
static

Definition at line 1529 of file SkGeometry.cpp.

1529 {
1530 SkASSERT(level >= 0);
1531
1532 if (0 == level) {
1533 memcpy(pts, &src.fPts[1], 2 * sizeof(SkPoint));
1534 return pts + 2;
1535 } else {
1536 SkConic dst[2];
1537 src.chop(dst);
1538 const SkScalar startY = src.fPts[0].fY;
1539 SkScalar endY = src.fPts[2].fY;
1540 if (between(startY, src.fPts[1].fY, endY)) {
1541 // If the input is monotonic and the output is not, the scan converter hangs.
1542 // Ensure that the chopped conics maintain their y-order.
1543 SkScalar midY = dst[0].fPts[2].fY;
1544 if (!between(startY, midY, endY)) {
1545 // If the computed midpoint is outside the ends, move it to the closer one.
1546 SkScalar closerY = SkTAbs(midY - startY) < SkTAbs(midY - endY) ? startY : endY;
1547 dst[0].fPts[2].fY = dst[1].fPts[0].fY = closerY;
1548 }
1549 if (!between(startY, dst[0].fPts[1].fY, dst[0].fPts[2].fY)) {
1550 // If the 1st control is not between the start and end, put it at the start.
1551 // This also reduces the quad to a line.
1552 dst[0].fPts[1].fY = startY;
1553 }
1554 if (!between(dst[1].fPts[0].fY, dst[1].fPts[1].fY, endY)) {
1555 // If the 2nd control is not between the start and end, put it at the end.
1556 // This also reduces the quad to a line.
1557 dst[1].fPts[1].fY = endY;
1558 }
1559 // Verify that all five points are in order.
1560 SkASSERT(between(startY, dst[0].fPts[1].fY, dst[0].fPts[2].fY));
1561 SkASSERT(between(dst[0].fPts[1].fY, dst[0].fPts[2].fY, dst[1].fPts[1].fY));
1562 SkASSERT(between(dst[0].fPts[2].fY, dst[1].fPts[1].fY, endY));
1563 }
1564 --level;
1565 pts = subdivide(dst[0], pts, level);
1566 return subdivide(dst[1], pts, level);
1567 }
1568}

◆ subdivide_w_value()

static SkScalar subdivide_w_value ( SkScalar  w)
static

Definition at line 1397 of file SkGeometry.cpp.

1397 {
1399}
#define SK_ScalarHalf
Definition SkScalar.h:19

◆ unchecked_mix()

template<int N, typename T >
static skvx::Vec< N, T > unchecked_mix ( const skvx::Vec< N, T > &  a,
const skvx::Vec< N, T > &  b,
const skvx::Vec< N, T > &  t 
)
inlinestatic

Definition at line 468 of file SkGeometry.cpp.

469 {
470 return (b - a)*t + a;
471}

◆ write_cubic_inflection_roots()

static void write_cubic_inflection_roots ( double  t0,
double  s0,
double  t1,
double  s1,
double *  t,
double *  s 
)
inlinestatic

Definition at line 791 of file SkGeometry.cpp.

792 {
793 t[0] = t0;
794 s[0] = s0;
795
796 // This copysign/abs business orients the implicit function so positive values are always on the
797 // "left" side of the curve.
798 t[1] = -copysign(t1, t1 * s1);
799 s[1] = -fabs(s1);
800
801 // Ensure t[0]/s[0] <= t[1]/s[1] (s[1] is negative from above).
802 if (copysign(s[1], s[0]) * t[0] > -fabs(s[0]) * t[1]) {
803 using std::swap;
804 swap(t[0], t[1]);
805 swap(s[0], s[1]);
806 }
807}