Как повернуть QPixmap вокруг центра, не обрезая части изображения

Допустим, у меня есть QPixmap размером (20 x 100). Как я могу создать копию этого QPixmap, которая повернута на определенную величину, а также имеет новые размеры для выделения новых размеров повернутого растрового изображения?

Я нашел несколько примеров того, как вращать с помощью QPainter и QTransform, но ни один из них, похоже, не обеспечивает надлежащего способа предотвращения обрезки QPixmap.

Лучший пример, который я нашел до сих пор:

// 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();

Это вращает QPixmap и помещает его в новый QPixmap тех же размеров. Однако я не понимаю, как изменить это для работы с новыми размерами.

Я даже пытался просто изменить начальный размер нового растрового изображения, но это просто приводит к тому, что изображение смещается от центра (и по какой-то причине все еще обрезается?)

Любая поддержка будет оценена!


person Griffort    schedule 25.03.2018    source источник


Ответы (1)


Одним из способов сделать это было бы вычислить минимальный ограничивающий прямоугольник для вашего повернутого изображения и создать новую растровую карту с этими размерами, на которую вы можете отобразить свое повернутое изображение, которое теперь гарантированно подходит. Для этого вы можете взять каждую угловую точку прямоугольника изображения и повернуть их вокруг центра. Полученные точки можно затем использовать для вычисления минимального ограничивающего прямоугольника, рассматривая каждую точку и находя как минимальное, так и максимальное значения x и y.

Например, в следующем гипотетическом примере у нас есть прямоугольник 100x100. Если мы используем простой алгоритм для поворота каждой угловой точки прямоугольника вокруг центра на наш угол (в данном случае 45 градусов), мы получим четыре новые угловые точки (50, -20), (-20, 50), (120 , 120) и (50, 120). Из этих точек мы можем видеть, что минимальное значение x равно -20, минимальное значение y равно -20, максимальное значение x равно 120 и максимальное значение y равно 120, поэтому минимальный ограничивающий прямоугольник может быть описан как topLeft:(-20 , -20) и нижний правый: (120, 120).

Пример поворота прямоугольника

Чтобы помочь вам в этом, вот функция, взятая из другого поста stackoverflow, для поворота точки вокруг другой точки:

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 );
}

И вот функция, которую я написал, которая использует ее для вычисления минимального ограничивающего прямоугольника для некоторого прямоугольника, повернутого на некоторый угол...

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 ) );
}

Итак, теперь у нас есть минимальный ограничивающий прямоугольник для нашего повернутого изображения, мы можем создать новое растровое изображение с его шириной и высотой и визуализировать наше повернутое изображение в его центре. Это сложно из-за задействованного преобразования, которое делает его немного более запутанным в отношении того, какими могут быть ваши исходные и целевые прямоугольники. На самом деле это не так сложно, как может показаться. Вы выполняете свой перевод / вращение, чтобы повернуть устройство рисования вокруг центра, затем вы можете просто визуализировать исходное изображение на целевом изображении точно так же, как если бы вы визуализировали источник в центре назначения.

Например:

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();

И вуаля, красиво повернутое изображение без обрезанных углов.

person aatwo    schedule 25.03.2018
comment
Блин, это превосходно! Большое спасибо! - person Griffort; 26.03.2018
comment
Одна ошибка в примере кода: rotatedPixmap должен быть rotatedPixmap - person Yonatan Tidhar; 11.07.2019
comment
Они идентичны, поэтому я не уверен, что следую. Я не могу найти никаких ошибок, глядя на это. - person aatwo; 14.07.2019