1 /* 2 * Copyright 2018 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "GrQuad.h" 9 10 #include "GrTypesPriv.h" 11 12 /////////////////////////////////////////////////////////////////////////////////////////////////// 13 // Functions for identifying the quad type from its coordinates, which are kept debug-only since 14 // production code should rely on the matrix to derive the quad type more efficiently. These are 15 // useful in asserts that the quad type is as expected. 16 /////////////////////////////////////////////////////////////////////////////////////////////////// 17 18 #ifdef SK_DEBUG 19 // Allow some tolerance from floating point matrix transformations, but SkScalarNearlyEqual doesn't 20 // support comparing infinity, and coords_form_rect should return true for infinite edges 21 #define NEARLY_EQUAL(f1, f2) (f1 == f2 || SkScalarNearlyEqual(f1, f2, 1e-5f)) 22 // Similarly, support infinite rectangles by looking at the sign of infinities 23 static bool dot_nearly_zero(const SkVector& e1, const SkVector& e2) { 24 static constexpr auto dot = SkPoint::DotProduct; 25 static constexpr auto sign = SkScalarSignAsScalar; 26 27 SkScalar dotValue = dot(e1, e2); 28 if (SkScalarIsNaN(dotValue)) { 29 // Form vectors from the signs of infinities, and check their dot product 30 dotValue = dot({sign(e1.fX), sign(e1.fY)}, {sign(e2.fX), sign(e2.fY)}); 31 } 32 33 return SkScalarNearlyZero(dotValue, 1e-3f); 34 } 35 36 // This is not the most performance critical function; code using GrQuad should rely on the faster 37 // quad type from matrix path, so this will only be called as part of SkASSERT. 38 static bool coords_form_rect(const float xs[4], const float ys[4]) { 39 return (NEARLY_EQUAL(xs[0], xs[1]) && NEARLY_EQUAL(xs[2], xs[3]) && 40 NEARLY_EQUAL(ys[0], ys[2]) && NEARLY_EQUAL(ys[1], ys[3])) || 41 (NEARLY_EQUAL(xs[0], xs[2]) && NEARLY_EQUAL(xs[1], xs[3]) && 42 NEARLY_EQUAL(ys[0], ys[1]) && NEARLY_EQUAL(ys[2], ys[3])); 43 } 44 45 static bool coords_rectilinear(const float xs[4], const float ys[4]) { 46 SkVector e0{xs[1] - xs[0], ys[1] - ys[0]}; // connects to e1 and e2(repeat) 47 SkVector e1{xs[3] - xs[1], ys[3] - ys[1]}; // connects to e0(repeat) and e3 48 SkVector e2{xs[0] - xs[2], ys[0] - ys[2]}; // connects to e0 and e3(repeat) 49 SkVector e3{xs[2] - xs[3], ys[2] - ys[3]}; // connects to e1(repeat) and e2 50 51 e0.normalize(); 52 e1.normalize(); 53 e2.normalize(); 54 e3.normalize(); 55 56 return dot_nearly_zero(e0, e1) && dot_nearly_zero(e1, e3) && 57 dot_nearly_zero(e2, e0) && dot_nearly_zero(e3, e2); 58 } 59 60 GrQuadType GrQuad::quadType() const { 61 // Since GrQuad applies any perspective information at construction time, there's only two 62 // types to choose from. 63 if (coords_form_rect(fX, fY)) { 64 return GrQuadType::kRect; 65 } else if (coords_rectilinear(fX, fY)) { 66 return GrQuadType::kRectilinear; 67 } else { 68 return GrQuadType::kStandard; 69 } 70 } 71 72 GrQuadType GrPerspQuad::quadType() const { 73 if (this->hasPerspective()) { 74 return GrQuadType::kPerspective; 75 } else { 76 // Rect or standard quad, can ignore w since they are all ones 77 if (coords_form_rect(fX, fY)) { 78 return GrQuadType::kRect; 79 } else if (coords_rectilinear(fX, fY)) { 80 return GrQuadType::kRectilinear; 81 } else { 82 return GrQuadType::kStandard; 83 } 84 } 85 } 86 #endif 87 88 /////////////////////////////////////////////////////////////////////////////////////////////////// 89 90 static bool aa_affects_rect(float ql, float qt, float qr, float qb) { 91 return !SkScalarIsInt(ql) || !SkScalarIsInt(qr) || !SkScalarIsInt(qt) || !SkScalarIsInt(qb); 92 } 93 94 static void map_rect_translate_scale(const SkRect& rect, const SkMatrix& m, 95 Sk4f* xs, Sk4f* ys) { 96 SkMatrix::TypeMask tm = m.getType(); 97 SkASSERT(tm <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)); 98 99 Sk4f r = Sk4f::Load(&rect); 100 if (tm > SkMatrix::kIdentity_Mask) { 101 const Sk4f t(m.getTranslateX(), m.getTranslateY(), m.getTranslateX(), m.getTranslateY()); 102 if (tm <= SkMatrix::kTranslate_Mask) { 103 r += t; 104 } else { 105 const Sk4f s(m.getScaleX(), m.getScaleY(), m.getScaleX(), m.getScaleY()); 106 r = r * s + t; 107 } 108 } 109 *xs = SkNx_shuffle<0, 0, 2, 2>(r); 110 *ys = SkNx_shuffle<1, 3, 1, 3>(r); 111 } 112 113 static void map_quad_general(const Sk4f& qx, const Sk4f& qy, const SkMatrix& m, 114 Sk4f* xs, Sk4f* ys, Sk4f* ws) { 115 static constexpr auto fma = SkNx_fma<4, float>; 116 *xs = fma(m.getScaleX(), qx, fma(m.getSkewX(), qy, m.getTranslateX())); 117 *ys = fma(m.getSkewY(), qx, fma(m.getScaleY(), qy, m.getTranslateY())); 118 if (m.hasPerspective()) { 119 Sk4f w = fma(m.getPerspX(), qx, fma(m.getPerspY(), qy, m.get(SkMatrix::kMPersp2))); 120 if (ws) { 121 // Output the calculated w coordinates 122 *ws = w; 123 } else { 124 // Apply perspective division immediately 125 Sk4f iw = w.invert(); 126 *xs *= iw; 127 *ys *= iw; 128 } 129 } else if (ws) { 130 *ws = 1.f; 131 } 132 } 133 134 static void map_rect_general(const SkRect& rect, const SkMatrix& matrix, 135 Sk4f* xs, Sk4f* ys, Sk4f* ws) { 136 Sk4f rx(rect.fLeft, rect.fLeft, rect.fRight, rect.fRight); 137 Sk4f ry(rect.fTop, rect.fBottom, rect.fTop, rect.fBottom); 138 map_quad_general(rx, ry, matrix, xs, ys, ws); 139 } 140 141 // Rearranges (top-left, top-right, bottom-right, bottom-left) ordered skQuadPts into xs and ys 142 // ordered (top-left, bottom-left, top-right, bottom-right) 143 static void rearrange_sk_to_gr_points(const SkPoint skQuadPts[4], Sk4f* xs, Sk4f* ys) { 144 *xs = Sk4f(skQuadPts[0].fX, skQuadPts[3].fX, skQuadPts[1].fX, skQuadPts[2].fX); 145 *ys = Sk4f(skQuadPts[0].fY, skQuadPts[3].fY, skQuadPts[1].fY, skQuadPts[2].fY); 146 } 147 148 template <typename Q> 149 void GrResolveAATypeForQuad(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags, 150 const Q& quad, GrQuadType knownType, 151 GrAAType* outAAType, GrQuadAAFlags* outEdgeFlags) { 152 // Most cases will keep the requested types unchanged 153 *outAAType = requestedAAType; 154 *outEdgeFlags = requestedEdgeFlags; 155 156 switch (requestedAAType) { 157 // When aa type is coverage, disable AA if the edge configuration doesn't actually need it 158 case GrAAType::kCoverage: 159 if (requestedEdgeFlags == GrQuadAAFlags::kNone) { 160 // Turn off anti-aliasing 161 *outAAType = GrAAType::kNone; 162 } else { 163 // For coverage AA, if the quad is a rect and it lines up with pixel boundaries 164 // then overall aa and per-edge aa can be completely disabled 165 if (knownType == GrQuadType::kRect && !quad.aaHasEffectOnRect()) { 166 *outAAType = GrAAType::kNone; 167 *outEdgeFlags = GrQuadAAFlags::kNone; 168 } 169 } 170 break; 171 // For no or msaa anti aliasing, override the edge flags since edge flags only make sense 172 // when coverage aa is being used. 173 case GrAAType::kNone: 174 *outEdgeFlags = GrQuadAAFlags::kNone; 175 break; 176 case GrAAType::kMSAA: 177 *outEdgeFlags = GrQuadAAFlags::kAll; 178 break; 179 case GrAAType::kMixedSamples: 180 SK_ABORT("Should not use mixed sample AA with edge AA flags"); 181 break; 182 } 183 }; 184 185 // Instantiate GrResolve... for GrQuad and GrPerspQuad 186 template void GrResolveAATypeForQuad(GrAAType, GrQuadAAFlags, const GrQuad&, GrQuadType, 187 GrAAType*, GrQuadAAFlags*); 188 template void GrResolveAATypeForQuad(GrAAType, GrQuadAAFlags, const GrPerspQuad&, GrQuadType, 189 GrAAType*, GrQuadAAFlags*); 190 191 GrQuadType GrQuadTypeForTransformedRect(const SkMatrix& matrix) { 192 if (matrix.rectStaysRect()) { 193 return GrQuadType::kRect; 194 } else if (matrix.preservesRightAngles()) { 195 return GrQuadType::kRectilinear; 196 } else if (matrix.hasPerspective()) { 197 return GrQuadType::kPerspective; 198 } else { 199 return GrQuadType::kStandard; 200 } 201 } 202 203 GrQuad GrQuad::MakeFromRect(const SkRect& rect, const SkMatrix& m) { 204 Sk4f x, y; 205 SkMatrix::TypeMask tm = m.getType(); 206 if (tm <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) { 207 map_rect_translate_scale(rect, m, &x, &y); 208 } else { 209 map_rect_general(rect, m, &x, &y, nullptr); 210 } 211 return GrQuad(x, y); 212 } 213 214 GrQuad GrQuad::MakeFromSkQuad(const SkPoint pts[4], const SkMatrix& matrix) { 215 Sk4f xs, ys; 216 rearrange_sk_to_gr_points(pts, &xs, &ys); 217 if (matrix.isIdentity()) { 218 return GrQuad(xs, ys); 219 } else { 220 Sk4f mx, my; 221 map_quad_general(xs, ys, matrix, &mx, &my, nullptr); 222 return GrQuad(mx, my); 223 } 224 } 225 226 bool GrQuad::aaHasEffectOnRect() const { 227 SkASSERT(this->quadType() == GrQuadType::kRect); 228 return aa_affects_rect(fX[0], fY[0], fX[3], fY[3]); 229 } 230 231 // Private constructor used by GrQuadList to quickly fill in a quad's values from the channel arrays 232 GrPerspQuad::GrPerspQuad(const float* xs, const float* ys, const float* ws) { 233 memcpy(fX, xs, 4 * sizeof(float)); 234 memcpy(fY, ys, 4 * sizeof(float)); 235 memcpy(fW, ws, 4 * sizeof(float)); 236 } 237 238 GrPerspQuad GrPerspQuad::MakeFromRect(const SkRect& rect, const SkMatrix& m) { 239 Sk4f x, y, w; 240 SkMatrix::TypeMask tm = m.getType(); 241 if (tm <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) { 242 map_rect_translate_scale(rect, m, &x, &y); 243 w = 1.f; 244 } else { 245 map_rect_general(rect, m, &x, &y, &w); 246 } 247 return GrPerspQuad(x, y, w); 248 } 249 250 GrPerspQuad GrPerspQuad::MakeFromSkQuad(const SkPoint pts[4], const SkMatrix& matrix) { 251 Sk4f xs, ys; 252 rearrange_sk_to_gr_points(pts, &xs, &ys); 253 if (matrix.isIdentity()) { 254 return GrPerspQuad(xs, ys, 1.f); 255 } else { 256 Sk4f mx, my, mw; 257 map_quad_general(xs, ys, matrix, &mx, &my, &mw); 258 return GrPerspQuad(mx, my, mw); 259 } 260 } 261 262 bool GrPerspQuad::aaHasEffectOnRect() const { 263 SkASSERT(this->quadType() == GrQuadType::kRect); 264 // If rect, ws must all be 1s so no need to divide 265 return aa_affects_rect(fX[0], fY[0], fX[3], fY[3]); 266 } 267