DMTrump
asked on
How do I manipulate a dynamic array of variable sized Delphi TBitmaps?
I need to know if it is possible to manipulate a dynamic array of tbitmaps of variable sizes. ( Because ot the varrying size, I can't use to TimageList)
This is the code I propose to use to add and remove elements from the array. Can anyone confirm that this will work - or correct any problems or definately tell me this can't be safely done at all. I'd like some "Expert" opinions before I try using this code. Because I'd like a good answer and if this or something like it will work I need to get this code out to my client today I'm spending 500 points to be shared among those who give me good information about this.
type
TBitmapArray = array of TBitmap;
public
BMParray : TBitmapArray;
procedure newBMP(ba: TBitmapArray; x,y,w,h: integer);
procedure killBMP(ba: TBitmapArray; index: integer);
implementation
procedure T_form.newBMP(ba: TBitmapArray; x,y,w,h: integer);
var
n: integer;
DC : HDC;
begin
DC := GetDC (GetDesktopWindow) ;
try
n:= High(BMParray)+1;
SetLength(BMParray,n);
BMParray[n]:=TBitMap.Creat e;
BMParray[n].Width:=w;
BMParray[n].Height:=h;
// The actual populating of the bitmaps is more complicate than the following
// but they will be variable size and this will suffice as an example
BitBlt(BMParray[n].Canvas. Handle, 0, 0, BMParray[n].Width, BMParray[n].Height, DC, x, y, SRCCOPY) ;
finally
ReleaseDC (GetDesktopWindow, DC) ;
end;
end;
procedure T_form.killBMP(ba: TBitmapArray; index: Integer);
begin
if (index<=High(ba)) and (index>=Low(ba)) then
begin
// is this the proper order? to do this first before the SetLength?
// it's the only way that makes sense to me
ba[index].FreeImage;
ba[index].Free;
if index<>High(ba) then
begin
Finalize(ba[Index]);
// this is the part I'm most concerned about...
System.Move(ba[Index+1], ba[Index], (Length(ba)-Index-1)*SizeO f(TBitmap) +1);
end;
SetLength(ba, Length(ba) - 1) ;
end;
end;
This is the code I propose to use to add and remove elements from the array. Can anyone confirm that this will work - or correct any problems or definately tell me this can't be safely done at all. I'd like some "Expert" opinions before I try using this code. Because I'd like a good answer and if this or something like it will work I need to get this code out to my client today I'm spending 500 points to be shared among those who give me good information about this.
type
TBitmapArray = array of TBitmap;
public
BMParray : TBitmapArray;
procedure newBMP(ba: TBitmapArray; x,y,w,h: integer);
procedure killBMP(ba: TBitmapArray; index: integer);
implementation
procedure T_form.newBMP(ba: TBitmapArray; x,y,w,h: integer);
var
n: integer;
DC : HDC;
begin
DC := GetDC (GetDesktopWindow) ;
try
n:= High(BMParray)+1;
SetLength(BMParray,n);
BMParray[n]:=TBitMap.Creat
BMParray[n].Width:=w;
BMParray[n].Height:=h;
// The actual populating of the bitmaps is more complicate than the following
// but they will be variable size and this will suffice as an example
BitBlt(BMParray[n].Canvas.
finally
ReleaseDC (GetDesktopWindow, DC) ;
end;
end;
procedure T_form.killBMP(ba: TBitmapArray; index: Integer);
begin
if (index<=High(ba)) and (index>=Low(ba)) then
begin
// is this the proper order? to do this first before the SetLength?
// it's the only way that makes sense to me
ba[index].FreeImage;
ba[index].Free;
if index<>High(ba) then
begin
Finalize(ba[Index]);
// this is the part I'm most concerned about...
System.Move(ba[Index+1], ba[Index], (Length(ba)-Index-1)*SizeO
end;
SetLength(ba, Length(ba) - 1) ;
end;
end;
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
developmentguru:
That seems way to easy! Could it be this simple? The following code complies but I'm scared of leaking memory!
var
bmplst: TList;
procedure T_form.newBMP(blst: Tlist; x,y,w,h: integer);
var
DC : HDC;
b: TBitmap;
i: integer;
begin
DC := GetDC (GetDesktopWindow) ;
try
b:=TBitMap.Create;
b.Width:=w;
b.Height:=h;
// The actual populating of the bitmaps is more complicate than the following
// but they will be variable size and this will suffice as an example
BitBlt(b.Canvas.Handle, 0, 0, b.Width, b.Height, DC, x, y, SRCCOPY) ;
blst.add(b);
finally
ReleaseDC (GetDesktopWindow, DC) ;
end;
end;
procedure T_form.killBMP(blst: TList; index: Integer);
begin
// is this going to free the memory?
// that seems way to simple
blst.Delete(index);
end;
That seems way to easy! Could it be this simple? The following code complies but I'm scared of leaking memory!
var
bmplst: TList;
procedure T_form.newBMP(blst: Tlist; x,y,w,h: integer);
var
DC : HDC;
b: TBitmap;
i: integer;
begin
DC := GetDC (GetDesktopWindow) ;
try
b:=TBitMap.Create;
b.Width:=w;
b.Height:=h;
// The actual populating of the bitmaps is more complicate than the following
// but they will be variable size and this will suffice as an example
BitBlt(b.Canvas.Handle, 0, 0, b.Width, b.Height, DC, x, y, SRCCOPY) ;
blst.add(b);
finally
ReleaseDC (GetDesktopWindow, DC) ;
end;
end;
procedure T_form.killBMP(blst: TList; index: Integer);
begin
// is this going to free the memory?
// that seems way to simple
blst.Delete(index);
end;
ASKER
Johnjces:
No, I do save them each selectively but not as a group.
No, I do save them each selectively but not as a group.
ASKER
developmentguru:
On further investigation I see that I need to free the object (Tbitmap) memory myself - and I'm also foggy on getting one of my Tlist bitmaps into a Timage by index - If you can help with the the code for these two tasks I'd be very (500 points worth) grateful!
On further investigation I see that I need to free the object (Tbitmap) memory myself - and I'm also foggy on getting one of my Tlist bitmaps into a Timage by index - If you can help with the the code for these two tasks I'd be very (500 points worth) grateful!
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Lots of good help - everyth8ing works fine now!
It took me a while, but here is a full example. The primary problem that you face is that a bitmap has no X or Y coordinates. In this instance you need a class that handles a bitmap and a coordinate of where to place it. In some programming I have seen this is called a sprite. With that in mind, here is full source for a main form that will give an example. Place two buttons btnAdd and btnDelete on the form in the lower left corner with the anchors set to left and bottom. Add a paint box in the upper left corner, stretch it to fill the form to just above the buttons and set its anchor property to Left, Top, Right, Bottom. Add the following code and set the events (create and destroy on the form, Paint on the Paint Box, click on the buttons).
-------------------------- ---------- ---------- ---------- ---------- ---------- ----------
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, contnrs, ExtCtrls;
type
{definition of these other classes would likely best be done in a separate
unit. They are defined here for simplicity.}
{This class holds the definition of the individual "sprites". You could add
properties for speed, iniertia, whatever. Keeping it simple, I started with
X and Y which are not natural for a bitmap to have.}
TSpriteDefinition = class
private
fBitmap : TBitmap;
fX, fY : integer;
fWidth, fHeight : integer;
function GetHeight: integer;
function GetWidth: integer;
procedure SetBitmap(const Value: TBitmap);
procedure SetHeight(const Value: integer);
procedure SetWidth(const Value: integer);
protected
public
constructor Create(x, y, Width, Height : integer);
destructor Destroy; override;
procedure PaintTo(Canvas : TCanvas);
property X : integer read fX;
property Y : integer read fY;
property Width : integer read GetWidth write SetWidth;
property Height : integer read GetHeight write SetHeight;
property Bitmap : TBitmap read fBitmap write SetBitmap;
end;
{this class makes it easier to manipulate the list}
TSpriteList = class(TObjectList)
private
function GetSprite(Index: integer): TSpriteDefinition;
protected
public
function NewSprite(FileName : string; x, y, w, h : integer) :
TSpriteDefinition; overload;
function NewSprite(Bitmap : TBitmap; x, y, w, h : integer) :
TSpriteDefinition; overload;
procedure PaintTo(Canvas : TCanvas);
property Sprites[Index : integer] : TSpriteDefinition read GetSprite;
end;
TForm1 = class(TForm)
btnAdd: TButton;
btnDelete: TButton;
OpenDialog1: TOpenDialog;
PaintBox1: TPaintBox;
procedure btnAddClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btnDeleteClick(Sender: TObject);
procedure PaintBox1Paint(Sender: TObject);
private
{ Private declarations }
Sprites : TSpriteList;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btnAddClick(Sender: TObject);
begin
if OpenDialog1.Execute then
begin
Sprites.NewSprite(OpenDial og1.FileNa me, Random(200), Random(200), 20, 20);
PaintBox1.Invalidate;
end;
end;
procedure TForm1.btnDeleteClick(Send er: TObject);
begin
with Sprites do
if Count > 0 then
Delete(Count - 1);
PaintBox1.invalidate;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Sprites := TSpriteList.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Sprites.Free;
end;
procedure TForm1.PaintBox1Paint(Send er: TObject);
begin
Sprites.PaintTo(PaintBox1. Canvas);
end;
{ TMyBitmapDefinition }
constructor TSpriteDefinition.Create(x , y, Width, Height : integer);
begin
fBitmap := TBitmap.Create;
fBitmap.Width := Width;
fBitmap.Height := Height;
fX := x;
fY := y;
end;
destructor TSpriteDefinition.Destroy;
begin
fBitmap.Free;
inherited;
end;
function TSpriteDefinition.GetHeigh t: integer;
begin
Result := fBitmap.Height;
end;
function TSpriteDefinition.GetWidth : integer;
begin
Result := fBitmap.Width;
end;
procedure TSpriteDefinition.PaintTo( Canvas: TCanvas);
begin
Canvas.CopyRect(Rect(X, Y, X + Bitmap.Width, Y + Bitmap.Height),
Bitmap.Canvas, Rect(0, 0, Bitmap.Width, Bitmap.Height));
end;
procedure TSpriteDefinition.SetBitma p(const Value: TBitmap);
var
BMP : TBitmap;
begin
BMP := TBitmap.Create;
try
BMP.Width := Width;
BMP.Height := Height;
BMP.Canvas.CopyRect(Rect(0 , 0, Width, Height), Value.Canvas,
Rect(0, 0, Width, Height));
fBitmap.Assign(BMP);
finally
BMP.Free;
end;
end;
procedure TSpriteDefinition.SetHeigh t(const Value: integer);
begin
fHeight := Value;
Bitmap.Height := Value;
end;
procedure TSpriteDefinition.SetWidth (const Value: integer);
begin
fWidth := Value;
Bitmap.Width := Value;
end;
{ TBitmapList }
function TSpriteList.GetSprite(Inde x: integer): TSpriteDefinition;
begin
Result := TSpriteDefinition(Items[In dex]);
end;
function TSpriteList.NewSprite(File Name: string; x, y, w, h: integer):
TSpriteDefinition;
var
Bitmap : TBitmap;
begin
Bitmap := TBitmap.Create;
try
Bitmap.LoadFromFile(FileNa me);
Result := NewSprite(Bitmap, x, y, w, h);
finally
Bitmap.Free;
end;
end;
function TSpriteList.NewSprite(Bitm ap: TBitmap; x, y, w, h: integer):
TSpriteDefinition;
var
Sprite : TSpriteDefinition;
begin
Sprite := TSpriteDefinition.Create(x , y, w, h);
Sprite.Bitmap := Bitmap;
Add(Sprite);
Result := Sprite;
end;
procedure TSpriteList.PaintTo(Canvas : TCanvas);
var
I : integer;
Sprite : TSpriteDefinition;
begin
for I := 0 to Count - 1 do
begin
Sprite := Sprites[I];
Sprite.PaintTo(Canvas);
end;
end;
end.
-------------------------- ---------- ---------- ---------- ---------- ---------- ----------
Let me know if this helps.
--------------------------
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, contnrs, ExtCtrls;
type
{definition of these other classes would likely best be done in a separate
unit. They are defined here for simplicity.}
{This class holds the definition of the individual "sprites". You could add
properties for speed, iniertia, whatever. Keeping it simple, I started with
X and Y which are not natural for a bitmap to have.}
TSpriteDefinition = class
private
fBitmap : TBitmap;
fX, fY : integer;
fWidth, fHeight : integer;
function GetHeight: integer;
function GetWidth: integer;
procedure SetBitmap(const Value: TBitmap);
procedure SetHeight(const Value: integer);
procedure SetWidth(const Value: integer);
protected
public
constructor Create(x, y, Width, Height : integer);
destructor Destroy; override;
procedure PaintTo(Canvas : TCanvas);
property X : integer read fX;
property Y : integer read fY;
property Width : integer read GetWidth write SetWidth;
property Height : integer read GetHeight write SetHeight;
property Bitmap : TBitmap read fBitmap write SetBitmap;
end;
{this class makes it easier to manipulate the list}
TSpriteList = class(TObjectList)
private
function GetSprite(Index: integer): TSpriteDefinition;
protected
public
function NewSprite(FileName : string; x, y, w, h : integer) :
TSpriteDefinition; overload;
function NewSprite(Bitmap : TBitmap; x, y, w, h : integer) :
TSpriteDefinition; overload;
procedure PaintTo(Canvas : TCanvas);
property Sprites[Index : integer] : TSpriteDefinition read GetSprite;
end;
TForm1 = class(TForm)
btnAdd: TButton;
btnDelete: TButton;
OpenDialog1: TOpenDialog;
PaintBox1: TPaintBox;
procedure btnAddClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btnDeleteClick(Sender: TObject);
procedure PaintBox1Paint(Sender: TObject);
private
{ Private declarations }
Sprites : TSpriteList;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btnAddClick(Sender:
begin
if OpenDialog1.Execute then
begin
Sprites.NewSprite(OpenDial
PaintBox1.Invalidate;
end;
end;
procedure TForm1.btnDeleteClick(Send
begin
with Sprites do
if Count > 0 then
Delete(Count - 1);
PaintBox1.invalidate;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Sprites := TSpriteList.Create;
end;
procedure TForm1.FormDestroy(Sender:
begin
Sprites.Free;
end;
procedure TForm1.PaintBox1Paint(Send
begin
Sprites.PaintTo(PaintBox1.
end;
{ TMyBitmapDefinition }
constructor TSpriteDefinition.Create(x
begin
fBitmap := TBitmap.Create;
fBitmap.Width := Width;
fBitmap.Height := Height;
fX := x;
fY := y;
end;
destructor TSpriteDefinition.Destroy;
begin
fBitmap.Free;
inherited;
end;
function TSpriteDefinition.GetHeigh
begin
Result := fBitmap.Height;
end;
function TSpriteDefinition.GetWidth
begin
Result := fBitmap.Width;
end;
procedure TSpriteDefinition.PaintTo(
begin
Canvas.CopyRect(Rect(X, Y, X + Bitmap.Width, Y + Bitmap.Height),
Bitmap.Canvas, Rect(0, 0, Bitmap.Width, Bitmap.Height));
end;
procedure TSpriteDefinition.SetBitma
var
BMP : TBitmap;
begin
BMP := TBitmap.Create;
try
BMP.Width := Width;
BMP.Height := Height;
BMP.Canvas.CopyRect(Rect(0
Rect(0, 0, Width, Height));
fBitmap.Assign(BMP);
finally
BMP.Free;
end;
end;
procedure TSpriteDefinition.SetHeigh
begin
fHeight := Value;
Bitmap.Height := Value;
end;
procedure TSpriteDefinition.SetWidth
begin
fWidth := Value;
Bitmap.Width := Value;
end;
{ TBitmapList }
function TSpriteList.GetSprite(Inde
begin
Result := TSpriteDefinition(Items[In
end;
function TSpriteList.NewSprite(File
TSpriteDefinition;
var
Bitmap : TBitmap;
begin
Bitmap := TBitmap.Create;
try
Bitmap.LoadFromFile(FileNa
Result := NewSprite(Bitmap, x, y, w, h);
finally
Bitmap.Free;
end;
end;
function TSpriteList.NewSprite(Bitm
TSpriteDefinition;
var
Sprite : TSpriteDefinition;
begin
Sprite := TSpriteDefinition.Create(x
Sprite.Bitmap := Bitmap;
Add(Sprite);
Result := Sprite;
end;
procedure TSpriteList.PaintTo(Canvas
var
I : integer;
Sprite : TSpriteDefinition;
begin
for I := 0 to Count - 1 do
begin
Sprite := Sprites[I];
Sprite.PaintTo(Canvas);
end;
end;
end.
--------------------------
Let me know if this helps.
If you want to save/load a bitmap array consisting of bitmaps of all different sizes to/from a file I can provide code, but I am unsure if this is what you want/need/desire.
John