Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Schnelleres LineTo (https://www.delphipraxis.net/73924-schnelleres-lineto.html)

ChrisE 25. Jul 2006 18:29


Schnelleres LineTo
 
Hallo,

ich weiß nicht, ob ich hier in "Programmieren allgemein" richtig bin, aber ich beschreibe mal mein Problem.
Gegeben seien Messwerte (8 Stunden / 200 Hz / 12 Kanäle). Die Darstellung der Kanäle Erfolgt auf einer Paintbox. Abgesehen von der Skalierung der Wert in X- und Y-Richtung (bereits gelöst), bleibt das Problem, dass dieser Ansatz:
Delphi-Quellcode:
with PaintBox1.Canvas do begin
  MoveTo(0, WertToY(Wert[0]));
  for i := 0 to high(Wert) do
    LineTo(IndexToX(i), WertToY(Wert[i]));
end;
zu lange dauert. Das Problem ist natürlich die sehr große Anzahl (~ 12 * 5,76 Mio) von LineTo's.
Folgendes habe ich bereits Probiert:
1. PolyLine von TCanvas
Nicht wirklich schneller als der Code oben. Eher langsamer

2. Nur alle n-ten Werte Zeichnen die auf eine X-Koordiante fallen
Problem: Die Anzahl zu verringen geht so in erster Näherung nicht, das in den Werten Peaks sein können (...0,0,3,205678.789,4,5,6,7....).

3. Das Min und Max aller Werte die auf eine X-Koordinate Fallen bestimmen und dann MoveTo(X,Min), LineTo(X,Max);
Problem: Das Suchen in den Werten braucht auch seine Zeit.

Nun die Frage, gibt es eine ganz anderen Ansatz eine solchen "Linienwald" mit höherer Performance zu zeichnen?

Vielen Dank für Eure mühe.

Mfg, Chris

P.S.: Sollte auch unter Win98 funktionieren :-)

mkinzler 25. Jul 2006 18:36

Re: Schnelleres LineTo
 
Vielleicht hilft das Manuelle Zeichen der Geraden unter Zuhilfenahme des Bresenham-Algorithmuses.

Dax 25. Jul 2006 18:40

Re: Schnelleres LineTo
 
Du könntest aber auch statt einer Paintbox ein Image nehmen, das dortige Bitmap auf die gewünschte Größe bringen und auf das Bitmap des Images zeichnen. Müsste glaube ich auch schneller sein.

SirThornberry 25. Jul 2006 18:43

Re: Schnelleres LineTo
 
So wie ich das sehe wird direkt auf die Canvas von der Paintbox gezeichnet. Nach jedem LineTo wird somit die Canvas ausgegeben (auf dem Bildschirm) was eine Ewigkeit dauert. Zeischne lieber auf ein TBitmap.Canvas und wenn du dann fertig bist gebe das Bitmap mit BitBlt (oder einer dir beliebigen Methode) auf der entsprechenden Canvas auf dem Bildschirm aus.

himitsu 25. Jul 2006 18:54

Re: Schnelleres LineTo
 
ich glaub das mit dem TBitMap, als Zwischenspeicher, dann noch ScanLines kombiniert mit dem vorherigen Berechnen der MinMaxWerte sollte so das schnellste auf diesme Wege sein.

klar dauert es, wenn man sich minMaxWerte berechnet, aber es sollte alle Male schneller sein, also alles zu zeichnen :zwinker:

paritycheck 25. Jul 2006 19:00

Re: Schnelleres LineTo
 
Als Alternative könnte man das ganze evtl auch mit OpenGL auf ein Panel rendern.

arbu man 25. Jul 2006 19:06

Re: Schnelleres LineTo
 
OpenGL Scene ist schonmal nicht schlecht bei GLScene gibt es eine Klasse TGLCanvas die behersch eigentlich alles was canvas kann, nur das sie wesentlich schneller ist :)

Jens Schumann 25. Jul 2006 19:13

Re: Schnelleres LineTo
 
Wie wäre es denn mit der TeeChart Komponente?
Mit der Komponente habe ich in Sachen Geschwindigkeit sehr gute Erfahrungen gemacht.

ChrisE 25. Jul 2006 21:57

Re: Schnelleres LineTo
 
Hallo an alle,

erstmal danke für die Rege Teilnahme an dieser Frage :-)

@mkinzler:
Wenn ich Wikipedia da richtig verstehe, ist das in den meisten Treibern bereits implementiert -> LineTo müsste sogar schon mit diesem Allgo. arbeiten. Aber danke trotzdem für den Tip. Das hat meinen Horizont wieder erweitert :-)

@Dax
Ich habe das schlechtere Erfahrungen mit einem Image gemacht. Ist meist Langsamer (Gemessen mit QueryPerf....)


@SirThornberr
Genau so sehe ich das auch. Deinen Vorschlag hatte ich bereits vor dem Post von mir in einem Testprogramm mit Zeitmessung Programmiert. Leider ist es langamer als direktes Zeichnen???? Verstehe ich aber nicht. War der selben Meinung wie du.


@himitsu
Ich werde es mal Probieren. ScanLines ist noch ein gutes Stichwort.

