Autor |
Beitrag |
Timbo
      
Beiträge: 166
|
Verfasst: Di 24.02.04 15:47
moin zusammen,
habe einen einfachen bildschirmschoner mit einem übergang geprogt, wo jpg angezeigt werden.
das anzeigen der bilder ist schnell genung, und die bilder sollen ja eh eine zeit lang stehen bleiben.
aber der übergang (ein bild faded in das andere) ist mir nicht schnell genug. kann ich an meinem code noch was verbessern?
der übergang funktioniert wie folgt:
bild1 rgb(100 150 100) bild2(200 100 200) dann wird mit einer vorher festgelegten anzahl von schritten die werte verändert: 120 140 120 / 140 130 140 / usw.
countFade sind die gesamtschritte und akt ist der aktuelle schritt (von 1 bis countFade)
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| function Fade(var bildFade:tbitmap; bild1, bild2:tbitmap; akt:Integer):integer; var x,y:integer; p1, p2, p3:pbytearray; tx, ty, tz:integer; xmal3: Integer; begin for y:=0 to screenHeight-1 do begin p1:=bild1.ScanLine[y]; p2:=bild2.ScanLine[y]; p3:=bildFade.ScanLine[y]; for x:=0 to screenWidth-1 do begin xmal3:= x*3; if p1[xmal3]< p2[xmal3] then p3[xmal3]:=round(((abs(p1[xmal3]-p2[xmal3]))/countFade)* akt)+ p1[xmal3] else p3[xmal3]:=p1[xmal3] - round(((abs(p1[xmal3]-p2[xmal3]))/countFade)* akt); if p1[xmal3+1]< p2[xmal3+1] then p3[xmal3+1]:=round(((abs(p1[xmal3+1]-p2[xmal3+1]))/countFade)* akt)+ p1[xmal3+1] else p3[xmal3+1]:=p1[xmal3+1] - round(((abs(p1[xmal3+1]-p2[xmal3+1]))/countFade)* akt); if p1[xmal3+2]< p2[xmal3+2] then p3[xmal3+2]:=round(((abs(p1[xmal3+2]-p2[xmal3+2]))/countFade)* akt)+ p1[xmal3+2] else p3[xmal3+2]:=p1[xmal3+2] - round(((abs(p1[xmal3+2]-p2[xmal3+2]))/countFade)* akt); end; end; end; |
aufruf:
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| while (i <= countFade) AND not (stoppAni) do begin Fade(bildFade, bild1, bild2, i); Image1.Picture.Bitmap:= bildFade; Application.ProcessMessages; inc(i); end; |
habe schon versucht abwechselnt jede zweite zeile zu berechnen, aber das konnte man dann auch sehen.
vielen dank.
tim.
Zuletzt bearbeitet von Timbo am Di 24.02.04 19:43, insgesamt 1-mal bearbeitet
|
|
thebe
      
Beiträge: 128
WinXP Home
D6 Enterprise
|
Verfasst: Di 24.02.04 17:02
Du verwendest für jede Zeile die Du bearbeitest nochmal die ScanLine Funktion, was Du gar nicht brauchst. Auch ScanLine braucht seine Zeit, besonders wenn Du die Funktion 639 - 1023 mal unnütz aufrufst.
ScanLine kopiert die Pointerdaten des Bildes von Zeile y bis Zeile 1 in dein Array (sprich wenn Du die letzte Zeile per Scanline rüberkopierst, dann kopiert Delphi die Pointer Daten des gesamten Bildes in dein Array), daß kannst Du ausnutzen sodaß Du nur EINMAL ScanLine pro Bild verwenden musst.
Wenn Du z.b. mit Bild1.ScanLine[ScreenHeight-1] alle Daten in das Array einlädst, sieht das im Array wie folgt aus:
{Pointer zu Pixel 1, Zeile Screenheight; ...... ; Pointer zu Pixel ScreenWidth, Zeile Screenheight ; Pointer zu Pixel 1, Zeile Screenheight-1 ; ..... ; Pointer zu Pixel ScreenWidth, Zeile Screenheigth-1; ... }
=> Einmal per p1 := Bild1.ScanLine[ScreenHeight-1] einladen und dann per
p1^[((heigth-y)*width)+x] := *bla irgendeinFarbwert* drauf zugreifen
Heigth muss ScreenHeight-1 sein, Width muss ScreenWidth sein (ohne -1)
x und y gehen von 0 bis ScreenHeigth-1 / ScreenWidth-1
Das sollte Dir nen gehörigen Geschwindigkeitsvorteil geben.
MfG
- Thebe
|
|
Keldorn
      
