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