(d)Rotating Bitmaps
Please take a moment to look through the following code, as I will be discussing it next.
The RotateMain pas file.
///////////////////////////////////////////////////////////////////////
// RotateMain.pas //
// Project: Rotate //
// Copyright (c) 1998 by Jeff Cottingham //
///////////////////////////////////////////////////////////////////////
unit rotatemain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
bitmap1: TBitmap;
bitmap2: TBitmap;
ptr: pByteArray;
newscanline: pByteArray;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
bitmap1 := TBitmap.Create;
bitmap2 := TBitmap.Create;
try
bitmap1.LoadFromFile('Factory.bmp');
bitmap1.PixelFormat := pf8bit;
bitmap2.PixelFormat := pf8bit;
bitmap2.Palette := bitmap1.Palette;
except
ShowMessage('Could not load or alter bitmap');
end;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
bitmap1.Free;
bitmap2.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
Image1.Canvas.Draw(0,0,bitmap1);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
y: integer;
x: integer;
begin
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
bitmap2.Height := bitmap1.Height;
bitmap2.Width := bitmap1.Width;
for y := 0 to bitmap2.Height - 1 do begin
ptr := bitmap1.ScanLine[y];
newscanline := bitmap2.ScanLine[((bitmap1.Height - 1) - y)];
for x := 0 to bitmap1.Width - 1 do begin
newscanline[x] := ptr[x];
end;
end;
Image1.Canvas.Draw(0,0,bitmap2);
end;
procedure TForm1.Button3Click(Sender: TObject);
var
y: integer;
x: integer;
begin
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
bitmap2.Height := bitmap1.Width;
bitmap2.Width := bitmap1.Height;
for y := 0 to bitmap1.Width - 1 do begin
newscanline := bitmap2.ScanLine[y];
for x := 0 to bitmap1.Height - 1 do begin
ptr := bitmap1.ScanLine[x];
newscanline[x] := ptr[y];
end;
end;
Image1.Canvas.Draw(0,0,bitmap2);
end;
end.
If you have read preceding code carefully you will notice that you have two bitmaps. bitmap1 is for the original image and bitmap2 is where you are doing your work before you display it. Now let’s take a look at what is going on.
procedure TForm1.FormCreate(Sender: TObject);
begin
bitmap1 := TBitmap.Create;
bitmap2 := TBitmap.Create;
try
bitmap1.LoadFromFile('Factory.bmp');
bitmap1.PixelFormat := pf8bit;
bitmap2.PixelFormat := pf8bit;
bitmap2.Palette := bitmap1.Palette;
except
ShowMessage('Could not load or alter bitmap');
end;
end;
In the preceding code after I create the two bitmaps, bitmap1 and bitmap2, and load an image into bitmap1 you will notice the following line of code.
bitmap2.PixelFormat := pf8bit;
Since you are working with a 256 color bitmap, you must make provisions for the work area (bitmap2) to have the same pixel format as your original image, otherwise it will not display properly, if at all. In the fade example at the end of this section you will be working with a different PixelFormat and there are other factors involved but more on these factors later. PixelFormat pf8bit means that each pixel has eight bits or one byte to hold its color, which in this case is a single palette entry.
Since you are working with a bitmap that might have a palette associated with it, you must also deal with that too. Here I am assigning the handle of bitmap1’s palette to bitmap2:
bitmap2.Palette := bitmap1.Palette;
This will ensure that I am working with the same palette. Without this line of code there are no guarantees that my work area, bitmap2, will display with the proper colors.
So how do you flip an image and display it? The following code accomplishes this.
procedure TForm1.Button2Click(Sender: TObject);
var
y: integer;
x: integer;
begin
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
bitmap2.Height := bitmap1.Height;
bitmap2.Width := bitmap1.Width;
for y := 0 to bitmap2.Height - 1 do begin
ptr := bitmap1.ScanLine[y];
newscanline := bitmap2.ScanLine[((bitmap1.Height - 1) - y)];
for x := 0 to bitmap1.Width - 1 do begin
newscanline[x] := ptr[x];
end;
end;
Image1.Canvas.Draw(0,0,bitmap2);
end;
First I do a cleanup of my display area. I use the ClipRect as it is just the dirty area, the area that have been alreay drawn on, no need to refresh the whole image canvas. Next, I have to define the size of my work area (bitmap2). You will notice that there are two for loops next. In the outer for loop, I start at the top of bitmap1 and work my way down and start at the bottom of bitmap2 and work my way up. This gives me the effect that I want: it flips the image. All the inner for loop does is copy each element of the ScanLine from bitmap1 to the ScanLine of bitmap2. All the is left to do is display bitmap2. I could achieve a mirror image instead of a flip with a small change in the ScanLine logic:
for y := 0 to bitmap2.Height - 1 do begin
ptr := bitmap1.ScanLine[y];
newscanline := bitmap2.ScanLine[y];
for x := 0 to bitmap1.Width - 1 do begin
newscanline[((bitmap1.Width - 1) - x)] := ptr[x];
end;
end;
Now lets look at the code to rotate the bitmap:
procedure TForm1.Button3Click(Sender: TObject);
var
y: integer;
x: integer;
begin
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
bitmap2.Height := bitmap1.Width;
bitmap2.Width := bitmap1.Height;
for y := 0 to bitmap1.Width - 1 do begin
newscanline := bitmap2.ScanLine[y];
for x := 0 to bitmap1.Height - 1 do begin
ptr := bitmap1.ScanLine[x];
newscanline[x] := ptr[y];
end;
end;
Image1.Canvas.Draw(0,0,bitmap2);
end;
The loop logic here has to be different because you have to write the ScanLine of bitmap1 into a specific location in all the ScanLines of bitmap2. If you take a close look at the preceding code, that is exactly what I am doing. With the same type of logic that we used to flip in the previous code, the preceding code could be modified to rotate and mirror the image. But, I will leave that up to you to implement. I do not want to have all the fun.