How do you draw an image to a rotated rectangle in FireMonkey?

  

In FireMonkey, it is simple to draw a bitmap to a source rectangle:

Canvas.DrawBitmap(FBitmap, ImageSrcRect, ImageDstRect, 1);

And I’m doing this on a TPaintBox’s canvas. I would instead like to draw the bitmap rotated (and scaled, since the destination size may not be the same as the source size.)

Specifically:

I have two points
The image should be placed below the centerpoint between these two points
The image should rotate to follow the angle between the two points

as in this image:

One the left is what I can currently do; on the right is what I would like to do.

What’s the best way to do it?

What I’ve tried

In order to keep existing code simple (eg, drawing to a destination rectangle, thereby scaling the result) I’ve been trying to add a rotation matrix to the canvas’s matrix before calling the existing DrawBitmap code. For example,

OldMatrix := Canvas.Matrix; // Original, to restore

W := PointB.X – PointA.X;
H := PointA.Y – PointB.Y;
RotationMatrix := TMatrix.CreateRotation(-ArcTan2(H, W));
Canvas.SetMatrix(OldMatrix * RotationMatrix);

Canvas.DrawBitmap(FImage, ImageSrcRect, ImageDstRect, 1);

Canvas.SetMatrix(OldMatrix);

and a couple of variations multiplying with the existing matrix, creating an entirely new matrix with both translation and rotation, etc. All these partially work: the rotation angle is correct, but I’m having a lot of trouble getting the position to stay consistent – for example, to rotate around the center point (and this isn’t even getting to rotating the top of the bitmap around the point, not rotating around the center.) I’ve found that the rotated image is offset fine in the bottom right quadrant, but in the other three is offset / translated incorrectly, such as far too far left, or clipped to the leftmost or topmost X or Y position of the two points. I do not know why this is, and it’s at this point I’m asking SO for help.

Details

Delphi 10 Seattle
FireMonkey (on Windows)
Target is the canvas of a TPaintBox, arbitrarily placed. The paint box may itself be on a TScaledLayout.
The goal is to draw a bitmap to a rotated target rectangle on the paintbox.

Comments are closed.