1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
3 
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10 
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13 
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 
23 #include "vmatrix.h"
24 #include <vglobal.h>
25 #include <cassert>
26 #include <cmath>
27 
28 V_BEGIN_NAMESPACE
29 
30 /*  m11  m21  mtx
31  *  m12  m22  mty
32  *  m13  m23  m33
33  */
34 
determinant() const35 inline float VMatrix::determinant() const
36 {
37     return m11 * (m33 * m22 - mty * m23) - m21 * (m33 * m12 - mty * m13) +
38            mtx * (m23 * m12 - m22 * m13);
39 }
40 
isAffine() const41 bool VMatrix::isAffine() const
42 {
43     return type() < MatrixType::Project;
44 }
45 
isIdentity() const46 bool VMatrix::isIdentity() const
47 {
48     return type() == MatrixType::None;
49 }
50 
isInvertible() const51 bool VMatrix::isInvertible() const
52 {
53     return !vIsZero(determinant());
54 }
55 
isScaling() const56 bool VMatrix::isScaling() const
57 {
58     return type() >= MatrixType::Scale;
59 }
isRotating() const60 bool VMatrix::isRotating() const
61 {
62     return type() >= MatrixType::Rotate;
63 }
64 
isTranslating() const65 bool VMatrix::isTranslating() const
66 {
67     return type() >= MatrixType::Translate;
68 }
69 
operator *=(float num)70 VMatrix &VMatrix::operator*=(float num)
71 {
72     if (num == 1.) return *this;
73 
74     m11 *= num;
75     m12 *= num;
76     m13 *= num;
77     m21 *= num;
78     m22 *= num;
79     m23 *= num;
80     mtx *= num;
81     mty *= num;
82     m33 *= num;
83     if (dirty < MatrixType::Scale) dirty = MatrixType::Scale;
84 
85     return *this;
86 }
87 
operator /=(float div)88 VMatrix &VMatrix::operator/=(float div)
89 {
90     if (div == 0) return *this;
91 
92     div = 1 / div;
93     return operator*=(div);
94 }
95 
type() const96 VMatrix::MatrixType VMatrix::type() const
97 {
98     if (dirty == MatrixType::None || dirty < mType) return mType;
99 
100     switch (dirty) {
101     case MatrixType::Project:
102         if (!vIsZero(m13) || !vIsZero(m23) || !vIsZero(m33 - 1)) {
103             mType = MatrixType::Project;
104             break;
105         }
106         VECTOR_FALLTHROUGH
107     case MatrixType::Shear:
108     case MatrixType::Rotate:
109         if (!vIsZero(m12) || !vIsZero(m21)) {
110             const float dot = m11 * m12 + m21 * m22;
111             if (vIsZero(dot))
112                 mType = MatrixType::Rotate;
113             else
114                 mType = MatrixType::Shear;
115             break;
116         }
117         VECTOR_FALLTHROUGH
118     case MatrixType::Scale:
119         if (!vIsZero(m11 - 1) || !vIsZero(m22 - 1)) {
120             mType = MatrixType::Scale;
121             break;
122         }
123         VECTOR_FALLTHROUGH
124     case MatrixType::Translate:
125         if (!vIsZero(mtx) || !vIsZero(mty)) {
126             mType = MatrixType::Translate;
127             break;
128         }
129         VECTOR_FALLTHROUGH
130     case MatrixType::None:
131         mType = MatrixType::None;
132         break;
133     }
134 
135     dirty = MatrixType::None;
136     return mType;
137 }
138 
translate(float dx,float dy)139 VMatrix &VMatrix::translate(float dx, float dy)
140 {
141     if (dx == 0 && dy == 0) return *this;
142 
143     switch (type()) {
144     case MatrixType::None:
145         mtx = dx;
146         mty = dy;
147         break;
148     case MatrixType::Translate:
149         mtx += dx;
150         mty += dy;
151         break;
152     case MatrixType::Scale:
153         mtx += dx * m11;
154         mty += dy * m22;
155         break;
156     case MatrixType::Project:
157         m33 += dx * m13 + dy * m23;
158         VECTOR_FALLTHROUGH
159     case MatrixType::Shear:
160     case MatrixType::Rotate:
161         mtx += dx * m11 + dy * m21;
162         mty += dy * m22 + dx * m12;
163         break;
164     }
165     if (dirty < MatrixType::Translate) dirty = MatrixType::Translate;
166     return *this;
167 }
168 
scale(float sx,float sy)169 VMatrix &VMatrix::scale(float sx, float sy)
170 {
171     if (sx == 1 && sy == 1) return *this;
172 
173     switch (type()) {
174     case MatrixType::None:
175     case MatrixType::Translate:
176         m11 = sx;
177         m22 = sy;
178         break;
179     case MatrixType::Project:
180         m13 *= sx;
181         m23 *= sy;
182         VECTOR_FALLTHROUGH
183     case MatrixType::Rotate:
184     case MatrixType::Shear:
185         m12 *= sx;
186         m21 *= sy;
187         VECTOR_FALLTHROUGH
188     case MatrixType::Scale:
189         m11 *= sx;
190         m22 *= sy;
191         break;
192     }
193     if (dirty < MatrixType::Scale) dirty = MatrixType::Scale;
194     return *this;
195 }
196 
shear(float sh,float sv)197 VMatrix &VMatrix::shear(float sh, float sv)
198 {
199     if (sh == 0 && sv == 0) return *this;
200 
201     switch (type()) {
202     case MatrixType::None:
203     case MatrixType::Translate:
204         m12 = sv;
205         m21 = sh;
206         break;
207     case MatrixType::Scale:
208         m12 = sv * m22;
209         m21 = sh * m11;
210         break;
211     case MatrixType::Project: {
212         float tm13 = sv * m23;
213         float tm23 = sh * m13;
214         m13 += tm13;
215         m23 += tm23;
216         VECTOR_FALLTHROUGH
217     }
218     case MatrixType::Rotate:
219     case MatrixType::Shear: {
220         float tm11 = sv * m21;
221         float tm22 = sh * m12;
222         float tm12 = sv * m22;
223         float tm21 = sh * m11;
224         m11 += tm11;
225         m12 += tm12;
226         m21 += tm21;
227         m22 += tm22;
228         break;
229     }
230     }
231     if (dirty < MatrixType::Shear) dirty = MatrixType::Shear;
232     return *this;
233 }
234 
235 static const float deg2rad = float(0.017453292519943295769);  // pi/180
236 static const float inv_dist_to_plane = 1. / 1024.;
237 
rotate(float a,Axis axis)238 VMatrix &VMatrix::rotate(float a, Axis axis)
239 {
240     if (a == 0) return *this;
241 
242     float sina = 0;
243     float cosa = 0;
244     if (a == 90. || a == -270.)
245         sina = 1.;
246     else if (a == 270. || a == -90.)
247         sina = -1.;
248     else if (a == 180.)
249         cosa = -1.;
250     else {
251         float b = deg2rad * a;  // convert to radians
252         sina = std::sin(b);     // fast and convenient
253         cosa = std::cos(b);
254     }
255 
256     if (axis == Axis::Z) {
257         switch (type()) {
258         case MatrixType::None:
259         case MatrixType::Translate:
260             m11 = cosa;
261             m12 = sina;
262             m21 = -sina;
263             m22 = cosa;
264             break;
265         case MatrixType::Scale: {
266             float tm11 = cosa * m11;
267             float tm12 = sina * m22;
268             float tm21 = -sina * m11;
269             float tm22 = cosa * m22;
270             m11 = tm11;
271             m12 = tm12;
272             m21 = tm21;
273             m22 = tm22;
274             break;
275         }
276         case MatrixType::Project: {
277             float tm13 = cosa * m13 + sina * m23;
278             float tm23 = -sina * m13 + cosa * m23;
279             m13 = tm13;
280             m23 = tm23;
281             VECTOR_FALLTHROUGH
282         }
283         case MatrixType::Rotate:
284         case MatrixType::Shear: {
285             float tm11 = cosa * m11 + sina * m21;
286             float tm12 = cosa * m12 + sina * m22;
287             float tm21 = -sina * m11 + cosa * m21;
288             float tm22 = -sina * m12 + cosa * m22;
289             m11 = tm11;
290             m12 = tm12;
291             m21 = tm21;
292             m22 = tm22;
293             break;
294         }
295         }
296         if (dirty < MatrixType::Rotate) dirty = MatrixType::Rotate;
297     } else {
298         VMatrix result;
299         if (axis == Axis::Y) {
300             result.m11 = cosa;
301             result.m13 = -sina * inv_dist_to_plane;
302         } else {
303             result.m22 = cosa;
304             result.m23 = -sina * inv_dist_to_plane;
305         }
306         result.mType = MatrixType::Project;
307         *this = result * *this;
308     }
309 
310     return *this;
311 }
312 
operator *(const VMatrix & m) const313 VMatrix VMatrix::operator*(const VMatrix &m) const
314 {
315     const MatrixType otherType = m.type();
316     if (otherType == MatrixType::None) return *this;
317 
318     const MatrixType thisType = type();
319     if (thisType == MatrixType::None) return m;
320 
321     VMatrix    t;
322     MatrixType type = vMax(thisType, otherType);
323     switch (type) {
324     case MatrixType::None:
325         break;
326     case MatrixType::Translate:
327         t.mtx = mtx + m.mtx;
328         t.mty += mty + m.mty;
329         break;
330     case MatrixType::Scale: {
331         float m11v = m11 * m.m11;
332         float m22v = m22 * m.m22;
333 
334         float m31v = mtx * m.m11 + m.mtx;
335         float m32v = mty * m.m22 + m.mty;
336 
337         t.m11 = m11v;
338         t.m22 = m22v;
339         t.mtx = m31v;
340         t.mty = m32v;
341         break;
342     }
343     case MatrixType::Rotate:
344     case MatrixType::Shear: {
345         float m11v = m11 * m.m11 + m12 * m.m21;
346         float m12v = m11 * m.m12 + m12 * m.m22;
347 
348         float m21v = m21 * m.m11 + m22 * m.m21;
349         float m22v = m21 * m.m12 + m22 * m.m22;
350 
351         float m31v = mtx * m.m11 + mty * m.m21 + m.mtx;
352         float m32v = mtx * m.m12 + mty * m.m22 + m.mty;
353 
354         t.m11 = m11v;
355         t.m12 = m12v;
356         t.m21 = m21v;
357         t.m22 = m22v;
358         t.mtx = m31v;
359         t.mty = m32v;
360         break;
361     }
362     case MatrixType::Project: {
363         float m11v = m11 * m.m11 + m12 * m.m21 + m13 * m.mtx;
364         float m12v = m11 * m.m12 + m12 * m.m22 + m13 * m.mty;
365         float m13v = m11 * m.m13 + m12 * m.m23 + m13 * m.m33;
366 
367         float m21v = m21 * m.m11 + m22 * m.m21 + m23 * m.mtx;
368         float m22v = m21 * m.m12 + m22 * m.m22 + m23 * m.mty;
369         float m23v = m21 * m.m13 + m22 * m.m23 + m23 * m.m33;
370 
371         float m31v = mtx * m.m11 + mty * m.m21 + m33 * m.mtx;
372         float m32v = mtx * m.m12 + mty * m.m22 + m33 * m.mty;
373         float m33v = mtx * m.m13 + mty * m.m23 + m33 * m.m33;
374 
375         t.m11 = m11v;
376         t.m12 = m12v;
377         t.m13 = m13v;
378         t.m21 = m21v;
379         t.m22 = m22v;
380         t.m23 = m23v;
381         t.mtx = m31v;
382         t.mty = m32v;
383         t.m33 = m33v;
384     }
385     }
386 
387     t.dirty = type;
388     t.mType = type;
389 
390     return t;
391 }
392 
operator *=(const VMatrix & o)393 VMatrix &VMatrix::operator*=(const VMatrix &o)
394 {
395     const MatrixType otherType = o.type();
396     if (otherType == MatrixType::None) return *this;
397 
398     const MatrixType thisType = type();
399     if (thisType == MatrixType::None) return operator=(o);
400 
401     MatrixType t = vMax(thisType, otherType);
402     switch (t) {
403     case MatrixType::None:
404         break;
405     case MatrixType::Translate:
406         mtx += o.mtx;
407         mty += o.mty;
408         break;
409     case MatrixType::Scale: {
410         float m11v = m11 * o.m11;
411         float m22v = m22 * o.m22;
412 
413         float m31v = mtx * o.m11 + o.mtx;
414         float m32v = mty * o.m22 + o.mty;
415 
416         m11 = m11v;
417         m22 = m22v;
418         mtx = m31v;
419         mty = m32v;
420         break;
421     }
422     case MatrixType::Rotate:
423     case MatrixType::Shear: {
424         float m11v = m11 * o.m11 + m12 * o.m21;
425         float m12v = m11 * o.m12 + m12 * o.m22;
426 
427         float m21v = m21 * o.m11 + m22 * o.m21;
428         float m22v = m21 * o.m12 + m22 * o.m22;
429 
430         float m31v = mtx * o.m11 + mty * o.m21 + o.mtx;
431         float m32v = mtx * o.m12 + mty * o.m22 + o.mty;
432 
433         m11 = m11v;
434         m12 = m12v;
435         m21 = m21v;
436         m22 = m22v;
437         mtx = m31v;
438         mty = m32v;
439         break;
440     }
441     case MatrixType::Project: {
442         float m11v = m11 * o.m11 + m12 * o.m21 + m13 * o.mtx;
443         float m12v = m11 * o.m12 + m12 * o.m22 + m13 * o.mty;
444         float m13v = m11 * o.m13 + m12 * o.m23 + m13 * o.m33;
445 
446         float m21v = m21 * o.m11 + m22 * o.m21 + m23 * o.mtx;
447         float m22v = m21 * o.m12 + m22 * o.m22 + m23 * o.mty;
448         float m23v = m21 * o.m13 + m22 * o.m23 + m23 * o.m33;
449 
450         float m31v = mtx * o.m11 + mty * o.m21 + m33 * o.mtx;
451         float m32v = mtx * o.m12 + mty * o.m22 + m33 * o.mty;
452         float m33v = mtx * o.m13 + mty * o.m23 + m33 * o.m33;
453 
454         m11 = m11v;
455         m12 = m12v;
456         m13 = m13v;
457         m21 = m21v;
458         m22 = m22v;
459         m23 = m23v;
460         mtx = m31v;
461         mty = m32v;
462         m33 = m33v;
463     }
464     }
465 
466     dirty = t;
467     mType = t;
468 
469     return *this;
470 }
471 
adjoint() const472 VMatrix VMatrix::adjoint() const
473 {
474     float h11, h12, h13, h21, h22, h23, h31, h32, h33;
475     h11 = m22 * m33 - m23 * mty;
476     h21 = m23 * mtx - m21 * m33;
477     h31 = m21 * mty - m22 * mtx;
478     h12 = m13 * mty - m12 * m33;
479     h22 = m11 * m33 - m13 * mtx;
480     h32 = m12 * mtx - m11 * mty;
481     h13 = m12 * m23 - m13 * m22;
482     h23 = m13 * m21 - m11 * m23;
483     h33 = m11 * m22 - m12 * m21;
484 
485     VMatrix res;
486     res.m11 = h11;
487     res.m12 = h12;
488     res.m13 = h13;
489     res.m21 = h21;
490     res.m22 = h22;
491     res.m23 = h23;
492     res.mtx = h31;
493     res.mty = h32;
494     res.m33 = h33;
495     res.mType = MatrixType::None;
496     res.dirty = MatrixType::Project;
497 
498     return res;
499 }
500 
inverted(bool * invertible) const501 VMatrix VMatrix::inverted(bool *invertible) const
502 {
503     VMatrix invert;
504     bool    inv = true;
505 
506     switch (type()) {
507     case MatrixType::None:
508         break;
509     case MatrixType::Translate:
510         invert.mtx = -mtx;
511         invert.mty = -mty;
512         break;
513     case MatrixType::Scale:
514         inv = !vIsZero(m11);
515         inv &= !vIsZero(m22);
516         if (inv) {
517             invert.m11 = 1.0f / m11;
518             invert.m22 = 1.0f / m22;
519             invert.mtx = -mtx * invert.m11;
520             invert.mty = -mty * invert.m22;
521         }
522         break;
523     default:
524         // general case
525         float det = determinant();
526         inv = !vIsZero(det);
527         if (inv) invert = (adjoint() /= det);
528         // TODO Test above line
529         break;
530     }
531 
532     if (invertible) *invertible = inv;
533 
534     if (inv) {
535         // inverting doesn't change the type
536         invert.mType = mType;
537         invert.dirty = dirty;
538     }
539 
540     return invert;
541 }
542 
operator ==(const VMatrix & o) const543 bool VMatrix::operator==(const VMatrix &o) const
544 {
545     return fuzzyCompare(o);
546 }
547 
operator !=(const VMatrix & o) const548 bool VMatrix::operator!=(const VMatrix &o) const
549 {
550     return !operator==(o);
551 }
552 
fuzzyCompare(const VMatrix & o) const553 bool VMatrix::fuzzyCompare(const VMatrix &o) const
554 {
555     return vCompare(m11, o.m11) && vCompare(m12, o.m12) &&
556            vCompare(m21, o.m21) && vCompare(m22, o.m22) &&
557            vCompare(mtx, o.mtx) && vCompare(mty, o.mty);
558 }
559 
560 #define V_NEAR_CLIP 0.000001f
561 #ifdef MAP
562 #undef MAP
563 #endif
564 #define MAP(x, y, nx, ny)                                \
565     do {                                                 \
566         float FX_ = x;                                   \
567         float FY_ = y;                                   \
568         switch (t) {                                     \
569         case MatrixType::None:                           \
570             nx = FX_;                                    \
571             ny = FY_;                                    \
572             break;                                       \
573         case MatrixType::Translate:                      \
574             nx = FX_ + mtx;                              \
575             ny = FY_ + mty;                              \
576             break;                                       \
577         case MatrixType::Scale:                          \
578             nx = m11 * FX_ + mtx;                        \
579             ny = m22 * FY_ + mty;                        \
580             break;                                       \
581         case MatrixType::Rotate:                         \
582         case MatrixType::Shear:                          \
583         case MatrixType::Project:                        \
584             nx = m11 * FX_ + m21 * FY_ + mtx;            \
585             ny = m12 * FX_ + m22 * FY_ + mty;            \
586             if (t == MatrixType::Project) {              \
587                 float w = (m13 * FX_ + m23 * FY_ + m33); \
588                 if (w < V_NEAR_CLIP) w = V_NEAR_CLIP;    \
589                 w = 1. / w;                              \
590                 nx *= w;                                 \
591                 ny *= w;                                 \
592             }                                            \
593         }                                                \
594     } while (0)
595 
map(const VRect & rect) const596 VRect VMatrix::map(const VRect &rect) const
597 {
598     VMatrix::MatrixType t = type();
599     if (t <= MatrixType::Translate)
600         return rect.translated(std::lround(mtx), std::lround(mty));
601 
602     if (t <= MatrixType::Scale) {
603         int x = std::lround(m11 * rect.x() + mtx);
604         int y = std::lround(m22 * rect.y() + mty);
605         int w = std::lround(m11 * rect.width());
606         int h = std::lround(m22 * rect.height());
607         if (w < 0) {
608             w = -w;
609             x -= w;
610         }
611         if (h < 0) {
612             h = -h;
613             y -= h;
614         }
615         return {x, y, w, h};
616     } else if (t < MatrixType::Project) {
617         // see mapToPolygon for explanations of the algorithm.
618         float x = 0, y = 0;
619         MAP(rect.left(), rect.top(), x, y);
620         float xmin = x;
621         float ymin = y;
622         float xmax = x;
623         float ymax = y;
624         MAP(rect.right() + 1, rect.top(), x, y);
625         xmin = vMin(xmin, x);
626         ymin = vMin(ymin, y);
627         xmax = vMax(xmax, x);
628         ymax = vMax(ymax, y);
629         MAP(rect.right() + 1, rect.bottom() + 1, x, y);
630         xmin = vMin(xmin, x);
631         ymin = vMin(ymin, y);
632         xmax = vMax(xmax, x);
633         ymax = vMax(ymax, y);
634         MAP(rect.left(), rect.bottom() + 1, x, y);
635         xmin = vMin(xmin, x);
636         ymin = vMin(ymin, y);
637         xmax = vMax(xmax, x);
638         ymax = vMax(ymax, y);
639         return VRect(std::lround(xmin), std::lround(ymin),
640                      std::lround(xmax) - std::lround(xmin),
641                      std::lround(ymax) - std::lround(ymin));
642     } else {
643         // Not supported
644         assert(0);
645         return {};
646     }
647 }
648 
map(const VPointF & p) const649 VPointF VMatrix::map(const VPointF &p) const
650 {
651     float fx = p.x();
652     float fy = p.y();
653 
654     float x = 0, y = 0;
655 
656     VMatrix::MatrixType t = type();
657     switch (t) {
658     case MatrixType::None:
659         x = fx;
660         y = fy;
661         break;
662     case MatrixType::Translate:
663         x = fx + mtx;
664         y = fy + mty;
665         break;
666     case MatrixType::Scale:
667         x = m11 * fx + mtx;
668         y = m22 * fy + mty;
669         break;
670     case MatrixType::Rotate:
671     case MatrixType::Shear:
672     case MatrixType::Project:
673         x = m11 * fx + m21 * fy + mtx;
674         y = m12 * fx + m22 * fy + mty;
675         if (t == MatrixType::Project) {
676             float w = 1.0f / (m13 * fx + m23 * fy + m33);
677             x *= w;
678             y *= w;
679         }
680     }
681     return {x, y};
682 }
683 
684 V_END_NAMESPACE
685