Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Seltsames Grafik-Phänomen - oder wie ich lernte, DELPHI zu "lieben"... (https://www.delphipraxis.net/183715-seltsames-grafik-phaenomen-oder-wie-ich-lernte-delphi-zu-lieben.html)

hathor 30. Jan 2015 16:14


Seltsames Grafik-Phänomen - oder wie ich lernte, DELPHI zu "lieben"...
 
Liste der Anhänge anzeigen (Anzahl: 1)
Seltsames Grafik-Phänomen - oder wie ich lernte, DELPHI zu "lieben"...

Eine kleine Grafik soll 80 x kopiert werden.
Das dauert etwa 12 sec auf meinem Notebook.
Wenn die fertige Grafik gelöscht wird mit FloodFill, dann dauert der Neu-Aufbau nur etwa 3 sec.

Hat jemand eine Erklärung dafür?

Im Anhang ist die EXE:
Bedienung:
Start: baut die Grafik auf
CheckBox SpeedUp beschleunigt den Grafik-Aufbau.
Reset: löscht die Grafik

Delphi-Quellcode:
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    ProgressBar1: TProgressBar;
    Button3: TButton;
    Memo1: TMemo;
    cbSpeedUp: TCheckBox;
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormPaint(Sender: TObject);
    procedure TerminatedProc(Sender:TObject);
    procedure Button3Click(Sender: TObject);
    procedure FormShow(Sender: TObject);

  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

   type
      TCopyThread = Class(TThread)
      private
        FPoint:TPoint;
        procedure UpdateProgressBar;
      protected
        procedure Execute;Override;
      public
        Constructor Create(p:TPoint);
      End;

var
  Form1: TForm1;
  CopyThread:TCopyThread;
  BitMap1 : TBitMap;
  GTC1, GTC2 : Cardinal;

implementation

{$R *.dfm}

procedure MM(s:string); begin Form1.Memo1.Lines.Add(s); end;

procedure TForm1.Button1Click(Sender: TObject);
var p:TPoint;
begin
if Button1.Tag=1 then Button3.Click; Button1.Tag:= 1;
if cbSpeedUp.Checked then
BEGIN
  Self.Canvas.FloodFill(ClientWidth div 2, ClientHeight div 2, clBlack, fsBorder);
  Self.Canvas.Draw(0,0,BitMap1);
END;

GTC1:= GetTickCount;
  p.X:= BitMap1.Width;
  P.Y:= 0;
  CopyThread:=TCopyThread.Create(p);
  CopyThread.OnTerminate:= TerminatedProc;
  ProgressBar1.Max:= BitMap1.Width*80 * BitMap1.Height;
  ProgressBar1.Position:= 0;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin if Assigned(CopyThread) then begin CopyThread.Terminate; end; end;

procedure TForm1.Button3Click(Sender: TObject);
var MyWindow : cardinal;
begin
  ProgressBar1.Position:= 0;
  Form1.WindowState:= wsMinimized;
  Sleep(100);
  MyWindow := FindWindow( Nil, PWIDECHAR('GRAFIKDEMO-201501'));
  If MyWindow <> 0 Then
  Begin
    ShowWindow(MyWindow, SW_Normal);
    SetForeGroundWindow(MyWindow);
  End;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin BitMap1:= TBitmap.Create; BitMap1.LoadFromFile('BG_Kacheln1.bmp'); end;

procedure TForm1.FormDestroy(Sender: TObject); begin BitMap1.Free; end;

procedure TForm1.FormPaint(Sender: TObject);
begin Self.Canvas.Draw(0,0,BitMap1); end;

procedure TForm1.FormShow(Sender: TObject);
begin Application.Title := 'GRAFIKDEMO-201501'; end;

procedure TForm1.TerminatedProc;
begin
GTC2:= GetTickCount;
MM(' Dauer: '+ INTTOSTR(GTC2-GTC1)+' ms');
Sleep(100); WinApi.Windows.Beep(1800,100);
end;

{ TCopyThread }
constructor TCopyThread.Create(p: TPoint);
begin FPoint:=p; inherited Create(False); end;

procedure TCopyThread.Execute;
var
  i,j:Integer;
  dot:TColorRef;
  DC:HDC;
begin
  FreeOnTerminate:=True;
  DC:=GetDC(Form1.Handle);
  for i := 0 to BitMap1.Width * 80 - 1 do
    for j := 0 to BitMap1.Height - 1 do
      if not Terminated then
         begin
           dot:=GetPixel(DC,i,j);
           SetPixel(DC,i + FPoint.X,j + FPoint.Y,dot);
           Synchronize(UpdateProgressBar);
         end;
end;

procedure TCopyThread.UpdateProgressBar;
begin Form1.ProgressBar1.Position:= Form1.ProgressBar1.Position +1; end;

end.

Medium 30. Jan 2015 16:32

AW: Seltsam - oder wie ich lernte, DELPHI zu "lieben"...
 
Sind GetPixel() und SetPixel() nicht am Ende das gleiche wie TCanvas.Pixels[]? Warum kopierst du nicht "ordentlich" mit BitBlt()? Und warum der Thread, der zudem auch noch auf einem nicht gelockten Canvas/DC rumhantiert? (Deinen Code-Style finde ich persölich auch eher zum weglaufen, schwer zu lesen.)

Edit: Noch etwas: Du setzt die Progressbar im Thread für JEDEN Pixel neu. Das frisst UNMENGEN Zeit.

Neutral General 30. Jan 2015 16:37

AW: Seltsam - oder wie ich lernte, DELPHI zu "lieben"...
 
Hallo,

Also abgesehen davon dass dein Code so wie du ihn formatierst absolut unlesbar ist und ich mir nicht sicher bin ob ich dein Problem richtig verstanden habe:

Natürlich ist ein Canvas.Draw tausend mal schneller als ein Thread (der in dem Fall ziemlich unnötig und auch durch den Zugriff auf Form1.Handle potenziell gefährlich ist) in dem GetPixel und SetPixel benutzt wird (die mit Abstand langsamsten Funktionen wenn es um das auslesen/setzen von Pixeln geht).
Ach ja und das Synchronize ist wie Medium auch schon gesagt hat wahrscheinlich nochmal für 30-50% Geschwindigkeitsverlust verantwortlich.

hathor 30. Jan 2015 16:45

AW: Seltsam - oder wie ich lernte, DELPHI zu "lieben"...
 
Ohne Synchronize(UpdateProgressBar); ist der Geschwindigkeitsunterschied noch größer: 10 sec zu 1 sec...

Aber bisher habe ich keine Erklärung dafür, dass ein FloodFill so eine Auswirkung hat!

Medium 31. Jan 2015 12:32

AW: Seltsames Grafik-Phänomen - oder wie ich lernte, DELPHI zu "lieben"...
 
Hat es auch nicht. Bei mir:
Zitat:

Dauer: 3588 ms
Dauer: 3635 ms
Dauer: 3651 ms
Dauer: 3635 ms
Die ersten 2 Durchläufe ohne Speedup, die letzten beiden mit.

Aber das alles wird komplett bedeutungslos, wenn du das Zeichnen mit vernünftigen Methoden machst, und diesen Thread inkl. der Progressbar los wirst.

Bernhard Geyer 31. Jan 2015 12:59

AW: Seltsames Grafik-Phänomen - oder wie ich lernte, DELPHI zu "lieben"...
 
Windows ist kein Echtzeit-OS. Und wenn du dein Thread geschätzt 1 Mio. mal jeweils nach 1 ns unterbrichst kann hier jegliche Windows-Aktion (Virenscanner wird aktualisiert. Festplattendefraktmentierung startet, ...) dafür sorgen das die Zeiten sehr weit streuen. Vermutlich wird ein entsprechend ungünstig implementiertes .NET/Java-Programm genau die gleichen Zeiten liefern.

Also bau erstmal dein Programm um (Bessere Zugriffsfunktionen auf Zeichenfläche von Bitmap, kein tausendfaches Synchronize, ...) und schau dann ob diese Streuung noch auftritt.

Sir Rufo 1. Feb 2015 11:59

AW: Seltsames Grafik-Phänomen - oder wie ich lernte, DELPHI zu "lieben"...
 
Bevor ich eine Teilaufgabe in einen Thread auslagere mache ich mir Gedanken darüber, wie das in der Realität aussehen würde.

Statt Threads nehme ich dann einfach Mitarbeiter.
Statt einem Pixel nehme ich einen Kieselstein.

Der Mitarbeiter soll also einen ganzen Haufen Kieselsteine von A nach B bewegen.

Möchte ich wirklich, dass der bei jedem einzelnen Kieselstein bei mir vorbeikommt und mich persönlich (Sychronize) informiert, dass er einen weiteren Kieselstein bewegt hat?
Er könnte mir ja auch eine Nachricht hinterlassen (Queue) die ich mir dann bei nächster Gelegenheit anschaue.

Aber will ich wirklich über jeden einzelnen eine Nachricht habe? Oder würden mir ein paar Rückmeldungen auch reichen (alle 10% Fortschritt)?

Wenn man das so durchspielt, dann weiß man, wo man selbst und der Mitarbeiter gestresst/gehemmt ist und das trifft exakt auf die GUI und den Thread zu. Das zusammen mit einer umständlichen Arbeitsweise (GetPixel) bringt jedes System in die Knie.


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:45 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