How to rotate a bitmap?

I want to rotate a bitmap that has been loaded from a jpeg file into a TImage by 90 degrees. Don't want to save it or anything else.

I have done a search and what I could find was procedures for arbitrary degree rotation, or did not work (or both).

I have done this before back in the days when I wrote in assembler, but when I tried to do it using the TCanvas.Pixels property my image kept disappearing (dimensions set to 0).

I also tried Charles Hacker's procedure (in the "How to rotate a png image?" thread further down (currently) in this forum. Same thing. Image disappears.

Can anybody give me a procedure that will do the rotation on a visible TImage?

TIA,
Malcolm
0
Malcolm
6/7/2013 1:51:03 PM
embarcadero.delphi.graphics 928 articles. 0 followers. Follow

13 Replies
8383 Views

Similar Articles

[PageSpeed] 0

Do you know this page?

http://www.efg2.com/Lab/ImageProcessing/RotateScanline.htm

He's using scanline what makes the procedure much quicker instead of doing it pixel by pixel.
If your picture is not square you have to enlarge the canvas somehow..

If you have the latest Delphi XE's with FireMonkey you can use this graphic library and
use just1 parameter to rotate images (2D and 3D)
0
Robert
6/7/2013 2:42:14 PM
Thanks for your reply Robert.

I was aware of the link you give. It does not help.

I have been using the scan lines, but the problem appears to be more fundamental than that.

Consider the following code:-
{code}
  Image1.Picture.LoadFromFile(TestFile);
  with Image1.Picture do Label1.caption := '('+IntToStr(Width)+'.'+IntToStr(Height)+')';
  Image1.Picture.Bitmap;
  with Image1.Picture do Label2.caption := '('+IntToStr(Width)+'.'+IntToStr(Height)+')';
{code}

After this the caption for Label1 {with my test image) is (800,600); while label2 caption is (0,0);

Malcolm

The same happens if one uses the bitmap width: e.g. W :=  Image1.Picture.Bitmap.Width;

*It used to be that writing a function with undocumented side effects was the most heinous crime a programmer could commit.* This no longer appears to be the case. 

I am ttrying to find a work around.



> {quote:title=Robert Triest wrote:}{quote}
> Do you know this page?
> 
> http://www.efg2.com/Lab/ImageProcessing/RotateScanline.htm
> 
> He's using scanline what makes the procedure much quicker instead of doing it pixel by pixel.
> If your picture is not square you have to enlarge the canvas somehow..
> 
> If you have the latest Delphi XE's with FireMonkey you can use this graphic library and
> use just1 parameter to rotate images (2D and 3D)
0
Malcolm
6/7/2013 5:47:10 PM
Giving actual working code (with eg the names of files you load included) is usually preferable
That way someone might notice that you loaded a jpg into the tpicture and then accessed  the properties of tpictures bitmap, clearing the jpg data in the process
(or something like that)
0
karl
6/7/2013 7:33:33 PM
Malcolm Coulter wrote:

> I want to rotate a bitmap that has been loaded from a jpeg file into a TImage by 90 degrees.
> Don't want to save it or anything else.
> 
> I have done a search and what I could find was procedures for arbitrary degree rotation, or did
> not work (or both).
> 
> I have done this before back in the days when I wrote in assembler, but when I tried to do it
> using the TCanvas.Pixels property my image kept disappearing (dimensions set to 0).

