| 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 |  | 
|---|
| 14 | enum class GrQuadAAFlags; | 
|---|
| 15 | enum class GrAA : bool; | 
|---|
| 16 | enum class GrAAType : unsigned; | 
|---|
| 17 | struct SkRect; | 
|---|
| 18 |  | 
|---|
| 19 | namespace 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* ); | 
|---|
| 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 | class TessellationHelper { | 
|---|
| 50 | public: | 
|---|
| 51 | // Set the original device and (optional) local coordinates that are inset or outset | 
|---|
| 52 | // by the requested edge distances. Use nullptr if there are no local coordinates to update. | 
|---|
| 53 | // This assumes all device coordinates have been clipped to W > 0. | 
|---|
| 54 | void reset(const GrQuad& deviceQuad, const GrQuad* localQuad); | 
|---|
| 55 |  | 
|---|
| 56 | // Calculates a new quadrilateral with edges parallel to the original except that they | 
|---|
| 57 | // have been moved inwards by edgeDistances (which should be positive). Distances are | 
|---|
| 58 | // ordered L, B, T, R to match CCW tristrip ordering of GrQuad vertices. Edges that are | 
|---|
| 59 | // not moved (i.e. distance == 0) will not be used in calculations and the corners will | 
|---|
| 60 | // remain on that edge. | 
|---|
| 61 | // | 
|---|
| 62 | // The per-vertex coverage will be returned. When the inset geometry does not collapse to | 
|---|
| 63 | // a point or line, this will be 1.0 for every vertex. When it does collapse, the per-vertex | 
|---|
| 64 | // coverages represent estimated pixel coverage to simulate drawing the subpixel-sized | 
|---|
| 65 | // original quad. | 
|---|
| 66 | // | 
|---|
| 67 | // Note: the edge distances are in device pixel units, so after rendering the new quad | 
|---|
| 68 | // edge's shortest distance to the original quad's edge would be equal to provided edge dist | 
|---|
| 69 | skvx::Vec<4, float> inset(const skvx::Vec<4, float>& edgeDistances, | 
|---|
| 70 | GrQuad* deviceInset, GrQuad* localInset); | 
|---|
| 71 |  | 
|---|
| 72 | // Calculates a new quadrilateral that outsets the original edges by the given distances. | 
|---|
| 73 | // Other than moving edges outwards, this function is equivalent to inset(). If the exact | 
|---|
| 74 | // same edge distances are provided, certain internal computations can be reused across | 
|---|
| 75 | // consecutive calls to inset() and outset() (in any order). | 
|---|
| 76 | void outset(const skvx::Vec<4, float>& edgeDistances, | 
|---|
| 77 | GrQuad* deviceOutset, GrQuad* localOutset); | 
|---|
| 78 |  | 
|---|
| 79 | private: | 
|---|
| 80 | // NOTE: This struct is named 'EdgeVectors' because it holds a lot of cached calculations | 
|---|
| 81 | // pertaining to the edge vectors of the input quad, projected into 2D device coordinates. | 
|---|
| 82 | // While they are not direction vectors, this struct represents a convenient storage space | 
|---|
| 83 | // for the projected corners of the quad. | 
|---|
| 84 | struct EdgeVectors { | 
|---|
| 85 | // Projected corners (x/w and y/w); these are the 2D coordinates that determine the | 
|---|
| 86 | // actual edge direction vectors, dx, dy, and invLengths | 
|---|
| 87 | skvx::Vec<4, float> fX2D, fY2D; | 
|---|
| 88 | // Normalized edge vectors of the device space quad, ordered L, B, T, R | 
|---|
| 89 | // (i.e. next_ccw(x) - x). | 
|---|
| 90 | skvx::Vec<4, float> fDX, fDY; | 
|---|
| 91 | // Reciprocal of edge length of the device space quad, i.e. 1 / sqrt(dx*dx + dy*dy) | 
|---|
| 92 | skvx::Vec<4, float> fInvLengths; | 
|---|
| 93 | // Theta represents the angle formed by the two edges connected at each corner. | 
|---|
| 94 | skvx::Vec<4, float> fCosTheta; | 
|---|
| 95 | skvx::Vec<4, float> fInvSinTheta; // 1 / sin(theta) | 
|---|
| 96 |  | 
|---|
| 97 | void reset(const skvx::Vec<4, float>& xs, const skvx::Vec<4, float>& ys, | 
|---|
| 98 | const skvx::Vec<4, float>& ws, GrQuad::Type quadType); | 
|---|
| 99 | }; | 
|---|
| 100 |  | 
|---|
| 101 | struct EdgeEquations { | 
|---|
| 102 | // a * x + b * y + c = 0; positive distance is inside the quad; ordered LBTR. | 
|---|
| 103 | skvx::Vec<4, float> fA, fB, fC; | 
|---|
| 104 |  | 
|---|
| 105 | void reset(const EdgeVectors& edgeVectors); | 
|---|
| 106 |  | 
|---|
| 107 | skvx::Vec<4, float> estimateCoverage(const skvx::Vec<4, float>& x2d, | 
|---|
| 108 | const skvx::Vec<4, float>& y2d) const; | 
|---|
| 109 |  | 
|---|
| 110 | // Outsets or insets 'x2d' and 'y2d' in place. To be used when the interior is very | 
|---|
| 111 | // small, edges are near parallel, or edges are very short/zero-length. Returns number | 
|---|
| 112 | // of effective vertices in the degenerate quad. | 
|---|
| 113 | int computeDegenerateQuad(const skvx::Vec<4, float>& signedEdgeDistances, | 
|---|
| 114 | skvx::Vec<4, float>* x2d, skvx::Vec<4, float>* y2d) const; | 
|---|
| 115 | }; | 
|---|
| 116 |  | 
|---|
| 117 | struct OutsetRequest { | 
|---|
| 118 | // Positive edge distances to move each edge of the quad. These distances represent the | 
|---|
| 119 | // shortest (perpendicular) distance between the original edge and the inset or outset | 
|---|
| 120 | // edge. If the distance is 0, then the edge will not move. | 
|---|
| 121 | skvx::Vec<4, float> fEdgeDistances; | 
|---|
| 122 | // True if the new corners cannot be calculated by simply adding scaled edge vectors. | 
|---|
| 123 | // The quad may be degenerate because of the original geometry (near colinear edges), or | 
|---|
| 124 | // be because of the requested edge distances (collapse of inset, etc.) | 
|---|
| 125 | bool fInsetDegenerate; | 
|---|
| 126 | bool fOutsetDegenerate; | 
|---|
| 127 |  | 
|---|
| 128 | void reset(const EdgeVectors& edgeVectors, GrQuad::Type quadType, | 
|---|
| 129 | const skvx::Vec<4, float>& edgeDistances); | 
|---|
| 130 | }; | 
|---|
| 131 |  | 
|---|
| 132 | struct Vertices { | 
|---|
| 133 | // X, Y, and W coordinates in device space. If not perspective, w should be set to 1.f | 
|---|
| 134 | skvx::Vec<4, float> fX, fY, fW; | 
|---|
| 135 | // U, V, and R coordinates representing local quad. | 
|---|
| 136 | // Ignored depending on uvrCount (0, 1, 2). | 
|---|
| 137 | skvx::Vec<4, float> fU, fV, fR; | 
|---|
| 138 | int fUVRCount; | 
|---|
| 139 |  | 
|---|
| 140 | void reset(const GrQuad& deviceQuad, const GrQuad* localQuad); | 
|---|
| 141 |  | 
|---|
| 142 | void asGrQuads(GrQuad* deviceOut, GrQuad::Type deviceType, | 
|---|
| 143 | GrQuad* localOut, GrQuad::Type localType) const; | 
|---|
| 144 |  | 
|---|
| 145 | // Update the device and optional local coordinates by moving the corners along their | 
|---|
| 146 | // edge vectors such that the new edges have moved 'signedEdgeDistances' from their | 
|---|
| 147 | // original lines. This should only be called if the 'edgeVectors' fInvSinTheta data is | 
|---|
| 148 | // numerically sound. | 
|---|
| 149 | void moveAlong(const EdgeVectors& edgeVectors, | 
|---|
| 150 | const skvx::Vec<4, float>& signedEdgeDistances); | 
|---|
| 151 |  | 
|---|
| 152 | // Update the device coordinates by deriving (x,y,w) that project to (x2d, y2d), with | 
|---|
| 153 | // optional local coordinates updated to match the new vertices. It is assumed that | 
|---|
| 154 | // 'mask' was respected when determining (x2d, y2d), but it is used to ensure that only | 
|---|
| 155 | // unmasked unprojected edge vectors are used when computing device and local coords. | 
|---|
| 156 | void moveTo(const skvx::Vec<4, float>& x2d, | 
|---|
| 157 | const skvx::Vec<4, float>& y2d, | 
|---|
| 158 | const skvx::Vec<4, int32_t>& mask); | 
|---|
| 159 | }; | 
|---|
| 160 |  | 
|---|
| 161 | Vertices            fOriginal; | 
|---|
| 162 | EdgeVectors         fEdgeVectors; | 
|---|
| 163 | GrQuad::Type        fDeviceType; | 
|---|
| 164 | GrQuad::Type        fLocalType; | 
|---|
| 165 |  | 
|---|
| 166 | // Lazily computed as needed; use accessor functions instead of direct access. | 
|---|
| 167 | OutsetRequest       fOutsetRequest; | 
|---|
| 168 | EdgeEquations       fEdgeEquations; | 
|---|
| 169 |  | 
|---|
| 170 | // Validity of Vertices/EdgeVectors (always true after first call to set()). | 
|---|
| 171 | bool fVerticesValid      = false; | 
|---|
| 172 | // Validity of outset request (true after calling getOutsetRequest() until next set() call | 
|---|
| 173 | // or next inset/outset() with different edge distances). | 
|---|
| 174 | bool fOutsetRequestValid = false; | 
|---|
| 175 | // Validity of edge equations (true after calling getEdgeEquations() until next set() call). | 
|---|
| 176 | bool fEdgeEquationsValid = false; | 
|---|
| 177 |  | 
|---|
| 178 | // The requested edge distances must be positive so that they can be reused between inset | 
|---|
| 179 | // and outset calls. | 
|---|
| 180 | const OutsetRequest& getOutsetRequest(const skvx::Vec<4, float>& edgeDistances); | 
|---|
| 181 | const EdgeEquations& getEdgeEquations(); | 
|---|
| 182 |  | 
|---|
| 183 | // Outsets or insets 'vertices' by the given perpendicular 'signedEdgeDistances' (inset or | 
|---|
| 184 | // outset is determined implicitly by the sign of the distances). | 
|---|
| 185 | void adjustVertices(const skvx::Vec<4, float>& signedEdgeDistances, Vertices* vertices); | 
|---|
| 186 | // Like adjustVertices() but handles empty edges, collapsed quads, numerical issues, and | 
|---|
| 187 | // returns the number of effective vertices in the adjusted shape. | 
|---|
| 188 | int adjustDegenerateVertices(const skvx::Vec<4, float>& signedEdgeDistances, | 
|---|
| 189 | Vertices* vertices); | 
|---|
| 190 |  | 
|---|
| 191 | friend int ClipToW0(DrawQuad*, DrawQuad*); // To reuse Vertices struct | 
|---|
| 192 | }; | 
|---|
| 193 |  | 
|---|
| 194 | }; // namespace GrQuadUtils | 
|---|
| 195 |  | 
|---|
| 196 | #endif | 
|---|
| 197 |  | 
|---|