Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   TPngImage freigeben (https://www.delphipraxis.net/205273-tpngimage-freigeben.html)

amigage 20. Aug 2020 11:41

Delphi-Version: 10.4 Sydney

TPngImage freigeben
 
Hallo,

ich bin gerade dabei, die MemLeaks aus meinem Programm zu entfernen. Ein Problem habe ich hierbei:
Ich lade, je nach Bildschirmauflösung, verschieden große Grafiken über ein Funktion.

Delphi-Quellcode:
ImageMain.Picture.Graphic := StartDlg.LoadImgFromResource('Main');

function TStartDlg.LoadImgFromResource(Bezeichner: String): TPngImage;
var
  Png: TPngImage;
begin
  Result := NIL;

  Png := TPngImage.Create;
  try
   // DPI 200%
   if Screen.PixelsPerInch >= 192 then
     Png.LoadFromResourceName(HInstance, Bezeichner + '_200')
   // DPI 150%
   else if Screen.PixelsPerInch >= 144 then
     Png.LoadFromResourceName(HInstance, Bezeichner + '_150')
   // DPI 100%
   else
     Png.LoadFromResourceName(HInstance, Bezeichner + '_100');

    Result := Png;
  finally
//    Png.Free; // kann ich hier nicht freigeben
  end;
end;
Beim Beenden wird mir ein TPngImage und TPngImageList Memory Leak ausgegeben. Wie kann ich das am Ende freigeben? Muss ich die Grafik dem TImage anders übergeben, damit ich das TPngImage in der Funktion freigeben kann?

Danke, für einen Hinweis.

Bernhard Geyer 20. Aug 2020 11:48

AW: TPngImage freigeben
 
Entweder übergibst du ein TPNGImage an die Funktion.
Dann brauchst du keines Freigeben.

Oder die Methode erzeugt es und übergibt es. Dann ist der Aufrufer dafür zuständig.

Was wird denn mit dem PNG gemacht? Evtl. an ein TImage gehängt?
Dann solltest du das TImage als Parameter übergeben.
Den beim Zuordnen an das TImage wird eine Kopie des TPNGImage erstellt.

hoika 20. Aug 2020 11:52

AW: TPngImage freigeben
 
Hallo,
pack das TPngImage doch als private Variable in dein TStartDlg.
So wie du das hier erzeugst, sollte der Start-Dialog das dann selber freigeben.

amigage 20. Aug 2020 13:15

AW: TPngImage freigeben
 
Danke, ich habe mit für Bernhards Variante entschieden, da noch weitere Formulare auf die Funktion zugreifen.

Zitat:

Zitat von Bernhard Geyer (Beitrag 1472143)
Dann solltest du das TImage als Parameter übergeben.
Den beim Zuordnen an das TImage wird eine Kopie des TPNGImage erstellt.

Der Tipp war Gold wert. Danke 👌
Delphi-Quellcode:
StartDlg.LoadImgFromResource('Main', ImageMain);

procedure TStartDlg.LoadImgFromResource(Bezeichner: String; MyImage : TImage);
var
  Png: TPngImage;
begin
  Png := TPngImage.Create;
  try
   // DPI 200%
   if Screen.PixelsPerInch >= 192 then
     Png.LoadFromResourceName(HInstance, Bezeichner + '_200')
   // DPI 150%
   else if Screen.PixelsPerInch >= 144 then
     Png.LoadFromResourceName(HInstance, Bezeichner + '_150')
   // DPI 100%
   else
     Png.LoadFromResourceName(HInstance, Bezeichner + '_100');

    MyImage.Picture.Graphic := Png;
  finally
    Png.Free;
  end;
end;

haentschman 20. Aug 2020 13:31

AW: TPngImage freigeben
 
Moin...8-)
https://www.detlef-heibing.de/progra...rueckgabe.html
Prinzipiell kann man sowas machen...der constructor ist ja auch nix anderes. Aber der Name der function sollte beinhalten, daß ein object erzeugt wird! :warn:
Delphi-Quellcode:
function TStartDlg.LoadImgFromResource(Bezeichner: String): TPngImage;
...
function TStartDlg.CreateImgFromResource(Bezeichner: string): TPngImage;
...dann weiß man, daß man sich selbst um die Freigabe kümmern muß. Persönlich bekomme ich u.a. die Objekte aus der DB und packe sie in eine Liste. Die Liste übernimmt dann die Freigabe. :thumb:

