blob: ed353001e48d3ea3eda10e41b939e6ba009fb7a9 [file] [log] [blame]
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef skgpu_graphite_geom_Transform_DEFINED
#define skgpu_graphite_geom_Transform_DEFINED
#include "include/core/SkM44.h"
namespace skgpu::graphite {
class Rect;
// Transform encapsulates an SkM44 matrix, its inverse, and other properties dependent on the
// original matrix value that are useful when rendering.
class Transform {
public:
// Type classifies the transform into coarse categories so that certain optimizations or
// properties can be queried efficiently
enum class Type : unsigned {
// Applying the matrix to a vector or point is a no-op, so could be skipped entirely.
kIdentity,
// The matrix transforms a rect to another rect, without mirrors or rotations, so both
// pre-and-post transform coordinates can be exactly represented as rects.
kSimpleRectStaysRect,
// The matrix transforms a rect to another rect, but may mirror or rotate the corners
// relative to each other. This means that the post-transformed rect completely fills
// that space.
kRectStaysRect,
// The matrix transform may have skew or rotation, so a mapped rect does not fill space,
// but there is no need to perform perspective division or w-plane clipping. This also
// includes orthographic projections.
kAffine,
// The matrix includes perspective and requires further projection to 2D, so care must be
// taken when w is less than or near 0, and homogeneous division and perspective-correct
// interpolation are needed when rendering.
kPerspective,
// The matrix is not invertible or not finite, so should not be used to draw.
kInvalid,
};
explicit Transform(const SkM44& m);
Transform(const Transform& t) = default;
static constexpr Transform Identity() {
return Transform(SkM44(), SkM44(), Type::kIdentity, 1.f, 1.f);
}
static constexpr Transform Invalid() {
return Transform(SkM44(SkM44::kNaN_Constructor), SkM44(SkM44::kNaN_Constructor),
Type::kInvalid, 1.f, 1.f);
}
static inline Transform Translate(float x, float y) {
if (x == 0.f && y == 0.f) {
return Identity();
} else if (SkScalarsAreFinite(x, y)) {
return Transform(SkM44::Translate(x, y), SkM44::Translate(-x, -y),
Type::kSimpleRectStaysRect, 1.f, 1.f);
} else {
return Invalid();
}
}
static inline Transform Inverse(const Transform& t) {
return Transform(t.fInvM, t.fM, t.fType, 1.f / t.fMaxScaleFactor, 1.f / t.fMinScaleFactor);
}
Transform& operator=(const Transform& t) = default;
operator const SkM44&() const { return fM; }
operator SkMatrix() const { return fM.asM33(); }
bool operator!=(const Transform& t) const { return !(*this == t); }
bool operator==(const Transform& t) const {
return this->valid() == t.valid() && (!this->valid() || fM == t.fM);
}
const SkM44& matrix() const { return fM; }
const SkM44& inverse() const { return fInvM; }
Type type() const { return fType; }
bool valid() const { return fType != Type::kInvalid; }
// Return the {min,max} scale factor at the pre-transformed location 'p'. A unit circle about
// 'p' transformed by this Transform will be contained in an ellipse with radii equal to 'min'
// and 'max', e.g. moving 1 local unit will move at least 'min' pixels and at most 'max' pixels
std::pair<float, float> scaleFactors(const SkV2& p) const;
// This is valid for non-projection types and 1.0 for projection matrices.
float maxScaleFactor() const {
SkASSERT(this->valid());
return fMaxScaleFactor;
}
// Return the minimum distance needed to move in local (pre-transform) space to ensure that the
// transformed coordinates are at least 1px away from the original mapped point. This minimum
// distance is specific to the given local 'bounds' since the scale factors change with
// perspective.
//
// If the bounds would be clipped by the w=0 plane or otherwise is ill-conditioned, this will
// return positive infinity.
float localAARadius(const Rect& bounds) const;
Rect mapRect(const Rect& rect) const;
Rect inverseMapRect(const Rect& rect) const;
void mapPoints(const Rect& localRect, SkV4 deviceOut[4]) const;
void mapPoints(const SkV2* localIn, SkV4* deviceOut, int count) const;
void mapPoints(const SkV4* localIn, SkV4* deviceOut, int count) const;
void inverseMapPoints(const SkV4* deviceIn, SkV4* localOut, int count) const;
// Returns a transform equal to the pre- or post-translation of this matrix
Transform preTranslate(float x, float y) const {
return this->concat(SkM44::Translate(x, y));
}
Transform postTranslate(float x, float y) const {
return Translate(x, y).concat(*this);
}
// Returns a transform equal to (this * t)
Transform concat(const Transform& t) const {
SkASSERT(this->valid());
return Transform(fM * t.fM);
}
Transform concat(const SkM44& t) const {
SkASSERT(this->valid());
return Transform(fM * t);
}
// Returns a transform equal to (this * t^-1)
Transform concatInverse(const Transform& t) const {
SkASSERT(this->valid());
return Transform(fM * t.fInvM);
}
Transform concatInverse(const SkM44& t) const {
SkASSERT(this->valid());
// Saves a multiply compared to inverting just 't' and calculating both fM*t^-1 and t*fInvM
// (t * this^-1)^-1 = this * t^-1
return Inverse(Transform(t * fInvM));
}
private:
// Used for static factories that have known properties
constexpr Transform(const SkM44& m, const SkM44& invM, Type type,
float minScale, float maxScale)
: fM(m)
, fInvM(invM)
, fType(type)
, fMinScaleFactor(minScale)
, fMaxScaleFactor(maxScale) {}
SkM44 fM;
SkM44 fInvM; // M^-1
Type fType;
// These are cached for non-projection transforms since they are constant; projection matrices
// must be computed per point, and these values are ignored.
float fMinScaleFactor = 1.f;
float fMaxScaleFactor = 1.f;
};
} // namespace skgpu::graphite
#endif // skgpu_graphite_geom_Transform_DEFINED