Recent

Author Topic: Rotating an Image  (Read 2602 times)

J-G

  • Hero Member
  • *****
  • Posts: 953
Rotating an Image
« on: June 01, 2022, 10:56:11 pm »
I've used Bitmap images in the past and made them rotate through 360° (ie. a clock), but it is a while ago and I'm 'rusty'  :-[  -  I do have the old code of course but I'm still confused - even reading at comments made at the the time.

I can see that I used BGRABitmaps and I've just added BGRAControls etc. into my Laz 2.2.0 installation. The BGRA Controls, Control Buttons & Themes all apear in the controls ribon but I can't find BGRABitmap as an option. 

I'm obviously missing something very simple but having spent the whole of this afternoon pouring over old code and BGRA controls I've succumbed and lay my stupidity open to the wise  :)

I only need to rotate this particular image a maximum of 26½° but it is totally variable (not discrete single degrees)  and I have another that - if I do move it at all - will be a specific 15½° either Clock or Anti-clock wise.

FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Rotating an Image
« Reply #1 on: June 01, 2022, 11:27:08 pm »
Hi!

Very simple.

Change the filename of tmp := TBGRAbitmap.create to your needs in the following code.

Put an TImage on your form (Here: Image2)
 
Code:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button3Click(Sender: TObject);
  2. var  tmp, dest : TBGRABitmap;
  3.       angle: Integer;
  4. begin
  5. tmp := TBGRAbitmap.create ('ZEP_Fire.png');
  6.  
  7. for angle := 1 to 360 do
  8.   begin
  9.   dest := TBGRAbitmap.create;
  10.   BGRAReplace(dest, tmp.FilterRotate(PointF(tmp.width / 2, tmp.Height / 2), angle,true));
  11.   dest.Draw(Image2.Canvas,0,0);
  12.   application.ProcessMessages;
  13.   dest.free;
  14.   end;
  15. tmp.free;
  16. end;          

If you got a highend machine insert a sleep (50) in the loop.

Done.

Winni

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: Rotating an Image
« Reply #2 on: June 02, 2022, 12:59:08 am »
Thanks for your input Winnie but I'm not sure that you have understood my question   -  or I don't understand your suggestion  ;D

I can see the logic in your sample code which makes an image rotate through 360°, 1° at a time upon the activation of a mouse-click.  What I wish to do is rotate an image a specific amount - which may be any angle between 0 and 26½°  to 3 decimal places - it could be in Degrees or Radians, that is to be decided. It also needs to be about a centre which is offset from the centre of the image.

From reviewing my old program I know that I need to assign the image to a BGRABitmap but I can't fathom how to add the BGRA.... Units (other than by manually typing the names into the appropriate location) - I'm used to having the units added automatically when I add a control to the form.  I have added 'BGRABitmap' to the Units section manually and the compilation does procede past the assignment in the [Private Declarations] section.

I have placed a second TImage (Image2) on the form (as a test) but compilation fails at 'PointF' which I can't resolve.

FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: Rotating an Image
« Reply #3 on: June 02, 2022, 02:01:05 am »
D'oh!   -   I've now added 'BGRABitmapTypes' to the [Uses] and it compiles !

I had already added the .PNG File to the [Project][Options][Recources] but I now get a message saying that the .PNG File can't be found.

Since I just want to test rotating the image I've commented out the [Loop] and changed the 'Angle' to 'Single'  so here's the modified code :
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   tmp, dest : TBGRABitmap;
  4.   angle     : Single;
  5. begin
  6.   tmp := TBGRAbitmap.create ('Banjo.png');
  7.   Angle := 15.3;
  8.  
  9. //  for angle := 1 to 360 do
  10. //    begin
  11.     dest := TBGRAbitmap.create;
  12.     BGRAReplace(dest, tmp.FilterRotate(PointF(tmp.width / 2, tmp.Height / 2), angle,true));
  13.     dest.Draw(Image2.Canvas,0,0);
  14.     application.ProcessMessages;
  15.     dest.free;
  16. //    end;
  17.   tmp.free;
  18. end;

