In Paint: Resize erkennen und Inhalt von Canvas sichern

Rund um die LCL und andere Komponenten
RSE
Beiträge: 462
Registriert: Mi 30. Jul 2008, 13:11
OS, Lazarus, FPC: WinXP SP3 (L 0.9.28.2 FPC 2.2.4)
CPU-Target: 32Bit
Kontaktdaten:

In Paint: Resize erkennen und Inhalt von Canvas sichern

Beitrag von RSE »

Hallo!

Problem: Wenn die Komponente vergrößert wird, wird sie für jeden Pixel komplett neu gezeichnet (Paint wird automatisch aufgerufen). Das führt zu Flackern.

Lösungsansatz: Beim Aufruf der Paint-Routine herausbekommen, ob lediglich die Größe geändert wurde und ggf. nur den entstandenen Rand dazuzeichnen.

Restprobleme:
1. Wie bekomme ich heraus, ob der Aufruf durch ein Resizing getriggert wurde? ClientHeight und ClientWidth mit den alten Werten zu vergleichen ist sicherlich möglich, jedoch unter Umständen unsicher...
2. Da das ganze Canvas schon vor dem Aufruf von Paint weiß gezeichnet wurde, muss ich irgendwie nach dem Abarbeiten von Paint den Inhalt meines Canvas sichern, damit ich es beim nächsten Paint-Aufruf wiederherstellen kann (geht ja schneller als komplett neu erstellen, was ja meist nicht nötig ist). Wie mache ich das?
Seit er seinen neuen Computer hat, löst er alle Probleme, die er vorher nicht hatte!

alexander
Beiträge: 423
Registriert: Di 5. Feb 2008, 12:45
OS, Lazarus, FPC: Linux, Lazarus svn, FPC svn
CPU-Target: 64Bit
Kontaktdaten:

Beitrag von alexander »

Ich weiß nicht so ganz was du ursprünglich verhindern willst...
Nur das flackern?

Kennst du die Option:

Code: Alles auswählen

DoubleBuffered:=true
Versuche es mal für die Form, vielleicht hilft es ja...
Du magst Freiheit? Gönne es auch deinem Computer mit Linux!
www.alexanderroth.eu

RSE
Beiträge: 462
Registriert: Mi 30. Jul 2008, 13:11
OS, Lazarus, FPC: WinXP SP3 (L 0.9.28.2 FPC 2.2.4)
CPU-Target: 32Bit
Kontaktdaten:

Beitrag von RSE »

DoubleBuffered hilft nicht. Ich will es auch einfach effizient gestalten, die Komponente ist selbstgeschrieben ;-)

Ursprünglich verhindern will ich das Flackern, was dadurch entsteht, dass die Komponente unnötigerweise dauernd komplett neu gezeichnet wird.
Seit er seinen neuen Computer hat, löst er alle Probleme, die er vorher nicht hatte!

alexander
Beiträge: 423
Registriert: Di 5. Feb 2008, 12:45
OS, Lazarus, FPC: Linux, Lazarus svn, FPC svn
CPU-Target: 64Bit
Kontaktdaten:

Beitrag von alexander »

hmmm dann kann ich dir leider nicht so viel helfen, aber ich würde mal vermuten, dass es normal ist, und dass du vielleicht einfach neu zeichnen solltest...
Musst du berechnungen anstellen während der berechnung? Dauert es außergewöhnlich lange? Wie zeichnest du?

Hmm also wenn ich das machen würde was du in Punkt 2. beschreibst, dann würde ich erst auf ein TBitmap zeichnen und das dann auf die Canvas kopieren... (weis leider nicht ganz genau wie das geht, geht aber bestimmt, jemand anderes weiß es sicherlich)
Du magst Freiheit? Gönne es auch deinem Computer mit Linux!
www.alexanderroth.eu

RSE
Beiträge: 462
Registriert: Mi 30. Jul 2008, 13:11
OS, Lazarus, FPC: WinXP SP3 (L 0.9.28.2 FPC 2.2.4)
CPU-Target: 32Bit
Kontaktdaten:

Beitrag von RSE »

d.h. man müsste tatsächlich ein verstecktes TCanvas irgendwo auf die Form (z.B. genau unter das eigentliche Steuerelement) legen und darauf kopieren. Einfach ein TCanvas instanzieren und dort hin kopieren geht ja leider nicht (Fehler á la "auf dem Canvas kann nicht gezeichnet werden", braucht wohl ein übergeordnetes Control oder so). Ich hatte gehofft, dass man den Inhalt vielleicht in ein geeignetes array schreiben könnte oder etwas vergleichbar einfaches.

Es handelt sich um eine selbst gezeichnete Listbox, bei der ich darauf geachtet habe nichts unnützes zu zeichnen, z.B. wird beim Scrollen das Bild verschoben statt komplett neu gezeichnet.

Das Flackern tritt vielleicht bei einer ordentlichen Grafikkarte nicht auf (arbeite am Laptop mit Intel Onboard Grafik), aber ich will nicht unnütz so viel Grafikleistung verbraten.
Seit er seinen neuen Computer hat, löst er alle Probleme, die er vorher nicht hatte!

alexander
Beiträge: 423
Registriert: Di 5. Feb 2008, 12:45
OS, Lazarus, FPC: Linux, Lazarus svn, FPC svn
CPU-Target: 64Bit
Kontaktdaten:

Beitrag von alexander »

hmmm ich glaube du verstehst mich falsch. Ich meine auf ein TBitmap zeichnen. Und dann mittels eines befehls (in windows ist es bitblt (oder so ähnlich)), wird der Inhalt des Bitmaps direkt in das Canvas übertragen.


hmmm gefällt dir die normale Listbox nicht? Die ist doch super...
Du magst Freiheit? Gönne es auch deinem Computer mit Linux!
www.alexanderroth.eu

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

du musst bei deiner Komponente einfach die Methode Resize überladen.
Die wird bei einer Größen Änderung aufgerufen.

Das Flackern liegt daran, das du das was du zeichnen möchtest vorher lieber in ein Buffer Zeichnen solltest. Also in z.b. einer TBItmap du du einmal erstellst und bei Resize setzt und installisiert damit meine ich einmal Fillrect aufrufen.

Bei Paint, kannst du dann einfach alles übermalen mit FillRect und deine restliche Zeichnen Funktion abarbeiten lassen und Fertig.
MFG
Michael Springwald

RSE
Beiträge: 462
Registriert: Mi 30. Jul 2008, 13:11
OS, Lazarus, FPC: WinXP SP3 (L 0.9.28.2 FPC 2.2.4)
CPU-Target: 32Bit
Kontaktdaten:

Beitrag von RSE »

Mich hatte an der normalen Listbox das Flackern gestört, und da hab ich mal eine eigene gebaut. Dummerweise hab ich jetzt das Flackern wieder... Aber ich bin um eine Erfahrung reicher und ich weiß wie sie funktioniert. Hab auch einige Sachen vom Ansatz her anders gelöst (z.B. ist Itemindex ein Element selbst, kein Integer). Ich werde jetzt also ein TBitmap zum Sichern des Canvas benutzen und die Resize-Methode überladen. Hoffe dann damit das Problem zu lösen. Leider werde ich wohl dann wegen Urlaub kein Internet mehr haben, um diesen Thread weiter zu verfolgen, ggf. melde ich mich dann in 2 Wochen wieder ;-)
Seit er seinen neuen Computer hat, löst er alle Probleme, die er vorher nicht hatte!

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

Warum flackert bei dir die ListBox ?
was verwendest du ? Linux, Windows ? GTK1 GTK2 ? Welche Lazarus Version ?
Das Flackern solltest du melden. Da es sich um Ein Fehler handel könnte. Oder eine Falschen Bedienung...

Hast du mal ein beispiel Projekt ? Wo das Flackern auftritt ? und wann Flackert es ?
Beim Klicken, beim Scrollen, ?
MFG
Michael Springwald

RSE
Beiträge: 462
Registriert: Mi 30. Jul 2008, 13:11
OS, Lazarus, FPC: WinXP SP3 (L 0.9.28.2 FPC 2.2.4)
CPU-Target: 32Bit
Kontaktdaten:

Beitrag von RSE »

Es Flackert beim Vergrößern der Listbox, dabei wird für jeden einzelnen Pixel Paint aufgerufen. Wenn die Liste eine große Fläche hat, flackert es demnach auch schlimmer. Das Problem tritt wohl nur bei solch schwachen Grafikkarten wie meiner auf. Als Bug würde ich das nicht bezeichnen wollen. Außerdem handelt es sich jetzt aktuell um mein eigenes Produkt...

Die Resize-Prozedur hilft mir übrigens nicht weiter, oder ich erkenne die Stelle nicht. Kann man innerhalb von Paint herausbekommen, ob der Aufruf von einem Resize getriggert worden ist (notfalls könnte ich mir den Hinweis in der Resize selbst setzen)?
Seit er seinen neuen Computer hat, löst er alle Probleme, die er vorher nicht hatte!

Euklid
Lazarusforum e. V.
Beiträge: 2808
Registriert: Fr 22. Sep 2006, 10:38
OS, Lazarus, FPC: Lazarus v2.0.10, FPC 3.2.0
Wohnort: Hessen
Kontaktdaten:

Re: In Paint: Resize erkennen und Inhalt von Canvas sichern

Beitrag von Euklid »

Hallo RSE,
RSE hat geschrieben: 2. Da das ganze Canvas schon vor dem Aufruf von Paint weiß gezeichnet wurde, muss ich irgendwie nach dem Abarbeiten von Paint den Inhalt meines Canvas sichern, damit ich es beim nächsten Paint-Aufruf wiederherstellen kann (geht ja schneller als komplett neu erstellen, was ja meist nicht nötig ist). Wie mache ich das?
Wie wärs, wenn du Paint in einem Bitmap im Arbeitsspeicher zeichnen lässt, und dieses dann auf die Canvas kopierst?

Damit würdest du gleich 2 Fliegen mit einer Klappe schlagen: Das Flackern würde ev. weniger UND die von Paint gezeichnete Zeichnung wäre gesichert.

Code: Alles auswählen

//...
var SpeicherBitmap: TBitmap;
begin
//... wenn z.B. das Bild auf Form1.Image1 ausgegeben werden sol:
SpeicherBitmap.Width:=Form1.Image1.Width;
SpeicherBitmap.Height:=Form1.Image1.Height;
// Hier wird nun auf die SpeicherBitmap gezeichnet anstatt auf Form1.Image1
 
Form1.image1.canvas.Draw(0,0,SpeicherBitmap); //SpeicherBitmap wird schließlich auf Image1 gezeichnet.
 
//...
end;
Das Flackern kannst du ganz beseitigen, indem du mit Timern arbeitest - und nach onresize z.B. eine halbe Sekunde Pause einprogrammierst, bevor er erneut zeichnet.

Gruß, Euklid

alexander
Beiträge: 423
Registriert: Di 5. Feb 2008, 12:45
OS, Lazarus, FPC: Linux, Lazarus svn, FPC svn
CPU-Target: 64Bit
Kontaktdaten:

Beitrag von alexander »

ahh ja das meinte ich:
Form1.image1.canvas.Draw(0,0,SpeicherBitmap);
Du magst Freiheit? Gönne es auch deinem Computer mit Linux!
www.alexanderroth.eu

RSE
Beiträge: 462
Registriert: Mi 30. Jul 2008, 13:11
OS, Lazarus, FPC: WinXP SP3 (L 0.9.28.2 FPC 2.2.4)
CPU-Target: 32Bit
Kontaktdaten:

Beitrag von RSE »

Ich habe jetzt Zeit gefunden das umzusetzen. In Resize setze ich eine eigene Variable Resizing auf true. In Paint habe ich folgenden Quelltext (Canvas ist von meiner Listbox, Bitmap ist die Sicherungskopie):

Code: Alles auswählen

if Resizing and (ClientWidth <= Bitmap.Width) and
     (ClientHeight <= Bitmap.Height) then begin
    Canvas.CopyRect(classes.rect(0,0,ClientWidth,ClientHeight),
                    Bitmap.Canvas,classes.rect(0,0,ClientWidth,ClientHeight));
  end else begin
    ZeichneKomplettNeu;
  end;
  Resizing := false;
  Bitmap.Width := ClientWidth;
  Bitmap.Height := ClientHeight;
  Bitmap.Canvas.CopyRect(classes.rect(0,0,ClientWidth,ClientHeight),
                         Canvas,classes.rect(0,0,ClientWidth,ClientHeight));
Leider scheint der Kopiervorgang nicht schneller zu sein als ZeichneKomplettNeu, da das Flimmern genauso weiterhin vorhanden ist. Es besteht kein Unterschied zwischen einer Verkleinerung und einer Vergrößerung der Listbox.

Kann man das Ganze noch wesentlich beschleunigen (außer das Kopieren ins Bitmap wegzulassen wenns unnötig ist), oder befinde ich mich in einer Sackgasse?
Seit er seinen neuen Computer hat, löst er alle Probleme, die er vorher nicht hatte!

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

Also die Schritte um das Flimmern zu vermeiden beim Zeichen währe folgende:
01 - einen Buffer von Typ TBitmap anlegen/Installisieren aber nur einmal !
02 - Die Größe setzten und einmal mit FillRect alles übermalen
03 - Eine Paint Mehtode erstellen und rein Zeichnen, vorher alles löschen mit FillRect.
04 - Wenn alles gezeichnet ist könntest du mit bitblt arbeiten oder du sagst z.b. Canvas.Draw(0,0, Buffer)

Wenn du diese Schritte beim zeichnen befolgst dürfte es eigentlich nicht mehr Flimmern.
Es ist Wichtig in den Buffer rein zu zeichnen. und CopyRect nehme ich schon lange nicht mehr da er unter Lazarus auch ein Fehler haben soll, welchen Genau weiß ich nicht mehr und ob dieser Fehler immer noch da ist weiß ich auch nicht !
MFG
Michael Springwald

RSE
Beiträge: 462
Registriert: Mi 30. Jul 2008, 13:11
OS, Lazarus, FPC: WinXP SP3 (L 0.9.28.2 FPC 2.2.4)
CPU-Target: 32Bit
Kontaktdaten:

Beitrag von RSE »

@01: Was meinst du genau mit nur einmal initialisieren? Alle Zeilen zeichnen und dann immer nur den entsprechenden Ausschnitt darstellen funktioniert nicht.
1. Die Liste kann sehr lang werden, dann werden Unmengen von Speicherplatz verbraten
2. Wenn die Breite geändert wird, muss sowieso komplett neu gezeichnet werden, auch beim Verkleinern, weil auch rechtsbündig Text dargestellt wird.

@02: Mit FillRect alles übermalen kann ich mir schenken:
1. Das passiert sowieso, sonst müsste ich es nicht erst wiederherstellen. Ich weiß aber nicht, wo ich das abstellen kann (meine Listbox ist von TPanel abgeleitet).
2. Ich übermale ja dann mit Draw sowieso nochmal alles, weil mein Buffer ist immer mindestens geauso groß wie die Zeichenfläche in der Listbox

@04: BitBlt kenne ich gar nicht, es scheint aber windows-spezifisch zu sein. Ich möchte lieber bei plattformunabhängigen Funktionen bleiben. Falls sie in Lazarus plattformunabhängig implementiert ist, wie benutze ich sie (Beispiel-Link)?
Seit er seinen neuen Computer hat, löst er alle Probleme, die er vorher nicht hatte!

Antworten