Beiträge: 2266
Erhaltene Danke: 4
Vista
D6 Prof, D 2005 Pro, D2007 Pro, DelphiXE2 Pro
|
Verfasst: Di 24.02.04 19:18
@thebe:
Bist du dir da sicher? Meines wissens nach sind die scanlines auf 4 Bytes ausgerichtet. Wenn du eine pf24bit-Bitmap hast und Pech mit der Breite hast, wirst du wahrscheinlich nicht das richtige Pixel erwischen.
hier wird eine andere Lösung gezeigt:
www.efg2.com/Lab/Ima...ine.htm#Optimization
(auch mal die Kommentare lesen, da steht das mit der ausrichtung auch nochmal drin)
@Timbo:
ich würd noch
- versuchen das "/countFade* akt" in einer Variablen außerhalb der schleife zu berechnen
- mal das round durch trunc zu ersetzen
- mir ist nicht ganz klar, was du mit dem if p1[xmal3]< p2[xmal3] bezweckst.
- Bist du dir mit dem inc(y); sicher?
- Ich denke aber, daß du die größte Zeit vielleicht hier verlierst
Zitat: |
Image1.Picture.Bitmap:= bildFade;
|
Da steckt viel mehr dahinter als ein einfaches Zeichnen. Probier hier mal ein Paintbox statt einem Image und zeichne hier deine Bitmap rein. (onpaint ereignis der paintbox nicht vergessen, sonst siehst du nix, wenn z.B. die Form minimiert wird oder ein anderes fenster drübergelegt wird).
Würd mich interessieren, was rauskommt
Mfg Frank
_________________ Lükes Grundlage der Programmierung: Es wird nicht funktionieren.
(Murphy)
|
|
Timbo 
      
Beiträge: 166
|
Verfasst: Di 24.02.04 19:55
das inc(y); war natürlich schwachsinn, hatte ich hier vergessen zu korrigieren, habe es jetzt rausgenommen. war ein überbleibsel, von optimierungsversuchen.
@thebe:
das mit dem scanline werde ich mal versuchen.
@Keldorn:
deine tipps hören sich auch gut an werde das alles mal testen.
mit p1[xmal3]< p2[xmal3] prüfe ich ob ich den berechneten wert auf pix1 addieren oder subtraieren muss. ich will ja von p1 den übergang zu p2 machen.
beispiel: p1 50 p2 100 dann muss ich immer auf p1 addieren
p1 200 p2 100 nun von p1 subtraieren.
|
|
Timbo 
      
Beiträge: 166
|
Verfasst: Di 24.02.04 21:04
konnte den vergleich auch rausschmeissen, nur mal ein bisschen nachdenken...
der faktor wir nun ein mal berechnet:
faktor:= akt/countFade;
und die drei werte berechnen sich so:
Delphi-Quelltext 1: 2: 3:
| p3[xmal3]:=round((p2[xmal3]-p1[xmal3])* faktor)+ p1[xmal3]; p3[xmal3+1]:=round((p2[xmal3+1]-p1[xmal3+1])* faktor)+ p1[xmal3+1]; p3[xmal3+2]:=round((p2[xmal3+2]-p1[xmal3+2])* faktor)+ p1[xmal3+2]; |
trunc hat das ganze sehr viel langsamer gemacht im gegensatz zu round, komisch.
nun schau ich mir nochmal die zuweisung des bildmaps an.
|
|
Phantom1
      
Beiträge: 390
|
Verfasst: Di 24.02.04 21:43
@Timbo:
ich hab deine procedure Fade(...) die du oben gepostet hast verbessert. Nach meiner messung um etwa 20%.
Hier der code:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| procedure Fade(bildFade, bild1, bild2: TBitmap; akt: Integer); Type TRGB = Packed Record b, g, r: Byte End; var x: Integer; p1, p2, p3: ^TRGB; begin p1:=bild1.ScanLine[bild1.Height-1]; p2:=bild2.ScanLine[bild1.Height-1]; p3:=bildFade.ScanLine[bild1.Height-1]; for x:=1 to bild1.Height*bild1.Width do begin p3.r:=p1.r + Round((p2.r-p1.r) / countFade * akt); p3.g:=p1.g + Round((p2.g-p1.g) / countFade * akt); p3.b:=p1.b + Round((p2.b-p1.b) / countFade * akt); Inc(p1); Inc(p2); Inc(p3); end; end; |
Mfg
|
|
Phantom1
      