Am I correct in thinking that I can specify the centre of revolution by changing the 0,0 in the dest.Draw to the X,Y figures (integer of course)?
« Last Edit: June 02, 2022, 02:04:21 am by J-G »
FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

friend

  • Guest
Re: Rotating an Image
« Reply #4 on: June 02, 2022, 05:37:27 am »
Am I correct in thinking that I can specify the centre of revolution by changing the 0,0 in the dest.Draw to the X,Y figures (integer of course)?

No friend, the rotation center is set by changing the point given to FilterRotate. It is set to the center of the image (W/2, H/2) in the example.

I had already added the .PNG File to the [Project][Options][Recources] but I now get a message saying that the .PNG File can't be found.

That constructor expects a file name. With no directory specified it will look at the directory from which the application is run. To load from a project's Resource, you can use the constructor that takes no arguments and then call the LoadFromResource function from TBGRABitmap:

Code: Pascal  [Select][+][-]
  1. tmp.LoadFromResource('Banjo.png');  

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: Rotating an Image
« Reply #5 on: June 02, 2022, 10:56:58 am »
Thanks 'friend'  -  if I'd had my proper thinking head on I should have realized that the 'FilterRotate' would specify the CoR  :-[

After a few attempts I have now massaged the program into a compilable state but the image still doesnt 'rotate'.  :(

I have checked that tmp.width is set to the correct dimension so can confirm that it is 'seeing' Image2 which is assigned to 'Banjo.png'.

I can't fathom what the 0,0 figures really are in dest.Draw but have tried setting them to the top-left figures of Image2 on the form - no rotation with either setting.

To make testing a little more like the eventual reality, I've removed the code from Form.Create and added a TEdit to specify the angle required dynamically - the rotation ought to take place upon 'EditingDone'.  Here's the new proc. -
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Angle_ValEditingDone(Sender: TObject);
  2. var
  3.   tmp, dest : TBGRABitmap;
  4.   angle     : Single;
  5. begin
  6.   tmp := TBGRAbitmap.create ('Banjo.png');
  7.   tmp.LoadFromResource('Banjo.png');
  8.  
  9.   Angle := StrToFloat(Angle_Val.Caption);
  10.  
  11.   dest := TBGRAbitmap.create;
  12.   BGRAReplace(dest, tmp.FilterRotate(PointF(tmp.width-64, 64),angle,true));
  13.   dest.Draw(Image2.Canvas,198,536);
  14.   application.ProcessMessages;
  15.   dest.free;
  16.   tmp.free;
  17.  
  18. end;
It's the only proc. in the program.

To provide better understanding of the program concept I've attached a ScreenGrab. The 'Banjo' is the Gold coloured device at the bottom which should rotate about the circular hole on the right which is 64px from the top and right of the image extents.


FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

friend

  • Guest
Re: Rotating an Image
« Reply #6 on: June 02, 2022, 03:55:38 pm »
The point given to dest.Draw is where, in the image's space (Image2.Canvas), the image 'dest' will be drawn.
I made a small test, trying to resemble the case here -- it does not have a banjo but the closest option I had. The code is exactly like yours. All works as expected. Typing an angle, in degrees, into the box and pressing Return will draw the image rotated by that angle, around the point that lies at a distance of 64 pixels from the top and right sides of the image.  :-\

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: Rotating an Image
« Reply #7 on: June 02, 2022, 08:01:33 pm »
Thanks again @friend

I'm getting there!!   -   I suspect that by changing the 0,0 to the relatively high numbers that is the top-left of Image2 it did actually draw the new version somewhere outside the window. Returning that to 0,0 and I did get a rotated image drawn  -  far from perfect but at least I could see a result.

Another ScreenGrab attached (I've done some tidying up) and I'm sure you can see the latest problem!!  I'm using transparent .PNG files and I've also set the [transparent] property of Image1 & Image 2 to true but as you see I get a black background. I suspect that this may be addressed by setting tmp.alpha....... (something) to 0 but as there are over 20 'alpha.... ' options I have no idea which.

I also noticed that a second entry did not show a change in the angle. This may be due to the first rotated image remaining in place and it needs to be removed but again I'm fumbling about in the dark here.

The initial rotation showed 'clipping' of the image so I've enlarged the TImage size to accommodate the largest space that will ever be needed so that issue has been solved  -  just the transparency now -  the annoying thing is that in 2016 I wrote two programs that displayed transparent rotating images but I can't follow the logic that I used then - one of them is an Internal Combustion Engine (ICE) which is available to download at crescentcomputing.co.uk/Laz so I know it can be done  %)

 
FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

friend

  • Guest
Re: Rotating an Image
« Reply #8 on: June 03, 2022, 08:28:36 am »
Very cool program, that; thanks for sharing.
The control TImage does not support RGBA, BGRA, formats that include alpha, as the BGRABitmap library uses. But it supports transparency key: using a color to represent transparent parts of the image, that will not be drawn. I have attached an example, that contains an internal combustion engine, internally.
The top portion of the image, above the green part, is transparent.

In the same example code, after replacing dest with the rotated image, this is done:

Code: Pascal  [Select][+][-]
  1. dest.ReplaceTransparent(clRed);
  2. image1.Picture.Clear();
  3. image1.Picture.Bitmap.SetSize(tmp.Width, tmp.Height);
  4. image1.Picture.Bitmap.TransparentColor := clRed;
  5. image1.Transparent := True;
  6. dest.Draw(Image1.Picture.Bitmap.Canvas, 0,0);

This replaces transparent pixels in the image with red, then sets red as the transparency key in TImage's picture. To support transparency directly, drawn in some other way the image must be. To do it using TImage, that works.  :)

friend

  • Guest
Re: Rotating an Image
« Reply #9 on: June 03, 2022, 08:31:02 am »
It seems that my attachments were killed, and I cannot modify my post, thanks to those mean people.  :(
So here's what I was referring to.

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: Rotating an Image
« Reply #10 on: June 03, 2022, 12:22:07 pm »
!! SORTED !!

Well - I have a working test program - thanks to your input @friend  -  I've put it on the web at the same link as the ICE. Since you appreciated ICE you might like the Scale_Clock which is also available.

The 'Lathe' program is just the initial [shell] - in reality, the angle will be calculated based upon many factors so there is much work still to be done. I've added a second 'rotatable' image - the 'Tumbler' which changes the direction of travel. Whether that is [Fwd] or [Rev] will depend upon the gearing which has yet to be added.





FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

friend

  • Guest
Re: Rotating an Image
« Reply #11 on: June 03, 2022, 01:23:01 pm »
Good. How did you do it?
Thanks for sharing these too. Interesting clock. There's a problem that the engine does not have: the image keeps flickering, blinking; perhaps 'double buffering' is not enabled, or the surface is not invalidated after drawing.

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: Rotating an Image
« Reply #12 on: June 03, 2022, 02:19:55 pm »
As it's a quick & dirty 'Test' I've just hard-coded the rotation for each image separately. Ultimately I would hope that I can create a single [Rotate] proc. to which I will pass the 'Image' and the 'angle'.

Here's the current code :
Code: Pascal  [Select][+][-]
  1. Const
  2.   Up : byte = 1;
  3.   Dn : byte = 0;
  4.  
  5. Var
  6.   Fwd,Rev : boolean;
  7.   Tumbler_Pos : byte;
  8.  
  9. procedure TForm1.Angle_ValEditingDone(Sender: TObject);
  10. var
  11.   tmp, dest : TBGRABitmap;
  12.   angle     : Single;
  13. begin
  14.   Error_Lab.Hide;
  15.   Angle := StrToFloat(Angle_Val.Caption);
  16.   if (angle > 26.5) or (Angle < 0) then
  17.     begin
  18.       Error_Lab.Show;
  19.       Exit;
  20.     end
  21.   else
  22.     begin
  23.       tmp := TBGRAbitmap.create ('Banjo.png');
  24.       tmp.LoadFromResource('Banjo.png');
  25.  
  26.       dest := TBGRAbitmap.create;
  27.       BGRAReplace(dest, tmp.FilterRotate(PointF(tmp.width-64, 150),angle,true));
  28.  
  29.       dest.ReplaceTransparent(clRed);
  30.       image1.Picture.Clear;
  31.       image1.Picture.Bitmap.SetSize(tmp.Width,tmp.Height);
  32.       image1.Picture.Bitmap.TransparentColor:=clRed;
  33.       image1.Transparent:=true;
  34.  
  35.       dest.Draw(Image1.Picture.Bitmap.Canvas,0,0);
  36.  
  37.       application.ProcessMessages;
  38.       dest.free;
  39.       tmp.free;
  40.     end;
  41. end;
  42.  
  43. procedure TForm1.FormCreate(Sender: TObject);
  44. begin
  45.   Fwd := True;
  46.   Tumbler_Pos := Dn;
  47.   Width := 600;
  48.   Left := (Screen.Width-600) div 2;
  49.   Top  := 100;
  50. end;
  51.  
  52. procedure MoveTumbler(Dir : byte);
  53. Var
  54.   tmp, dest : TBGRABitmap;
  55. begin
  56.   tmp := TBGRAbitmap.create ('Tumbler.png');
  57.   tmp.LoadFromResource('Tumbler.png');
  58.   dest := TBGRAbitmap.create;
  59.  
  60.   if Dir = Up then
  61.     begin
  62.       BGRAReplace(dest, tmp.FilterRotate(PointF(110,191),-15.5,true));
  63.  
  64.     end
  65.   else
  66.     begin
  67.       BGRAReplace(dest, tmp.FilterRotate(PointF(110,191),0,true));
  68.  
  69.     end;
  70.  
  71.   dest.ReplaceTransparent(clRed);
  72.   Form1.Tumbler.Picture.Clear;
  73.   Form1.Tumbler.Picture.Bitmap.SetSize(tmp.Width,tmp.Height);
  74.   Form1.Tumbler.Picture.Bitmap.TransparentColor:=clRed;
  75.   Form1.Tumbler.Transparent:=true;
  76.  
  77.   dest.Draw(Form1.Tumbler.Picture.Bitmap.Canvas,0,0);
  78.  
  79. end;
  80.  
  81. procedure TForm1.Up_ArowClick(Sender: TObject);
  82. begin
  83.   if Tumbler_pos = Up then
  84.     exit
  85.   else
  86.     begin
  87.       MoveTumbler(Up);
  88.       Tumbler_Pos := Up;
  89.     end;
  90. end;
  91.  
  92. procedure TForm1.Dn_ArowClick(Sender: TObject);
  93. begin
  94.   if Tumbler_pos = Dn then
  95.     exit
  96.   else
  97.     begin
  98.       MoveTumbler(Dn);
  99.       Tumbler_Pos := Dn;
  100.     end;
  101. end;

I've been running the Scale_Clock for many years and I've given it to a number of my customers and I don't see any 'flickering'. I didn't recompile it using Laz 2.2 so I'm at a loss as to understand why you see this. I do seem to recall that there was a question about 'Double Buffering' but it was some years ago  :D

I may have to start another thread about problems with that since I've just tried to open it in Laz 1.6 and I get an error message about 'missing packages' :(  - with a reference to BGRA...... >:D





FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Rotating an Image
« Reply #13 on: June 03, 2022, 02:54:46 pm »

I may have to start another thread about problems with that since I've just tried to open it in Laz 1.6 and I get an error message about 'missing packages' :(  - with a reference to BGRA...... >:D

Hi!

If the BGRA Package is missing in your Laz 1.6  - so why don't you add it?

The error message is very clear about that.

Winni

J-G

  • Hero Member
  • *****
  • Posts: 953
Re: Rotating an Image
« Reply #14 on: June 03, 2022, 03:08:47 pm »
Good idea Winnie - why didn't I think of that  ;D

Of course - I did  - - -   It appears in the OPM list as 'Installed' along with a reference to the fact that there is a [new] version available so I selected it and requested an [update] - all seemed to go according to plan but at the end I was told that some 'support' units were missing. I regret that I don't really understand the 'Package Graph' - though I do see many 'negative' signs which are obviously dire warnings!

I have other duties to attend to but I will return to the issue shortly.
FPC 3.0.0 - Lazarus 1.6 &
FPC 3.2.2  - Lazarus 2.2.0 
Win 7 Ult 64

 

TinyPortal © 2005-2018