As far as I understand, the main problem is to find the coordinates of the image angle in the new rotated coordinate system. This can be solved as follows:
procedure DrawRotatedBitmap(const Canvas : TCanvas; const Bitmap : TBitmap; const PointA, PointB : TPointF; const Offset : TPointF; const Scale : Single); var OldMatrix, TranslationAlongLineMatrix, RotationMatrix, TranslationMatrix, ScaleMatrix, FinalMatrix: TMatrix; W, H : Single; SrcRect, DestRect: TRectF; Corner: TPointF; LineLength : Single; LineAngleDeg : Integer; begin OldMatrix := Canvas.Matrix; // Original, to restore try {$ifdef DRAW_HELPERS} Canvas.Fill.Color := TAlphaColorRec.Black; Canvas.DrawLine(PointA, PointB, 0.5); {$endif} W := PointB.X - PointA.X; H := PointA.Y - PointB.Y; LineLength := abs(PointA.Distance(PointB)); // Looking for the middle of the task line // and the coordinates of the image left upper angle // solving the proportion width/linelength=xo/0.5requireddimensions Corner := TPointF.Create((PointB.X + PointA.X) / 2, (PointA.Y + PointB.Y) / 2);// Middle {$ifdef DRAW_HELPERS} Canvas.Stroke.Color := TAlphaColorRec.Red; Canvas.DrawEllipse(TRectF.Create(Corner,2,2),1); {$endif} Corner.X := Corner.X - Bitmap.Width / 2 * W / LineLength; Corner.Y := Corner.Y + Bitmap.Width / 2 * H / LineLength; {$ifdef DRAW_HELPERS} Canvas.Stroke.Color := TAlphaColorRec.Green; Canvas.DrawEllipse(TRectF.Create(Corner,2,2),1); {$endif} // Account for scale (if the FMX control is scaled / zoomed); translation // (the control may not be located at (0, 0) in its parent form, so its canvas // is offset) and rotation ScaleMatrix := TMatrix.CreateScaling(Scale, Scale); TranslationMatrix := TMatrix.CreateTranslation(Offset.X, Offset.Y); RotationMatrix := TMatrix.CreateRotation(-ArcTan2(H, W)); TranslationAlongLineMatrix := TMatrix.CreateTranslation(Corner.X, Corner.Y); FinalMatrix := ((RotationMatrix * ScaleMatrix) * TranslationMatrix) * TranslationAlongLineMatrix; // If in the top left or top right quadrants, the image will appear // upside down. So, rotate the image 180 degrees // This is useful when the image contains text, ie is an annotation or similar, // or needs to always appear "under" the line LineAngleDeg := Round(RadToDeg(-Arctan2(H, W))); case LineAngleDeg of -180..-90, 90..180 : FinalMatrix := TMatrix.CreateRotation(DegToRad(180)) * TMatrix.CreateTranslation(Bitmap.Width, 0) * FinalMatrix; end; Canvas.SetMatrix(FinalMatrix); // And finally draw the bitmap DestRect := TRectF.Create(PointF(0, 0), Bitmap.Width, Bitmap.Height); SrcRect := TRectF.Create(0, 0, Bitmap.Width, Bitmap.Height); {$ifdef DRAW_HELPERS} Canvas.DrawBitmap(Bitmap, SrcRect, DestRect, 0.5); {$else} Canvas.DrawBitmap(Bitmap, SrcRect, DestRect, 1); {$endif} finally // Restore the original matrix Canvas.SetMatrix(OldMatrix); end; end;
There is an ifdef — these are pictures of lines and points that can help you — they draw a line and some useful points (the linear center and upper left corner of the image) that are useful for debugging.
Edit DavidM: In addition, there are also translation and scaling matrices. The lightbox relies on the canvas of the parent form (ultimately), but may not be located on (0, 0), so you need to consider the position of the offset canvas destination. In addition, the control can be scaled, so it also needs to be embedded in the final rotated matrix.
This code is heavily edited and works regardless of the orientation / quadrant that is in . That is, it should work when the line is completely horizontal or vertical, as well as in quadrants, except for the lower right.
One interesting tweak is recognizing that the bitmap in the example contains text. When the line is in the upper left or upper right quadrants, that is, rising up, and then either to the left or to the right of its beginning, the raster image is displayed upside down for the human eye. The twist recognizes this and rotates the bitmap so that the "top" of the bitmap is always facing the line, and the "bottom" of the bitmap is usually pointing down, making the image look right. You can remove this setting if you do not need an image that represents something recognizable (for example, a symbol, text, label, etc.).
Artwork
With different angles and scaling.
