![]() |
Geht das noch schneller? - Bitmap-Verrechnung
Mit dieser Funktion wird eine 32-Bitmap - unter Berücksichtigung der Transparenz auf einen Konstanten Untergrund (die Hintergrund-Bitmap) gemalt.
Mal abgesehen von Parallel-Berechnung mit TParallel (was ich schon erledigt habe, hier die einfache Version, um die Funktion leichter nachvollziehen zu können), kann man das noch irgendwie schneller berechnen?
Delphi-Quellcode:
// Setz vorraus, dass die Bitmaps die gleiche Größe haben!!!
procedure Draw32BitToBitmapold(const BitOben: TBitmap; BitUnten: TBitmap); var h,w,i: Integer; RGBA_Unten, RGBA_Oben: pRGBALine; begin For h := 0 to BitUnten.Height-1 do begin RGBA_Unten := BitUnten.ScanLine[h]; RGBA_Oben := BitOben.ScanLine[h]; For w:= 0 to BitUnten.Width-1 do begin if RGBA_Oben^[w].rgbReserved = 0 then begin // unten bleibt end else begin i := Round (RGBA_Unten^[w].rgbBlue - ((RGBA_Unten^[w].rgbBlue - RGBA_Oben^[w].rgbBlue) / 255 * RGBA_Oben^[w].rgbReserved)); if i < 0 then RGBA_Unten^[w].rgbBlue := 0 else if i > 255 then RGBA_Unten^[w].rgbBlue := 255 else RGBA_Unten^[w].rgbBlue := i; i := Round (RGBA_Unten^[w].rgbGreen - ((RGBA_Unten^[w].rgbGreen - RGBA_Oben^[w].rgbGreen) / 255 * RGBA_Oben^[w].rgbReserved)); if i < 0 then RGBA_Unten^[w].rgbGreen := 0 else if i > 255 then RGBA_Unten^[w].rgbGreen := 255 else RGBA_Unten^[w].rgbGreen := i; i := Round (RGBA_Unten^[w].rgbRed - ((RGBA_Unten^[w].rgbRed - RGBA_Oben^[w].rgbRed) / 255 * RGBA_Oben^[w].rgbReserved)); if i < 0 then RGBA_Unten^[w].rgbRed := 0 else if i > 255 then RGBA_Unten^[w].rgbRed := 255 else RGBA_Unten^[w].rgbRed := i; RGBA_Unten^[w].rgbReserved := 255; end; end; end; end; |
AW: Geht das noch schneller? - Bitmap-Verrechnung
schneller wird es wenn man die Formeln umbau so dass mit Integer anstatt Floats gerechnet wird,
und die geschachtelten Abfragen so umstellen dass das wahrscheinlich häuftigste Ergebnis zuerst abgeprüft wird um den 2. Vergleich zu vermeiden |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Mhh, wenn ich integer verwende, wird es ungenau. Aber wäre vielleicht akeptabel, wenn es nur um Anzeige eines Ergebnisses geht ( im Unterschied zu einem konkret weiterverwendbaren Ergebnis).
Das wahrscheinlichste Ergebis ist, dass der Wert zwischen 0 und 255 liegt, aber das muss ich zuvor durch Überprüfung sicherstellen, komme da also nicht dran vorbei... |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Hast du eigentlich schon mal mit AQTime geprüft?
Ich vermute wenn du für RGBA_Unten^[w] und Co. Lokale Variablen verwendest wird es auch schneller |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Parallele Berechnung mehrerer Bytes = Streaming-Funktionen ala 3DNow!, MMX, SSE, AVX, FMA :stupid:
Da kann man "gleichzeitig", in einem Thread, mit 128 Bit (z.B. 8 Byte) oder gar 256 Bit rechnen, also mit mindestens 2 Color-Dingern (a 4 Bytes) in einem Rutsch. |
AW: Geht das noch schneller? - Bitmap-Verrechnung
@ Berhard: Klar, AQTime (Prof) ist mein ständiger Begleiter.
Lokale Variablen zu verwenden, bringt im vorliegenden Fall leider nichts, weil die Zuweisung an die lokalen Variablen in der Summe länger braucht (ca. 30%), als der Zugriff auf den Speicher:
Delphi-Quellcode:
procedure Draw32BitToBitmapNew(const BitOben: TBitmap; BitUnten: TBitmap);
var h,w,i: Integer; rublue, rugreen, rured, rureserved, roblue, rogreen, rored, roreserved: Byte; RGBA_Unten, RGBA_Oben: pRGBALine; begin For h := 0 to BitUnten.Height-1 do begin RGBA_Unten := BitUnten.ScanLine[h]; RGBA_Oben := BitOben.ScanLine[h]; For w:= 0 to BitUnten.Width-1 do begin if RGBA_Oben^[w].rgbReserved = 0 then begin // unten bleibt end else begin rublue := RGBA_Unten^[w].rgbBlue; rugreen := RGBA_Unten^[w].rgbGreen; rured := RGBA_Unten^[w].rgbred; rureserved := RGBA_Unten^[w].rgbreserved; roblue := RGBA_Oben^[w].rgbBlue; rogreen := RGBA_Oben^[w].rgbGreen; rored := RGBA_Oben^[w].rgbred; roreserved := RGBA_Oben^[w].rgbreserved; i := Round (ruBlue - ((ruBlue - roBlue) / 255 * roReserved)); if i < 0 then RGBA_Unten^[w].rgbBlue := 0 else if i > 255 then RGBA_Unten^[w].rgbBlue := 255 else RGBA_Unten^[w].rgbBlue := i; i := Round (ruGreen - ((ruGreen - roGreen) / 255 * roReserved)); if i < 0 then RGBA_Unten^[w].rgbGreen := 0 else if i > 255 then RGBA_Unten^[w].rgbGreen := 255 else RGBA_Unten^[w].rgbGreen := i; i := Round (ruRed - ((ruRed - roRed) / 255 * roReserved)); if i < 0 then RGBA_Unten^[w].rgbRed := 0 else if i > 255 then RGBA_Unten^[w].rgbRed := 255 else RGBA_Unten^[w].rgbRed := i; RGBA_Unten^[w].rgbReserved := 255; end; end; end; end; |
AW: Geht das noch schneller? - Bitmap-Verrechnung
@himitsu:
MMX,SSE Streaming-Funktionen: Noch nie verwendet. Wie nutzt man das? Muss ich Assembler verwenden? Gibt es Beispiele für Bitmap-Verwendung? |
AW: Geht das noch schneller? - Bitmap-Verrechnung
man nehme einfach "MULDIV" für solche Berechnungen in Integer ohne Genauigkeitsverlust.
aus: i := Round (RGBA_Unten^[w].rgbBlue - ((RGBA_Unten^[w].rgbBlue - RGBA_Oben^[w].rgbBlue) / 255 * RGBA_Oben^[w].rgbReserved)); wird dann: i := RGBA_Unten^[w].rgbBlue - MulDiv(RGBA_Unten^[w].rgbBlue - RGBA_Oben^[w].rgbBlue, RGBA_Oben^[w].rgbReserved, 255); Mit Zusatzvariable und nur einer Dereferenzierung am Schleifenanfang, also "col:=RGBA_Unten^[w]" und dann nur mit "col.rgbXXX" rechnen wird es nochmal schneller. Die hier oft verwendete "255" finde ich nicht ganz so dolle, denn 0..255 sind 256 Werte, also setze man doch lieber alle "0..255" Werte mit 256 ins Verhältnis, dann gibt es auch keine Resultate > 255... :) Ansonsten rechne ich das lieber komplett auf Speicherarrays und verwende simple Indexzugriffe auf vor berechnete Wertetabellen(statt ständiges "MulDiv" von zwei "8Bit" Werten) sowie das Ganze möglichst als vorzeichenlose Ganzzahlarithmetik, das ist noch schneller und damit sind dann auch so Sachen wie SSEx für weiter Filteroperationen und andere Spielereien möglich. Das führt aber hier zuweit. Einen Blick in die SourceCodes von optimierten Grafiklibs würde ich da empfehlen. |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Ob es dafür bereits fertige Bibliotheken gibt, weiß ich nicht, aber am Ende kommt es auf Assembler hinaus, da Pascal das natürlich nicht direkt unterstützt (ob du das nunb schreibst, oder jemand anderes)
Ich war mal vor Jahren auf die Idee gekommen, die Operationen in einem Record zu kapseln (Record Operatoren und Methoden), aber da ich selber keinen Anwendungsfall und nicht wirklich Zeit hatte ....... Bezüglich des MulDiv: Jupp, solange du bei Integer-Operationen bleibst, sollte es etwas schneller gehen. Bezüglich dem (Auf)Runden kann man einfach bissl was Aufaddieren, vor dem Dividieren. |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Also, nur statt "/" ein div zu nehmen und das round wegzulassen (also nur mit Integer zu rechnen), benötigt ca. 30% mehr Zeit.
Mit MultiDiv zu arbeiten, dauert gar direkt 100% länger. Aber die anderen beschriebenen Ansätze werde ich auch mal versuchen. |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Selbst dieser Versuch, einen lokalen TRGBQuad zu verwenden (cu- und co-Variablen), führt bei 5 Durchläufen, bei einem Bitmap von ca. 3500x2500 Pixel zu 300 MS mehr Zeit:
Delphi-Quellcode:
procedure Draw32BitToBitmapNew(const BitOben: TBitmap; BitUnten: TBitmap);
var h,w,i: Integer; RGBA_Unten, RGBA_Oben: pRGBALine; cu, co : TRGBQuad; begin For h := 0 to BitUnten.Height-1 do begin RGBA_Unten := BitUnten.ScanLine[h]; RGBA_Oben := BitOben.ScanLine[h]; For w:= 0 to BitUnten.Width-1 do begin if RGBA_Oben^[w].rgbReserved = 0 then begin // unten bleibt end else begin cu := RGBA_Unten^[w]; co := RGBA_Oben^[w]; i := Round (cu.rgbBlue - ((cu.rgbBlue - co.rgbBlue) / 255 * co.rgbReserved)); if i < 0 then RGBA_Unten^[w].rgbBlue := 0 else if i > 255 then RGBA_Unten^[w].rgbBlue := 255 else RGBA_Unten^[w].rgbBlue := i; i := Round (cu.rgbGreen - ((cu.rgbGreen - RGBA_Oben^[w].rgbGreen) / 255 * co.rgbReserved)); if i < 0 then RGBA_Unten^[w].rgbGreen := 0 else if i > 255 then RGBA_Unten^[w].rgbGreen := 255 else RGBA_Unten^[w].rgbGreen := i; i := Round (cu.rgbRed - ((cu.rgbRed - co.rgbRed) / 255 * co.rgbReserved)); if i < 0 then RGBA_Unten^[w].rgbRed := 0 else if i > 255 then RGBA_Unten^[w].rgbRed := 255 else RGBA_Unten^[w].rgbRed := i; RGBA_Unten^[w].rgbReserved := 255; end; end; end; end; |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Zitat:
Ich vermute mal, eine x in 0..255 Prüfung braucht mehr Zeit und ich weiß dann immer noch nicht, ob x evtl. kleiner als 0 (dann schwarz) oder größer 255 ist (dann weiß). |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Schon mal bei/mit Graphics32 versucht?!
|
AW: Geht das noch schneller? - Bitmap-Verrechnung
länger:
Ist ein bissl erschreckend, aber liegt auch an deiner Speicherverwaltung. (zuviele ungünstig liegende Variablen) Integer-Operationen werden in den CPU-Registern (EAX usw.) durchgeführt. Die Fließkommaoperationen in den FPU-Registern, wobei EAX und Co. frei bleibt. Das Selbe würde dann auch bei den Streaming-Registern auftreten. Und warum eigentlich ständig
Delphi-Quellcode:
und nicht Anfangs mal in eine Variable?
RGBA_Unten^[w]
|
AW: Geht das noch schneller? - Bitmap-Verrechnung
Ich verwende "/256" statt "/255".
MulDiv ist live wegen der internen multiplen 32Bit über Int64 Logik Langsamer als aktuelle FPU Nutzung, aber mit ging es ums erkennen des Prinzips! wenn aus "Round (RGBA_Unten^[w].rgbBlue - ((RGBA_Unten^[w].rgbBlue - RGBA_Oben^[w].rgbBlue) / 255 * RGBA_Oben^[w].rgbReserved));" besser "RGBA_Unten^[w].rgbBlue - ((RGBA_Unten^[w].rgbBlue - RGBA_Oben^[w].rgbBlue) * RGBA_Oben^[w].rgbReserved) div 255; //<div 256> oder besser <shr 8>;" wird... also geht das auch in 32Bit(Integer) ohne Muldiv völlig ohne Genauigkeitsverlust, weil eben ohne Informationsverlust erst multipliziert und dann dividiert wird. Nochmal im Detail: - du hast zwei 8Bit (Farb)Werte, die können multipliziert max. einen 16Bit Wert ergeben(0.65535), welcher Locker in eine 32 Bit passt - ein "SHR 8" ist minimal schneller wie ein "DIV 256" - mit glatten 2er Potenzen zu rechnen schadet nie Also: Du hast 2 Farbwerte welche du multiplizierst und fix wieder durch 255teislt(oder 256 bzw. um 8 rechts schiebst)... das geht, ist aber per möglicher Tabelle 1 aus 65536(2^16) im Indexbereich von 0.65535 mit vor berechneten Einträgen am schnellsten: RelColResult:=RelColTable[(ColValueA shl 8)+ColValueB]; ("RelColTable" sein ein Array 0..65535 of BYTE) (Ich rechne das irgendwie anders, deshalb erschließt sich mir der Sinn deine nachfolgenden Abfrage aus ">255" nicht, denn mir fällt kein Wertepaar der 2 Ausgangsfarben ein, wo das Ergebnis >255 sein kann... Nur weil Round auch "aufrundet" passiert dir das eventuell... Ich denke wenn du Round durch Floor ersetzt kannst du dir das "if" sparen und Kommafarbwerte gibt es bei 8Bit 0.255 eh nicht :) ) |
AW: Geht das noch schneller? - Bitmap-Verrechnung
wenn ich Round durch Floor ersetze, dauert es 3 mal so lang...
|
AW: Geht das noch schneller? - Bitmap-Verrechnung
Ich glaube, Du könntest Recht haben, mit der Vermutung, dass es eigentlich nie größer als 255 werden kann, da ja von einem Ausgangswert, der nie größer als 255 sein kann, immer etwas abgezogen wird. Also muss man in der Tat nur den <0 Fall berücksichtigen.
Auch /256 statt /255 müsste richtig sein, da es ja um die Anzahl der möglichen Farbabstufungen geht. Super!! Das umgesetzt werden laut AQTime pro Aufruf der Funktion ca. 20 MS gespart:-D Das Ding sieht jetzt also so aus:
Delphi-Quellcode:
procedure Draw32BitToBitmapNew(const BitOben: TBitmap; BitUnten: TBitmap);
var h,w,i: Integer; RGBA_Unten, RGBA_Oben: pRGBALine; begin for h := 0 to BitUnten.Height-1 do begin RGBA_Unten := BitUnten.ScanLine[h]; RGBA_Oben := BitOben.ScanLine[h]; For w:= 0 to BitUnten.Width-1 do begin if RGBA_Oben^[w].rgbReserved = 0 then begin // unten bleibt end else begin i := Round (RGBA_Unten^[w].rgbBlue - ((RGBA_Unten^[w].rgbBlue - RGBA_Oben^[w].rgbBlue) / 256 * RGBA_Oben^[w].rgbReserved)); if i < 0 then RGBA_Unten^[w].rgbBlue := 0 else RGBA_Unten^[w].rgbBlue := i; i := Round (RGBA_Unten^[w].rgbGreen - ((RGBA_Unten^[w].rgbGreen - RGBA_Oben^[w].rgbGreen) / 256 * RGBA_Oben^[w].rgbReserved)); if i < 0 then RGBA_Unten^[w].rgbGreen := 0 else RGBA_Unten^[w].rgbGreen := i; i := Round (RGBA_Unten^[w].rgbRed - ((RGBA_Unten^[w].rgbRed - RGBA_Oben^[w].rgbRed) / 256 * RGBA_Oben^[w].rgbReserved)); if i < 0 then RGBA_Unten^[w].rgbRed := 0 else RGBA_Unten^[w].rgbRed := i; RGBA_Unten^[w].rgbReserved := 255; end; end; end; end; |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Jetzt noch mal Ergebnisse bei Parallelisierung des ganzen mit TParallel:
Auf 2-Kern-Rechner von ca. 300 MS auf 150 Auf 4-Kern-Rechner von ca. 300 MS auf 100 Auf 6-Kern-Rechner von ca. 300 MS auf 56 Also fast 6 mal so schnell!! Jetzt lohnt sich das mit den Kernen so richtig, insbesondere bei den rechenintensiven Grafikbearbeitungsaufgaben eine echt hilfreiche Sache. Also was das angeht, bringt XE7 voll den Turbo!!:thumb: Mein nächster Entwicklungsrechner wird also mit mindestens 6 Kernen kommen... |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Eine Bitte:
mach dir mal für mich die Mühe und "messe" die Zeiten noch für diese 2 Varianten: i := RGBA_Unten^[w].rgbBlue - (((Int(RGBA_Unten^[w].rgbBlue - RGBA_Oben^[w].rgbBlue) * Int(RGBA_Oben^[w].rgbReserved)) div 256); und i := RGBA_Unten^[w].rgbBlue - (((Int(RGBA_Unten^[w].rgbBlue - RGBA_Oben^[w].rgbBlue) * Int(RGBA_Oben^[w].rgbReserved)) shr 8); (für je alle 3 Farben halt) Das wird genauso exakt und "müsste" schneller sein... Das "div 256" und "shr 8" könnte sogar der Compiler optimieren, wenn es keinen Unterschied machen sollte. Bin gespannt auf deine Ergebnisse:) |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Mache ich doch gerne. Rückmeldung zu Variante 1+2:
E2015: Operator ist auf diesen Operandentyp nicht anwendbar. |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Sorry, der "Sicherheits-Cast" muss Integer(...) und nicht int(xxx) heißen!
also i := RGBA_Unten^[w].rgbBlue - (((Integer(RGBA_Unten^[w].rgbBlue - RGBA_Oben^[w].rgbBlue) * Integer(RGBA_Oben^[w].rgbReserved)) div 256); und i := RGBA_Unten^[w].rgbBlue - (((Integer(RGBA_Unten^[w].rgbBlue - RGBA_Oben^[w].rgbBlue) * Integer(RGBA_Oben^[w].rgbReserved)) shr 8); (ich sollte nicht so viel C++ programmieren;) ) |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Liste der Anhänge anzeigen (Anzahl: 2)
Die Variante mit SHR ist compilierbar, ist auch schneller, aber das Ergebnis ist falsch, siehe anliegende Screenshots.
Das habe ich von Dir da verwendet:
Delphi-Quellcode:
procedure Draw32BitToBitmapNew(const BitOben: TBitmap; BitUnten: TBitmap);
var h,w,i: Integer; RGBA_Unten, RGBA_Oben: pRGBALine; begin for h := 0 to BitUnten.Height-1 do begin RGBA_Unten := BitUnten.ScanLine[h]; RGBA_Oben := BitOben.ScanLine[h]; For w:= 0 to BitUnten.Width-1 do begin if RGBA_Oben^[w].rgbReserved = 0 then begin // unten bleibt end else begin i := RGBA_Unten^[w].rgbBlue - (((Integer(RGBA_Unten^[w].rgbBlue - RGBA_Oben^[w].rgbBlue) * Integer(RGBA_Oben^[w].rgbReserved)) shr 8)); if i < 0 then RGBA_Unten^[w].rgbBlue := 0 else RGBA_Unten^[w].rgbBlue := i; i := RGBA_Unten^[w].rgbGreen - (((Integer(RGBA_Unten^[w].rgbGreen - RGBA_Oben^[w].rgbGreen) * Integer(RGBA_Oben^[w].rgbReserved)) shr 8)); if i < 0 then RGBA_Unten^[w].rgbGreen := 0 else RGBA_Unten^[w].rgbGreen := i; i := RGBA_Unten^[w].rgbred - (((Integer(RGBA_Unten^[w].rgbred - RGBA_Oben^[w].rgbred) * Integer(RGBA_Oben^[w].rgbReserved)) shr 8)); if i < 0 then RGBA_Unten^[w].rgbRed := 0 else RGBA_Unten^[w].rgbRed := i; RGBA_Unten^[w].rgbReserved := 255; end; end; end; end; |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Und die erste Variante ist nach wie vor nicht kompilierbar. Zwar akzeptiert der Compiler die erste Zeile, bei der zweiten sagt er aber "E2014: Anweisung erforderlich aber Ausdruck vom Typ Integer gefunden"
|
AW: Geht das noch schneller? - Bitmap-Verrechnung
Wenn der Compiler sagt, das da Irgendwo kein Integer ist funktioniert das mit "shr 8" nicht... morgen werfe ich ml kurz mein Delphi an und finde das "Syntax" Problem... muss ja meine Pascalsyntax "schulen".
Auch macht SHR Mist, bei negativen Zahlen... ich tippe es mal komplett als "unsigned int" ähhh "cardinal"... muss aber erst noch verstehen, "warum" du aktuell den Weg über negative werte gehst... meine Routine welche ich irgendwo habe macht die Größenprüfung irgendwie vor der Differenz und bearbeitet mit angepasster Folgelogik dann nur positive Werte... Setze doch mal einen Breakpunkt auf den Zweig "wenn kleiner 0" und schreibe gib mir mal die Farb und Trasparenz-Werte als Beispiel. Konstruieren kann ich mir das auch, aber möchte mal deine Zahlen in meine Routine stecken. |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Bei so vielen Operationen mit Bitmaps würde ich auch recht schnell zur schon erwähnten Graphics32 greifen. Insbesondere die Überblendungsoperationen sind da extrem flott umgesetzt, genaue Zahlen habe ich aber nicht an der Hand zum vergleichen. Parallel-Processing macht die von Hause aus zwar nicht, ließe sich aber bestimmt auf kleinen Umwegen auch machen. Wenn es dann überhaupt noch nötig ist.
|
AW: Geht das noch schneller? - Bitmap-Verrechnung
Liste der Anhänge anzeigen (Anzahl: 2)
Ich habe mal die Bilder was kleiner gemacht, damit ich sie noch in die Form integrieren kann (im Original sind sie ca. jeweils 30 MB groß, da weigert sich Delphi) und das Testprojekt mal hier hochgeladen.
Auf dem Screenshot kann man sehen: Alt gibt die Zeit für die Ursprungs-Procedure vor. Optimiert ist das, wo wir uns gerade bemühen (im Source jetzt gleich mit "Alt") und Par ist die Bearbeitung in Parallel. Zeige "Ergebnis" zeigt das Ergebnis einer der 3 Berechnungsmethoden an, und Prüft bei Optimiert und Parallel, ob das Ergebnis gleich ist mit der Ursprungsfunktion. Zur Info: Der Hintergrund ist ein Farbverlauf, das obere Bitmap ist in den Streifen Teiltransparent. |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Hast du es mal ohne die Prüfung auf i < 0 probiert? Der Fall sollte ja eigentlich nicht vorkommen
Ansonsten habe ich noch Vorschläge: 1. Termumformung, sollte aber eigentlich nicht schneller sein. Ist aber imho etwas verständlicher
Delphi-Quellcode:
etc. für die anderen Farben.
RGBA_Unten^[w].rgbBlue := Cardinal((255 - RGBA_Oben^[w].rgbReserved) * RGBA_Unten^[w].rgbBlue + RGBA_Oben^[w].rgbReserved * RGBA_Oben^[w].rgbBlue) shr 8;
2. Nimm mal nicht immer die gleiche Variable i, sondern drei verschiedene Variablen. Dann können die drei Zeilen parallel ablaufen. 3. Fall die if-Abfrage doch nötig sein sollte, schreibe sowas:
Delphi-Quellcode:
Effekt: Wenn der Wert negativ wird, dann wird das obere Byte des ints aus einsen bestehen. Durch das bitweise and wird der Wert zu 0. Du vermeidest aber das if, das auf Maschinenebene möglicherweise teuer ist.
RGBA_Unten^[w].rgbBlue := i and (not i shr 24)
4. Sagt wir "premultiplied alpha" etwas? Klingt erstmal komisch, ist aber eigentlich die sinnvollere Variante. Würde deinen Rechenaufwand auch hier reduzieren, aber deine Bilddaten müssten dafür natürlich angepasst werden. |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Ich habe einige sehr einfache Änderungen vorgenommen. Bei mir benötigt der Algorithmus dann nur noch die halbe Zeit. Das Ergebnis ist nicht identisch mit dem alten Algorithmus. Die Ursache liegt meiner Meinung nach im Floatingpoint-Rundungsalgorithmus begründet. Das Bild sieht aber nicht offensichtlich falsch aus.
Delphi-Quellcode:
procedure Draw32BitToBitmapSamso(const BitOben: TBitmap; BitUnten: TBitmap);
var h, w, i: Integer; RGBA_Unten, RGBA_Oben: PRGBQuad; begin for h := 0 to BitUnten.Height-1 do begin RGBA_Unten := BitUnten.ScanLine[h]; RGBA_Oben := BitOben.ScanLine[h]; For w:= 0 to BitUnten.Width-1 do begin if RGBA_Oben^.rgbReserved = 0 then begin // unten bleibt end else begin i := RGBA_Unten^.rgbBlue - (RGBA_Unten^.rgbBlue - RGBA_Oben^.rgbBlue) * RGBA_Oben^.rgbReserved div 256; if i < 0 then RGBA_Unten^.rgbBlue := 0 else RGBA_Unten^.rgbBlue := i; i := RGBA_Unten^.rgbGreen - (RGBA_Unten^.rgbGreen - RGBA_Oben^.rgbGreen) * RGBA_Oben^.rgbReserved div 256; if i < 0 then RGBA_Unten^.rgbGreen := 0 else RGBA_Unten^.rgbGreen := i; i := RGBA_Unten^.rgbRed - (RGBA_Unten^.rgbRed - RGBA_Oben^.rgbRed) * RGBA_Oben^.rgbReserved div 256; if i < 0 then RGBA_Unten^.rgbRed := 0 else RGBA_Unten^.rgbRed := i; RGBA_Unten^.rgbReserved := 255; end; inc(RGBA_Unten); inc(RGBA_Oben); end; end; end; |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Erst mal vielen Dank an Euch fürs mit überlegen, ich finde es echt spannend, hier die anderen Ansätze mal zu sehen. Jede der zuletzt genannten Varianten war immer ein weniger schneller als die Ausgangsfunktion.
Ich habe jetzt einfach mal bei ImageEn nachgesehen, die ich hier lizenziert habe, wie die das machen. Da ist das Bild zwar auch geringfügig unterschiedlich als meine Ausgangsfunktion, aber vielleicht war meine ja auch nicht korrekt. Jedenfalls in Anlehnung an die ImageEn-Lösung kann man das hier so machen ist das ist dann mit 71 MS die bislang schnellste Lösung und toppt sogar die Parallel-Lösung. Da ist dann irgendwann anscheinend der Zeitbedarf für das Management des Threading größer, als der Zeitgewinn durch den Einsatz mehrerer Kerne. Insofern muss man sich genau ansehen, wann man die Parallelisierung einsetzt. Besserer Code geht also meistens doch noch vor Parallel-Bearbeitung. Wahrscheinlich lohnt sich die Parallelisierung eher bei Prozessen, die eh schon etwas länger dauern.
Delphi-Quellcode:
// Setz vorraus, dass die Bitmaps die gleiche Größe haben!!!
procedure Draw32BitToBitmap(const BitOben: TBitmap; BitUnten: TBitmap); var h,w,i: Integer; RGBA_Unten, RGBA_Oben: pRGBALine; begin For h := 0 to BitUnten.Height-1 do begin RGBA_Unten := BitUnten.ScanLine[h]; RGBA_Oben := BitOben.ScanLine[h]; For w:= 0 to BitUnten.Width-1 do begin if RGBA_Oben^[w].rgbReserved = 0 then begin // unten bleibt end else begin (* Das war die Ausgangslösung i := Round (RGBA_Unten^[w].rgbBlue - ((RGBA_Unten^[w].rgbBlue - RGBA_Oben^[w].rgbBlue) / 255 * RGBA_Oben^[w].rgbReserved)); if i < 0 then RGBA_Unten^[w].rgbBlue := 0 else if i > 255 then RGBA_Unten^[w].rgbBlue := 255 else RGBA_Unten^[w].rgbBlue := i; i := Round (RGBA_Unten^[w].rgbGreen - ((RGBA_Unten^[w].rgbGreen - RGBA_Oben^[w].rgbGreen) / 255 * RGBA_Oben^[w].rgbReserved)); if i < 0 then RGBA_Unten^[w].rgbGreen := 0 else if i > 255 then RGBA_Unten^[w].rgbGreen := 255 else RGBA_Unten^[w].rgbGreen := i; i := Round (RGBA_Unten^[w].rgbRed - ((RGBA_Unten^[w].rgbRed - RGBA_Oben^[w].rgbRed) / 255 * RGBA_Oben^[w].rgbReserved)); if i < 0 then RGBA_Unten^[w].rgbRed := 0 else if i > 255 then RGBA_Unten^[w].rgbRed := 255 else RGBA_Unten^[w].rgbRed := i; *) RGBA_Unten^[w].rgbred := (RGBA_Oben^[w].rgbReserved * (RGBA_Oben^[w].rgbred - RGBA_Unten^[w].rgbred) shr 8 + RGBA_Unten^[w].rgbred); RGBA_Unten^[w].rgbGreen := (RGBA_Oben^[w].rgbReserved * (RGBA_Oben^[w].rgbgreen - RGBA_Unten^[w].rgbGreen) shr 8 + RGBA_Unten^[w].rgbGreen); RGBA_Unten^[w].rgbBlue := (RGBA_Oben^[w].rgbReserved * (RGBA_Oben^[w].rgbBlue - RGBA_Unten^[w].rgbBlue) shr 8 + RGBA_Unten^[w].rgbBlue); RGBA_Unten^[w].rgbReserved := 255; end; end; end; end; |
AW: Geht das noch schneller? - Bitmap-Verrechnung
super... das war eigentlich exakt das was ich dir gerade als "Berechnungslösung" vorschlagen wollte, denn auch wenn ich das vor Jahren woanders her kopiert habe... das sieht einfach schön klar aus:)
Ich würde aber eine zusätzliche Sicherheitsklammerung einführen, damit auch wirklich erst multipliziert wird und erst dann dividiert(shr 8) wird... das sollte genauer sein, wenn hier bicht absichtlich mit den BitUmschlag im Shift gerechnet wird. RGBA_Unten^[w].rgbred := ((RGBA_Oben^[w].rgbReserved * (RGBA_Oben^[w].rgbred - RGBA_Unten^[w].rgbred)) shr 8 + RGBA_Unten^[w].rgbred); RGBA_Unten^[w].rgbGreen := ((RGBA_Oben^[w].rgbReserved * (RGBA_Oben^[w].rgbgreen - RGBA_Unten^[w].rgbGreen)) shr 8 + RGBA_Unten^[w].rgbGreen); RGBA_Unten^[w].rgbBlue := ((RGBA_Oben^[w].rgbReserved * (RGBA_Oben^[w].rgbBlue - RGBA_Unten^[w].rgbBlue)) shr 8 + RGBA_Unten^[w].rgbBlue); Einen Versuch wäre es wert:) |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Zitat:
Im Ernst: Mit Sicherheitsklammerung wird ein Programm nicht besser, nur der WTF-Faktor* erhöht sich. Nun ist es aber gerade erstrebenswert, den WTF-Faktor auf 0 zu setzen. (*) WTF-Faktor: WTF-Shouts / LoC. PS: Muldiv hat eingebaute Sicherheitsklammerung. |
AW: Geht das noch schneller? - Bitmap-Verrechnung
=> solange alles es in der Wertebereich der Operatoren passt, bei Ganzzahl-Arithmetik IMMER erst "vergrößern" (also addieren oder multiplizieren oder "shl") und dann verkleinern(also subtrahieren, dividieren oder "shr")
=> weil Pascal hier nicht immer das mach was ich will oder ich auch oft zu nur faul zum Nachzudenken, was der Kompiler draus macht, gebe ich es lieber vor), klammere da lieber einmal zuviel wie zu wenig, außerdem merkt so ein anderer welcher das liest, das man hier besonderen Wert auf die Rechenreihefolge gelegt hat und kommt nicht auf die Idee, weil's eventuell schöner "aussieht" aus "100 * 128 div 256" etwa "128 div 256 * 100" zu machen... mit "WTF" hat dies also nix zu tun! => Mein Vertrauen in die math. & logische Hierarchie der Operatoren von Pascal ist leider etwas getrübt, denn wenn man von C/C++ kommt ist das in Pascal bei AND & OR ständig nötige Klammern, weil hier kein "And vor Or" gilt sehr lästig... für mich echt vergleichbar dem wenn der Compiler nicht Punkt vor Strich rechnen könnte... daher klammere ich zur Sicherheit in Pascal eben da wo es mir drauf ankommt noch etwas mehr wie in C/C++. Hier im Fall wird es nicht "richtiger", sondern WorstCase falsch, auch wenn mathematisch oder mit Fließkomma es an sich hier völlig egal ist ob man erst multipliziert oder erst dividiert. Bei Ganzzahl-Arithmetik spielt es eben doch eine Rolle, denn wir haben in diesem Fall einen begrenzten Rechenwertebereich von 32Bit, jeder Operator hat einen Wertebereich von zum Glück nur 8Bit, das heißt dann hier im Detail für ein mathematisch eigentlich identisches Beispiel: 128 div 256 * 100 = !0! weil (128 div 256) * 100 = !0! aber 128*100 div 256 = !50! weil (128*100) div 256 = !50! Und da finde ich die sicherheitsgeklammerte Schreibweise sehr sinnvoll, weil wenn das keine kurzen Zahlen sondern lange Variablennamen in einer Kette von Operatoren sind, erschließt sich ohne Klammern die Konsequenz falscher Rechenreihenfolge nicht mehr jedem Codebetrachter sofort. |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Es wird von links nach rechts ausgewertet, da werden keine Klammern benötigt. So einfach kann die Sinnlosigkeit der 'Sicherheitsklammern' zusammenfassen. Der Compiler macht das so. Genau so. Jedes mal. Bei C(++) mögen deine Sicherheitsklammern ja sinnvoll sein, aber bei Delphi nicht.
Dein fehlendes Vertrauen könntest Du durch einmaliges Ausprobieren aufbauen, aber dazu bist Du ja leider zu faul (deine Worte). Daher meine Bitte: Verbreite aber deine Faulheit und deine dadurch bedingte Unsicherheit nicht weiter, insbesondere nicht durch Tipps, wie 'Sicherheitsklammern'. Klammen sind entweder sinnvoll oder sinnlos. 'Zur Sicherheit' werden diese nicht benötigt. Zur Lesbarkeit: Jeder mathematisch halbwegs versierte Programmierer wird hier WTF rufen, denn alles, was überflüssig ist, ist eben 'falsch' (weil überflüssig). Lies mal Bücher über sauberes Programmieren, und was genau den Unterschied zwischen einem sauberen Programm und Schrottcode ausmacht (nämlich genau der WTF-Faktor). Was Du natürlich in deiner Freizeit und privat auf deinem Rechner machst, bleibt Dir überlassen. Klammere ruhig zu viel als zu wenig. Wie wäre es noch mit Sicherheitsblöcken à la "Begin End" (man kann ja nie wissen)? Oder doppelte Zuweisungen, damit es richtig hält. :stupid: |
AW: Geht das noch schneller? - Bitmap-Verrechnung
... Grundgütiger - was für eine schroffe Zurechtweisung. War die in dieser Schärfe tatsächlich nötig?
Sei Dir bitte bewusst, dass es sich hier um Deine Meinung handelt - ich beispielsweise finde sehr wohl, dass sinnvoll gesetzte Klammern zur Lesbarkeit beitragen können, selbst wenn sie mathematisch nicht erforderlich sind. |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Zitat:
Klammer haben Vorrang vor Allem, dann kommt nacheinander die Gruppe der unären Operatoren
Delphi-Quellcode:
, dann die Multiplitationsoperatoren
@ not
Delphi-Quellcode:
, danach die Additionen
* / div mod and shl shr as
Delphi-Quellcode:
und zum Schluß die Vergleichsoperationen
+ - or xor
Delphi-Quellcode:
dran,
= <> < > <= >= in is
so wie man es aus dem Matheunterricht kennt. (Klammer, Multiplikation/Division und Addition/Subtraktion) Gut, hier sind * / div und shr gleichrangig, dann stimmt die Aussage zufällig. Und ja, ich mache Klamern auch nur dann, wenn es nötig ist, somit fällt gleich auf, was los ist und man wird nicht unnötig abgelenkt, aber Andere sind sich mit der Reihenfolge nicht sicher und da sind Klammern dann (für sich) sicherer. |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Zitat:
Was Du gut findest, sind aber keine Sicherheitsklammern, sondern Übersichtlichkeitsklammern. Auch 'falsch' (nimm lieber sprechende Zwischenvariablen).... Aber eben I-M-H-O. Das hätte ich stärker betonen müssen. |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Der Befehl "shr 8" bringt nach meinen Tests gegenüber "div 256" keine Verbesserung, weil der Compiler (zumindestens ab XE4) die 2er Potenz korrekt erkennt und selbstständig aus dem div einen entsprechenden Shift-Befehl macht (logisch/arithmetisch bei Cardinal/Integer).
|
AW: Geht das noch schneller? - Bitmap-Verrechnung
MulDiv könnte etwas bringen, aber -wenn überhaupt- nur marginal.
Was auch helfen könnte: Statt 'RGBA_Unten^[w]....' nur 'RGBA_Unten^...' und am Ende ein 'Inc(RGBA_Unten)'... Damit wird der Zeiger weiter bewegt (ich schätze, automatisch um 4 Bytes). allgemein sind Array-Zugriffe fast immer etwas langsamer, als Dereferenzierung über einen Pointer und anschließendes Pointer-Increment. |
AW: Geht das noch schneller? - Bitmap-Verrechnung
Ich hatte mich gestern bei DP angemeldet, weil mich das Thema "Bitmap"
interessiert, ich auch etwas dazu beisteuern kann und ich mich gern an sachlichen Diskussionen beteilige. Heute beschleichen mich etwas gemischte Gefühle, wenn ich mir den Verlauf der Diskussion ansehe. Aus meiner ersten Sicht wirkt das nicht sehr sachdienlich. Als Praktiker kann ich beim Debuggen des CPU-Codes einschätzen, welche Eskapaden der Compiler machte und dann am Quellcode schrauben. um eine 'Linderung' zu versuchen. Auf diese Weise habe ich ein Antialiasing mit MMX-Assemblercode passabel hinbekommen. Ich 'tethere' mit einem Androiden. Es gelingt mir nicht, eine korrekte Zip-Datei eines kompletten Delphi-Projektes oder ein Refernzbitmap abzusaugen. Heruntergeladene Zip-Files werden von RAR, AntekZIP und anderen als inkorrekt abgewiesen. Was habe ich nicht beachtet? |
AW: Geht das noch schneller? - Bitmap-Verrechnung
@manfred42:
Habe mal das Zip-Paket (mit einem anderen ZIP-Programm gepackt) an Deine private Mailadresse geschickt, wie gewünscht. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:50 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz