Reputation: 1306
Let's say I have a QPixmap that has the dimensions of (20 x 100). How can I create a copy of this QPixmap that's rotated a specific amount and also has new dimensions to allocate the new dimensions of the rotated pixmap?
I've found multiple examples on how to rotate using QPainter and QTransform, but none seem to provide a proper manner to keep the QPixmap from cutting off.
The best example I've found so far is:
// original = Original QPixmap
QSize size = original.size();
QPixmap newPixmap(size);
newPixmap.fill(QColor::fromRgb(0, 0, 0, 0));
QPainter p(&newPixmap);
p.translate(size.height() / 2, size.height() / 2);
p.rotate(35); // Any rotation, for this example 35 degrees
p.translate(size.height() / -2, size.height() / -2);
p.drawPixmap(0, 0, original);
p.end();
This rotates a QPixmap, and places it on a new QPixmap of the same dimensions. However, I am at a loss on how to modify this to work with new dimensions.
I've even tried simply modifying the initial size of the new pixmap, but that just causes the image to be off center (and still cut off for some reason?)
Any support would be appreciated!
Upvotes: 2
Views: 4123
Reputation: 1008
One way to do this would be to calculate the minimum bounding rect for your rotated image and to create a new pixmap with these dimensions onto which you can render your rotated image which is now guarenteed to fit. To do this you could take each corner point of your image rectangle and rotate them around the center. The resulting points can then be used to calculate your minimum bounding rectangle by looking at each point and finding both the minimum and maximum x and y values.
For example in the following hypothetical example we have a 100x100 rectangle. If we use a simple algorithm to rotate each corner point of the rectangle around the center by our angle (in this case 45 degrees) we get the four new corner points (50, -20), (-20, 50), (120, 120) and (50, 120). From these points we can see the minimum x value is -20, the minimum y value is -20, the maximum x value is 120 and the maximum y value is 120, so the minimum bounding rect can be described by topLeft:(-20, -20) and bottomRight:(120, 120).
To help you with this here is a function taken from another stackoverflow post for rotating a point around another point:
QPointF getRotatedPoint( QPointF p, QPointF center, qreal angleRads )
{
qreal x = p.x();
qreal y = p.y();
float s = qSin( angleRads );
float c = qCos( angleRads );
// translate point back to origin:
x -= center.x();
y -= center.y();
// rotate point
float xnew = x * c - y * s;
float ynew = x * s + y * c;
// translate point back:
x = xnew + center.x();
y = ynew + center.y();
return QPointF( x, y );
}
And here is a function I wrote that uses it to calculate the minimum bounding rect for some rectangle rotated by some angle...
QRectF getMinimumBoundingRect( QRect r, qreal angleRads )
{
QPointF topLeft = getRotatedPoint( r.topLeft(), r.center(), angleRads );
QPointF bottomRight = getRotatedPoint( r.bottomRight(), r.center(), angleRads );
QPointF topRight = getRotatedPoint( r.topRight(), r.center(), angleRads );
QPointF bottomLeft = getRotatedPoint( r.bottomLeft(), r.center(), angleRads );
// getMin and getMax just return the min / max of their arguments
qreal minX = getMin( topLeft.x(), bottomRight.x(), topRight.x(), bottomLeft.x() );
qreal minY = getMin( topLeft.y(), bottomRight.y(), topRight.y(), bottomLeft.y() );
qreal maxX = getMax( topLeft.x(), bottomRight.x(), topRight.x(), bottomLeft.x() );
qreal maxY = getMax( topLeft.y(), bottomRight.y(), topRight.y(), bottomLeft.y() );
return QRectF( QPointF( minX, minY ), QPointF( maxX, maxY ) );
}
So now we have the minimum bounding rectangle for our rotated image we can create a new pixmap with its width and height and render our rotated image to it at the center. This is tricky because of the transformation involved which makes it a bit more confusing as to what your source and target rects might be. It's actually not as hard as it might seem. You perform your translation / rotation to rotate the paint device around the center, then you can simply render your source image onto your destination image exactly as you would if you were rendering the source to the center of the destination.
For example:
QPixmap originalPixmap; // Load this from somewhere
QRectF minimumBoundingRect = getMinimumBoundingRect( originalPixmap.rect(), angleRads);
QPixmap rotatedPixmap( minimumBoundingRect.width(), minimumBoundingRect.height() );
QPainter p( &rotatedPixmap );
p.save();
// Rotate the rotated pixmap paint device around the center...
p.translate( 0.5 * rotatedPixmap.width(), 0.5 * rotatedPixmap.height() );
p.rotate( angleDegrees );
p.translate( -0.5 * rotatedPixmap.width(), -0.5 * rotatedPixmap.height() );
// The render rectangle is simply the originalPixmap rectangle as it would be if placed at the center of the rotatedPixmap rectangle...
QRectF renderRect( 0.5 * rotatedRect.width() - 0.5 * originalPixmap.width(),
0.5 * rotatedRect.height() - 0.5 * originalPixmap.height(),
originalPixmap.width(),
originalPixmap.height() );
p.drawPixmap( renderRect, originalPixmap, originalPixmap.rect() );
p.restore();
And voila, a nicely rotated image with no corners chopped off.
Upvotes: 8