0
label=new QLabel(this);
label->setGeometry(this->width()/2,this->height()/2,label->width(),label->height());
QPixmap myPixmapForNow;
myPixmapForNow.load("C://Users//abc//Documents//QpixMap//hub_needle.png");
label->setMinimumSize(QSize(myPixmapForNow.width(),myPixmapForNow.width()));
label->setAlignment(Qt::AlignCenter);
QPixmap rotated(label->width(),label->width());
QPainter p(&rotated);
p.setRenderHint(QPainter::Antialiasing);
p.setRenderHint(QPainter::SmoothPixmapTransform);
p.setRenderHint(QPainter::HighQualityAntialiasing);

p.translate(myPixmapForNow.size().width() / 2,
            (myPixmapForNow.size().height() / 2));
qDebug()<<"before rotation width:"<<rotated.size().width()<<"height:"<<rotated.size().width();
p.rotate(arg1);
p.translate(-myPixmapForNow.size().width() / 2,
            -(myPixmapForNow.size().height() / 2));
qDebug()<<"after rotation height:"<<-rotated.size().width()<<"height:"<<-rotated.size().height();[![enter image description here][1]][1]
p.drawPixmap(QRect(0,0,myPixmapForNow.width(),myPixmapForNow.height()), myPixmapForNow);
p.end();
label->setPixmap(rotated);

After rotation

enter image description here

before rotation

enter image description here

eyllanesc
  • 190,383
  • 15
  • 87
  • 142
Raghav Reddy
  • 53
  • 1
  • 4

1 Answers1

1

I must admit the OP could have explained the issue a bit more in detail. Unfortunately, OP didn't react on comments.

However, out of curiosity, I tried to puzzle this out in a little demo. (I really like to write little Qt demos, especially with image manipulation and cat pictures.)


My first assumption was that OP has struggled with the order of transformations.