@paritycheck & @arbu man
Werde ich mir mal anschauen. Das Problem ist, dass die Software ohne installation von [zitat:] ...sollchen spielerein wie DirectX bzw. OpenGL...[zitat ende] funktionieren soll :-(


@Jens Schumann
TeeChart erfüllt die Bedingungen nicht ganz, die ich benötige (Doppelte Daten wegen TSeries (Daten werden derzeit über FileStream gelesen um arbeitsspeicher zu sparen, wären dann aber mit TSeries doch wieder im Arbeitsspeicher). Aber danke für den Tip. Ich werde nochmal darüber nachdenken

DP-Maintenance 25. Jul 2006 22:17

DP-Maintenance
 
Dieses Thema wurde von "SirThornberry" von "Programmieren allgemein" nach "VCL / WinForms / Controls" verschoben.

arbu man 25. Jul 2006 22:20

Re: Schnelleres LineTo
 
Zitat:

Werde ich mir mal anschauen. Das Problem ist, dass die Software ohne installation von [zitat:] ...sollchen spielerein wie DirectX bzw. OpenGL...[zitat ende] funktionieren soll Sad
OpenGL ist keine Spielerrein sondern der 3D Standart in vielen Bereichen auch braucht OpenGL unter Windows keine Installtion, da es mit windows installiert wird, anders als DirectX wo es alle parr Monate ein Update gibt :)

Luckie 25. Jul 2006 23:33

Re: Schnelleres LineTo
 
Zitat:

Zitat von ChrisE
@SirThornberr
Genau so sehe ich das auch. Deinen Vorschlag hatte ich bereits vor dem Post von mir in einem Testprogramm mit Zeitmessung Programmiert. Leider ist es langamer als direktes Zeichnen???? Verstehe ich aber nicht. War der selben Meinung wie du.

Zeig mal Code, das kann nicht sein.

ChrisE 26. Jul 2006 00:02

Re: Schnelleres LineTo
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zum Drüberschauen:

Delphi-Quellcode:
type
  TForm1 = class(TForm)
    Panel1: TPanel;
    PaintBox1: TPaintBox;
    Label1: TLabel;
    lTime: TLabel;
    Edit1: TEdit;
    Label2: TLabel;
    cbMitBMP: TCheckBox;
    bMoveToLineTo: TButton;
    bPolyLine: TButton;
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ZeichnenClick(Sender: TObject);
  private
    { Private-Deklarationen }
    FBMP: TBitmap;
    FStart: Int64;
    FStop: Int64;
    FFreq: Int64;
    procedure Start;
    function Stop:string;
    function TestMoveToLineTo(ACanvas: TCanvas; ACount, w, h: Integer): Integer;
    function TestPolyLine(ACanvas: TCanvas; ACount, w, h: Integer): Integer;    
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


procedure TForm1.FormCreate(Sender: TObject);
begin
  FBMP := TBitmap.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if Assigned(FBMP) then FreeAndNil(FBMP);
end;

procedure TForm1.Start;
begin
  QueryPerformanceFrequency(FFreq);
  QueryPerformanceCounter(FStart);
end;

function TForm1.Stop:string;
begin
  QueryPerformanceCounter(FStop);
  result := FloatToStrF((FStop-FStart)/FFreq * 1000, ffFixed, 10, 4); // ms
end;

function TForm1.TestMoveToLineTo(ACanvas: TCanvas; ACount, w, h: Integer): Integer;
var
  i : Integer;
  kette : string;
begin
  Start;
  result := 0;
  with ACanvas do
  begin
    Pen.Color := clRed;
    MoveTo(10,10);
    for i := 0 to (ACount -1) div 2 do
    begin
      LineTo(w-10,h-10);
      LineTo(10,10);
      result := result +2; // result = Anzahl von LineTo
    end;
  end;
  kette := Stop; // wie lang hat es gedauert ...
  lTime.Caption := kette; // Label auf der Oberfläche -> Zeit in ms anzeigen
end;

function TForm1.TestPolyLine(ACanvas: TCanvas; ACount, w, h: Integer): Integer;
var
  Points: Array of TPoint;
  kette : string;
  i    : Integer;
begin
  SetLength(Points, ACount); //Setlength nicht mit in die Zeitmessung
  Start;
  result := 0;
  for i := 0 to (ACount -1) div 2 do
  begin
    Points[i*2].X := 10;
    Points[i*2].Y := H-10;
    Points[i*2+1].X := W-10;
    Points[i*2+1].Y := 10;  
    result := result +2; // result = Anzahl der Punkt
  end;
  ACanvas.Pen.Color := clGreen;
  ACanvas.Polyline(Points);
  kette := Stop; // wie lang hat es gedauert ...
  lTime.Caption := kette; // Label auf der Oberfläche -> Zeit in ms anzeigen
end;

procedure TForm1.ZeichnenClick(Sender: TObject);
var
  ACanvas: TCanvas;
  wdh: Integer;
  w,h: Integer;
  i: Integer;
  pFromSL, pToSL: PByteArray;
begin
  // Canvas auswählen
  if cbMitBMP.Checked then ACanvas := FBMP.Canvas
                      else ACanvas := PaintBox1.Canvas;
  // Bitmap anpassen (Resize von Form & PaintBox1.Align = alClient)
  FBMP.Width := PaintBox1.Width;
  FBMP.Height := PaintBox1.Height;
  wdh := StrToInt(Edit1.Text);
  w := PaintBox1.Width;
  h := PaintBox1.Height;
  // Hintergrund übermalen
  with ACanvas do
  begin
    Brush.Color := clWhite;
    Pen.Color := clBlack;
    Rectangle(0,0,w,h);
  end;
  // Sender entscheidet über Methode
  if Sender = bMoveToLineTo then Caption := IntToStr(TestMoveToLineTo(ACanvas, wdh, w, h))
  else if Sender = bPolyLine then Caption := IntToStr(TestPolyLine(ACanvas, wdh, w, h));
 
  // Wenn auf das Bitmap gezeichnet wurde, dann auf Paintbox übertragen
  if ACanvas = FBMP.Canvas then BitBlt(PaintBox1.Canvas.Handle, 0, 0, w, h, FBMP.Canvas.Handle, 0,0, SRCCOPY);
end;
[Edit:]Sourcen im Anhang


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