Hi Malcolm,
You did not mention which Delphi version you have so the following might be different in your
version (I'm using D2010).
The TImage.Picture can hold various picture formats, jpg, bmp etc.
TImage.Picture.Bitmap is usable only if you have loaded a bitmap into TImage.
Specifically, if you have loaded any other graphic format and attempt to access the Bitmap, the
current image will be cleared as you have seen. TImage.Picture.Bitmap is not the internal
representation of what the TImage shows on screen for other than bitmaps.

> 
> I also tried Charles Hacker's procedure (in the "How to rotate a png image?" thread further down
> (currently) in this forum. Same thing. Image disappears.

You must operate on in-memory bitmaps just as Charles Hackers procedure does.
Regardless of what file format you have loaded into TImage, you can assign the image to a temporary
bitmap like:
{code}
  OrigBmp.Assign(Image1.Picture.Graphic);
{code}
then rotate into a TempBmp and assign back to TImage:
{code}
  Image1.Picture.Assign(TempBmp);
{code}

I understand that you are familiar with the rotating algorithm, so I leave it here, unless you need
assistance with that.

Cheers

-- 
Tom Brunberg
firstname.lastname@welho.com
0
Tom
6/7/2013 7:37:56 PM
look at the comment of Tom below.. You should use image.picture.graphic for your operations. First put the graphic into a memory space and do your manipulations. After you assign the memory back to the TImage.
0
Robert
6/8/2013 6:50:52 AM
It seems that my problem has nothing to do with rotation, but with accessing the bitmap for Jpeg files (BMP bitmaps give no problems.)

I am using XE3

It seems that, for Jpeg files, TPicture.LoadFromFile does not create a recognisable TBitmap

When one tries to access the Bitmap, {code}TPicture.GetBitmap{code} is called.

The first thing this does is to call {code}ForceType(TBitmap){code}
This checks if the graphic created by LoadFromFile is a TBitmap (if a Jpeg image is loaded the class is TJpegImage)

Since the check fails, the graphic is cleared by creating a new (empty)Graphic and the image is lost.

*So my problem is: How do I access the in-memory jpeg image data?*

I see that if the vcl routines are compiled with the *CLR* compiler directive, then a Jpeg image is of class TBitmap
*+What is the CLR compiler directive?+*

The problem, as I experience it, may be be dependent on the Delphi version. I no longer have earlier versions, so perhaps someone who has an earlier version can try out  my code:
{code}
procedure Form1.Button1Click(Sender: TObject);
begin
  Image1.Picture.LoadFromFile('AnyJpegImage.jpg');
  with Image1.Picture do Label1.caption := '('+IntToStr(Width)+'.'+IntToStr(Height)+')';
  Image1.Picture.Bitmap;
  with Image1.Picture do Label2.caption := '('+IntToStr(Width)+'.'+IntToStr(Height)+')';
end;
{code}

(If Image1.Picture.Bitmap is not a function call in your version you may have to use {code}IntegerVariable := Image1.Picture.Bitmap.Height){code}

After this the caption for Label1 {with my test image) is (800,600); while Label2 caption is (0,0);
Without the Bitmap reference both captions are the same (800,600).

Malcolm
0
Malcolm
6/8/2013 7:30:15 AM
Malcolm Coulter wrote:

> It seems that my problem has nothing to do with rotation, but with accessing the bitmap for Jpeg
> files (BMP bitmaps give no problems.)

Correct!

> I am using XE3
> 
> It seems that, for Jpeg files, TPicture.LoadFromFile does not create a recognisable TBitmap

Also correct in the sense I told you:
"TImage.Picture.Bitmap is not the internal representation of what the TImage shows on screen for
other than bitmaps." Emphasis on *not*.

> When one tries to access the Bitmap, {code}TPicture.GetBitmap{code} is called.
> 
> The first thing this does is to call {code}ForceType(TBitmap){code}
> This checks if the graphic created by LoadFromFile is a TBitmap (if a Jpeg image is loaded the
> class is TJpegImage)
> 
> Since the check fails, the graphic is cleared by creating a new (empty)Graphic and the image is
> lost.
> 
> *So my problem is: How do I access the in-memory jpeg image data?*

You can not acces it directly. If you try by erroneously accessing the TImage.Picture.Bitmap, the
jpeg image is cleared.

You must copy the jpeg image to a separate bitmap, manipulate that bitmap and then copy it back to
the TImage. Rotating can not be done in one bitmap, so you actually need two separate bitmaps: one
to hold the original image and one into you build the rotated. Finally you copy the rotated back to
the TImage.

> I see that if the vcl routines are compiled with the CLR compiler directive, then a Jpeg image is
> of class TBitmap *+What is the CLR compiler directive?+*

It is for compiling for dot net Common Language Runtime.

> The problem, as I experience it, may be be dependent on the Delphi version. I no longer have
> earlier versions, so perhaps someone who has an earlier version can try out  my code:  {code}
> procedure Form1.Button1Click(Sender: TObject);
> begin
>   Image1.Picture.LoadFromFile('AnyJpegImage.jpg');
>   with Image1.Picture do Label1.caption := '('+IntToStr(Width)+'.'+IntToStr(Height)+')';
>   Image1.Picture.Bitmap;
>   with Image1.Picture do Label2.caption := '('+IntToStr(Width)+'.'+IntToStr(Height)+')';
> end;
> {code}
> 
> (If Image1.Picture.Bitmap is not a function call in your version you may have to use
> {code}IntegerVariable := Image1.Picture.Bitmap.Height){code}
> 
> After this the caption for Label1 {with my test image) is (800,600); while Label2 caption is
> (0,0); Without the Bitmap reference both captions are the same (800,600).

Which just prooves what I told you: "Specifically, if you have loaded any other graphic format and
attempt to access the Bitmap, the current image will be cleared as you have seen."

Once again, here's an outline of what you need to do:
{code}
  //Load the file into TImage.
  Image1.Picture.LoadFromFile('testfile.jpg');
  //Create two temporary bitmaps
  OrigBmp := TBitmap.Create;
  RotatedBmp := TBitmap.Create;
  // Copy the image from TImage to one of the bitmaps
  OrigBmp.Assign(Image1.Picture.Graphic);
  // Set the size of the other bitmap swapping width and height
  RotatedBmp.SetSize(OrigBmp.Height, OrigBmp.Width);
  // Apply the rotating algorithm by copying pixels
  // from OrigBmp to RotatedBmp
  // ....
  // Copy the Rotated bitmap back to the TImage
  Image1.Picture.Assign(RotatedBmp);
{code}


Cheers

-- 
Tom Brunberg
firstname.lastname@welho.com
0
Tom
6/8/2013 8:22:13 AM
I did try assigning the graphic to a bitmap and using it in Charles Hacker's routine.
However I got inaccessible values for the scanline elements.
Working with a BMP bitmap the scanline values are valid, so it seems something more is needed.

Type-casting the graphic to a TJpegImage did no produce anything useful that I could see. A TPNGInage does have scanlines, but I am not interested in PNG for the current purpose.


(This was meant to be a reply to Robert - oops)

Edited by: Malcolm Coulter on Jun 8, 2013 10:24 AM
0
Malcolm
6/8/2013 8:26:19 AM
> {quote:title=Tom Brunberg wrote:}{quote}
> Which just prooves what I told you: "Specifically, if you have loaded any other graphic format and
> attempt to access the Bitmap, the current image will be cleared as you have seen."
> 
> Once again, here's an outline of what you need to do:
> {code}
>   //Load the file into TImage.
>   Image1.Picture.LoadFromFile('testfile.jpg');
>   //Create two temporary bitmaps
>   OrigBmp := TBitmap.Create;
>   RotatedBmp := TBitmap.Create;
>   // Copy the image from TImage to one of the bitmaps
>   OrigBmp.Assign(Image1.Picture.Graphic);
>   // Set the size of the other bitmap swapping width and height
>   RotatedBmp.SetSize(OrigBmp.Height, OrigBmp.Width);
>   // Apply the rotating algorithm by copying pixels
>   // from OrigBmp to RotatedBmp
>   // ....
>   // Copy the Rotated bitmap back to the TImage
>   Image1.Picture.Assign(RotatedBmp);
> {code}
> 
Thanks, Tom.

That is exactly what I did, but I still get inaccessible values for the elements of the scanline.

Malcolm
0
Malcolm
6/8/2013 8:39:58 AM
Malcolm Coulter wrote:

> > {quote:title=Tom Brunberg wrote:}{quote}
> > Which just prooves what I told you: "Specifically, if you have loaded any other graphic format
> > and attempt to access the Bitmap, the current image will be cleared as you have seen."
> > 
> > Once again, here's an outline of what you need to do:
> > {code}
> >   //Load the file into TImage.
> >   Image1.Picture.LoadFromFile('testfile.jpg');
> >   //Create two temporary bitmaps
> >   OrigBmp := TBitmap.Create;
> >   RotatedBmp := TBitmap.Create;
> >   // Copy the image from TImage to one of the bitmaps
> >   OrigBmp.Assign(Image1.Picture.Graphic);
> >   // Set the size of the other bitmap swapping width and height
> >   RotatedBmp.SetSize(OrigBmp.Height, OrigBmp.Width);
> >   // Apply the rotating algorithm by copying pixels
> >   // from OrigBmp to RotatedBmp
> >   // ....
> >   // Copy the Rotated bitmap back to the TImage
> >   Image1.Picture.Assign(RotatedBmp);
> > {code}
> > 
> Thanks, Tom.
> 
> That is exactly what I did, but I still get inaccessible values for the elements of the scanline.

Malcolm,
OK, here's a testcode I just wrote (in a form with a TImage and two TButtons):

{code}
implementation

uses jpeg;

type
  TRGBPixel = record
    b: byte;
    g: byte;
    r: byte;
  end;
  TRGBArray = array[0..65535] of TRGBPixel;
  PRGBArray = ^TRGBArray;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Image1.Picture.LoadFromFile('1sink.jpg');
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  bmpA, bmpB: TBitmap;
  ix, iy: integer;
  rowIn: pRGBArray;
begin
  bmpA := TBitmap.Create;
  bmpA.Assign(Image1.Picture.Graphic);
  bmpA.PixelFormat := pf24bit;

  bmpB := TBitmap.Create;
  bmpB.PixelFormat := pf24bit;
  bmpB.SetSize(bmpA.Height, bmpA.Width);

  for iy := 0 to bmpA.Height - 1 do
  begin
    rowIn := bmpA.ScanLine[iy];
    for ix := 0 to bmpA.Width - 1 do
      pRGBARray(bmpB.ScanLine[ix])[bmpA.Height - iy - 1] := rowIn[ix];
  end;

  Image1.Picture.Assign(bmpB);
end;
{code}

Cheers

-- 
Tom Brunberg
firstname.lastname@welho.com
0
Tom
6/8/2013 8:50:43 AM
Thanks Tom...

My problem was that Charles Hacker did not define pRGBArray, and +without thinking+ I took it to be a pointer to an integer/tColor/32bit array!!!

Thanks again for all your trouble.

Regards...
....Malcolm
0
Malcolm
6/8/2013 9:09:30 AM
> {quote:title=Malcolm Coulter wrote:}{quote}
> It seems that my problem has nothing to do with rotation, but with accessing the bitmap for Jpeg files (BMP bitmaps give no problems.)
> 
> I am using XE3
> 
> It seems that, for Jpeg files, TPicture.LoadFromFile does not create a recognisable TBitmap
> 
> When one tries to access the Bitmap, {code}TPicture.GetBitmap{code} is called.
> 
> The first thing this does is to call {code}ForceType(TBitmap){code}
> This checks if the graphic created by LoadFromFile is a TBitmap (if a Jpeg image is loaded the class is TJpegImage)
> 
> Since the check fails, the graphic is cleared by creating a new (empty)Graphic and the image is lost.
> 
> *So my problem is: How do I access the in-memory jpeg image data?*
> 
> I see that if the vcl routines are compiled with the *CLR* compiler directive, then a Jpeg image is of class TBitmap
> *+What is the CLR compiler directive?+*
> 
> The problem, as I experience it, may be be dependent on the Delphi version. I no longer have earlier versions, so perhaps someone who has an earlier version can try out  my code:
> {code}
> procedure Form1.Button1Click(Sender: TObject);
> begin
>   Image1.Picture.LoadFromFile('AnyJpegImage.jpg');
>   with Image1.Picture do Label1.caption := '('+IntToStr(Width)+'.'+IntToStr(Height)+')';
>   Image1.Picture.Bitmap;
>   with Image1.Picture do Label2.caption := '('+IntToStr(Width)+'.'+IntToStr(Height)+')';
> end;
> {code}
> 
> (If Image1.Picture.Bitmap is not a function call in your version you may have to use {code}IntegerVariable := Image1.Picture.Bitmap.Height){code}
> 
> After this the caption for Label1 {with my test image) is (800,600); while Label2 caption is (0,0);
> Without the Bitmap reference both captions are the same (800,600).
> 
> Malcolm

hi, is there anyone doing this with .net?  I came across [url=http://www.rasteredge.com/how-to/csharp-imaging/rotate-image/]image rotate component[/url]  on web ,is it possible to be integrated into XE3. Any idea?
if possible ,Plz let me know how to start.

Edited by: John M on Jul 24, 2013 11:50 PM
0
John
7/25/2013 6:51:22 AM
Since you use XE3 why don't you use FireMonkey to do these graphical things?
Instead of trying to integrate .NET techniques you could see if you can integrate 
FireMonkey 2D or 3D in your project. Not sure if that will work for you but I would try it.

http://docwiki.embarcadero.com/RADStudio/XE2/en/Using_FireMonkey_Animation_Effects
http://stackoverflow.com/questions/7315050/delphi-xe2-possible-to-instantiate-a-firemonkey-form-in-vcl-application
0
Robert
7/25/2013 7:23:01 AM
Reply: