Allow specify distance to plane for QTransform
The default camera to plane distance is 1024, when rotating a big image along the x or y axis, some areas of the screen may move above the camera, causing the rotation to fail. A new rotation interface has been added to allow users to specify the distance from the camera to the plane themselves when rotating the QImage. Also, this support has been added to QMatrix4x4::projectedRotate. [ChangeLog][QtGui][QTransform] Added overloads to rotate() and rotateRadians() that allow specifying of the distance to the rotation plane. Fixes: QTBUG-105088 Change-Id: I81f629916ddd9b6ab84e0282191e4284a88a85f5 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
This commit is contained in:
parent
371214dea7
commit
a6b55b3c46
@ -1164,12 +1164,15 @@ void QMatrix4x4::rotate(float angle, float x, float y, float z)
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
void QMatrix4x4::projectedRotate(float angle, float x, float y, float z)
|
||||
void QMatrix4x4::projectedRotate(float angle, float x, float y, float z, float distanceToPlane)
|
||||
{
|
||||
// Used by QGraphicsRotation::applyTo() to perform a rotation
|
||||
// and projection back to 2D in a single step.
|
||||
if (qIsNull(distanceToPlane))
|
||||
return rotate(angle, x, y, z);
|
||||
if (angle == 0.0f)
|
||||
return;
|
||||
|
||||
float c, s;
|
||||
if (angle == 90.0f || angle == -270.0f) {
|
||||
s = 1.0f;
|
||||
@ -1185,6 +1188,8 @@ void QMatrix4x4::projectedRotate(float angle, float x, float y, float z)
|
||||
c = std::cos(a);
|
||||
s = std::sin(a);
|
||||
}
|
||||
|
||||
const qreal d = 1.0 / distanceToPlane;
|
||||
if (x == 0.0f) {
|
||||
if (y == 0.0f) {
|
||||
if (z != 0.0f) {
|
||||
@ -1208,10 +1213,11 @@ void QMatrix4x4::projectedRotate(float angle, float x, float y, float z)
|
||||
// Rotate around the Y axis.
|
||||
if (y < 0)
|
||||
s = -s;
|
||||
m[0][0] = m[0][0] * c + m[3][0] * s * inv_dist_to_plane;
|
||||
m[0][1] = m[0][1] * c + m[3][1] * s * inv_dist_to_plane;
|
||||
m[0][2] = m[0][2] * c + m[3][2] * s * inv_dist_to_plane;
|
||||
m[0][3] = m[0][3] * c + m[3][3] * s * inv_dist_to_plane;
|
||||
s *= d;
|
||||
m[0][0] = m[0][0] * c + m[3][0] * s;
|
||||
m[0][1] = m[0][1] * c + m[3][1] * s;
|
||||
m[0][2] = m[0][2] * c + m[3][2] * s;
|
||||
m[0][3] = m[0][3] * c + m[3][3] * s;
|
||||
flagBits = General;
|
||||
return;
|
||||
}
|
||||
@ -1219,10 +1225,11 @@ void QMatrix4x4::projectedRotate(float angle, float x, float y, float z)
|
||||
// Rotate around the X axis.
|
||||
if (x < 0)
|
||||
s = -s;
|
||||
m[1][0] = m[1][0] * c - m[3][0] * s * inv_dist_to_plane;
|
||||
m[1][1] = m[1][1] * c - m[3][1] * s * inv_dist_to_plane;
|
||||
m[1][2] = m[1][2] * c - m[3][2] * s * inv_dist_to_plane;
|
||||
m[1][3] = m[1][3] * c - m[3][3] * s * inv_dist_to_plane;
|
||||
s *= d;
|
||||
m[1][0] = m[1][0] * c - m[3][0] * s;
|
||||
m[1][1] = m[1][1] * c - m[3][1] * s;
|
||||
m[1][2] = m[1][2] * c - m[3][2] * s;
|
||||
m[1][3] = m[1][3] * c - m[3][3] * s;
|
||||
flagBits = General;
|
||||
return;
|
||||
}
|
||||
@ -1249,14 +1256,24 @@ void QMatrix4x4::projectedRotate(float angle, float x, float y, float z)
|
||||
rot.m[1][2] = 0.0f;
|
||||
rot.m[2][2] = 1.0f;
|
||||
rot.m[3][2] = 0.0f;
|
||||
rot.m[0][3] = (x * z * ic - y * s) * -inv_dist_to_plane;
|
||||
rot.m[1][3] = (y * z * ic + x * s) * -inv_dist_to_plane;
|
||||
rot.m[0][3] = (x * z * ic - y * s) * -d;
|
||||
rot.m[1][3] = (y * z * ic + x * s) * -d;
|
||||
rot.m[2][3] = 0.0f;
|
||||
rot.m[3][3] = 1.0f;
|
||||
rot.flagBits = General;
|
||||
*this *= rot;
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
void QMatrix4x4::projectedRotate(float angle, float x, float y, float z)
|
||||
{
|
||||
projectedRotate(angle, x, y, z, 1024.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\fn int QMatrix4x4::flags() const
|
||||
\internal
|
||||
|
@ -154,7 +154,13 @@ public:
|
||||
friend Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QMatrix4x4 &m);
|
||||
#endif
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
|
||||
void projectedRotate(float angle, float x, float y, float z, float distanceToPlane);
|
||||
// ### Qt7: Remove
|
||||
void projectedRotate(float angle, float x, float y, float z);
|
||||
#else
|
||||
void projectedRotate(float angle, float x, float y, float z, float distanceToPlane = 1024.0);
|
||||
#endif
|
||||
|
||||
// When matrices are multiplied, the flag bits are or-ed together.
|
||||
// Note that the ordering of the bit values matters. (ident < t < s < r2d < r < p)
|
||||
|
@ -533,28 +533,33 @@ QTransform & QTransform::shear(qreal sh, qreal sv)
|
||||
return *this;
|
||||
}
|
||||
|
||||
const qreal inv_dist_to_plane = 1. / 1024.;
|
||||
|
||||
/*!
|
||||
\fn QTransform &QTransform::rotate(qreal angle, Qt::Axis axis)
|
||||
\since 6.5
|
||||
|
||||
Rotates the coordinate system counterclockwise by the given \a angle
|
||||
about the specified \a axis and returns a reference to the matrix.
|
||||
Rotates the coordinate system counterclockwise by the given angle \a a
|
||||
about the specified \a axis at distance \a distanceToPlane from the
|
||||
screen and returns a reference to the matrix.
|
||||
|
||||
//! [transform-rotate-note]
|
||||
Note that if you apply a QTransform to a point defined in widget
|
||||
coordinates, the direction of the rotation will be clockwise
|
||||
because the y-axis points downwards.
|
||||
|
||||
The angle is specified in degrees.
|
||||
//! [transform-rotate-note]
|
||||
|
||||
If \a distanceToPlane is zero, it will be ignored. This is suitable
|
||||
for implementing orthographic projections where the z coordinate should
|
||||
be dropped rather than projected.
|
||||
|
||||
\sa setMatrix()
|
||||
*/
|
||||
QTransform & QTransform::rotate(qreal a, Qt::Axis axis)
|
||||
QTransform & QTransform::rotate(qreal a, Qt::Axis axis, qreal distanceToPlane)
|
||||
{
|
||||
if (a == 0)
|
||||
return *this;
|
||||
#ifndef QT_NO_DEBUG
|
||||
if (qIsNaN(a)) {
|
||||
if (qIsNaN(a) || qIsNaN(distanceToPlane)) {
|
||||
nanWarning("rotate");
|
||||
return *this;
|
||||
}
|
||||
@ -617,13 +622,16 @@ QTransform & QTransform::rotate(qreal a, Qt::Axis axis)
|
||||
if (m_dirty < TxRotate)
|
||||
m_dirty = TxRotate;
|
||||
} else {
|
||||
if (!qIsNull(distanceToPlane))
|
||||
sina /= distanceToPlane;
|
||||
|
||||
QTransform result;
|
||||
if (axis == Qt::YAxis) {
|
||||
result.m_matrix[0][0] = cosa;
|
||||
result.m_matrix[0][2] = -sina * inv_dist_to_plane;
|
||||
result.m_matrix[0][2] = -sina;
|
||||
} else {
|
||||
result.m_matrix[1][1] = cosa;
|
||||
result.m_matrix[1][2] = -sina * inv_dist_to_plane;
|
||||
result.m_matrix[1][2] = -sina;
|
||||
}
|
||||
result.m_type = TxProject;
|
||||
*this = result * *this;
|
||||
@ -632,24 +640,49 @@ QTransform & QTransform::rotate(qreal a, Qt::Axis axis)
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
|
||||
/*!
|
||||
\fn QTransform & QTransform::rotateRadians(qreal angle, Qt::Axis axis)
|
||||
\overload
|
||||
|
||||
Rotates the coordinate system counterclockwise by the given \a angle
|
||||
about the specified \a axis and returns a reference to the matrix.
|
||||
Rotates the coordinate system counterclockwise by the given angle \a a
|
||||
about the specified \a axis at distance 1024.0 from the screen and
|
||||
returns a reference to the matrix.
|
||||
|
||||
\include qtransform.cpp transform-rotate-note
|
||||
|
||||
\sa setMatrix
|
||||
*/
|
||||
QTransform &QTransform::rotate(qreal a, Qt::Axis axis)
|
||||
{
|
||||
return rotate(a, axis, 1024.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\since 6.5
|
||||
|
||||
Rotates the coordinate system counterclockwise by the given angle \a a
|
||||
about the specified \a axis at distance \a distanceToPlane from the
|
||||
screen and returns a reference to the matrix.
|
||||
|
||||
//! [transform-rotate-radians-note]
|
||||
Note that if you apply a QTransform to a point defined in widget
|
||||
coordinates, the direction of the rotation will be clockwise
|
||||
because the y-axis points downwards.
|
||||
|
||||
The angle is specified in radians.
|
||||
//! [transform-rotate-radians-note]
|
||||
|
||||
If \a distanceToPlane is zero, it will be ignored. This is suitable
|
||||
for implementing orthographic projections where the z coordinate should
|
||||
be dropped rather than projected.
|
||||
|
||||
\sa setMatrix()
|
||||
*/
|
||||
QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis)
|
||||
QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis, qreal distanceToPlane)
|
||||
{
|
||||
#ifndef QT_NO_DEBUG
|
||||
if (qIsNaN(a)) {
|
||||
if (qIsNaN(a) || qIsNaN(distanceToPlane)) {
|
||||
nanWarning("rotateRadians");
|
||||
return *this;
|
||||
}
|
||||
@ -700,13 +733,16 @@ QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis)
|
||||
if (m_dirty < TxRotate)
|
||||
m_dirty = TxRotate;
|
||||
} else {
|
||||
if (!qIsNull(distanceToPlane))
|
||||
sina /= distanceToPlane;
|
||||
|
||||
QTransform result;
|
||||
if (axis == Qt::YAxis) {
|
||||
result.m_matrix[0][0] = cosa;
|
||||
result.m_matrix[0][2] = -sina * inv_dist_to_plane;
|
||||
result.m_matrix[0][2] = -sina;
|
||||
} else {
|
||||
result.m_matrix[1][1] = cosa;
|
||||
result.m_matrix[1][2] = -sina * inv_dist_to_plane;
|
||||
result.m_matrix[1][2] = -sina;
|
||||
}
|
||||
result.m_type = TxProject;
|
||||
*this = result * *this;
|
||||
@ -714,6 +750,24 @@ QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis)
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
|
||||
/*!
|
||||
\overload
|
||||
|
||||
Rotates the coordinate system counterclockwise by the given angle \a a
|
||||
about the specified \a axis at distance 1024.0 from the screen and
|
||||
returns a reference to the matrix.
|
||||
|
||||
\include qtransform.cpp transform-rotate-radians-note
|
||||
|
||||
\sa setMatrix()
|
||||
*/
|
||||
QTransform &QTransform::rotateRadians(qreal a, Qt::Axis axis)
|
||||
{
|
||||
return rotateRadians(a, axis, 1024.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\fn bool QTransform::operator==(const QTransform &matrix) const
|
||||
Returns \c true if this matrix is equal to the given \a matrix,
|
||||
|
@ -84,8 +84,17 @@ public:
|
||||
QTransform &translate(qreal dx, qreal dy);
|
||||
QTransform &scale(qreal sx, qreal sy);
|
||||
QTransform &shear(qreal sh, qreal sv);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
|
||||
QTransform &rotate(qreal a, Qt::Axis axis, qreal distanceToPlane);
|
||||
// ### Qt7: Remove
|
||||
QTransform &rotate(qreal a, Qt::Axis axis = Qt::ZAxis);
|
||||
QTransform &rotateRadians(qreal a, Qt::Axis axis, qreal distanceToPlane);
|
||||
// ### Qt7: Remove
|
||||
QTransform &rotateRadians(qreal a, Qt::Axis axis = Qt::ZAxis);
|
||||
#else
|
||||
QTransform &rotate(qreal a, Qt::Axis axis = Qt::ZAxis, qreal distanceToPlane = 1024.0f);
|
||||
QTransform &rotateRadians(qreal a, Qt::Axis axis = Qt::ZAxis, qreal distanceToPlane = 1024.0f);
|
||||
#endif
|
||||
|
||||
static bool squareToQuad(const QPolygonF &square, QTransform &result);
|
||||
static bool quadToSquare(const QPolygonF &quad, QTransform &result);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <QTest>
|
||||
#include <QBuffer>
|
||||
#include <QMatrix4x4>
|
||||
|
||||
#include <qimage.h>
|
||||
#include <qimagereader.h>
|
||||
@ -67,6 +68,7 @@ private slots:
|
||||
|
||||
void rotate_data();
|
||||
void rotate();
|
||||
void rotateBigImage();
|
||||
|
||||
void copy();
|
||||
|
||||
@ -1212,6 +1214,23 @@ void tst_QImage::rotate()
|
||||
QCOMPARE(original, dest);
|
||||
}
|
||||
|
||||
void tst_QImage::rotateBigImage()
|
||||
{
|
||||
// QTBUG-105088
|
||||
QImage big_image(3840, 2160, QImage::Format_ARGB32_Premultiplied);
|
||||
QTransform t;
|
||||
t.translate(big_image.width() / 2.0, big_image.height() / 2.0);
|
||||
t.rotate(-89, Qt::YAxis, big_image.width());
|
||||
t.translate(-big_image.width() / 2.0, -big_image.height() / 2.0);
|
||||
QVERIFY(!big_image.transformed(t).isNull());
|
||||
|
||||
QMatrix4x4 m;
|
||||
m.translate(big_image.width() / 2.0, big_image.height() / 2.0);
|
||||
m.projectedRotate(89, 0, 1, 0, big_image.width());
|
||||
m.translate(-big_image.width() / 2.0, -big_image.height() / 2.0);
|
||||
QVERIFY(!big_image.transformed(m.toTransform()).isNull());
|
||||
}
|
||||
|
||||
void tst_QImage::copy()
|
||||
{
|
||||
// Task 99250
|
||||
|
@ -100,6 +100,7 @@ private slots:
|
||||
|
||||
void rotate4x4_data();
|
||||
void rotate4x4();
|
||||
void projectedRotate();
|
||||
|
||||
void normalMatrix_data();
|
||||
void normalMatrix();
|
||||
@ -2383,6 +2384,22 @@ void tst_QMatrixNxN::rotate4x4()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QMatrixNxN::projectedRotate()
|
||||
{
|
||||
QMatrix4x4 m1, m2;
|
||||
const QPointF origin(1000, 1000);
|
||||
|
||||
m1.translate(origin.x(), origin.y());
|
||||
m1.projectedRotate(60, 0, 1, 0, 0);
|
||||
m1.translate(-origin.x(), -origin.y());
|
||||
|
||||
m2.translate(origin.x(), origin.y());
|
||||
m2.rotate(60, 0, 1, 0);
|
||||
m2.translate(-origin.x(), -origin.y());
|
||||
|
||||
QCOMPARE(m1.toTransform(), m2.toTransform());
|
||||
}
|
||||
|
||||
static bool isSame(const QMatrix3x3& m1, const Matrix3& m2)
|
||||
{
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user