1/* 2 * Copyright 2019 Google LLC 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 GrQuadUtils_DEFINED 9#define GrQuadUtils_DEFINED 10 11#include "include/private/SkVx.h" 12#include "src/gpu/geometry/GrQuad.h" 13 14enum class GrQuadAAFlags; 15enum class GrAA : bool; 16enum class GrAAType : unsigned; 17struct SkRect; 18 19namespace GrQuadUtils { 20 21 // Resolve disagreements between the overall requested AA type and the per-edge quad AA flags. 22 // Both outAAType and outEdgeFlags will be updated. 23 void ResolveAAType(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags, 24 const GrQuad& quad, GrAAType* outAAtype, GrQuadAAFlags* outEdgeFlags); 25 26 /** 27 * Clip the device vertices of 'quad' to be in front of the W = 0 plane (w/in epsilon). The 28 * local coordinates will be updated to match the new clipped vertices. This returns the number 29 * of clipped quads that need to be drawn: 0 if 'quad' was entirely behind the plane, 1 if 30 * 'quad' did not need to be clipped or if 2 or 3 vertices were clipped, or 2 if 'quad' had one 31 * vertex clipped (producing a pentagonal shape spanned by 'quad' and 'extraVertices'). 32 */ 33 int ClipToW0(DrawQuad* quad, DrawQuad* extraVertices); 34 35 /** 36 * Crops quad to the provided device-space axis-aligned rectangle. If the intersection of this 37 * quad (projected) and cropRect results in a quadrilateral, this returns true. If not, this 38 * quad may be updated to be a smaller quad of the same type such that its intersection with 39 * cropRect is visually the same. This function assumes that the 'quad' coordinates are finite. 40 * 41 * The provided edge flags are updated to reflect edges clipped by cropRect (toggling on or off 42 * based on cropAA policy). If provided, the local coordinates will be updated to reflect the 43 * updated device coordinates of this quad. 44 * 45 * If 'computeLocal' is false, the local coordinates in 'quad' will not be modified. 46 */ 47 bool CropToRect(const SkRect& cropRect, GrAA cropAA, DrawQuad* quad, bool computeLocal=true); 48 49 inline void Outset(const skvx::Vec<4, float>& edgeDistances, GrQuad* quad); 50 51 bool WillUseHairline(const GrQuad& quad, GrAAType aaType, GrQuadAAFlags edgeFlags); 52 53 class TessellationHelper { 54 public: 55 // Set the original device and (optional) local coordinates that are inset or outset 56 // by the requested edge distances. Use nullptr if there are no local coordinates to update. 57 // This assumes all device coordinates have been clipped to W > 0. 58 void reset(const GrQuad& deviceQuad, const GrQuad* localQuad); 59 60 // Calculates a new quadrilateral with edges parallel to the original except that they 61 // have been moved inwards by edgeDistances (which should be positive). Distances are 62 // ordered L, B, T, R to match CCW tristrip ordering of GrQuad vertices. Edges that are 63 // not moved (i.e. distance == 0) will not be used in calculations and the corners will 64 // remain on that edge. 65 // 66 // The per-vertex coverage will be returned. When the inset geometry does not collapse to 67 // a point or line, this will be 1.0 for every vertex. When it does collapse, the per-vertex 68 // coverages represent estimated pixel coverage to simulate drawing the subpixel-sized 69 // original quad. 70 // 71 // Note: the edge distances are in device pixel units, so after rendering the new quad 72 // edge's shortest distance to the original quad's edge would be equal to provided edge dist 73 skvx::Vec<4, float> inset(const skvx::Vec<4, float>& edgeDistances, 74 GrQuad* deviceInset, GrQuad* localInset); 75 76 // Calculates a new quadrilateral that outsets the original edges by the given distances. 77 // Other than moving edges outwards, this function is equivalent to inset(). If the exact 78 // same edge distances are provided, certain internal computations can be reused across 79 // consecutive calls to inset() and outset() (in any order). 80 void outset(const skvx::Vec<4, float>& edgeDistances, 81 GrQuad* deviceOutset, GrQuad* localOutset); 82 83 // Compute the edge equations of the original device space quad passed to 'reset()'. The 84 // coefficients are stored per-edge in 'a', 'b', and 'c', such that ax + by + c = 0, and 85 // a positive distance indicates the interior of the quad. Edges are ordered L, B, T, R, 86 // matching edge distances passed to inset() and outset(). 87 void getEdgeEquations(skvx::Vec<4, float>* a, 88 skvx::Vec<4, float>* b, 89 skvx::Vec<4, float>* c); 90 91 // Compute the edge lengths of the original device space quad passed to 'reset()'. The 92 // edge lengths are ordered LBTR to match distances passed to inset() and outset(). 93 skvx::Vec<4, float> getEdgeLengths(); 94 95 // Determine if the original device space quad has vertices closer than 1px to its opposing 96 // edges, without going through the full work of computing the insets (assuming that the 97 // inset distances would be 0.5px). 98 bool isSubpixel(); 99 100 private: 101 // NOTE: This struct is named 'EdgeVectors' because it holds a lot of cached calculations 102 // pertaining to the edge vectors of the input quad, projected into 2D device coordinates. 103 // While they are not direction vectors, this struct represents a convenient storage space 104 // for the projected corners of the quad. 105 struct EdgeVectors { 106 // Projected corners (x/w and y/w); these are the 2D coordinates that determine the 107 // actual edge direction vectors, dx, dy, and invLengths 108 skvx::Vec<4, float> fX2D, fY2D; 109 // Normalized edge vectors of the device space quad, ordered L, B, T, R 110 // (i.e. next_ccw(x) - x). 111 skvx::Vec<4, float> fDX, fDY; 112 // Reciprocal of edge length of the device space quad, i.e. 1 / sqrt(dx*dx + dy*dy) 113 skvx::Vec<4, float> fInvLengths; 114 // Theta represents the angle formed by the two edges connected at each corner. 115 skvx::Vec<4, float> fCosTheta; 116 skvx::Vec<4, float> fInvSinTheta; // 1 / sin(theta) 117 118 void reset(const skvx::Vec<4, float>& xs, const skvx::Vec<4, float>& ys, 119 const skvx::Vec<4, float>& ws, GrQuad::Type quadType); 120 }; 121 122 struct EdgeEquations { 123 // a * x + b * y + c = 0; positive distance is inside the quad; ordered LBTR. 124 skvx::Vec<4, float> fA, fB, fC; 125 126 void reset(const EdgeVectors& edgeVectors); 127 128 skvx::Vec<4, float> estimateCoverage(const skvx::Vec<4, float>& x2d, 129 const skvx::Vec<4, float>& y2d) const; 130 131 bool isSubpixel(const skvx::Vec<4, float>& x2d, const skvx::Vec<4, float>& y2d) const; 132 133 // Outsets or insets 'x2d' and 'y2d' in place. To be used when the interior is very 134 // small, edges are near parallel, or edges are very short/zero-length. Returns number 135 // of effective vertices in the degenerate quad. 136 int computeDegenerateQuad(const skvx::Vec<4, float>& signedEdgeDistances, 137 skvx::Vec<4, float>* x2d, skvx::Vec<4, float>* y2d, 138 skvx::Vec<4, int32_t>* aaMask) const; 139 }; 140 141 struct OutsetRequest { 142 // Positive edge distances to move each edge of the quad. These distances represent the 143 // shortest (perpendicular) distance between the original edge and the inset or outset 144 // edge. If the distance is 0, then the edge will not move. 145 skvx::Vec<4, float> fEdgeDistances; 146 // True if the new corners cannot be calculated by simply adding scaled edge vectors. 147 // The quad may be degenerate because of the original geometry (near colinear edges), or 148 // be because of the requested edge distances (collapse of inset, etc.) 149 bool fInsetDegenerate; 150 bool fOutsetDegenerate; 151 152 void reset(const EdgeVectors& edgeVectors, GrQuad::Type quadType, 153 const skvx::Vec<4, float>& edgeDistances); 154 }; 155 156 struct Vertices { 157 // X, Y, and W coordinates in device space. If not perspective, w should be set to 1.f 158 skvx::Vec<4, float> fX, fY, fW; 159 // U, V, and R coordinates representing local quad. 160 // Ignored depending on uvrCount (0, 1, 2). 161 skvx::Vec<4, float> fU, fV, fR; 162 int fUVRCount; 163 164 void reset(const GrQuad& deviceQuad, const GrQuad* localQuad); 165 166 void asGrQuads(GrQuad* deviceOut, GrQuad::Type deviceType, 167 GrQuad* localOut, GrQuad::Type localType) const; 168 169 // Update the device and optional local coordinates by moving the corners along their 170 // edge vectors such that the new edges have moved 'signedEdgeDistances' from their 171 // original lines. This should only be called if the 'edgeVectors' fInvSinTheta data is 172 // numerically sound. 173 void moveAlong(const EdgeVectors& edgeVectors, 174 const skvx::Vec<4, float>& signedEdgeDistances); 175 176 // Update the device coordinates by deriving (x,y,w) that project to (x2d, y2d), with 177 // optional local coordinates updated to match the new vertices. It is assumed that 178 // 'mask' was respected when determining (x2d, y2d), but it is used to ensure that only 179 // unmasked unprojected edge vectors are used when computing device and local coords. 180 void moveTo(const skvx::Vec<4, float>& x2d, 181 const skvx::Vec<4, float>& y2d, 182 const skvx::Vec<4, int32_t>& mask); 183 }; 184 185 Vertices fOriginal; 186 EdgeVectors fEdgeVectors; 187 GrQuad::Type fDeviceType; 188 GrQuad::Type fLocalType; 189 190 // Lazily computed as needed; use accessor functions instead of direct access. 191 OutsetRequest fOutsetRequest; 192 EdgeEquations fEdgeEquations; 193 194 // Validity of Vertices/EdgeVectors (always true after first call to set()). 195 bool fVerticesValid = false; 196 // Validity of outset request (true after calling getOutsetRequest() until next set() call 197 // or next inset/outset() with different edge distances). 198 bool fOutsetRequestValid = false; 199 // Validity of edge equations (true after calling getEdgeEquations() until next set() call). 200 bool fEdgeEquationsValid = false; 201 202 // The requested edge distances must be positive so that they can be reused between inset 203 // and outset calls. 204 const OutsetRequest& getOutsetRequest(const skvx::Vec<4, float>& edgeDistances); 205 const EdgeEquations& getEdgeEquations(); 206 207 // Outsets or insets 'vertices' by the given perpendicular 'signedEdgeDistances' (inset or 208 // outset is determined implicitly by the sign of the distances). 209 void adjustVertices(const skvx::Vec<4, float>& signedEdgeDistances, Vertices* vertices); 210 // Like adjustVertices() but handles empty edges, collapsed quads, numerical issues, and 211 // returns the number of effective vertices in the adjusted shape. 212 int adjustDegenerateVertices(const skvx::Vec<4, float>& signedEdgeDistances, 213 Vertices* vertices); 214 215 friend int ClipToW0(DrawQuad*, DrawQuad*); // To reuse Vertices struct 216 }; 217 218}; // namespace GrQuadUtils 219 220void GrQuadUtils::Outset(const skvx::Vec<4, float>& edgeDistances, GrQuad* quad) { 221 TessellationHelper outsetter; 222 outsetter.reset(*quad, nullptr); 223 outsetter.outset(edgeDistances, quad, nullptr); 224} 225 226#endif 227