Jak poprawnie zwolnić obiekt z biblioteki QuickImageFX ?

0

Witajcie forumowicze. Zwracam się z prośbą o pomoc z pytaniem jak w temacie.
Biblioteka dostępna https://github.com/exilon/QuickImageFX

Kod testowy:


unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Quick.ImageFX, Quick.ImageFX.GR32, Quick.ImageFX.GDI,
  Quick.ImageFX.Types, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var ImageFX : IImageFX;
    ms1,ms2: TMemoryStream;
begin
 try
  ms1:= TMemoryStream.Create;
  ms2:= TMemoryStream.Create;

  ms1.LoadFromFile('papagayo.jpg');
  ms1.Position:=0;

  ImageFX := TImageFXGR32.Create;
  ImageFX.LoadFromStream(ms1);
  ImageFX.Resize(400, 300, rmStretch, [rfNoMagnify], rsNearest);
  ImageFX.SaveToStream(ms2, ifJPG);

  ms2.Position:=0;
  ms2.SaveToFile('out.jpg');

 finally
  ms1.Free;
  ms2.Free;
 end;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 ReportMemoryLeaksOnShutdown:=true;
end;

end.

0

Ale Ty nigdzie tego obiektu nie zwalniasz, więc nie dziwne, że dostajesz wyciek pamięci. Dorzuć destrukcję do finally:

  {..}
finally
  ImageFX.Free(); // tu się zwalnia

  ms1.Free();
  ms2.Free();
end;
0

@furious programming: No właśnie nie jest to takie proste jak by się wydawało ;) zobacz na var ImageFX : IImageFX; to jest interfejs. Tak zresztą autor podaje na stronie github-a którą podlinkowałem.

0

Ooo, faktycznie — I wygląda niemal identycznie jak T i nie zauważyłem. :D

No ale czekaj, to masz wycieki czy nie masz? Bo w sumie to nie napisałeś w czym problem, czyli co się dzieje obecnie. Trochę też dziwi mnie zapis, jaki podaje w instrukcji autor:

var
  ImageFX : IImageFX;
begin
  ImageFX := TImageFXGDI //You can create as TImageFXGDI, TImageFXGR32, TImageFXOpenCV or TImageFXVampyre to use different graphic engines
  ImageFX.LoadFromFile('.\test.jpg');
  ImageFX.Rotate90;
  ImageFX.SaveAsPNG('.\Test.png');
end;

W pierwszej linijce przypisuje klasę, a nie tworzy jej instancję. Nie wiem jak to rozumieć — czy to jest uproszczenie na potrzeby prezentacji (które niczego nie upraszcza), czy faktyczny zapis poprawny składniowo.

0

Dokładnie mam wyciek pamięci TJPEGData i TJPEGImage.

Nie da się przypisać do zmiennej typu ImageFX:=TImageFXGDI
samej klasy bez „Create”;

Próbowałem w finally konstrukacje typu: TImageFXGR32(ImageFX).Free;
co zaś generuje błąd „Invalid pointer operation”

ps2. sprawdziłem na Lazarus-ie się nie skompiluje, zbiła mnie z tropu biblioteka Graphics32 :/

0

Niestety ten twór obiektowo-interfejsowy nie posiada takiej metody jak Release.

Natomiast na 95% jestem pewien że jest to błąd w tej bibliotece bo jak nie wywołam ImageFX.SaveToStream(ms2, ifJPG); to wycieku nie raportuje.

3

Zgadza w pliku Quick.ImageFX.GR32.pas linia 1139 jest funkcja, którą należy poprawić:

procedure TImageFXGR32.SaveToStream(stream : TStream; imgFormat : TImageFormat = ifJPG);
var
  graf : TGraphic;
begin
  if stream.Position > 0 then stream.Seek(0,soBeginning);

  case imgFormat of
    ifBMP:
      begin
        //graf := TBitmap.Create; //Tu ten sam błąd w przypadku BMP
        try
          graf := Self.AsBitmap;
          graf.SaveToStream(stream);
        finally
          graf.Free;
        end;
      end;
    ifJPG:
      begin
        //graf := TJPEGImage.Create; //<--- To robi wyciek
        try
          graf := Self.AsJPG;
          graf.SaveToStream(stream);
        finally
          graf.Free;
        end;
      end;
    ifPNG:
      begin
        //graf := TPngImage.Create; //Tu ten sam błąd w przypadku PNG
        try
          graf := Self.AsPNG;
          graf.SaveToStream(stream);
        finally
          graf.Free;
        end;
      end;
    ifGIF:
      begin
       //graf := TGIFImage.Create; //Tu ten sam błąd w przypadku GIF
        try
          graf := Self.AsGIF;
          graf.SaveToStream(stream);
        finally
          graf.Free;
        end;
      end;
  end;
end;

Funkcje As* tworzą obiekt i min. przypisują (przez Assign) do niego "roboczą" bitmapę po czym go zwracają więc nie można go tak sobie przypisać do tego utworzonego, bo zgubimy ten tworzony w SaveToStream . Po prostu w funkcji SaveToStream nie trzeba tworzyć a tylko zwolnić obiekty utworzony w metodzie As*. Po tej poprawce w podanym kodzie wycieku nie ma ale trzeba testować kod czy nie ma więcej podobnych przypadków.

Właściwie całość można sprowadzić do:

procedure TImageFXGR32.SaveToStream(stream : TStream; imgFormat : TImageFormat = ifJPG);
var
  graf : TGraphic;
begin
  stream.Size:= 0;
  try
    case imgFormat of
      ifBMP: graf:= Self.AsBitmap;
      ifJPG: graf:= Self.AsJPG;
      ifPNG: graf:= Self.AsPNG;
      ifGIF: graf:= Self.AsGIF;
    end;
    graf.SaveToStream(stream);
  finally
    graf.Free;
  end;
end;
0

kAzek, dziękuję dokładnie to powodowało wyciek.

1 użytkowników online, w tym zalogowanych: 0, gości: 1