While translations are commutative (changing order doesn't change result), this is not the case for rotations (and other transformations).

However, after having wrapped OPs code into a MCVE, I convinced myself that the order of transformations matched my expectation – a rotation about the center of image.

Snapshot of testQPainterRotateCenter

So, I focused on the title

how to avoid pixmap cutting issues when we using qpainter while rotation

The reason for the “cutting issue” is simple:
To paint a rotated image (rectangle), the output may require a greater range of pixels then the original.

There are two possibilities to fix this:

  1. enlarge the QPixmap for output
  2. scale the result to match the original size of QPixmap.

So, this leaves the task to determine the output size of the rotated image beforehand, to either make the output QPixmap respectively larger or to add the respective scaling.

The bounding rectangle of a rotated rectangle can be calculated with trigonometric functions (sin, cos, etc.) I decided instead (for an IMHO more naïve way) to let Qt do the work for me.

To achieve this, the transformation has to be calculated before creating the QPixmap and QPainter. Hence, the prior

  qPainter.translate(cx, cy);
  qPainter.rotate(ra);
  qPainter.translate(-cx, -cy);

is replaced by:

  QTransform xform;
  xform.translate(cx, cy);
  xform.rotate(ra);
  xform.translate(-cx, -cy);

which can be later applied as is:

  qPainter.setTransform(xform);

I used the fact that all four corners of the rotated rectangle will touch the bounding rectangle. So, the bounding rectangle can be calculated by applying min() and max() to the x and y components of the rotated image corners:

    const QPoint ptTL = xform * QPoint(0, 0);
    const QPoint ptTR = xform * QPoint(w - 1, 0);
    const QPoint ptBL = xform * QPoint(0, h - 1);
    const QPoint ptBR = xform * QPoint(w - 1, h - 1);
    QRect qRectBB(
      QPoint(
        min(ptTL.x(), ptTR.x(), ptBL.x(), ptBR.x()),
        min(ptTL.y(), ptTR.y(), ptBL.y(), ptBR.y())),
      QPoint(
        max(ptTL.x(), ptTR.x(), ptBL.x(), ptBR.x()),
        max(ptTL.y(), ptTR.y(), ptBL.y(), ptBR.y())));

Afterwards, the output may be adjusted using the origin and size of qRectBB.

The whole demo application testQPainterRotateCenter.cc:

#include <algorithm>

// Qt header:
#include <QtWidgets>

int min(int x0, int x1, int x2, int x3)
{
  return std::min(std::min(x0, x1), std::min(x2, x3));
}

int max(int x0, int x1, int x2, int x3)
{
  return std::max(std::max(x0, x1), std::max(x2, x3));
}

QPixmap rotate(
  const QPixmap &qPixMapOrig, int cx, int cy, int ra,
  bool fitIn, bool keepSize)
{
  int w = qPixMapOrig.width(), h = qPixMapOrig.height();
  QTransform xform;
  xform.translate(cx, cy);
  xform.rotate(ra);
  xform.translate(-cx, -cy);
  if (fitIn) {
    // find bounding rect
    const QPoint ptTL = xform * QPoint(0, 0);
    const QPoint ptTR = xform * QPoint(w - 1, 0);
    const QPoint ptBL = xform * QPoint(0, h - 1);
    const QPoint ptBR = xform * QPoint(w - 1, h - 1);
    QRect qRectBB(
      QPoint(
        min(ptTL.x(), ptTR.x(), ptBL.x(), ptBR.x()),
        min(ptTL.y(), ptTR.y(), ptBL.y(), ptBR.y())),
      QPoint(
        max(ptTL.x(), ptTR.x(), ptBL.x(), ptBR.x()),
        max(ptTL.y(), ptTR.y(), ptBL.y(), ptBR.y())));
    qDebug() << "Bounding box:" << qRectBB;
    // translate top left corner to (0, 0)
    xform *= QTransform().translate(-qRectBB.left(), -qRectBB.top());
    if (keepSize) {
      // center align scaled image
      xform *= w > h
        ? QTransform().translate((w - h) / 2, 0)
        : QTransform().translate(0, (h - w) / 2);
      // add scaling to transform
      const qreal sx = qreal(w) / qRectBB.width();
      const qreal sy = qreal(h) / qRectBB.height();
      const qreal s = std::min(sx, sy);
      xform *= QTransform().scale(s, s);
    } else {
      // adjust w and h
      w = qRectBB.width(); h = qRectBB.height();
    }
  }
  QPixmap qPixMap(w, h);
  qPixMap.fill(Qt::gray);
  { QPainter qPainter(&qPixMap);
    qPainter.setRenderHint(QPainter::Antialiasing);
    qPainter.setRenderHint(QPainter::SmoothPixmapTransform);
    qPainter.setRenderHint(QPainter::HighQualityAntialiasing);
    qPainter.setTransform(xform);
    qPainter.drawPixmap(0, 0, qPixMapOrig.width(), qPixMapOrig.height(), qPixMapOrig);
  } // end of scope -> finalize QPainter
  return qPixMap;
}

// main application
int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  // setup data
  const QString file = QString::fromUtf8("cats.jpg");
  QPixmap qPixMapOrig;
  qPixMapOrig.load(file);
  int cx = qPixMapOrig.width() / 2, cy = qPixMapOrig.height() / 2;
  int ra = 0;
  // setup GUI
  QWidget qWin;
  qWin.setWindowTitle(
    file % QString(" (")
      % QString::number(qPixMapOrig.width())
      % " x " % QString::number(qPixMapOrig.height())
      % ") - testQPainterRotateCenter");
  QVBoxLayout qVBox;
  QHBoxLayout qHBox1;
  QLabel qLblCX(QString::fromUtf8("center x:"));
  qHBox1.addWidget(&qLblCX);
  QLineEdit qEditCX;
  qEditCX.setText(QString::number(cx));
  qHBox1.addWidget(&qEditCX, 1);
  QLabel qLblCY(QString::fromUtf8("center y:"));
  qHBox1.addWidget(&qLblCY);
  QLineEdit qEditCY;
  qEditCY.setText(QString::number(cy));
  qHBox1.addWidget(&qEditCY, 1);
  QLabel qLblRA(QString::fromUtf8("rotation angle:"));
  qHBox1.addWidget(&qLblRA);
  QSpinBox qEditRA;
  qEditRA.setValue(ra);
  qHBox1.addWidget(&qEditRA, 1);
  qVBox.addLayout(&qHBox1);
  QHBoxLayout qHBox2;
  QCheckBox qTglFitIn(QString::fromUtf8("Zoom to Fit"));
  qTglFitIn.setChecked(false);
  qHBox2.addWidget(&qTglFitIn);
  QCheckBox qTglKeepSize(QString::fromUtf8("Keep Size"));
  qTglKeepSize.setChecked(false);
  qHBox2.addWidget(&qTglKeepSize);
  qVBox.addLayout(&qHBox2);
  QLabel qLblImg;
  qLblImg.setPixmap(qPixMapOrig);
  qLblImg.setAlignment(Qt::AlignCenter);
  qVBox.addWidget(&qLblImg, 1);
  qWin.setLayout(&qVBox);
  qWin.show();
  // helper to update pixmap
  auto update = [&]() {
    cx = qEditCX.text().toInt();
    cy = qEditCY.text().toInt();
    ra = qEditRA.value();
    const bool fitIn = qTglFitIn.isChecked();
    const bool keepSize = qTglKeepSize.isChecked();
    QPixmap qPixMap = rotate(qPixMapOrig, cx, cy, ra, fitIn, keepSize);
    qLblImg.setPixmap(qPixMap);
  };
  // install signal handlers
  QObject::connect(&qEditCX, &QLineEdit::textChanged,
    [&](const QString&) { update(); });
  QObject::connect(&qEditCY, &QLineEdit::textChanged,
    [&](const QString&) { update(); });
  QObject::connect(&qEditRA, QOverload<int>::of(&QSpinBox::valueChanged),
    [&](int) { update(); });
  QObject::connect(&qTglFitIn, &QCheckBox::toggled,
    [&](bool) { update(); });
  QObject::connect(&qTglKeepSize, &QCheckBox::toggled,
    [&](bool) { update(); });
  // runtime loop
  return app.exec();
}

The Qt project file testQPainterRotateCenter.pro:

SOURCES = testQPainterRotateCenter.cc

QT += widgets

Output:

The rotated image without zoom to fit:

Snapshot of testQPainterRotateCenter (angle: 30°)

Zoomed to fit:

Snapshot of testQPainterRotateCenter (angle: 30°, zoom to fit)

Zoomed to fit original size:

Snapshot of testQPainterRotateCenter (angle: 30°, zoom to fit, keep size)

Notes:

While fiddling originally with a square image of 300×300 pixels, I became aware that rotating a non-square rectangle may result in a bounding box with a different aspect-ratio than the original. Hence, an additional translation might be desirable to align the scaled output in the original bounding box again. I switched to a non-square sample image of 300×200 pixels to illustrate this.

With the fit in calculations, the translations before/after rotation are actually obsolete. The result will be translated in any case to the intended position.

Instead of Qt::gray, the “background color” (i.e. the color the QPixmap is filled with initially) might be set complete transparently. I decided to stick to the Qt::gray for illustration.

Scheff's Cat
  • 16,517
  • 5
  • 25
  • 45