1/* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#ifndef GrPathUtils_DEFINED 9#define GrPathUtils_DEFINED 10 11#include "include/core/SkRect.h" 12#include "include/private/SkTArray.h" 13#include "src/core/SkGeometry.h" 14#include "src/core/SkPathPriv.h" 15#include "src/gpu/BufferWriter.h" 16#include "src/gpu/GrVx.h" 17 18class SkMatrix; 19 20/** 21 * Utilities for evaluating paths. 22 */ 23namespace GrPathUtils { 24 25// When tessellating curved paths into linear segments, this defines the maximum distance in screen 26// space which a segment may deviate from the mathematically correct value. Above this value, the 27// segment will be subdivided. 28// This value was chosen to approximate the supersampling accuracy of the raster path (16 samples, 29// or one quarter pixel). 30static const SkScalar kDefaultTolerance = SkDoubleToScalar(0.25); 31 32// We guarantee that no quad or cubic will ever produce more than this many points 33static const int kMaxPointsPerCurve = 1 << 10; 34 35// Very small tolerances will be increased to a minimum threshold value, to avoid division problems 36// in subsequent math. 37SkScalar scaleToleranceToSrc(SkScalar devTol, 38 const SkMatrix& viewM, 39 const SkRect& pathBounds); 40 41// Returns the maximum number of vertices required when using a recursive chopping algorithm to 42// linearize the quadratic Bezier (e.g. generateQuadraticPoints below) to the given error tolerance. 43// This is a power of two and will not exceed kMaxPointsPerCurve. 44uint32_t quadraticPointCount(const SkPoint points[], SkScalar tol); 45 46// Returns the number of points actually written to 'points', will be <= to 'pointsLeft' 47uint32_t generateQuadraticPoints(const SkPoint& p0, 48 const SkPoint& p1, 49 const SkPoint& p2, 50 SkScalar tolSqd, 51 SkPoint** points, 52 uint32_t pointsLeft); 53 54// Returns the maximum number of vertices required when using a recursive chopping algorithm to 55// linearize the cubic Bezier (e.g. generateQuadraticPoints below) to the given error tolerance. 56// This is a power of two and will not exceed kMaxPointsPerCurve. 57uint32_t cubicPointCount(const SkPoint points[], SkScalar tol); 58 59// Returns the number of points actually written to 'points', will be <= to 'pointsLeft' 60uint32_t generateCubicPoints(const SkPoint& p0, 61 const SkPoint& p1, 62 const SkPoint& p2, 63 const SkPoint& p3, 64 SkScalar tolSqd, 65 SkPoint** points, 66 uint32_t pointsLeft); 67 68// A 2x3 matrix that goes from the 2d space coordinates to UV space where u^2-v = 0 specifies the 69// quad. The matrix is determined by the control points of the quadratic. 70class QuadUVMatrix { 71public: 72 QuadUVMatrix() {} 73 // Initialize the matrix from the control pts 74 QuadUVMatrix(const SkPoint controlPts[3]) { this->set(controlPts); } 75 void set(const SkPoint controlPts[3]); 76 77 /** 78 * Applies the matrix to vertex positions to compute UV coords. 79 * 80 * vertices is a pointer to the first vertex. 81 * vertexCount is the number of vertices. 82 * stride is the size of each vertex. 83 * uvOffset is the offset of the UV values within each vertex. 84 */ 85 void apply(void* vertices, int vertexCount, size_t stride, size_t uvOffset) const { 86 intptr_t xyPtr = reinterpret_cast<intptr_t>(vertices); 87 intptr_t uvPtr = reinterpret_cast<intptr_t>(vertices) + uvOffset; 88 float sx = fM[0]; 89 float kx = fM[1]; 90 float tx = fM[2]; 91 float ky = fM[3]; 92 float sy = fM[4]; 93 float ty = fM[5]; 94 for (int i = 0; i < vertexCount; ++i) { 95 const SkPoint* xy = reinterpret_cast<const SkPoint*>(xyPtr); 96 SkPoint* uv = reinterpret_cast<SkPoint*>(uvPtr); 97 uv->fX = sx * xy->fX + kx * xy->fY + tx; 98 uv->fY = ky * xy->fX + sy * xy->fY + ty; 99 xyPtr += stride; 100 uvPtr += stride; 101 } 102 } 103private: 104 float fM[6]; 105}; 106 107// Input is 3 control points and a weight for a bezier conic. Calculates the three linear 108// functionals (K,L,M) that represent the implicit equation of the conic, k^2 - lm. 109// 110// Output: klm holds the linear functionals K,L,M as row vectors: 111// 112// | ..K.. | | x | | k | 113// | ..L.. | * | y | == | l | 114// | ..M.. | | 1 | | m | 115// 116void getConicKLM(const SkPoint p[3], const SkScalar weight, SkMatrix* klm); 117 118// Converts a cubic into a sequence of quads. If working in device space use tolScale = 1, otherwise 119// set based on stretchiness of the matrix. The result is sets of 3 points in quads. This will 120// preserve the starting and ending tangent vectors (modulo FP precision). 121void convertCubicToQuads(const SkPoint p[4], 122 SkScalar tolScale, 123 SkTArray<SkPoint, true>* quads); 124 125// When we approximate a cubic {a,b,c,d} with a quadratic we may have to ensure that the new control 126// point lies between the lines ab and cd. The convex path renderer requires this. It starts with a 127// path where all the control points taken together form a convex polygon. It relies on this 128// property and the quadratic approximation of cubics step cannot alter it. This variation enforces 129// this constraint. The cubic must be simple and dir must specify the orientation of the contour 130// containing the cubic. 131void convertCubicToQuadsConstrainToTangents(const SkPoint p[4], 132 SkScalar tolScale, 133 SkPathFirstDirection dir, 134 SkTArray<SkPoint, true>* quads); 135 136// Converts the given line to a cubic bezier. 137// NOTE: This method interpolates at 1/3 and 2/3, but if suitable in context, the cubic 138// {p0, p0, p1, p1} may also work. 139inline void writeLineAsCubic(SkPoint startPt, SkPoint endPt, skgpu::VertexWriter* writer) { 140 using grvx::float2, skvx::bit_pun; 141 float2 p0 = bit_pun<float2>(startPt); 142 float2 p1 = bit_pun<float2>(endPt); 143 float2 v = (p1 - p0) * (1/3.f); 144 *writer << p0 << (p0 + v) << (p1 - v) << p1; 145} 146 147// Converts the given quadratic bezier to a cubic. 148inline void writeQuadAsCubic(const SkPoint p[3], skgpu::VertexWriter* writer) { 149 using grvx::float2, skvx::bit_pun; 150 float2 p0 = bit_pun<float2>(p[0]); 151 float2 p1 = bit_pun<float2>(p[1]); 152 float2 p2 = bit_pun<float2>(p[2]); 153 float2 c = p1 * (2/3.f); 154 *writer << p0 << (p0*(1/3.f) + c) << (p2 * (1/3.f) + c) << p2; 155} 156inline void convertQuadToCubic(const SkPoint p[3], SkPoint out[4]) { 157 skgpu::VertexWriter writer(out); 158 writeQuadAsCubic(p, &writer); 159} 160 161// Finds 0, 1, or 2 T values at which to chop the given curve in order to guarantee the resulting 162// cubics are convex and rotate no more than 180 degrees. 163// 164// - If the cubic is "serpentine", then the T values are any inflection points in [0 < T < 1]. 165// - If the cubic is linear, then the T values are any 180-degree cusp points in [0 < T < 1]. 166// - Otherwise the T value is the point at which rotation reaches 180 degrees, iff in [0 < T < 1]. 167// 168// 'areCusps' is set to true if the chop point occurred at a cusp (within tolerance), or if the chop 169// point(s) occurred at 180-degree turnaround points on a degenerate flat line. 170int findCubicConvex180Chops(const SkPoint[], float T[2], bool* areCusps); 171 172// Returns true if the given conic (or quadratic) has a cusp point. The w value is not necessary in 173// determining this. If there is a cusp, it can be found at the midtangent. 174inline bool conicHasCusp(const SkPoint p[3]) { 175 SkVector a = p[1] - p[0]; 176 SkVector b = p[2] - p[1]; 177 // A conic of any class can only have a cusp if it is a degenerate flat line with a 180 degree 178 // turnarund. To detect this, the beginning and ending tangents must be parallel 179 // (a.cross(b) == 0) and pointing in opposite directions (a.dot(b) < 0). 180 return a.cross(b) == 0 && a.dot(b) < 0; 181} 182 183} // namespace GrPathUtils 184 185#endif 186