QtBase  v6.3.1
qtransform.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2021 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 #include "qtransform.h"
40 
41 #include "qdatastream.h"
42 #include "qdebug.h"
43 #include "qhashfunctions.h"
44 #include "qregion.h"
45 #include "qpainterpath.h"
46 #include "qpainterpath_p.h"
47 #include "qvariant.h"
48 #include "qmath_p.h"
49 #include <qnumeric.h>
50 
51 #include <private/qbezier_p.h>
52 
54 
55 #ifndef QT_NO_DEBUG
56 Q_NEVER_INLINE
57 static void nanWarning(const char *func)
58 {
59  qWarning("QTransform::%s with NaN called", func);
60 }
61 #endif // QT_NO_DEBUG
62 
63 #define Q_NEAR_CLIP (sizeof(qreal) == sizeof(double) ? 0.000001 : 0.0001)
64 
65 #ifdef MAP
66 # undef MAP
67 #endif
68 #define MAP(x, y, nx, ny) \
69  do { \
70  qreal FX_ = x; \
71  qreal FY_ = y; \
72  switch(t) { \
73  case TxNone: \
74  nx = FX_; \
75  ny = FY_; \
76  break; \
77  case TxTranslate: \
78  nx = FX_ + m_matrix[2][0]; \
79  ny = FY_ + m_matrix[2][1]; \
80  break; \
81  case TxScale: \
82  nx = m_matrix[0][0] * FX_ + m_matrix[2][0]; \
83  ny = m_matrix[1][1] * FY_ + m_matrix[2][1]; \
84  break; \
85  case TxRotate: \
86  case TxShear: \
87  case TxProject: \
88  nx = m_matrix[0][0] * FX_ + m_matrix[1][0] * FY_ + m_matrix[2][0]; \
89  ny = m_matrix[0][1] * FX_ + m_matrix[1][1] * FY_ + m_matrix[2][1]; \
90  if (t == TxProject) { \
91  qreal w = (m_matrix[0][2] * FX_ + m_matrix[1][2] * FY_ + m_matrix[2][2]); \
92  if (w < qreal(Q_NEAR_CLIP)) w = qreal(Q_NEAR_CLIP); \
93  w = 1./w; \
94  nx *= w; \
95  ny *= w; \
96  } \
97  } \
98  } while (0)
99 
299 {
300  qreal h11, h12, h13,
301  h21, h22, h23,
302  h31, h32, h33;
303  h11 = m_matrix[1][1] * m_matrix[2][2] - m_matrix[1][2] * m_matrix[2][1];
304  h21 = m_matrix[1][2] * m_matrix[2][0] - m_matrix[1][0] * m_matrix[2][2];
305  h31 = m_matrix[1][0] * m_matrix[2][1] - m_matrix[1][1] * m_matrix[2][0];
306  h12 = m_matrix[0][2] * m_matrix[2][1] - m_matrix[0][1] * m_matrix[2][2];
307  h22 = m_matrix[0][0] * m_matrix[2][2] - m_matrix[0][2] * m_matrix[2][0];
308  h32 = m_matrix[0][1] * m_matrix[2][0] - m_matrix[0][0] * m_matrix[2][1];
309  h13 = m_matrix[0][1] * m_matrix[1][2] - m_matrix[0][2] * m_matrix[1][1];
310  h23 = m_matrix[0][2] * m_matrix[1][0] - m_matrix[0][0] * m_matrix[1][2];
311  h33 = m_matrix[0][0] * m_matrix[1][1] - m_matrix[0][1] * m_matrix[1][0];
312 
313  return QTransform(h11, h12, h13,
314  h21, h22, h23,
315  h31, h32, h33);
316 }
317 
322 {
323  QTransform t(m_matrix[0][0], m_matrix[1][0], m_matrix[2][0],
324  m_matrix[0][1], m_matrix[1][1], m_matrix[2][1],
325  m_matrix[0][2], m_matrix[1][2], m_matrix[2][2]);
326  return t;
327 }
328 
339 QTransform QTransform::inverted(bool *invertible) const
340 {
342  bool inv = true;
343 
344  switch(inline_type()) {
345  case TxNone:
346  break;
347  case TxTranslate:
348  invert.m_matrix[2][0] = -m_matrix[2][0];
349  invert.m_matrix[2][1] = -m_matrix[2][1];
350  break;
351  case TxScale:
352  inv = !qFuzzyIsNull(m_matrix[0][0]);
353  inv &= !qFuzzyIsNull(m_matrix[1][1]);
354  if (inv) {
355  invert.m_matrix[0][0] = 1. / m_matrix[0][0];
356  invert.m_matrix[1][1] = 1. / m_matrix[1][1];
357  invert.m_matrix[2][0] = -m_matrix[2][0] * invert.m_matrix[0][0];
358  invert.m_matrix[2][1] = -m_matrix[2][1] * invert.m_matrix[1][1];
359  }
360  break;
361 // case TxRotate:
362 // case TxShear:
363 // invert.affine = affine.inverted(&inv);
364 // break;
365  default:
366  // general case
367  qreal det = determinant();
368  inv = !qFuzzyIsNull(det);
369  if (inv)
370  invert = adjoint() / det;
371  break;
372  }
373 
374  if (invertible)
375  *invertible = inv;
376 
377  if (inv) {
378  // inverting doesn't change the type
379  invert.m_type = m_type;
380  invert.m_dirty = m_dirty;
381  }
382 
383  return invert;
384 }
385 
393 {
394  if (dx == 0 && dy == 0)
395  return *this;
396 #ifndef QT_NO_DEBUG
397  if (qIsNaN(dx) | qIsNaN(dy)) {
398  nanWarning("translate");
399  return *this;
400  }
401 #endif
402 
403  switch(inline_type()) {
404  case TxNone:
405  m_matrix[2][0] = dx;
406  m_matrix[2][1] = dy;
407  break;
408  case TxTranslate:
409  m_matrix[2][0] += dx;
410  m_matrix[2][1] += dy;
411  break;
412  case TxScale:
413  m_matrix[2][0] += dx * m_matrix[0][0];
414  m_matrix[2][1] += dy * m_matrix[1][1];
415  break;
416  case TxProject:
417  m_matrix[2][2] += dx * m_matrix[0][2] + dy * m_matrix[1][2];
418  Q_FALLTHROUGH();
419  case TxShear:
420  case TxRotate:
421  m_matrix[2][0] += dx * m_matrix[0][0] + dy * m_matrix[1][0];
422  m_matrix[2][1] += dy * m_matrix[1][1] + dx * m_matrix[0][1];
423  break;
424  }
425  if (m_dirty < TxTranslate)
426  m_dirty = TxTranslate;
427  return *this;
428 }
429 
438 {
439 #ifndef QT_NO_DEBUG
440  if (qIsNaN(dx) | qIsNaN(dy)) {
441  nanWarning("fromTranslate");
442  return QTransform();
443 }
444 #endif
445  QTransform transform(1, 0, 0, 0, 1, 0, dx, dy, 1);
446  if (dx == 0 && dy == 0)
447  transform.m_type = TxNone;
448  else
449  transform.m_type = TxTranslate;
450  transform.m_dirty = TxNone;
451  return transform;
452 }
453 
461 {
462  if (sx == 1 && sy == 1)
463  return *this;
464 #ifndef QT_NO_DEBUG
465  if (qIsNaN(sx) | qIsNaN(sy)) {
466  nanWarning("scale");
467  return *this;
468  }
469 #endif
470 
471  switch(inline_type()) {
472  case TxNone:
473  case TxTranslate:
474  m_matrix[0][0] = sx;
475  m_matrix[1][1] = sy;
476  break;
477  case TxProject:
478  m_matrix[0][2] *= sx;
479  m_matrix[1][2] *= sy;
480  Q_FALLTHROUGH();
481  case TxRotate:
482  case TxShear:
483  m_matrix[0][1] *= sx;
484  m_matrix[1][0] *= sy;
485  Q_FALLTHROUGH();
486  case TxScale:
487  m_matrix[0][0] *= sx;
488  m_matrix[1][1] *= sy;
489  break;
490  }
491  if (m_dirty < TxScale)
492  m_dirty = TxScale;
493  return *this;
494 }
495 
504 {
505 #ifndef QT_NO_DEBUG
506  if (qIsNaN(sx) | qIsNaN(sy)) {
507  nanWarning("fromScale");
508  return QTransform();
509 }
510 #endif
511  QTransform transform(sx, 0, 0, 0, sy, 0, 0, 0, 1);
512  if (sx == 1. && sy == 1.)
513  transform.m_type = TxNone;
514  else
515  transform.m_type = TxScale;
516  transform.m_dirty = TxNone;
517  return transform;
518 }
519 
527 {
528  if (sh == 0 && sv == 0)
529  return *this;
530 #ifndef QT_NO_DEBUG
531  if (qIsNaN(sh) | qIsNaN(sv)) {
532  nanWarning("shear");
533  return *this;
534  }
535 #endif
536 
537  switch(inline_type()) {
538  case TxNone:
539  case TxTranslate:
540  m_matrix[0][1] = sv;
541  m_matrix[1][0] = sh;
542  break;
543  case TxScale:
544  m_matrix[0][1] = sv*m_matrix[1][1];
545  m_matrix[1][0] = sh*m_matrix[0][0];
546  break;
547  case TxProject: {
548  qreal tm13 = sv * m_matrix[1][2];
549  qreal tm23 = sh * m_matrix[0][2];
550  m_matrix[0][2] += tm13;
551  m_matrix[1][2] += tm23;
552  }
553  Q_FALLTHROUGH();
554  case TxRotate:
555  case TxShear: {
556  qreal tm11 = sv * m_matrix[1][0];
557  qreal tm22 = sh * m_matrix[0][1];
558  qreal tm12 = sv * m_matrix[1][1];
559  qreal tm21 = sh * m_matrix[0][0];
560  m_matrix[0][0] += tm11;
561  m_matrix[0][1] += tm12;
562  m_matrix[1][0] += tm21;
563  m_matrix[1][1] += tm22;
564  break;
565  }
566  }
567  if (m_dirty < TxShear)
568  m_dirty = TxShear;
569  return *this;
570 }
571 
572 const qreal inv_dist_to_plane = 1. / 1024.;
573 
589 {
590  if (a == 0)
591  return *this;
592 #ifndef QT_NO_DEBUG
593  if (qIsNaN(a)) {
594  nanWarning("rotate");
595  return *this;
596  }
597 #endif
598 
599  qreal sina = 0;
600  qreal cosa = 0;
601  if (a == 90. || a == -270.)
602  sina = 1.;
603  else if (a == 270. || a == -90.)
604  sina = -1.;
605  else if (a == 180.)
606  cosa = -1.;
607  else{
609  sina = qSin(b); // fast and convenient
610  cosa = qCos(b);
611  }
612 
613  if (axis == Qt::ZAxis) {
614  switch(inline_type()) {
615  case TxNone:
616  case TxTranslate:
617  m_matrix[0][0] = cosa;
618  m_matrix[0][1] = sina;
619  m_matrix[1][0] = -sina;
620  m_matrix[1][1] = cosa;
621  break;
622  case TxScale: {
623  qreal tm11 = cosa * m_matrix[0][0];
624  qreal tm12 = sina * m_matrix[1][1];
625  qreal tm21 = -sina * m_matrix[0][0];
626  qreal tm22 = cosa * m_matrix[1][1];
627  m_matrix[0][0] = tm11;
628  m_matrix[0][1] = tm12;
629  m_matrix[1][0] = tm21;
630  m_matrix[1][1] = tm22;
631  break;
632  }
633  case TxProject: {
634  qreal tm13 = cosa * m_matrix[0][2] + sina * m_matrix[1][2];
635  qreal tm23 = -sina * m_matrix[0][2] + cosa * m_matrix[1][2];
636  m_matrix[0][2] = tm13;
637  m_matrix[1][2] = tm23;
638  Q_FALLTHROUGH();
639  }
640  case TxRotate:
641  case TxShear: {
642  qreal tm11 = cosa * m_matrix[0][0] + sina * m_matrix[1][0];
643  qreal tm12 = cosa * m_matrix[0][1] + sina * m_matrix[1][1];
644  qreal tm21 = -sina * m_matrix[0][0] + cosa * m_matrix[1][0];
645  qreal tm22 = -sina * m_matrix[0][1] + cosa * m_matrix[1][1];
646  m_matrix[0][0] = tm11;
647  m_matrix[0][1] = tm12;
648  m_matrix[1][0] = tm21;
649  m_matrix[1][1] = tm22;
650  break;
651  }
652  }
653  if (m_dirty < TxRotate)
654  m_dirty = TxRotate;
655  } else {
657  if (axis == Qt::YAxis) {
658  result.m_matrix[0][0] = cosa;
659  result.m_matrix[0][2] = -sina * inv_dist_to_plane;
660  } else {
661  result.m_matrix[1][1] = cosa;
662  result.m_matrix[1][2] = -sina * inv_dist_to_plane;
663  }
664  result.m_type = TxProject;
665  *this = result * *this;
666  }
667 
668  return *this;
669 }
670 
686 {
687 #ifndef QT_NO_DEBUG
688  if (qIsNaN(a)) {
689  nanWarning("rotateRadians");
690  return *this;
691  }
692 #endif
693  qreal sina = qSin(a);
694  qreal cosa = qCos(a);
695 
696  if (axis == Qt::ZAxis) {
697  switch(inline_type()) {
698  case TxNone:
699  case TxTranslate:
700  m_matrix[0][0] = cosa;
701  m_matrix[0][1] = sina;
702  m_matrix[1][0] = -sina;
703  m_matrix[1][1] = cosa;
704  break;
705  case TxScale: {
706  qreal tm11 = cosa * m_matrix[0][0];
707  qreal tm12 = sina * m_matrix[1][1];
708  qreal tm21 = -sina * m_matrix[0][0];
709  qreal tm22 = cosa * m_matrix[1][1];
710  m_matrix[0][0] = tm11;
711  m_matrix[0][1] = tm12;
712  m_matrix[1][0] = tm21;
713  m_matrix[1][1] = tm22;
714  break;
715  }
716  case TxProject: {
717  qreal tm13 = cosa * m_matrix[0][2] + sina * m_matrix[1][2];
718  qreal tm23 = -sina * m_matrix[0][2] + cosa * m_matrix[1][2];
719  m_matrix[0][2] = tm13;
720  m_matrix[1][2] = tm23;
721  Q_FALLTHROUGH();
722  }
723  case TxRotate:
724  case TxShear: {
725  qreal tm11 = cosa * m_matrix[0][0] + sina * m_matrix[1][0];
726  qreal tm12 = cosa * m_matrix[0][1] + sina * m_matrix[1][1];
727  qreal tm21 = -sina * m_matrix[0][0] + cosa * m_matrix[1][0];
728  qreal tm22 = -sina * m_matrix[0][1] + cosa * m_matrix[1][1];
729  m_matrix[0][0] = tm11;
730  m_matrix[0][1] = tm12;
731  m_matrix[1][0] = tm21;
732  m_matrix[1][1] = tm22;
733  break;
734  }
735  }
736  if (m_dirty < TxRotate)
737  m_dirty = TxRotate;
738  } else {
740  if (axis == Qt::YAxis) {
741  result.m_matrix[0][0] = cosa;
742  result.m_matrix[0][2] = -sina * inv_dist_to_plane;
743  } else {
744  result.m_matrix[1][1] = cosa;
745  result.m_matrix[1][2] = -sina * inv_dist_to_plane;
746  }
747  result.m_type = TxProject;
748  *this = result * *this;
749  }
750  return *this;
751 }
752 
759 {
760  return m_matrix[0][0] == o.m_matrix[0][0] &&
761  m_matrix[0][1] == o.m_matrix[0][1] &&
762  m_matrix[1][0] == o.m_matrix[1][0] &&
763  m_matrix[1][1] == o.m_matrix[1][1] &&
764  m_matrix[2][0] == o.m_matrix[2][0] &&
765  m_matrix[2][1] == o.m_matrix[2][1] &&
766  m_matrix[0][2] == o.m_matrix[0][2] &&
767  m_matrix[1][2] == o.m_matrix[1][2] &&
768  m_matrix[2][2] == o.m_matrix[2][2];
769 }
770 
778 size_t qHash(const QTransform &key, size_t seed) noexcept
779 {
781  seed = hash(seed, key.m11());
782  seed = hash(seed, key.m12());
783  seed = hash(seed, key.m21());
784  seed = hash(seed, key.m22());
785  seed = hash(seed, key.dx());
786  seed = hash(seed, key.dy());
787  seed = hash(seed, key.m13());
788  seed = hash(seed, key.m23());
789  seed = hash(seed, key.m33());
790  return seed;
791 }
792 
793 
800 {
801  return !operator==(o);
802 }
803 
812 {
813  const TransformationType otherType = o.inline_type();
814  if (otherType == TxNone)
815  return *this;
816 
817  const TransformationType thisType = inline_type();
818  if (thisType == TxNone)
819  return operator=(o);
820 
821  TransformationType t = qMax(thisType, otherType);
822  switch(t) {
823  case TxNone:
824  break;
825  case TxTranslate:
826  m_matrix[2][0] += o.m_matrix[2][0];
827  m_matrix[2][1] += o.m_matrix[2][1];
828  break;
829  case TxScale:
830  {
831  qreal m11 = m_matrix[0][0] * o.m_matrix[0][0];
832  qreal m22 = m_matrix[1][1] * o.m_matrix[1][1];
833 
834  qreal m31 = m_matrix[2][0] * o.m_matrix[0][0] + o.m_matrix[2][0];
835  qreal m32 = m_matrix[2][1] * o.m_matrix[1][1] + o.m_matrix[2][1];
836 
837  m_matrix[0][0] = m11;
838  m_matrix[1][1] = m22;
839  m_matrix[2][0] = m31; m_matrix[2][1] = m32;
840  break;
841  }
842  case TxRotate:
843  case TxShear:
844  {
845  qreal m11 = m_matrix[0][0] * o.m_matrix[0][0] + m_matrix[0][1] * o.m_matrix[1][0];
846  qreal m12 = m_matrix[0][0] * o.m_matrix[0][1] + m_matrix[0][1] * o.m_matrix[1][1];
847 
848  qreal m21 = m_matrix[1][0] * o.m_matrix[0][0] + m_matrix[1][1] * o.m_matrix[1][0];
849  qreal m22 = m_matrix[1][0] * o.m_matrix[0][1] + m_matrix[1][1] * o.m_matrix[1][1];
850 
851  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];
852  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];
853 
854  m_matrix[0][0] = m11;
855  m_matrix[0][1] = m12;
856  m_matrix[1][0] = m21;
857  m_matrix[1][1] = m22;
858  m_matrix[2][0] = m31;
859  m_matrix[2][1] = m32;
860  break;
861  }
862  case TxProject:
863  {
864  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];
865  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];
866  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];
867 
868  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];
869  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];
870  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];
871 
872  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];
873  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];
874  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];
875 
876  m_matrix[0][0] = m11; m_matrix[0][1] = m12; m_matrix[0][2] = m13;
877  m_matrix[1][0] = m21; m_matrix[1][1] = m22; m_matrix[1][2] = m23;
878  m_matrix[2][0] = m31; m_matrix[2][1] = m32; m_matrix[2][2] = m33;
879  }
880  }
881 
882  m_dirty = t;
883  m_type = t;
884 
885  return *this;
886 }
887 
897 {
898  const TransformationType otherType = m.inline_type();
899  if (otherType == TxNone)
900  return *this;
901 
902  const TransformationType thisType = inline_type();
903  if (thisType == TxNone)
904  return m;
905 
906  QTransform t;
907  TransformationType type = qMax(thisType, otherType);
908  switch(type) {
909  case TxNone:
910  break;
911  case TxTranslate:
912  t.m_matrix[2][0] = m_matrix[2][0] + m.m_matrix[2][0];
913  t.m_matrix[2][1] = m_matrix[2][1] + m.m_matrix[2][1];
914  break;
915  case TxScale:
916  {
917  qreal m11 = m_matrix[0][0] * m.m_matrix[0][0];
918  qreal m22 = m_matrix[1][1] * m.m_matrix[1][1];
919 
920  qreal m31 = m_matrix[2][0] * m.m_matrix[0][0] + m.m_matrix[2][0];
921  qreal m32 = m_matrix[2][1] * m.m_matrix[1][1] + m.m_matrix[2][1];
922 
923  t.m_matrix[0][0] = m11;
924  t.m_matrix[1][1] = m22;
925  t.m_matrix[2][0] = m31;
926  t.m_matrix[2][1] = m32;
927  break;
928  }
929  case TxRotate:
930  case TxShear:
931  {
932  qreal m11 = m_matrix[0][0] * m.m_matrix[0][0] + m_matrix[0][1] * m.m_matrix[1][0];
933  qreal m12 = m_matrix[0][0] * m.m_matrix[0][1] + m_matrix[0][1] * m.m_matrix[1][1];
934 
935  qreal m21 = m_matrix[1][0] * m.m_matrix[0][0] + m_matrix[1][1] * m.m_matrix[1][0];
936  qreal m22 = m_matrix[1][0] * m.m_matrix[0][1] + m_matrix[1][1] * m.m_matrix[1][1];
937 
938  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];
939  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];
940 
941  t.m_matrix[0][0] = m11; t.m_matrix[0][1] = m12;
942  t.m_matrix[1][0] = m21; t.m_matrix[1][1] = m22;
943  t.m_matrix[2][0] = m31; t.m_matrix[2][1] = m32;
944  break;
945  }
946  case TxProject:
947  {
948  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];
949  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];
950  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];
951 
952  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];
953  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];
954  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];
955 
956  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];
957  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];
958  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];
959 
960  t.m_matrix[0][0] = m11; t.m_matrix[0][1] = m12; t.m_matrix[0][2] = m13;
961  t.m_matrix[1][0] = m21; t.m_matrix[1][1] = m22; t.m_matrix[1][2] = m23;
962  t.m_matrix[2][0] = m31; t.m_matrix[2][1] = m32; t.m_matrix[2][2] = m33;
963  }
964  }
965 
966  t.m_dirty = type;
967  t.m_type = type;
968 
969  return t;
970 }
971 
1019 {
1020  *this = QTransform();
1021 }
1022 
1023 #ifndef QT_NO_DATASTREAM
1035 {
1036  s << double(m.m11())
1037  << double(m.m12())
1038  << double(m.m13())
1039  << double(m.m21())
1040  << double(m.m22())
1041  << double(m.m23())
1042  << double(m.m31())
1043  << double(m.m32())
1044  << double(m.m33());
1045  return s;
1046 }
1047 
1059 {
1060  double m11, m12, m13,
1061  m21, m22, m23,
1062  m31, m32, m33;
1063 
1064  s >> m11;
1065  s >> m12;
1066  s >> m13;
1067  s >> m21;
1068  s >> m22;
1069  s >> m23;
1070  s >> m31;
1071  s >> m32;
1072  s >> m33;
1073  t.setMatrix(m11, m12, m13,
1074  m21, m22, m23,
1075  m31, m32, m33);
1076  return s;
1077 }
1078 
1079 #endif // QT_NO_DATASTREAM
1080 
1081 #ifndef QT_NO_DEBUG_STREAM
1083 {
1084  static const char typeStr[][12] =
1085  {
1086  "TxNone",
1087  "TxTranslate",
1088  "TxScale",
1089  "",
1090  "TxRotate",
1091  "", "", "",
1092  "TxShear",
1093  "", "", "", "", "", "", "",
1094  "TxProject"
1095  };
1096 
1097  QDebugStateSaver saver(dbg);
1098  dbg.nospace() << "QTransform(type=" << typeStr[m.type()] << ','
1099  << " 11=" << m.m11()
1100  << " 12=" << m.m12()
1101  << " 13=" << m.m13()
1102  << " 21=" << m.m21()
1103  << " 22=" << m.m22()
1104  << " 23=" << m.m23()
1105  << " 31=" << m.m31()
1106  << " 32=" << m.m32()
1107  << " 33=" << m.m33()
1108  << ')';
1109 
1110  return dbg;
1111 }
1112 #endif
1113 
1123 {
1124  qreal fx = p.x();
1125  qreal fy = p.y();
1126 
1127  qreal x = 0, y = 0;
1128 
1129  TransformationType t = inline_type();
1130  switch(t) {
1131  case TxNone:
1132  x = fx;
1133  y = fy;
1134  break;
1135  case TxTranslate:
1136  x = fx + m_matrix[2][0];
1137  y = fy + m_matrix[2][1];
1138  break;
1139  case TxScale:
1140  x = m_matrix[0][0] * fx + m_matrix[2][0];
1141  y = m_matrix[1][1] * fy + m_matrix[2][1];
1142  break;
1143  case TxRotate:
1144  case TxShear:
1145  case TxProject:
1146  x = m_matrix[0][0] * fx + m_matrix[1][0] * fy + m_matrix[2][0];
1147  y = m_matrix[0][1] * fx + m_matrix[1][1] * fy + m_matrix[2][1];
1148  if (t == TxProject) {
1149  qreal w = 1./(m_matrix[0][2] * fx + m_matrix[1][2] * fy + m_matrix[2][2]);
1150  x *= w;
1151  y *= w;
1152  }
1153  }
1154  return QPoint(qRound(x), qRound(y));
1155 }
1156 
1157 
1174 {
1175  qreal fx = p.x();
1176  qreal fy = p.y();
1177 
1178  qreal x = 0, y = 0;
1179 
1180  TransformationType t = inline_type();
1181  switch(t) {
1182  case TxNone:
1183  x = fx;
1184  y = fy;
1185  break;
1186  case TxTranslate:
1187  x = fx + m_matrix[2][0];
1188  y = fy + m_matrix[2][1];
1189  break;
1190  case TxScale:
1191  x = m_matrix[0][0] * fx + m_matrix[2][0];
1192  y = m_matrix[1][1] * fy + m_matrix[2][1];
1193  break;
1194  case TxRotate:
1195  case TxShear:
1196  case TxProject:
1197  x = m_matrix[0][0] * fx + m_matrix[1][0] * fy + m_matrix[2][0];
1198  y = m_matrix[0][1] * fx + m_matrix[1][1] * fy + m_matrix[2][1];
1199  if (t == TxProject) {
1200  qreal w = 1./(m_matrix[0][2] * fx + m_matrix[1][2] * fy + m_matrix[2][2]);
1201  x *= w;
1202  y *= w;
1203  }
1204  }
1205  return QPointF(x, y);
1206 }
1207 
1243 {
1244  qreal fx1 = l.x1();
1245  qreal fy1 = l.y1();
1246  qreal fx2 = l.x2();
1247  qreal fy2 = l.y2();
1248 
1249  qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0;
1250 
1251  TransformationType t = inline_type();
1252  switch(t) {
1253  case TxNone:
1254  x1 = fx1;
1255  y1 = fy1;
1256  x2 = fx2;
1257  y2 = fy2;
1258  break;
1259  case TxTranslate:
1260  x1 = fx1 + m_matrix[2][0];
1261  y1 = fy1 + m_matrix[2][1];
1262  x2 = fx2 + m_matrix[2][0];
1263  y2 = fy2 + m_matrix[2][1];
1264  break;
1265  case TxScale:
1266  x1 = m_matrix[0][0] * fx1 + m_matrix[2][0];
1267  y1 = m_matrix[1][1] * fy1 + m_matrix[2][1];
1268  x2 = m_matrix[0][0] * fx2 + m_matrix[2][0];
1269  y2 = m_matrix[1][1] * fy2 + m_matrix[2][1];
1270  break;
1271  case TxRotate:
1272  case TxShear:
1273  case TxProject:
1274  x1 = m_matrix[0][0] * fx1 + m_matrix[1][0] * fy1 + m_matrix[2][0];
1275  y1 = m_matrix[0][1] * fx1 + m_matrix[1][1] * fy1 + m_matrix[2][1];
1276  x2 = m_matrix[0][0] * fx2 + m_matrix[1][0] * fy2 + m_matrix[2][0];
1277  y2 = m_matrix[0][1] * fx2 + m_matrix[1][1] * fy2 + m_matrix[2][1];
1278  if (t == TxProject) {
1279  qreal w = 1./(m_matrix[0][2] * fx1 + m_matrix[1][2] * fy1 + m_matrix[2][2]);
1280  x1 *= w;
1281  y1 *= w;
1282  w = 1./(m_matrix[0][2] * fx2 + m_matrix[1][2] * fy2 + m_matrix[2][2]);
1283  x2 *= w;
1284  y2 *= w;
1285  }
1286  }
1287  return QLine(qRound(x1), qRound(y1), qRound(x2), qRound(y2));
1288 }
1289 
1302 {
1303  qreal fx1 = l.x1();
1304  qreal fy1 = l.y1();
1305  qreal fx2 = l.x2();
1306  qreal fy2 = l.y2();
1307 
1308  qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0;
1309 
1310  TransformationType t = inline_type();
1311  switch(t) {
1312  case TxNone:
1313  x1 = fx1;
1314  y1 = fy1;
1315  x2 = fx2;
1316  y2 = fy2;
1317  break;
1318  case TxTranslate:
1319  x1 = fx1 + m_matrix[2][0];
1320  y1 = fy1 + m_matrix[2][1];
1321  x2 = fx2 + m_matrix[2][0];
1322  y2 = fy2 + m_matrix[2][1];
1323  break;
1324  case TxScale:
1325  x1 = m_matrix[0][0] * fx1 + m_matrix[2][0];
1326  y1 = m_matrix[1][1] * fy1 + m_matrix[2][1];
1327  x2 = m_matrix[0][0] * fx2 + m_matrix[2][0];
1328  y2 = m_matrix[1][1] * fy2 + m_matrix[2][1];
1329  break;
1330  case TxRotate:
1331  case TxShear:
1332  case TxProject:
1333  x1 = m_matrix[0][0] * fx1 + m_matrix[1][0] * fy1 + m_matrix[2][0];
1334  y1 = m_matrix[0][1] * fx1 + m_matrix[1][1] * fy1 + m_matrix[2][1];
1335  x2 = m_matrix[0][0] * fx2 + m_matrix[1][0] * fy2 + m_matrix[2][0];
1336  y2 = m_matrix[0][1] * fx2 + m_matrix[1][1] * fy2 + m_matrix[2][1];
1337  if (t == TxProject) {
1338  qreal w = 1./(m_matrix[0][2] * fx1 + m_matrix[1][2] * fy1 + m_matrix[2][2]);
1339  x1 *= w;
1340  y1 *= w;
1341  w = 1./(m_matrix[0][2] * fx2 + m_matrix[1][2] * fy2 + m_matrix[2][2]);
1342  x2 *= w;
1343  y2 *= w;
1344  }
1345  }
1346  return QLineF(x1, y1, x2, y2);
1347 }
1348 
1349 static QPolygonF mapProjective(const QTransform &transform, const QPolygonF &poly)
1350 {
1351  if (poly.size() == 0)
1352  return poly;
1353 
1354  if (poly.size() == 1)
1355  return QPolygonF() << transform.map(poly.at(0));
1356 
1358  path.addPolygon(poly);
1359 
1360  path = transform.map(path);
1361 
1362  QPolygonF result;
1363  const int elementCount = path.elementCount();
1364  result.reserve(elementCount);
1365  for (int i = 0; i < elementCount; ++i)
1366  result << path.elementAt(i);
1367  return result;
1368 }
1369 
1370 
1399 {
1400  TransformationType t = inline_type();
1401  if (t <= TxTranslate)
1402  return a.translated(m_matrix[2][0], m_matrix[2][1]);
1403 
1404  if (t >= QTransform::TxProject)
1405  return mapProjective(*this, a);
1406 
1407  int size = a.size();
1408  int i;
1409  QPolygonF p(size);
1410  const QPointF *da = a.constData();
1411  QPointF *dp = p.data();
1412 
1413  for(i = 0; i < size; ++i) {
1414  MAP(da[i].xp, da[i].yp, dp[i].xp, dp[i].yp);
1415  }
1416  return p;
1417 }
1418 
1429 {
1430  TransformationType t = inline_type();
1431  if (t <= TxTranslate)
1432  return a.translated(qRound(m_matrix[2][0]), qRound(m_matrix[2][1]));
1433 
1434  if (t >= QTransform::TxProject)
1435  return mapProjective(*this, QPolygonF(a)).toPolygon();
1436 
1437  int size = a.size();
1438  int i;
1439  QPolygon p(size);
1440  const QPoint *da = a.constData();
1441  QPoint *dp = p.data();
1442 
1443  for(i = 0; i < size; ++i) {
1444  qreal nx = 0, ny = 0;
1445  MAP(da[i].xp, da[i].yp, nx, ny);
1446  dp[i].xp = qRound(nx);
1447  dp[i].yp = qRound(ny);
1448  }
1449  return p;
1450 }
1451 
1461 extern QPainterPath qt_regionToPath(const QRegion &region);
1462 
1474 {
1475  TransformationType t = inline_type();
1476  if (t == TxNone)
1477  return r;
1478 
1479  if (t == TxTranslate) {
1480  QRegion copy(r);
1481  copy.translate(qRound(m_matrix[2][0]), qRound(m_matrix[2][1]));
1482  return copy;
1483  }
1484 
1485  if (t == TxScale) {
1486  QRegion res;
1487  if (m11() < 0 || m22() < 0) {
1488  for (const QRect &rect : r)
1489  res += qt_mapFillRect(QRectF(rect), *this);
1490  } else {
1492  rects.reserve(r.rectCount());
1493  for (const QRect &rect : r) {
1494  QRect nr = qt_mapFillRect(QRectF(rect), *this);
1495  if (!nr.isEmpty())
1496  rects.append(nr);
1497  }
1498  res.setRects(rects.constData(), rects.count());
1499  }
1500  return res;
1501  }
1502 
1504  return p.toFillPolygon().toPolygon();
1505 }
1506 
1508 {
1512 
1514  QHomogeneousCoordinate(qreal x_, qreal y_, qreal w_) : x(x_), y(y_), w(w_) {}
1515 
1516  const QPointF toPoint() const {
1517  qreal iw = 1. / w;
1518  return QPointF(x * iw, y * iw);
1519  }
1520 };
1521 
1522 static inline QHomogeneousCoordinate mapHomogeneous(const QTransform &transform, const QPointF &p)
1523 {
1525  c.x = transform.m11() * p.x() + transform.m21() * p.y() + transform.m31();
1526  c.y = transform.m12() * p.x() + transform.m22() * p.y() + transform.m32();
1527  c.w = transform.m13() * p.x() + transform.m23() * p.y() + transform.m33();
1528  return c;
1529 }
1530 
1531 static inline bool lineTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b,
1532  bool needsMoveTo, bool needsLineTo = true)
1533 {
1534  QHomogeneousCoordinate ha = mapHomogeneous(transform, a);
1535  QHomogeneousCoordinate hb = mapHomogeneous(transform, b);
1536 
1537  if (ha.w < Q_NEAR_CLIP && hb.w < Q_NEAR_CLIP)
1538  return false;
1539 
1540  if (hb.w < Q_NEAR_CLIP) {
1541  const qreal t = (Q_NEAR_CLIP - hb.w) / (ha.w - hb.w);
1542 
1543  hb.x += (ha.x - hb.x) * t;
1544  hb.y += (ha.y - hb.y) * t;
1545  hb.w = qreal(Q_NEAR_CLIP);
1546  } else if (ha.w < Q_NEAR_CLIP) {
1547  const qreal t = (Q_NEAR_CLIP - ha.w) / (hb.w - ha.w);
1548 
1549  ha.x += (hb.x - ha.x) * t;
1550  ha.y += (hb.y - ha.y) * t;
1551  ha.w = qreal(Q_NEAR_CLIP);
1552 
1553  const QPointF p = ha.toPoint();
1554  if (needsMoveTo) {
1555  path.moveTo(p);
1556  needsMoveTo = false;
1557  } else {
1558  path.lineTo(p);
1559  }
1560  }
1561 
1562  if (needsMoveTo)
1563  path.moveTo(ha.toPoint());
1564 
1565  if (needsLineTo)
1566  path.lineTo(hb.toPoint());
1567 
1568  return true;
1569 }
1570 Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
1571 
1572 static inline bool cubicTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b, const QPointF &c, const QPointF &d, bool needsMoveTo)
1573 {
1574  // Convert projective xformed curves to line
1575  // segments so they can be transformed more accurately
1576 
1577  qreal scale;
1579 
1580  qreal curveThreshold = scale == 0 ? qreal(0.25) : (qreal(0.25) / scale);
1581 
1582  QPolygonF segment = QBezier::fromPoints(a, b, c, d).toPolygon(curveThreshold);
1583 
1584  for (int i = 0; i < segment.size() - 1; ++i)
1585  if (lineTo_clipped(path, transform, segment.at(i), segment.at(i+1), needsMoveTo))
1586  needsMoveTo = false;
1587 
1588  return !needsMoveTo;
1589 }
1590 
1591 static QPainterPath mapProjective(const QTransform &transform, const QPainterPath &path)
1592 {
1594 
1595  QPointF last;
1596  QPointF lastMoveTo;
1597  bool needsMoveTo = true;
1598  for (int i = 0; i < path.elementCount(); ++i) {
1599  switch (path.elementAt(i).type) {
1601  if (i > 0 && lastMoveTo != last)
1602  lineTo_clipped(result, transform, last, lastMoveTo, needsMoveTo);
1603 
1604  lastMoveTo = path.elementAt(i);
1605  last = path.elementAt(i);
1606  needsMoveTo = true;
1607  break;
1609  if (lineTo_clipped(result, transform, last, path.elementAt(i), needsMoveTo))
1610  needsMoveTo = false;
1611  last = path.elementAt(i);
1612  break;
1614  if (cubicTo_clipped(result, transform, last, path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), needsMoveTo))
1615  needsMoveTo = false;
1616  i += 2;
1617  last = path.elementAt(i);
1618  break;
1619  default:
1620  Q_ASSERT(false);
1621  }
1622  }
1623 
1624  if (path.elementCount() > 0 && lastMoveTo != last)
1625  lineTo_clipped(result, transform, last, lastMoveTo, needsMoveTo, false);
1626 
1627  result.setFillRule(path.fillRule());
1628  return result;
1629 }
1630 
1649 {
1650  TransformationType t = inline_type();
1651  if (t == TxNone || path.elementCount() == 0)
1652  return path;
1653 
1654  if (t >= TxProject)
1655  return mapProjective(*this, path);
1656 
1657  QPainterPath copy = path;
1658 
1659  if (t == TxTranslate) {
1660  copy.translate(m_matrix[2][0], m_matrix[2][1]);
1661  } else {
1662  copy.detach();
1663  // Full xform
1664  for (int i=0; i<path.elementCount(); ++i) {
1665  QPainterPath::Element &e = copy.d_ptr->elements[i];
1666  MAP(e.x, e.y, e.x, e.y);
1667  }
1668  }
1669 
1670  return copy;
1671 }
1672 
1694 {
1695  TransformationType t = inline_type();
1696 
1697  QPolygon a(4);
1698  qreal x[4] = { 0, 0, 0, 0 }, y[4] = { 0, 0, 0, 0 };
1699  if (t <= TxScale) {
1700  x[0] = m_matrix[0][0]*rect.x() + m_matrix[2][0];
1701  y[0] = m_matrix[1][1]*rect.y() + m_matrix[2][1];
1702  qreal w = m_matrix[0][0]*rect.width();
1703  qreal h = m_matrix[1][1]*rect.height();
1704  if (w < 0) {
1705  w = -w;
1706  x[0] -= w;
1707  }
1708  if (h < 0) {
1709  h = -h;
1710  y[0] -= h;
1711  }
1712  x[1] = x[0]+w;
1713  x[2] = x[1];
1714  x[3] = x[0];
1715  y[1] = y[0];
1716  y[2] = y[0]+h;
1717  y[3] = y[2];
1718  } else {
1719  qreal right = rect.x() + rect.width();
1720  qreal bottom = rect.y() + rect.height();
1721  MAP(rect.x(), rect.y(), x[0], y[0]);
1722  MAP(right, rect.y(), x[1], y[1]);
1723  MAP(right, bottom, x[2], y[2]);
1724  MAP(rect.x(), bottom, x[3], y[3]);
1725  }
1726 
1727  // all coordinates are correctly, transform to a pointarray
1728  // (rounding to the next integer)
1729  a.setPoints(4, qRound(x[0]), qRound(y[0]),
1730  qRound(x[1]), qRound(y[1]),
1731  qRound(x[2]), qRound(y[2]),
1732  qRound(x[3]), qRound(y[3]));
1733  return a;
1734 }
1735 
1744 {
1745  if (quad.count() != 4)
1746  return false;
1747 
1748  qreal dx0 = quad[0].x();
1749  qreal dx1 = quad[1].x();
1750  qreal dx2 = quad[2].x();
1751  qreal dx3 = quad[3].x();
1752 
1753  qreal dy0 = quad[0].y();
1754  qreal dy1 = quad[1].y();
1755  qreal dy2 = quad[2].y();
1756  qreal dy3 = quad[3].y();
1757 
1758  double ax = dx0 - dx1 + dx2 - dx3;
1759  double ay = dy0 - dy1 + dy2 - dy3;
1760 
1761  if (!ax && !ay) { //afine transform
1762  trans.setMatrix(dx1 - dx0, dy1 - dy0, 0,
1763  dx2 - dx1, dy2 - dy1, 0,
1764  dx0, dy0, 1);
1765  } else {
1766  double ax1 = dx1 - dx2;
1767  double ax2 = dx3 - dx2;
1768  double ay1 = dy1 - dy2;
1769  double ay2 = dy3 - dy2;
1770 
1771  /*determinants */
1772  double gtop = ax * ay2 - ax2 * ay;
1773  double htop = ax1 * ay - ax * ay1;
1774  double bottom = ax1 * ay2 - ax2 * ay1;
1775 
1776  double a, b, c, d, e, f, g, h; /*i is always 1*/
1777 
1778  if (!bottom)
1779  return false;
1780 
1781  g = gtop/bottom;
1782  h = htop/bottom;
1783 
1784  a = dx1 - dx0 + g * dx1;
1785  b = dx3 - dx0 + h * dx3;
1786  c = dx0;
1787  d = dy1 - dy0 + g * dy1;
1788  e = dy3 - dy0 + h * dy3;
1789  f = dy0;
1790 
1791  trans.setMatrix(a, d, g,
1792  b, e, h,
1793  c, f, 1.0);
1794  }
1795 
1796  return true;
1797 }
1798 
1809 {
1810  if (!squareToQuad(quad, trans))
1811  return false;
1812 
1813  bool invertible = false;
1814  trans = trans.inverted(&invertible);
1815 
1816  return invertible;
1817 }
1818 
1832  const QPolygonF &two,
1833  QTransform &trans)
1834 {
1835  QTransform stq;
1836  if (!quadToSquare(one, trans))
1837  return false;
1838  if (!squareToQuad(two, stq))
1839  return false;
1840  trans *= stq;
1841  //qDebug()<<"Final = "<<trans;
1842  return true;
1843 }
1844 
1857  qreal m21, qreal m22, qreal m23,
1858  qreal m31, qreal m32, qreal m33)
1859 {
1860  m_matrix[0][0] = m11; m_matrix[0][1] = m12; m_matrix[0][2] = m13;
1861  m_matrix[1][0] = m21; m_matrix[1][1] = m22; m_matrix[1][2] = m23;
1862  m_matrix[2][0] = m31; m_matrix[2][1] = m32; m_matrix[2][2] = m33;
1863  m_type = TxNone;
1864  m_dirty = TxProject;
1865 }
1866 
1867 static inline bool needsPerspectiveClipping(const QRectF &rect, const QTransform &transform)
1868 {
1869  const qreal wx = qMin(transform.m13() * rect.left(), transform.m13() * rect.right());
1870  const qreal wy = qMin(transform.m23() * rect.top(), transform.m23() * rect.bottom());
1871 
1872  return wx + wy + transform.m33() < Q_NEAR_CLIP;
1873 }
1874 
1876 {
1877  TransformationType t = inline_type();
1878  if (t <= TxTranslate)
1879  return rect.translated(qRound(m_matrix[2][0]), qRound(m_matrix[2][1]));
1880 
1881  if (t <= TxScale) {
1882  int x = qRound(m_matrix[0][0] * rect.x() + m_matrix[2][0]);
1883  int y = qRound(m_matrix[1][1] * rect.y() + m_matrix[2][1]);
1884  int w = qRound(m_matrix[0][0] * rect.width());
1885  int h = qRound(m_matrix[1][1] * rect.height());
1886  if (w < 0) {
1887  w = -w;
1888  x -= w;
1889  }
1890  if (h < 0) {
1891  h = -h;
1892  y -= h;
1893  }
1894  return QRect(x, y, w, h);
1895  } else if (t < TxProject || !needsPerspectiveClipping(rect, *this)) {
1896  // see mapToPolygon for explanations of the algorithm.
1897  qreal x = 0, y = 0;
1898  MAP(rect.left(), rect.top(), x, y);
1899  qreal xmin = x;
1900  qreal ymin = y;
1901  qreal xmax = x;
1902  qreal ymax = y;
1903  MAP(rect.right() + 1, rect.top(), x, y);
1904  xmin = qMin(xmin, x);
1905  ymin = qMin(ymin, y);
1906  xmax = qMax(xmax, x);
1907  ymax = qMax(ymax, y);
1908  MAP(rect.right() + 1, rect.bottom() + 1, x, y);
1909  xmin = qMin(xmin, x);
1910  ymin = qMin(ymin, y);
1911  xmax = qMax(xmax, x);
1912  ymax = qMax(ymax, y);
1913  MAP(rect.left(), rect.bottom() + 1, x, y);
1914  xmin = qMin(xmin, x);
1915  ymin = qMin(ymin, y);
1916  xmax = qMax(xmax, x);
1917  ymax = qMax(ymax, y);
1918  return QRect(qRound(xmin), qRound(ymin), qRound(xmax)-qRound(xmin), qRound(ymax)-qRound(ymin));
1919  } else {
1921  path.addRect(rect);
1922  return map(path).boundingRect().toRect();
1923  }
1924 }
1925 
1946 {
1947  TransformationType t = inline_type();
1948  if (t <= TxTranslate)
1949  return rect.translated(m_matrix[2][0], m_matrix[2][1]);
1950 
1951  if (t <= TxScale) {
1952  qreal x = m_matrix[0][0] * rect.x() + m_matrix[2][0];
1953  qreal y = m_matrix[1][1] * rect.y() + m_matrix[2][1];
1954  qreal w = m_matrix[0][0] * rect.width();
1955  qreal h = m_matrix[1][1] * rect.height();
1956  if (w < 0) {
1957  w = -w;
1958  x -= w;
1959  }
1960  if (h < 0) {
1961  h = -h;
1962  y -= h;
1963  }
1964  return QRectF(x, y, w, h);
1965  } else if (t < TxProject || !needsPerspectiveClipping(rect, *this)) {
1966  qreal x = 0, y = 0;
1967  MAP(rect.x(), rect.y(), x, y);
1968  qreal xmin = x;
1969  qreal ymin = y;
1970  qreal xmax = x;
1971  qreal ymax = y;
1972  MAP(rect.x() + rect.width(), rect.y(), x, y);
1973  xmin = qMin(xmin, x);
1974  ymin = qMin(ymin, y);
1975  xmax = qMax(xmax, x);
1976  ymax = qMax(ymax, y);
1977  MAP(rect.x() + rect.width(), rect.y() + rect.height(), x, y);
1978  xmin = qMin(xmin, x);
1979  ymin = qMin(ymin, y);
1980  xmax = qMax(xmax, x);
1981  ymax = qMax(ymax, y);
1982  MAP(rect.x(), rect.y() + rect.height(), x, y);
1983  xmin = qMin(xmin, x);
1984  ymin = qMin(ymin, y);
1985  xmax = qMax(xmax, x);
1986  ymax = qMax(ymax, y);
1987  return QRectF(xmin, ymin, xmax-xmin, ymax - ymin);
1988  } else {
1990  path.addRect(rect);
1991  return map(path).boundingRect();
1992  }
1993 }
1994 
2020 {
2021  TransformationType t = inline_type();
2022  MAP(x, y, *tx, *ty);
2023 }
2024 
2033 void QTransform::map(int x, int y, int *tx, int *ty) const
2034 {
2035  TransformationType t = inline_type();
2036  qreal fx = 0, fy = 0;
2037  MAP(x, y, fx, fy);
2038  *tx = qRound(fx);
2039  *ty = qRound(fy);
2040 }
2041 
2055 {
2056  if (m_dirty == TxNone || m_dirty < m_type)
2057  return static_cast<TransformationType>(m_type);
2058 
2059  switch (static_cast<TransformationType>(m_dirty)) {
2060  case TxProject:
2061  if (!qFuzzyIsNull(m_matrix[0][2]) || !qFuzzyIsNull(m_matrix[1][2]) || !qFuzzyIsNull(m_matrix[2][2] - 1)) {
2062  m_type = TxProject;
2063  break;
2064  }
2065  Q_FALLTHROUGH();
2066  case TxShear:
2067  case TxRotate:
2068  if (!qFuzzyIsNull(m_matrix[0][1]) || !qFuzzyIsNull(m_matrix[1][0])) {
2069  const qreal dot = m_matrix[0][0] * m_matrix[1][0] + m_matrix[0][1] * m_matrix[1][1];
2070  if (qFuzzyIsNull(dot))
2071  m_type = TxRotate;
2072  else
2073  m_type = TxShear;
2074  break;
2075  }
2076  Q_FALLTHROUGH();
2077  case TxScale:
2078  if (!qFuzzyIsNull(m_matrix[0][0] - 1) || !qFuzzyIsNull(m_matrix[1][1] - 1)) {
2079  m_type = TxScale;
2080  break;
2081  }
2082  Q_FALLTHROUGH();
2083  case TxTranslate:
2084  if (!qFuzzyIsNull(m_matrix[2][0]) || !qFuzzyIsNull(m_matrix[2][1])) {
2085  m_type = TxTranslate;
2086  break;
2087  }
2088  Q_FALLTHROUGH();
2089  case TxNone:
2090  m_type = TxNone;
2091  break;
2092  }
2093 
2094  m_dirty = TxNone;
2095  return static_cast<TransformationType>(m_type);
2096 }
2097 
2102 QTransform::operator QVariant() const
2103 {
2104  return QVariant::fromValue(*this);
2105 }
2106 
2107 
2279 // returns true if the transform is uniformly scaling
2280 // (same scale in x and y direction)
2281 // scale is set to the max of x and y scaling factors
2282 Q_GUI_EXPORT
2284 {
2286  if (type <= QTransform::TxTranslate) {
2287  if (scale)
2288  *scale = 1;
2289  return true;
2290  } else if (type == QTransform::TxScale) {
2291  const qreal xScale = qAbs(transform.m11());
2292  const qreal yScale = qAbs(transform.m22());
2293  if (scale)
2294  *scale = qMax(xScale, yScale);
2295  return qFuzzyCompare(xScale, yScale);
2296  }
2297 
2298  // rotate then scale: compare columns
2299  const qreal xScale1 = transform.m11() * transform.m11()
2300  + transform.m21() * transform.m21();
2301  const qreal yScale1 = transform.m12() * transform.m12()
2302  + transform.m22() * transform.m22();
2303 
2304  // scale then rotate: compare rows
2305  const qreal xScale2 = transform.m11() * transform.m11()
2306  + transform.m12() * transform.m12();
2307  const qreal yScale2 = transform.m21() * transform.m21()
2308  + transform.m22() * transform.m22();
2309 
2310  // decide the order of rotate and scale operations
2311  if (qAbs(xScale1 - yScale1) > qAbs(xScale2 - yScale2)) {
2312  if (scale)
2313  *scale = qSqrt(qMax(xScale1, yScale1));
2314 
2315  return type == QTransform::TxRotate && qFuzzyCompare(xScale1, yScale1);
2316  } else {
2317  if (scale)
2318  *scale = qSqrt(qMax(xScale2, yScale2));
2319 
2320  return type == QTransform::TxRotate && qFuzzyCompare(xScale2, yScale2);
2321  }
2322 }
2323 
2324 QDataStream & operator>>(QDataStream &s, QTransform::Affine &m)
2325 {
2326  if (s.version() == 1) {
2327  float m11, m12, m21, m22, dx, dy;
2328  s >> m11; s >> m12; s >> m21; s >> m22; s >> dx; s >> dy;
2329 
2330  m.m_matrix[0][0] = m11;
2331  m.m_matrix[0][1] = m12;
2332  m.m_matrix[1][0] = m21;
2333  m.m_matrix[1][1] = m22;
2334  m.m_matrix[2][0] = dx;
2335  m.m_matrix[2][1] = dy;
2336  } else {
2337  s >> m.m_matrix[0][0];
2338  s >> m.m_matrix[0][1];
2339  s >> m.m_matrix[1][0];
2340  s >> m.m_matrix[1][1];
2341  s >> m.m_matrix[2][0];
2342  s >> m.m_matrix[2][1];
2343  }
2344  m.m_matrix[0][2] = 0;
2345  m.m_matrix[1][2] = 0;
2346  m.m_matrix[2][2] = 1;
2347  return s;
2348 }
2349 
2350 QDataStream &operator<<(QDataStream &s, const QTransform::Affine &m)
2351 {
2352  if (s.version() == 1) {
2353  s << (float)m.m_matrix[0][0]
2354  << (float)m.m_matrix[0][1]
2355  << (float)m.m_matrix[1][0]
2356  << (float)m.m_matrix[1][1]
2357  << (float)m.m_matrix[2][0]
2358  << (float)m.m_matrix[2][1];
2359  } else {
2360  s << m.m_matrix[0][0]
2361  << m.m_matrix[0][1]
2362  << m.m_matrix[1][0]
2363  << m.m_matrix[1][1]
2364  << m.m_matrix[2][0]
2365  << m.m_matrix[2][1];
2366  }
2367  return s;
2368 }
2369 
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
static QBezier fromPoints(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4)
Definition: qbezier_p.h:70
QPolygonF toPolygon(qreal bezier_flattening_threshold=0.5) const
Definition: qbezier.cpp:57
The QDataStream class provides serialization of binary data to a QIODevice.
Definition: qdatastream.h:66
operator>>(QDataStream &ds, qfloat16 &f)
Definition: qfloat16.cpp:344
operator<<(QDataStream &ds, qfloat16 f)
Definition: qfloat16.cpp:327
The QDebug class provides an output stream for debugging information.
Definition: qdebug.h:65
QDebug & nospace()
Definition: qdebug.h:113
Convenience class for custom QDebug operators.
Definition: qdebug.h:176
The QLineF class provides a two-dimensional vector using floating point precision.
Definition: qline.h:215
The QLine class provides a two-dimensional vector using integer precision.
Definition: qline.h:53
qsizetype size() const noexcept
Definition: qlist.h:414
const_reference at(qsizetype i) const noexcept
Definition: qlist.h:457
qsizetype count() const noexcept
Definition: qlist.h:415
bool qFuzzyCompare(const QMatrix4x4 &m1, const QMatrix4x4 &m2)
Definition: qmatrix4x4.cpp:774
The QPainterPath::Element class specifies the position and type of a subpath.
Definition: qpainterpath.h:74
The QPainterPath class provides a container for painting operations, enabling graphical shapes to be ...
Definition: qpainterpath.h:65
void translate(qreal dx, qreal dy)
The QPointF class defines a point in the plane using floating point precision.
Definition: qpoint.h:242
The QPoint class defines a point in the plane using integer precision.
Definition: qpoint.h:52
The QPolygonF class provides a list of points using floating point precision. \inmodule QtGui.
Definition: qpolygon.h:128
QPolygon Q_GUI_EXPORT toPolygon() const
Definition: qpolygon.cpp:645
The QPolygon class provides a list of points using integer precision. \inmodule QtGui.
Definition: qpolygon.h:57
The QRectF class defines a finite rectangle in the plane using floating point precision.
Definition: qrect.h:511
The QRect class defines a rectangle in the plane using integer precision.
Definition: qrect.h:59
constexpr bool isEmpty() const noexcept
Definition: qrect.h:194
The QRegion class specifies a clip region for a painter.
Definition: qregion.h:63
void translate(int dx, int dy)
The QTransform class specifies 2D transformations of a coordinate system.
Definition: qtransform.h:56
QTransform & rotateRadians(qreal a, Qt::Axis axis=Qt::ZAxis)
Definition: qtransform.cpp:685
static bool quadToQuad(const QPolygonF &one, const QPolygonF &two, QTransform &result)
qreal m21() const
Definition: qtransform.h:238
QTransform & operator*=(const QTransform &)
Definition: qtransform.cpp:811
qreal m23() const
Definition: qtransform.h:246
static QTransform fromScale(qreal dx, qreal dy)
Definition: qtransform.cpp:503
static bool quadToSquare(const QPolygonF &quad, QTransform &result)
QTransform adjoint() const
Definition: qtransform.cpp:298
QTransform & scale(qreal sx, qreal sy)
Definition: qtransform.cpp:460
qreal m12() const
Definition: qtransform.h:230
bool operator==(const QTransform &) const
Definition: qtransform.cpp:758
qreal m33() const
Definition: qtransform.h:258
QTransform operator*(const QTransform &o) const
Definition: qtransform.cpp:896
qreal dx() const
Definition: qtransform.h:262
QPoint map(const QPoint &p) const
qreal m31() const
Definition: qtransform.h:250
QTransform & operator=(QTransform &&other) noexcept=default
qreal m32() const
Definition: qtransform.h:254
QTransform & shear(qreal sh, qreal sv)
Definition: qtransform.cpp:526
qreal m11() const
Definition: qtransform.h:226
static bool squareToQuad(const QPolygonF &square, QTransform &result)
void setMatrix(qreal m11, qreal m12, qreal m13, qreal m21, qreal m22, qreal m23, qreal m31, qreal m32, qreal m33)
size_t qHash(const QTransform &key, size_t seed) noexcept
Definition: qtransform.cpp:778
static QTransform fromTranslate(qreal dx, qreal dy)
Definition: qtransform.cpp:437
QTransform inverted(bool *invertible=nullptr) const
Definition: qtransform.cpp:339
bool operator!=(const QTransform &) const
Definition: qtransform.cpp:799
QPolygon mapToPolygon(const QRect &r) const
QTransform & rotate(qreal a, Qt::Axis axis=Qt::ZAxis)
Definition: qtransform.cpp:588
void reset()
TransformationType type() const
qreal determinant() const
Definition: qtransform.h:220
QTransform & translate(qreal dx, qreal dy)
Definition: qtransform.cpp:392
qreal m13() const
Definition: qtransform.h:234
QTransform transposed() const
Definition: qtransform.cpp:321
qreal m22() const
Definition: qtransform.h:242
QRect mapRect(const QRect &) const
TransformationType
Definition: qtransform.h:58
qreal dy() const
Definition: qtransform.h:266
qsizetype count() const
const T * constData() const
void append(const T &t)
void reserve(qsizetype sz)
The QVariant class acts like a union for the most common Qt data types.
Definition: qvariant.h:95
static auto fromValue(const T &value) -> std::enable_if_t< std::is_copy_constructible_v< T >, QVariant >
Definition: qvariant.h:391
QHash< int, QWidget * > hash
[35multi]
double e
rect
[4]
@ ZAxis
Definition: qnamespace.h:1357
@ YAxis
Definition: qnamespace.h:1356
#define Q_FALLTHROUGH()
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition: qfloat16.h:249
bool qIsNaN(qfloat16 f) noexcept
Definition: qfloat16.h:221
int qRound(qfloat16 d) noexcept
Definition: qfloat16.h:227
QT_END_INCLUDE_NAMESPACE typedef double qreal
Definition: qglobal.h:341
#define qWarning
Definition: qlogging.h:179
auto qSqrt(T v)
Definition: qmath.h:132
auto qCos(T v)
Definition: qmath.h:96
auto qSin(T v)
Definition: qmath.h:90
constexpr float qDegreesToRadians(float degrees)
Definition: qmath.h:296
QRect qt_mapFillRect(const QRectF &rect, const QTransform &xf)
Definition: qmath_p.h:62
GLenum type
Definition: qopengl.h:270
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLuint64 key
GLboolean r
[2]
GLfloat GLfloat GLfloat w
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLfloat GLfloat GLfloat x1
GLdouble GLdouble right
GLfloat GLfloat f
GLint GLint bottom
GLboolean GLboolean g
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLuint GLenum GLenum transform
Definition: qopenglext.h:11564
GLenum func
Definition: qopenglext.h:663
GLbyte nx
Definition: qopenglext.h:6430
GLuint res
Definition: qopenglext.h:8867
const GLubyte * c
Definition: qopenglext.h:12701
GLfixed GLfixed GLfixed y2
Definition: qopenglext.h:5231
GLuint segment
Definition: qopenglext.h:9597
GLfixed ny
Definition: qopenglext.h:5169
GLfixed GLfixed x2
Definition: qopenglext.h:5231
GLdouble GLdouble t
[9]
Definition: qopenglext.h:243
GLsizei const GLchar *const * path
Definition: qopenglext.h:4283
GLuint64EXT * result
[6]
Definition: qopenglext.h:10932
GLdouble s
[6]
Definition: qopenglext.h:235
GLfloat GLfloat p
[1]
Definition: qopenglext.h:12698
GLenum GLenum GLenum GLenum GLenum scale
Definition: qopenglext.h:10817
GLbyte ty
Definition: qopenglext.h:6700
GLboolean invert
Definition: qopenglext.h:226
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
QPointF qAbs(const QPointF &p)
Definition: qscroller.cpp:119
#define Q_NEAR_CLIP
Definition: qtransform.cpp:63
#define MAP(x, y, nx, ny)
Definition: qtransform.cpp:68
Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale)
QPainterPath qt_regionToPath(const QRegion &region)
Definition: qregion.cpp:1043
const qreal inv_dist_to_plane
Definition: qtransform.cpp:572
const QPointF toPoint() const
QHomogeneousCoordinate(qreal x_, qreal y_, qreal w_)