Lazarus

Programming => General => Topic started by: RedOctober on May 18, 2020, 09:09:01 pm

Title: I need to rotate a picture in a TImage 90°
Post by: RedOctober on May 18, 2020, 09:09:01 pm
Platform:  Microsfot Windows, FPC 3.0.4, Lazarus 1.8.4
I can't find a method on a TImage to do this, so did some quick research in this forum and I found a post about a package called BGRABit, so I found it listed in the on-line package manager and selected both the bitmap and the controls.  The controls install failed (no other info given) and I clicked "Yes to all" to attempt to install the remaining package.   At the moment, I seem to have an OpenGL component on my IDE tool bar, nothing new regarding the BGRABit (probably due to the failed package install).

I have two questions: 
1) How do I determine what is wrong with the install so I can maybe fix it and use it to rotate my image
2) Is there an easier way to rotate an image in a TImage (or some other Lazarus component that comes in the basic new installation)
3) Is there an alternative component set that will allow me to rotate my image that is not BGRABit, and will install correctly.

Thanks in advance for any help you can provide.

Title: Re: I need to rotate a picture in a TImage 90°
Post by: winni on May 18, 2020, 09:32:57 pm
Hi!


I think you should update your Lazarus.

1.84 is two years old.

Maybe that is the reason for the broken BGRA installation,

I updated BGRA on Win7/64 and Laz 2.08 with no problems.

Winni
Title: Re: I need to rotate a picture in a TImage 90°
Post by: RedOctober on May 18, 2020, 09:38:34 pm
I can't update my Lazarus.  Too many custom fixes for bugs in existing code base.  Upgrading risks being unable to install older components into the latest Lazarus version. I could spent a week trying to resolve all the issue.  Running out of time trying to meet an end-of-year deadline.
Title: Re: I need to rotate a picture in a TImage 90°
Post by: GetMem on May 18, 2020, 09:52:16 pm
@RedOctober
Quote
How do I determine what is wrong with the install so I can maybe fix it and use it to rotate my image
Look at the message window. Usually it's not obscured by OPM.

Quote
I can't update my Lazarus.  Too many custom fixes for bugs in existing code base.  Upgrading risks being unable to install older components into the latest Lazarus version. I could spent a week trying to resolve all the issue.  Running out of time trying to meet an end-of-year deadline.
Then perhaps you should install an older version of BGRABitmap. AFAIK version 9.1 worked fine with Lazarus 1.8.4.
Title: Re: I need to rotate a picture in a TImage 90°
Post by: winni on May 18, 2020, 10:35:46 pm
Hi!

If nothing else helps - awful slow:

Code: Pascal  [Select][+][-]
  1. function rotate90 (Source: TBitmap) : TBitmap;
  2. var dest : TBitmap;
  3.       x,y : Integer;
  4.       col : TColor;
  5.  
  6. begin
  7. dest := TBitmap.create;
  8. dest.setSize (Source.height,source.width);
  9. for x := 0 to Source.width - 1 do
  10.   begin
  11.      for y := 0 to Source.Height - 1 do
  12.        begin
  13.        col := Source.Canvas.pixels[x,y];
  14.        dest.canvas.pixels[y, dest.Height-x-1] := col;
  15.        end;//y
  16.      end; // x
  17.  result := dest;
  18. end; // proc
  19.  

Winni
Title: Re: I need to rotate a picture in a TImage 90°
Post by: marcov on May 18, 2020, 10:49:12 pm
For high speed and colour apps, I rotate using Opengl's coordinate system.

