|
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #ifndef GFX_MATRIX_H |
|
7 #define GFX_MATRIX_H |
|
8 |
|
9 #include "gfxPoint.h" |
|
10 #include "gfxTypes.h" |
|
11 #include "gfxRect.h" |
|
12 |
|
13 // XX - I don't think this class should use gfxFloat at all, |
|
14 // but should use 'double' and be called gfxDoubleMatrix; |
|
15 // we can then typedef that to gfxMatrix where we typedef |
|
16 // double to be gfxFloat. |
|
17 |
|
18 /** |
|
19 * A matrix that represents an affine transformation. Projective |
|
20 * transformations are not supported. This matrix looks like: |
|
21 * |
|
22 * / a b 0 \ |
|
23 * | c d 0 | |
|
24 * \ tx ty 1 / |
|
25 * |
|
26 * So, transforming a point (x, y) results in: |
|
27 * |
|
28 * / a b 0 \ / a * x + c * y + tx \ T |
|
29 * (x y 1) * | c d 0 | = | b * x + d * y + ty | |
|
30 * \ tx ty 1 / \ 1 / |
|
31 * |
|
32 */ |
|
33 struct gfxMatrix { |
|
34 double xx; double yx; |
|
35 double xy; double yy; |
|
36 double x0; double y0; |
|
37 |
|
38 public: |
|
39 /** |
|
40 * Initializes this matrix as the identity matrix. |
|
41 */ |
|
42 gfxMatrix() { Reset(); } |
|
43 |
|
44 /** |
|
45 * Initializes the matrix from individual components. See the class |
|
46 * description for the layout of the matrix. |
|
47 */ |
|
48 gfxMatrix(gfxFloat a, gfxFloat b, gfxFloat c, gfxFloat d, gfxFloat tx, gfxFloat ty) : |
|
49 xx(a), yx(b), |
|
50 xy(c), yy(d), |
|
51 x0(tx), y0(ty) { } |
|
52 |
|
53 /** |
|
54 * Post-multiplies m onto the matrix. |
|
55 */ |
|
56 const gfxMatrix& operator *= (const gfxMatrix& m) { |
|
57 return Multiply(m); |
|
58 } |
|
59 |
|
60 /** |
|
61 * Multiplies *this with m and returns the result. |
|
62 */ |
|
63 gfxMatrix operator * (const gfxMatrix& m) const { |
|
64 return gfxMatrix(*this).Multiply(m); |
|
65 } |
|
66 |
|
67 /* Returns true if the other matrix is fuzzy-equal to this matrix. |
|
68 * Note that this isn't a cheap comparison! |
|
69 */ |
|
70 bool operator==(const gfxMatrix& other) const |
|
71 { |
|
72 return FuzzyEqual(xx, other.xx) && FuzzyEqual(yx, other.yx) && |
|
73 FuzzyEqual(xy, other.xy) && FuzzyEqual(yy, other.yy) && |
|
74 FuzzyEqual(x0, other.x0) && FuzzyEqual(y0, other.y0); |
|
75 } |
|
76 |
|
77 bool operator!=(const gfxMatrix& other) const |
|
78 { |
|
79 return !(*this == other); |
|
80 } |
|
81 |
|
82 // matrix operations |
|
83 /** |
|
84 * Resets this matrix to the identity matrix. |
|
85 */ |
|
86 const gfxMatrix& Reset(); |
|
87 |
|
88 bool IsIdentity() const { |
|
89 return xx == 1.0 && yx == 0.0 && |
|
90 xy == 0.0 && yy == 1.0 && |
|
91 x0 == 0.0 && y0 == 0.0; |
|
92 } |
|
93 |
|
94 /** |
|
95 * Inverts this matrix, if possible. Otherwise, the matrix is left |
|
96 * unchanged. |
|
97 * |
|
98 * XXX should this do something with the return value of |
|
99 * cairo_matrix_invert? |
|
100 */ |
|
101 const gfxMatrix& Invert(); |
|
102 |
|
103 /** |
|
104 * Check if matrix is singular (no inverse exists). |
|
105 */ |
|
106 bool IsSingular() const { |
|
107 // if the determinant (ad - bc) is zero it's singular |
|
108 return (xx * yy) == (yx * xy); |
|
109 } |
|
110 |
|
111 /** |
|
112 * Scales this matrix. The scale is pre-multiplied onto this matrix, |
|
113 * i.e. the scaling takes place before the other transformations. |
|
114 */ |
|
115 const gfxMatrix& Scale(gfxFloat x, gfxFloat y); |
|
116 |
|
117 /** |
|
118 * Translates this matrix. The translation is pre-multiplied onto this matrix, |
|
119 * i.e. the translation takes place before the other transformations. |
|
120 */ |
|
121 const gfxMatrix& Translate(const gfxPoint& pt); |
|
122 |
|
123 /** |
|
124 * Rotates this matrix. The rotation is pre-multiplied onto this matrix, |
|
125 * i.e. the translation takes place after the other transformations. |
|
126 * |
|
127 * @param radians Angle in radians. |
|
128 */ |
|
129 const gfxMatrix& Rotate(gfxFloat radians); |
|
130 |
|
131 /** |
|
132 * Multiplies the current matrix with m. |
|
133 * This is a post-multiplication, i.e. the transformations of m are |
|
134 * applied _after_ the existing transformations. |
|
135 * |
|
136 * XXX is that difference (compared to Rotate etc) a good thing? |
|
137 */ |
|
138 const gfxMatrix& Multiply(const gfxMatrix& m); |
|
139 |
|
140 /** |
|
141 * Multiplies the current matrix with m. |
|
142 * This is a pre-multiplication, i.e. the transformations of m are |
|
143 * applied _before_ the existing transformations. |
|
144 */ |
|
145 const gfxMatrix& PreMultiply(const gfxMatrix& m); |
|
146 |
|
147 /** |
|
148 * Transforms a point according to this matrix. |
|
149 */ |
|
150 gfxPoint Transform(const gfxPoint& point) const; |
|
151 |
|
152 |
|
153 /** |
|
154 * Transform a distance according to this matrix. This does not apply |
|
155 * any translation components. |
|
156 */ |
|
157 gfxSize Transform(const gfxSize& size) const; |
|
158 |
|
159 /** |
|
160 * Transforms both the point and distance according to this matrix. |
|
161 */ |
|
162 gfxRect Transform(const gfxRect& rect) const; |
|
163 |
|
164 gfxRect TransformBounds(const gfxRect& rect) const; |
|
165 |
|
166 /** |
|
167 * Returns the translation component of this matrix. |
|
168 */ |
|
169 gfxPoint GetTranslation() const { |
|
170 return gfxPoint(x0, y0); |
|
171 } |
|
172 |
|
173 /** |
|
174 * Returns true if the matrix is anything other than a straight |
|
175 * translation by integers. |
|
176 */ |
|
177 bool HasNonIntegerTranslation() const { |
|
178 return HasNonTranslation() || |
|
179 !FuzzyEqual(x0, floor(x0 + 0.5)) || |
|
180 !FuzzyEqual(y0, floor(y0 + 0.5)); |
|
181 } |
|
182 |
|
183 /** |
|
184 * Returns true if the matrix has any transform other |
|
185 * than a straight translation |
|
186 */ |
|
187 bool HasNonTranslation() const { |
|
188 return !FuzzyEqual(xx, 1.0) || !FuzzyEqual(yy, 1.0) || |
|
189 !FuzzyEqual(xy, 0.0) || !FuzzyEqual(yx, 0.0); |
|
190 } |
|
191 |
|
192 /** |
|
193 * Returns true if the matrix only has an integer translation. |
|
194 */ |
|
195 bool HasOnlyIntegerTranslation() const { |
|
196 return !HasNonIntegerTranslation(); |
|
197 } |
|
198 |
|
199 /** |
|
200 * Returns true if the matrix has any transform other |
|
201 * than a translation or a -1 y scale (y axis flip) |
|
202 */ |
|
203 bool HasNonTranslationOrFlip() const { |
|
204 return !FuzzyEqual(xx, 1.0) || |
|
205 (!FuzzyEqual(yy, 1.0) && !FuzzyEqual(yy, -1.0)) || |
|
206 !FuzzyEqual(xy, 0.0) || !FuzzyEqual(yx, 0.0); |
|
207 } |
|
208 |
|
209 /** |
|
210 * Returns true if the matrix has any transform other |
|
211 * than a translation or scale; this is, if there is |
|
212 * no rotation. |
|
213 */ |
|
214 bool HasNonAxisAlignedTransform() const { |
|
215 return !FuzzyEqual(xy, 0.0) || !FuzzyEqual(yx, 0.0); |
|
216 } |
|
217 |
|
218 /** |
|
219 * Computes the determinant of this matrix. |
|
220 */ |
|
221 double Determinant() const { |
|
222 return xx*yy - yx*xy; |
|
223 } |
|
224 |
|
225 /* Computes the scale factors of this matrix; that is, |
|
226 * the amounts each basis vector is scaled by. |
|
227 * The xMajor parameter indicates if the larger scale is |
|
228 * to be assumed to be in the X direction or not. |
|
229 */ |
|
230 gfxSize ScaleFactors(bool xMajor) const { |
|
231 double det = Determinant(); |
|
232 |
|
233 if (det == 0.0) |
|
234 return gfxSize(0.0, 0.0); |
|
235 |
|
236 gfxSize sz = xMajor ? gfxSize(1.0, 0.0) : gfxSize(0.0, 1.0); |
|
237 sz = Transform(sz); |
|
238 |
|
239 double major = sqrt(sz.width * sz.width + sz.height * sz.height); |
|
240 double minor = 0.0; |
|
241 |
|
242 // ignore mirroring |
|
243 if (det < 0.0) |
|
244 det = - det; |
|
245 |
|
246 if (major) |
|
247 minor = det / major; |
|
248 |
|
249 if (xMajor) |
|
250 return gfxSize(major, minor); |
|
251 |
|
252 return gfxSize(minor, major); |
|
253 } |
|
254 |
|
255 /** |
|
256 * Snap matrix components that are close to integers |
|
257 * to integers. In particular, components that are integral when |
|
258 * converted to single precision are set to those integers. |
|
259 */ |
|
260 void NudgeToIntegers(void); |
|
261 |
|
262 /** |
|
263 * Returns true if matrix is multiple of 90 degrees rotation with flipping, |
|
264 * scaling and translation. |
|
265 */ |
|
266 bool PreservesAxisAlignedRectangles() const { |
|
267 return ((FuzzyEqual(xx, 0.0) && FuzzyEqual(yy, 0.0)) |
|
268 || (FuzzyEqual(xy, 0.0) && FuzzyEqual(yx, 0.0))); |
|
269 } |
|
270 |
|
271 /** |
|
272 * Returns true if the matrix has non-integer scale |
|
273 */ |
|
274 bool HasNonIntegerScale() const { |
|
275 return !FuzzyEqual(xx, floor(xx + 0.5)) || |
|
276 !FuzzyEqual(yy, floor(yy + 0.5)); |
|
277 } |
|
278 |
|
279 private: |
|
280 static bool FuzzyEqual(gfxFloat aV1, gfxFloat aV2) { |
|
281 return fabs(aV2 - aV1) < 1e-6; |
|
282 } |
|
283 }; |
|
284 |
|
285 #endif /* GFX_MATRIX_H */ |