Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qtransform.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3#include "qtransform.h"
4
5#include "qdatastream.h"
6#include "qdebug.h"
7#include "qhashfunctions.h"
8#include "qregion.h"
9#include "qpainterpath.h"
10#include "qpainterpath_p.h"
11#include "qvariant.h"
12#include "qmath_p.h"
13#include <qnumeric.h>
14
15#include <private/qbezier_p.h>
16
18
19#ifndef QT_NO_DEBUG
21static void nanWarning(const char *func)
22{
23 qWarning("QTransform::%s with NaN called", func);
24}
25#endif // QT_NO_DEBUG
26
27#define Q_NEAR_CLIP (sizeof(qreal) == sizeof(double) ? 0.000001 : 0.0001)
28
29#ifdef MAP
30# undef MAP
31#endif
32#define MAP(x, y, nx, ny) \
33 do { \
34 qreal FX_ = x; \
35 qreal FY_ = y; \
36 switch(t) { \
37 case TxNone: \
38 nx = FX_; \
39 ny = FY_; \
40 break; \
41 case TxTranslate: \
42 nx = FX_ + m_matrix[2][0]; \
43 ny = FY_ + m_matrix[2][1]; \
44 break; \
45 case TxScale: \
46 nx = m_matrix[0][0] * FX_ + m_matrix[2][0]; \
47 ny = m_matrix[1][1] * FY_ + m_matrix[2][1]; \
48 break; \
49 case TxRotate: \
50 case TxShear: \
51 case TxProject: \
52 nx = m_matrix[0][0] * FX_ + m_matrix[1][0] * FY_ + m_matrix[2][0]; \
53 ny = m_matrix[0][1] * FX_ + m_matrix[1][1] * FY_ + m_matrix[2][1]; \
54 if (t == TxProject) { \
55 qreal w = (m_matrix[0][2] * FX_ + m_matrix[1][2] * FY_ + m_matrix[2][2]); \
56 if (w < qreal(Q_NEAR_CLIP)) w = qreal(Q_NEAR_CLIP); \
57 w = 1./w; \
58 nx *= w; \
59 ny *= w; \
60 } \
61 } \
62 } while (0)
63
263{
264 qreal h11, h12, h13,
265 h21, h22, h23,
266 h31, h32, h33;
267 h11 = m_matrix[1][1] * m_matrix[2][2] - m_matrix[1][2] * m_matrix[2][1];
268 h21 = m_matrix[1][2] * m_matrix[2][0] - m_matrix[1][0] * m_matrix[2][2];
269 h31 = m_matrix[1][0] * m_matrix[2][1] - m_matrix[1][1] * m_matrix[2][0];
270 h12 = m_matrix[0][2] * m_matrix[2][1] - m_matrix[0][1] * m_matrix[2][2];
271 h22 = m_matrix[0][0] * m_matrix[2][2] - m_matrix[0][2] * m_matrix[2][0];
272 h32 = m_matrix[0][1] * m_matrix[2][0] - m_matrix[0][0] * m_matrix[2][1];
273 h13 = m_matrix[0][1] * m_matrix[1][2] - m_matrix[0][2] * m_matrix[1][1];
274 h23 = m_matrix[0][2] * m_matrix[1][0] - m_matrix[0][0] * m_matrix[1][2];
275 h33 = m_matrix[0][0] * m_matrix[1][1] - m_matrix[0][1] * m_matrix[1][0];
276
277 return QTransform(h11, h12, h13,
278 h21, h22, h23,
279 h31, h32, h33);
280}
281
286{
287 QTransform t(m_matrix[0][0], m_matrix[1][0], m_matrix[2][0],
288 m_matrix[0][1], m_matrix[1][1], m_matrix[2][1],
289 m_matrix[0][2], m_matrix[1][2], m_matrix[2][2]);
290 return t;
291}
292
303QTransform QTransform::inverted(bool *invertible) const
304{
306 bool inv = true;
307
308 switch(inline_type()) {
309 case TxNone:
310 break;
311 case TxTranslate:
312 invert.m_matrix[2][0] = -m_matrix[2][0];
313 invert.m_matrix[2][1] = -m_matrix[2][1];
314 break;
315 case TxScale:
316 inv = !qFuzzyIsNull(m_matrix[0][0]);
317 inv &= !qFuzzyIsNull(m_matrix[1][1]);
318 if (inv) {
319 invert.m_matrix[0][0] = 1. / m_matrix[0][0];
320 invert.m_matrix[1][1] = 1. / m_matrix[1][1];
321 invert.m_matrix[2][0] = -m_matrix[2][0] * invert.m_matrix[0][0];
322 invert.m_matrix[2][1] = -m_matrix[2][1] * invert.m_matrix[1][1];
323 }
324 break;
325// case TxRotate:
326// case TxShear:
327// invert.affine = affine.inverted(&inv);
328// break;
329 default:
330 // general case
331 qreal det = determinant();
332 inv = !qFuzzyIsNull(det);
333 if (inv)
334 invert = adjoint() / det;
335 break;
336 }
337
338 if (invertible)
339 *invertible = inv;
340
341 if (inv) {
342 // inverting doesn't change the type
343 invert.m_type = m_type;
344 invert.m_dirty = m_dirty;
345 }
346
347 return invert;
348}
349
357{
358 if (dx == 0 && dy == 0)
359 return *this;
360#ifndef QT_NO_DEBUG
361 if (qIsNaN(dx) || qIsNaN(dy)) {
362 nanWarning("translate");
363 return *this;
364 }
365#endif
366
367 switch(inline_type()) {
368 case TxNone:
369 m_matrix[2][0] = dx;
370 m_matrix[2][1] = dy;
371 break;
372 case TxTranslate:
373 m_matrix[2][0] += dx;
374 m_matrix[2][1] += dy;
375 break;
376 case TxScale:
377 m_matrix[2][0] += dx * m_matrix[0][0];
378 m_matrix[2][1] += dy * m_matrix[1][1];
379 break;
380 case TxProject:
381 m_matrix[2][2] += dx * m_matrix[0][2] + dy * m_matrix[1][2];
383 case TxShear:
384 case TxRotate:
385 m_matrix[2][0] += dx * m_matrix[0][0] + dy * m_matrix[1][0];
386 m_matrix[2][1] += dy * m_matrix[1][1] + dx * m_matrix[0][1];
387 break;
388 }
389 if (m_dirty < TxTranslate)
390 m_dirty = TxTranslate;
391 return *this;
392}
393
402{
403#ifndef QT_NO_DEBUG
404 if (qIsNaN(dx) || qIsNaN(dy)) {
405 nanWarning("fromTranslate");
406 return QTransform();
407}
408#endif
409 QTransform transform(1, 0, 0, 0, 1, 0, dx, dy, 1);
410 if (dx == 0 && dy == 0)
411 transform.m_type = TxNone;
412 else
413 transform.m_type = TxTranslate;
414 transform.m_dirty = TxNone;
415 return transform;
416}
417
425{
426 if (sx == 1 && sy == 1)
427 return *this;
428#ifndef QT_NO_DEBUG
429 if (qIsNaN(sx) || qIsNaN(sy)) {
430 nanWarning("scale");
431 return *this;
432 }
433#endif
434
435 switch(inline_type()) {
436 case TxNone:
437 case TxTranslate:
438 m_matrix[0][0] = sx;
439 m_matrix[1][1] = sy;
440 break;
441 case TxProject:
442 m_matrix[0][2] *= sx;
443 m_matrix[1][2] *= sy;
445 case TxRotate:
446 case TxShear:
447 m_matrix[0][1] *= sx;
448 m_matrix[1][0] *= sy;
450 case TxScale:
451 m_matrix[0][0] *= sx;
452 m_matrix[1][1] *= sy;
453 break;
454 }
455 if (m_dirty < TxScale)
456 m_dirty = TxScale;
457 return *this;
458}
459
468{
469#ifndef QT_NO_DEBUG
470 if (qIsNaN(sx) || qIsNaN(sy)) {
471 nanWarning("fromScale");
472 return QTransform();
473}
474#endif
475 QTransform transform(sx, 0, 0, 0, sy, 0, 0, 0, 1);
476 if (sx == 1. && sy == 1.)
477 transform.m_type = TxNone;
478 else
479 transform.m_type = TxScale;
480 transform.m_dirty = TxNone;
481 return transform;
482}
483
491{
492 if (sh == 0 && sv == 0)
493 return *this;
494#ifndef QT_NO_DEBUG
495 if (qIsNaN(sh) || qIsNaN(sv)) {
496 nanWarning("shear");
497 return *this;
498 }
499#endif
500
501 switch(inline_type()) {
502 case TxNone:
503 case TxTranslate:
504 m_matrix[0][1] = sv;
505 m_matrix[1][0] = sh;
506 break;
507 case TxScale:
508 m_matrix[0][1] = sv*m_matrix[1][1];
509 m_matrix[1][0] = sh*m_matrix[0][0];
510 break;
511 case TxProject: {
512 qreal tm13 = sv * m_matrix[1][2];
513 qreal tm23 = sh * m_matrix[0][2];
514 m_matrix[0][2] += tm13;
515 m_matrix[1][2] += tm23;
516 }
518 case TxRotate:
519 case TxShear: {
520 qreal tm11 = sv * m_matrix[1][0];
521 qreal tm22 = sh * m_matrix[0][1];
522 qreal tm12 = sv * m_matrix[1][1];
523 qreal tm21 = sh * m_matrix[0][0];
524 m_matrix[0][0] += tm11;
525 m_matrix[0][1] += tm12;
526 m_matrix[1][0] += tm21;
527 m_matrix[1][1] += tm22;
528 break;
529 }
530 }
531 if (m_dirty < TxShear)
532 m_dirty = TxShear;
533 return *this;
534}
535
544
550
558{
559 if (a == 0)
560 return *this;
561#ifndef QT_NO_DEBUG
562 if (qIsNaN(a) || qIsNaN(distanceToPlane)) {
563 nanWarning("rotate");
564 return *this;
565 }
566#endif
567
568 qreal sina = 0;
569 qreal cosa = 0;
570 if (a == 90. || a == -270.)
571 sina = 1.;
572 else if (a == 270. || a == -90.)
573 sina = -1.;
574 else if (a == 180.)
575 cosa = -1.;
576 else{
578 sina = qSin(b); // fast and convenient
579 cosa = qCos(b);
580 }
581
582 if (axis == Qt::ZAxis) {
583 switch(inline_type()) {
584 case TxNone:
585 case TxTranslate:
586 m_matrix[0][0] = cosa;
587 m_matrix[0][1] = sina;
588 m_matrix[1][0] = -sina;
589 m_matrix[1][1] = cosa;
590 break;
591 case TxScale: {
592 qreal tm11 = cosa * m_matrix[0][0];
593 qreal tm12 = sina * m_matrix[1][1];
594 qreal tm21 = -sina * m_matrix[0][0];
595 qreal tm22 = cosa * m_matrix[1][1];
596 m_matrix[0][0] = tm11;
597 m_matrix[0][1] = tm12;
598 m_matrix[1][0] = tm21;
599 m_matrix[1][1] = tm22;
600 break;
601 }
602 case TxProject: {
603 qreal tm13 = cosa * m_matrix[0][2] + sina * m_matrix[1][2];
604 qreal tm23 = -sina * m_matrix[0][2] + cosa * m_matrix[1][2];
605 m_matrix[0][2] = tm13;
606 m_matrix[1][2] = tm23;
608 }
609 case TxRotate:
610 case TxShear: {
611 qreal tm11 = cosa * m_matrix[0][0] + sina * m_matrix[1][0];
612 qreal tm12 = cosa * m_matrix[0][1] + sina * m_matrix[1][1];
613 qreal tm21 = -sina * m_matrix[0][0] + cosa * m_matrix[1][0];
614 qreal tm22 = -sina * m_matrix[0][1] + cosa * m_matrix[1][1];
615 m_matrix[0][0] = tm11;
616 m_matrix[0][1] = tm12;
617 m_matrix[1][0] = tm21;
618 m_matrix[1][1] = tm22;
619 break;
620 }
621 }
622 if (m_dirty < TxRotate)
623 m_dirty = TxRotate;
624 } else {
625 if (!qIsNull(distanceToPlane))
626 sina /= distanceToPlane;
627
629 if (axis == Qt::YAxis) {
630 result.m_matrix[0][0] = cosa;
631 result.m_matrix[0][2] = -sina;
632 } else {
633 result.m_matrix[1][1] = cosa;
634 result.m_matrix[1][2] = -sina;
635 }
636 result.m_type = TxProject;
637 *this = result * *this;
638 }
639
640 return *this;
641}
642
643#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
656{
657 return rotate(a, axis, 1024.0);
658}
659#endif
660
669
675
683{
684#ifndef QT_NO_DEBUG
685 if (qIsNaN(a) || qIsNaN(distanceToPlane)) {
686 nanWarning("rotateRadians");
687 return *this;
688 }
689#endif
690 qreal sina = qSin(a);
691 qreal cosa = qCos(a);
692
693 if (axis == Qt::ZAxis) {
694 switch(inline_type()) {
695 case TxNone:
696 case TxTranslate:
697 m_matrix[0][0] = cosa;
698 m_matrix[0][1] = sina;
699 m_matrix[1][0] = -sina;
700 m_matrix[1][1] = cosa;
701 break;
702 case TxScale: {
703 qreal tm11 = cosa * m_matrix[0][0];
704 qreal tm12 = sina * m_matrix[1][1];
705 qreal tm21 = -sina * m_matrix[0][0];
706 qreal tm22 = cosa * m_matrix[1][1];
707 m_matrix[0][0] = tm11;
708 m_matrix[0][1] = tm12;
709 m_matrix[1][0] = tm21;
710 m_matrix[1][1] = tm22;
711 break;
712 }
713 case TxProject: {
714 qreal tm13 = cosa * m_matrix[0][2] + sina * m_matrix[1][2];
715 qreal tm23 = -sina * m_matrix[0][2] + cosa * m_matrix[1][2];
716 m_matrix[0][2] = tm13;
717 m_matrix[1][2] = tm23;
719 }
720 case TxRotate:
721 case TxShear: {
722 qreal tm11 = cosa * m_matrix[0][0] + sina * m_matrix[1][0];
723 qreal tm12 = cosa * m_matrix[0][1] + sina * m_matrix[1][1];
724 qreal tm21 = -sina * m_matrix[0][0] + cosa * m_matrix[1][0];
725 qreal tm22 = -sina * m_matrix[0][1] + cosa * m_matrix[1][1];
726 m_matrix[0][0] = tm11;
727 m_matrix[0][1] = tm12;
728 m_matrix[1][0] = tm21;
729 m_matrix[1][1] = tm22;
730 break;
731 }
732 }
733 if (m_dirty < TxRotate)
734 m_dirty = TxRotate;
735 } else {
736 if (!qIsNull(distanceToPlane))
737 sina /= distanceToPlane;
738
740 if (axis == Qt::YAxis) {
741 result.m_matrix[0][0] = cosa;
742 result.m_matrix[0][2] = -sina;
743 } else {
744 result.m_matrix[1][1] = cosa;
745 result.m_matrix[1][2] = -sina;
746 }
747 result.m_type = TxProject;
748 *this = result * *this;
749 }
750 return *this;
751}
752
753#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
766{
767 return rotateRadians(a, axis, 1024.0);
768}
769#endif
770
777{
778 return m_matrix[0][0] == o.m_matrix[0][0] &&
779 m_matrix[0][1] == o.m_matrix[0][1] &&
780 m_matrix[1][0] == o.m_matrix[1][0] &&
781 m_matrix[1][1] == o.m_matrix[1][1] &&
782 m_matrix[2][0] == o.m_matrix[2][0] &&
783 m_matrix[2][1] == o.m_matrix[2][1] &&
784 m_matrix[0][2] == o.m_matrix[0][2] &&
785 m_matrix[1][2] == o.m_matrix[1][2] &&
786 m_matrix[2][2] == o.m_matrix[2][2];
787}
788
796size_t qHash(const QTransform &key, size_t seed) noexcept
797{
799 seed = hash(seed, key.m11());
800 seed = hash(seed, key.m12());
801 seed = hash(seed, key.m21());
802 seed = hash(seed, key.m22());
803 seed = hash(seed, key.dx());
804 seed = hash(seed, key.dy());
805 seed = hash(seed, key.m13());
806 seed = hash(seed, key.m23());
807 seed = hash(seed, key.m33());
808 return seed;
809}
810
811
818{
819 return !operator==(o);
820}
821
830{
831 const TransformationType otherType = o.inline_type();
832 if (otherType == TxNone)
833 return *this;
834
835 const TransformationType thisType = inline_type();
836 if (thisType == TxNone)
837 return operator=(o);
838
839 TransformationType t = qMax(thisType, otherType);
840 switch(t) {
841 case TxNone:
842 break;
843 case TxTranslate:
844 m_matrix[2][0] += o.m_matrix[2][0];
845 m_matrix[2][1] += o.m_matrix[2][1];
846 break;
847 case TxScale:
848 {
849 qreal m11 = m_matrix[0][0] * o.m_matrix[0][0];
850 qreal m22 = m_matrix[1][1] * o.m_matrix[1][1];
851
852 qreal m31 = m_matrix[2][0] * o.m_matrix[0][0] + o.m_matrix[2][0];
853 qreal m32 = m_matrix[2][1] * o.m_matrix[1][1] + o.m_matrix[2][1];
854
855 m_matrix[0][0] = m11;
856 m_matrix[1][1] = m22;
857 m_matrix[2][0] = m31; m_matrix[2][1] = m32;
858 break;
859 }
860 case TxRotate:
861 case TxShear:
862 {
863 qreal m11 = m_matrix[0][0] * o.m_matrix[0][0] + m_matrix[0][1] * o.m_matrix[1][0];
864 qreal m12 = m_matrix[0][0] * o.m_matrix[0][1] + m_matrix[0][1] * o.m_matrix[1][1];
865
866 qreal m21 = m_matrix[1][0] * o.m_matrix[0][0] + m_matrix[1][1] * o.m_matrix[1][0];
867 qreal m22 = m_matrix[1][0] * o.m_matrix[0][1] + m_matrix[1][1] * o.m_matrix[1][1];
868
869 qreal m31 = m_matrix[2][0] * o.m_matrix[0][0] + m_matrix[2][1] * o.m_matrix[1][0] + o.m_matrix[2][0];
870 qreal m32 = m_matrix[2][0] * o.m_matrix[0][1] + m_matrix[2][1] * o.m_matrix[1][1] + o.m_matrix[2][1];
871
872 m_matrix[0][0] = m11;
873 m_matrix[0][1] = m12;
874 m_matrix[1][0] = m21;
875 m_matrix[1][1] = m22;
876 m_matrix[2][0] = m31;
877 m_matrix[2][1] = m32;
878 break;
879 }
880 case TxProject:
881 {
882 qreal m11 = m_matrix[0][0] * o.m_matrix[0][0] + m_matrix[0][1] * o.m_matrix[1][0] + m_matrix[0][2] * o.m_matrix[2][0];
883 qreal m12 = m_matrix[0][0] * o.m_matrix[0][1] + m_matrix[0][1] * o.m_matrix[1][1] + m_matrix[0][2] * o.m_matrix[2][1];
884 qreal m13 = m_matrix[0][0] * o.m_matrix[0][2] + m_matrix[0][1] * o.m_matrix[1][2] + m_matrix[0][2] * o.m_matrix[2][2];
885
886 qreal m21 = m_matrix[1][0] * o.m_matrix[0][0] + m_matrix[1][1] * o.m_matrix[1][0] + m_matrix[1][2] * o.m_matrix[2][0];
887 qreal m22 = m_matrix[1][0] * o.m_matrix[0][1] + m_matrix[1][1] * o.m_matrix[1][1] + m_matrix[1][2] * o.m_matrix[2][1];
888 qreal m23 = m_matrix[1][0] * o.m_matrix[0][2] + m_matrix[1][1] * o.m_matrix[1][2] + m_matrix[1][2] * o.m_matrix[2][2];
889
890 qreal m31 = m_matrix[2][0] * o.m_matrix[0][0] + m_matrix[2][1] * o.m_matrix[1][0] + m_matrix[2][2] * o.m_matrix[2][0];
891 qreal m32 = m_matrix[2][0] * o.m_matrix[0][1] + m_matrix[2][1] * o.m_matrix[1][1] + m_matrix[2][2] * o.m_matrix[2][1];
892 qreal m33 = m_matrix[2][0] * o.m_matrix[0][2] + m_matrix[2][1] * o.m_matrix[1][2] + m_matrix[2][2] * o.m_matrix[2][2];
893
894 m_matrix[0][0] = m11; m_matrix[0][1] = m12; m_matrix[0][2] = m13;
895 m_matrix[1][0] = m21; m_matrix[1][1] = m22; m_matrix[1][2] = m23;
896 m_matrix[2][0] = m31; m_matrix[2][1] = m32; m_matrix[2][2] = m33;
897 }
898 }
899
900 m_dirty = t;
901 m_type = t;
902
903 return *this;
904}
905
915{
916 const TransformationType otherType = m.inline_type();
917 if (otherType == TxNone)
918 return *this;
919
920 const TransformationType thisType = inline_type();
921 if (thisType == TxNone)
922 return m;
923
925 TransformationType type = qMax(thisType, otherType);
926 switch(type) {
927 case TxNone:
928 break;
929 case TxTranslate:
930 t.m_matrix[2][0] = m_matrix[2][0] + m.m_matrix[2][0];
931 t.m_matrix[2][1] = m_matrix[2][1] + m.m_matrix[2][1];
932 break;
933 case TxScale:
934 {
935 qreal m11 = m_matrix[0][0] * m.m_matrix[0][0];
936 qreal m22 = m_matrix[1][1] * m.m_matrix[1][1];
937
938 qreal m31 = m_matrix[2][0] * m.m_matrix[0][0] + m.m_matrix[2][0];
939 qreal m32 = m_matrix[2][1] * m.m_matrix[1][1] + m.m_matrix[2][1];
940
941 t.m_matrix[0][0] = m11;
942 t.m_matrix[1][1] = m22;
943 t.m_matrix[2][0] = m31;
944 t.m_matrix[2][1] = m32;
945 break;
946 }
947 case TxRotate:
948 case TxShear:
949 {
950 qreal m11 = m_matrix[0][0] * m.m_matrix[0][0] + m_matrix[0][1] * m.m_matrix[1][0];
951 qreal m12 = m_matrix[0][0] * m.m_matrix[0][1] + m_matrix[0][1] * m.m_matrix[1][1];
952
953 qreal m21 = m_matrix[1][0] * m.m_matrix[0][0] + m_matrix[1][1] * m.m_matrix[1][0];
954 qreal m22 = m_matrix[1][0] * m.m_matrix[0][1] + m_matrix[1][1] * m.m_matrix[1][1];
955
956 qreal m31 = m_matrix[2][0] * m.m_matrix[0][0] + m_matrix[2][1] * m.m_matrix[1][0] + m.m_matrix[2][0];
957 qreal m32 = m_matrix[2][0] * m.m_matrix[0][1] + m_matrix[2][1] * m.m_matrix[1][1] + m.m_matrix[2][1];
958
959 t.m_matrix[0][0] = m11; t.m_matrix[0][1] = m12;
960 t.m_matrix[1][0] = m21; t.m_matrix[1][1] = m22;
961 t.m_matrix[2][0] = m31; t.m_matrix[2][1] = m32;
962 break;
963 }
964 case TxProject:
965 {
966 qreal m11 = m_matrix[0][0] * m.m_matrix[0][0] + m_matrix[0][1] * m.m_matrix[1][0] + m_matrix[0][2] * m.m_matrix[2][0];
967 qreal m12 = m_matrix[0][0] * m.m_matrix[0][1] + m_matrix[0][1] * m.m_matrix[1][1] + m_matrix[0][2] * m.m_matrix[2][1];
968 qreal m13 = m_matrix[0][0] * m.m_matrix[0][2] + m_matrix[0][1] * m.m_matrix[1][2] + m_matrix[0][2] * m.m_matrix[2][2];
969
970 qreal m21 = m_matrix[1][0] * m.m_matrix[0][0] + m_matrix[1][1] * m.m_matrix[1][0] + m_matrix[1][2] * m.m_matrix[2][0];
971 qreal m22 = m_matrix[1][0] * m.m_matrix[0][1] + m_matrix[1][1] * m.m_matrix[1][1] + m_matrix[1][2] * m.m_matrix[2][1];
972 qreal m23 = m_matrix[1][0] * m.m_matrix[0][2] + m_matrix[1][1] * m.m_matrix[1][2] + m_matrix[1][2] * m.m_matrix[2][2];
973
974 qreal m31 = m_matrix[2][0] * m.m_matrix[0][0] + m_matrix[2][1] * m.m_matrix[1][0] + m_matrix[2][2] * m.m_matrix[2][0];
975 qreal m32 = m_matrix[2][0] * m.m_matrix[0][1] + m_matrix[2][1] * m.m_matrix[1][1] + m_matrix[2][2] * m.m_matrix[2][1];
976 qreal m33 = m_matrix[2][0] * m.m_matrix[0][2] + m_matrix[2][1] * m.m_matrix[1][2] + m_matrix[2][2] * m.m_matrix[2][2];
977
978 t.m_matrix[0][0] = m11; t.m_matrix[0][1] = m12; t.m_matrix[0][2] = m13;
979 t.m_matrix[1][0] = m21; t.m_matrix[1][1] = m22; t.m_matrix[1][2] = m23;
980 t.m_matrix[2][0] = m31; t.m_matrix[2][1] = m32; t.m_matrix[2][2] = m33;
981 }
982 }
983
984 t.m_dirty = type;
985 t.m_type = type;
986
987 return t;
988}
989
1037{
1038 *this = QTransform();
1039}
1040
1041#ifndef QT_NO_DATASTREAM
1053{
1054 s << double(m.m11())
1055 << double(m.m12())
1056 << double(m.m13())
1057 << double(m.m21())
1058 << double(m.m22())
1059 << double(m.m23())
1060 << double(m.m31())
1061 << double(m.m32())
1062 << double(m.m33());
1063 return s;
1064}
1065
1077{
1078 double m11, m12, m13,
1079 m21, m22, m23,
1080 m31, m32, m33;
1081
1082 s >> m11;
1083 s >> m12;
1084 s >> m13;
1085 s >> m21;
1086 s >> m22;
1087 s >> m23;
1088 s >> m31;
1089 s >> m32;
1090 s >> m33;
1091 t.setMatrix(m11, m12, m13,
1092 m21, m22, m23,
1093 m31, m32, m33);
1094 return s;
1095}
1096
1097#endif // QT_NO_DATASTREAM
1098
1099#ifndef QT_NO_DEBUG_STREAM
1101{
1102 static const char typeStr[][12] =
1103 {
1104 "TxNone",
1105 "TxTranslate",
1106 "TxScale",
1107 "",
1108 "TxRotate",
1109 "", "", "",
1110 "TxShear",
1111 "", "", "", "", "", "", "",
1112 "TxProject"
1113 };
1114
1115 QDebugStateSaver saver(dbg);
1116 dbg.nospace() << "QTransform(type=" << typeStr[m.type()] << ','
1117 << " 11=" << m.m11()
1118 << " 12=" << m.m12()
1119 << " 13=" << m.m13()
1120 << " 21=" << m.m21()
1121 << " 22=" << m.m22()
1122 << " 23=" << m.m23()
1123 << " 31=" << m.m31()
1124 << " 32=" << m.m32()
1125 << " 33=" << m.m33()
1126 << ')';
1127
1128 return dbg;
1129}
1130#endif
1131
1141{
1142 qreal fx = p.x();
1143 qreal fy = p.y();
1144
1145 qreal x = 0, y = 0;
1146
1147 TransformationType t = inline_type();
1148 switch(t) {
1149 case TxNone:
1150 x = fx;
1151 y = fy;
1152 break;
1153 case TxTranslate:
1154 x = fx + m_matrix[2][0];
1155 y = fy + m_matrix[2][1];
1156 break;
1157 case TxScale:
1158 x = m_matrix[0][0] * fx + m_matrix[2][0];
1159 y = m_matrix[1][1] * fy + m_matrix[2][1];
1160 break;
1161 case TxRotate:
1162 case TxShear:
1163 case TxProject:
1164 x = m_matrix[0][0] * fx + m_matrix[1][0] * fy + m_matrix[2][0];
1165 y = m_matrix[0][1] * fx + m_matrix[1][1] * fy + m_matrix[2][1];
1166 if (t == TxProject) {
1167 qreal w = 1./(m_matrix[0][2] * fx + m_matrix[1][2] * fy + m_matrix[2][2]);
1168 x *= w;
1169 y *= w;
1170 }
1171 }
1172 return QPoint(qRound(x), qRound(y));
1173}
1174
1175
1192{
1193 qreal fx = p.x();
1194 qreal fy = p.y();
1195
1196 qreal x = 0, y = 0;
1197
1198 TransformationType t = inline_type();
1199 switch(t) {
1200 case TxNone:
1201 x = fx;
1202 y = fy;
1203 break;
1204 case TxTranslate:
1205 x = fx + m_matrix[2][0];
1206 y = fy + m_matrix[2][1];
1207 break;
1208 case TxScale:
1209 x = m_matrix[0][0] * fx + m_matrix[2][0];
1210 y = m_matrix[1][1] * fy + m_matrix[2][1];
1211 break;
1212 case TxRotate:
1213 case TxShear:
1214 case TxProject:
1215 x = m_matrix[0][0] * fx + m_matrix[1][0] * fy + m_matrix[2][0];
1216 y = m_matrix[0][1] * fx + m_matrix[1][1] * fy + m_matrix[2][1];
1217 if (t == TxProject) {
1218 qreal w = 1./(m_matrix[0][2] * fx + m_matrix[1][2] * fy + m_matrix[2][2]);
1219 x *= w;
1220 y *= w;
1221 }
1222 }
1223 return QPointF(x, y);
1224}
1225
1261{
1262 qreal fx1 = l.x1();
1263 qreal fy1 = l.y1();
1264 qreal fx2 = l.x2();
1265 qreal fy2 = l.y2();
1266
1267 qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0;
1268
1269 TransformationType t = inline_type();
1270 switch(t) {
1271 case TxNone:
1272 x1 = fx1;
1273 y1 = fy1;
1274 x2 = fx2;
1275 y2 = fy2;
1276 break;
1277 case TxTranslate:
1278 x1 = fx1 + m_matrix[2][0];
1279 y1 = fy1 + m_matrix[2][1];
1280 x2 = fx2 + m_matrix[2][0];
1281 y2 = fy2 + m_matrix[2][1];
1282 break;
1283 case TxScale:
1284 x1 = m_matrix[0][0] * fx1 + m_matrix[2][0];
1285 y1 = m_matrix[1][1] * fy1 + m_matrix[2][1];
1286 x2 = m_matrix[0][0] * fx2 + m_matrix[2][0];
1287 y2 = m_matrix[1][1] * fy2 + m_matrix[2][1];
1288 break;
1289 case TxRotate:
1290 case TxShear:
1291 case TxProject:
1292 x1 = m_matrix[0][0] * fx1 + m_matrix[1][0] * fy1 + m_matrix[2][0];
1293 y1 = m_matrix[0][1] * fx1 + m_matrix[1][1] * fy1 + m_matrix[2][1];
1294 x2 = m_matrix[0][0] * fx2 + m_matrix[1][0] * fy2 + m_matrix[2][0];
1295 y2 = m_matrix[0][1] * fx2 + m_matrix[1][1] * fy2 + m_matrix[2][1];
1296 if (t == TxProject) {
1297 qreal w = 1./(m_matrix[0][2] * fx1 + m_matrix[1][2] * fy1 + m_matrix[2][2]);
1298 x1 *= w;
1299 y1 *= w;
1300 w = 1./(m_matrix[0][2] * fx2 + m_matrix[1][2] * fy2 + m_matrix[2][2]);
1301 x2 *= w;
1302 y2 *= w;
1303 }
1304 }
1305 return QLine(qRound(x1), qRound(y1), qRound(x2), qRound(y2));
1306}
1307
1320{
1321 qreal fx1 = l.x1();
1322 qreal fy1 = l.y1();
1323 qreal fx2 = l.x2();
1324 qreal fy2 = l.y2();
1325
1326 qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0;
1327
1328 TransformationType t = inline_type();
1329 switch(t) {
1330 case TxNone:
1331 x1 = fx1;
1332 y1 = fy1;
1333 x2 = fx2;
1334 y2 = fy2;
1335 break;
1336 case TxTranslate:
1337 x1 = fx1 + m_matrix[2][0];
1338 y1 = fy1 + m_matrix[2][1];
1339 x2 = fx2 + m_matrix[2][0];
1340 y2 = fy2 + m_matrix[2][1];
1341 break;
1342 case TxScale:
1343 x1 = m_matrix[0][0] * fx1 + m_matrix[2][0];
1344 y1 = m_matrix[1][1] * fy1 + m_matrix[2][1];
1345 x2 = m_matrix[0][0] * fx2 + m_matrix[2][0];
1346 y2 = m_matrix[1][1] * fy2 + m_matrix[2][1];
1347 break;
1348 case TxRotate:
1349 case TxShear:
1350 case TxProject:
1351 x1 = m_matrix[0][0] * fx1 + m_matrix[1][0] * fy1 + m_matrix[2][0];
1352 y1 = m_matrix[0][1] * fx1 + m_matrix[1][1] * fy1 + m_matrix[2][1];
1353 x2 = m_matrix[0][0] * fx2 + m_matrix[1][0] * fy2 + m_matrix[2][0];
1354 y2 = m_matrix[0][1] * fx2 + m_matrix[1][1] * fy2 + m_matrix[2][1];
1355 if (t == TxProject) {
1356 qreal w = 1./(m_matrix[0][2] * fx1 + m_matrix[1][2] * fy1 + m_matrix[2][2]);
1357 x1 *= w;
1358 y1 *= w;
1359 w = 1./(m_matrix[0][2] * fx2 + m_matrix[1][2] * fy2 + m_matrix[2][2]);
1360 x2 *= w;
1361 y2 *= w;
1362 }
1363 }
1364 return QLineF(x1, y1, x2, y2);
1365}
1366
1368{
1369 if (poly.size() == 0)
1370 return poly;
1371
1372 if (poly.size() == 1)
1373 return QPolygonF() << transform.map(poly.at(0));
1374
1376 path.addPolygon(poly);
1377
1378 path = transform.map(path);
1379
1381 const int elementCount = path.elementCount();
1382 result.reserve(elementCount);
1383 for (int i = 0; i < elementCount; ++i)
1384 result << path.elementAt(i);
1385 return result;
1386}
1387
1388
1417{
1418 TransformationType t = inline_type();
1419 if (t <= TxTranslate)
1420 return a.translated(m_matrix[2][0], m_matrix[2][1]);
1421
1422 if (t >= QTransform::TxProject)
1423 return mapProjective(*this, a);
1424
1425 int size = a.size();
1426 int i;
1427 QPolygonF p(size);
1428 const QPointF *da = a.constData();
1429 QPointF *dp = p.data();
1430
1431 for(i = 0; i < size; ++i) {
1432 MAP(da[i].xp, da[i].yp, dp[i].xp, dp[i].yp);
1433 }
1434 return p;
1435}
1436
1447{
1448 TransformationType t = inline_type();
1449 if (t <= TxTranslate)
1450 return a.translated(qRound(m_matrix[2][0]), qRound(m_matrix[2][1]));
1451
1452 if (t >= QTransform::TxProject)
1453 return mapProjective(*this, QPolygonF(a)).toPolygon();
1454
1455 int size = a.size();
1456 int i;
1457 QPolygon p(size);
1458 const QPoint *da = a.constData();
1459 QPoint *dp = p.data();
1460
1461 for(i = 0; i < size; ++i) {
1462 qreal nx = 0, ny = 0;
1463 MAP(da[i].xp, da[i].yp, nx, ny);
1464 dp[i].xp = qRound(nx);
1465 dp[i].yp = qRound(ny);
1466 }
1467 return p;
1468}
1469
1479extern QPainterPath qt_regionToPath(const QRegion &region);
1480
1492{
1493 TransformationType t = inline_type();
1494 if (t == TxNone)
1495 return r;
1496
1497 if (t == TxTranslate) {
1498 QRegion copy(r);
1499 copy.translate(qRound(m_matrix[2][0]), qRound(m_matrix[2][1]));
1500 return copy;
1501 }
1502
1503 if (t == TxScale) {
1504 QRegion res;
1505 if (m11() < 0 || m22() < 0) {
1506 for (const QRect &rect : r)
1507 res += qt_mapFillRect(QRectF(rect), *this);
1508 } else {
1510 rects.reserve(r.rectCount());
1511 for (const QRect &rect : r) {
1512 QRect nr = qt_mapFillRect(QRectF(rect), *this);
1513 if (!nr.isEmpty())
1514 rects.append(nr);
1515 }
1516 res.setRects(rects.constData(), rects.size());
1517 }
1518 return res;
1519 }
1520
1522 return p.toFillPolygon().toPolygon();
1523}
1524
1526{
1530
1532 QHomogeneousCoordinate(qreal x_, qreal y_, qreal w_) : x(x_), y(y_), w(w_) {}
1533
1534 const QPointF toPoint() const {
1535 qreal iw = 1. / w;
1536 return QPointF(x * iw, y * iw);
1537 }
1538};
1539
1541{
1543 c.x = transform.m11() * p.x() + transform.m21() * p.y() + transform.m31();
1544 c.y = transform.m12() * p.x() + transform.m22() * p.y() + transform.m32();
1545 c.w = transform.m13() * p.x() + transform.m23() * p.y() + transform.m33();
1546 return c;
1547}
1548
1549static inline bool lineTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b,
1550 bool needsMoveTo, bool needsLineTo = true)
1551{
1554
1555 if (ha.w < Q_NEAR_CLIP && hb.w < Q_NEAR_CLIP)
1556 return false;
1557
1558 if (hb.w < Q_NEAR_CLIP) {
1559 const qreal t = (Q_NEAR_CLIP - hb.w) / (ha.w - hb.w);
1560
1561 hb.x += (ha.x - hb.x) * t;
1562 hb.y += (ha.y - hb.y) * t;
1563 hb.w = qreal(Q_NEAR_CLIP);
1564 } else if (ha.w < Q_NEAR_CLIP) {
1565 const qreal t = (Q_NEAR_CLIP - ha.w) / (hb.w - ha.w);
1566
1567 ha.x += (hb.x - ha.x) * t;
1568 ha.y += (hb.y - ha.y) * t;
1569 ha.w = qreal(Q_NEAR_CLIP);
1570
1571 const QPointF p = ha.toPoint();
1572 if (needsMoveTo) {
1573 path.moveTo(p);
1574 needsMoveTo = false;
1575 } else {
1576 path.lineTo(p);
1577 }
1578 }
1579
1580 if (needsMoveTo)
1581 path.moveTo(ha.toPoint());
1582
1583 if (needsLineTo)
1584 path.lineTo(hb.toPoint());
1585
1586 return true;
1587}
1588Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
1589
1590static inline bool cubicTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b, const QPointF &c, const QPointF &d, bool needsMoveTo)
1591{
1592 // Convert projective xformed curves to line
1593 // segments so they can be transformed more accurately
1594
1595 qreal scale;
1597
1598 qreal curveThreshold = scale == 0 ? qreal(0.25) : (qreal(0.25) / scale);
1599
1600 QPolygonF segment = QBezier::fromPoints(a, b, c, d).toPolygon(curveThreshold);
1601
1602 for (int i = 0; i < segment.size() - 1; ++i)
1603 if (lineTo_clipped(path, transform, segment.at(i), segment.at(i+1), needsMoveTo))
1604 needsMoveTo = false;
1605
1606 return !needsMoveTo;
1607}
1608
1610{
1612
1613 QPointF last;
1614 QPointF lastMoveTo;
1615 bool needsMoveTo = true;
1616 for (int i = 0; i < path.elementCount(); ++i) {
1617 switch (path.elementAt(i).type) {
1619 if (i > 0 && lastMoveTo != last)
1620 lineTo_clipped(result, transform, last, lastMoveTo, needsMoveTo);
1621
1622 lastMoveTo = path.elementAt(i);
1623 last = path.elementAt(i);
1624 needsMoveTo = true;
1625 break;
1627 if (lineTo_clipped(result, transform, last, path.elementAt(i), needsMoveTo))
1628 needsMoveTo = false;
1629 last = path.elementAt(i);
1630 break;
1632 if (cubicTo_clipped(result, transform, last, path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), needsMoveTo))
1633 needsMoveTo = false;
1634 i += 2;
1635 last = path.elementAt(i);
1636 break;
1637 default:
1638 Q_ASSERT(false);
1639 }
1640 }
1641
1642 if (path.elementCount() > 0 && lastMoveTo != last)
1643 lineTo_clipped(result, transform, last, lastMoveTo, needsMoveTo, false);
1644
1645 result.setFillRule(path.fillRule());
1646 return result;
1647}
1648
1667{
1668 TransformationType t = inline_type();
1669 if (t == TxNone || path.elementCount() == 0)
1670 return path;
1671
1672 if (t >= TxProject)
1673 return mapProjective(*this, path);
1674
1676
1677 if (t == TxTranslate) {
1678 copy.translate(m_matrix[2][0], m_matrix[2][1]);
1679 } else {
1680 copy.detach();
1681 // Full xform
1682 for (int i=0; i<path.elementCount(); ++i) {
1683 QPainterPath::Element &e = copy.d_ptr->elements[i];
1684 MAP(e.x, e.y, e.x, e.y);
1685 }
1686 }
1687
1688 return copy;
1689}
1690
1712{
1713 TransformationType t = inline_type();
1714
1715 QPolygon a(4);
1716 qreal x[4] = { 0, 0, 0, 0 }, y[4] = { 0, 0, 0, 0 };
1717 if (t <= TxScale) {
1718 x[0] = m_matrix[0][0]*rect.x() + m_matrix[2][0];
1719 y[0] = m_matrix[1][1]*rect.y() + m_matrix[2][1];
1720 qreal w = m_matrix[0][0]*rect.width();
1721 qreal h = m_matrix[1][1]*rect.height();
1722 if (w < 0) {
1723 w = -w;
1724 x[0] -= w;
1725 }
1726 if (h < 0) {
1727 h = -h;
1728 y[0] -= h;
1729 }
1730 x[1] = x[0]+w;
1731 x[2] = x[1];
1732 x[3] = x[0];
1733 y[1] = y[0];
1734 y[2] = y[0]+h;
1735 y[3] = y[2];
1736 } else {
1737 qreal right = rect.x() + rect.width();
1738 qreal bottom = rect.y() + rect.height();
1739 MAP(rect.x(), rect.y(), x[0], y[0]);
1740 MAP(right, rect.y(), x[1], y[1]);
1741 MAP(right, bottom, x[2], y[2]);
1742 MAP(rect.x(), bottom, x[3], y[3]);
1743 }
1744
1745 // all coordinates are correctly, transform to a pointarray
1746 // (rounding to the next integer)
1747 a.setPoints(4, qRound(x[0]), qRound(y[0]),
1748 qRound(x[1]), qRound(y[1]),
1749 qRound(x[2]), qRound(y[2]),
1750 qRound(x[3]), qRound(y[3]));
1751 return a;
1752}
1753
1762{
1763 if (quad.size() != 4)
1764 return false;
1765
1766 qreal dx0 = quad[0].x();
1767 qreal dx1 = quad[1].x();
1768 qreal dx2 = quad[2].x();
1769 qreal dx3 = quad[3].x();
1770
1771 qreal dy0 = quad[0].y();
1772 qreal dy1 = quad[1].y();
1773 qreal dy2 = quad[2].y();
1774 qreal dy3 = quad[3].y();
1775
1776 double ax = dx0 - dx1 + dx2 - dx3;
1777 double ay = dy0 - dy1 + dy2 - dy3;
1778
1779 if (!ax && !ay) { //afine transform
1780 trans.setMatrix(dx1 - dx0, dy1 - dy0, 0,
1781 dx2 - dx1, dy2 - dy1, 0,
1782 dx0, dy0, 1);
1783 } else {
1784 double ax1 = dx1 - dx2;
1785 double ax2 = dx3 - dx2;
1786 double ay1 = dy1 - dy2;
1787 double ay2 = dy3 - dy2;
1788
1789 /*determinants */
1790 double gtop = ax * ay2 - ax2 * ay;
1791 double htop = ax1 * ay - ax * ay1;
1792 double bottom = ax1 * ay2 - ax2 * ay1;
1793
1794 double a, b, c, d, e, f, g, h; /*i is always 1*/
1795
1796 if (!bottom)
1797 return false;
1798
1799 g = gtop/bottom;
1800 h = htop/bottom;
1801
1802 a = dx1 - dx0 + g * dx1;
1803 b = dx3 - dx0 + h * dx3;
1804 c = dx0;
1805 d = dy1 - dy0 + g * dy1;
1806 e = dy3 - dy0 + h * dy3;
1807 f = dy0;
1808
1809 trans.setMatrix(a, d, g,
1810 b, e, h,
1811 c, f, 1.0);
1812 }
1813
1814 return true;
1815}
1816
1827{
1828 if (!squareToQuad(quad, trans))
1829 return false;
1830
1831 bool invertible = false;
1832 trans = trans.inverted(&invertible);
1833
1834 return invertible;
1835}
1836
1850 const QPolygonF &two,
1851 QTransform &trans)
1852{
1853 QTransform stq;
1854 if (!quadToSquare(one, trans))
1855 return false;
1856 if (!squareToQuad(two, stq))
1857 return false;
1858 trans *= stq;
1859 //qDebug()<<"Final = "<<trans;
1860 return true;
1861}
1862
1875 qreal m21, qreal m22, qreal m23,
1876 qreal m31, qreal m32, qreal m33)
1877{
1878 m_matrix[0][0] = m11; m_matrix[0][1] = m12; m_matrix[0][2] = m13;
1879 m_matrix[1][0] = m21; m_matrix[1][1] = m22; m_matrix[1][2] = m23;
1880 m_matrix[2][0] = m31; m_matrix[2][1] = m32; m_matrix[2][2] = m33;
1881 m_type = TxNone;
1882 m_dirty = TxProject;
1883}
1884
1885static inline bool needsPerspectiveClipping(const QRectF &rect, const QTransform &transform)
1886{
1887 const qreal wx = qMin(transform.m13() * rect.left(), transform.m13() * rect.right());
1888 const qreal wy = qMin(transform.m23() * rect.top(), transform.m23() * rect.bottom());
1889
1890 return wx + wy + transform.m33() < Q_NEAR_CLIP;
1891}
1892
1894{
1895 TransformationType t = inline_type();
1896 if (t <= TxTranslate)
1897 return rect.translated(qRound(m_matrix[2][0]), qRound(m_matrix[2][1]));
1898
1899 if (t <= TxScale) {
1900 int x = qRound(m_matrix[0][0] * rect.x() + m_matrix[2][0]);
1901 int y = qRound(m_matrix[1][1] * rect.y() + m_matrix[2][1]);
1902 int w = qRound(m_matrix[0][0] * rect.width());
1903 int h = qRound(m_matrix[1][1] * rect.height());
1904 if (w < 0) {
1905 w = -w;
1906 x -= w;
1907 }
1908 if (h < 0) {
1909 h = -h;
1910 y -= h;
1911 }
1912 return QRect(x, y, w, h);
1913 } else if (t < TxProject || !needsPerspectiveClipping(rect, *this)) {
1914 // see mapToPolygon for explanations of the algorithm.
1915 qreal x = 0, y = 0;
1916 MAP(rect.left(), rect.top(), x, y);
1917 qreal xmin = x;
1918 qreal ymin = y;
1919 qreal xmax = x;
1920 qreal ymax = y;
1921 MAP(rect.right() + 1, rect.top(), x, y);
1922 xmin = qMin(xmin, x);
1923 ymin = qMin(ymin, y);
1924 xmax = qMax(xmax, x);
1925 ymax = qMax(ymax, y);
1926 MAP(rect.right() + 1, rect.bottom() + 1, x, y);
1927 xmin = qMin(xmin, x);
1928 ymin = qMin(ymin, y);
1929 xmax = qMax(xmax, x);
1930 ymax = qMax(ymax, y);
1931 MAP(rect.left(), rect.bottom() + 1, x, y);
1932 xmin = qMin(xmin, x);
1933 ymin = qMin(ymin, y);
1934 xmax = qMax(xmax, x);
1935 ymax = qMax(ymax, y);
1936 return QRect(qRound(xmin), qRound(ymin), qRound(xmax)-qRound(xmin), qRound(ymax)-qRound(ymin));
1937 } else {
1939 path.addRect(rect);
1940 return map(path).boundingRect().toRect();
1941 }
1942}
1943
1964{
1965 TransformationType t = inline_type();
1966 if (t <= TxTranslate)
1967 return rect.translated(m_matrix[2][0], m_matrix[2][1]);
1968
1969 if (t <= TxScale) {
1970 qreal x = m_matrix[0][0] * rect.x() + m_matrix[2][0];
1971 qreal y = m_matrix[1][1] * rect.y() + m_matrix[2][1];
1972 qreal w = m_matrix[0][0] * rect.width();
1973 qreal h = m_matrix[1][1] * rect.height();
1974 if (w < 0) {
1975 w = -w;
1976 x -= w;
1977 }
1978 if (h < 0) {
1979 h = -h;
1980 y -= h;
1981 }
1982 return QRectF(x, y, w, h);
1983 } else if (t < TxProject || !needsPerspectiveClipping(rect, *this)) {
1984 qreal x = 0, y = 0;
1985 MAP(rect.x(), rect.y(), x, y);
1986 qreal xmin = x;
1987 qreal ymin = y;
1988 qreal xmax = x;
1989 qreal ymax = y;
1990 MAP(rect.x() + rect.width(), rect.y(), x, y);
1991 xmin = qMin(xmin, x);
1992 ymin = qMin(ymin, y);
1993 xmax = qMax(xmax, x);
1994 ymax = qMax(ymax, y);
1995 MAP(rect.x() + rect.width(), rect.y() + rect.height(), x, y);
1996 xmin = qMin(xmin, x);
1997 ymin = qMin(ymin, y);
1998 xmax = qMax(xmax, x);
1999 ymax = qMax(ymax, y);
2000 MAP(rect.x(), rect.y() + rect.height(), x, y);
2001 xmin = qMin(xmin, x);
2002 ymin = qMin(ymin, y);
2003 xmax = qMax(xmax, x);
2004 ymax = qMax(ymax, y);
2005 return QRectF(xmin, ymin, xmax-xmin, ymax - ymin);
2006 } else {
2008 path.addRect(rect);
2009 return map(path).boundingRect();
2010 }
2011}
2012
2038{
2039 TransformationType t = inline_type();
2040 MAP(x, y, *tx, *ty);
2041}
2042
2051void QTransform::map(int x, int y, int *tx, int *ty) const
2052{
2053 TransformationType t = inline_type();
2054 qreal fx = 0, fy = 0;
2055 MAP(x, y, fx, fy);
2056 *tx = qRound(fx);
2057 *ty = qRound(fy);
2058}
2059
2073{
2074 if (m_dirty == TxNone || m_dirty < m_type)
2075 return static_cast<TransformationType>(m_type);
2076
2077 switch (static_cast<TransformationType>(m_dirty)) {
2078 case TxProject:
2079 if (!qFuzzyIsNull(m_matrix[0][2]) || !qFuzzyIsNull(m_matrix[1][2]) || !qFuzzyIsNull(m_matrix[2][2] - 1)) {
2080 m_type = TxProject;
2081 break;
2082 }
2083 Q_FALLTHROUGH();
2084 case TxShear:
2085 case TxRotate:
2086 if (!qFuzzyIsNull(m_matrix[0][1]) || !qFuzzyIsNull(m_matrix[1][0])) {
2087 const qreal dot = m_matrix[0][0] * m_matrix[1][0] + m_matrix[0][1] * m_matrix[1][1];
2088 if (qFuzzyIsNull(dot))
2089 m_type = TxRotate;
2090 else
2091 m_type = TxShear;
2092 break;
2093 }
2094 Q_FALLTHROUGH();
2095 case TxScale:
2096 if (!qFuzzyIsNull(m_matrix[0][0] - 1) || !qFuzzyIsNull(m_matrix[1][1] - 1)) {
2097 m_type = TxScale;
2098 break;
2099 }
2100 Q_FALLTHROUGH();
2101 case TxTranslate:
2102 if (!qFuzzyIsNull(m_matrix[2][0]) || !qFuzzyIsNull(m_matrix[2][1])) {
2103 m_type = TxTranslate;
2104 break;
2105 }
2106 Q_FALLTHROUGH();
2107 case TxNone:
2108 m_type = TxNone;
2109 break;
2110 }
2111
2112 m_dirty = TxNone;
2113 return static_cast<TransformationType>(m_type);
2114}
2115
2120QTransform::operator QVariant() const
2121{
2122 return QVariant::fromValue(*this);
2123}
2124
2125
2297// returns true if the transform is uniformly scaling
2298// (same scale in x and y direction)
2299// scale is set to the max of x and y scaling factors
2300Q_GUI_EXPORT
2302{
2305 if (scale)
2306 *scale = 1;
2307 return true;
2308 } else if (type == QTransform::TxScale) {
2309 const qreal xScale = qAbs(transform.m11());
2310 const qreal yScale = qAbs(transform.m22());
2311 if (scale)
2312 *scale = qMax(xScale, yScale);
2313 return qFuzzyCompare(xScale, yScale);
2314 }
2315
2316 // rotate then scale: compare columns
2317 const qreal xScale1 = transform.m11() * transform.m11()
2318 + transform.m21() * transform.m21();
2319 const qreal yScale1 = transform.m12() * transform.m12()
2320 + transform.m22() * transform.m22();
2321
2322 // scale then rotate: compare rows
2323 const qreal xScale2 = transform.m11() * transform.m11()
2324 + transform.m12() * transform.m12();
2325 const qreal yScale2 = transform.m21() * transform.m21()
2326 + transform.m22() * transform.m22();
2327
2328 // decide the order of rotate and scale operations
2329 if (qAbs(xScale1 - yScale1) > qAbs(xScale2 - yScale2)) {
2330 if (scale)
2331 *scale = qSqrt(qMax(xScale1, yScale1));
2332
2333 return type == QTransform::TxRotate && qFuzzyCompare(xScale1, yScale1);
2334 } else {
2335 if (scale)
2336 *scale = qSqrt(qMax(xScale2, yScale2));
2337
2338 return type == QTransform::TxRotate && qFuzzyCompare(xScale2, yScale2);
2339 }
2340}
2341
2342QDataStream & operator>>(QDataStream &s, QTransform::Affine &m)
2343{
2344 if (s.version() == 1) {
2345 float m11, m12, m21, m22, dx, dy;
2346 s >> m11; s >> m12; s >> m21; s >> m22; s >> dx; s >> dy;
2347
2348 m.m_matrix[0][0] = m11;
2349 m.m_matrix[0][1] = m12;
2350 m.m_matrix[1][0] = m21;
2351 m.m_matrix[1][1] = m22;
2352 m.m_matrix[2][0] = dx;
2353 m.m_matrix[2][1] = dy;
2354 } else {
2355 s >> m.m_matrix[0][0];
2356 s >> m.m_matrix[0][1];
2357 s >> m.m_matrix[1][0];
2358 s >> m.m_matrix[1][1];
2359 s >> m.m_matrix[2][0];
2360 s >> m.m_matrix[2][1];
2361 }
2362 m.m_matrix[0][2] = 0;
2363 m.m_matrix[1][2] = 0;
2364 m.m_matrix[2][2] = 1;
2365 return s;
2366}
2367
2368QDataStream &operator<<(QDataStream &s, const QTransform::Affine &m)
2369{
2370 if (s.version() == 1) {
2371 s << (float)m.m_matrix[0][0]
2372 << (float)m.m_matrix[0][1]
2373 << (float)m.m_matrix[1][0]
2374 << (float)m.m_matrix[1][1]
2375 << (float)m.m_matrix[2][0]
2376 << (float)m.m_matrix[2][1];
2377 } else {
2378 s << m.m_matrix[0][0]
2379 << m.m_matrix[0][1]
2380 << m.m_matrix[1][0]
2381 << m.m_matrix[1][1]
2382 << m.m_matrix[2][0]
2383 << m.m_matrix[2][1];
2384 }
2385 return s;
2386}
2387
static QBezier fromPoints(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4)
Definition qbezier_p.h:34
QPolygonF toPolygon(qreal bezier_flattening_threshold=0.5) const
Definition qbezier.cpp:21
\inmodule QtCore\reentrant
Definition qdatastream.h:30
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qline.h:182
constexpr qreal x1() const
Returns the x-coordinate of the line's start point.
Definition qline.h:264
constexpr qreal y2() const
Returns the y-coordinate of the line's end point.
Definition qline.h:279
constexpr qreal x2() const
Returns the x-coordinate of the line's end point.
Definition qline.h:274
constexpr qreal y1() const
Returns the y-coordinate of the line's start point.
Definition qline.h:269
\inmodule QtCore
Definition qline.h:18
constexpr int x2() const
Returns the x-coordinate of the line's end point.
Definition qline.h:86
constexpr int y2() const
Returns the y-coordinate of the line's end point.
Definition qline.h:91
constexpr int y1() const
Returns the y-coordinate of the line's start point.
Definition qline.h:81
constexpr int x1() const
Returns the x-coordinate of the line's start point.
Definition qline.h:76
qsizetype size() const noexcept
Definition qlist.h:386
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
\inmodule QtGui
\inmodule QtGui
\inmodule QtCore\reentrant
Definition qpoint.h:214
\inmodule QtCore\reentrant
Definition qpoint.h:23
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:127
The QPolygonF class provides a list of points using floating point precision.
Definition qpolygon.h:96
QPolygon Q_GUI_EXPORT toPolygon() const
Creates and returns a QPolygon by converting each QPointF to a QPoint.
Definition qpolygon.cpp:619
The QPolygon class provides a list of points using integer precision.
Definition qpolygon.h:23
\inmodule QtCore\reentrant
Definition qrect.h:483
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr bool isEmpty() const noexcept
Returns true if the rectangle is empty, otherwise returns false.
Definition qrect.h:166
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
QTransform & rotate(qreal a, Qt::Axis axis=Qt::ZAxis, qreal distanceToPlane=1024.0f)
static bool quadToQuad(const QPolygonF &one, const QPolygonF &two, QTransform &result)
Creates a transformation matrix, trans, that maps a four-sided polygon, one, to another four-sided po...
qreal m21() const
Returns the horizontal shearing factor.
Definition qtransform.h:211
QTransform & operator*=(const QTransform &)
This is an overloaded member function, provided for convenience. It differs from the above function o...
qreal m23() const
Returns the vertical projection factor.
Definition qtransform.h:219
static QTransform fromScale(qreal dx, qreal dy)
Creates a matrix which corresponds to a scaling of sx horizontally and sy vertically.
static bool quadToSquare(const QPolygonF &quad, QTransform &result)
Creates a transformation matrix, trans, that maps a four-sided polygon, quad, to a unit square.
QTransform adjoint() const
Returns the adjoint of this matrix.
QTransform & scale(qreal sx, qreal sy)
Scales the coordinate system by sx horizontally and sy vertically, and returns a reference to the mat...
qreal m12() const
Returns the vertical shearing factor.
Definition qtransform.h:203
bool operator==(const QTransform &) const
Returns true if this matrix is equal to the given matrix, otherwise returns false.
qreal m33() const
Returns the division factor.
Definition qtransform.h:231
QTransform operator*(const QTransform &o) const
Returns the result of multiplying this matrix by the given matrix.
qreal dx() const
Returns the horizontal translation factor.
Definition qtransform.h:235
QPoint map(const QPoint &p) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
qreal m31() const
Returns the horizontal translation factor.
Definition qtransform.h:223
qreal m32() const
Returns the vertical translation factor.
Definition qtransform.h:227
QTransform & shear(qreal sh, qreal sv)
Shears the coordinate system by sh horizontally and sv vertically, and returns a reference to the mat...
QTransform & operator=(QTransform &&other) noexcept=default
qreal m11() const
Returns the horizontal scaling factor.
Definition qtransform.h:199
static bool squareToQuad(const QPolygonF &square, QTransform &result)
Creates a transformation matrix, trans, that maps a unit square to a four-sided polygon,...
void setMatrix(qreal m11, qreal m12, qreal m13, qreal m21, qreal m22, qreal m23, qreal m31, qreal m32, qreal m33)
Sets the matrix elements to the specified values, m11, m12, m13 m21, m22, m23 m31,...
size_t qHash(const QTransform &key, size_t seed) noexcept
static QTransform fromTranslate(qreal dx, qreal dy)
Creates a matrix which corresponds to a translation of dx along the x axis and dy along the y axis.
QTransform inverted(bool *invertible=nullptr) const
Returns an inverted copy of this matrix.
bool operator!=(const QTransform &) const
Returns true if this matrix is not equal to the given matrix, otherwise returns false.
QPolygon mapToPolygon(const QRect &r) const
Creates and returns a QPolygon representation of the given rectangle, mapped into the coordinate syst...
void reset()
Resets the matrix to an identity matrix, i.e.
QTransform()
Constructs an identity matrix.
Definition qtransform.h:32
TransformationType type() const
Returns the transformation type of this matrix.
qreal determinant() const
Returns the matrix's determinant.
Definition qtransform.h:193
QTransform & translate(qreal dx, qreal dy)
Moves the coordinate system dx along the x axis and dy along the y axis, and returns a reference to t...
qreal m13() const
Returns the horizontal projection factor.
Definition qtransform.h:207
QTransform transposed() const
Returns the transpose of this matrix.
qreal m22() const
Returns the vertical scaling factor.
Definition qtransform.h:215
QRect mapRect(const QRect &) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QTransform & rotateRadians(qreal a, Qt::Axis axis=Qt::ZAxis, qreal distanceToPlane=1024.0f)
TransformationType
\value TxNone \value TxTranslate \value TxScale \value TxRotate \value TxShear \value TxProject
Definition qtransform.h:22
qreal dy() const
Returns the vertical translation factor.
Definition qtransform.h:239
constexpr size_type size() const noexcept
void append(const T &t)
const T * constData() const
void reserve(qsizetype sz)
\inmodule QtCore
Definition qvariant.h:64
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:531
QHash< int, QWidget * > hash
[35multi]
QMap< QString, QString > map
[6]
double e
rect
[4]
Combined button and popup list for selecting options.
@ ZAxis
@ YAxis
static jboolean copy(JNIEnv *, jobject)
#define Q_FALLTHROUGH()
#define Q_NEVER_INLINE
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:303
bool qIsNaN(qfloat16 f) noexcept
Definition qfloat16.h:238
bool qIsNull(qfloat16 f) noexcept
Definition qfloat16.h:308
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:243
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:281
#define qWarning
Definition qlogging.h:162
auto qCos(T v)
Definition qmath.h:60
auto qSin(T v)
Definition qmath.h:54
constexpr float qDegreesToRadians(float degrees)
Definition qmath.h:260
QRect qt_mapFillRect(const QRectF &rect, const QTransform &xf)
Definition qmath_p.h:27
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLuint64 key
GLfloat GLfloat GLfloat w
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLuint GLfloat GLfloat GLfloat x1
GLdouble GLdouble right
GLfloat GLfloat f
GLenum type
GLint GLint bottom
GLboolean GLboolean g
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLuint GLenum GLenum transform
GLenum func
Definition qopenglext.h:663
GLbyte nx
GLuint res
const GLubyte * c
GLfixed GLfixed GLfixed y2
GLuint segment
GLfixed ny
GLfixed GLfixed x2
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
GLenum GLenum GLenum GLenum GLenum scale
GLbyte ty
GLboolean invert
Definition qopenglext.h:226
QPainterPath qt_regionToPath(const QRegion &region)
Definition qregion.cpp:1007
static qreal dot(const QPointF &a, const QPointF &b)
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static const struct TessellationModeTab quad[]
static QLatin1StringView typeStr(QShaderDescription::VariableType t)
static bool needsPerspectiveClipping(const QRectF &rect, const QTransform &transform)
#define Q_NEAR_CLIP
#define MAP(x, y, nx, ny)
static QHomogeneousCoordinate mapHomogeneous(const QTransform &transform, const QPointF &p)
Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale)
static bool lineTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b, bool needsMoveTo, bool needsLineTo=true)
static QPolygonF mapProjective(const QTransform &transform, const QPolygonF &poly)
QT_BEGIN_NAMESPACE static Q_NEVER_INLINE void nanWarning(const char *func)
static bool cubicTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b, const QPointF &c, const QPointF &d, bool needsMoveTo)
QPainterPath qt_regionToPath(const QRegion &region)
Definition qregion.cpp:1007
QDataStream & operator>>(QDataStream &s, QTransform &t)
QDataStream & operator<<(QDataStream &s, const QTransform &m)
double qreal
Definition qtypes.h:92
const QPointF toPoint() const
QHomogeneousCoordinate(qreal x_, qreal y_, qreal w_)