For smaller apps it is sometimes easier to just rotate using SSE/AVX (so you don't have to redo the coordinate systems of everything else to match)

For pf8bit I use http://www.stack.nl/~marcov/rot8x8.txt
Title: Re: I need to rotate a picture in a TImage 90°
Post by: RedOctober on May 19, 2020, 12:04:27 am
Hi Winni, your code works, but as you say, it's quite slow.  I will keep that snippet for future reference.
Hi GetMem, you are correct.  I DL'd and installed version 991 successfully.
Now I'm almost there but, have no idea with I'm doing with TBGRABitmap.  My goal is to load a file, rotate it either 90°, 180° or 270° and save it.
Here is my code.  I end up with a file containing only the color black, and I can tell by the size, it's not rotated.  Can you give me the correct code to accomplish this?  Thanks.

Code: Pascal  [Select][+][-]
  1. // Does NOT work
  2. procedure GrabItRotate(const src_fnm: String; const dgr: Integer);
  3. var bmp1, bmp2: TBGRABitmap;  trfm: TBGRAAffineBitmapTransform;
  4. begin
  5.   bmp1 := TBGRABitmap.Create(src_fnm);
  6.   bmp2 := TBGRABitmap.Create(bmp1.Width, bmp1.Height, BGRABlack);
  7.   trfm := TBGRAAffineBitmapTransform.Create(bmp2, False);
  8.   trfm.RotateDeg(dgr);
  9.   bmp2.SaveToFile(src_fnm);
  10. end;  
  11.  
Title: Re: I need to rotate a picture in a TImage 90°
Post by: winni on May 19, 2020, 12:47:52 am
Hi!

BGRAbitmap has in the Filters also the rotation.


Code: Pascal  [Select][+][-]
  1. var
  2. bmp : TBGRAbitmap;
  3. Center : TPointF;
  4. AngleDegree : single;
  5. .....
  6. BGRAReplace(bmp,bmp.Filterrotate(Center,angleDegree,false));
  7.  
  8.  

The third boolean parameter is

correctBlur: Boolean;



Heavy rotation on monday night!

Winni
Title: Re: I need to rotate a picture in a TImage 90°
Post by: jamie on May 19, 2020, 02:03:22 am
It used to be and I don't know if it is supported in other widgets but you can rotate an image using the Tbitmap  Rectangle copy over to another bitmap..

The trick is to specify the the LEFT,TOP value as the bottom, right values.

So reversing the Top and bottom will do the 180, left to right 270 etc..
I may need to experiment a little here.
Title: Re: I need to rotate a picture in a TImage 90°
Post by: RedOctober on May 19, 2020, 02:04:16 am
Still struggling.  I can get the 180° rotation to work, but the 90° and 270° always leaves black bars along the sides and the image is rotated "within" the original dimensional picture frame, if that makes sense.  Can you fix this code so it works in all cases?  thanks.

Code: Pascal  [Select][+][-]
  1. procedure GrabItRotate(const src_fnm: String; const dgr: Integer);
  2. var bmp1, bmp2: TBGRABitmap;  Center : TPointF;
  3. begin
  4.  
  5.   if (dgr = 180) then
  6.     begin
  7.   // Works
  8.       bmp1 := TBGRABitmap.Create(src_fnm);
  9.       bmp2 := TBGRABitmap.Create(bmp1.Width, bmp1.Height);
  10.       Center.x := bmp1.Width div 2;
  11.       Center.y := bmp1.Height div 2;
  12.       BGRAReplace(bmp2, bmp1.FilterRotate(Center, dgr, False));
  13.       bmp2.SaveToFile(src_fnm);
  14.     end;
  15.  
  16.   if (dgr = 90) or (dgr = 270) then
  17.     begin
  18.   // Does NOT work
  19.       bmp1 := TBGRABitmap.Create(src_fnm);
  20.       bmp2 := TBGRABitmap.Create(bmp1.Height, bmp1.Width);
  21.       Center.x := bmp1.Width div 2;
  22.       Center.y := bmp1.Height div 2;
  23.       BGRAReplace(bmp2, bmp1.FilterRotate(Center, dgr, False));
  24.       bmp2.SaveToFile(src_fnm);
  25.     end;
  26. end;
Title: Re: I need to rotate a picture in a TImage 90°
Post by: RedOctober on May 19, 2020, 02:09:03 am
Hi Jamie, I see where you are going with this.  Can you give me a code snippet?  I'm looking for RectangleCopy or CopyRectangle on a TBitmpap.. not there.
Title: Re: I need to rotate a picture in a TImage 90°
Post by: jamie on May 19, 2020, 02:49:40 am
what I was talking about is more of a mirror..


https://www.lazarusforum.de/viewtopic.php?p=33489#p33489
 (https://www.lazarusforum.de/viewtopic.php?p=33489#p33489)
That is a fast 90 rotate...

and the other code is just a simple CopyRect with reverse logic on the source or destination cords for the up/down or left/right
for 90s, perform one of the simple up/down left right then do a 90.

 This is a left and top flip.

Canvas.CopyRect(Rect(100,100,0,0),Canvas,Rect(0,0,100,100));
Title: Re: I need to rotate a picture in a TImage 90°
Post by: Thaddy on May 19, 2020, 08:00:07 am
On Windows, the plgblt api  is still the fasted way to rotate images at any angle.
Title: Re: I need to rotate a picture in a TImage 90°
Post by: winni on May 19, 2020, 10:55:21 am
Still struggling.  I can get the 180° rotation to work, but the 90° and 270° always leaves black bars along the sides and the image is rotated "within" the original dimensional picture frame, if that makes sense.  Can you fix this code so it works in all cases?  thanks.

Code: Pascal  [Select][+][-]
  1. procedure GrabItRotate(const src_fnm: String; const dgr: Integer);
  2. var bmp1, bmp2: TBGRABitmap;  Center : TPointF;
  3. begin
  4.  
  5.   if (dgr = 180) then
  6.     begin
  7.   // Works
  8.       bmp1 := TBGRABitmap.Create(src_fnm);
  9.       bmp2 := TBGRABitmap.Create(bmp1.Width, bmp1.Height);
  10.       Center.x := bmp1.Width div 2;
  11.       Center.y := bmp1.Height div 2;
  12.       BGRAReplace(bmp2, bmp1.FilterRotate(Center, dgr, False));
  13.       bmp2.SaveToFile(src_fnm);
  14.     end;
  15.  
  16.   if (dgr = 90) or (dgr = 270) then
  17.     begin
  18.   // Does NOT work
  19.       bmp1 := TBGRABitmap.Create(src_fnm);
  20.       bmp2 := TBGRABitmap.Create(bmp1.Height, bmp1.Width);
  21.       Center.x := bmp1.Width div 2;
  22.       Center.y := bmp1.Height div 2;
  23.       BGRAReplace(bmp2, bmp1.FilterRotate(Center, dgr, False));
  24.       bmp2.SaveToFile(src_fnm);
  25.     end;
  26. end;

Hi!

As the angles 90°,180° and 270° are only special cases there will allways be a border.
This is needed if you rotate with other angles.

You can cut the border off:
you know the center and the width and height of the original.
So you can compute the bounding box of the new bitmap.

Code: Pascal  [Select][+][-]
  1. var R : Trect;
  2.  
  3. .....
  4. CroppedBitmap := TBGRABitmap (RotatedBitmap.GetPart(R));
  5.  

The problem with 180° I don't know but I think I never tested it.

The workaround is:

Code: Pascal  [Select][+][-]
  1. OrigBitmap.VerticalFlip;
  2. OrigBitmap.HorzontalFlip;
  3.  

Winni


Title: Re: I need to rotate a picture in a TImage 90°
Post by: winni on May 19, 2020, 11:30:43 am
@RedOctober

I have just tested

FilterRotateAngle(center,180,false);

For me it works as it should - no problems.

There must be an other error in the code.

Winni
Title: Re: I need to rotate a picture in a TImage 90°
Post by: marcov on May 19, 2020, 12:04:00 pm
On Windows, the plgblt api  is still the fasted way to rotate images at any angle.

A n*90 degree is fundamentally different (and way faster) than a "any angle" operation.

Note that flipping (mirror in y direction) and 180 rotation are different things.

Flipping is usually very cheap (change bottom up flag to top down, e.g. image.imageheight:=-image.imageheight;)

Mirror is more complicated to do quickly. I never needed it, so I never worked on it.

Note that I also have a pascal version of the looptiling rotate algorithm that also works for other bpp and allows to set different (8x8,16x16 etc) tilesizes.  Looptiling used to be about 4 times faster on Core 2 machines compared to naive coordinate inversion. I haven't retested with recent machines. And of course, it mostly matters for images > cachesize.

Title: Re: I need to rotate a picture in a TImage 90°
Post by: RedOctober on May 19, 2020, 05:57:08 pm
Thanks to all contributors.  You all helped in solving this problem.  Here is the solution I am using now.  It's fast "enough", and the code is simple.

Code: Pascal  [Select][+][-]
  1. ...
  2. , BGRABitmap, BGRABitmapTypes, BGRATransform
  3. ...
  4.  
  5.  
  6. procedure GrabItRotate(const src_fnm: String; const dgr: Integer);
  7. var bmp: TBGRABitmap;
  8. begin
  9.   bmp := TBGRABitmap.Create(src_fnm);
  10.  
  11.   case dgr of
  12.      90:BGRAReplace(bmp, bmp.RotateCW);
  13.     180:begin
  14.           bmp.HorizontalFlip;
  15.           bmp.VerticalFlip;
  16.         end;
  17.     270:BGRAReplace(bmp, bmp.RotateCCW);
  18.   end;
  19.  
  20.   bmp.SaveToFile(src_fnm);
  21. end;
  22.  
  23.  
Title: Re: I need to rotate a picture in a TImage 90°
Post by: Thaddy on May 19, 2020, 06:04:00 pm
A n*90 degree is fundamentally different (and way faster) than a "any angle" operation.
Did you try my code...very old but near real time.. and I can port it back if required. http://members.chello.nl/t.koning8/kolsmoothrotate.zip
The plgblt api call is very fast on windows, much faster than presented here.
Title: Re: I need to rotate a picture in a TImage 90°
Post by: RedOctober on May 19, 2020, 06:05:24 pm
Something went wrong with my other final reply post, so I am re-posting the same thing again.  Can the moderators remove the duplicate?  I cannot find a "delete" button.

Code: Pascal  [Select][+][-]
  1. ...
  2. uses BGRABitmap, BGRABitmapTypes, BGRATransform
  3. ...
  4. procedure GrabItRotate(const src_fnm: String; const dgr: Integer);
  5. var bmp: TBGRABitmap;
  6. begin
  7.   bmp := TBGRABitmap.Create(src_fnm);
  8.  
  9.   case dgr of
  10.      90:BGRAReplace(bmp, bmp.RotateCW);
  11.     180:begin
  12.           bmp.HorizontalFlip;
  13.           bmp.VerticalFlip;
  14.         end;
  15.     270:BGRAReplace(bmp, bmp.RotateCCW);
  16.   end;
  17.  
  18.   bmp.SaveToFile(src_fnm);
  19. end;
  20.  
Title: Re: I need to rotate a picture in a TImage 90°
Post by: RedOctober on May 19, 2020, 06:06:41 pm
I tried posting my final solution twice, and it failed twice.  So, thanks to all the contributors, my problem is solved.  I cannot post the solution.. something wrong with the site I assume.
Title: Re: I need to rotate a picture in a TImage 90°
Post by: RedOctober on May 19, 2020, 06:08:30 pm
Thank you Thaddi, I'm out of time and I have a "good enough" solution, and have to get moving with other parts of the project.
Title: Re: I need to rotate a picture in a TImage 90°
Post by: marcov on May 19, 2020, 06:18:26 pm
A n*90 degree is fundamentally different (and way faster) than a "any angle" operation.
Did you try my code...very old but near real time.. and I can port it back if required. http://members.chello.nl/t.koning8/kolsmoothrotate.zip
The plgblt api call is very fast on windows, much faster than presented here.

No. Do you have a gui less example? Or normal lazarus ?
Title: Re: I need to rotate a picture in a TImage 90°
Post by: DJMaster on July 11, 2020, 09:32:36 am
The following routines perform quick Bitmap clockwise rotations using scanlines.

It would be nice to include this code somewhere into Lazarus as standard goodies.

Code: Pascal  [Select][+][-]
  1. uses
  2.   lcltype;
  3.  
  4. procedure Rotate90(Bitmap: TBitmap);
  5. type
  6.   TRGBArray = array[0..0] of TRGBTriple;
  7.   pRGBArray = ^TRGBArray;
  8. var
  9.   oldRows, oldColumns: integer;
  10.   rowIn, rowOut: pRGBArray;
  11.   tmpBitmap: TBitmap;
  12. begin
  13.   tmpBitmap := TBitmap.Create;
  14.  
  15.   tmpBitmap.Width := Bitmap.Height;
  16.   tmpBitmap.Height := Bitmap.Width;
  17.   tmpBitmap.PixelFormat := Bitmap.PixelFormat;
  18.  
  19.   for oldColumns := 0 to Bitmap.Width - 1 do
  20.   begin
  21.     rowOut := tmpBitmap.ScanLine[oldColumns];
  22.  
  23.     for oldRows := 0 to Bitmap.Height - 1 do
  24.     begin
  25.       rowIn := Bitmap.ScanLine[oldRows];
  26.       rowOut[Bitmap.Height - oldRows - 1] := rowIn[oldColumns];
  27.     end;
  28.   end;
  29.  
  30.   Bitmap.assign(tmpBitmap);
  31.   tmpBitmap.free;
  32. end;
  33.  
  34. procedure Rotate180(Bitmap: TBitmap);
  35. type
  36.   TRGBArray = array[0..0] of TRGBTriple;
  37.   pRGBArray = ^TRGBArray;
  38. var
  39.   countRows, countColumns: integer;
  40.   rowIn,rowOut: pRGBArray;
  41.   tmpBitmap: TBitmap;
  42. begin
  43.   tmpBitmap := TBitmap.Create;
  44.  
  45.   tmpBitmap.Width := Bitmap.Width;
  46.   tmpBitmap.Height := Bitmap.Height;
  47.   tmpBitmap.PixelFormat := Bitmap.PixelFormat;
  48.  
  49.   for countRows := 0 to Bitmap.Height - 1 do
  50.   begin
  51.     rowIn := Bitmap.ScanLine[countRows];
  52.     rowOut := tmpBitmap.ScanLine[Bitmap.Height - countRows - 1];
  53.  
  54.     for countColumns := 0 to Bitmap.Width - 1 do
  55.       rowOut[Bitmap.Width - countColumns - 1] := rowIn[countColumns];
  56.   end;
  57.  
  58.   Bitmap.assign(tmpBitmap);
  59.   tmpBitmap.free;
  60. end;
  61.  
  62. procedure Rotate270(Bitmap: TBitmap);
  63. type
  64.   TRGBArray = array[0..0] of TRGBTriple;
  65.   pRGBArray = ^TRGBArray;
  66. var
  67.   oldRows, oldColumns: integer;
  68.   rowIn, rowOut: pRGBArray;
  69.   tmpBitmap: TBitmap;
  70. begin
  71.   tmpBitmap := TBitmap.Create;
  72.  
  73.   tmpBitmap.Width := Bitmap.Height;
  74.   tmpBitmap.Height := Bitmap.Width;
  75.   tmpBitmap.PixelFormat := Bitmap.PixelFormat;
  76.  
  77.   for oldColumns := 0 to Bitmap.Width - 1 do
  78.   begin
  79.     rowOut := tmpBitmap.ScanLine[oldColumns];
  80.  
  81.     for oldRows := 0 to Bitmap.Height - 1 do
  82.     begin
  83.       rowIn := Bitmap.ScanLine[oldRows];
  84.       rowOut[oldRows] := rowIn[Bitmap.Width - oldColumns - 1];
  85.     end;
  86.   end;
  87.  
  88.   Bitmap.assign(tmpBitmap);
  89.   tmpBitmap.free;
  90. end;
  91.  
Title: Re: I need to rotate a picture in a TImage 90°
Post by: marcov on July 11, 2020, 05:17:40 pm
The following routines perform quick Bitmap clockwise rotations using scanlines.

It would be nice to include this code somewhere into Lazarus as standard goodies.

I'm also working on something like that, converting old generic code to FPC 3.2+ generic functions

My current version is not just for RGB24, (but also for 1,2,4 byte images) and 4-10 times faster because it adds looptiling to the algorithm.

See http://www.stack.nl/~marcov/files/bitmaprotatetest.zip
TinyPortal © 2005-2018