Board index » delphi » Re: Memory leak with TImage

Re: Memory leak with TImage


2006-11-12 01:50:56 AM
delphi268
On Sat, 11 Nov 2006 13:03:23 +0100, David J Taylor writes:
Quote
Is this the correct way to do this:

FMyBitmap := nil;
ABitmap := TMyBitmap.Create;
try
ABitmap.PixelFormat := pf24bit;
ABitmap.Width := 1024;
ABitmap.Height := 1024;
Image1.Picture.Bitmap := ABitmap;
FMyBitmap := TMyBitmap (Image1.Picture.Bitmap);
In this case Image1.Picture.Bitmap will be of type TBitmap, so your
typecast is going to cause trouble.
What do you think about this?
type
TMyBitmap = class(TBitmap)
private
FHistogram: TObject;
public
constructor Create; override;
destructor Destroy; override;
procedure Test;
end;
{$IF compilerversion>= 18} // Delphi 2006+
TPictureHelper = class helper for TPicture
public
function MyBitmap: TMyBitmap;
end;
{$IFEND}
implementation
{$R *.dfm}
{ TMyBitmap }
constructor TMyBitmap.Create;
begin
inherited;
FHistogram := TObject.Create;
end;
destructor TMyBitmap.Destroy;
begin
FreeAndNil(FHistogram);
inherited;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
{$IF compilerversion>= 18}
Image1.Picture.MyBitmap.Test;
{$ELSE}
if Image1.Picture.Bitmap is TMyBitmap then
TMyBitmap(Image1.Picture.Bitmap).Test;
{$IFEND}
end;
procedure TForm1.FormCreate(Sender: TObject);
var
MyBitmap: TBitmap;
begin
{$IF compilerversion>= 18}
Image1.Picture.MyBitmap.PixelFormat := pf24bit;
Image1.Picture.MyBitmap.Width := 100;
Image1.Picture.MyBitmap.Height := 100;
{$ELSE}
MyBitmap := TMyBitmap.Create;
try
MyBitmap.PixelFormat := pf24bit;
MyBitmap.Width := 100;
MyBitmap.Height := 100;
Image1.Picture.Graphic := MyBitmap;
finally
FreeAndNil(MyBitmap);
end;
{$IFEND}
end;
procedure TMyBitmap.Test;
begin
Canvas.Rectangle(1,1,20, 20);
end;
{$IF compilerversion>= 18} // Delphi 2006+
{ TPictureHelper }
function TPictureHelper.MyBitmap: TMyBitmap;
var
Tmp: TMyBitmap;
begin
if not (Graphic is TMyBitmap) then
begin
Tmp := TMyBitmap.Create;
try
Graphic := Tmp;
finally
FreeAndNil(Tmp);
end;
end;
Result := TMyBitmap(Graphic);
end;
{$IFEND}
If you have Delphi 2006 then you can use class helpers and "add" MyBitmap
to TPicture. You might want to override the Assign method of TMyBitmap.
--
Sebastian
 
 

Re: Memory leak with TImage

Quote
FMyBitmap := nil;
ABitmap := TMyBitmap.Create;
try
ABitmap.PixelFormat := pf24bit;
ABitmap.Width := 1024;
ABitmap.Height := 1024;
Image1.Picture.Bitmap := ABitmap;
FMyBitmap := TMyBitmap (Image1.Picture.Bitmap);
finally
ABitmap.Free;
end;
Why would you want to use FMyBitmap and ABitmap at the same time? Why not
FMyBitmap := TMyBitmap.Create;
try
FMyBitmap.PixelFormat := pf24bit;
FMyBitmap.Width := 1024;
FMyBitmap.Height := 1024;
Image1.Picture.Bitmap := FMyBitmap;// or
Image1.Picture.Bitmap.Assign(FMyBitmap);
finally
FMyBitmap.Free;
end;
You're making it way more complicated than it should be :)
Nils
 

Re: Memory leak with TImage

On Sat, 11 Nov 2006 12:03:23 -0000, "David J Taylor"
<XXXX@XXXXX.COM>writes:
Quote
I then want to display on the screen the results of manipulating a bitmap
using the new class I have created
I'd just use a TPaintBox, and draw your bitmap in its OnDraw method.
Alternatively write a descendant of TPaintBox.
- Asbjørn
 

