/* * Copyright 2015 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef GrQuad_DEFINED #define GrQuad_DEFINED #include "include/core/SkMatrix.h" #include "include/core/SkPoint.h" #include "include/core/SkPoint3.h" #include "include/private/SkVx.h" /** * GrQuad is a collection of 4 points which can be used to represent an arbitrary quadrilateral. The * points make a triangle strip with CCW triangles (top-left, bottom-left, top-right, bottom-right). */ class GrQuad { public: // Quadrilaterals can be classified in several useful ways that assist AA tessellation and other // analysis when drawing, in particular, knowing if it was originally a rectangle transformed by // certain types of matrices: enum class Type { // The 4 points remain an axis-aligned rectangle; their logical indices may not respect // TL, BL, TR, BR ordering if the transform was a 90 degre rotation or mirror. kAxisAligned, // The 4 points represent a rectangle subjected to a rotation, its corners are right angles. kRectilinear, // Arbitrary 2D quadrilateral; may have been a rectangle transformed with skew or some // clipped polygon. Its w coordinates will all be 1. kGeneral, // Even more general-purpose than kGeneral, this allows the w coordinates to be non-unity. kPerspective, kLast = kPerspective }; static const int kTypeCount = static_cast(Type::kLast) + 1; GrQuad() = default; explicit GrQuad(const SkRect& rect) : fX{rect.fLeft, rect.fLeft, rect.fRight, rect.fRight} , fY{rect.fTop, rect.fBottom, rect.fTop, rect.fBottom} , fW{1.f, 1.f, 1.f, 1.f} , fType(Type::kAxisAligned) {} GrQuad(const skvx::Vec<4, float>& xs, const skvx::Vec<4, float>& ys, Type type) : fType(type) { SkASSERT(type != Type::kPerspective); xs.store(fX); ys.store(fY); fW[0] = fW[1] = fW[2] = fW[3] = 1.f; } GrQuad(const skvx::Vec<4, float>& xs, const skvx::Vec<4, float>& ys, const skvx::Vec<4, float>& ws, Type type) : fType(type) { xs.store(fX); ys.store(fY); ws.store(fW); } // Copy 4 values from each of the arrays into the quad's components GrQuad(const float xs[4], const float ys[4], const float ws[4], Type type) : fType(type) { memcpy(fX, xs, 4 * sizeof(float)); memcpy(fY, ys, 4 * sizeof(float)); memcpy(fW, ws, 4 * sizeof(float)); } static GrQuad MakeFromRect(const SkRect&, const SkMatrix&); // Creates a GrQuad from the quadrilateral 'pts', transformed by the matrix. The input // points array is arranged as per SkRect::toQuad (top-left, top-right, bottom-right, // bottom-left). The returned instance's point order will still be CCW tri-strip order. static GrQuad MakeFromSkQuad(const SkPoint pts[4], const SkMatrix&); GrQuad& operator=(const GrQuad&) = default; SkPoint3 point3(int i) const { return {fX[i], fY[i], fW[i]}; } SkPoint point(int i) const { if (fType == Type::kPerspective) { return {fX[i] / fW[i], fY[i] / fW[i]}; } else { return {fX[i], fY[i]}; } } SkRect bounds() const { auto x = this->x4f(); auto y = this->y4f(); if (fType == Type::kPerspective) { auto iw = this->iw4f(); x *= iw; y *= iw; } return {min(x), min(y), max(x), max(y)}; } bool isFinite() const { // If any coordinate is infinity or NaN, then multiplying it with 0 will make accum NaN float accum = 0; for (int i = 0; i < 4; ++i) { accum *= fX[i]; accum *= fY[i]; accum *= fW[i]; } SkASSERT(0 == accum || SkScalarIsNaN(accum)); return !SkScalarIsNaN(accum); } float x(int i) const { return fX[i]; } float y(int i) const { return fY[i]; } float w(int i) const { return fW[i]; } float iw(int i) const { return sk_ieee_float_divide(1.f, fW[i]); } skvx::Vec<4, float> x4f() const { return skvx::Vec<4, float>::Load(fX); } skvx::Vec<4, float> y4f() const { return skvx::Vec<4, float>::Load(fY); } skvx::Vec<4, float> w4f() const { return skvx::Vec<4, float>::Load(fW); } skvx::Vec<4, float> iw4f() const { return 1.f / this->w4f(); } Type quadType() const { return fType; } bool hasPerspective() const { return fType == Type::kPerspective; } // True if anti-aliasing affects this quad. Only valid when quadType == kAxisAligned bool aaHasEffectOnRect() const; // True if this quad is axis-aligned and still has its top-left corner at v0. Equivalently, // quad == GrQuad(quad->bounds()). Axis-aligned quads with flips and rotations may exactly // fill their bounds, but their vertex order will not match TL BL TR BR anymore. bool asRect(SkRect* rect) const; // The non-const pointers are provided to support modifying a GrQuad in-place, but care must be // taken to keep its quad type aligned with the geometric nature of the new coordinates. This is // no different than using the constructors that accept a quad type. const float* xs() const { return fX; } float* xs() { return fX; } const float* ys() const { return fY; } float* ys() { return fY; } const float* ws() const { return fW; } float* ws() { return fW; } void setQuadType(Type newType) { fType = newType; } private: template friend class GrQuadListBase; // for access to fX, fY, fW float fX[4]; float fY[4]; float fW[4]; Type fType; }; #endif