Work around compiler bug in QTransform

gcc version 13.2.0 (Ubuntu 13.2.0-23ubuntu4) seems to remove some
code in release builds that is embedded in a macro in qtransfrom.cpp.
This patch changes the macro into a function such that it is not
removed by gcc.

It seems like the switch statement is (wrongly) optimized away if it is
embedded in the QTransform::map(QPolygonF) overload. nx and ny are
always 0 (as initialized by QPolygonF) after the macro (but only within
the QTransform::map(QPolygonF) function.

The bug happens only in a release build. Added debug messages of nx and
ny created no output, adding qDebug() << t, which made the macro work.

It seems that a complex combination of macro and switch statement can
tigger the bug in gcc. So rewriting the macro as a function works around
the bug.

Fixes: QTBUG-127723
Change-Id: I7e0b02cde276e591cf773f4e18dd505134421957
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
(cherry picked from commit 11b70b66de627d3bd0ef4193d5cfa624c88ac96a)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Matthias Rauter 2024-08-06 13:01:45 +02:00 committed by Qt Cherry-pick Bot
parent 11473410c2
commit 1909435b03
3 changed files with 72 additions and 65 deletions

View File

@ -26,40 +26,38 @@ static void nanWarning(const char *func)
#define Q_NEAR_CLIP (sizeof(qreal) == sizeof(double) ? 0.000001 : 0.0001) #define Q_NEAR_CLIP (sizeof(qreal) == sizeof(double) ? 0.000001 : 0.0001)
#ifdef MAP void QTransform::do_map(qreal x, qreal y, qreal &nx, qreal &ny) const
# undef MAP {
#endif const TransformationType t = inline_type();
#define MAP(x, y, nx, ny) \ switch (t) {
do { \ case QTransform::TxNone:
qreal FX_ = x; \ nx = x;
qreal FY_ = y; \ ny = y;
switch(t) { \ return;
case TxNone: \ case QTransform::TxTranslate:
nx = FX_; \ nx = x + m_matrix[2][0];
ny = FY_; \ ny = y + m_matrix[2][1];
break; \ return;
case TxTranslate: \ case QTransform::TxScale:
nx = FX_ + m_matrix[2][0]; \ nx = m_matrix[0][0] * x + m_matrix[2][0];
ny = FY_ + m_matrix[2][1]; \ ny = m_matrix[1][1] * y + m_matrix[2][1];
break; \ return;
case TxScale: \ case QTransform::TxRotate:
nx = m_matrix[0][0] * FX_ + m_matrix[2][0]; \ case QTransform::TxShear:
ny = m_matrix[1][1] * FY_ + m_matrix[2][1]; \ case QTransform::TxProject:
break; \ nx = m_matrix[0][0] * x + m_matrix[1][0] * y + m_matrix[2][0];
case TxRotate: \ ny = m_matrix[0][1] * x + m_matrix[1][1] * y + m_matrix[2][1];
case TxShear: \ if (t == QTransform::TxProject) {
case TxProject: \ qreal w = (m_matrix[0][2] * x + m_matrix[1][2] * y + m_matrix[2][2]);
nx = m_matrix[0][0] * FX_ + m_matrix[1][0] * FY_ + m_matrix[2][0]; \ if (w < qreal(Q_NEAR_CLIP)) w = qreal(Q_NEAR_CLIP);
ny = m_matrix[0][1] * FX_ + m_matrix[1][1] * FY_ + m_matrix[2][1]; \ w = qreal(1.)/w;
if (t == TxProject) { \ nx *= w;
qreal w = (m_matrix[0][2] * FX_ + m_matrix[1][2] * FY_ + m_matrix[2][2]); \ ny *= w;
if (w < qreal(Q_NEAR_CLIP)) w = qreal(Q_NEAR_CLIP); \ }
w = qreal(1.)/w; \ return;
nx *= w; \ }
ny *= w; \ Q_UNREACHABLE_RETURN();
} \ }
} \
} while (0)
/*! /*!
\class QTransform \class QTransform
@ -1144,8 +1142,7 @@ QPoint QTransform::map(const QPoint &p) const
qreal x = 0, y = 0; qreal x = 0, y = 0;
TransformationType t = inline_type(); do_map(fx, fy, x, y);
MAP(fx, fy, x, y);
return QPoint(qRound(x), qRound(y)); return QPoint(qRound(x), qRound(y));
} }
@ -1173,8 +1170,7 @@ QPointF QTransform::map(const QPointF &p) const
qreal x = 0, y = 0; qreal x = 0, y = 0;
TransformationType t = inline_type(); do_map(fx, fy, x, y);
MAP(fx, fy, x, y);
return QPointF(x, y); return QPointF(x, y);
} }
@ -1222,9 +1218,8 @@ QLine QTransform::map(const QLine &l) const
qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0; qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0;
TransformationType t = inline_type(); do_map(fx1, fy1, x1, y1);
MAP(fx1, fy1, x1, y1); do_map(fx2, fy2, x2, y2);
MAP(fx2, fy2, x2, y2);
return QLine(qRound(x1), qRound(y1), qRound(x2), qRound(y2)); return QLine(qRound(x1), qRound(y1), qRound(x2), qRound(y2));
} }
@ -1249,9 +1244,8 @@ QLineF QTransform::map(const QLineF &l) const
qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0; qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0;
TransformationType t = inline_type(); do_map(fx1, fy1, x1, y1);
MAP(fx1, fy1, x1, y1); do_map(fx2, fy2, x2, y2);
MAP(fx2, fy2, x2, y2);
return QLineF(x1, y1, x2, y2); return QLineF(x1, y1, x2, y2);
} }
@ -1296,7 +1290,7 @@ QPolygonF QTransform::map(const QPolygonF &a) const
QPointF *dp = p.data(); QPointF *dp = p.data();
for(i = 0; i < size; ++i) { for(i = 0; i < size; ++i) {
MAP(da[i].xp, da[i].yp, dp[i].xp, dp[i].yp); do_map(da[i].xp, da[i].yp, dp[i].xp, dp[i].yp);
} }
return p; return p;
} }
@ -1324,7 +1318,7 @@ QPolygon QTransform::map(const QPolygon &a) const
for(i = 0; i < size; ++i) { for(i = 0; i < size; ++i) {
qreal nx = 0, ny = 0; qreal nx = 0, ny = 0;
MAP(da[i].xp, da[i].yp, nx, ny); do_map(da[i].xp, da[i].yp, nx, ny);
dp[i].xp = qRound(nx); dp[i].xp = qRound(nx);
dp[i].yp = qRound(ny); dp[i].yp = qRound(ny);
} }
@ -1545,7 +1539,7 @@ QPainterPath QTransform::map(const QPainterPath &path) const
// Full xform // Full xform
for (int i=0; i<path.elementCount(); ++i) { for (int i=0; i<path.elementCount(); ++i) {
QPainterPath::Element &e = copy.d_ptr->elements[i]; QPainterPath::Element &e = copy.d_ptr->elements[i];
MAP(e.x, e.y, e.x, e.y); do_map(e.x, e.y, e.x, e.y);
} }
} }
@ -1598,12 +1592,12 @@ QPolygon QTransform::mapToPolygon(const QRect &rect) const
y[2] = y[0]+h; y[2] = y[0]+h;
y[3] = y[2]; y[3] = y[2];
} else { } else {
qreal right = rect.x() + rect.width(); auto right = rect.x() + rect.width();
qreal bottom = rect.y() + rect.height(); auto bottom = rect.y() + rect.height();
MAP(rect.x(), rect.y(), x[0], y[0]); do_map(rect.x(), rect.y(), x[0], y[0]);
MAP(right, rect.y(), x[1], y[1]); do_map(right, rect.y(), x[1], y[1]);
MAP(right, bottom, x[2], y[2]); do_map(right, bottom, x[2], y[2]);
MAP(rect.x(), bottom, x[3], y[3]); do_map(rect.x(), bottom, x[3], y[3]);
} }
// all coordinates are correctly, transform to a pointarray // all coordinates are correctly, transform to a pointarray
@ -1768,22 +1762,22 @@ QRect QTransform::mapRect(const QRect &rect) const
return QRect(x, y, w, h); return QRect(x, y, w, h);
} else { } else {
qreal x = 0, y = 0; qreal x = 0, y = 0;
MAP(rect.left(), rect.top(), x, y); do_map(rect.left(), rect.top(), x, y);
qreal xmin = x; qreal xmin = x;
qreal ymin = y; qreal ymin = y;
qreal xmax = x; qreal xmax = x;
qreal ymax = y; qreal ymax = y;
MAP(rect.right() + 1, rect.top(), x, y); do_map(rect.right() + 1, rect.top(), x, y);
xmin = qMin(xmin, x); xmin = qMin(xmin, x);
ymin = qMin(ymin, y); ymin = qMin(ymin, y);
xmax = qMax(xmax, x); xmax = qMax(xmax, x);
ymax = qMax(ymax, y); ymax = qMax(ymax, y);
MAP(rect.right() + 1, rect.bottom() + 1, x, y); do_map(rect.right() + 1, rect.bottom() + 1, x, y);
xmin = qMin(xmin, x); xmin = qMin(xmin, x);
ymin = qMin(ymin, y); ymin = qMin(ymin, y);
xmax = qMax(xmax, x); xmax = qMax(xmax, x);
ymax = qMax(ymax, y); ymax = qMax(ymax, y);
MAP(rect.left(), rect.bottom() + 1, x, y); do_map(rect.left(), rect.bottom() + 1, x, y);
xmin = qMin(xmin, x); xmin = qMin(xmin, x);
ymin = qMin(ymin, y); ymin = qMin(ymin, y);
xmax = qMax(xmax, x); xmax = qMax(xmax, x);
@ -1833,22 +1827,22 @@ QRectF QTransform::mapRect(const QRectF &rect) const
return QRectF(x, y, w, h); return QRectF(x, y, w, h);
} else { } else {
qreal x = 0, y = 0; qreal x = 0, y = 0;
MAP(rect.x(), rect.y(), x, y); do_map(rect.x(), rect.y(), x, y);
qreal xmin = x; qreal xmin = x;
qreal ymin = y; qreal ymin = y;
qreal xmax = x; qreal xmax = x;
qreal ymax = y; qreal ymax = y;
MAP(rect.x() + rect.width(), rect.y(), x, y); do_map(rect.x() + rect.width(), rect.y(), x, y);
xmin = qMin(xmin, x); xmin = qMin(xmin, x);
ymin = qMin(ymin, y); ymin = qMin(ymin, y);
xmax = qMax(xmax, x); xmax = qMax(xmax, x);
ymax = qMax(ymax, y); ymax = qMax(ymax, y);
MAP(rect.x() + rect.width(), rect.y() + rect.height(), x, y); do_map(rect.x() + rect.width(), rect.y() + rect.height(), x, y);
xmin = qMin(xmin, x); xmin = qMin(xmin, x);
ymin = qMin(ymin, y); ymin = qMin(ymin, y);
xmax = qMax(xmax, x); xmax = qMax(xmax, x);
ymax = qMax(ymax, y); ymax = qMax(ymax, y);
MAP(rect.x(), rect.y() + rect.height(), x, y); do_map(rect.x(), rect.y() + rect.height(), x, y);
xmin = qMin(xmin, x); xmin = qMin(xmin, x);
ymin = qMin(ymin, y); ymin = qMin(ymin, y);
xmax = qMax(xmax, x); xmax = qMax(xmax, x);
@ -1883,8 +1877,7 @@ QRectF QTransform::mapRect(const QRectF &rect) const
*/ */
void QTransform::map(qreal x, qreal y, qreal *tx, qreal *ty) const void QTransform::map(qreal x, qreal y, qreal *tx, qreal *ty) const
{ {
TransformationType t = inline_type(); do_map(x, y, *tx, *ty);
MAP(x, y, *tx, *ty);
} }
/*! /*!
@ -1897,9 +1890,8 @@ void QTransform::map(qreal x, qreal y, qreal *tx, qreal *ty) const
*/ */
void QTransform::map(int x, int y, int *tx, int *ty) const void QTransform::map(int x, int y, int *tx, int *ty) const
{ {
TransformationType t = inline_type();
qreal fx = 0, fy = 0; qreal fx = 0, fy = 0;
MAP(x, y, fx, fy); do_map(x, y, fx, fy);
*tx = qRound(fx); *tx = qRound(fx);
*ty = qRound(fy); *ty = qRound(fy);
} }

