Add quad type and persp/aa utilities to GrQuad

This refactor makes some of the quad logic in GrTextureOp available for
use with other quad GrOps.

Bug: skia:
Change-Id: I1c173cfdf61b33c8422ddd8b91406a970a1c8e5d
Reviewed-on: https://skia-review.googlesource.com/c/163253
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Michael Ludwig 2018-10-19 09:36:57 -04:00 committed by Skia Commit-Bot
parent 12956725cc
commit 1f7e4385d2
3 changed files with 103 additions and 15 deletions

View File

@ -7,6 +7,59 @@
#include "GrQuad.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// Functions for identifying the quad type from its coordinates, which are kept debug-only since
// production code should rely on the matrix to derive the quad type more efficiently. These are
// useful in asserts that the quad type is as expected.
///////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef SK_DEBUG
// Allow some tolerance from floating point matrix transformations, but SkScalarNearlyEqual doesn't
// support comparing infinity, and coords_form_rect should return true for infinite edges
#define NEARLY_EQUAL(f1, f2) (f1 == f2 || SkScalarNearlyEqual(f1, f2, 1e-5f))
// This is not the most performance critical function; code using GrQuad should rely on the faster
// quad type from matrix path, so this will only be called as part of SkASSERT.
static bool coords_form_rect(const float xs[4], const float ys[4]) {
return (NEARLY_EQUAL(xs[0], xs[1]) && NEARLY_EQUAL(xs[2], xs[3]) &&
NEARLY_EQUAL(ys[0], ys[2]) && NEARLY_EQUAL(ys[1], ys[3])) ||
(NEARLY_EQUAL(xs[0], xs[2]) && NEARLY_EQUAL(xs[1], xs[3]) &&
NEARLY_EQUAL(ys[0], ys[1]) && NEARLY_EQUAL(ys[2], ys[3]));
}
GrQuadType GrQuad::quadType() const {
// Since GrQuad applies any perspective information at construction time, there's only two
// types to choose from.
return coords_form_rect(fX, fY) ? GrQuadType::kRect_QuadType : GrQuadType::kStandard_QuadType;
}
GrQuadType GrPerspQuad::quadType() const {
if (this->hasPerspective()) {
return GrQuadType::kPerspective_QuadType;
} else {
// Rect or standard quad, can ignore w since they are all ones
return coords_form_rect(fX, fY) ? GrQuadType::kRect_QuadType
: GrQuadType::kStandard_QuadType;
}
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
static bool aa_affects_rect(float ql, float qt, float qr, float qb) {
return !SkScalarIsInt(ql) || !SkScalarIsInt(qr) || !SkScalarIsInt(qt) || !SkScalarIsInt(qb);
}
GrQuadType GrQuadTypeForTransformedRect(const SkMatrix& matrix) {
if (matrix.rectStaysRect()) {
return GrQuadType::kRect_QuadType;
} else if (matrix.hasPerspective()) {
return GrQuadType::kPerspective_QuadType;
} else {
return GrQuadType::kStandard_QuadType;
}
}
GrQuad::GrQuad(const SkRect& rect, const SkMatrix& m) {
SkMatrix::TypeMask tm = m.getType();
if (tm <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
@ -44,6 +97,11 @@ GrQuad::GrQuad(const SkRect& rect, const SkMatrix& m) {
}
}
bool GrQuad::aaHasEffectOnRect() const {
SkASSERT(this->quadType() == GrQuadType::kRect_QuadType);
return aa_affects_rect(fX[0], fY[0], fX[3], fY[3]);
}
GrPerspQuad::GrPerspQuad(const SkRect& rect, const SkMatrix& m) {
SkMatrix::TypeMask tm = m.getType();
if (tm <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
@ -83,3 +141,9 @@ GrPerspQuad::GrPerspQuad(const SkRect& rect, const SkMatrix& m) {
}
}
}
bool GrPerspQuad::aaHasEffectOnRect() const {
SkASSERT(this->quadType() == GrQuadType::kRect_QuadType);
// If rect, ws must all be 1s so no need to divide
return aa_affects_rect(fX[0], fY[0], fX[3], fY[3]);
}

View File

@ -13,6 +13,24 @@
#include "SkPoint.h"
#include "SkPoint3.h"
// Rectangles transformed by matrices (view or local) can be classified in three ways:
// 1. Stays a rectangle - the matrix rectStaysRect() is true, or x(0) == x(1) && x(2) == x(3)
// and y(0) == y(2) && y(1) == y(3). Or under mirrors, x(0) == x(2) && x(1) == x(3) and
// y(0) == y(1) && y(2) == y(3).
// 2. Is a quadrilateral - the matrix does not have perspective, but may rotate or skew, or
// ws() == all ones.
// 3. Is a perspective quad - the matrix has perspective, subsuming all previous quad types.
enum class GrQuadType {
kRect_QuadType,
kStandard_QuadType,
kPerspective_QuadType
};
// If an SkRect is transformed by this matrix, what class of quad is required to represent it. Since
// quadType() is only provided on Gr[Persp]Quad in debug builds, production code should use this
// to efficiently determine quad types.
GrQuadType GrQuadTypeForTransformedRect(const SkMatrix& matrix);
/**
* 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).
@ -49,6 +67,13 @@ public:
Sk4f x4f() const { return Sk4f::Load(fX); }
Sk4f y4f() const { return Sk4f::Load(fY); }
// True if anti-aliasing affects this quad. Requires quadType() == kRect_QuadType
bool aaHasEffectOnRect() const;
#ifdef SK_DEBUG
GrQuadType quadType() const;
#endif
private:
float fX[4];
float fY[4];
@ -80,6 +105,15 @@ public:
Sk4f w4f() const { return Sk4f::Load(fW); }
Sk4f iw4f() const { return Sk4f::Load(fIW); }
bool hasPerspective() const { return (w4f() != Sk4f(1.f)).anyTrue(); }
// True if anti-aliasing affects this quad. Requires quadType() == kRect_QuadType
bool aaHasEffectOnRect() const;
#ifdef SK_DEBUG
GrQuadType quadType() const;
#endif
private:
float fX[4];
float fY[4];

View File

@ -351,7 +351,7 @@ public:
GrQuadAAFlags aaFlags, const SkRect& texRect) {
// Should be kNone for non-AA and kAll for MSAA.
SkASSERT(aaFlags == GrQuadAAFlags::kNone || aaFlags == GrQuadAAFlags::kAll);
SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
SkASSERT(!quad.hasPerspective());
SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
for (int i = 0; i < 4; ++i) {
vertices[i].fPosition = {quad.x(i), quad.y(i)};
@ -376,7 +376,7 @@ template<typename V> class VertexAAHandler<V, GrAA::kYes, SkPoint> {
public:
static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
GrQuadAAFlags aaFlags, const SkRect& texRect) {
SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
SkASSERT(!quad.hasPerspective());
if (aaFlags == GrQuadAAFlags::kNone) {
for (int i = 0; i < 4; ++i) {
vertices[i].fPosition = {quad.x(i), quad.y(i)};
@ -593,17 +593,8 @@ static void tessellate_quad(const GrPerspQuad& devQuad, GrQuadAAFlags aaFlags,
DomainAssigner<V>::Assign(vertices, domain, filter, srcRect, origin, iw, ih);
}
static bool aa_has_effect_for_rect_stays_rect(const GrPerspQuad& quad) {
SkASSERT((quad.w4f() == Sk4f(1)).allTrue());
float ql = quad.x(0);
float qt = quad.y(0);
float qr = quad.x(3);
float qb = quad.y(3);
return !SkScalarIsInt(ql) || !SkScalarIsInt(qr) || !SkScalarIsInt(qt) || !SkScalarIsInt(qb);
}
static bool filter_has_effect_for_rect_stays_rect(const GrPerspQuad& quad, const SkRect& srcRect) {
SkASSERT((quad.w4f() == Sk4f(1)).allTrue());
SkASSERT(quad.quadType() == GrQuadType::kRect_QuadType);
float ql = quad.x(0);
float qt = quad.y(0);
float qr = quad.x(3);
@ -751,7 +742,7 @@ private:
SkASSERT(!srcRect.contains(proxy->getWorstCaseBoundsRect()) ||
constraint == SkCanvas::kFast_SrcRectConstraint);
if (viewMatrix.rectStaysRect()) {
if (this->aaType() == GrAAType::kCoverage && !aa_has_effect_for_rect_stays_rect(quad)) {
if (this->aaType() == GrAAType::kCoverage && !quad.aaHasEffectOnRect()) {
fAAType = static_cast<unsigned>(GrAAType::kNone);
aaFlags = GrQuadAAFlags::kNone;
}
@ -811,8 +802,7 @@ private:
break;
case GrAAType::kCoverage:
if (rectStaysRect) {
if (aaFlags != GrQuadAAFlags::kNone &&
!aa_has_effect_for_rect_stays_rect(quad)) {
if (aaFlags != GrQuadAAFlags::kNone && !quad.aaHasEffectOnRect()) {
aaFlags = GrQuadAAFlags::kNone;
}
}