3

I am facing a problem which is probably easy to solve but I do not get it right: I have two QImages, a fixed-size background image imgBg, and a second QImage imgFg which should be transformed using affine transformations and then painted (and clipped) on the background image.

The affine transformation is as follows:

  • Scale image around its origin (uniform scaling)
  • Rotate image around its origin
  • Translate the foreground image to a certain reference position on the background image so that the foreground's image center is on the reference position.

Here is what I have done so far:

// Setup transform
QTransform trans;
trans.translate(-imgFg.width()/2, -imgFg.height()/2); // move center of image to origin
trans.scale(scaleFac, scaleFac);
trans.rotate(angleDegrees);
trans.translate(imgFg.width()/2, imgFg.height()/2);

// Transform foreground image
imgFg = imgFg.transformed(trans);

// Paint on background image
QPointF referencePos(imgBg.width()/3, imgBg.height()/2); // some reference position
QPainter painter(&imgBg);
painter.drawImage(referencePos - QPointF(imgFg.width()/2, imgFg.height()/2), imgFg);

As long as I only change the reference position everything works fine. When using scaling and/or rotation the image is also correctly scaled and rotated but it is not placed on the correct position (the foreground's image center does not match the reference point). Is there something wrong with the transformation pipeline? Do I have made a mistake?

leemes
  • 44,967
  • 21
  • 135
  • 183
Hyndrix
  • 4,282
  • 7
  • 41
  • 82

1 Answers1

3

You need to invert the order of statements you perform on your QTransform. The order has to be the inverse of intuitive thinking. This has to do with matrix multiplication of transformation matrices in linear algebra. (Change of basis of the transformation matrix...) The problem is that the transformations apply on the source coordinate system, which is kind of unintuitive.

So remember: Whenever you want to apply multiple transformations, do them in the inverse order of what you would do with the image.

For an example see the example code in the documentation of QTransform:

Scale text to the half width, then rotate it clockwise by 45 degrees, then move its origin to (50, 50):

enter image description here

QTransform transform;
transform.translate(50, 50);
transform.rotate(45);
transform.scale(0.5, 1.0);

Another problem is that you should translate the image back to its origin by taking the scaling factor into account (thanks to Mat for pointing this out in the question comment).

Applied to your code snippet (read from bottom to top for intuitive order of operations):

// Setup transform
QTransform trans;
trans.translate(imgFg.width()*scaleFac/2, imgFg.height()*scaleFac/2);
trans.rotate(angleDegrees);
trans.scale(scaleFac, scaleFac);
trans.translate(-imgFg.width()/2, -imgFg.height()/2);

Last but not least, you want to have the center of your image as the new origin (reference point), so you should not translate back (now the first operation; in your code the last one). Then just paint the image at the reference point rather than adding another size/2. Also, you should not apply the transform to the image and then paint this image; at least it is no good style. Just apply the transform to the painter and un-apply it afterwards.

QTransform trans;
trans.scale(scaleFac, scaleFac);
trans.rotate(angleDegrees);
trans.translate(imgFg.width()/2,imgFg.height()/2);

// Save old transform (only needed if you use other transformations)
const QTransform &oldTrans = painter.transform();
// Apply new transform
painter.setTransform(trans);
// Paint image at reference point
painter.drawImage(referencePoint, imgFg);
// Restore transform
painter.setTransform(oldTrans);
Community
  • 1
  • 1
leemes
  • 44,967
  • 21
  • 135
  • 183
  • Thanks for the extensive explanation! Scaling the final translation by the scaling factor solved the problem. – Hyndrix May 27 '12 at 13:17