1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2019 Google LLC 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrQuadUtils.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "include/core/SkRect.h" 11cb93a386Sopenharmony_ci#include "include/private/GrTypesPriv.h" 12cb93a386Sopenharmony_ci#include "include/private/SkVx.h" 13cb93a386Sopenharmony_ci#include "src/core/SkPathPriv.h" 14cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrQuad.h" 15cb93a386Sopenharmony_ci 16cb93a386Sopenharmony_ciusing V4f = skvx::Vec<4, float>; 17cb93a386Sopenharmony_ciusing M4f = skvx::Vec<4, int32_t>; 18cb93a386Sopenharmony_ci 19cb93a386Sopenharmony_ci#define AI SK_ALWAYS_INLINE 20cb93a386Sopenharmony_ci 21cb93a386Sopenharmony_ci// General tolerance used for denominators, checking div-by-0 22cb93a386Sopenharmony_cistatic constexpr float kTolerance = 1e-9f; 23cb93a386Sopenharmony_ci// Increased slop when comparing signed distances / lengths 24cb93a386Sopenharmony_cistatic constexpr float kDistTolerance = 1e-2f; 25cb93a386Sopenharmony_cistatic constexpr float kDist2Tolerance = kDistTolerance * kDistTolerance; 26cb93a386Sopenharmony_cistatic constexpr float kInvDistTolerance = 1.f / kDistTolerance; 27cb93a386Sopenharmony_ci 28cb93a386Sopenharmony_ci// These rotate the points/edge values either clockwise or counterclockwise assuming tri strip 29cb93a386Sopenharmony_ci// order. 30cb93a386Sopenharmony_citemplate<typename T> 31cb93a386Sopenharmony_cistatic AI skvx::Vec<4, T> next_cw(const skvx::Vec<4, T>& v) { 32cb93a386Sopenharmony_ci return skvx::shuffle<2, 0, 3, 1>(v); 33cb93a386Sopenharmony_ci} 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_citemplate<typename T> 36cb93a386Sopenharmony_cistatic AI skvx::Vec<4, T> next_ccw(const skvx::Vec<4, T>& v) { 37cb93a386Sopenharmony_ci return skvx::shuffle<1, 3, 0, 2>(v); 38cb93a386Sopenharmony_ci} 39cb93a386Sopenharmony_ci 40cb93a386Sopenharmony_cistatic AI V4f next_diag(const V4f& v) { 41cb93a386Sopenharmony_ci // Same as next_ccw(next_ccw(v)), or next_cw(next_cw(v)), e.g. two rotations either direction. 42cb93a386Sopenharmony_ci return skvx::shuffle<3, 2, 1, 0>(v); 43cb93a386Sopenharmony_ci} 44cb93a386Sopenharmony_ci 45cb93a386Sopenharmony_ci// Replaces zero-length 'bad' edge vectors with the reversed opposite edge vector. 46cb93a386Sopenharmony_ci// e3 may be null if only 2D edges need to be corrected for. 47cb93a386Sopenharmony_cistatic AI void correct_bad_edges(const M4f& bad, V4f* e1, V4f* e2, V4f* e3) { 48cb93a386Sopenharmony_ci if (any(bad)) { 49cb93a386Sopenharmony_ci // Want opposite edges, L B T R -> R T B L but with flipped sign to preserve winding 50cb93a386Sopenharmony_ci *e1 = if_then_else(bad, -next_diag(*e1), *e1); 51cb93a386Sopenharmony_ci *e2 = if_then_else(bad, -next_diag(*e2), *e2); 52cb93a386Sopenharmony_ci if (e3) { 53cb93a386Sopenharmony_ci *e3 = if_then_else(bad, -next_diag(*e3), *e3); 54cb93a386Sopenharmony_ci } 55cb93a386Sopenharmony_ci } 56cb93a386Sopenharmony_ci} 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_ci// Replace 'bad' coordinates by rotating CCW to get the next point. c3 may be null for 2D points. 59cb93a386Sopenharmony_cistatic AI void correct_bad_coords(const M4f& bad, V4f* c1, V4f* c2, V4f* c3) { 60cb93a386Sopenharmony_ci if (any(bad)) { 61cb93a386Sopenharmony_ci *c1 = if_then_else(bad, next_ccw(*c1), *c1); 62cb93a386Sopenharmony_ci *c2 = if_then_else(bad, next_ccw(*c2), *c2); 63cb93a386Sopenharmony_ci if (c3) { 64cb93a386Sopenharmony_ci *c3 = if_then_else(bad, next_ccw(*c3), *c3); 65cb93a386Sopenharmony_ci } 66cb93a386Sopenharmony_ci } 67cb93a386Sopenharmony_ci} 68cb93a386Sopenharmony_ci 69cb93a386Sopenharmony_ci// Since the local quad may not be type kRect, this uses the opposites for each vertex when 70cb93a386Sopenharmony_ci// interpolating, and calculates new ws in addition to new xs, ys. 71cb93a386Sopenharmony_cistatic void interpolate_local(float alpha, int v0, int v1, int v2, int v3, 72cb93a386Sopenharmony_ci float lx[4], float ly[4], float lw[4]) { 73cb93a386Sopenharmony_ci SkASSERT(v0 >= 0 && v0 < 4); 74cb93a386Sopenharmony_ci SkASSERT(v1 >= 0 && v1 < 4); 75cb93a386Sopenharmony_ci SkASSERT(v2 >= 0 && v2 < 4); 76cb93a386Sopenharmony_ci SkASSERT(v3 >= 0 && v3 < 4); 77cb93a386Sopenharmony_ci 78cb93a386Sopenharmony_ci float beta = 1.f - alpha; 79cb93a386Sopenharmony_ci lx[v0] = alpha * lx[v0] + beta * lx[v2]; 80cb93a386Sopenharmony_ci ly[v0] = alpha * ly[v0] + beta * ly[v2]; 81cb93a386Sopenharmony_ci lw[v0] = alpha * lw[v0] + beta * lw[v2]; 82cb93a386Sopenharmony_ci 83cb93a386Sopenharmony_ci lx[v1] = alpha * lx[v1] + beta * lx[v3]; 84cb93a386Sopenharmony_ci ly[v1] = alpha * ly[v1] + beta * ly[v3]; 85cb93a386Sopenharmony_ci lw[v1] = alpha * lw[v1] + beta * lw[v3]; 86cb93a386Sopenharmony_ci} 87cb93a386Sopenharmony_ci 88cb93a386Sopenharmony_ci// Crops v0 to v1 based on the clipDevRect. v2 is opposite of v0, v3 is opposite of v1. 89cb93a386Sopenharmony_ci// It is written to not modify coordinates if there's no intersection along the edge. 90cb93a386Sopenharmony_ci// Ideally this would have been detected earlier and the entire draw is skipped. 91cb93a386Sopenharmony_cistatic bool crop_rect_edge(const SkRect& clipDevRect, int v0, int v1, int v2, int v3, 92cb93a386Sopenharmony_ci float x[4], float y[4], float lx[4], float ly[4], float lw[4]) { 93cb93a386Sopenharmony_ci SkASSERT(v0 >= 0 && v0 < 4); 94cb93a386Sopenharmony_ci SkASSERT(v1 >= 0 && v1 < 4); 95cb93a386Sopenharmony_ci SkASSERT(v2 >= 0 && v2 < 4); 96cb93a386Sopenharmony_ci SkASSERT(v3 >= 0 && v3 < 4); 97cb93a386Sopenharmony_ci 98cb93a386Sopenharmony_ci if (SkScalarNearlyEqual(x[v0], x[v1])) { 99cb93a386Sopenharmony_ci // A vertical edge 100cb93a386Sopenharmony_ci if (x[v0] < clipDevRect.fLeft && x[v2] >= clipDevRect.fLeft) { 101cb93a386Sopenharmony_ci // Overlapping with left edge of clipDevRect 102cb93a386Sopenharmony_ci if (lx) { 103cb93a386Sopenharmony_ci float alpha = (x[v2] - clipDevRect.fLeft) / (x[v2] - x[v0]); 104cb93a386Sopenharmony_ci interpolate_local(alpha, v0, v1, v2, v3, lx, ly, lw); 105cb93a386Sopenharmony_ci } 106cb93a386Sopenharmony_ci x[v0] = clipDevRect.fLeft; 107cb93a386Sopenharmony_ci x[v1] = clipDevRect.fLeft; 108cb93a386Sopenharmony_ci return true; 109cb93a386Sopenharmony_ci } else if (x[v0] > clipDevRect.fRight && x[v2] <= clipDevRect.fRight) { 110cb93a386Sopenharmony_ci // Overlapping with right edge of clipDevRect 111cb93a386Sopenharmony_ci if (lx) { 112cb93a386Sopenharmony_ci float alpha = (clipDevRect.fRight - x[v2]) / (x[v0] - x[v2]); 113cb93a386Sopenharmony_ci interpolate_local(alpha, v0, v1, v2, v3, lx, ly, lw); 114cb93a386Sopenharmony_ci } 115cb93a386Sopenharmony_ci x[v0] = clipDevRect.fRight; 116cb93a386Sopenharmony_ci x[v1] = clipDevRect.fRight; 117cb93a386Sopenharmony_ci return true; 118cb93a386Sopenharmony_ci } 119cb93a386Sopenharmony_ci } else { 120cb93a386Sopenharmony_ci // A horizontal edge 121cb93a386Sopenharmony_ci SkASSERT(SkScalarNearlyEqual(y[v0], y[v1])); 122cb93a386Sopenharmony_ci if (y[v0] < clipDevRect.fTop && y[v2] >= clipDevRect.fTop) { 123cb93a386Sopenharmony_ci // Overlapping with top edge of clipDevRect 124cb93a386Sopenharmony_ci if (lx) { 125cb93a386Sopenharmony_ci float alpha = (y[v2] - clipDevRect.fTop) / (y[v2] - y[v0]); 126cb93a386Sopenharmony_ci interpolate_local(alpha, v0, v1, v2, v3, lx, ly, lw); 127cb93a386Sopenharmony_ci } 128cb93a386Sopenharmony_ci y[v0] = clipDevRect.fTop; 129cb93a386Sopenharmony_ci y[v1] = clipDevRect.fTop; 130cb93a386Sopenharmony_ci return true; 131cb93a386Sopenharmony_ci } else if (y[v0] > clipDevRect.fBottom && y[v2] <= clipDevRect.fBottom) { 132cb93a386Sopenharmony_ci // Overlapping with bottom edge of clipDevRect 133cb93a386Sopenharmony_ci if (lx) { 134cb93a386Sopenharmony_ci float alpha = (clipDevRect.fBottom - y[v2]) / (y[v0] - y[v2]); 135cb93a386Sopenharmony_ci interpolate_local(alpha, v0, v1, v2, v3, lx, ly, lw); 136cb93a386Sopenharmony_ci } 137cb93a386Sopenharmony_ci y[v0] = clipDevRect.fBottom; 138cb93a386Sopenharmony_ci y[v1] = clipDevRect.fBottom; 139cb93a386Sopenharmony_ci return true; 140cb93a386Sopenharmony_ci } 141cb93a386Sopenharmony_ci } 142cb93a386Sopenharmony_ci 143cb93a386Sopenharmony_ci // No overlap so don't crop it 144cb93a386Sopenharmony_ci return false; 145cb93a386Sopenharmony_ci} 146cb93a386Sopenharmony_ci 147cb93a386Sopenharmony_ci// Updates x and y to intersect with clipDevRect. lx, ly, and lw are updated appropriately and may 148cb93a386Sopenharmony_ci// be null to skip calculations. Returns bit mask of edges that were clipped. 149cb93a386Sopenharmony_cistatic GrQuadAAFlags crop_rect(const SkRect& clipDevRect, float x[4], float y[4], 150cb93a386Sopenharmony_ci float lx[4], float ly[4], float lw[4]) { 151cb93a386Sopenharmony_ci GrQuadAAFlags clipEdgeFlags = GrQuadAAFlags::kNone; 152cb93a386Sopenharmony_ci 153cb93a386Sopenharmony_ci // The quad's left edge may not align with the SkRect notion of left due to 90 degree rotations 154cb93a386Sopenharmony_ci // or mirrors. So, this processes the logical edges of the quad and clamps it to the 4 sides of 155cb93a386Sopenharmony_ci // clipDevRect. 156cb93a386Sopenharmony_ci 157cb93a386Sopenharmony_ci // Quad's left is v0 to v1 (op. v2 and v3) 158cb93a386Sopenharmony_ci if (crop_rect_edge(clipDevRect, 0, 1, 2, 3, x, y, lx, ly, lw)) { 159cb93a386Sopenharmony_ci clipEdgeFlags |= GrQuadAAFlags::kLeft; 160cb93a386Sopenharmony_ci } 161cb93a386Sopenharmony_ci // Quad's top edge is v0 to v2 (op. v1 and v3) 162cb93a386Sopenharmony_ci if (crop_rect_edge(clipDevRect, 0, 2, 1, 3, x, y, lx, ly, lw)) { 163cb93a386Sopenharmony_ci clipEdgeFlags |= GrQuadAAFlags::kTop; 164cb93a386Sopenharmony_ci } 165cb93a386Sopenharmony_ci // Quad's right edge is v2 to v3 (op. v0 and v1) 166cb93a386Sopenharmony_ci if (crop_rect_edge(clipDevRect, 2, 3, 0, 1, x, y, lx, ly, lw)) { 167cb93a386Sopenharmony_ci clipEdgeFlags |= GrQuadAAFlags::kRight; 168cb93a386Sopenharmony_ci } 169cb93a386Sopenharmony_ci // Quad's bottom edge is v1 to v3 (op. v0 and v2) 170cb93a386Sopenharmony_ci if (crop_rect_edge(clipDevRect, 1, 3, 0, 2, x, y, lx, ly, lw)) { 171cb93a386Sopenharmony_ci clipEdgeFlags |= GrQuadAAFlags::kBottom; 172cb93a386Sopenharmony_ci } 173cb93a386Sopenharmony_ci 174cb93a386Sopenharmony_ci return clipEdgeFlags; 175cb93a386Sopenharmony_ci} 176cb93a386Sopenharmony_ci 177cb93a386Sopenharmony_ci// Similar to crop_rect, but assumes that both the device coordinates and optional local coordinates 178cb93a386Sopenharmony_ci// geometrically match the TL, BL, TR, BR vertex ordering, i.e. axis-aligned but not flipped, etc. 179cb93a386Sopenharmony_cistatic GrQuadAAFlags crop_simple_rect(const SkRect& clipDevRect, float x[4], float y[4], 180cb93a386Sopenharmony_ci float lx[4], float ly[4]) { 181cb93a386Sopenharmony_ci GrQuadAAFlags clipEdgeFlags = GrQuadAAFlags::kNone; 182cb93a386Sopenharmony_ci 183cb93a386Sopenharmony_ci // Update local coordinates proportionately to how much the device rect edge was clipped 184cb93a386Sopenharmony_ci const SkScalar dx = lx ? (lx[2] - lx[0]) / (x[2] - x[0]) : 0.f; 185cb93a386Sopenharmony_ci const SkScalar dy = ly ? (ly[1] - ly[0]) / (y[1] - y[0]) : 0.f; 186cb93a386Sopenharmony_ci if (clipDevRect.fLeft > x[0]) { 187cb93a386Sopenharmony_ci if (lx) { 188cb93a386Sopenharmony_ci lx[0] += (clipDevRect.fLeft - x[0]) * dx; 189cb93a386Sopenharmony_ci lx[1] = lx[0]; 190cb93a386Sopenharmony_ci } 191cb93a386Sopenharmony_ci x[0] = clipDevRect.fLeft; 192cb93a386Sopenharmony_ci x[1] = clipDevRect.fLeft; 193cb93a386Sopenharmony_ci clipEdgeFlags |= GrQuadAAFlags::kLeft; 194cb93a386Sopenharmony_ci } 195cb93a386Sopenharmony_ci if (clipDevRect.fTop > y[0]) { 196cb93a386Sopenharmony_ci if (ly) { 197cb93a386Sopenharmony_ci ly[0] += (clipDevRect.fTop - y[0]) * dy; 198cb93a386Sopenharmony_ci ly[2] = ly[0]; 199cb93a386Sopenharmony_ci } 200cb93a386Sopenharmony_ci y[0] = clipDevRect.fTop; 201cb93a386Sopenharmony_ci y[2] = clipDevRect.fTop; 202cb93a386Sopenharmony_ci clipEdgeFlags |= GrQuadAAFlags::kTop; 203cb93a386Sopenharmony_ci } 204cb93a386Sopenharmony_ci if (clipDevRect.fRight < x[2]) { 205cb93a386Sopenharmony_ci if (lx) { 206cb93a386Sopenharmony_ci lx[2] -= (x[2] - clipDevRect.fRight) * dx; 207cb93a386Sopenharmony_ci lx[3] = lx[2]; 208cb93a386Sopenharmony_ci } 209cb93a386Sopenharmony_ci x[2] = clipDevRect.fRight; 210cb93a386Sopenharmony_ci x[3] = clipDevRect.fRight; 211cb93a386Sopenharmony_ci clipEdgeFlags |= GrQuadAAFlags::kRight; 212cb93a386Sopenharmony_ci } 213cb93a386Sopenharmony_ci if (clipDevRect.fBottom < y[1]) { 214cb93a386Sopenharmony_ci if (ly) { 215cb93a386Sopenharmony_ci ly[1] -= (y[1] - clipDevRect.fBottom) * dy; 216cb93a386Sopenharmony_ci ly[3] = ly[1]; 217cb93a386Sopenharmony_ci } 218cb93a386Sopenharmony_ci y[1] = clipDevRect.fBottom; 219cb93a386Sopenharmony_ci y[3] = clipDevRect.fBottom; 220cb93a386Sopenharmony_ci clipEdgeFlags |= GrQuadAAFlags::kBottom; 221cb93a386Sopenharmony_ci } 222cb93a386Sopenharmony_ci 223cb93a386Sopenharmony_ci return clipEdgeFlags; 224cb93a386Sopenharmony_ci} 225cb93a386Sopenharmony_ci// Consistent with GrQuad::asRect()'s return value but requires fewer operations since we don't need 226cb93a386Sopenharmony_ci// to calculate the bounds of the quad. 227cb93a386Sopenharmony_cistatic bool is_simple_rect(const GrQuad& quad) { 228cb93a386Sopenharmony_ci if (quad.quadType() != GrQuad::Type::kAxisAligned) { 229cb93a386Sopenharmony_ci return false; 230cb93a386Sopenharmony_ci } 231cb93a386Sopenharmony_ci // v0 at the geometric top-left is unique, so we only need to compare x[0] < x[2] for left 232cb93a386Sopenharmony_ci // and y[0] < y[1] for top, but add a little padding to protect against numerical precision 233cb93a386Sopenharmony_ci // on R90 and R270 transforms tricking this check. 234cb93a386Sopenharmony_ci return ((quad.x(0) + SK_ScalarNearlyZero) < quad.x(2)) && 235cb93a386Sopenharmony_ci ((quad.y(0) + SK_ScalarNearlyZero) < quad.y(1)); 236cb93a386Sopenharmony_ci} 237cb93a386Sopenharmony_ci 238cb93a386Sopenharmony_ci// Calculates barycentric coordinates for each point in (testX, testY) in the triangle formed by 239cb93a386Sopenharmony_ci// (x0,y0) - (x1,y1) - (x2, y2) and stores them in u, v, w. 240cb93a386Sopenharmony_cistatic bool barycentric_coords(float x0, float y0, float x1, float y1, float x2, float y2, 241cb93a386Sopenharmony_ci const V4f& testX, const V4f& testY, 242cb93a386Sopenharmony_ci V4f* u, V4f* v, V4f* w) { 243cb93a386Sopenharmony_ci // The 32-bit calculations can have catastrophic cancellation if the device-space coordinates 244cb93a386Sopenharmony_ci // are really big, and this code needs to handle that because we evaluate barycentric coords 245cb93a386Sopenharmony_ci // pre-cropping to the render target bounds. This preserves some precision by shrinking the 246cb93a386Sopenharmony_ci // coordinate space if the bounds are large. 247cb93a386Sopenharmony_ci static constexpr float kCoordLimit = 1e7f; // Big but somewhat arbitrary, fixes crbug:10141204 248cb93a386Sopenharmony_ci float scaleX = std::max(std::max(x0, x1), x2) - std::min(std::min(x0, x1), x2); 249cb93a386Sopenharmony_ci float scaleY = std::max(std::max(y0, y1), y2) - std::min(std::min(y0, y1), y2); 250cb93a386Sopenharmony_ci if (scaleX > kCoordLimit) { 251cb93a386Sopenharmony_ci scaleX = kCoordLimit / scaleX; 252cb93a386Sopenharmony_ci x0 *= scaleX; 253cb93a386Sopenharmony_ci x1 *= scaleX; 254cb93a386Sopenharmony_ci x2 *= scaleX; 255cb93a386Sopenharmony_ci } else { 256cb93a386Sopenharmony_ci // Don't scale anything 257cb93a386Sopenharmony_ci scaleX = 1.f; 258cb93a386Sopenharmony_ci } 259cb93a386Sopenharmony_ci if (scaleY > kCoordLimit) { 260cb93a386Sopenharmony_ci scaleY = kCoordLimit / scaleY; 261cb93a386Sopenharmony_ci y0 *= scaleY; 262cb93a386Sopenharmony_ci y1 *= scaleY; 263cb93a386Sopenharmony_ci y2 *= scaleY; 264cb93a386Sopenharmony_ci } else { 265cb93a386Sopenharmony_ci scaleY = 1.f; 266cb93a386Sopenharmony_ci } 267cb93a386Sopenharmony_ci 268cb93a386Sopenharmony_ci // Modeled after SkPathOpsQuad::pointInTriangle() but uses float instead of double, is 269cb93a386Sopenharmony_ci // vectorized and outputs normalized barycentric coordinates instead of inside/outside test 270cb93a386Sopenharmony_ci float v0x = x2 - x0; 271cb93a386Sopenharmony_ci float v0y = y2 - y0; 272cb93a386Sopenharmony_ci float v1x = x1 - x0; 273cb93a386Sopenharmony_ci float v1y = y1 - y0; 274cb93a386Sopenharmony_ci 275cb93a386Sopenharmony_ci float dot00 = v0x * v0x + v0y * v0y; 276cb93a386Sopenharmony_ci float dot01 = v0x * v1x + v0y * v1y; 277cb93a386Sopenharmony_ci float dot11 = v1x * v1x + v1y * v1y; 278cb93a386Sopenharmony_ci 279cb93a386Sopenharmony_ci // Not yet 1/d, first check d != 0 with a healthy tolerance (worst case is we end up not 280cb93a386Sopenharmony_ci // cropping something we could have, which is better than cropping something we shouldn't have). 281cb93a386Sopenharmony_ci // The tolerance is partly so large because these comparisons operate in device px^4 units, 282cb93a386Sopenharmony_ci // with plenty of subtractions thrown in. The SkPathOpsQuad code's use of doubles helped, and 283cb93a386Sopenharmony_ci // because it only needed to return "inside triangle", it could compare against [0, denom] and 284cb93a386Sopenharmony_ci // skip the normalization entirely. 285cb93a386Sopenharmony_ci float invDenom = dot00 * dot11 - dot01 * dot01; 286cb93a386Sopenharmony_ci static constexpr SkScalar kEmptyTriTolerance = SK_Scalar1 / (1 << 5); 287cb93a386Sopenharmony_ci if (SkScalarNearlyZero(invDenom, kEmptyTriTolerance)) { 288cb93a386Sopenharmony_ci // The triangle was degenerate/empty, which can cause the following UVW calculations to 289cb93a386Sopenharmony_ci // return (0,0,1) for every test point. This in turn makes the cropping code think that the 290cb93a386Sopenharmony_ci // empty triangle contains the crop rect and we turn the draw into a fullscreen clear, which 291cb93a386Sopenharmony_ci // is definitely the utter opposite of what we'd expect for an empty shape. 292cb93a386Sopenharmony_ci return false; 293cb93a386Sopenharmony_ci } else { 294cb93a386Sopenharmony_ci // Safe to divide 295cb93a386Sopenharmony_ci invDenom = sk_ieee_float_divide(1.f, invDenom); 296cb93a386Sopenharmony_ci } 297cb93a386Sopenharmony_ci 298cb93a386Sopenharmony_ci V4f v2x = (scaleX * testX) - x0; 299cb93a386Sopenharmony_ci V4f v2y = (scaleY * testY) - y0; 300cb93a386Sopenharmony_ci 301cb93a386Sopenharmony_ci V4f dot02 = v0x * v2x + v0y * v2y; 302cb93a386Sopenharmony_ci V4f dot12 = v1x * v2x + v1y * v2y; 303cb93a386Sopenharmony_ci 304cb93a386Sopenharmony_ci // These are relative to the vertices, so there's no need to undo the scale factor 305cb93a386Sopenharmony_ci *u = (dot11 * dot02 - dot01 * dot12) * invDenom; 306cb93a386Sopenharmony_ci *v = (dot00 * dot12 - dot01 * dot02) * invDenom; 307cb93a386Sopenharmony_ci *w = 1.f - *u - *v; 308cb93a386Sopenharmony_ci 309cb93a386Sopenharmony_ci return true; 310cb93a386Sopenharmony_ci} 311cb93a386Sopenharmony_ci 312cb93a386Sopenharmony_cistatic M4f inside_triangle(const V4f& u, const V4f& v, const V4f& w) { 313cb93a386Sopenharmony_ci return ((u >= 0.f) & (u <= 1.f)) & ((v >= 0.f) & (v <= 1.f)) & ((w >= 0.f) & (w <= 1.f)); 314cb93a386Sopenharmony_ci} 315cb93a386Sopenharmony_ci 316cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////////////// 317cb93a386Sopenharmony_ci 318cb93a386Sopenharmony_ciSkRect GrQuad::projectedBounds() const { 319cb93a386Sopenharmony_ci V4f xs = this->x4f(); 320cb93a386Sopenharmony_ci V4f ys = this->y4f(); 321cb93a386Sopenharmony_ci V4f ws = this->w4f(); 322cb93a386Sopenharmony_ci M4f clipW = ws < SkPathPriv::kW0PlaneDistance; 323cb93a386Sopenharmony_ci if (any(clipW)) { 324cb93a386Sopenharmony_ci V4f x2d = xs / ws; 325cb93a386Sopenharmony_ci V4f y2d = ys / ws; 326cb93a386Sopenharmony_ci // Bounds of just the projected points in front of w = epsilon 327cb93a386Sopenharmony_ci SkRect frontBounds = { 328cb93a386Sopenharmony_ci min(if_then_else(clipW, V4f(SK_ScalarInfinity), x2d)), 329cb93a386Sopenharmony_ci min(if_then_else(clipW, V4f(SK_ScalarInfinity), y2d)), 330cb93a386Sopenharmony_ci max(if_then_else(clipW, V4f(SK_ScalarNegativeInfinity), x2d)), 331cb93a386Sopenharmony_ci max(if_then_else(clipW, V4f(SK_ScalarNegativeInfinity), y2d)) 332cb93a386Sopenharmony_ci }; 333cb93a386Sopenharmony_ci // Calculate clipped coordinates by following CCW edges, only keeping points where the w 334cb93a386Sopenharmony_ci // actually changes sign between the vertices. 335cb93a386Sopenharmony_ci V4f t = (SkPathPriv::kW0PlaneDistance - ws) / (next_ccw(ws) - ws); 336cb93a386Sopenharmony_ci x2d = (t * next_ccw(xs) + (1.f - t) * xs) / SkPathPriv::kW0PlaneDistance; 337cb93a386Sopenharmony_ci y2d = (t * next_ccw(ys) + (1.f - t) * ys) / SkPathPriv::kW0PlaneDistance; 338cb93a386Sopenharmony_ci // True if (w < e) xor (ccw(w) < e), i.e. crosses the w = epsilon plane 339cb93a386Sopenharmony_ci clipW = clipW ^ (next_ccw(ws) < SkPathPriv::kW0PlaneDistance); 340cb93a386Sopenharmony_ci return { 341cb93a386Sopenharmony_ci min(if_then_else(clipW, x2d, V4f(frontBounds.fLeft))), 342cb93a386Sopenharmony_ci min(if_then_else(clipW, y2d, V4f(frontBounds.fTop))), 343cb93a386Sopenharmony_ci max(if_then_else(clipW, x2d, V4f(frontBounds.fRight))), 344cb93a386Sopenharmony_ci max(if_then_else(clipW, y2d, V4f(frontBounds.fBottom))) 345cb93a386Sopenharmony_ci }; 346cb93a386Sopenharmony_ci } else { 347cb93a386Sopenharmony_ci // Nothing is behind the viewer, so the projection is straight forward and valid 348cb93a386Sopenharmony_ci ws = 1.f / ws; 349cb93a386Sopenharmony_ci V4f x2d = xs * ws; 350cb93a386Sopenharmony_ci V4f y2d = ys * ws; 351cb93a386Sopenharmony_ci return {min(x2d), min(y2d), max(x2d), max(y2d)}; 352cb93a386Sopenharmony_ci } 353cb93a386Sopenharmony_ci} 354cb93a386Sopenharmony_ci 355cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////////////// 356cb93a386Sopenharmony_ci 357cb93a386Sopenharmony_cinamespace GrQuadUtils { 358cb93a386Sopenharmony_ci 359cb93a386Sopenharmony_civoid ResolveAAType(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags, const GrQuad& quad, 360cb93a386Sopenharmony_ci GrAAType* outAAType, GrQuadAAFlags* outEdgeFlags) { 361cb93a386Sopenharmony_ci // Most cases will keep the requested types unchanged 362cb93a386Sopenharmony_ci *outAAType = requestedAAType; 363cb93a386Sopenharmony_ci *outEdgeFlags = requestedEdgeFlags; 364cb93a386Sopenharmony_ci 365cb93a386Sopenharmony_ci switch (requestedAAType) { 366cb93a386Sopenharmony_ci // When aa type is coverage, disable AA if the edge configuration doesn't actually need it 367cb93a386Sopenharmony_ci case GrAAType::kCoverage: 368cb93a386Sopenharmony_ci if (requestedEdgeFlags == GrQuadAAFlags::kNone) { 369cb93a386Sopenharmony_ci // Turn off anti-aliasing 370cb93a386Sopenharmony_ci *outAAType = GrAAType::kNone; 371cb93a386Sopenharmony_ci } else { 372cb93a386Sopenharmony_ci // For coverage AA, if the quad is a rect and it lines up with pixel boundaries 373cb93a386Sopenharmony_ci // then overall aa and per-edge aa can be completely disabled 374cb93a386Sopenharmony_ci if (quad.quadType() == GrQuad::Type::kAxisAligned && !quad.aaHasEffectOnRect()) { 375cb93a386Sopenharmony_ci *outAAType = GrAAType::kNone; 376cb93a386Sopenharmony_ci *outEdgeFlags = GrQuadAAFlags::kNone; 377cb93a386Sopenharmony_ci } 378cb93a386Sopenharmony_ci } 379cb93a386Sopenharmony_ci break; 380cb93a386Sopenharmony_ci // For no or msaa anti aliasing, override the edge flags since edge flags only make sense 381cb93a386Sopenharmony_ci // when coverage aa is being used. 382cb93a386Sopenharmony_ci case GrAAType::kNone: 383cb93a386Sopenharmony_ci *outEdgeFlags = GrQuadAAFlags::kNone; 384cb93a386Sopenharmony_ci break; 385cb93a386Sopenharmony_ci case GrAAType::kMSAA: 386cb93a386Sopenharmony_ci *outEdgeFlags = GrQuadAAFlags::kAll; 387cb93a386Sopenharmony_ci break; 388cb93a386Sopenharmony_ci } 389cb93a386Sopenharmony_ci} 390cb93a386Sopenharmony_ci 391cb93a386Sopenharmony_ciint ClipToW0(DrawQuad* quad, DrawQuad* extraVertices) { 392cb93a386Sopenharmony_ci using Vertices = TessellationHelper::Vertices; 393cb93a386Sopenharmony_ci 394cb93a386Sopenharmony_ci SkASSERT(quad && extraVertices); 395cb93a386Sopenharmony_ci 396cb93a386Sopenharmony_ci if (quad->fDevice.quadType() < GrQuad::Type::kPerspective) { 397cb93a386Sopenharmony_ci // W implicitly 1s for each vertex, so nothing to do but draw unmodified 'quad' 398cb93a386Sopenharmony_ci return 1; 399cb93a386Sopenharmony_ci } 400cb93a386Sopenharmony_ci 401cb93a386Sopenharmony_ci M4f validW = quad->fDevice.w4f() >= SkPathPriv::kW0PlaneDistance; 402cb93a386Sopenharmony_ci if (all(validW)) { 403cb93a386Sopenharmony_ci // Nothing to clip, can proceed normally drawing just 'quad' 404cb93a386Sopenharmony_ci return 1; 405cb93a386Sopenharmony_ci } else if (!any(validW)) { 406cb93a386Sopenharmony_ci // Everything is clipped, so draw nothing 407cb93a386Sopenharmony_ci return 0; 408cb93a386Sopenharmony_ci } 409cb93a386Sopenharmony_ci 410cb93a386Sopenharmony_ci // The clipped local coordinates will most likely not remain rectilinear 411cb93a386Sopenharmony_ci GrQuad::Type localType = quad->fLocal.quadType(); 412cb93a386Sopenharmony_ci if (localType < GrQuad::Type::kGeneral) { 413cb93a386Sopenharmony_ci localType = GrQuad::Type::kGeneral; 414cb93a386Sopenharmony_ci } 415cb93a386Sopenharmony_ci 416cb93a386Sopenharmony_ci // If we got here, there are 1, 2, or 3 points behind the w = 0 plane. If 2 or 3 points are 417cb93a386Sopenharmony_ci // clipped we can define a new quad that covers the clipped shape directly. If there's 1 clipped 418cb93a386Sopenharmony_ci // out, the new geometry is a pentagon. 419cb93a386Sopenharmony_ci Vertices v; 420cb93a386Sopenharmony_ci v.reset(quad->fDevice, &quad->fLocal); 421cb93a386Sopenharmony_ci 422cb93a386Sopenharmony_ci int clipCount = (validW[0] ? 0 : 1) + (validW[1] ? 0 : 1) + 423cb93a386Sopenharmony_ci (validW[2] ? 0 : 1) + (validW[3] ? 0 : 1); 424cb93a386Sopenharmony_ci SkASSERT(clipCount >= 1 && clipCount <= 3); 425cb93a386Sopenharmony_ci 426cb93a386Sopenharmony_ci // FIXME de-duplicate from the projectedBounds() calculations. 427cb93a386Sopenharmony_ci V4f t = (SkPathPriv::kW0PlaneDistance - v.fW) / (next_ccw(v.fW) - v.fW); 428cb93a386Sopenharmony_ci 429cb93a386Sopenharmony_ci Vertices clip; 430cb93a386Sopenharmony_ci clip.fX = (t * next_ccw(v.fX) + (1.f - t) * v.fX); 431cb93a386Sopenharmony_ci clip.fY = (t * next_ccw(v.fY) + (1.f - t) * v.fY); 432cb93a386Sopenharmony_ci clip.fW = SkPathPriv::kW0PlaneDistance; 433cb93a386Sopenharmony_ci 434cb93a386Sopenharmony_ci clip.fU = (t * next_ccw(v.fU) + (1.f - t) * v.fU); 435cb93a386Sopenharmony_ci clip.fV = (t * next_ccw(v.fV) + (1.f - t) * v.fV); 436cb93a386Sopenharmony_ci clip.fR = (t * next_ccw(v.fR) + (1.f - t) * v.fR); 437cb93a386Sopenharmony_ci 438cb93a386Sopenharmony_ci M4f ccwValid = next_ccw(v.fW) >= SkPathPriv::kW0PlaneDistance; 439cb93a386Sopenharmony_ci M4f cwValid = next_cw(v.fW) >= SkPathPriv::kW0PlaneDistance; 440cb93a386Sopenharmony_ci 441cb93a386Sopenharmony_ci if (clipCount != 1) { 442cb93a386Sopenharmony_ci // Simplest case, replace behind-w0 points with their clipped points by following CCW edge 443cb93a386Sopenharmony_ci // or CW edge, depending on if the edge crosses from neg. to pos. w or pos. to neg. 444cb93a386Sopenharmony_ci SkASSERT(clipCount == 2 || clipCount == 3); 445cb93a386Sopenharmony_ci 446cb93a386Sopenharmony_ci // NOTE: when 3 vertices are clipped, this results in a degenerate quad where one vertex 447cb93a386Sopenharmony_ci // is replicated. This is preferably to inserting a 3rd vertex on the w = 0 intersection 448cb93a386Sopenharmony_ci // line because two parallel edges make inset/outset math unstable for large quads. 449cb93a386Sopenharmony_ci v.fX = if_then_else(validW, v.fX, 450cb93a386Sopenharmony_ci if_then_else((!ccwValid) & (!cwValid), next_ccw(clip.fX), 451cb93a386Sopenharmony_ci if_then_else(ccwValid, clip.fX, /* cwValid */ next_cw(clip.fX)))); 452cb93a386Sopenharmony_ci v.fY = if_then_else(validW, v.fY, 453cb93a386Sopenharmony_ci if_then_else((!ccwValid) & (!cwValid), next_ccw(clip.fY), 454cb93a386Sopenharmony_ci if_then_else(ccwValid, clip.fY, /* cwValid */ next_cw(clip.fY)))); 455cb93a386Sopenharmony_ci v.fW = if_then_else(validW, v.fW, clip.fW); 456cb93a386Sopenharmony_ci 457cb93a386Sopenharmony_ci v.fU = if_then_else(validW, v.fU, 458cb93a386Sopenharmony_ci if_then_else((!ccwValid) & (!cwValid), next_ccw(clip.fU), 459cb93a386Sopenharmony_ci if_then_else(ccwValid, clip.fU, /* cwValid */ next_cw(clip.fU)))); 460cb93a386Sopenharmony_ci v.fV = if_then_else(validW, v.fV, 461cb93a386Sopenharmony_ci if_then_else((!ccwValid) & (!cwValid), next_ccw(clip.fV), 462cb93a386Sopenharmony_ci if_then_else(ccwValid, clip.fV, /* cwValid */ next_cw(clip.fV)))); 463cb93a386Sopenharmony_ci v.fR = if_then_else(validW, v.fR, 464cb93a386Sopenharmony_ci if_then_else((!ccwValid) & (!cwValid), next_ccw(clip.fR), 465cb93a386Sopenharmony_ci if_then_else(ccwValid, clip.fR, /* cwValid */ next_cw(clip.fR)))); 466cb93a386Sopenharmony_ci 467cb93a386Sopenharmony_ci // For 2 or 3 clipped vertices, the resulting shape is a quad or a triangle, so it can be 468cb93a386Sopenharmony_ci // entirely represented in 'quad'. 469cb93a386Sopenharmony_ci v.asGrQuads(&quad->fDevice, GrQuad::Type::kPerspective, 470cb93a386Sopenharmony_ci &quad->fLocal, localType); 471cb93a386Sopenharmony_ci return 1; 472cb93a386Sopenharmony_ci } else { 473cb93a386Sopenharmony_ci // The clipped geometry is a pentagon, so it will be represented as two quads connected by 474cb93a386Sopenharmony_ci // a new non-AA edge. Use the midpoint along one of the unclipped edges as a split vertex. 475cb93a386Sopenharmony_ci Vertices mid; 476cb93a386Sopenharmony_ci mid.fX = 0.5f * (v.fX + next_ccw(v.fX)); 477cb93a386Sopenharmony_ci mid.fY = 0.5f * (v.fY + next_ccw(v.fY)); 478cb93a386Sopenharmony_ci mid.fW = 0.5f * (v.fW + next_ccw(v.fW)); 479cb93a386Sopenharmony_ci 480cb93a386Sopenharmony_ci mid.fU = 0.5f * (v.fU + next_ccw(v.fU)); 481cb93a386Sopenharmony_ci mid.fV = 0.5f * (v.fV + next_ccw(v.fV)); 482cb93a386Sopenharmony_ci mid.fR = 0.5f * (v.fR + next_ccw(v.fR)); 483cb93a386Sopenharmony_ci 484cb93a386Sopenharmony_ci // Make a quad formed by the 2 clipped points, the inserted mid point, and the good vertex 485cb93a386Sopenharmony_ci // that is CCW rotated from the clipped vertex. 486cb93a386Sopenharmony_ci Vertices v2; 487cb93a386Sopenharmony_ci v2.fUVRCount = v.fUVRCount; 488cb93a386Sopenharmony_ci v2.fX = if_then_else((!validW) | (!ccwValid), clip.fX, 489cb93a386Sopenharmony_ci if_then_else(cwValid, next_cw(mid.fX), v.fX)); 490cb93a386Sopenharmony_ci v2.fY = if_then_else((!validW) | (!ccwValid), clip.fY, 491cb93a386Sopenharmony_ci if_then_else(cwValid, next_cw(mid.fY), v.fY)); 492cb93a386Sopenharmony_ci v2.fW = if_then_else((!validW) | (!ccwValid), clip.fW, 493cb93a386Sopenharmony_ci if_then_else(cwValid, next_cw(mid.fW), v.fW)); 494cb93a386Sopenharmony_ci 495cb93a386Sopenharmony_ci v2.fU = if_then_else((!validW) | (!ccwValid), clip.fU, 496cb93a386Sopenharmony_ci if_then_else(cwValid, next_cw(mid.fU), v.fU)); 497cb93a386Sopenharmony_ci v2.fV = if_then_else((!validW) | (!ccwValid), clip.fV, 498cb93a386Sopenharmony_ci if_then_else(cwValid, next_cw(mid.fV), v.fV)); 499cb93a386Sopenharmony_ci v2.fR = if_then_else((!validW) | (!ccwValid), clip.fR, 500cb93a386Sopenharmony_ci if_then_else(cwValid, next_cw(mid.fR), v.fR)); 501cb93a386Sopenharmony_ci // The non-AA edge for this quad is the opposite of the clipped vertex's edge 502cb93a386Sopenharmony_ci GrQuadAAFlags v2EdgeFlag = (!validW[0] ? GrQuadAAFlags::kRight : // left clipped -> right 503cb93a386Sopenharmony_ci (!validW[1] ? GrQuadAAFlags::kTop : // bottom clipped -> top 504cb93a386Sopenharmony_ci (!validW[2] ? GrQuadAAFlags::kBottom : // top clipped -> bottom 505cb93a386Sopenharmony_ci GrQuadAAFlags::kLeft))); // right clipped -> left 506cb93a386Sopenharmony_ci extraVertices->fEdgeFlags = quad->fEdgeFlags & ~v2EdgeFlag; 507cb93a386Sopenharmony_ci 508cb93a386Sopenharmony_ci // Make a quad formed by the remaining two good vertices, one clipped point, and the 509cb93a386Sopenharmony_ci // inserted mid point. 510cb93a386Sopenharmony_ci v.fX = if_then_else(!validW, next_cw(clip.fX), 511cb93a386Sopenharmony_ci if_then_else(!cwValid, mid.fX, v.fX)); 512cb93a386Sopenharmony_ci v.fY = if_then_else(!validW, next_cw(clip.fY), 513cb93a386Sopenharmony_ci if_then_else(!cwValid, mid.fY, v.fY)); 514cb93a386Sopenharmony_ci v.fW = if_then_else(!validW, clip.fW, 515cb93a386Sopenharmony_ci if_then_else(!cwValid, mid.fW, v.fW)); 516cb93a386Sopenharmony_ci 517cb93a386Sopenharmony_ci v.fU = if_then_else(!validW, next_cw(clip.fU), 518cb93a386Sopenharmony_ci if_then_else(!cwValid, mid.fU, v.fU)); 519cb93a386Sopenharmony_ci v.fV = if_then_else(!validW, next_cw(clip.fV), 520cb93a386Sopenharmony_ci if_then_else(!cwValid, mid.fV, v.fV)); 521cb93a386Sopenharmony_ci v.fR = if_then_else(!validW, next_cw(clip.fR), 522cb93a386Sopenharmony_ci if_then_else(!cwValid, mid.fR, v.fR)); 523cb93a386Sopenharmony_ci // The non-AA edge for this quad is the clipped vertex's edge 524cb93a386Sopenharmony_ci GrQuadAAFlags v1EdgeFlag = (!validW[0] ? GrQuadAAFlags::kLeft : 525cb93a386Sopenharmony_ci (!validW[1] ? GrQuadAAFlags::kBottom : 526cb93a386Sopenharmony_ci (!validW[2] ? GrQuadAAFlags::kTop : 527cb93a386Sopenharmony_ci GrQuadAAFlags::kRight))); 528cb93a386Sopenharmony_ci 529cb93a386Sopenharmony_ci v.asGrQuads(&quad->fDevice, GrQuad::Type::kPerspective, 530cb93a386Sopenharmony_ci &quad->fLocal, localType); 531cb93a386Sopenharmony_ci quad->fEdgeFlags &= ~v1EdgeFlag; 532cb93a386Sopenharmony_ci 533cb93a386Sopenharmony_ci v2.asGrQuads(&extraVertices->fDevice, GrQuad::Type::kPerspective, 534cb93a386Sopenharmony_ci &extraVertices->fLocal, localType); 535cb93a386Sopenharmony_ci // Caller must draw both 'quad' and 'extraVertices' to cover the clipped geometry 536cb93a386Sopenharmony_ci return 2; 537cb93a386Sopenharmony_ci } 538cb93a386Sopenharmony_ci} 539cb93a386Sopenharmony_ci 540cb93a386Sopenharmony_cibool CropToRect(const SkRect& cropRect, GrAA cropAA, DrawQuad* quad, bool computeLocal) { 541cb93a386Sopenharmony_ci SkASSERT(quad->fDevice.isFinite()); 542cb93a386Sopenharmony_ci 543cb93a386Sopenharmony_ci if (quad->fDevice.quadType() == GrQuad::Type::kAxisAligned) { 544cb93a386Sopenharmony_ci // crop_rect and crop_rect_simple keep the rectangles as rectangles, so the intersection 545cb93a386Sopenharmony_ci // of the crop and quad can be calculated exactly. Some care must be taken if the quad 546cb93a386Sopenharmony_ci // is axis-aligned but does not satisfy asRect() due to flips, etc. 547cb93a386Sopenharmony_ci GrQuadAAFlags clippedEdges; 548cb93a386Sopenharmony_ci if (computeLocal) { 549cb93a386Sopenharmony_ci if (is_simple_rect(quad->fDevice) && is_simple_rect(quad->fLocal)) { 550cb93a386Sopenharmony_ci clippedEdges = crop_simple_rect(cropRect, quad->fDevice.xs(), quad->fDevice.ys(), 551cb93a386Sopenharmony_ci quad->fLocal.xs(), quad->fLocal.ys()); 552cb93a386Sopenharmony_ci } else { 553cb93a386Sopenharmony_ci clippedEdges = crop_rect(cropRect, quad->fDevice.xs(), quad->fDevice.ys(), 554cb93a386Sopenharmony_ci quad->fLocal.xs(), quad->fLocal.ys(), quad->fLocal.ws()); 555cb93a386Sopenharmony_ci } 556cb93a386Sopenharmony_ci } else { 557cb93a386Sopenharmony_ci if (is_simple_rect(quad->fDevice)) { 558cb93a386Sopenharmony_ci clippedEdges = crop_simple_rect(cropRect, quad->fDevice.xs(), quad->fDevice.ys(), 559cb93a386Sopenharmony_ci nullptr, nullptr); 560cb93a386Sopenharmony_ci } else { 561cb93a386Sopenharmony_ci clippedEdges = crop_rect(cropRect, quad->fDevice.xs(), quad->fDevice.ys(), 562cb93a386Sopenharmony_ci nullptr, nullptr, nullptr); 563cb93a386Sopenharmony_ci } 564cb93a386Sopenharmony_ci } 565cb93a386Sopenharmony_ci 566cb93a386Sopenharmony_ci // Apply the clipped edge updates to the original edge flags 567cb93a386Sopenharmony_ci if (cropAA == GrAA::kYes) { 568cb93a386Sopenharmony_ci // Turn on all edges that were clipped 569cb93a386Sopenharmony_ci quad->fEdgeFlags |= clippedEdges; 570cb93a386Sopenharmony_ci } else { 571cb93a386Sopenharmony_ci // Turn off all edges that were clipped 572cb93a386Sopenharmony_ci quad->fEdgeFlags &= ~clippedEdges; 573cb93a386Sopenharmony_ci } 574cb93a386Sopenharmony_ci return true; 575cb93a386Sopenharmony_ci } 576cb93a386Sopenharmony_ci 577cb93a386Sopenharmony_ci if (computeLocal || quad->fDevice.quadType() == GrQuad::Type::kPerspective) { 578cb93a386Sopenharmony_ci // FIXME (michaelludwig) Calculate cropped local coordinates when not kAxisAligned 579cb93a386Sopenharmony_ci // FIXME (michaelludwig) crbug.com/1204347 and skbug.com/9906 - disable this when there's 580cb93a386Sopenharmony_ci // perspective; it does not prove numerical robust enough in the wild and should be 581cb93a386Sopenharmony_ci // revisited. 582cb93a386Sopenharmony_ci return false; 583cb93a386Sopenharmony_ci } 584cb93a386Sopenharmony_ci 585cb93a386Sopenharmony_ci V4f devX = quad->fDevice.x4f(); 586cb93a386Sopenharmony_ci V4f devY = quad->fDevice.y4f(); 587cb93a386Sopenharmony_ci 588cb93a386Sopenharmony_ci V4f clipX = {cropRect.fLeft, cropRect.fLeft, cropRect.fRight, cropRect.fRight}; 589cb93a386Sopenharmony_ci V4f clipY = {cropRect.fTop, cropRect.fBottom, cropRect.fTop, cropRect.fBottom}; 590cb93a386Sopenharmony_ci 591cb93a386Sopenharmony_ci // Calculate barycentric coordinates for the 4 rect corners in the 2 triangles that the quad 592cb93a386Sopenharmony_ci // is tessellated into when drawn. 593cb93a386Sopenharmony_ci V4f u1, v1, w1; 594cb93a386Sopenharmony_ci V4f u2, v2, w2; 595cb93a386Sopenharmony_ci if (!barycentric_coords(devX[0], devY[0], devX[1], devY[1], devX[2], devY[2], clipX, clipY, 596cb93a386Sopenharmony_ci &u1, &v1, &w1) || 597cb93a386Sopenharmony_ci !barycentric_coords(devX[1], devY[1], devX[3], devY[3], devX[2], devY[2], clipX, clipY, 598cb93a386Sopenharmony_ci &u2, &v2, &w2)) { 599cb93a386Sopenharmony_ci // Bad triangles, skip cropping 600cb93a386Sopenharmony_ci return false; 601cb93a386Sopenharmony_ci } 602cb93a386Sopenharmony_ci 603cb93a386Sopenharmony_ci // clipDevRect is completely inside this quad if each corner is in at least one of two triangles 604cb93a386Sopenharmony_ci M4f inTri1 = inside_triangle(u1, v1, w1); 605cb93a386Sopenharmony_ci M4f inTri2 = inside_triangle(u2, v2, w2); 606cb93a386Sopenharmony_ci if (all(inTri1 | inTri2)) { 607cb93a386Sopenharmony_ci // We can crop to exactly the clipDevRect. 608cb93a386Sopenharmony_ci // FIXME (michaelludwig) - there are other ways to have determined quad covering the clip 609cb93a386Sopenharmony_ci // rect, but the barycentric coords will be useful to derive local coordinates in the future 610cb93a386Sopenharmony_ci 611cb93a386Sopenharmony_ci // Since we are cropped to exactly clipDevRect, we have discarded any perspective and the 612cb93a386Sopenharmony_ci // type becomes kRect. If updated locals were requested, they will incorporate perspective. 613cb93a386Sopenharmony_ci // FIXME (michaelludwig) - once we have local coordinates handled, it may be desirable to 614cb93a386Sopenharmony_ci // keep the draw as perspective so that the hardware does perspective interpolation instead 615cb93a386Sopenharmony_ci // of pushing it into a local coord w and having the shader do an extra divide. 616cb93a386Sopenharmony_ci clipX.store(quad->fDevice.xs()); 617cb93a386Sopenharmony_ci clipY.store(quad->fDevice.ys()); 618cb93a386Sopenharmony_ci quad->fDevice.setQuadType(GrQuad::Type::kAxisAligned); 619cb93a386Sopenharmony_ci 620cb93a386Sopenharmony_ci // Update the edge flags to match the clip setting since all 4 edges have been clipped 621cb93a386Sopenharmony_ci quad->fEdgeFlags = cropAA == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone; 622cb93a386Sopenharmony_ci 623cb93a386Sopenharmony_ci return true; 624cb93a386Sopenharmony_ci } 625cb93a386Sopenharmony_ci 626cb93a386Sopenharmony_ci // FIXME (michaelludwig) - use TessellationHelper's inset/outset math to move 627cb93a386Sopenharmony_ci // edges to the closest clip corner they are outside of 628cb93a386Sopenharmony_ci 629cb93a386Sopenharmony_ci return false; 630cb93a386Sopenharmony_ci} 631cb93a386Sopenharmony_ci 632cb93a386Sopenharmony_cibool WillUseHairline(const GrQuad& quad, GrAAType aaType, GrQuadAAFlags edgeFlags) { 633cb93a386Sopenharmony_ci if (aaType != GrAAType::kCoverage || edgeFlags != GrQuadAAFlags::kAll) { 634cb93a386Sopenharmony_ci // Non-aa or msaa don't do any outsetting so they will not be hairlined; mixed edge flags 635cb93a386Sopenharmony_ci // could be hairlined in theory, but applying hairline bloat would extend beyond the 636cb93a386Sopenharmony_ci // original tiled shape. 637cb93a386Sopenharmony_ci return false; 638cb93a386Sopenharmony_ci } 639cb93a386Sopenharmony_ci 640cb93a386Sopenharmony_ci if (quad.quadType() == GrQuad::Type::kAxisAligned) { 641cb93a386Sopenharmony_ci // Fast path that avoids computing edge properties via TessellationHelper. 642cb93a386Sopenharmony_ci // Taking the absolute value of the diagonals always produces the minimum of width or 643cb93a386Sopenharmony_ci // height given that this is axis-aligned, regardless of mirror or 90/180-degree rotations. 644cb93a386Sopenharmony_ci float d = std::min(std::abs(quad.x(3) - quad.x(0)), std::abs(quad.y(3) - quad.y(0))); 645cb93a386Sopenharmony_ci return d < 1.f; 646cb93a386Sopenharmony_ci } else { 647cb93a386Sopenharmony_ci TessellationHelper helper; 648cb93a386Sopenharmony_ci helper.reset(quad, nullptr); 649cb93a386Sopenharmony_ci return helper.isSubpixel(); 650cb93a386Sopenharmony_ci } 651cb93a386Sopenharmony_ci} 652cb93a386Sopenharmony_ci 653cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////////////// 654cb93a386Sopenharmony_ci// TessellationHelper implementation and helper struct implementations 655cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////////////// 656cb93a386Sopenharmony_ci 657cb93a386Sopenharmony_ci//** EdgeVectors implementation 658cb93a386Sopenharmony_ci 659cb93a386Sopenharmony_civoid TessellationHelper::EdgeVectors::reset(const skvx::Vec<4, float>& xs, 660cb93a386Sopenharmony_ci const skvx::Vec<4, float>& ys, 661cb93a386Sopenharmony_ci const skvx::Vec<4, float>& ws, 662cb93a386Sopenharmony_ci GrQuad::Type quadType) { 663cb93a386Sopenharmony_ci // Calculate all projected edge vector values for this quad. 664cb93a386Sopenharmony_ci if (quadType == GrQuad::Type::kPerspective) { 665cb93a386Sopenharmony_ci V4f iw = 1.f / ws; 666cb93a386Sopenharmony_ci fX2D = xs * iw; 667cb93a386Sopenharmony_ci fY2D = ys * iw; 668cb93a386Sopenharmony_ci } else { 669cb93a386Sopenharmony_ci fX2D = xs; 670cb93a386Sopenharmony_ci fY2D = ys; 671cb93a386Sopenharmony_ci } 672cb93a386Sopenharmony_ci 673cb93a386Sopenharmony_ci fDX = next_ccw(fX2D) - fX2D; 674cb93a386Sopenharmony_ci fDY = next_ccw(fY2D) - fY2D; 675cb93a386Sopenharmony_ci fInvLengths = 1.f / sqrt(fDX*fDX + fDY*fDY); 676cb93a386Sopenharmony_ci 677cb93a386Sopenharmony_ci // Normalize edge vectors 678cb93a386Sopenharmony_ci fDX *= fInvLengths; 679cb93a386Sopenharmony_ci fDY *= fInvLengths; 680cb93a386Sopenharmony_ci 681cb93a386Sopenharmony_ci // Calculate angles between vectors 682cb93a386Sopenharmony_ci if (quadType <= GrQuad::Type::kRectilinear) { 683cb93a386Sopenharmony_ci fCosTheta = 0.f; 684cb93a386Sopenharmony_ci fInvSinTheta = 1.f; 685cb93a386Sopenharmony_ci } else { 686cb93a386Sopenharmony_ci fCosTheta = fDX*next_cw(fDX) + fDY*next_cw(fDY); 687cb93a386Sopenharmony_ci // NOTE: if cosTheta is close to 1, inset/outset math will avoid the fast paths that rely 688cb93a386Sopenharmony_ci // on thefInvSinTheta since it will approach infinity. 689cb93a386Sopenharmony_ci fInvSinTheta = 1.f / sqrt(1.f - fCosTheta * fCosTheta); 690cb93a386Sopenharmony_ci } 691cb93a386Sopenharmony_ci} 692cb93a386Sopenharmony_ci 693cb93a386Sopenharmony_ci//** EdgeEquations implementation 694cb93a386Sopenharmony_ci 695cb93a386Sopenharmony_civoid TessellationHelper::EdgeEquations::reset(const EdgeVectors& edgeVectors) { 696cb93a386Sopenharmony_ci V4f dx = edgeVectors.fDX; 697cb93a386Sopenharmony_ci V4f dy = edgeVectors.fDY; 698cb93a386Sopenharmony_ci // Correct for bad edges by copying adjacent edge information into the bad component 699cb93a386Sopenharmony_ci correct_bad_edges(edgeVectors.fInvLengths >= kInvDistTolerance, &dx, &dy, nullptr); 700cb93a386Sopenharmony_ci 701cb93a386Sopenharmony_ci V4f c = dx*edgeVectors.fY2D - dy*edgeVectors.fX2D; 702cb93a386Sopenharmony_ci // Make sure normals point into the shape 703cb93a386Sopenharmony_ci V4f test = dy * next_cw(edgeVectors.fX2D) + (-dx * next_cw(edgeVectors.fY2D) + c); 704cb93a386Sopenharmony_ci if (any(test < -kDistTolerance)) { 705cb93a386Sopenharmony_ci fA = -dy; 706cb93a386Sopenharmony_ci fB = dx; 707cb93a386Sopenharmony_ci fC = -c; 708cb93a386Sopenharmony_ci } else { 709cb93a386Sopenharmony_ci fA = dy; 710cb93a386Sopenharmony_ci fB = -dx; 711cb93a386Sopenharmony_ci fC = c; 712cb93a386Sopenharmony_ci } 713cb93a386Sopenharmony_ci} 714cb93a386Sopenharmony_ci 715cb93a386Sopenharmony_ciV4f TessellationHelper::EdgeEquations::estimateCoverage(const V4f& x2d, const V4f& y2d) const { 716cb93a386Sopenharmony_ci // Calculate distance of the 4 inset points (px, py) to the 4 edges 717cb93a386Sopenharmony_ci V4f d0 = fA[0]*x2d + (fB[0]*y2d + fC[0]); 718cb93a386Sopenharmony_ci V4f d1 = fA[1]*x2d + (fB[1]*y2d + fC[1]); 719cb93a386Sopenharmony_ci V4f d2 = fA[2]*x2d + (fB[2]*y2d + fC[2]); 720cb93a386Sopenharmony_ci V4f d3 = fA[3]*x2d + (fB[3]*y2d + fC[3]); 721cb93a386Sopenharmony_ci 722cb93a386Sopenharmony_ci // For each point, pretend that there's a rectangle that touches e0 and e3 on the horizontal 723cb93a386Sopenharmony_ci // axis, so its width is "approximately" d0 + d3, and it touches e1 and e2 on the vertical axis 724cb93a386Sopenharmony_ci // so its height is d1 + d2. Pin each of these dimensions to [0, 1] and approximate the coverage 725cb93a386Sopenharmony_ci // at each point as clamp(d0+d3, 0, 1) x clamp(d1+d2, 0, 1). For rectilinear quads this is an 726cb93a386Sopenharmony_ci // accurate calculation of its area clipped to an aligned pixel. For arbitrary quads it is not 727cb93a386Sopenharmony_ci // mathematically accurate but qualitatively provides a stable value proportional to the size of 728cb93a386Sopenharmony_ci // the shape. 729cb93a386Sopenharmony_ci V4f w = max(0.f, min(1.f, d0 + d3)); 730cb93a386Sopenharmony_ci V4f h = max(0.f, min(1.f, d1 + d2)); 731cb93a386Sopenharmony_ci return w * h; 732cb93a386Sopenharmony_ci} 733cb93a386Sopenharmony_ci 734cb93a386Sopenharmony_cibool TessellationHelper::EdgeEquations::isSubpixel(const V4f& x2d, const V4f& y2d) const { 735cb93a386Sopenharmony_ci // Compute the minimum distances from vertices to opposite edges. If all 4 minimum distances 736cb93a386Sopenharmony_ci // are less than 1px, then the inset geometry would be a point or line and quad rendering 737cb93a386Sopenharmony_ci // will switch to hairline mode. 738cb93a386Sopenharmony_ci V4f d = min(x2d * skvx::shuffle<1,2,1,2>(fA) + y2d * skvx::shuffle<1,2,1,2>(fB) 739cb93a386Sopenharmony_ci + skvx::shuffle<1,2,1,2>(fC), 740cb93a386Sopenharmony_ci x2d * skvx::shuffle<3,3,0,0>(fA) + y2d * skvx::shuffle<3,3,0,0>(fB) 741cb93a386Sopenharmony_ci + skvx::shuffle<3,3,0,0>(fC)); 742cb93a386Sopenharmony_ci return all(d < 1.f); 743cb93a386Sopenharmony_ci} 744cb93a386Sopenharmony_ci 745cb93a386Sopenharmony_ciint TessellationHelper::EdgeEquations::computeDegenerateQuad(const V4f& signedEdgeDistances, 746cb93a386Sopenharmony_ci V4f* x2d, V4f* y2d, 747cb93a386Sopenharmony_ci M4f* aaMask) const { 748cb93a386Sopenharmony_ci // If the original points form a line in the 2D projection then give up on antialiasing. 749cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 750cb93a386Sopenharmony_ci V4f d = (*x2d)*fA[i] + (*y2d)*fB[i] + fC[i]; 751cb93a386Sopenharmony_ci if (all(abs(d) < kDistTolerance)) { 752cb93a386Sopenharmony_ci *aaMask = M4f(0); 753cb93a386Sopenharmony_ci return 4; 754cb93a386Sopenharmony_ci } 755cb93a386Sopenharmony_ci } 756cb93a386Sopenharmony_ci 757cb93a386Sopenharmony_ci *aaMask = signedEdgeDistances != 0.f; 758cb93a386Sopenharmony_ci 759cb93a386Sopenharmony_ci // Move the edge by the signed edge adjustment. 760cb93a386Sopenharmony_ci V4f oc = fC + signedEdgeDistances; 761cb93a386Sopenharmony_ci 762cb93a386Sopenharmony_ci // There are 6 points that we care about to determine the final shape of the polygon, which 763cb93a386Sopenharmony_ci // are the intersections between (e0,e2), (e1,e0), (e2,e3), (e3,e1) (corresponding to the 764cb93a386Sopenharmony_ci // 4 corners), and (e1, e2), (e0, e3) (representing the intersections of opposite edges). 765cb93a386Sopenharmony_ci V4f denom = fA * next_cw(fB) - fB * next_cw(fA); 766cb93a386Sopenharmony_ci V4f px = (fB * next_cw(oc) - oc * next_cw(fB)) / denom; 767cb93a386Sopenharmony_ci V4f py = (oc * next_cw(fA) - fA * next_cw(oc)) / denom; 768cb93a386Sopenharmony_ci correct_bad_coords(abs(denom) < kTolerance, &px, &py, nullptr); 769cb93a386Sopenharmony_ci 770cb93a386Sopenharmony_ci // Calculate the signed distances from these 4 corners to the other two edges that did not 771cb93a386Sopenharmony_ci // define the intersection. So p(0) is compared to e3,e1, p(1) to e3,e2 , p(2) to e0,e1, and 772cb93a386Sopenharmony_ci // p(3) to e0,e2 773cb93a386Sopenharmony_ci V4f dists1 = px * skvx::shuffle<3, 3, 0, 0>(fA) + 774cb93a386Sopenharmony_ci py * skvx::shuffle<3, 3, 0, 0>(fB) + 775cb93a386Sopenharmony_ci skvx::shuffle<3, 3, 0, 0>(oc); 776cb93a386Sopenharmony_ci V4f dists2 = px * skvx::shuffle<1, 2, 1, 2>(fA) + 777cb93a386Sopenharmony_ci py * skvx::shuffle<1, 2, 1, 2>(fB) + 778cb93a386Sopenharmony_ci skvx::shuffle<1, 2, 1, 2>(oc); 779cb93a386Sopenharmony_ci 780cb93a386Sopenharmony_ci // If all the distances are >= 0, the 4 corners form a valid quadrilateral, so use them as 781cb93a386Sopenharmony_ci // the 4 points. If any point is on the wrong side of both edges, the interior has collapsed 782cb93a386Sopenharmony_ci // and we need to use a central point to represent it. If all four points are only on the 783cb93a386Sopenharmony_ci // wrong side of 1 edge, one edge has crossed over another and we use a line to represent it. 784cb93a386Sopenharmony_ci // Otherwise, use a triangle that replaces the bad points with the intersections of 785cb93a386Sopenharmony_ci // (e1, e2) or (e0, e3) as needed. 786cb93a386Sopenharmony_ci M4f d1v0 = dists1 < kDistTolerance; 787cb93a386Sopenharmony_ci M4f d2v0 = dists2 < kDistTolerance; 788cb93a386Sopenharmony_ci M4f d1And2 = d1v0 & d2v0; 789cb93a386Sopenharmony_ci M4f d1Or2 = d1v0 | d2v0; 790cb93a386Sopenharmony_ci 791cb93a386Sopenharmony_ci if (!any(d1Or2)) { 792cb93a386Sopenharmony_ci // Every dists1 and dists2 >= kTolerance so it's not degenerate, use all 4 corners as-is 793cb93a386Sopenharmony_ci // and use full coverage 794cb93a386Sopenharmony_ci *x2d = px; 795cb93a386Sopenharmony_ci *y2d = py; 796cb93a386Sopenharmony_ci return 4; 797cb93a386Sopenharmony_ci } else if (any(d1And2)) { 798cb93a386Sopenharmony_ci // A point failed against two edges, so reduce the shape to a single point, which we take as 799cb93a386Sopenharmony_ci // the center of the original quad to ensure it is contained in the intended geometry. Since 800cb93a386Sopenharmony_ci // it has collapsed, we know the shape cannot cover a pixel so update the coverage. 801cb93a386Sopenharmony_ci SkPoint center = {0.25f * ((*x2d)[0] + (*x2d)[1] + (*x2d)[2] + (*x2d)[3]), 802cb93a386Sopenharmony_ci 0.25f * ((*y2d)[0] + (*y2d)[1] + (*y2d)[2] + (*y2d)[3])}; 803cb93a386Sopenharmony_ci *x2d = center.fX; 804cb93a386Sopenharmony_ci *y2d = center.fY; 805cb93a386Sopenharmony_ci *aaMask = any(*aaMask); 806cb93a386Sopenharmony_ci return 1; 807cb93a386Sopenharmony_ci } else if (all(d1Or2)) { 808cb93a386Sopenharmony_ci // Degenerates to a line. Compare p[2] and p[3] to edge 0. If they are on the wrong side, 809cb93a386Sopenharmony_ci // that means edge 0 and 3 crossed, and otherwise edge 1 and 2 crossed. 810cb93a386Sopenharmony_ci if (dists1[2] < kDistTolerance && dists1[3] < kDistTolerance) { 811cb93a386Sopenharmony_ci // Edges 0 and 3 have crossed over, so make the line from average of (p0,p2) and (p1,p3) 812cb93a386Sopenharmony_ci *x2d = 0.5f * (skvx::shuffle<0, 1, 0, 1>(px) + skvx::shuffle<2, 3, 2, 3>(px)); 813cb93a386Sopenharmony_ci *y2d = 0.5f * (skvx::shuffle<0, 1, 0, 1>(py) + skvx::shuffle<2, 3, 2, 3>(py)); 814cb93a386Sopenharmony_ci // If edges 0 and 3 crossed then one must have AA but we moved both 2D points on the 815cb93a386Sopenharmony_ci // edge so we need moveTo() to be able to move both 3D points along the shared edge. So 816cb93a386Sopenharmony_ci // ensure both have AA. 817cb93a386Sopenharmony_ci *aaMask = *aaMask | M4f({1, 0, 0, 1}); 818cb93a386Sopenharmony_ci } else { 819cb93a386Sopenharmony_ci // Edges 1 and 2 have crossed over, so make the line from average of (p0,p1) and (p2,p3) 820cb93a386Sopenharmony_ci *x2d = 0.5f * (skvx::shuffle<0, 0, 2, 2>(px) + skvx::shuffle<1, 1, 3, 3>(px)); 821cb93a386Sopenharmony_ci *y2d = 0.5f * (skvx::shuffle<0, 0, 2, 2>(py) + skvx::shuffle<1, 1, 3, 3>(py)); 822cb93a386Sopenharmony_ci *aaMask = *aaMask | M4f({0, 1, 1, 0}); 823cb93a386Sopenharmony_ci } 824cb93a386Sopenharmony_ci return 2; 825cb93a386Sopenharmony_ci } else { 826cb93a386Sopenharmony_ci // This turns into a triangle. Replace corners as needed with the intersections between 827cb93a386Sopenharmony_ci // (e0,e3) and (e1,e2), which must now be calculated. Because of kDistTolarance we can 828cb93a386Sopenharmony_ci // have cases where the intersection lies far outside the quad. For example, consider top 829cb93a386Sopenharmony_ci // and bottom edges that are nearly parallel and their intersections with the right edge are 830cb93a386Sopenharmony_ci // nearly but not quite swapped (top edge intersection is barely above bottom edge 831cb93a386Sopenharmony_ci // intersection). In this case we replace the point with the average of itself and the point 832cb93a386Sopenharmony_ci // calculated using the edge equation it failed (in the example case this would be the 833cb93a386Sopenharmony_ci // average of the points calculated by the top and bottom edges intersected with the right 834cb93a386Sopenharmony_ci // edge.) 835cb93a386Sopenharmony_ci using V2f = skvx::Vec<2, float>; 836cb93a386Sopenharmony_ci V2f eDenom = skvx::shuffle<0, 1>(fA) * skvx::shuffle<3, 2>(fB) - 837cb93a386Sopenharmony_ci skvx::shuffle<0, 1>(fB) * skvx::shuffle<3, 2>(fA); 838cb93a386Sopenharmony_ci V2f ex = (skvx::shuffle<0, 1>(fB) * skvx::shuffle<3, 2>(oc) - 839cb93a386Sopenharmony_ci skvx::shuffle<0, 1>(oc) * skvx::shuffle<3, 2>(fB)) / eDenom; 840cb93a386Sopenharmony_ci V2f ey = (skvx::shuffle<0, 1>(oc) * skvx::shuffle<3, 2>(fA) - 841cb93a386Sopenharmony_ci skvx::shuffle<0, 1>(fA) * skvx::shuffle<3, 2>(oc)) / eDenom; 842cb93a386Sopenharmony_ci 843cb93a386Sopenharmony_ci V4f avgX = 0.5f * (skvx::shuffle<0, 1, 0, 2>(px) + skvx::shuffle<2, 3, 1, 3>(px)); 844cb93a386Sopenharmony_ci V4f avgY = 0.5f * (skvx::shuffle<0, 1, 0, 2>(py) + skvx::shuffle<2, 3, 1, 3>(py)); 845cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 846cb93a386Sopenharmony_ci // Note that we would not have taken this branch if any point failed both of its edges 847cb93a386Sopenharmony_ci // tests. That is, it can't be the case that d1v0[i] and d2v0[i] are both true. 848cb93a386Sopenharmony_ci if (dists1[i] < -kDistTolerance && abs(eDenom[0]) > kTolerance) { 849cb93a386Sopenharmony_ci px[i] = ex[0]; 850cb93a386Sopenharmony_ci py[i] = ey[0]; 851cb93a386Sopenharmony_ci } else if (d1v0[i]) { 852cb93a386Sopenharmony_ci px[i] = avgX[i % 2]; 853cb93a386Sopenharmony_ci py[i] = avgY[i % 2]; 854cb93a386Sopenharmony_ci } else if (dists2[i] < -kDistTolerance && abs(eDenom[1]) > kTolerance) { 855cb93a386Sopenharmony_ci px[i] = ex[1]; 856cb93a386Sopenharmony_ci py[i] = ey[1]; 857cb93a386Sopenharmony_ci } else if (d2v0[i]) { 858cb93a386Sopenharmony_ci px[i] = avgX[i / 2 + 2]; 859cb93a386Sopenharmony_ci py[i] = avgY[i / 2 + 2]; 860cb93a386Sopenharmony_ci } 861cb93a386Sopenharmony_ci } 862cb93a386Sopenharmony_ci 863cb93a386Sopenharmony_ci // If we replace a vertex with an intersection then it will not fall along the 864cb93a386Sopenharmony_ci // edges that intersect at the original vertex. When we apply AA later to the 865cb93a386Sopenharmony_ci // original points we move along the original 3d edges to move towards the 2d 866cb93a386Sopenharmony_ci // points we're computing here. If we have an AA edge and a non-AA edge we 867cb93a386Sopenharmony_ci // can only move along 1 edge, but now the point we're moving toward isn't 868cb93a386Sopenharmony_ci // on that edge. Thus, we provide an additional degree of freedom by turning 869cb93a386Sopenharmony_ci // AA on for both edges if either edge is AA at each point. 870cb93a386Sopenharmony_ci *aaMask = *aaMask | (d1Or2 & next_cw(*aaMask)) | (next_ccw(d1Or2) & next_ccw(*aaMask)); 871cb93a386Sopenharmony_ci *x2d = px; 872cb93a386Sopenharmony_ci *y2d = py; 873cb93a386Sopenharmony_ci return 3; 874cb93a386Sopenharmony_ci } 875cb93a386Sopenharmony_ci} 876cb93a386Sopenharmony_ci 877cb93a386Sopenharmony_ci//** OutsetRequest implementation 878cb93a386Sopenharmony_ci 879cb93a386Sopenharmony_civoid TessellationHelper::OutsetRequest::reset(const EdgeVectors& edgeVectors, GrQuad::Type quadType, 880cb93a386Sopenharmony_ci const skvx::Vec<4, float>& edgeDistances) { 881cb93a386Sopenharmony_ci fEdgeDistances = edgeDistances; 882cb93a386Sopenharmony_ci 883cb93a386Sopenharmony_ci // Based on the edge distances, determine if it's acceptable to use fInvSinTheta to 884cb93a386Sopenharmony_ci // calculate the inset or outset geometry. 885cb93a386Sopenharmony_ci if (quadType <= GrQuad::Type::kRectilinear) { 886cb93a386Sopenharmony_ci // Since it's rectangular, the width (edge[1] or edge[2]) collapses if subtracting 887cb93a386Sopenharmony_ci // (dist[0] + dist[3]) makes the new width negative (minus for inset, outsetting will 888cb93a386Sopenharmony_ci // never be degenerate in this case). The same applies for height (edge[0] or edge[3]) 889cb93a386Sopenharmony_ci // and (dist[1] + dist[2]). 890cb93a386Sopenharmony_ci fOutsetDegenerate = false; 891cb93a386Sopenharmony_ci float widthChange = edgeDistances[0] + edgeDistances[3]; 892cb93a386Sopenharmony_ci float heightChange = edgeDistances[1] + edgeDistances[2]; 893cb93a386Sopenharmony_ci // (1/len > 1/(edge sum) implies len - edge sum < 0. 894cb93a386Sopenharmony_ci fInsetDegenerate = 895cb93a386Sopenharmony_ci (widthChange > 0.f && edgeVectors.fInvLengths[1] > 1.f / widthChange) || 896cb93a386Sopenharmony_ci (heightChange > 0.f && edgeVectors.fInvLengths[0] > 1.f / heightChange); 897cb93a386Sopenharmony_ci } else if (any(edgeVectors.fInvLengths >= kInvDistTolerance)) { 898cb93a386Sopenharmony_ci // Have an edge that is effectively length 0, so we're dealing with a triangle, which 899cb93a386Sopenharmony_ci // must always go through the degenerate code path. 900cb93a386Sopenharmony_ci fOutsetDegenerate = true; 901cb93a386Sopenharmony_ci fInsetDegenerate = true; 902cb93a386Sopenharmony_ci } else { 903cb93a386Sopenharmony_ci // If possible, the corners will move +/-edgeDistances * 1/sin(theta). The entire 904cb93a386Sopenharmony_ci // request is degenerate if 1/sin(theta) -> infinity (or cos(theta) -> 1). 905cb93a386Sopenharmony_ci if (any(abs(edgeVectors.fCosTheta) >= 0.9f)) { 906cb93a386Sopenharmony_ci fOutsetDegenerate = true; 907cb93a386Sopenharmony_ci fInsetDegenerate = true; 908cb93a386Sopenharmony_ci } else { 909cb93a386Sopenharmony_ci // With an edge-centric view, an edge's length changes by 910cb93a386Sopenharmony_ci // edgeDistance * cos(pi - theta) / sin(theta) for each of its corners (the second 911cb93a386Sopenharmony_ci // corner uses ccw theta value). An edge's length also changes when its adjacent 912cb93a386Sopenharmony_ci // edges move, in which case it's updated by edgeDistance / sin(theta) 913cb93a386Sopenharmony_ci // (or cos(theta) for the other edge). 914cb93a386Sopenharmony_ci 915cb93a386Sopenharmony_ci // cos(pi - theta) = -cos(theta) 916cb93a386Sopenharmony_ci V4f halfTanTheta = -edgeVectors.fCosTheta * edgeVectors.fInvSinTheta; 917cb93a386Sopenharmony_ci V4f edgeAdjust = edgeDistances * (halfTanTheta + next_ccw(halfTanTheta)) + 918cb93a386Sopenharmony_ci next_ccw(edgeDistances) * next_ccw(edgeVectors.fInvSinTheta) + 919cb93a386Sopenharmony_ci next_cw(edgeDistances) * edgeVectors.fInvSinTheta; 920cb93a386Sopenharmony_ci 921cb93a386Sopenharmony_ci // If either outsetting (plus edgeAdjust) or insetting (minus edgeAdjust) make 922cb93a386Sopenharmony_ci // the edge lengths negative, then it's degenerate. 923cb93a386Sopenharmony_ci V4f threshold = 0.1f - (1.f / edgeVectors.fInvLengths); 924cb93a386Sopenharmony_ci fOutsetDegenerate = any(edgeAdjust < threshold); 925cb93a386Sopenharmony_ci fInsetDegenerate = any(edgeAdjust > -threshold); 926cb93a386Sopenharmony_ci } 927cb93a386Sopenharmony_ci } 928cb93a386Sopenharmony_ci} 929cb93a386Sopenharmony_ci 930cb93a386Sopenharmony_ci//** Vertices implementation 931cb93a386Sopenharmony_ci 932cb93a386Sopenharmony_civoid TessellationHelper::Vertices::reset(const GrQuad& deviceQuad, const GrQuad* localQuad) { 933cb93a386Sopenharmony_ci // Set vertices to match the device and local quad 934cb93a386Sopenharmony_ci fX = deviceQuad.x4f(); 935cb93a386Sopenharmony_ci fY = deviceQuad.y4f(); 936cb93a386Sopenharmony_ci fW = deviceQuad.w4f(); 937cb93a386Sopenharmony_ci 938cb93a386Sopenharmony_ci if (localQuad) { 939cb93a386Sopenharmony_ci fU = localQuad->x4f(); 940cb93a386Sopenharmony_ci fV = localQuad->y4f(); 941cb93a386Sopenharmony_ci fR = localQuad->w4f(); 942cb93a386Sopenharmony_ci fUVRCount = localQuad->hasPerspective() ? 3 : 2; 943cb93a386Sopenharmony_ci } else { 944cb93a386Sopenharmony_ci fUVRCount = 0; 945cb93a386Sopenharmony_ci } 946cb93a386Sopenharmony_ci} 947cb93a386Sopenharmony_ci 948cb93a386Sopenharmony_civoid TessellationHelper::Vertices::asGrQuads(GrQuad* deviceOut, GrQuad::Type deviceType, 949cb93a386Sopenharmony_ci GrQuad* localOut, GrQuad::Type localType) const { 950cb93a386Sopenharmony_ci SkASSERT(deviceOut); 951cb93a386Sopenharmony_ci SkASSERT(fUVRCount == 0 || localOut); 952cb93a386Sopenharmony_ci 953cb93a386Sopenharmony_ci fX.store(deviceOut->xs()); 954cb93a386Sopenharmony_ci fY.store(deviceOut->ys()); 955cb93a386Sopenharmony_ci if (deviceType == GrQuad::Type::kPerspective) { 956cb93a386Sopenharmony_ci fW.store(deviceOut->ws()); 957cb93a386Sopenharmony_ci } 958cb93a386Sopenharmony_ci deviceOut->setQuadType(deviceType); // This sets ws == 1 when device type != perspective 959cb93a386Sopenharmony_ci 960cb93a386Sopenharmony_ci if (fUVRCount > 0) { 961cb93a386Sopenharmony_ci fU.store(localOut->xs()); 962cb93a386Sopenharmony_ci fV.store(localOut->ys()); 963cb93a386Sopenharmony_ci if (fUVRCount == 3) { 964cb93a386Sopenharmony_ci fR.store(localOut->ws()); 965cb93a386Sopenharmony_ci } 966cb93a386Sopenharmony_ci localOut->setQuadType(localType); 967cb93a386Sopenharmony_ci } 968cb93a386Sopenharmony_ci} 969cb93a386Sopenharmony_ci 970cb93a386Sopenharmony_civoid TessellationHelper::Vertices::moveAlong(const EdgeVectors& edgeVectors, 971cb93a386Sopenharmony_ci const V4f& signedEdgeDistances) { 972cb93a386Sopenharmony_ci // This shouldn't be called if fInvSinTheta is close to infinity (cosTheta close to 1). 973cb93a386Sopenharmony_ci // FIXME (michaelludwig) - Temporarily allow NaNs on debug builds here, for crbug:224618's GM 974cb93a386Sopenharmony_ci // Once W clipping is implemented, shouldn't see NaNs unless it's actually time to fail. 975cb93a386Sopenharmony_ci SkASSERT(all(abs(edgeVectors.fCosTheta) < 0.9f) || 976cb93a386Sopenharmony_ci any(edgeVectors.fCosTheta != edgeVectors.fCosTheta)); 977cb93a386Sopenharmony_ci 978cb93a386Sopenharmony_ci // When the projected device quad is not degenerate, the vertex corners can move 979cb93a386Sopenharmony_ci // cornerOutsetLen along their edge and their cw-rotated edge. The vertex's edge points 980cb93a386Sopenharmony_ci // inwards and the cw-rotated edge points outwards, hence the minus-sign. 981cb93a386Sopenharmony_ci // The edge distances are rotated compared to the corner outsets and (dx, dy), since if 982cb93a386Sopenharmony_ci // the edge is "on" both its corners need to be moved along their other edge vectors. 983cb93a386Sopenharmony_ci V4f signedOutsets = -edgeVectors.fInvSinTheta * next_cw(signedEdgeDistances); 984cb93a386Sopenharmony_ci V4f signedOutsetsCW = edgeVectors.fInvSinTheta * signedEdgeDistances; 985cb93a386Sopenharmony_ci 986cb93a386Sopenharmony_ci // x = x + outset * mask * next_cw(xdiff) - outset * next_cw(mask) * xdiff 987cb93a386Sopenharmony_ci fX += signedOutsetsCW * next_cw(edgeVectors.fDX) + signedOutsets * edgeVectors.fDX; 988cb93a386Sopenharmony_ci fY += signedOutsetsCW * next_cw(edgeVectors.fDY) + signedOutsets * edgeVectors.fDY; 989cb93a386Sopenharmony_ci if (fUVRCount > 0) { 990cb93a386Sopenharmony_ci // We want to extend the texture coords by the same proportion as the positions. 991cb93a386Sopenharmony_ci signedOutsets *= edgeVectors.fInvLengths; 992cb93a386Sopenharmony_ci signedOutsetsCW *= next_cw(edgeVectors.fInvLengths); 993cb93a386Sopenharmony_ci V4f du = next_ccw(fU) - fU; 994cb93a386Sopenharmony_ci V4f dv = next_ccw(fV) - fV; 995cb93a386Sopenharmony_ci fU += signedOutsetsCW * next_cw(du) + signedOutsets * du; 996cb93a386Sopenharmony_ci fV += signedOutsetsCW * next_cw(dv) + signedOutsets * dv; 997cb93a386Sopenharmony_ci if (fUVRCount == 3) { 998cb93a386Sopenharmony_ci V4f dr = next_ccw(fR) - fR; 999cb93a386Sopenharmony_ci fR += signedOutsetsCW * next_cw(dr) + signedOutsets * dr; 1000cb93a386Sopenharmony_ci } 1001cb93a386Sopenharmony_ci } 1002cb93a386Sopenharmony_ci} 1003cb93a386Sopenharmony_ci 1004cb93a386Sopenharmony_civoid TessellationHelper::Vertices::moveTo(const V4f& x2d, const V4f& y2d, const M4f& mask) { 1005cb93a386Sopenharmony_ci // Left to right, in device space, for each point 1006cb93a386Sopenharmony_ci V4f e1x = skvx::shuffle<2, 3, 2, 3>(fX) - skvx::shuffle<0, 1, 0, 1>(fX); 1007cb93a386Sopenharmony_ci V4f e1y = skvx::shuffle<2, 3, 2, 3>(fY) - skvx::shuffle<0, 1, 0, 1>(fY); 1008cb93a386Sopenharmony_ci V4f e1w = skvx::shuffle<2, 3, 2, 3>(fW) - skvx::shuffle<0, 1, 0, 1>(fW); 1009cb93a386Sopenharmony_ci M4f e1Bad = e1x*e1x + e1y*e1y < kDist2Tolerance; 1010cb93a386Sopenharmony_ci correct_bad_edges(e1Bad, &e1x, &e1y, &e1w); 1011cb93a386Sopenharmony_ci 1012cb93a386Sopenharmony_ci // // Top to bottom, in device space, for each point 1013cb93a386Sopenharmony_ci V4f e2x = skvx::shuffle<1, 1, 3, 3>(fX) - skvx::shuffle<0, 0, 2, 2>(fX); 1014cb93a386Sopenharmony_ci V4f e2y = skvx::shuffle<1, 1, 3, 3>(fY) - skvx::shuffle<0, 0, 2, 2>(fY); 1015cb93a386Sopenharmony_ci V4f e2w = skvx::shuffle<1, 1, 3, 3>(fW) - skvx::shuffle<0, 0, 2, 2>(fW); 1016cb93a386Sopenharmony_ci M4f e2Bad = e2x*e2x + e2y*e2y < kDist2Tolerance; 1017cb93a386Sopenharmony_ci correct_bad_edges(e2Bad, &e2x, &e2y, &e2w); 1018cb93a386Sopenharmony_ci 1019cb93a386Sopenharmony_ci // Can only move along e1 and e2 to reach the new 2D point, so we have 1020cb93a386Sopenharmony_ci // x2d = (x + a*e1x + b*e2x) / (w + a*e1w + b*e2w) and 1021cb93a386Sopenharmony_ci // y2d = (y + a*e1y + b*e2y) / (w + a*e1w + b*e2w) for some a, b 1022cb93a386Sopenharmony_ci // This can be rewritten to a*c1x + b*c2x + c3x = 0; a * c1y + b*c2y + c3y = 0, where 1023cb93a386Sopenharmony_ci // the cNx and cNy coefficients are: 1024cb93a386Sopenharmony_ci V4f c1x = e1w * x2d - e1x; 1025cb93a386Sopenharmony_ci V4f c1y = e1w * y2d - e1y; 1026cb93a386Sopenharmony_ci V4f c2x = e2w * x2d - e2x; 1027cb93a386Sopenharmony_ci V4f c2y = e2w * y2d - e2y; 1028cb93a386Sopenharmony_ci V4f c3x = fW * x2d - fX; 1029cb93a386Sopenharmony_ci V4f c3y = fW * y2d - fY; 1030cb93a386Sopenharmony_ci 1031cb93a386Sopenharmony_ci // Solve for a and b 1032cb93a386Sopenharmony_ci V4f a, b, denom; 1033cb93a386Sopenharmony_ci if (all(mask)) { 1034cb93a386Sopenharmony_ci // When every edge is outset/inset, each corner can use both edge vectors 1035cb93a386Sopenharmony_ci denom = c1x * c2y - c2x * c1y; 1036cb93a386Sopenharmony_ci a = (c2x * c3y - c3x * c2y) / denom; 1037cb93a386Sopenharmony_ci b = (c3x * c1y - c1x * c3y) / denom; 1038cb93a386Sopenharmony_ci } else { 1039cb93a386Sopenharmony_ci // Force a or b to be 0 if that edge cannot be used due to non-AA 1040cb93a386Sopenharmony_ci M4f aMask = skvx::shuffle<0, 0, 3, 3>(mask); 1041cb93a386Sopenharmony_ci M4f bMask = skvx::shuffle<2, 1, 2, 1>(mask); 1042cb93a386Sopenharmony_ci 1043cb93a386Sopenharmony_ci // When aMask[i]&bMask[i], then a[i], b[i], denom[i] match the kAll case. 1044cb93a386Sopenharmony_ci // When aMask[i]&!bMask[i], then b[i] = 0, a[i] = -c3x/c1x or -c3y/c1y, using better denom 1045cb93a386Sopenharmony_ci // When !aMask[i]&bMask[i], then a[i] = 0, b[i] = -c3x/c2x or -c3y/c2y, "" 1046cb93a386Sopenharmony_ci // When !aMask[i]&!bMask[i], then both a[i] = 0 and b[i] = 0 1047cb93a386Sopenharmony_ci M4f useC1x = abs(c1x) > abs(c1y); 1048cb93a386Sopenharmony_ci M4f useC2x = abs(c2x) > abs(c2y); 1049cb93a386Sopenharmony_ci 1050cb93a386Sopenharmony_ci denom = if_then_else(aMask, 1051cb93a386Sopenharmony_ci if_then_else(bMask, 1052cb93a386Sopenharmony_ci c1x * c2y - c2x * c1y, /* A & B */ 1053cb93a386Sopenharmony_ci if_then_else(useC1x, c1x, c1y)), /* A & !B */ 1054cb93a386Sopenharmony_ci if_then_else(bMask, 1055cb93a386Sopenharmony_ci if_then_else(useC2x, c2x, c2y), /* !A & B */ 1056cb93a386Sopenharmony_ci V4f(1.f))); /* !A & !B */ 1057cb93a386Sopenharmony_ci 1058cb93a386Sopenharmony_ci a = if_then_else(aMask, 1059cb93a386Sopenharmony_ci if_then_else(bMask, 1060cb93a386Sopenharmony_ci c2x * c3y - c3x * c2y, /* A & B */ 1061cb93a386Sopenharmony_ci if_then_else(useC1x, -c3x, -c3y)), /* A & !B */ 1062cb93a386Sopenharmony_ci V4f(0.f)) / denom; /* !A */ 1063cb93a386Sopenharmony_ci b = if_then_else(bMask, 1064cb93a386Sopenharmony_ci if_then_else(aMask, 1065cb93a386Sopenharmony_ci c3x * c1y - c1x * c3y, /* A & B */ 1066cb93a386Sopenharmony_ci if_then_else(useC2x, -c3x, -c3y)), /* !A & B */ 1067cb93a386Sopenharmony_ci V4f(0.f)) / denom; /* !B */ 1068cb93a386Sopenharmony_ci } 1069cb93a386Sopenharmony_ci 1070cb93a386Sopenharmony_ci fX += a * e1x + b * e2x; 1071cb93a386Sopenharmony_ci fY += a * e1y + b * e2y; 1072cb93a386Sopenharmony_ci fW += a * e1w + b * e2w; 1073cb93a386Sopenharmony_ci 1074cb93a386Sopenharmony_ci // If fW has gone negative, flip the point to the other side of w=0. This only happens if the 1075cb93a386Sopenharmony_ci // edge was approaching a vanishing point and it was physically impossible to outset 1/2px in 1076cb93a386Sopenharmony_ci // screen space w/o going behind the viewer and being mirrored. Scaling by -1 preserves the 1077cb93a386Sopenharmony_ci // computed screen space position but moves the 3D point off of the original quad. So far, this 1078cb93a386Sopenharmony_ci // seems to be a reasonable compromise. 1079cb93a386Sopenharmony_ci if (any(fW < 0.f)) { 1080cb93a386Sopenharmony_ci V4f scale = if_then_else(fW < 0.f, V4f(-1.f), V4f(1.f)); 1081cb93a386Sopenharmony_ci fX *= scale; 1082cb93a386Sopenharmony_ci fY *= scale; 1083cb93a386Sopenharmony_ci fW *= scale; 1084cb93a386Sopenharmony_ci } 1085cb93a386Sopenharmony_ci 1086cb93a386Sopenharmony_ci correct_bad_coords(abs(denom) < kTolerance, &fX, &fY, &fW); 1087cb93a386Sopenharmony_ci 1088cb93a386Sopenharmony_ci if (fUVRCount > 0) { 1089cb93a386Sopenharmony_ci // Calculate R here so it can be corrected with U and V in case it's needed later 1090cb93a386Sopenharmony_ci V4f e1u = skvx::shuffle<2, 3, 2, 3>(fU) - skvx::shuffle<0, 1, 0, 1>(fU); 1091cb93a386Sopenharmony_ci V4f e1v = skvx::shuffle<2, 3, 2, 3>(fV) - skvx::shuffle<0, 1, 0, 1>(fV); 1092cb93a386Sopenharmony_ci V4f e1r = skvx::shuffle<2, 3, 2, 3>(fR) - skvx::shuffle<0, 1, 0, 1>(fR); 1093cb93a386Sopenharmony_ci correct_bad_edges(e1Bad, &e1u, &e1v, &e1r); 1094cb93a386Sopenharmony_ci 1095cb93a386Sopenharmony_ci V4f e2u = skvx::shuffle<1, 1, 3, 3>(fU) - skvx::shuffle<0, 0, 2, 2>(fU); 1096cb93a386Sopenharmony_ci V4f e2v = skvx::shuffle<1, 1, 3, 3>(fV) - skvx::shuffle<0, 0, 2, 2>(fV); 1097cb93a386Sopenharmony_ci V4f e2r = skvx::shuffle<1, 1, 3, 3>(fR) - skvx::shuffle<0, 0, 2, 2>(fR); 1098cb93a386Sopenharmony_ci correct_bad_edges(e2Bad, &e2u, &e2v, &e2r); 1099cb93a386Sopenharmony_ci 1100cb93a386Sopenharmony_ci fU += a * e1u + b * e2u; 1101cb93a386Sopenharmony_ci fV += a * e1v + b * e2v; 1102cb93a386Sopenharmony_ci if (fUVRCount == 3) { 1103cb93a386Sopenharmony_ci fR += a * e1r + b * e2r; 1104cb93a386Sopenharmony_ci correct_bad_coords(abs(denom) < kTolerance, &fU, &fV, &fR); 1105cb93a386Sopenharmony_ci } else { 1106cb93a386Sopenharmony_ci correct_bad_coords(abs(denom) < kTolerance, &fU, &fV, nullptr); 1107cb93a386Sopenharmony_ci } 1108cb93a386Sopenharmony_ci } 1109cb93a386Sopenharmony_ci} 1110cb93a386Sopenharmony_ci 1111cb93a386Sopenharmony_ci//** TessellationHelper implementation 1112cb93a386Sopenharmony_ci 1113cb93a386Sopenharmony_civoid TessellationHelper::reset(const GrQuad& deviceQuad, const GrQuad* localQuad) { 1114cb93a386Sopenharmony_ci // Record basic state that isn't recorded on the Vertices struct itself 1115cb93a386Sopenharmony_ci fDeviceType = deviceQuad.quadType(); 1116cb93a386Sopenharmony_ci fLocalType = localQuad ? localQuad->quadType() : GrQuad::Type::kAxisAligned; 1117cb93a386Sopenharmony_ci 1118cb93a386Sopenharmony_ci // Reset metadata validity 1119cb93a386Sopenharmony_ci fOutsetRequestValid = false; 1120cb93a386Sopenharmony_ci fEdgeEquationsValid = false; 1121cb93a386Sopenharmony_ci 1122cb93a386Sopenharmony_ci // Compute vertex properties that are always needed for a quad, so no point in doing it lazily. 1123cb93a386Sopenharmony_ci fOriginal.reset(deviceQuad, localQuad); 1124cb93a386Sopenharmony_ci fEdgeVectors.reset(fOriginal.fX, fOriginal.fY, fOriginal.fW, fDeviceType); 1125cb93a386Sopenharmony_ci 1126cb93a386Sopenharmony_ci fVerticesValid = true; 1127cb93a386Sopenharmony_ci} 1128cb93a386Sopenharmony_ci 1129cb93a386Sopenharmony_ciV4f TessellationHelper::inset(const skvx::Vec<4, float>& edgeDistances, 1130cb93a386Sopenharmony_ci GrQuad* deviceInset, GrQuad* localInset) { 1131cb93a386Sopenharmony_ci SkASSERT(fVerticesValid); 1132cb93a386Sopenharmony_ci 1133cb93a386Sopenharmony_ci Vertices inset = fOriginal; 1134cb93a386Sopenharmony_ci const OutsetRequest& request = this->getOutsetRequest(edgeDistances); 1135cb93a386Sopenharmony_ci int vertexCount; 1136cb93a386Sopenharmony_ci if (request.fInsetDegenerate) { 1137cb93a386Sopenharmony_ci vertexCount = this->adjustDegenerateVertices(-request.fEdgeDistances, &inset); 1138cb93a386Sopenharmony_ci } else { 1139cb93a386Sopenharmony_ci this->adjustVertices(-request.fEdgeDistances, &inset); 1140cb93a386Sopenharmony_ci vertexCount = 4; 1141cb93a386Sopenharmony_ci } 1142cb93a386Sopenharmony_ci 1143cb93a386Sopenharmony_ci inset.asGrQuads(deviceInset, fDeviceType, localInset, fLocalType); 1144cb93a386Sopenharmony_ci if (vertexCount < 3) { 1145cb93a386Sopenharmony_ci // The interior has less than a full pixel's area so estimate reduced coverage using 1146cb93a386Sopenharmony_ci // the distance of the inset's projected corners to the original edges. 1147cb93a386Sopenharmony_ci return this->getEdgeEquations().estimateCoverage(inset.fX / inset.fW, 1148cb93a386Sopenharmony_ci inset.fY / inset.fW); 1149cb93a386Sopenharmony_ci } else { 1150cb93a386Sopenharmony_ci return 1.f; 1151cb93a386Sopenharmony_ci } 1152cb93a386Sopenharmony_ci} 1153cb93a386Sopenharmony_ci 1154cb93a386Sopenharmony_civoid TessellationHelper::outset(const skvx::Vec<4, float>& edgeDistances, 1155cb93a386Sopenharmony_ci GrQuad* deviceOutset, GrQuad* localOutset) { 1156cb93a386Sopenharmony_ci SkASSERT(fVerticesValid); 1157cb93a386Sopenharmony_ci 1158cb93a386Sopenharmony_ci Vertices outset = fOriginal; 1159cb93a386Sopenharmony_ci const OutsetRequest& request = this->getOutsetRequest(edgeDistances); 1160cb93a386Sopenharmony_ci if (request.fOutsetDegenerate) { 1161cb93a386Sopenharmony_ci this->adjustDegenerateVertices(request.fEdgeDistances, &outset); 1162cb93a386Sopenharmony_ci } else { 1163cb93a386Sopenharmony_ci this->adjustVertices(request.fEdgeDistances, &outset); 1164cb93a386Sopenharmony_ci } 1165cb93a386Sopenharmony_ci 1166cb93a386Sopenharmony_ci outset.asGrQuads(deviceOutset, fDeviceType, localOutset, fLocalType); 1167cb93a386Sopenharmony_ci} 1168cb93a386Sopenharmony_ci 1169cb93a386Sopenharmony_civoid TessellationHelper::getEdgeEquations(skvx::Vec<4, float>* a, 1170cb93a386Sopenharmony_ci skvx::Vec<4, float>* b, 1171cb93a386Sopenharmony_ci skvx::Vec<4, float>* c) { 1172cb93a386Sopenharmony_ci SkASSERT(a && b && c); 1173cb93a386Sopenharmony_ci SkASSERT(fVerticesValid); 1174cb93a386Sopenharmony_ci const EdgeEquations& eq = this->getEdgeEquations(); 1175cb93a386Sopenharmony_ci *a = eq.fA; 1176cb93a386Sopenharmony_ci *b = eq.fB; 1177cb93a386Sopenharmony_ci *c = eq.fC; 1178cb93a386Sopenharmony_ci} 1179cb93a386Sopenharmony_ci 1180cb93a386Sopenharmony_ciskvx::Vec<4, float> TessellationHelper::getEdgeLengths() { 1181cb93a386Sopenharmony_ci SkASSERT(fVerticesValid); 1182cb93a386Sopenharmony_ci return 1.f / fEdgeVectors.fInvLengths; 1183cb93a386Sopenharmony_ci} 1184cb93a386Sopenharmony_ci 1185cb93a386Sopenharmony_ciconst TessellationHelper::OutsetRequest& TessellationHelper::getOutsetRequest( 1186cb93a386Sopenharmony_ci const skvx::Vec<4, float>& edgeDistances) { 1187cb93a386Sopenharmony_ci // Much of the code assumes that we start from positive distances and apply it unmodified to 1188cb93a386Sopenharmony_ci // create an outset; knowing that it's outset simplifies degeneracy checking. 1189cb93a386Sopenharmony_ci SkASSERT(all(edgeDistances >= 0.f)); 1190cb93a386Sopenharmony_ci 1191cb93a386Sopenharmony_ci // Rebuild outset request if invalid or if the edge distances have changed. 1192cb93a386Sopenharmony_ci if (!fOutsetRequestValid || any(edgeDistances != fOutsetRequest.fEdgeDistances)) { 1193cb93a386Sopenharmony_ci fOutsetRequest.reset(fEdgeVectors, fDeviceType, edgeDistances); 1194cb93a386Sopenharmony_ci fOutsetRequestValid = true; 1195cb93a386Sopenharmony_ci } 1196cb93a386Sopenharmony_ci return fOutsetRequest; 1197cb93a386Sopenharmony_ci} 1198cb93a386Sopenharmony_ci 1199cb93a386Sopenharmony_cibool TessellationHelper::isSubpixel() { 1200cb93a386Sopenharmony_ci SkASSERT(fVerticesValid); 1201cb93a386Sopenharmony_ci if (fDeviceType <= GrQuad::Type::kRectilinear) { 1202cb93a386Sopenharmony_ci // Check the edge lengths, if the shortest is less than 1px it's degenerate, which is the 1203cb93a386Sopenharmony_ci // same as if the max 1/length is greater than 1px. 1204cb93a386Sopenharmony_ci return any(fEdgeVectors.fInvLengths > 1.f); 1205cb93a386Sopenharmony_ci } else { 1206cb93a386Sopenharmony_ci // Compute edge equations and then distance from each vertex to the opposite edges. 1207cb93a386Sopenharmony_ci return this->getEdgeEquations().isSubpixel(fEdgeVectors.fX2D, fEdgeVectors.fY2D); 1208cb93a386Sopenharmony_ci } 1209cb93a386Sopenharmony_ci} 1210cb93a386Sopenharmony_ci 1211cb93a386Sopenharmony_ciconst TessellationHelper::EdgeEquations& TessellationHelper::getEdgeEquations() { 1212cb93a386Sopenharmony_ci if (!fEdgeEquationsValid) { 1213cb93a386Sopenharmony_ci fEdgeEquations.reset(fEdgeVectors); 1214cb93a386Sopenharmony_ci fEdgeEquationsValid = true; 1215cb93a386Sopenharmony_ci } 1216cb93a386Sopenharmony_ci return fEdgeEquations; 1217cb93a386Sopenharmony_ci} 1218cb93a386Sopenharmony_ci 1219cb93a386Sopenharmony_civoid TessellationHelper::adjustVertices(const skvx::Vec<4, float>& signedEdgeDistances, 1220cb93a386Sopenharmony_ci Vertices* vertices) { 1221cb93a386Sopenharmony_ci SkASSERT(vertices); 1222cb93a386Sopenharmony_ci SkASSERT(vertices->fUVRCount == 0 || vertices->fUVRCount == 2 || vertices->fUVRCount == 3); 1223cb93a386Sopenharmony_ci 1224cb93a386Sopenharmony_ci if (fDeviceType < GrQuad::Type::kPerspective) { 1225cb93a386Sopenharmony_ci // For non-perspective, non-degenerate quads, moveAlong is correct and most efficient 1226cb93a386Sopenharmony_ci vertices->moveAlong(fEdgeVectors, signedEdgeDistances); 1227cb93a386Sopenharmony_ci } else { 1228cb93a386Sopenharmony_ci // For perspective, non-degenerate quads, use moveAlong for the projected points and then 1229cb93a386Sopenharmony_ci // reconstruct Ws with moveTo. 1230cb93a386Sopenharmony_ci Vertices projected = { fEdgeVectors.fX2D, fEdgeVectors.fY2D, /*w*/ 1.f, 0.f, 0.f, 0.f, 0 }; 1231cb93a386Sopenharmony_ci projected.moveAlong(fEdgeVectors, signedEdgeDistances); 1232cb93a386Sopenharmony_ci vertices->moveTo(projected.fX, projected.fY, signedEdgeDistances != 0.f); 1233cb93a386Sopenharmony_ci } 1234cb93a386Sopenharmony_ci} 1235cb93a386Sopenharmony_ci 1236cb93a386Sopenharmony_ciint TessellationHelper::adjustDegenerateVertices(const skvx::Vec<4, float>& signedEdgeDistances, 1237cb93a386Sopenharmony_ci Vertices* vertices) { 1238cb93a386Sopenharmony_ci SkASSERT(vertices); 1239cb93a386Sopenharmony_ci SkASSERT(vertices->fUVRCount == 0 || vertices->fUVRCount == 2 || vertices->fUVRCount == 3); 1240cb93a386Sopenharmony_ci 1241cb93a386Sopenharmony_ci if (fDeviceType <= GrQuad::Type::kRectilinear) { 1242cb93a386Sopenharmony_ci // For rectilinear, degenerate quads, can use moveAlong if the edge distances are adjusted 1243cb93a386Sopenharmony_ci // to not cross over each other. 1244cb93a386Sopenharmony_ci SkASSERT(all(signedEdgeDistances <= 0.f)); // Only way rectilinear can degenerate is insets 1245cb93a386Sopenharmony_ci V4f halfLengths = -0.5f / next_cw(fEdgeVectors.fInvLengths); // Negate to inset 1246cb93a386Sopenharmony_ci M4f crossedEdges = halfLengths > signedEdgeDistances; 1247cb93a386Sopenharmony_ci V4f safeInsets = if_then_else(crossedEdges, halfLengths, signedEdgeDistances); 1248cb93a386Sopenharmony_ci vertices->moveAlong(fEdgeVectors, safeInsets); 1249cb93a386Sopenharmony_ci 1250cb93a386Sopenharmony_ci // A degenerate rectilinear quad is either a point (both w and h crossed), or a line 1251cb93a386Sopenharmony_ci return all(crossedEdges) ? 1 : 2; 1252cb93a386Sopenharmony_ci } else { 1253cb93a386Sopenharmony_ci // Degenerate non-rectangular shape, must go through slowest path (which automatically 1254cb93a386Sopenharmony_ci // handles perspective). 1255cb93a386Sopenharmony_ci V4f x2d = fEdgeVectors.fX2D; 1256cb93a386Sopenharmony_ci V4f y2d = fEdgeVectors.fY2D; 1257cb93a386Sopenharmony_ci 1258cb93a386Sopenharmony_ci M4f aaMask; 1259cb93a386Sopenharmony_ci int vertexCount = this->getEdgeEquations().computeDegenerateQuad(signedEdgeDistances, 1260cb93a386Sopenharmony_ci &x2d, &y2d, &aaMask); 1261cb93a386Sopenharmony_ci vertices->moveTo(x2d, y2d, aaMask); 1262cb93a386Sopenharmony_ci return vertexCount; 1263cb93a386Sopenharmony_ci } 1264cb93a386Sopenharmony_ci} 1265cb93a386Sopenharmony_ci 1266cb93a386Sopenharmony_ci}; // namespace GrQuadUtils 1267