Re: Memory leak with TImage

I just filed a QC report requesting better documentation for TImage:
qc.borland.com/wc/qcmain.aspx
--JohnH
 

Re: Memory leak with TImage

Nils Haeck writes:
[]
Quote
Why would you want to use FMyBitmap and ABitmap at the same time? Why
not
FMyBitmap := TMyBitmap.Create;
try
FMyBitmap.PixelFormat := pf24bit;
FMyBitmap.Width := 1024;
FMyBitmap.Height := 1024;
Image1.Picture.Bitmap := FMyBitmap;// or
Image1.Picture.Bitmap.Assign(FMyBitmap);
finally
FMyBitmap.Free;
end;

You're making it way more complicated than it should be :)

Nils
I seem to recall that if I did something like this, the changes I made in
FMyBitmap were /not/ reflected by the TImage component. It was essential
that I had:
FMyWorkingBitmap := TMyBitmap (Image1.Picture.Bitmap);
and make changes to FMyWorkingBitmap rather than trying to use FMyBitmap
(as in your example) throughout the rest of the program. It was some time
ago when I first tried this, though.
Cheers,
David
 

Re: Memory leak with TImage

Sebastian Zierer writes:
Quote
On Sat, 11 Nov 2006 13:03:23 +0100, David J Taylor writes:

>Is this the correct way to do this:
>
>FMyBitmap := nil;
>ABitmap := TMyBitmap.Create;
>try
>ABitmap.PixelFormat := pf24bit;
>ABitmap.Width := 1024;
>ABitmap.Height := 1024;
>Image1.Picture.Bitmap := ABitmap;
>FMyBitmap := TMyBitmap (Image1.Picture.Bitmap);

In this case Image1.Picture.Bitmap will be of type TBitmap, so your
typecast is going to cause trouble.
How will it cause trouble? Are you saying that I can not access the new
methods and fields in FMyBitmap? Yes, that is what I thought but it
acutally seems to work OK.
Quote
What do you think about this?
Sorry, but I wasn't sure what it was supposed to do, and with all the
IFDEFs I couldn't follow it. I am using Delphi 5, by the way.
Cheers,
David
 

Re: Memory leak with TImage

Lord Crc writes:
Quote
On Sat, 11 Nov 2006 12:03:23 -0000, "David J Taylor"
<XXXX@XXXXX.COM>writes:

>I then want to display on the screen the results of manipulating a
>bitmap using the new class I have created

I'd just use a TPaintBox, and draw your bitmap in its OnDraw method.
Alternatively write a descendant of TPaintBox.

- Asbjørn
Asbjørn,
I actually have my own TPaintBox (to handle the MouseEnter and MouseLeave
events), but the simple typecast seems to work OK and cure the memory
leak. It still surprises me slightly that I can typecase a TBitmap into a
TMyBitmap, but I do just the same thing with other classes so perhaps I
shouldn't be surprised!
Thanks for your help.
David
 

Re: Memory leak with TImage

John Herbster writes:
Quote
I just filed a QC report requesting better documentation for TImage:
qc.borland.com/wc/qcmain.aspx
--JohnH
I'm not holding my breath, but many thanks! I have added my vote.
Cheers,
David
 

Re: Memory leak with TImage

On Sun, 12 Nov 2006 08:06:29 -0000, "David J Taylor"
<XXXX@XXXXX.COM>writes:
Quote
It still surprises me slightly that I can typecase a TBitmap into a
TMyBitmap, but I do just the same thing with other classes so perhaps I
shouldn't be surprised!
The reason it works must be because you don't store any extra data in
TMyBitmap, or you don't use methods that uses that data after you've
done the typecast. I would consider this a {*word*193} hack and avoid it like
the plague.
- Asbjørn
 

Re: Memory leak with TImage

Lord Crc writes:
Quote
On Sun, 12 Nov 2006 08:06:29 -0000, "David J Taylor"
<XXXX@XXXXX.COM>writes:

>It still surprises me slightly that I can typecast a TBitmap into a
>TMyBitmap, but I do just the same thing with other classes so
>perhaps I shouldn't be surprised!