himitsu 20. Aug 2020 17:21

AW: TPngImage freigeben
 
Besser den kleinsten gemeinsammen Nenner verwenden, also statt TImage das TPicture.
(Igentlich wäre das Kleineste TGraphic, aber für diesen Aufruf geht es nur bis TPicture)
Delphi-Quellcode:
StartDlg.LoadImgFromResource('Main', ImageMain.Picture);

procedure TStartDlg.LoadImgFromResource(Bezeichner: String; Image: TPicture);
So kann man die Funktion auch zum internen Laden benutzen, wo man keine große/sichtbare Komponente nutzen möchte.


Auch wenn nicht mehr nötig, nochmal als Erklärung:


Joar, da hier bei der Zuweisung nur den Inhalt des Objektes kopiert und nicht das Objekt übergeben/freigibt wird, mußt du das Result der Funktion am Ende freigeben.

Und wie haentschman bereits sagte, sollte man den Namen der Funktion anfassen, um das Verhalten auch gleich mit zu dokumentieren. (Create statt Load oder CreateAndLoadImageFormResource)

Delphi-Quellcode:
Image := StartDlg.CreateImgFromResource('Main');
try
  ImageMain.Picture.Graphic := Image; // entspricht ja eigentlich einem ImageMain.Picture.Graphic.Assign(Image);
finally
  Image.Free;
end;

function TStartDlg.CreateImgFromResource(Bezeichner: String): TPngImage;
begin
  Result := TPngImage.Create;
  try
    if Screen.PixelsPerInch >= 192 then
      Result.LoadFromResourceName(HInstance, Bezeichner + '_200')  // DPI 200%
    else if Screen.PixelsPerInch >= 144 then
      Result.LoadFromResourceName(HInstance, Bezeichner + '_150')  // DPI 150%
    else
      Result.LoadFromResourceName(HInstance, Bezeichner + '_100');  // DPI 100%
  except
    Result.Free; // wenn es beim Laden knallte, muß es hier schon freigegeben werden, da Result nicht aus fer Funktion raus kommt
    raise;
  end;
end;
Oder als Parameter: Objekt extern erstellen und dort freigeben.
Delphi-Quellcode:
Image := TPngImage.Create;
try
  LoadImgFromResource('Main', Image);
  ImageMain.Picture.Graphic := Image;
finally
  Image.Free;
end;

procedure TStartDlg.LoadImgFromResource(Bezeichner: String; Image: TPngImage);
begin
  if Screen.PixelsPerInch >= 192 then
    Image.LoadFromResourceName(HInstance, Bezeichner + '_200') // DPI 200%
  else if Screen.PixelsPerInch >= 144 then
    Image.LoadFromResourceName(HInstance, Bezeichner + '_150') // DPI 150%
  else
    Image.LoadFromResourceName(HInstance, Bezeichner + '_100'); // DPI 100%
end;

Entspricht deiner letzten Variante, außer
* TPicture statt TImage übergeben
* die Instanz wird nur erzeugt, wenn noch kein PNG vorhanden ist
* die Behandlung des Namen vom dem Ladeaufruf entkoppelt
Delphi-Quellcode:
LoadImgFromResource('Main', ImageMain.Picture);

function TStartDlg.LoadImgFromResource(Bezeichner: String; Picture: TPicture);
var
  Temp: TPngImage;
begin
  if Screen.PixelsPerInch >= 192 then
    Bezeichner := Bezeichner + '_200'  // DPI 200%
  else if Screen.PixelsPerInch >= 144 then
    Bezeichner := Bezeichner + '_150'  // DPI 150%
  else
    Bezeichner := Bezeichner + '_100'; // DPI 100%

  if not (Picture.Graphic is TPngImage) then begin // wenn TPngImage schon drin ist, muß man es nicht nochmal neu erzeugen
    Temp := TPngImage.Create;
    try
      Picture.Graphic := Temp; // ja, die Behandlung von Graphic ist echt krank, aber das liegt nicht an dir
    finally
      Temp.Free;
    end;
  end;

  //Picture.Graphic.LoadFromResourceName(HInstance, Bezeichner);
  TPngImage(Picture.Graphic).LoadFromResourceName(HInstance, Bezeichner); // vermutlich geht es auch direkt und wird durchgereicht, aber ich war mir grad nicht ganz sicher
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:56 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz