Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi TBitmap Grössenänderung langsam (https://www.delphipraxis.net/184292-tbitmap-groessenaenderung-langsam.html)

toenne 15. Mär 2015 22:38

TBitmap Grössenänderung langsam
 
Ich hole mal ein bisschen aus um das Drumherum zu erklären:
Ich habe für den Töpferofen meiner Gattin eine Steuerung gebaut (AVR µC) welche die benötigten Temperaturkurven abfährt. Die jeweils aktuellen Mess- und Vorgabewerte werden sekündlich via RS232/BT rausgehauen und zunächst mittels einer kleinen Applikation auf meinem Laptop empfangen und in Textdateien geschrieben (jeweils eine für Ist-Temperatur, Soll-Temperatur, Schaltzustand des Steuerrelais, aktuelle Brennphase).
Das alles funzt prima, keine Probleme. Die Dateien umfassen dabei gut 30.000 Messwerte (so ein Brennvorgang dauert mehrere Stunden).

Ein weiteres Tool soll diese Daten dann später einlesen und grafisch darstellen, und da klemmt es.
Auf der Form liegt eine Scrollbox in Bildschirmbreite und z.B. 800px Höhe. Auf dieser Scrollbox liegt ein Image1 auf dessen Canvas die Brennkurve geschrieben wird. Zusätzlich gibts noch eine Scrollbar mit der ich die Ausgabe skalieren/stauchen kann, z.B. um die Brennkurve auch im Ganzen sehen zu können.
Auf der Scrollbox liegt ein weiteres Image2 mit der gleichen Grösse wie Image1, jedoch transparent.
Nachdem die Brennkurve gezeichnet wurde will ich ein Fadenkreuz (bzw. eine vertikale Linie) an der Mausposition einblenden um Messwerte an der Stelle lesen zu können (die werden in Labels eingeblendet, habe ich der Übersichtlichkeit halber im Code unten weggelassen).
Dazu erstelle ich ein transparentes TBitmap, setze es auf die gleiche Grösse wie Image1, zeichne an der Mausposition meine Linie, kopiere es mit image2.Picture.Bitmap.assign(Bmp) aufs Image2 und lösche das TBitmap wieder:

Delphi-Quellcode:
procedure TTempLogView.MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); // Aufruf über OnMouseMove von Image2
var bmp : tbitmap;
begin
  if mausflag = false then exit; // mausflag wird erst nach Zeichnen der Brennkurve auf TRUE gesetzt...quick&dirty, ich weiss...
  bmp := tbitmap.Create ;
  Bmp.Transparent := true;
  Bmp.TransparentColor := clwhite;
  Bmp.TransparentMode := tmFixed;
  bmp.canvas.pen.Color := clblue;

  bmp.Width := image1.Width; // z.B. 30000
  bmp.Height := image1.Height; // z.B. 800

  with bmp.Canvas do
  begin
   moveto(x,0);
   lineto(x,image1.Height);
  end;

  image2.Picture.Bitmap.assign(Bmp);

  FreeAndNil(bmp);
end;
Leider dauert die Änderung der Grösse des TBitmap sehr lange, geschätzt fast 1 Sekunde. Was natürlich bedeutet dass die Linie den Mausbewegungen nur sehr verzögert folgt.
Es ist definitiv die Grössenänderung die bremst, ich habe das durch ein Label getestet dass ich danach beschreibe und den Rest einfach auskommentiert, so dass ausser der Grössenänderung in der Prozedur nix passiert -> auch das Label wird mit dieser Verzögerung beschrieben, das kopieren aufs Image2 passiert z.B. blitzschnell. Und mache ich Image1 (und damit das TBitmap) mittels o.g. Scrollbar kleiner so wird die Ausgabe auch immer schneller, bis die Linie bei einem Image1 in Bildschirmbreite der Mausbewegung praktisch verzögerungsfrei folgt.
Das irritiert mich jetzt, wieso dauert die Grössenänderung so lange? Irgendeine Idee wie man das beschleunigen kann?

Danke & Gruss
Toenne


Achso, es handelt sich übrigens um XE5

Perlsau 15. Mär 2015 22:44

AW: TBitmap Grössenänderung langsam
 
Ich würde den Mauszeiger in Form des Fadenkreuzes nicht über ein zweites TImage darstellen, sondern direkt ins erste TImage malen. Dazu zeichnest du die gewünschten Linien im Modus XOR, wobei das Fadenkreuz dort, wo bereits etwas gezeichnet wurde, invertiert dargestellt wird. Zum Löschen des alten Fadenkreuzes vor dem Zeichnen des neuen, was ja nach jeder Positionsänderung der Maus nötig ist, zeichnest du das alte Fadenkreuz noch einmal mit XOR und danach das neue ebenfalls wieder mit XOR. Das geht wesentlich schneller als die Darstellung über ein zweites TImage.

toenne 15. Mär 2015 22:50

AW: TBitmap Grössenänderung langsam
 
Kann ich mal ausprobieren, danke :).

Dennoch würde mich die Lösung für mein ursprüngliches Problem interessieren. Im Speicher ein Bitmap von meinetwegen 30000x800px anlegen sollte doch eigentlich ruckzuck gehen? Das kopieren dieses Bitmaps aufs Canvas von Image2 geht ja auch schlagartig.

BUG 15. Mär 2015 22:54

AW: TBitmap Grössenänderung langsam
 
Im Zweifelsfall steckt hinter jeder Größenänderung eine Speicherallokation, einmal umkopieren und dann den alten Speicher freigeben. Das kann dauern, auch wenn eine Sekunde seehr lange wäre.

Im Prinzip kannst du deine Bitmap-Instanz wiederverwenden und nur dessen Größe ändern, wenn sich sich die Größe von image2 geändert hat.

toenne 15. Mär 2015 23:09

AW: TBitmap Grössenänderung langsam
 
Zitat:

Zitat von BUG (Beitrag 1293546)
Im Prinzip kannst du deine Bitmap-Instanz wiederverwenden und nur dessen Größe ändern, wenn sich sich die Größe von image2 geändert hat.

Dann bräuchte ich noch einen Trick wie ich den Inhalt des TBitmap wieder löschen könnte (*), ansonsten bleiben ja die Linien erhalten.
Das einfachste wäre es ja die Höhe und Breite dafür auf Null zu setzen...nur stehe ich dann wieder da wo ich jetzt bin, nämlich dass die Änderung auf die richtige Grösse für die nächste Linie wieder so lange dauert. Deswegen bin ich dazu übergegangen gleich 'Nägel mit Köpfen' zu machen und das TBitmap jeweils neu zu erstellen und anschliessend wieder komplett zu löschen. Macht von der Performance keinen Unterschied, die Grössenänderung ist das Problem.

(*) Hmm, vielleicht das TBitmap komplett mit einem weissen Rechteck füllen? Weiss = transparent, könnte ich auch mal probieren.
Heute aber nicht mehr, mein Bett ruft ;).

Bis hierhin erstmal Danke...lasst euch aber nicht abhalten weitere Tips zu posten.
Vielleicht will ja auch jemand das Verhalten einfach mal nachstellen?

Gruss
Toenne

Perlsau 16. Mär 2015 00:53

AW: TBitmap Grössenänderung langsam
 
Zitat:

Zitat von toenne (Beitrag 1293545)
Kann ich mal ausprobieren, danke :).
Dennoch würde mich die Lösung für mein ursprüngliches Problem interessieren. Im Speicher ein Bitmap von meinetwegen 30000x800px anlegen sollte doch eigentlich ruckzuck gehen? Das kopieren dieses Bitmaps aufs Canvas von Image2 geht ja auch schlagartig.

Die Indianergeschichte mit dem toten Pferd kennst du, oder? :mrgreen:

Wenn du den Aufwand, den du jetzt treibst, zu den Schwierigkeiten, die dein derzeitiges Konzept bereitet, dazuzählst, scheint mir der Aufwand mit dem einfachen Zeichnen des Fadenkreuzes im Vergleich dazu minimal. Übrigens machen Zeichenprogramme das auch nicht anders, wenn z.B. interaktiv eine Ellipse gezeichnet werden soll, wobei Größe und Breite der Ellipse den Bewegungen des Mauszeigers folgen. Vergleiche doch einmal, wieviele Pixel rumgeschaufelt werden müssen, um ein Fadenkreuz zu zeichnen, mit der Anzahl der Pixel, die bewegt werden müssen, um das komplette Bitmap zu kopieren. Offensichtlich ist Letzteres zu langsam, um einen flüssigen Ablauf zu gewährleisten.

Sollte das Motiv deiner Ablehnung meines Vorschlags darin liegen, daß du nicht weißt, wie man das Fadenkreuz dynamisch den Größenverhältnissen des Bitmaps anpaßt, helfe ich dir dabei gerne weiter.

toenne 16. Mär 2015 19:22

AW: TBitmap Grössenänderung langsam
 
Wieso Ablehnung? Ich habe doch gesagt ich probiere es aus. Und habe es auch gemacht: Funzt 1A, erwartungsgemäss. :thumb:
Man muss halt nur daran denken in der OnChange-Routine der Scrollbar das alte Kreuz zu löschen und im nächsten OnMouseMove das Kreuz eben dann nicht zu löschen, dann ist alles gut. Nur übersichtlicher wird der Code dadurch natürlich auch nicht unbedingt...;)

Ich bin halt nach wie vor verblüfft wie lange die Grössenänderung des TBitmap dauert derweil das image2.Picture.Bitmap.assign(Bmp) ratzfatz geht. Vermutlich wird in letzterem Fall gar keine Datenmenge bewegt sondern lediglich ein Vektor auf den Speicherbereich des TBitmaps umgeleitet?

Harry Stahl 16. Mär 2015 20:23

AW: TBitmap Grössenänderung langsam
 
Also ich kann hier die Zeitdauer von 1 Sekunde nicht nachvollziehen.
Habe Deinen Code hier mal getestet, die beiden Zuweisungen für das Bitmap (Breite und Höhe) zusammen benötigen 0,72 ms, das ist weit entfernt von 1 Sekunde.

Man kann das aber noch etwas beschleunigen, mit

Delphi-Quellcode:
bmp.SetSize (Image1.width, Image1.height);


dauert es nur 0,58 ms.

Auf einem 7 Jahre alten PC getestet.

Perlsau 16. Mär 2015 20:29

AW: TBitmap Grössenänderung langsam
 
Zitat:

Zitat von toenne (Beitrag 1293698)
Wieso Ablehnung? Ich habe doch gesagt ich probiere es aus. Und habe es auch gemacht: Funzt 1A, erwartungsgemäss. :thumb:

Okay, das mit der Ablehnung war nicht so gemeint, wie du es vielleicht verstanden hattest. Ich hatte den nächsten Satz so interpretiert, daß du dennoch bei deiner alten Methode bleiben möchtest.

Zitat:

Zitat von toenne (Beitrag 1293698)
Man muss halt nur daran denken in der OnChange-Routine der Scrollbar das alte Kreuz zu löschen und im nächsten OnMouseMove das Kreuz eben dann nicht zu löschen, dann ist alles gut. Nur übersichtlicher wird der Code dadurch natürlich auch nicht unbedingt...;)

Auch mit deiner Methode muß man auf OnChange reagieren. Wenn du beim nächsten MouseMove das Kreuz nicht löschst, sondern ein zweites zeichnest, hast du danach zwei Kreuze in deinem Bitmap. Du mußt dir aber auf jeden Fall in MouseMove die Mausposition merken, um das letzte Kreuz korrekt löschen sprich mit XOR übermalen zu können.

Zitat:

Zitat von toenne (Beitrag 1293698)
Ich bin halt nach wie vor verblüfft wie lange die Grössenänderung des TBitmap dauert derweil das image2.Picture.Bitmap.assign(Bmp) ratzfatz geht. Vermutlich wird in letzterem Fall gar keine Datenmenge bewegt sondern lediglich ein Vektor auf den Speicherbereich des TBitmaps umgeleitet?

Assign weist in der Tat lediglich einen Pointer zu, da der Speicherbereich, auf den der Quell-Pointer verweist, ja bereits existiert.

Bjoerk 16. Mär 2015 21:17

AW: TBitmap Grössenänderung langsam
 
Kann da auch keine Verzögerung feststellen?

Beispiel:

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls;

type
  TForm1 = class(TForm)
    ScrollBox1: TScrollBox;
    PaintBox1: TPaintBox;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure PaintBox1Paint(Sender: TObject);
    procedure PaintBox1MouseEnter(Sender: TObject);
    procedure PaintBox1MouseLeave(Sender: TObject);
  private
    FGraphic: TBitmap;
    FP: TPoint;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FGraphic := TBitmap.Create;
  PaintBox1.Left := 0;
  PaintBox1.Top := 0;
  PaintBox1.Align := alNone;
  PaintBox1.Width := 3000;
  PaintBox1.Height := 3000;
  FGraphic.Width := PaintBox1.Width;
  FGraphic.Height :=PaintBox1.Height;
  ScrollBox1.DoubleBuffered := true;
end;

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

procedure TForm1.PaintBox1MouseEnter(Sender: TObject);
begin
  ShowCursor(false);
end;

procedure TForm1.PaintBox1MouseLeave(Sender: TObject);
begin
  ShowCursor(true);
end;

procedure TForm1.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  FP := Point(X, Y);
  PaintBox1.Invalidate;
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
  PaintBox1.Canvas.Draw(0, 0, FGraphic);
  PaintBox1.Canvas.MoveTo(FP.X, 0);
  PaintBox1.Canvas.LineTo(FP.X, PaintBox1.Height);
  PaintBox1.Canvas.MoveTo(0, FP.Y);
  PaintBox1.Canvas.LineTo (PaintBox1.Width, FP.Y);
end;

end.


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:12 Uhr.
Seite 1 von 2  1 2      

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