The reason it works must be because you don't store any extra data in
TMyBitmap, or you don't use methods that uses that data after you've
done the typecast. I would consider this a {*word*193} hack and avoid it like
the plague.

- Asbjørn
Asbjørn, yes I store extra data (the FHistogram class), and I just tried
calling the EqualiseHistogram method which uses data in that field, and it
works just fine.
I don't like doing this either.
David
 

Re: Memory leak with TImage

David J Taylor writes:
Quote

Let me rephrase the question, then. I am not concerned in the least
with the internal workings of TImage, TPicture etc.
But you should be. Unless you understand how these components work your
will continue to make the same errors again. As witnessed by your
example code:
Quote
FMyBitmap := nil;
ABitmap := TMyBitmap.Create;
try
ABitmap.PixelFormat := pf24bit;
ABitmap.Width := 1024;
ABitmap.Height := 1024;
Image1.Picture.Bitmap := ABitmap;
FMyBitmap := TMyBitmap (Image1.Picture.Bitmap);
That simply does not work! Image1.Picture.Bitmap is *not* a TMyBitmap,
and you can try to convince the compiler that it is until you are blue
in the face, it will not spontaneously mutate into TMyBitmap. Which
means that this construct (even though it appears to work) will blow up
spectacularly the first time you try to call a method on FMyBitmap that
exists in your TBitmap class but not in the original TBitmap class.
Quote
with the intention that I use FMyBitmap throughout the program to
load images in. manipulate them and resize them, with the result
automatically appearing on the screen?
No chance if you want to use the additional functionality you have
build into the TMyBitmap class. The way to do this is to work with the
instance of TMyBitmap you create somewhere and store into FMyBitmap,
and then, when you have prepared an image to your satisfaction, do a
image1.picture.bitmap.Assign(FMyBitmap);
to copy the pixel data to the images internal bitmap. For this to work
your class needs to implement the Assign or AssignTo methods. It
inherits such methods from TBitmap, so that may be enough. But whether
it actually is enough depends on the modifications you made to the
class.
--
Peter Below (TeamB)
Don't be a vampire (slash7.com/pages/vampires),
use the newsgroup archives :
www.tamaracka.com/search.htm
groups.google.com
www.prolix.be
 

Re: Memory leak with TImage

Peter Below (TeamB) writes:
Quote
David J Taylor writes:

>
>Let me rephrase the question, then. I am not concerned in the least
>with the internal workings of TImage, TPicture etc.

But you should be. Unless you understand how these components work
your will continue to make the same errors again. As witnessed by your
example code:

>FMyBitmap := nil;
>ABitmap := TMyBitmap.Create;
>try
>ABitmap.PixelFormat := pf24bit;
>ABitmap.Width := 1024;
>ABitmap.Height := 1024;
>Image1.Picture.Bitmap := ABitmap;
>FMyBitmap := TMyBitmap (Image1.Picture.Bitmap);

That simply does not work! Image1.Picture.Bitmap is *not* a TMyBitmap,
and you can try to convince the compiler that it is until you are blue
in the face, it will not spontaneously mutate into TMyBitmap. Which
means that this construct (even though it appears to work) will blow
up spectacularly the first time you try to call a method on FMyBitmap
that exists in your TBitmap class but not in the original TBitmap
class.
Well, it /does/ work perfectly with Delphi 5.
Quote
>with the intention that I use FMyBitmap throughout the program to
>load images in. manipulate them and resize them, with the result
>automatically appearing on the screen?

No chance if you want to use the additional functionality you have
build into the TMyBitmap class. The way to do this is to work with the
instance of TMyBitmap you create somewhere and store into FMyBitmap,
and then, when you have prepared an image to your satisfaction, do a

image1.picture.bitmap.Assign(FMyBitmap);

to copy the pixel data to the images internal bitmap. For this to work
your class needs to implement the Assign or AssignTo methods. It
inherits such methods from TBitmap, so that may be enough. But whether
it actually is enough depends on the modifications you made to the
class.
Yes, I can see that it is the right way. Let me see if I can concoct a
simple program to demonstrate that this really does work (at least with
Delphi 5).
I've done that and posted it in .attachments. Can you explain why this
does work when you think it should not? Perhaps I still haven't explained
well enough what I am trying to do, or I haven't understood well enough
what you are saying!
Thanks,
David
 