Beiträge: 390
|
Verfasst: Di 24.02.04 22:08
und hier der andere teil, sollte auch nochmal etwa 10% bringen.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| Image1.Picture.Bitmap:= bildFade; while (i <= countFade) AND not (stoppAni) do begin Fade(Image1.Picture.Bitmap, bild1, bild2, i); Image1.Repaint; Application.ProcessMessages; inc(i); end; |
|
|
Phantom1
      
Beiträge: 390
|
Verfasst: Di 24.02.04 23:41
Ach mir ist nochwas eingefallen *g Und zwar bringt das nochmals etwa 15% geschwindigkeitszuwachs  (insgesamt schätze ich mal so um die 40% !)
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| procedure Fade(bildFade, bild1, bild2: TBitmap; akt: Integer); Type TRGB = Packed Record b, g, r: Byte End; var i: Integer; p1, p2, p3: ^TRGB; LUT: Array[-255..255] Of SmallInt; begin For i:=Low(LUT) To High(LUT) Do LUT[i]:=Round((i) / countFade * akt); p1:=bild1.ScanLine[bild1.Height-1]; p2:=bild2.ScanLine[bild1.Height-1]; p3:=bildFade.ScanLine[bild1.Height-1]; for i:=1 to bild1.Height*bild1.Width do begin p3.r:=p1.r + LUT[p2.r-p1.r]; p3.g:=p1.g + LUT[p2.g-p1.g]; p3.b:=p1.b + LUT[p2.b-p1.b]; Inc(p1); Inc(p2); Inc(p3); end; end; |
aufruf:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| Image1.Picture.Bitmap:= bildFade; while (i <= countFade) AND not (stoppAni) do begin Fade(Image1.Picture.Bitmap, bild1, bild2, i); Image1.Repaint; Application.ProcessMessages; inc(i); end; |
mfg
|
|
thebe
      
Beiträge: 128
WinXP Home
D6 Enterprise
|
Verfasst: Mi 25.02.04 09:19
Zitat: | @thebe:
Bist du dir da sicher? Meines wissens nach sind die scanlines auf 4 Bytes ausgerichtet. Wenn du eine pf24bit-Bitmap hast und Pech mit der Breite hast, wirst du wahrscheinlich nicht das richtige Pixel erwischen. |
Lies bitte den Code den ich gepostet hab. Er kopiert die Scanlines in ein PByteArray und greift dann nur auf auf die jeweiligen Werte in meinem Code zu ( p1 ^[((heigth-y)*width)+x] ). Und der Code funzt BTW, meine Programme verwenden den genauso.
|
|
Timbo 
      
Beiträge: 166
|
Verfasst: Mi 25.02.04 12:13
@Phantom1:
ich habe zum vergleichen der berechnung des bildes Image1.Picture.Bitmap:= bildFade; auskommentiert. also das berechnete bild wird nicht mehr angezeigt, aber die rechnung läuft ganz normal.
der erste quellcode von dir lief bei mir nicht schneller, aber der zweite läuft mehr als doppelt so schnell wow...
nun muss ich nur noch die darstellung des bildes optimieren.
habe das ausprobiert:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| Image1.Picture.Bitmap:= bildFade; while (i <= countFade) AND not (stoppAni) do begin Fade(Image1.Picture.Bitmap, bild1, bild2, i); Image1.Repaint; Application.ProcessMessages; inc(i); end; |
aber bei mir läft das schneller:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| Image1.Picture.Bitmap:= bildFade; while (i <= countFade) AND not (stoppAni) do begin Fade(bildFade, bild1, bild2, i); Image1.Picture.Bitmap:= bildFade; Application.ProcessMessages; inc(i); end; |
weil hier brauch ich komischerweise nicht repaint aufrufen.
im moment sieht es so aus:
1/5 der zeit berechnung des bildes
4/5 der zeit darstellung des bildes (wobei diese zeit wohl auch von der grafikkarte mit abhängt)
werde nun mal den vorschlag von Keldorn verfolgen.
vielen dank schon mal an alle.
|
|
Timbo 
      
Beiträge: 166
|
Verfasst: Mi 25.02.04 12:35
nun mit paintbox:
ist noch ein bißchen schneller
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| while (i <= countFade) AND not (stoppAni) do begin Fade(bildFade, bild1, bild2, i); BitBlt(PaintBox1.Canvas.Handle, 0, 0, screenWidth, screenHeight, bildFade.Canvas.Handle, 0, 0, SrcCopy); Application.ProcessMessages; inc(i); end; |
|
|
Phantom1
      
Beiträge: 390
|
Verfasst: Mi 25.02.04 14:31
Hab noch ein Tipp: arbeite mit 32bit Farbtiefe, ist meist schneller! (bei mir waren es sogar 15%)
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| bild1.pixelformat:=pf32bit; bild2.pixelformat:=pf32bit; bildFade.pixelformat:=pf32bit; while (i <= countFade) AND not (stoppAni) do begin Fade(bildFade, bild1, bild2, i); BitBlt(PaintBox1.Canvas.Handle, 0, 0, screenWidth, screenHeight, bildFade.Canvas.Handle, 0, 0, SrcCopy); Application.ProcessMessages; inc(i); end; |
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| procedure Fade(bildFade, bild1, bild2: TBitmap; akt: Integer); Type TRGB = Packed Record b, g, r, a: Byte End; var i: Integer; p1, p2, p3: ^TRGB; LUT: Array[-255..255] Of Integer; begin For i:=Low(LUT) To High(LUT) Do LUT[i]:=Round((i) / countFade * akt); p1:=bild1.ScanLine[bild1.Height-1]; p2:=bild2.ScanLine[bild1.Height-1]; p3:=bildFade.ScanLine[bild1.Height-1]; for i:=1 to bild1.Height*bild1.Width do begin p3.r:=p1.r + LUT[p2.r-p1.r]; p3.g:=p1.g + LUT[p2.g-p1.g]; p3.b:=p1.b + LUT[p2.b-p1.b]; Inc(p1); Inc(p2); Inc(p3); end; end; |
|
|
obbschtkuche
Gast
Erhaltene Danke: 1
|
Verfasst: Mi 25.02.04 18:49
Und jetzt könntest du den LUT noch global definieren und nur einmal berechnen lassen.
|
|
Phantom1
      
Beiträge: 390
|
Verfasst: Mi 25.02.04 20:27
@obbschtkuche: so einfach ist das leider nicht, da wir hier zwei unbekannte variabeln haben, dh wir müssten ein mehrdimensionales array verwenden und erheblich mehr werte im vorraus berechnen, um genau zu sein wären es genauso viele wie jetzt im moment, es würde also in diesem fall nix bringen.
Ich habe aber noch eine Idee und ich hätte nicht gedacht das das soviel ausmacht (es ist jetzt etwa 4mal so schnell !)
hier der code, erstmal brauchen wir eine globale variable:
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| Type TRGB = Packed Record b, g, r, a: Byte End; TRGBs = Packed Record b, g, r: SmallInt End;
var diff: Array of TRGBs; |
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34:
| procedure Getdiff(bild1, bild2: TBitmap); var i: Integer; p1, p2: ^TRGB; begin p1:=bild1.ScanLine[bild1.Height-1]; p2:=bild2.ScanLine[bild1.Height-1]; for i:=0 to bild1.Height*bild1.Width-1 do begin diff[i].r:=p2.r-p1.r; diff[i].g:=p2.g-p1.g; diff[i].b:=p2.b-p1.b; Inc(p1); Inc(p2); end; end;
procedure Fade(bildFade, bild1: TBitmap; akt: Integer); var i: Integer; p1, p3: ^TRGB; LUT: Array[-255..255] Of Integer; begin For i:=Low(LUT) To High(LUT) Do LUT[i]:=Round(i / countFade * akt); p1:=bild1.ScanLine[bild1.Height-1]; p3:=bildFade.ScanLine[bild1.Height-1]; for i:=0 to bild1.Height*bild1.Width-1 do begin p3.r:=p1.r+LUT[diff[i].r]; p3.g:=p1.g+LUT[diff[i].g]; p3.b:=p1.b+LUT[diff[i].b]; Inc(p1); Inc(p3); end; end; |
Der aufruf sieht wie folgt aus:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| bild1.pixelformat:=pf32bit; bild2.pixelformat:=pf32bit; bildFade.pixelformat:=pf32bit; SetLength(diff,bild1.Height*bild1.Width); Getdiff(bild1, bild2); while (i <= countFade) AND not (stoppAni) do begin Fade(bildFade, bild1, i); BitBlt(PaintBox1.Canvas.Handle, 0, 0, screenWidth, screenHeight, bildFade.Canvas.Handle, 0, 0, SrcCopy); Application.ProcessMessages; inc(i); end; |
|
|
obbschtkuche
Gast
Erhaltene Danke: 1
|
Verfasst: Fr 27.02.04 16:48
Phantom1 hat folgendes geschrieben: | @obbschtkuche: so einfach ist das leider nicht, da wir hier zwei unbekannte variabeln haben, dh wir müssten ein mehrdimensionales array verwenden und erheblich mehr werte im vorraus berechnen, um genau zu sein wären es genauso viele wie jetzt im moment, es würde also in diesem fall nix bringen. |
Wenn mehrere Bilder gefadet werden (Wie es hier den Anschein hat), bringt es sehr wohl was. Es braucht zwar mehr Speicher, aber ein Paar KB (Je nach dem in wie vielen Schritte das Bild gefadet wird) wird man wohl noch berappen können :P
|
|
Phantom1
      
