1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_GFX_GEOMETRY_AXIS_TRANSFORM2D_H_
#define UI_GFX_GEOMETRY_AXIS_TRANSFORM2D_H_
#include <optional>
#include "base/check_op.h"
#include "base/component_export.h"
#include "ui/gfx/geometry/clamp_float_geometry.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace gfx {
struct DecomposedTransform;
// This class implements the subset of 2D linear transforms that only
// translation and uniform scaling are allowed.
// Internally this is stored as a vector for pre-scale, and another vector
// for post-translation. The class constructor and member accessor follows
// the same convention, but a scalar scale factor is also accepted.
//
// Results of the *Map* methods are clamped with ClampFloatGeometry().
// See the definition of the function for details.
//
class COMPONENT_EXPORT(GEOMETRY) AxisTransform2d {
public:
constexpr AxisTransform2d() = default;
constexpr AxisTransform2d(float scale, const Vector2dF& translation)
: scale_(scale, scale), translation_(translation) {}
static constexpr AxisTransform2d FromScaleAndTranslation(
const Vector2dF& scale,
const Vector2dF& translation) {
return AxisTransform2d(scale, translation);
}
friend constexpr bool operator==(const AxisTransform2d&,
const AxisTransform2d&) = default;
void PreScale(const Vector2dF& scale) { scale_.Scale(scale.x(), scale.y()); }
void PostScale(const Vector2dF& scale) {
scale_.Scale(scale.x(), scale.y());
translation_.Scale(scale.x(), scale.y());
}
void PreTranslate(const Vector2dF& translation) {
translation_ += ScaleVector2d(translation, scale_.x(), scale_.y());
}
void PostTranslate(const Vector2dF& translation) {
translation_ += translation;
}
void PreConcat(const AxisTransform2d& pre) {
PreTranslate(pre.translation_);
PreScale(pre.scale_);
}
void PostConcat(const AxisTransform2d& post) {
PostScale(post.scale_);
PostTranslate(post.translation_);
}
double Determinant() const { return double{scale_.x()} * scale_.y(); }
bool IsInvertible() const {
// Check float determinant (stricter than checking each component or double
// determinant) to keep consistency with Matrix44.
// TODO(crbug.com/40237414): This may be stricter than necessary. Revisit
// this after combination of gfx::Transform and blink::TransformationMatrix.
return std::isnormal(scale_.x() * scale_.y());
}
void Invert() {
DCHECK(IsInvertible());
scale_ = Vector2dF(1.f / scale_.x(), 1.f / scale_.y());
translation_.Scale(-scale_.x(), -scale_.y());
}
// Changes the transform to: scale(z) * mat * scale(1/z).
// Useful for mapping zoomed points to their zoomed transformed result:
// new_mat * (scale(z) * x) == scale(z) * (mat * x).
void Zoom(float zoom_factor) { translation_.Scale(zoom_factor); }
PointF MapPoint(const PointF& p) const {
return PointF(MapX(p.x()), MapY(p.y()));
}
PointF InverseMapPoint(const PointF& p) const {
return PointF(InverseMapX(p.x()), InverseMapY(p.y()));
}
RectF MapRect(const RectF& r) const {
DCHECK_GE(scale_.x(), 0.f);
DCHECK_GE(scale_.y(), 0.f);
return RectF(MapX(r.x()), MapY(r.y()),
ClampFloatGeometry(r.width() * scale_.x()),
ClampFloatGeometry(r.height() * scale_.y()));
}
RectF InverseMapRect(const RectF& r) const {
DCHECK_GT(scale_.x(), 0.f);
DCHECK_GT(scale_.y(), 0.f);
return RectF(InverseMapX(r.x()), InverseMapY(r.y()),
// |* (1.f / scale)| instead of '/ scale' to keep the same
// precision before crrev.com/c/3937107.
ClampFloatGeometry(r.width() * (1.f / scale_.x())),
ClampFloatGeometry(r.height() * (1.f / scale_.y())));
}
// Decomposes this transform into |decomp|, following the 2d decomposition
// spec: https://www.w3.org/TR/css-transforms-1/#decomposing-a-2d-matrix.
// It's a simplified version of Matrix44::Decompose2d().
DecomposedTransform Decompose() const;
constexpr const Vector2dF& scale() const { return scale_; }
constexpr const Vector2dF& translation() const { return translation_; }
std::string ToString() const;
private:
constexpr AxisTransform2d(const Vector2dF& scale,
const Vector2dF& translation)
: scale_(scale), translation_(translation) {}
float MapX(float x) const {
return ClampFloatGeometry(x * scale_.x() + translation_.x());
}
float MapY(float y) const {
return ClampFloatGeometry(y * scale_.y() + translation_.y());
}
float InverseMapX(float x) const {
// |* (1.f / scale)| instead of '/ scale' to keep the same precision
// before crrev.com/c/3937107.
return ClampFloatGeometry((x - translation_.x()) * (1.f / scale_.x()));
}
float InverseMapY(float y) const {
// |* (1.f / scale)| instead of '/ scale' to keep the same precision
// before crrev.com/c/3937107.
return ClampFloatGeometry((y - translation_.y()) * (1.f / scale_.y()));
}
// Scale is applied before translation, i.e.
// this->Transform(p) == scale_ * p + translation_
Vector2dF scale_{1.f, 1.f};
Vector2dF translation_;
};
inline AxisTransform2d PreScaleAxisTransform2d(const AxisTransform2d& t,
float scale) {
AxisTransform2d result(t);
result.PreScale(Vector2dF(scale, scale));
return result;
}
inline AxisTransform2d PostScaleAxisTransform2d(const AxisTransform2d& t,
float scale) {
AxisTransform2d result(t);
result.PostScale(Vector2dF(scale, scale));
return result;
}
inline AxisTransform2d PreTranslateAxisTransform2d(
const AxisTransform2d& t,
const Vector2dF& translation) {
AxisTransform2d result(t);
result.PreTranslate(translation);
return result;
}
inline AxisTransform2d PostTranslateAxisTransform2d(
const AxisTransform2d& t,
const Vector2dF& translation) {
AxisTransform2d result(t);
result.PostTranslate(translation);
return result;
}
inline AxisTransform2d ConcatAxisTransform2d(const AxisTransform2d& post,
const AxisTransform2d& pre) {
AxisTransform2d result(post);
result.PreConcat(pre);
return result;
}
inline AxisTransform2d InvertAxisTransform2d(const AxisTransform2d& t) {
AxisTransform2d result = t;
result.Invert();
return result;
}
// This is declared here for use in gtest-based unit tests but is defined in
// the //ui/gfx:test_support target. Depend on that to use this in your unit
// test. This should not be used in production code - call ToString() instead.
void PrintTo(const AxisTransform2d&, ::std::ostream* os);
} // namespace gfx
#endif // UI_GFX_GEOMETRY_AXIS_TRANSFORM2D_H_
|