Re: Memory leak with TImage

Quote
Asbjørn, yes I store extra data (the FHistogram class), and I just tried
calling the EqualiseHistogram method which uses data in that field, and it
works just fine.
What you're doing is playing with fire. It might work until you start to use
another memory manager, or it might suddenly crash on some user's computer
who is a doctor and uses it for a medical application and people die as a
result :)
In other words: don't do it. If you want to do such thing why not write an
accessor class
TMyBitmapAccessor = class
private
FBitmap: TBitmap;
FHisto: array[0..255] of integer;
public
property Bitmap: TBitmap read FBitmap write FBitmap; // point this one to
Image.Picture.Bitmap
procedure HistogramMethod;
end;
procedure TMyBitmapAccessor.HistogramMethod;
begin
if not assigned(FBitmap) then exit;
// do stuff with the bitmap here
if FBitmap.Pixels[0, 0] = ... // etc
end;
If you *still* want to descend from TBitmap, then you can also just reassign
the TMyBitmap to the image whenever it has changed. You can even use the
OnChange event for that.
Nils
 

Re: Memory leak with TImage

Nils Haeck writes:
Quote
>Asbjørn, yes I store extra data (the FHistogram class), and I just
>tried calling the EqualiseHistogram method which uses data in that
>field, and it works just fine.

What you're doing is playing with fire. It might work until you start
to use another memory manager, or it might suddenly crash on some
user's computer who is a doctor and uses it for a medical application
and people die as a result :)

In other words: don't do it. If you want to do such thing why not
write an accessor class

TMyBitmapAccessor = class
private
FBitmap: TBitmap;
FHisto: array[0..255] of integer;
public
property Bitmap: TBitmap read FBitmap write FBitmap; // point this
one to Image.Picture.Bitmap
procedure HistogramMethod;
end;

procedure TMyBitmapAccessor.HistogramMethod;
begin
if not assigned(FBitmap) then exit;
// do stuff with the bitmap here
if FBitmap.Pixels[0, 0] = ... // etc
end;

If you *still* want to descend from TBitmap, then you can also just
reassign the TMyBitmap to the image whenever it has changed. You can
even use the OnChange event for that.

Nils
Nils,
I appreciate your comments. Would you agree that the version I have
posted as "Bitmap Helper Class test for Nils Haeck" is acceptable and
correct?
I would just add the the code I posted before works fine in Delphi 5, with
both the standard and the FastMM memory managers, and has been in use
world-wide for the past serveral years, 24 x 7 operation, with no
problems. And, yes, there is a caveat in the read-me about not being used
in life-critical or safety critical situations!
Cheers,
David
 

Re: Memory leak with TImage

David J Taylor writes:
Quote

Well, it does work perfectly with Delphi 5.

Yes, I can see that it is the right way. Let me see if I can concoct
a simple program to demonstrate that this really does work (at least
with Delphi 5).

I've done that and posted it in .attachments. Can you explain why
this does work when you think it should not?
Simple: because I was wrong <g>. I never pays to rely on memory without
checking the source <sigh>. As it turns out TPicture is smarter than I
remembered. Assigning to picture.bitmap calls the SetBitmap method,
which directly calls the SetGraphic method. Which does this:
procedure TPicture.SetGraphic(Value: TGraphic);
var
NewGraphic: TGraphic;
begin
NewGraphic := nil;
if Value <>nil then
begin
NewGraphic := TGraphicClass(Value.ClassType).Create;
NewGraphic.Assign(Value);
That is the key, the TPicture class does create an internal instance,
but it creates an instance of the class you use in the assignment
statement, not of TBitmap. So the statement
image.picture.bitmap := amyBimap;
is *not* equivalent to
image.picture.bitmap.Assign(aMyBitmap);
That would indeed create a TBitmap since it invokes the
TPicture.GetBitmap method (which creates a TBitmap internally if the
GRaphics property does not contain one), and then calls the Assign
method of the returned instance.
Sorry to lead you astray, mea culpa....
--
Peter Below (TeamB)
Don't be a vampire (slash7.com/pages/vampires),
use the newsgroup archives :
www.tamaracka.com/search.htm
groups.google.com
www.prolix.be