View File

@ -145,6 +145,7 @@ public:
private: private:
inline TransformationType inline_type() const; inline TransformationType inline_type() const;
void do_map(qreal x, qreal y, qreal &nx, qreal &ny) const;
qreal m_matrix[3][3]; qreal m_matrix[3][3];
mutable uint m_type : 5; mutable uint m_type : 5;

View File

@ -34,6 +34,7 @@ private slots:
void projectivePathMapping(); void projectivePathMapping();
void mapInt(); void mapInt();
void mapPathWithPoint(); void mapPathWithPoint();
void mapRectToPolygon(); // QTBUG-127723
private: private:
void mapping_data(); void mapping_data();
@ -700,6 +701,19 @@ void tst_QTransform::mapPathWithPoint()
QCOMPARE(p.currentPosition(), QPointF(20, 20)); QCOMPARE(p.currentPosition(), QPointF(20, 20));
} }
void tst_QTransform::mapRectToPolygon()
{
QRectF r(7, 7, 36, 36);
QTransform tx(2, 0, 0, 2, 0, 0);
QPolygonF polygon1 = tx.mapToPolygon(r.toRect()).toPolygonF();
QPolygonF polygon2 = tx.map(QPolygonF(r));
if (polygon1.size() > 4)
polygon1.removeLast();
if (polygon2.size() > 4)
polygon2.removeLast();
QCOMPARE(polygon1, polygon2);
}
QTEST_APPLESS_MAIN(tst_QTransform) QTEST_APPLESS_MAIN(tst_QTransform)