1// [Blend2D]
2// 2D Vector Graphics Powered by a JIT Compiler.
3//
4// [License]
5// Zlib - See LICENSE.md file in the package.
6
7#include "./blapi-build_p.h"
8#include "./blmath_p.h"
9#include "./blmatrix_p.h"
10#include "./blruntime_p.h"
11#include "./blsimd_p.h"
12
13// ============================================================================
14// [BLMatrix2D - Global Variables]
15// ============================================================================
16
17const BLMatrix2D blMatrix2DIdentity { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 };
18
19BLMapPointDArrayFunc blMatrix2DMapPointDArrayFuncs[BL_MATRIX2D_TYPE_COUNT];
20
21// ============================================================================
22// [BLMatrix2D - Reset]
23// ============================================================================
24
25BLResult blMatrix2DSetIdentity(BLMatrix2D* self) noexcept {
26 self->reset(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
27 return BL_SUCCESS;
28}
29
30BLResult blMatrix2DSetTranslation(BLMatrix2D* self, double x, double y) noexcept {
31 self->reset(1.0, 0.0, 0.0, 1.0, x, y);
32 return BL_SUCCESS;
33}
34
35BLResult blMatrix2DSetScaling(BLMatrix2D* self, double x, double y) noexcept {
36 self->reset(x, 0.0, 0.0, y, 0.0, 0.0);
37 return BL_SUCCESS;
38}
39
40BLResult blMatrix2DSetSkewing(BLMatrix2D* self, double x, double y) noexcept {
41 double xTan = blTan(x);
42 double yTan = blTan(y);
43
44 self->reset(1.0, yTan, xTan, 1.0, 0.0, 0.0);
45 return BL_SUCCESS;
46}
47
48BLResult blMatrix2DSetRotation(BLMatrix2D* self, double angle, double x, double y) noexcept {
49 double as = blSin(angle);
50 double ac = blCos(angle);
51
52 self->reset(ac, as, -as, ac, x, y);
53 return BL_SUCCESS;
54}
55
56// ============================================================================
57// [BLMatrix2D - Ops]
58// ============================================================================
59
60BLResult blMatrix2DApplyOp(BLMatrix2D* self, uint32_t opType, const void* opData) noexcept {
61 BLMatrix2D* a = self;
62 const double* data = static_cast<const double*>(opData);
63
64 switch (opType) {
65 // |1 0|
66 // A' = |0 1|
67 // |0 0|
68 case BL_MATRIX2D_OP_RESET:
69 a->reset();
70 return BL_SUCCESS;
71
72 //
73 // A' = B
74 //
75 case BL_MATRIX2D_OP_ASSIGN:
76 a->reset(*static_cast<const BLMatrix2D*>(opData));
77 return BL_SUCCESS;
78
79 // [1 0]
80 // A' = [0 1] * A
81 // [X Y]
82 case BL_MATRIX2D_OP_TRANSLATE: {
83 double x = data[0];
84 double y = data[1];
85
86 a->m20 += x * a->m00 + y * a->m10;
87 a->m21 += x * a->m01 + y * a->m11;
88
89 return BL_SUCCESS;
90 }
91
92 // [X 0]
93 // A' = [0 Y] * A
94 // [0 0]
95 case BL_MATRIX2D_OP_SCALE: {
96 double x = data[0];
97 double y = data[1];
98
99 a->m00 *= x;
100 a->m01 *= x;
101 a->m10 *= y;
102 a->m11 *= y;
103
104 return BL_SUCCESS;
105 }
106
107 // [ 1 tan(y)]
108 // A' = [tan(x) 1 ] * A
109 // [ 0 0 ]
110 case BL_MATRIX2D_OP_SKEW: {
111 double x = data[0];
112 double y = data[1];
113 double xTan = blTan(x);
114 double yTan = blTan(y);
115
116 double t00 = yTan * a->m10;
117 double t01 = yTan * a->m11;
118
119 a->m10 += xTan * a->m00;
120 a->m11 += xTan * a->m01;
121
122 a->m00 += t00;
123 a->m01 += t01;
124
125 return BL_SUCCESS;
126 }
127
128 // Tx and Ty are zero unless rotating about a point:
129 //
130 // Tx = Px - cos(a) * Px + sin(a) * Py
131 // Ty = Py - sin(a) * Px - cos(a) * Py
132 //
133 // [ cos(a) sin(a)]
134 // A' = [-sin(a) cos(a)] * A
135 // [ Tx Ty ]
136 case BL_MATRIX2D_OP_ROTATE:
137 case BL_MATRIX2D_OP_ROTATE_PT: {
138 double angle = data[0];
139 double as = blSin(angle);
140 double ac = blCos(angle);
141
142 double t00 = as * a->m10 + ac * a->m00;
143 double t01 = as * a->m11 + ac * a->m01;
144 double t10 = ac * a->m10 - as * a->m00;
145 double t11 = ac * a->m11 - as * a->m01;
146
147 if (opType == BL_MATRIX2D_OP_ROTATE_PT) {
148 double px = data[1];
149 double py = data[2];
150
151 double tx = px - ac * px + as * py;
152 double ty = py - as * px - ac * py;
153
154 double t20 = tx * a->m00 + ty * a->m10 + a->m20;
155 double t21 = tx * a->m01 + ty * a->m11 + a->m21;
156
157 a->m20 = t20;
158 a->m21 = t21;
159 }
160
161 a->m00 = t00;
162 a->m01 = t01;
163
164 a->m10 = t10;
165 a->m11 = t11;
166
167 return BL_SUCCESS;
168 }
169
170 // A' = B * A
171 case BL_MATRIX2D_OP_TRANSFORM: {
172 const BLMatrix2D* b = static_cast<const BLMatrix2D*>(opData);
173
174 a->reset(b->m00 * a->m00 + b->m01 * a->m10,
175 b->m00 * a->m01 + b->m01 * a->m11,
176 b->m10 * a->m00 + b->m11 * a->m10,
177 b->m10 * a->m01 + b->m11 * a->m11,
178 b->m20 * a->m00 + b->m21 * a->m10 + a->m20,
179 b->m20 * a->m01 + b->m21 * a->m11 + a->m21);
180
181 return BL_SUCCESS;
182 }
183
184 // [1 0]
185 // A' = A * [0 1]
186 // [X Y]
187 case BL_MATRIX2D_OP_POST_TRANSLATE: {
188 double x = data[0];
189 double y = data[1];
190
191 a->m20 += x;
192 a->m21 += y;
193
194 return BL_SUCCESS;
195 }
196
197 // [X 0]
198 // A' = A * [0 Y]
199 // [0 0]
200 case BL_MATRIX2D_OP_POST_SCALE: {
201 double x = data[0];
202 double y = data[1];
203
204 a->m00 *= x;
205 a->m01 *= y;
206 a->m10 *= x;
207 a->m11 *= y;
208 a->m20 *= x;
209 a->m21 *= y;
210
211 return BL_SUCCESS;
212 }
213
214 // [ 1 tan(y)]
215 // A' = A * [tan(x) 1 ]
216 // [ 0 0 ]
217 case BL_MATRIX2D_OP_POST_SKEW:{
218 double x = data[0];
219 double y = data[1];
220 double xTan = blTan(x);
221 double yTan = blTan(y);
222
223 double t00 = a->m01 * xTan;
224 double t10 = a->m11 * xTan;
225 double t20 = a->m21 * xTan;
226
227 a->m01 += a->m00 * yTan;
228 a->m11 += a->m10 * yTan;
229 a->m21 += a->m20 * yTan;
230
231 a->m00 += t00;
232 a->m10 += t10;
233 a->m20 += t20;
234
235 return BL_SUCCESS;
236 }
237
238 // [ cos(a) sin(a)]
239 // A' = A * [-sin(a) cos(a)]
240 // [ x' y' ]
241 case BL_MATRIX2D_OP_POST_ROTATE:
242 case BL_MATRIX2D_OP_POST_ROTATE_PT: {
243 double angle = data[0];
244 double as = blSin(angle);
245 double ac = blCos(angle);
246
247 double t00 = a->m00 * ac - a->m01 * as;
248 double t01 = a->m00 * as + a->m01 * ac;
249 double t10 = a->m10 * ac - a->m11 * as;
250 double t11 = a->m10 * as + a->m11 * ac;
251 double t20 = a->m20 * ac - a->m21 * as;
252 double t21 = a->m20 * as + a->m21 * ac;
253
254 a->reset(t00, t01, t10, t11, t20, t21);
255 if (opType != BL_MATRIX2D_OP_POST_ROTATE_PT)
256 return BL_SUCCESS;
257
258 double px = data[1];
259 double py = data[2];
260
261 a->m20 = t20 + px - ac * px + as * py;
262 a->m21 = t21 + py - as * px - ac * py;
263
264 return BL_SUCCESS;
265 }
266
267 // A' = A * B
268 case BL_MATRIX2D_OP_POST_TRANSFORM: {
269 const BLMatrix2D* b = static_cast<const BLMatrix2D*>(opData);
270
271 a->reset(a->m00 * b->m00 + a->m01 * b->m10,
272 a->m00 * b->m01 + a->m01 * b->m11,
273 a->m10 * b->m00 + a->m11 * b->m10,
274 a->m10 * b->m01 + a->m11 * b->m11,
275 a->m20 * b->m00 + a->m21 * b->m10 + b->m20,
276 a->m20 * b->m01 + a->m21 * b->m11 + b->m21);
277
278 return BL_SUCCESS;
279 }
280
281 default:
282 return blTraceError(BL_ERROR_INVALID_VALUE);
283 }
284}
285
286BLResult blMatrix2DInvert(BLMatrix2D* dst, const BLMatrix2D* src) noexcept {
287 double d = src->m00 * src->m11 - src->m01 * src->m10;
288
289 if (d == 0.0)
290 return blTraceError(BL_ERROR_INVALID_VALUE);
291
292 double t00 = src->m11;
293 double t01 = -src->m01;
294 double t10 = -src->m10;
295 double t11 = src->m00;
296
297 t00 /= d;
298 t01 /= d;
299 t10 /= d;
300 t11 /= d;
301
302 double t20 = -(src->m20 * t00 + src->m21 * t10);
303 double t21 = -(src->m20 * t01 + src->m21 * t11);
304
305 dst->reset(t00, t01, t10, t11, t20, t21);
306 return BL_SUCCESS;
307}
308
309// ============================================================================
310// [BLMatrix2D - Type]
311// ============================================================================
312
313uint32_t blMatrix2DGetType(const BLMatrix2D* self) noexcept {
314 double m00 = self->m00;
315 double m01 = self->m01;
316 double m10 = self->m10;
317 double m11 = self->m11;
318 double m20 = self->m20;
319 double m21 = self->m21;
320
321 const uint32_t kBit00 = 1u << 3;
322 const uint32_t kBit01 = 1u << 2;
323 const uint32_t kBit10 = 1u << 1;
324 const uint32_t kBit11 = 1u << 0;
325
326 #if defined(BL_TARGET_OPT_SSE2)
327
328 // NOTE: Ideally this should be somewhere else, but for simplicity and easier
329 // displatch it was placed here. We do this as C++ compilers still cannot figure
330 // out how to do this and this is a much better solution compared to scalar
331 // versions compilers produce.
332 using namespace SIMD;
333 uint32_t valueMsk = uint32_t(_mm_movemask_pd(vcmpnepd(vsetd128(m00, m01), vzerod128())) << 2) |
334 uint32_t(_mm_movemask_pd(vcmpnepd(vsetd128(m10, m11), vzerod128())) << 0) ;
335 #else
336 uint32_t valueMsk = (uint32_t(m00 != 0.0) << 3) | (uint32_t(m01 != 0.0) << 2) |
337 (uint32_t(m10 != 0.0) << 1) | (uint32_t(m11 != 0.0) << 0) ;
338 #endif
339
340 // Bit-table that contains ones for `valueMsk` combinations that are considered valid.
341 uint32_t validTab = (0u << (0 | 0 | 0 | 0 )) | // [m00==0 m01==0 m10==0 m11==0]
342 (0u << (0 | 0 | 0 | kBit11)) | // [m00==0 m01==0 m10==0 m11!=0]
343 (0u << (0 | 0 | kBit10 | 0 )) | // [m00==0 m01==0 m10!=0 m11==0]
344 (1u << (0 | 0 | kBit10 | kBit11)) | // [m00==0 m01==0 m10!=0 m11!=0]
345 (0u << (0 | kBit01 | 0 | 0 )) | // [m00==0 m01!=0 m10==0 m11==0]
346 (0u << (0 | kBit01 | 0 | kBit11)) | // [m00==0 m01!=0 m10==0 m11!=0]
347 (1u << (0 | kBit01 | kBit10 | 0 )) | // [m00==0 m01!=0 m10!=0 m11==0] [SWAP]
348 (1u << (0 | kBit01 | kBit10 | kBit11)) | // [m00==0 m01!=0 m10!=0 m11!=0]
349 (0u << (kBit00 | 0 | 0 | 0 )) | // [m00!=0 m01==0 m10==0 m11==0]
350 (1u << (kBit00 | 0 | 0 | kBit11)) | // [m00!=0 m01==0 m10==0 m11!=0] [SCALE]
351 (0u << (kBit00 | 0 | kBit10 | 0 )) | // [m00!=0 m01==0 m10!=0 m11==0]
352 (1u << (kBit00 | 0 | kBit10 | kBit11)) | // [m00!=0 m01==0 m10!=0 m11!=0] [AFFINE]
353 (1u << (kBit00 | kBit01 | 0 | 0 )) | // [m00!=0 m01!=0 m10==0 m11==0]
354 (1u << (kBit00 | kBit01 | 0 | kBit11)) | // [m00!=0 m01!=0 m10==0 m11!=0] [AFFINE]
355 (1u << (kBit00 | kBit01 | kBit10 | 0 )) | // [m00!=0 m01!=0 m10!=0 m11==0] [AFFINE]
356 (1u << (kBit00 | kBit01 | kBit10 | kBit11)) ; // [m00!=0 m01!=0 m10!=0 m11!=0] [AFFINE]
357
358 double d = m00 * m11 - m01 * m10;
359 if (!((1u << valueMsk) & validTab) || !blIsFinite(d) || !blIsFinite(m20) || !blIsFinite(m21))
360 return BL_MATRIX2D_TYPE_INVALID;
361
362 // Matrix is not swap/affine if:
363 // [. 0]
364 // [0 .]
365 // [. .]
366 if (valueMsk != (kBit00 | kBit11))
367 return (valueMsk == (kBit01 | kBit10))
368 ? BL_MATRIX2D_TYPE_SWAP
369 : BL_MATRIX2D_TYPE_AFFINE;
370
371 // Matrix is not scaling if:
372 // [1 .]
373 // [. 1]
374 // [. .]
375 if (!((m00 == 1.0) & (m11 == 1.0)))
376 return BL_MATRIX2D_TYPE_SCALE;
377
378 // Matrix is not translation if:
379 // [. .]
380 // [. .]
381 // [0 0]
382 if (!((m20 == 0.0) & (m21 == 0.0)))
383 return BL_MATRIX2D_TYPE_TRANSLATE;
384
385 return BL_MATRIX2D_TYPE_IDENTITY;
386}
387
388// ============================================================================
389// [BLMatrix2D - Map]
390// ============================================================================
391
392BLResult blMatrix2DMapPointDArray(const BLMatrix2D* self, BLPoint* dst, const BLPoint* src, size_t count) noexcept {
393 uint32_t matrixType = BL_MATRIX2D_TYPE_AFFINE;
394
395 if (count >= BL_MATRIX_TYPE_MINIMUM_SIZE)
396 matrixType = self->type();
397
398 return blMatrix2DMapPointDArrayFuncs[matrixType](self, dst, src, count);
399}
400
401// ============================================================================
402// [BLMatrix2D - MapPointDArray]
403// ============================================================================
404
405BL_DIAGNOSTIC_PUSH(BL_DIAGNOSTIC_NO_UNUSED_FUNCTIONS)
406
407static BLResult BL_CDECL blMatrix2DMapPointDArrayIdentity(const BLMatrix2D* self, BLPoint* dst, const BLPoint* src, size_t size) noexcept {
408 BL_UNUSED(self);
409 if (dst == src)
410 return BL_SUCCESS;
411
412 for (size_t i = 0; i < size; i++)
413 dst[i] = src[i];
414
415 return BL_SUCCESS;
416}
417
418static BLResult BL_CDECL blMatrix2DMapPointDArrayTranslate(const BLMatrix2D* self, BLPoint* dst, const BLPoint* src, size_t size) noexcept {
419 double m20 = self->m20;
420 double m21 = self->m21;
421
422 for (size_t i = 0; i < size; i++)
423 dst[i].reset(src[i].x + m20,
424 src[i].y + m21);
425
426 return BL_SUCCESS;
427}
428
429static BLResult BL_CDECL blMatrix2DMapPointDArrayScale(const BLMatrix2D* self, BLPoint* dst, const BLPoint* src, size_t size) noexcept {
430 double m00 = self->m00;
431 double m11 = self->m11;
432 double m20 = self->m20;
433 double m21 = self->m21;
434
435 for (size_t i = 0; i < size; i++)
436 dst[i].reset(src[i].x * m00 + m20,
437 src[i].y * m11 + m21);
438
439 return BL_SUCCESS;
440}
441
442static BLResult BL_CDECL blMatrix2DMapPointDArraySwap(const BLMatrix2D* self, BLPoint* dst, const BLPoint* src, size_t size) noexcept {
443 double m10 = self->m10;
444 double m01 = self->m01;
445 double m20 = self->m20;
446 double m21 = self->m21;
447
448 for (size_t i = 0; i < size; i++)
449 dst[i].reset(src[i].y * m10 + m20,
450 src[i].x * m01 + m21);
451
452 return BL_SUCCESS;
453}
454
455static BLResult BL_CDECL blMatrix2DMapPointDArrayAffine(const BLMatrix2D* self, BLPoint* dst, const BLPoint* src, size_t size) noexcept {
456 double m00 = self->m00;
457 double m01 = self->m01;
458 double m10 = self->m10;
459 double m11 = self->m11;
460 double m20 = self->m20;
461 double m21 = self->m21;
462
463 for (size_t i = 0; i < size; i++)
464 dst[i].reset(src[i].x * m00 + src[i].y * m10 + m20,
465 src[i].x * m01 + src[i].y * m11 + m21);
466
467 return BL_SUCCESS;
468}
469
470BL_DIAGNOSTIC_POP
471
472// ============================================================================
473// [BLMatrix2D - Runtime Init]
474// ============================================================================
475
476#ifdef BL_BUILD_OPT_SSE2
477BL_HIDDEN void blMatrix2DRtInit_SSE2(BLRuntimeContext* rt) noexcept;
478#endif
479
480#ifdef BL_BUILD_OPT_AVX
481BL_HIDDEN void blMatrix2DRtInit_AVX(BLRuntimeContext* rt) noexcept;
482#endif
483
484void blMatrix2DRtInit(BLRuntimeContext* rt) noexcept {
485 #if !defined(BL_TARGET_OPT_SSE2)
486 BL_UNUSED(rt);
487 BLMapPointDArrayFunc* funcs = blMatrix2DMapPointDArrayFuncs;
488
489 blAssignFunc(&funcs[BL_MATRIX2D_TYPE_IDENTITY ], blMatrix2DMapPointDArrayIdentity);
490 blAssignFunc(&funcs[BL_MATRIX2D_TYPE_TRANSLATE], blMatrix2DMapPointDArrayTranslate);
491 blAssignFunc(&funcs[BL_MATRIX2D_TYPE_SCALE ], blMatrix2DMapPointDArrayScale);
492 blAssignFunc(&funcs[BL_MATRIX2D_TYPE_SWAP ], blMatrix2DMapPointDArraySwap);
493 blAssignFunc(&funcs[BL_MATRIX2D_TYPE_AFFINE ], blMatrix2DMapPointDArrayAffine);
494 blAssignFunc(&funcs[BL_MATRIX2D_TYPE_INVALID ], blMatrix2DMapPointDArrayAffine);
495 #endif
496
497 #ifdef BL_BUILD_OPT_SSE2
498 if (blRuntimeHasSSE2(rt)) blMatrix2DRtInit_SSE2(rt);
499 #endif
500
501 #ifdef BL_BUILD_OPT_AVX
502 if (blRuntimeHasAVX(rt)) blMatrix2DRtInit_AVX(rt);
503 #endif
504}
505
506// ============================================================================
507// [BLMatrix2D - Unit Tests]
508// ============================================================================
509
510#ifdef BL_TEST
511UNIT(blend2d_matrix) {
512 INFO("Testing matrix types");
513 {
514 BLMatrix2D m;
515
516 m = BLMatrix2D::makeIdentity();
517 EXPECT(m.type() == BL_MATRIX2D_TYPE_IDENTITY);
518
519 m = BLMatrix2D::makeTranslation(1.0, 2.0);
520 EXPECT(m.type() == BL_MATRIX2D_TYPE_TRANSLATE);
521
522 m = BLMatrix2D::makeScaling(2.0, 2.0);
523 EXPECT(m.type() == BL_MATRIX2D_TYPE_SCALE);
524
525 m.m10 = 3.0;
526 EXPECT(m.type() == BL_MATRIX2D_TYPE_AFFINE);
527
528 m.reset(0.0, 1.0, 1.0, 0.0, 0.0, 0.0);
529 EXPECT(m.type() == BL_MATRIX2D_TYPE_SWAP);
530
531 m.reset(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
532 EXPECT(m.type() == BL_MATRIX2D_TYPE_INVALID);
533 }
534
535 INFO("Testing whether special-case transformations match matrix multiplication");
536 {
537 enum BL_TEST_MATRIX : uint32_t {
538 BL_TEST_MATRIX_IDENTITY,
539 BL_TEST_MATRIX_TRANSLATE,
540 BL_TEST_MATRIX_SCALE,
541 BL_TEST_MATRIX_SKEW,
542 BL_TEST_MATRIX_ROTATE,
543 BL_TEST_MATRIX_COUNT
544 };
545
546 static const BLPoint ptOffset(128.0, 64.0);
547 static const BLPoint ptScale(1.5, 2.0);
548 static const BLPoint ptSkew(1.5, 2.0);
549 static const double angle = 0.9;
550
551 auto testMatrixName = [](uint32_t type) noexcept -> const char* {
552 switch (type) {
553 case BL_TEST_MATRIX_IDENTITY : return "Identity";
554 case BL_TEST_MATRIX_TRANSLATE: return "Translate";
555 case BL_TEST_MATRIX_SCALE : return "Scale";
556 case BL_TEST_MATRIX_SKEW : return "Skew";
557 case BL_TEST_MATRIX_ROTATE : return "Rotate";
558 default: return "Unknown";
559 }
560 };
561
562 auto createTestMatrix = [](uint32_t type) noexcept -> BLMatrix2D {
563 switch (type) {
564 case BL_TEST_MATRIX_TRANSLATE: return BLMatrix2D::makeTranslation(ptOffset);
565 case BL_TEST_MATRIX_SCALE : return BLMatrix2D::makeScaling(ptScale);
566 case BL_TEST_MATRIX_SKEW : return BLMatrix2D::makeSkewing(ptSkew);
567 case BL_TEST_MATRIX_ROTATE : return BLMatrix2D::makeRotation(angle);
568
569 default:
570 return BLMatrix2D::makeIdentity();
571 }
572 };
573
574 auto compare = [](const BLMatrix2D& a, const BLMatrix2D& b) noexcept -> bool {
575 double diff = blMax(blAbs(a.m00 - b.m00),
576 blAbs(a.m01 - b.m01),
577 blAbs(a.m10 - b.m10),
578 blAbs(a.m11 - b.m11),
579 blAbs(a.m20 - b.m20),
580 blAbs(a.m21 - b.m21));
581 // If Blend2D is compiled with FMA enabled there could be a difference
582 // greater than our blEpsilon<double>, so use a more relaxed value here.
583 return diff < 1e-8;
584 };
585
586 BLMatrix2D m, n;
587 BLMatrix2D a = BLMatrix2D::makeIdentity();
588 BLMatrix2D b;
589
590 for (uint32_t aType = 0; aType < BL_TEST_MATRIX_COUNT; aType++) {
591 for (uint32_t bType = 0; bType < BL_TEST_MATRIX_COUNT; bType++) {
592 a = createTestMatrix(aType);
593 b = createTestMatrix(bType);
594
595 m = a;
596 n = a;
597
598 for (uint32_t post = 0; post < 2; post++) {
599 if (!post)
600 m.transform(b);
601 else
602 m.postTransform(b);
603
604 switch (bType) {
605 case BL_TEST_MATRIX_IDENTITY:
606 break;
607
608 case BL_TEST_MATRIX_TRANSLATE:
609 if (!post)
610 n.translate(ptOffset);
611 else
612 n.postTranslate(ptOffset);
613 break;
614
615 case BL_TEST_MATRIX_SCALE:
616 if (!post)
617 n.scale(ptScale);
618 else
619 n.postScale(ptScale);
620 break;
621
622 case BL_TEST_MATRIX_SKEW:
623 if (!post)
624 n.skew(ptSkew);
625 else
626 n.postSkew(ptSkew);
627 break;
628
629 case BL_TEST_MATRIX_ROTATE:
630 if (!post)
631 n.rotate(angle);
632 else
633 n.postRotate(angle);
634 break;
635 }
636
637 if (!compare(m, n)) {
638 INFO("Matrices don't match [%s x %s]\n", testMatrixName(aType), testMatrixName(bType));
639 INFO(" [% 3.14f | % 3.14f] [% 3.14f | % 3.14f]\n", a.m00, a.m01, b.m00, b.m01);
640 INFO(" A [% 3.14f | % 3.14f] B [% 3.14f | % 3.14f]\n", a.m10, a.m11, b.m10, b.m11);
641 INFO(" [% 3.14f | % 3.14f] [% 3.14f | % 3.14f]\n", a.m20, a.m21, b.m20, b.m21);
642 INFO("\n");
643 INFO("Operation: %s\n", post ? "M = A * B" : "M = B * A");
644 INFO(" [% 3.14f | % 3.14f] [% 3.14f | % 3.14f]\n", m.m00, m.m01, n.m00, n.m01);
645 INFO(" M [% 3.14f | % 3.14f] != N [% 3.14f | % 3.14f]\n", m.m10, m.m11, n.m10, n.m11);
646 INFO(" [% 3.14f | % 3.14f] [% 3.14f | % 3.14f]\n", m.m20, m.m21, n.m20, n.m21);
647 EXPECT(false);
648 }
649 }
650 }
651 }
652 }
653}
654#endif
655