Beiträge: 390
|
Verfasst: Fr 27.02.04 17:56
obbschtkuche hat folgendes geschrieben: | Phantom1 hat folgendes geschrieben: | @obbschtkuche: so einfach ist das leider nicht, da wir hier zwei unbekannte variabeln haben, dh wir müssten ein mehrdimensionales array verwenden und erheblich mehr werte im vorraus berechnen, um genau zu sein wären es genauso viele wie jetzt im moment, es würde also in diesem fall nix bringen. |
Wenn mehrere Bilder gefadet werden (Wie es hier den Anschein hat), bringt es sehr wohl was. Es braucht zwar mehr Speicher, aber ein Paar KB (Je nach dem in wie vielen Schritte das Bild gefadet wird) wird man wohl noch berappen können  |
Dann zeig doch mal wie du es gemacht hättest, weil ich habe keine möglichkeit gefunden das sinnvoll umzusetzen.
|
|
obbschtkuche
Gast
Erhaltene Danke: 1
|
Verfasst: Fr 27.02.04 19:02
Für 20 Schritte würde ich das in der Art machen:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
| type TLUT: Array[0..19, -255..255] of Integer;
...
var LUT: TLUT; i,j: integer;
...
initialization for i := 0 to 19 do for j := -255 to 255 do LUT[i,j] := round(j / 20 * (i+1)) end. |
Oder hab ich jetzt was falsch verstanden? :roll:
|
|
Phantom1
      
Beiträge: 390
|
Verfasst: Fr 27.02.04 20:45
@obbschtkuche:
Und wo ist da jetzt der vorteil? du berechnest 20mal die LUT, das ist bei meinem Code genauso (bei 20 durchgängen).
Verstehst du jetzt was ich meine? außerdem kostet es nichtmal 1 millisekunde, selbst wenn ich 100mal die LUT berechne
|
|
obbschtkuche
Gast
Erhaltene Danke: 1
|
Verfasst: Fr 27.02.04 21:21
Verstehst du mich nicht? :roll: Bei einem Bildschirmschoner wird nicht nur ein Bild gefadet und dann ist schluss, sondern in der Regel folgen mehrere Übergänge aufeinander. Dadurch muss nur einmal am Anfang der LUT berechnet werden und danach können die Werte daraus genommen werden.
|
|
Phantom1
      
Beiträge: 390
|
Verfasst: Fr 27.02.04 21:34
Achso ja, daran hatte ich jetzt nicht gedacht. Aber eigentlich kann man das eh vernachlässigen, denn wie gesagt sebst 100 mal die LUT berechnen kostet nichtmal 1 millisekunde. Wir sollten uns daher auf die wirklichen Geschwindigkeitsbremsen konzentrieren.
|
|
|