File: transform_util.cc

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (186 lines) | stat: -rw-r--r-- 6,483 bytes parent folder | download | duplicates (4)
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
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.


#include "ui/gfx/geometry/transform_util.h"

#include <algorithm>
#include <cmath>
#include <ostream>
#include <string>

#include "base/check.h"
#include "ui/gfx/geometry/clamp_float_geometry.h"
#include "ui/gfx/geometry/point3_f.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"

namespace gfx {

namespace {

template <int n>
void Combine(std::array<double, n>& out,
             const std::array<double, n> a,
             const std::array<double, n> b,
             double scale_a,
             double scale_b) {
  for (int i = 0; i < n; ++i)
    out[i] = a[i] * scale_a + b[i] * scale_b;
}

}  // namespace

Transform GetScaleTransform(const Point& anchor, float scale) {
  Transform transform;
  transform.Translate(anchor.x() * (1 - scale), anchor.y() * (1 - scale));
  transform.Scale(scale, scale);
  return transform;
}

DecomposedTransform BlendDecomposedTransforms(const DecomposedTransform& to,
                                              const DecomposedTransform& from,
                                              double progress) {
  DecomposedTransform out;
  double scalea = progress;
  double scaleb = 1.0 - progress;
  Combine<3>(out.translate, to.translate, from.translate, scalea, scaleb);
  Combine<3>(out.scale, to.scale, from.scale, scalea, scaleb);
  Combine<3>(out.skew, to.skew, from.skew, scalea, scaleb);
  Combine<4>(out.perspective, to.perspective, from.perspective, scalea, scaleb);
  out.quaternion = from.quaternion.Slerp(to.quaternion, progress);
  return out;
}

DecomposedTransform AccumulateDecomposedTransforms(
    const DecomposedTransform& a,
    const DecomposedTransform& b) {
  DecomposedTransform out;

  // Translate is a simple addition.
  for (size_t i = 0; i < std::size(a.translate); i++)
    out.translate[i] = a.translate[i] + b.translate[i];

  // Scale is accumulated using 1-based addition.
  for (size_t i = 0; i < std::size(a.scale); i++)
    out.scale[i] = a.scale[i] + b.scale[i] - 1;

  // Skew can be added.
  for (size_t i = 0; i < std::size(a.skew); i++)
    out.skew[i] = a.skew[i] + b.skew[i];

  // We sum the perspective components; note that w is 1-based.
  for (size_t i = 0; i < std::size(a.perspective); i++)
    out.perspective[i] = a.perspective[i] + b.perspective[i];
  out.perspective[3] -= 1;

  // To accumulate quaternions, we multiply them. This is equivalent to 'adding'
  // the rotations that they represent.
  out.quaternion = a.quaternion * b.quaternion;

  return out;
}

Transform TransformAboutPivot(const PointF& pivot, const Transform& transform) {
  Transform result;
  result.Translate(pivot.x(), pivot.y());
  result.PreConcat(transform);
  result.Translate(-pivot.x(), -pivot.y());
  return result;
}

Transform TransformBetweenRects(const RectF& src, const RectF& dst) {
  DCHECK(!src.IsEmpty());
  Transform result;
  result.Translate(dst.origin() - src.origin());
  result.Scale(dst.width() / src.width(), dst.height() / src.height());
  return result;
}

AxisTransform2d OrthoProjectionTransform(float left,
                                         float right,
                                         float bottom,
                                         float top) {
  // Use the standard formula to map the clipping frustum to the square from
  // [-1, -1] to [1, 1].
  float delta_x = right - left;
  float delta_y = top - bottom;
  if (!delta_x || !delta_y)
    return AxisTransform2d();

  return AxisTransform2d::FromScaleAndTranslation(
      Vector2dF(2.0f / delta_x, 2.0f / delta_y),
      Vector2dF(-(right + left) / delta_x, -(top + bottom) / delta_y));
}

AxisTransform2d WindowTransform(int x, int y, int width, int height) {
  // Map from ([-1, -1] to [1, 1]) -> ([x, y] to [x + width, y + height]).
  return AxisTransform2d::FromScaleAndTranslation(
      Vector2dF(width * 0.5f, height * 0.5f),
      Vector2dF(x + width * 0.5f, y + height * 0.5f));
}

static inline bool NearlyZero(double value) {
  return std::abs(value) < std::numeric_limits<double>::epsilon();
}

static inline float ScaleOnAxis(double a, double b, double c) {
  if (NearlyZero(b) && NearlyZero(c))
    return ClampFloatGeometry(std::abs(a));
  if (NearlyZero(a) && NearlyZero(c))
    return ClampFloatGeometry(std::abs(b));
  if (NearlyZero(a) && NearlyZero(b))
    return ClampFloatGeometry(std::abs(c));

  // Do the sqrt as a double to not lose precision.
  return ClampFloatGeometry(std::sqrt(a * a + b * b + c * c));
}

std::optional<Vector2dF> TryComputeTransform2dScaleComponents(
    const Transform& transform) {
  if (transform.rc(3, 0) != 0.0f || transform.rc(3, 1) != 0.0f) {
    return std::nullopt;
  }

  float w = transform.rc(3, 3);
  if (!std::isnormal(w)) {
    return std::nullopt;
  }
  float w_scale = 1.0f / w;

  // In theory, this shouldn't be using the matrix.getDouble(2, 0) and
  // .getDouble(1, 0) values; creating a large transfer from input x or
  // y (in the layer) to output z has no visible difference when the
  // transform being considered is a transform to device space, since
  // the resulting z values are ignored.  However, ignoring them here
  // might be risky because it would mean that we would have more
  // variation in the results under animation of rotateX() or rotateY(),
  // and we'd be relying more heavily on code to compute correct scales
  // during animation.  Currently some such code only considers the
  // endpoints, which would become problematic for cases like animation
  // from rotateY(-60deg) to rotateY(60deg).
  float x_scale =
      ScaleOnAxis(transform.rc(0, 0), transform.rc(1, 0), transform.rc(2, 0));
  float y_scale =
      ScaleOnAxis(transform.rc(0, 1), transform.rc(1, 1), transform.rc(2, 1));
  return Vector2dF(ClampFloatGeometry(x_scale * w_scale),
                   ClampFloatGeometry(y_scale * w_scale));
}

Vector2dF ComputeTransform2dScaleComponents(const Transform& transform,
                                            float fallback_value) {
  std::optional<Vector2dF> scale =
      TryComputeTransform2dScaleComponents(transform);
  if (scale) {
    return *scale;
  }
  return Vector2dF(fallback_value, fallback_value);
}

float ComputeApproximateMaxScale(const Transform& transform) {
  gfx::RectF unit = transform.MapRect(RectF(0.f, 0.f, 1.f, 1.f));
  return std::max(unit.width(), unit.height());
}

}  // namespace gfx