blob: 516f542494b9c376b3afb4a63f5dd979fc2c3476 [file] [log] [blame]
reed@google.com1fcd51e2011-01-05 15:50:27 +00001/*
2 Copyright 2010 Google Inc.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
reed@google.comac10a2d2010-12-22 21:39:39 +000017
18#include "GrMatrix.h"
19#include "GrRect.h"
20#include <stddef.h>
21
22#if GR_SCALAR_IS_FLOAT
23 const GrScalar GrMatrix::gRESCALE(GR_Scalar1);
24#else
25 GR_STATIC_ASSERT(GR_SCALAR_IS_FIXED);
26 // fixed point isn't supported right now
27 GR_STATIC_ASSERT(false);
28const GrScalar GrMatrix::gRESCALE(1 << 30);
29#endif
30
31const GrMatrix::MapProc GrMatrix::gMapProcs[] = {
32// Scales are not both zero
33 &GrMatrix::mapIdentity,
34 &GrMatrix::mapScale,
35 &GrMatrix::mapTranslate,
36 &GrMatrix::mapScaleAndTranslate,
37 &GrMatrix::mapSkew,
38 &GrMatrix::mapScaleAndSkew,
39 &GrMatrix::mapSkewAndTranslate,
40 &GrMatrix::mapNonPerspective,
41 // no optimizations for perspective matrices
42 &GrMatrix::mapPerspective,
43 &GrMatrix::mapPerspective,
44 &GrMatrix::mapPerspective,
45 &GrMatrix::mapPerspective,
46 &GrMatrix::mapPerspective,
47 &GrMatrix::mapPerspective,
48 &GrMatrix::mapPerspective,
49 &GrMatrix::mapPerspective,
reed@google.com1fcd51e2011-01-05 15:50:27 +000050
reed@google.comac10a2d2010-12-22 21:39:39 +000051// Scales are zero (every other is invalid because kScale_TypeBit must be set if
52// kZeroScale_TypeBit is set)
53 &GrMatrix::mapInvalid,
54 &GrMatrix::mapZero,
55 &GrMatrix::mapInvalid,
56 &GrMatrix::mapSetToTranslate,
57 &GrMatrix::mapInvalid,
58 &GrMatrix::mapSwappedScale,
59 &GrMatrix::mapInvalid,
60 &GrMatrix::mapSwappedScaleAndTranslate,
reed@google.com1fcd51e2011-01-05 15:50:27 +000061
reed@google.comac10a2d2010-12-22 21:39:39 +000062 // no optimizations for perspective matrices
63 &GrMatrix::mapInvalid,
64 &GrMatrix::mapZero,
65 &GrMatrix::mapInvalid,
66 &GrMatrix::mapPerspective,
67 &GrMatrix::mapInvalid,
68 &GrMatrix::mapPerspective,
69 &GrMatrix::mapInvalid,
70 &GrMatrix::mapPerspective,
71};
72
73const GrMatrix& GrMatrix::I() {
reed@google.com1fcd51e2011-01-05 15:50:27 +000074 static GrMatrix* gIdent;
75 if (NULL == gIdent) {
76 gIdent = new GrMatrix;
77 gIdent->setIdentity();
78 }
79 return *gIdent;
reed@google.comac10a2d2010-12-22 21:39:39 +000080}
81
82void GrMatrix::setIdentity() {
83 fM[0] = GR_Scalar1; fM[1] = 0; fM[2] = 0;
84 fM[3] = 0; fM[4] = GR_Scalar1; fM[5] = 0;
85 fM[6] = 0; fM[7] = 0; fM[8] = gRESCALE;
reed@google.com1fcd51e2011-01-05 15:50:27 +000086 fTypeMask = 0;
reed@google.comac10a2d2010-12-22 21:39:39 +000087}
88
89void GrMatrix::setTranslate(GrScalar dx, GrScalar dy) {
90 fM[0] = GR_Scalar1; fM[1] = 0; fM[2] = dx;
91 fM[3] = 0; fM[4] = GR_Scalar1; fM[5] = dy;
92 fM[6] = 0; fM[7] = 0; fM[8] = gRESCALE;
93 fTypeMask = kTranslate_TypeBit;
94}
95
96void GrMatrix::setScale(GrScalar sx, GrScalar sy) {
97 fM[0] = sx; fM[1] = 0; fM[2] = 0;
98 fM[3] = 0; fM[4] = sy; fM[5] = 0;
99 fM[6] = 0; fM[7] = 0; fM[8] = gRESCALE;
100 fTypeMask = kScale_TypeBit;
101}
102
103void GrMatrix::setSkew(GrScalar skx, GrScalar sky) {
104 fM[0] = GR_Scalar1; fM[1] = skx; fM[2] = 0;
105 fM[3] = sky; fM[4] = GR_Scalar1; fM[5] = 0;
106 fM[6] = 0; fM[7] = 0; fM[8] = gRESCALE;
107 fTypeMask = kSkew_TypeBit;
108}
109
110void GrMatrix::setConcat(const GrMatrix& a, const GrMatrix& b) {
111 if (a.isIdentity()) {
112 if (this != &b) {
113 for (int i = 0; i < 9; ++i) {
114 fM[i] = b.fM[i];
115 }
116 fTypeMask = b.fTypeMask;
117 }
118 return;
119 }
reed@google.com1fcd51e2011-01-05 15:50:27 +0000120
reed@google.comac10a2d2010-12-22 21:39:39 +0000121 if (b.isIdentity()) {
122 GrAssert(!a.isIdentity());
123 if (this != &a) {
124 for (int i = 0; i < 9; ++i) {
125 fM[i] = a.fM[i];
126 }
127 fTypeMask = a.fTypeMask;
128 }
129 return;
130 }
reed@google.com1fcd51e2011-01-05 15:50:27 +0000131
reed@google.comac10a2d2010-12-22 21:39:39 +0000132 // a and/or b could be this
133 GrMatrix tmp;
reed@google.com1fcd51e2011-01-05 15:50:27 +0000134
reed@google.comac10a2d2010-12-22 21:39:39 +0000135 // could do more optimizations based on type bits. Hopefully this call is
136 // low frequency.
137 // TODO: make this work for fixed point
138 if (!((b.fTypeMask | a.fTypeMask) & kPerspective_TypeBit)) {
139 tmp.fM[0] = a.fM[0] * b.fM[0] + a.fM[1] * b.fM[3];
140 tmp.fM[1] = a.fM[0] * b.fM[1] + a.fM[1] * b.fM[4];
141 tmp.fM[2] = a.fM[0] * b.fM[2] + a.fM[1] * b.fM[5] + a.fM[2] * gRESCALE;
reed@google.com1fcd51e2011-01-05 15:50:27 +0000142
reed@google.comac10a2d2010-12-22 21:39:39 +0000143 tmp.fM[3] = a.fM[3] * b.fM[0] + a.fM[4] * b.fM[3];
144 tmp.fM[4] = a.fM[3] * b.fM[1] + a.fM[4] * b.fM[4];
145 tmp.fM[5] = a.fM[3] * b.fM[2] + a.fM[4] * b.fM[5] + a.fM[5] * gRESCALE;
reed@google.com1fcd51e2011-01-05 15:50:27 +0000146
reed@google.comac10a2d2010-12-22 21:39:39 +0000147 tmp.fM[6] = 0;
148 tmp.fM[7] = 0;
reed@google.com1fcd51e2011-01-05 15:50:27 +0000149 tmp.fM[8] = gRESCALE * gRESCALE;
reed@google.comac10a2d2010-12-22 21:39:39 +0000150 } else {
151 tmp.fM[0] = a.fM[0] * b.fM[0] + a.fM[1] * b.fM[3] + a.fM[2] * b.fM[6];
152 tmp.fM[1] = a.fM[0] * b.fM[1] + a.fM[1] * b.fM[4] + a.fM[2] * b.fM[7];
153 tmp.fM[2] = a.fM[0] * b.fM[2] + a.fM[1] * b.fM[5] + a.fM[2] * b.fM[8];
154
155 tmp.fM[3] = a.fM[3] * b.fM[0] + a.fM[4] * b.fM[3] + a.fM[5] * b.fM[6];
156 tmp.fM[4] = a.fM[3] * b.fM[1] + a.fM[4] * b.fM[4] + a.fM[5] * b.fM[7];
157 tmp.fM[5] = a.fM[3] * b.fM[2] + a.fM[4] * b.fM[5] + a.fM[5] * b.fM[8];
158
159 tmp.fM[6] = a.fM[6] * b.fM[0] + a.fM[7] * b.fM[3] + a.fM[8] * b.fM[6];
160 tmp.fM[7] = a.fM[6] * b.fM[1] + a.fM[7] * b.fM[4] + a.fM[8] * b.fM[7];
161 tmp.fM[8] = a.fM[6] * b.fM[2] + a.fM[7] * b.fM[5] + a.fM[8] * b.fM[8];
162 }
163 *this = tmp;
164 setTypeMask();
165}
166
167void GrMatrix::preConcat(const GrMatrix& m) {
168 setConcat(*this, m);
169}
170
171void GrMatrix::postConcat(const GrMatrix& m) {
172 setConcat(m, *this);
173}
174
175double GrMatrix::determinant() const {
176 if (fTypeMask & kPerspective_TypeBit) {
reed@google.com1fcd51e2011-01-05 15:50:27 +0000177 return fM[0]*((double)fM[4]*fM[8] - (double)fM[5]*fM[7]) +
178 fM[1]*((double)fM[5]*fM[6] - (double)fM[3]*fM[8]) +
reed@google.comac10a2d2010-12-22 21:39:39 +0000179 fM[2]*((double)fM[3]*fM[7] - (double)fM[4]*fM[6]);
180 } else {
reed@google.com1fcd51e2011-01-05 15:50:27 +0000181 return (double)fM[0]*fM[4]*gRESCALE -
reed@google.comac10a2d2010-12-22 21:39:39 +0000182 (double)fM[1]*fM[3]*gRESCALE;
183 }
184}
185
186bool GrMatrix::invert(GrMatrix* inverted) const {
reed@google.com1fcd51e2011-01-05 15:50:27 +0000187
reed@google.comac10a2d2010-12-22 21:39:39 +0000188 if (isIdentity()) {
189 if (inverted != this) {
190 inverted->setIdentity();
191 }
192 return true;
193 }
194 static const double MIN_DETERMINANT_SQUARED = 1.e-16;
reed@google.com1fcd51e2011-01-05 15:50:27 +0000195
reed@google.comac10a2d2010-12-22 21:39:39 +0000196 // could do more optimizations based on type bits. Hopefully this call is
197 // low frequency.
reed@google.com1fcd51e2011-01-05 15:50:27 +0000198
reed@google.comac10a2d2010-12-22 21:39:39 +0000199 double det = determinant();
200
201 // check if we can't be inverted
202 if (det*det <= MIN_DETERMINANT_SQUARED) {
203 return false;
204 } else if (NULL == inverted) {
205 return true;
206 }
207
208 double t[9];
reed@google.com1fcd51e2011-01-05 15:50:27 +0000209
reed@google.comac10a2d2010-12-22 21:39:39 +0000210 if (fTypeMask & kPerspective_TypeBit) {
reed@google.com1fcd51e2011-01-05 15:50:27 +0000211 t[0] = ((double)fM[4]*fM[8] - (double)fM[5]*fM[7]);
reed@google.comac10a2d2010-12-22 21:39:39 +0000212 t[1] = ((double)fM[2]*fM[7] - (double)fM[1]*fM[8]);
213 t[2] = ((double)fM[1]*fM[5] - (double)fM[2]*fM[4]);
214 t[3] = ((double)fM[5]*fM[6] - (double)fM[3]*fM[8]);
215 t[4] = ((double)fM[0]*fM[8] - (double)fM[2]*fM[6]);
216 t[5] = ((double)fM[2]*fM[3] - (double)fM[0]*fM[5]);
217 t[6] = ((double)fM[3]*fM[7] - (double)fM[4]*fM[6]);
218 t[7] = ((double)fM[1]*fM[6] - (double)fM[0]*fM[7]);
219 t[8] = ((double)fM[0]*fM[4] - (double)fM[1]*fM[3]);
220 det = 1.0 / det;
221 for (int i = 0; i < 9; ++i) {
222 inverted->fM[i] = (GrScalar)(t[i] * det);
223 }
224 } else {
reed@google.com1fcd51e2011-01-05 15:50:27 +0000225 t[0] = (double)fM[4]*gRESCALE;
reed@google.comac10a2d2010-12-22 21:39:39 +0000226 t[1] = -(double)fM[1]*gRESCALE;
227 t[2] = (double)fM[1]*fM[5] - (double)fM[2]*fM[4];
228 t[3] = -(double)fM[3]*gRESCALE;
229 t[4] = (double)fM[0]*gRESCALE;
230 t[5] = (double)fM[2]*fM[3] - (double)fM[0]*fM[5];
231 //t[6] = 0.0;
232 //t[7] = 0.0;
233 t[8] = (double)fM[0]*fM[4] - (double)fM[1]*fM[3];
234 det = 1.0 / det;
235 for (int i = 0; i < 6; ++i) {
236 inverted->fM[i] = (GrScalar)(t[i] * det);
237 }
238 inverted->fM[6] = 0;
239 inverted->fM[7] = 0;
240 inverted->fM[8] = (GrScalar)(t[8] * det);
241 }
242 inverted->setTypeMask();
243 return true;
244}
245
246void GrMatrix::mapRect(GrRect* dst, const GrRect& src) const {
247 GrPoint srcPts[4], dstPts[4];
248 srcPts[0].set(src.fLeft, src.fTop);
249 srcPts[1].set(src.fRight, src.fTop);
250 srcPts[2].set(src.fRight, src.fBottom);
251 srcPts[3].set(src.fLeft, src.fBottom);
252 this->mapPoints(dstPts, srcPts, 4);
253 dst->setBounds(dstPts, 4);
254}
255
256bool GrMatrix::hasPerspective() const {
257 GrAssert(!!(kPerspective_TypeBit & fTypeMask) ==
258 (fM[kPersp0] != 0 || fM[kPersp1] != 0 || fM[kPersp2] != gRESCALE));
259 return 0 != (kPerspective_TypeBit & fTypeMask);
260}
261
262bool GrMatrix::isIdentity() const {
reed@google.com1fcd51e2011-01-05 15:50:27 +0000263 GrAssert((0 == fTypeMask) ==
reed@google.comac10a2d2010-12-22 21:39:39 +0000264 (GR_Scalar1 == fM[kScaleX] && 0 == fM[kSkewX] && 0 == fM[kTransX] &&
265 0 == fM[kSkewY] && GR_Scalar1 == fM[kScaleY] && 0 == fM[kTransY] &&
266 0 == fM[kPersp0] && 0 == fM[kPersp1] && gRESCALE == fM[kPersp2]));
267 return (0 == fTypeMask);
268}
269
270
271GrScalar GrMatrix::getMaxStretch() const {
272
273 if (fTypeMask & kPerspective_TypeBit) {
274 return -GR_Scalar1;
275 }
276
277 GrScalar stretch;
reed@google.com1fcd51e2011-01-05 15:50:27 +0000278
reed@google.comac10a2d2010-12-22 21:39:39 +0000279 if (isIdentity()) {
280 stretch = GR_Scalar1;
281 } else if (!(fTypeMask & kSkew_TypeBit)) {
282 stretch = GrMax(GrScalarAbs(fM[kScaleX]), GrScalarAbs(fM[kScaleY]));
reed@google.com1fcd51e2011-01-05 15:50:27 +0000283 } else if (fTypeMask & kZeroScale_TypeBit) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000284 stretch = GrMax(GrScalarAbs(fM[kSkewX]), GrScalarAbs(fM[kSkewY]));
reed@google.com1fcd51e2011-01-05 15:50:27 +0000285 } else {
reed@google.comac10a2d2010-12-22 21:39:39 +0000286 // ignore the translation part of the matrix, just look at 2x2 portion.
287 // compute singular values, take largest abs value.
reed@google.com1fcd51e2011-01-05 15:50:27 +0000288 // [a b; b c] = A^T*A
reed@google.comac10a2d2010-12-22 21:39:39 +0000289 GrScalar a = GrMul(fM[kScaleX], fM[kScaleX]) + GrMul(fM[kSkewY], fM[kSkewY]);
290 GrScalar b = GrMul(fM[kScaleX], fM[kSkewX]) + GrMul(fM[kScaleY], fM[kSkewY]);
291 GrScalar c = GrMul(fM[kSkewX], fM[kSkewX]) + GrMul(fM[kScaleY], fM[kScaleY]);
292 // eigenvalues of A^T*A are the squared singular values of A.
293 // characteristic equation is det((A^T*A) - l*I) = 0
294 // l^2 - (a + c)l + (ac-b^2)
295 // solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff
296 // and roots are guaraunteed to be pos and real).
297 GrScalar largerRoot;
298 GrScalar bSqd = GrMul(b,b);
299 // TODO: fixed point tolerance value.
300 if (bSqd < 1e-10) { // will be true if upper left 2x2 is orthogonal, which is common, so save some math
301 largerRoot = GrMax(a, c);
302 } else {
303 GrScalar aminusc = a - c;
304 GrScalar apluscdiv2 = (a + c) / 2;
305 GrScalar x = sqrtf(GrMul(aminusc,aminusc) + GrMul(4,(bSqd))) / 2;
306 largerRoot = apluscdiv2 + x;
307 }
reed@google.com1fcd51e2011-01-05 15:50:27 +0000308
reed@google.comac10a2d2010-12-22 21:39:39 +0000309 stretch = sqrtf(largerRoot);
310 }
311#if GR_DEBUG && 0
reed@google.com1fcd51e2011-01-05 15:50:27 +0000312 // test a bunch of vectors. None should be scaled by more than stretch
reed@google.comac10a2d2010-12-22 21:39:39 +0000313 // (modulo some error) and we should find a vector that is scaled by almost
314 // stretch.
315 GrPoint pt;
316 GrScalar max = 0;
317 for (int i = 0; i < 1000; ++i) {
318 GrScalar x = (float)rand() / RAND_MAX;
319 GrScalar y = sqrtf(1 - (x*x));
320 pt.fX = fM[kScaleX]*x + fM[kSkewX]*y;
321 pt.fY = fM[kSkewY]*x + fM[kScaleY]*y;
reed@google.com1fcd51e2011-01-05 15:50:27 +0000322 GrScalar d = pt.distanceToOrigin();
reed@google.comac10a2d2010-12-22 21:39:39 +0000323 GrAssert(d <= (1.0001 * stretch));
324 max = GrMax(max, pt.distanceToOrigin());
325 }
326 GrAssert((stretch - max) < .05*stretch);
327#endif
328 return stretch;
329}
330
331bool GrMatrix::operator == (const GrMatrix& m) const {
332 if (fTypeMask != m.fTypeMask) {
333 return false;
334 }
335 if (!fTypeMask) {
336 return true;
337 }
338 for (int i = 0; i < 9; ++i) {
339 if (m.fM[i] != fM[i]) {
340 return false;
341 }
342 }
343 return true;
344}
345
346bool GrMatrix::operator != (const GrMatrix& m) const {
347 return !(*this == m);
348}
349
350void GrMatrix::setTypeMask()
351{
352 fTypeMask = 0;
353 if (0 != fM[kPersp0] || 0 != fM[kPersp1] || gRESCALE != fM[kPersp2]) {
354 fTypeMask |= kPerspective_TypeBit;
355 }
356 if (GR_Scalar1 != fM[kScaleX] || GR_Scalar1 != fM[kScaleY]) {
357 fTypeMask |= kScale_TypeBit;
358 if (0 == fM[kScaleX] && 0 == fM[kScaleY]) {
359 fTypeMask |= kZeroScale_TypeBit;
360 }
361 }
362 if (0 != fM[kSkewX] || 0 != fM[kSkewY]) {
363 fTypeMask |= kSkew_TypeBit;
364 }
365 if (0 != fM[kTransX] || 0 != fM[kTransY]) {
366 fTypeMask |= kTranslate_TypeBit;
reed@google.com1fcd51e2011-01-05 15:50:27 +0000367 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000368}
369
370////////////////////////////////////////////////////////////////////////////////
371// Matrix transformation procs
372//////
373
374void GrMatrix::mapIdentity(GrPoint* dst, const GrPoint* src, uint32_t count) const {
375 if (src != dst) {
376 for (uint32_t i = 0; i < count; ++i) {
377 dst[i] = src[i];
378 }
379 }
380}
381
382void GrMatrix::mapScale(GrPoint* dst, const GrPoint* src, uint32_t count) const {
383 for (uint32_t i = 0; i < count; ++i) {
384 dst[i].fX = GrMul(src[i].fX, fM[kScaleX]);
385 dst[i].fY = GrMul(src[i].fY, fM[kScaleY]);
386 }
387}
388
389
390void GrMatrix::mapTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {
391 for (uint32_t i = 0; i < count; ++i) {
392 dst[i].fX = src[i].fX + fM[kTransX];
393 dst[i].fY = src[i].fY + fM[kTransY];
394 }
395}
396
397void GrMatrix::mapScaleAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {
398 for (uint32_t i = 0; i < count; ++i) {
399 dst[i].fX = GrMul(src[i].fX, fM[kScaleX]) + fM[kTransX];
400 dst[i].fY = GrMul(src[i].fY, fM[kScaleY]) + fM[kTransY];
401 }
402}
403
404void GrMatrix::mapSkew(GrPoint* dst, const GrPoint* src, uint32_t count) const {
405 if (src != dst) {
406 for (uint32_t i = 0; i < count; ++i) {
407 dst[i].fX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]);
408 dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]);
409 }
410 } else {
411 for (uint32_t i = 0; i < count; ++i) {
412 GrScalar newX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]);
413 dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]);
414 dst[i].fX = newX;
415 }
416 }
417}
418
419void GrMatrix::mapScaleAndSkew(GrPoint* dst, const GrPoint* src, uint32_t count) const {
420 if (src != dst) {
421 for (uint32_t i = 0; i < count; ++i) {
422 dst[i].fX = GrMul(src[i].fX, fM[kScaleX]) + GrMul(src[i].fY, fM[kSkewX]);
423 dst[i].fY = GrMul(src[i].fY, fM[kScaleY]) + GrMul(src[i].fX, fM[kSkewY]);
424 }
425 } else {
426 for (uint32_t i = 0; i < count; ++i) {
427 GrScalar newX = GrMul(src[i].fX, fM[kScaleX]) + GrMul(src[i].fY, fM[kSkewX]);
428 dst[i].fY = GrMul(src[i].fY, fM[kScaleY]) + GrMul(src[i].fX, fM[kSkewY]);
429 dst[i].fX = newX;
430 }
431 }
432}
433
434void GrMatrix::mapSkewAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {
435 if (src != dst) {
436 for (uint32_t i = 0; i < count; ++i) {
437 dst[i].fX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX];
438 dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY];
439 }
440 } else {
441 for (uint32_t i = 0; i < count; ++i) {
442 GrScalar newX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX];
443 dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY];
444 dst[i].fX = newX;
445 }
446 }
447}
448
449void GrMatrix::mapNonPerspective(GrPoint* dst, const GrPoint* src, uint32_t count) const {
450 if (src != dst) {
451 for (uint32_t i = 0; i < count; ++i) {
452 dst[i].fX = GrMul(fM[kScaleX], src[i].fX) + GrMul(fM[kSkewX], src[i].fY) + fM[kTransX];
reed@google.com1fcd51e2011-01-05 15:50:27 +0000453 dst[i].fY = GrMul(fM[kSkewY], src[i].fX) + GrMul(fM[kScaleY], src[i].fY) + fM[kTransY];
reed@google.comac10a2d2010-12-22 21:39:39 +0000454 }
455 } else {
456 for (uint32_t i = 0; i < count; ++i) {
457 GrScalar newX = GrMul(fM[kScaleX], src[i].fX) + GrMul(fM[kSkewX], src[i].fY) + fM[kTransX];
458 dst[i].fY = GrMul(fM[kSkewY], src[i].fX) + GrMul(fM[kScaleY], src[i].fY) + fM[kTransY];
459 dst[i].fX = newX;
460 }
461 }
462}
463
464void GrMatrix::mapPerspective(GrPoint* dst, const GrPoint* src, uint32_t count) const {
465 for (uint32_t i = 0; i < count; ++i) {
466 GrScalar x, y, w;
467 x = GrMul(fM[kScaleX], src[i].fX) + GrMul(fM[kSkewX], src[i].fY) + fM[kTransX];
468 y = GrMul(fM[kSkewY], src[i].fX) + GrMul(fM[kScaleY], src[i].fY) + fM[kTransY];
469 w = GrMul(fM[kPersp0], src[i].fX) + GrMul(fM[kPersp1], src[i].fY) + fM[kPersp2];
470 // TODO need fixed point invert
471 if (w) {
472 w = 1 / w;
473 }
474 dst[i].fX = GrMul(x, w);
reed@google.com1fcd51e2011-01-05 15:50:27 +0000475 dst[i].fY = GrMul(y, w);
reed@google.comac10a2d2010-12-22 21:39:39 +0000476 }
477}
478
479void GrMatrix::mapInvalid(GrPoint* dst, const GrPoint* src, uint32_t count) const {
480 GrAssert(0);
481}
482
483void GrMatrix::mapZero(GrPoint* dst, const GrPoint* src, uint32_t count) const {
484 memset(dst, 0, sizeof(GrPoint)*count);
485}
486
487void GrMatrix::mapSetToTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {
488 for (uint32_t i = 0; i < count; ++i) {
489 dst[i].fX = fM[kTransX];
reed@google.com1fcd51e2011-01-05 15:50:27 +0000490 dst[i].fY = fM[kTransY];
reed@google.comac10a2d2010-12-22 21:39:39 +0000491 }
492}
493
494void GrMatrix::mapSwappedScale(GrPoint* dst, const GrPoint* src, uint32_t count) const {
495 if (src != dst) {
496 for (uint32_t i = 0; i < count; ++i) {
497 dst[i].fX = GrMul(src[i].fY, fM[kSkewX]);
498 dst[i].fY = GrMul(src[i].fX, fM[kSkewY]);
499 }
500 } else {
501 for (uint32_t i = 0; i < count; ++i) {
502 GrScalar newX = GrMul(src[i].fY, fM[kSkewX]);
503 dst[i].fY = GrMul(src[i].fX, fM[kSkewY]);
504 dst[i].fX = newX;
505 }
506 }
507}
508
509void GrMatrix::mapSwappedScaleAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {
510 if (src != dst) {
511 for (uint32_t i = 0; i < count; ++i) {
512 dst[i].fX = GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX];
513 dst[i].fY = GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY];
514 }
515 } else {
516 for (uint32_t i = 0; i < count; ++i) {
517 GrScalar newX = GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX];
518 dst[i].fY = GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY];
519 dst[i].fX = newX;
520 }
521 }
522}
523
524///////////////////////////////////////////////////////////////////////////////
525// Unit test
526//////
527
528#include "GrRandom.h"
529
530#if GR_DEBUG
531enum MatrixType {
532 kRotate_MatrixType,
533 kScaleX_MatrixType,
534 kScaleY_MatrixType,
535 kSkewX_MatrixType,
536 kSkewY_MatrixType,
537 kTranslateX_MatrixType,
538 kTranslateY_MatrixType,
539 kSwapScaleXY_MatrixType,
540 kPersp_MatrixType,
reed@google.com1fcd51e2011-01-05 15:50:27 +0000541
reed@google.comac10a2d2010-12-22 21:39:39 +0000542 kMatrixTypeCount
543};
544
545static void create_matrix(GrMatrix* matrix, GrRandom& rand) {
546 MatrixType type = (MatrixType)(rand.nextU() % kMatrixTypeCount);
547 switch (type) {
548 case kRotate_MatrixType: {
549 float angle = rand.nextF() * 2 *3.14159265358979323846f;
550 GrScalar cosa = GrFloatToScalar(cosf(angle));
551 GrScalar sina = GrFloatToScalar(sinf(angle));
reed@google.com1fcd51e2011-01-05 15:50:27 +0000552 matrix->setAll(cosa, -sina, 0,
553 sina, cosa, 0,
reed@google.comac10a2d2010-12-22 21:39:39 +0000554 0, 0, GrMatrix::I()[8]);
555 } break;
556 case kScaleX_MatrixType: {
557 GrScalar scale = GrFloatToScalar(rand.nextF(-2, 2));
reed@google.com1fcd51e2011-01-05 15:50:27 +0000558 matrix->setAll(scale, 0, 0,
559 0, GR_Scalar1, 0,
reed@google.comac10a2d2010-12-22 21:39:39 +0000560 0, 0, GrMatrix::I()[8]);
561 } break;
562 case kScaleY_MatrixType: {
563 GrScalar scale = GrFloatToScalar(rand.nextF(-2, 2));
reed@google.com1fcd51e2011-01-05 15:50:27 +0000564 matrix->setAll(GR_Scalar1, 0, 0,
565 0, scale, 0,
reed@google.comac10a2d2010-12-22 21:39:39 +0000566 0, 0, GrMatrix::I()[8]);
567 } break;
568 case kSkewX_MatrixType: {
569 GrScalar skew = GrFloatToScalar(rand.nextF(-2, 2));
reed@google.com1fcd51e2011-01-05 15:50:27 +0000570 matrix->setAll(GR_Scalar1, skew, 0,
571 0, GR_Scalar1, 0,
reed@google.comac10a2d2010-12-22 21:39:39 +0000572 0, 0, GrMatrix::I()[8]);
reed@google.com1fcd51e2011-01-05 15:50:27 +0000573 } break;
reed@google.comac10a2d2010-12-22 21:39:39 +0000574 case kSkewY_MatrixType: {
575 GrScalar skew = GrFloatToScalar(rand.nextF(-2, 2));
reed@google.com1fcd51e2011-01-05 15:50:27 +0000576 matrix->setAll(GR_Scalar1, 0, 0,
577 skew, GR_Scalar1, 0,
reed@google.comac10a2d2010-12-22 21:39:39 +0000578 0, 0, GrMatrix::I()[8]);
579 } break;
580 case kTranslateX_MatrixType: {
581 GrScalar trans = GrFloatToScalar(rand.nextF(-10, 10));
reed@google.com1fcd51e2011-01-05 15:50:27 +0000582 matrix->setAll(GR_Scalar1, 0, trans,
583 0, GR_Scalar1, 0,
reed@google.comac10a2d2010-12-22 21:39:39 +0000584 0, 0, GrMatrix::I()[8]);
reed@google.com1fcd51e2011-01-05 15:50:27 +0000585 } break;
reed@google.comac10a2d2010-12-22 21:39:39 +0000586 case kTranslateY_MatrixType: {
587 GrScalar trans = GrFloatToScalar(rand.nextF(-10, 10));
reed@google.com1fcd51e2011-01-05 15:50:27 +0000588 matrix->setAll(GR_Scalar1, 0, 0,
589 0, GR_Scalar1, trans,
reed@google.comac10a2d2010-12-22 21:39:39 +0000590 0, 0, GrMatrix::I()[8]);
reed@google.com1fcd51e2011-01-05 15:50:27 +0000591 } break;
reed@google.comac10a2d2010-12-22 21:39:39 +0000592 case kSwapScaleXY_MatrixType: {
593 GrScalar xy = GrFloatToScalar(rand.nextF(-2, 2));
594 GrScalar yx = GrFloatToScalar(rand.nextF(-2, 2));
reed@google.com1fcd51e2011-01-05 15:50:27 +0000595 matrix->setAll(0, xy, 0,
596 yx, 0, 0,
reed@google.comac10a2d2010-12-22 21:39:39 +0000597 0, 0, GrMatrix::I()[8]);
598 } break;
599 case kPersp_MatrixType: {
600 GrScalar p0 = GrFloatToScalar(rand.nextF(-2, 2));
601 GrScalar p1 = GrFloatToScalar(rand.nextF(-2, 2));
602 GrScalar p2 = GrFloatToScalar(rand.nextF(-0.5f, 0.75f));
reed@google.com1fcd51e2011-01-05 15:50:27 +0000603 matrix->setAll(GR_Scalar1, 0, 0,
604 0, GR_Scalar1, 0,
reed@google.comac10a2d2010-12-22 21:39:39 +0000605 p0, p1, GrMul(p2,GrMatrix::I()[8]));
606 } break;
607 default:
608 GrAssert(0);
609 break;
610 }
611}
612#endif
613
614void GrMatrix::UnitTest() {
615 GrRandom rand;
616
reed@google.com1fcd51e2011-01-05 15:50:27 +0000617 // Create a bunch of matrices and test point mapping, max stretch calc,
reed@google.comac10a2d2010-12-22 21:39:39 +0000618 // inversion and multiply-by-inverse.
619#if GR_DEBUG
620 for (int i = 0; i < 10000; ++i) {
621 GrMatrix a, b;
622 a.setIdentity();
623 int num = rand.nextU() % 6;
624 // force testing of I and swapXY
625 if (0 == i) {
626 num = 0;
627 GrAssert(a.isIdentity());
628 } else if (1 == i) {
629 num = 0;
630 a.setAll(0, GR_Scalar1, 0,
reed@google.com1fcd51e2011-01-05 15:50:27 +0000631 GR_Scalar1, 0, 0,
reed@google.comac10a2d2010-12-22 21:39:39 +0000632 0, 0, I()[8]);
633 }
634 for (int j = 0; j < num; ++j) {
635 create_matrix(&b, rand);
636 a.preConcat(b);
637 }
reed@google.com1fcd51e2011-01-05 15:50:27 +0000638
reed@google.comac10a2d2010-12-22 21:39:39 +0000639 GrScalar maxStretch = a.getMaxStretch();
640 if (maxStretch > 0) {
641 maxStretch = GrMul(GR_Scalar1 + GR_Scalar1 / 100, maxStretch);
642 }
643 GrPoint origin = a.mapPoint(GrPoint(0,0));
644
645 for (int j = 0; j < 9; ++j) {
646 int mask, origMask = a.fTypeMask;
647 GrScalar old = a[j];
reed@google.com1fcd51e2011-01-05 15:50:27 +0000648
reed@google.comac10a2d2010-12-22 21:39:39 +0000649 a.set(j, GR_Scalar1);
650 mask = a.fTypeMask;
651 a.setTypeMask();
652 GrAssert(mask == a.fTypeMask);
653
654 a.set(j, 0);
655 mask = a.fTypeMask;
656 a.setTypeMask();
657 GrAssert(mask == a.fTypeMask);
658
659 a.set(j, 10 * GR_Scalar1);
660 mask = a.fTypeMask;
661 a.setTypeMask();
662 GrAssert(mask == a.fTypeMask);
reed@google.com1fcd51e2011-01-05 15:50:27 +0000663
664 a.set(j, old);
reed@google.comac10a2d2010-12-22 21:39:39 +0000665 GrAssert(a.fTypeMask == origMask);
666 }
reed@google.com1fcd51e2011-01-05 15:50:27 +0000667
reed@google.comac10a2d2010-12-22 21:39:39 +0000668 for (int j = 0; j < 100; ++j) {
669 GrPoint pt;
670 pt.fX = GrFloatToScalar(rand.nextF(-10, 10));
reed@google.com1fcd51e2011-01-05 15:50:27 +0000671 pt.fY = GrFloatToScalar(rand.nextF(-10, 10));
672
reed@google.comac10a2d2010-12-22 21:39:39 +0000673 GrPoint t0, t1, t2;
674 t0 = a.mapPoint(pt); // map to a new point
675 t1 = pt;
676 a.mapPoints(&t1, &t1, 1); // in place
677 a.mapPerspective(&t2, &pt, 1); // full mult
678 GrAssert(t0 == t1 && t1 == t2);
679 if (maxStretch >= 0.f) {
680 GrVec vec;
681 vec.setBetween(t0, origin);
682 GrScalar stretch = vec.length() / pt.distanceToOrigin();
683 GrAssert(stretch <= maxStretch);
684 }
685 }
686 double det = a.determinant();
687 if (fabs(det) > 1e-3 && a.invert(&b)) {
688 GrMatrix c;
689 c.setConcat(a,b);
690 for (int i = 0; i < 9; ++i) {
691 GrScalar diff = GrScalarAbs(c[i] - I()[i]);
692 GrAssert(diff < (5*GR_Scalar1 / 100));
693 }
694 }
695 }
696#endif
697}
698
699///////////////////////////////////////////////////////////////////////////////
700
701int Gr_clz(uint32_t n) {
702 if (0 == n) {
703 return 32;
704 }
705
706 int count = 0;
707 if (0 == (n & 0xFFFF0000)) {
708 count += 16;
709 n <<= 16;
710 }
711 if (0 == (n & 0xFF000000)) {
712 count += 8;
713 n <<= 8;
714 }
715 if (0 == (n & 0xF0000000)) {
716 count += 4;
717 n <<= 4;
718 }
719 if (0 == (n & 0xC0000000)) {
720 count += 2;
721 n <<= 2;
722 }
723 if (0 == (n & 0x80000000)) {
724 count += 1;
725 }
726 return count;
727}
728
729///////////////////////////////////////////////////////////////////////////////
730#include "GrRect.h"
731
732void GrRect::setBounds(const GrPoint pts[], int count) {
733 if (count <= 0) {
734 this->setEmpty();
735 } else {
736 GrScalar L, R, T, B;
737 L = R = pts[0].fX;
738 T = B = pts[0].fY;
739 for (int i = 1; i < count; i++) {
740 GrScalar x = pts[i].fX;
741 GrScalar y = pts[i].fY;
742 if (x < L) {
743 L = x;
744 } else if (x > R) {
745 R = x;
746 }
747 if (y < T) {
748 T = y;
749 } else if (y > B) {
750 B = y;
751 }
752 }
753 this->setLTRB(L, T, R, B);
754 }
755}
756
reed@google.com1fcd51e2011-01-05 15:50:27 +0000757
758