Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Zwei transparente Bitmaps miteinader verrechnen (https://www.delphipraxis.net/192942-zwei-transparente-bitmaps-miteinader-verrechnen.html)

Harry Stahl 4. Jun 2017 12:44

Zwei transparente Bitmaps miteinader verrechnen
 
Ich krieg es leider nicht selbst hin.

Ausgangslage ist, dass ich zwei transparente Bitmaps habe und möchte das obere nun mit dem unteren Verrechnen.

Dabei interessiert mich nur der Fall, wo mindest ein Bitmap eine Transparenz hat.

Gegeben wären als

Delphi-Quellcode:
var
  Ru, GU, BU, AU: Byte; // für Farb- und Alphawert des unteren ( "U" für unten) Bitmaps.
  RO, GO, BO, AO: Byte; // für Farb- und Alphawert des oberen Bitmaps ("O" für oben)
  RN, GN, BN, AN: Byte; // für Farbe und Alphawert des Ergebnis-Bitmaps ("N" für neu)
begin
  // Werte für Variablen holen
  //...
  // Jetzt berechnen, aber wie?
 
  RN := ...
  GN := ...
  BN := ...
  AN := ...
end;
Jemand eine Idee oder einen Verweis auf eine schon bestehende Lösung?

EWeiss 4. Jun 2017 12:49

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Keine Ahnung was du genau willst..
Aber in meinen GDIClock projekt gibt es diese function.

procedure CombineImage(BackImg, OverlayImg: HBITMAP; LocX, LocY: Integer;
Transparent: Boolean);

EDIT:
Glaube das ist nicht das was du willst.
Verrechnen <> Kombinieren.
---------------
Auf jeden fall benötigst du ja erst mal die Farbwerte die sich in den jeweiligen Bitmaps befinden.
Dafür würde ich zu Anfangs das Handle (HBitmap) zu rate ziehen.

DibSection erstellen hbmReturn wäre dein HBitmap
mit Fillchar alles auf 0 setzen

Delphi-Quellcode:
FillChar(bm, sizeof(bm), 0);
Delphi-Quellcode:
      if GetObject(hbmReturn, sizeof(bm), @bm) <> 0 then
      begin
        pBits := bm.bmBits;
in pBits stehen dann alle Farbwerte bsp. des unteren Bitmaps
Wie man diese ausliest sollte dir bekannt sein.

Anschließen das gleiche bei den anderen, dann die Farbwerte miteinander vergleichen.. .wie auch immer.
Sollte nicht einfach sein vor allem wenn die Bitmaps unterschiedliche Ausmaße haben.
Das sollte man zumindest sicher stellen das sie gleich sind!

gruss

mensch72 4. Jun 2017 14:10

AW: Zwei transparente Bitmaps miteinader verrechnen
 
...ich gehe einfach mal davon aus, das die Werte für alle Pixel "wie und woher auch immer" verfügbar sind...


Teilen wir das Problem dann zunächst:
1. wir bestimmen die korrespondierende Transperenz für das Ergebinisbild
2. wir kombinieren die effektive Farbdeckung der 2 ihrerseits schon transparenten Bilder

- vereinfacht nehme ich jetzt mal als korrespondierende Transperenz den Durchschnitt der Einzeltransparenzwerte
- meine Berechnung funktioniert, wenn man Ax = 255 für 100% "volle Deckung", also null transparenz definiert
- jede Farbekomponente eines Pixels hat pro Ausgangsbild einen Wertebereich von 0..255, macht zusammen mit der Transparenz(Deckung 0..255) einen Wertebereich von 0.65535
- wir rechnen in gleich DWORD und im Wertebereich von 1.. und vermeiden so die Nullwerte und auch um so einfach mit dem vollen Wertebereich zu weiter zu rechnen(das ist der eigentliche und sehr einfache "Trick")

AN:=BYTE(DWORD((DWORD(AU)+DWORD(AO)) div 2));
RN:=BYTE(DWORD(((((DWORD(RU+1)*DWORD(AU+1))+(DWORD (RO+1)*DWORD(AO+1))) div 2) div (DWORD(AN)+1)) - 1));
GN:=BYTE(DWORD(((((DWORD(GU+1)*DWORD(AU+1))+(DWORD (GO+1)*DWORD(AO+1))) div 2) div (DWORD(AN)+1)) - 1));
BN:=BYTE(DWORD(((((DWORD(BU+1)*DWORD(AU+1))+(DWORD (BO+1)*DWORD(AO+1))) div 2) div (DWORD(AN)+1)) - 1));

Prkatische Erfahrung habe ich aber in der Nutzung einer ähnlichen Variante nur, wenn ich als Resultat ein NICHTTRANSPARENTES Bitmap berechne und für den aktzuellen Hintergrund die Pixel als als RH,GH,BH definiere... das ergibt dann dies:

AX:=BYTE(DWORD((DWORD(AU)+DWORD(AO)) div 2));
RN:=BYTE(DWORD((((DWORD(RU+1)*DWORD(AU+1))+(DWORD( RO+1)*DWORD(AO+1))+(DWORD(RH+1)*DWORD(AX+1))) div (3*256))-1));
GN:=BYTE(DWORD((((DWORD(GU+1)*DWORD(AU+1))+(DWORD( GO+1)*DWORD(AO+1))+(DWORD(GH+1)*DWORD(AX+1))) div (3*256))-1));
BN:=BYTE(DWORD((((DWORD(BU+1)*DWORD(AU+1))+(DWORD( BO+1)*DWORD(AO+1))+(DWORD(BH+1)*DWORD(AX+1))) div (3*256))-1));

(professionelle Varianten machen das wohl über/in einem anderem Farbmodell, weil es nach Farbenlehre wohl besser wäre Helligkeit und Farbe separat zu kombinieren)
Wenn ich mich bei den Klammern nicht verzählt habe und mein Gedächtnis stimmt, wird da bei beiden Varianten zumindest etwas mathematisch brauchbares herauskommen:)


ps:
ich weiß, das man wegen Punkt vor Strich ein paar Klammern sparen kann, aber wenn ich schon so unübersichtlich alles in eine Zeile schreibe, reicht mir so wenigstens für die Bearbeitungsfolge einfaches Klammerzählen ohne weitere Gehrinakrobatik;)

EWeiss 4. Jun 2017 14:30

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Da fehlt die Berechnung der Ausmaße der Bitmaps..
Wenn diese unterschiedlich sind.

gruss

mensch72 4. Jun 2017 16:41

AW: Zwei transparente Bitmaps miteinader verrechnen
 
nach Skalierung hat Harry nicht gefragt;)

Letztendlich skaliere man "wie auch immer" zuerst, so dass dann die "Overlayrechnung" quasi 1:1 geschieht... da es eh Pixelweise berechnet wird, kann man das auch in einem Schritt mit 2 äusseren Schleifen rechnen, wo man in "double" hochzählt, aber auf reale GanzzahlStep "pixelweise" X Y rückrundet und davon die Werte[x,y] .r .g .b .a , weil es ja nur ganze Pixel gibt:)

Harry Stahl 4. Jun 2017 17:20

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Liste der Anhänge anzeigen (Anzahl: 3)
Ja, mir geht es nicht um das ganze Bild, sondern nur der Part, wo beide Bilder Transparenzen haben, inwieweit die Bilder sich überlappen oder nicht, wird in der eigentlichen Funktion schon berücksichtigt.

Habe anliegend mal einen ganz ganz groben Auszug aus der Funktion für diese eine Stelle gemacht, und als Projekt angefügt.

Screenshot 1 zeigt im Bild links unten, wie Photoshop im Ergebnis die beiden oberen Bilder verrechnet (so hätte ich es auch gerne).
Im Bild rechts unten wird gezeigt, wie das Ergebnis meiner derzeitigen Routine aussieht. Man erkennt, dass die Überlagerungen der beiden roten Pinselstriche nicht OK ist (in der Mitte müsste sich ein tieferes Rot bilden, auch die Farbüberlagerungen der anderen Pinselstriche - grün und blau über rot - sind nicht 100% OK, die Grenzen sind zu scharf).

In Screenshot 2 habe ich mal den Vorschlag 1 von Mensch72 (vielen Dank dafür schon mal) umgesetzt, führt leider auch nicht zum korrekten Ergebnis (Vorschlag 2 hat eine krasse Farbverfälschung, da stimmt wohl etwas grundsätzlich nicht).

Anmerken möchte ich noch, dass ich die Transparenzen der Bitmaps, die ich aus dem TImage hole (sind dort in einem PNG-Format drin) mit einer Notlösung hier mal hergestellt habe, da gibt es eine leichte Farbverfälschung - etwas zu dunkel - (war aber die einzige Lösung, die mir auf die schnelle einfiel, ohne Dritt-Bibliotheken nutzen zu müssen).

Harry Stahl 4. Jun 2017 17:33

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Liste der Anhänge anzeigen (Anzahl: 2)
Und damit man die Ergebnisse auch ohne die leichte Farbverfälschung ansehen kann, wenn ich meine Routine und die vorgeschlagene verwende, anliegend 2 Screenshots, Original in meinem Programm verwendet (Result3 von mir, Result4 von Mensch72).

mensch72 4. Jun 2017 17:44

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Ja, das entspricht meinen Erwartungen, speziell bei dem 2x "TeilRot" übereinander, wo Photoshop die Schnittmenge bei stimmiger Farbe "intensiver" darstellt, zeugt es davon das dort die Regeln für Farbmischung und Helligkeitsmischung der "Tranparenz" getrennt mit verschiedenen Regeln gerechnet wird... der einfache Durchschnitt zur Transparenzaddition scheint
noch nicht ganz optimal:)


ps:
- man püfe mal ob bei "DWORD(RO+1)" und RO=255 auch 256 raus kommt... das funktioniert nur, wenn RO erst von BYTE auf DWORD hoch gezogen wird und dann die eins addiert wird.
- ich habe das jetzt nur aus dem Gedächtnis getippt und es könnte sein das ich real da nochmal oder anders "geklammert" habe

Michael II 4. Jun 2017 18:18

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Hallo Harry Stahl

falls du dein "Alpha Bitmap A über B" selbst schreiben willst (wenn du's zum Beispiel für FMX und VCL nutzen willst, dann drängt sich das ja eventuell auf ;-)), dann kannst du Porter Duff verwenden:

https://de.wikipedia.org/wiki/Alpha_Blending

Irgendwo gibt's sicher bereits fertige Plattform unabhängige Beispiele für A über B. [Viele Dinge findest du auch im Source Code von Graphics32 u.a..]

Gruss
Michael

Harry Stahl 4. Jun 2017 18:50

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Hallo Michael,

das hatte ich schon befürchtet, dass das ein wenig komplizierter sein wird und mit einer Formel endet, die mir Kopfschmerzen bereiten wird.

Aber jedenfalls Danke für den Tipp(:thumb:), was es nicht alles gibt. Jetzt muss ich also sehen, dass ich die Formel irgendwie auf die RGB-Zahlenwelt übertragen kann...

himitsu 4. Jun 2017 19:31

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Es gibt ja mindestens zwei Varianten wie man sowas verrechnet.

* Entweder die Bilder zu 50:50 vemischen, also als wenn man zwei "flüssige" Farben zusammen schüttet.
Das ergibt praktisch ganz billig 'nen Mittelwert und es ist egal was oben oder unten ist
x := (x + y) / 2
Die Transparent natürlich umgekehrt
x := 255 - ((255 - x) + (255 - y)) / 2

* Oder man macht es richtig, so als wenn man zwei bemalte Glasscheiben übereinander legt
die Gesamttransparenz zusammenrechnen (transparenz-unten * trasparenz-oben ... 50% * 50% = 25%)
und bei den Farben die Transparenz des oberen Bildes nehmen, damit den eigenen Farbanteil berechnen (255 - tansparenz)
dann den Farbanteil mit der Transparenz des unteren Bildes (ebenfalls 255 - tansparenz)
dann nochmal von unten den oben durchscheinenden Teil (tansparenz ... also (255 - transparenz-unten) * trasparenz-oben)
und zum Schluß beide Farben addieren



und egal was du tust, du mußt immer auf Überläufe achten, sonst gibt es solche "Farbverfälschungen", wo z.B. aus zwei Mal fast 0% plötzlich 100% Transparenz entsteht

Michael II 4. Jun 2017 19:41

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Hallo Harry

die Formel darf dich nicht abschrecken. Du musst eigentlich nur zwei Dinge tun für jedes Pixel:

1. Den neuen Alphawert berechnen
2. Die neuen Farbwerte für R,G,B.

Ich hab's rasch getan und das Resultat sieht wirklich gut aus.

In diesem Beispiel siehst du, wie du es für zwei gleich grosse Bitmaps A, B tun kannst, welche direkt übereinander gelegt werden.
( Du willst dann natürlich Code schreiben für A an Position x, y über B legen oder ähnlich. )

Delphi-Quellcode:
procedure AueberB( a, b, c : TBitMap );

var lineA, lineB, lineC : PRGB32Array;
    x, y : integer;
    alphaA, alphaB, alphaC : extended;
begin
  a.PixelFormat := pf32Bit;
  b.PixelFormat := pf32Bit;
  c.PixelFormat := pf32bit;

  for y := 0 to a.Height-1 do
  begin
    lineA := a.ScanLine[y];
    lineB := b.ScanLine[y];
    lineC := c.ScanLine[y];

    for x := 0 to a.Width-1 do
    begin
       // Test
      (* lineA[x].A := 255;
      lineB[x].A := 0; *)
      alphaA := lineA[x].A/255;
      alphaB := lineB[x].A/255;
      // 0 = Transparent -  1 = instransparent
      alphaC := alphaA + ( 1 - alphaA )*alphaB; // (den Fall alphaA=0 UND alphaB=0 musst du hier noch sep. behandeln)
      lineC[x].A := round(alphaC*255);
      lineC[x].R := round(1/alphaC*(alphaA*lineA[x].R + (1-alphaA)*alphaB*lineB[x].R ));
      lineC[x].G := round(1/alphaC*(alphaA*lineA[x].G + (1-alphaA)*alphaB*lineB[x].G ));
      lineC[x].B := round(1/alphaC*(alphaA*lineA[x].B + (1-alphaA)*alphaB*lineB[x].B ));
    end;
  end;

end;

Testen:
So rufe ich die Prozedur auf:
Ich lade in diesem Beispiel in A und B das gleiche Bild, in A um 10,10 versetzt.
Ins Bild A schreibe ich den Buchstaben 'A' rein, ins Bild B den Buchstaben 'B'. (Damit sehe ich im Resultatbild C in etwa, wie A und B zum Resultat C beitragen.)

In der Prozedur AueberB (siehe Code oben) habe ich zwei Zeilen
lineA[x].A := 255;
lineB[x].A := 0;
"auskommentiert". Dort kannst du globale Alpha-Werte setzen und sehen, wie sich diese Werte aufs Resultatbitmap C auswirken.


Delphi-Quellcode:
procedure TForm100.Button1Click(Sender: TObject);
var a,b,c : TBitMap;
begin
  a := TBitMap.Create;
  b := TBitMap.Create;
  c := TBitMap.Create;

  a.LoadFromFile( 'C:\Users\Michael\Desktop\thun.bmp' );
  b.LoadFromFile( 'C:\Users\Michael\Desktop\thun.bmp' );
  a.Canvas.Draw( 10,10,b );

  a.Canvas.Font.Size := 100;
  a.Canvas.TextOut( 10,10, 'A' );
  b.Canvas.Font.Size := 100;
  b.Canvas.TextOut( 10,210, 'B' );
  c.SetSize( a.Width, a.Height );

  AueberB( a, b, c );

  c.SaveToFile( 'C:\Users\Michael\Desktop\thun2.bmp');

  a.Free;
  b.Free;
  c.Free;
end;

Natürlich kann man die ganze Sache auch in den "Integer Bereich" verlegen. Bei modernen Prozessoren gewinnt man aber (leider ;-)) häufig gar nicht mehr viel Zeit; auf älteren Kisten jedoch sehr...

mensch72 4. Jun 2017 19:44

AW: Zwei transparente Bitmaps miteinader verrechnen
 
also gilt:

AN:=255-BYTE(DWORD( (DWORD(AU)*DWORD(AO)) div (DWORD(AU)+DWORD(AO)) ));

Gemäß Beispiel
50% & 50% -> 25% ...
(128*128) / (128+128) -> 64 was ja 25% entspricht:)

Jens01 4. Jun 2017 19:48

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Graphhics32?
https://github.com/graphics32/graphi...g/TextureBlend

himitsu 4. Jun 2017 20:20

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Da, wo die zwei Bilder nicht übereinander liegen, dann kann man da für das "fehlende" Bild als Bummywert ein Pixel mit 100% Transparenz verwenden und damit weiterrechen.

Nach Wegkürzen aller Parameter bleibt dann das Pixel des anderen Bildes 1:1 übrig.

Harry Stahl 5. Jun 2017 00:27

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Liste der Anhänge anzeigen (Anzahl: 1)
So, danke erst mal für die Hinweise und die erste Umsetzung der Formel.

Habe daran noch ein wenig weiter gearbeitet, das sieht nun so aus:

Delphi-Quellcode:
  alphaA := A/255;
  alphaB := AO/255;
  alphaC := alphaA + (1 - alphaA )* AlphaB;

  R := round((1/alphaC) * (alphaA* (R/255) + (1-alphaA) * alphaB * (Ro/255) ) * 255);
  G := round((1/alphaC) * (alphaA* (G/255) + (1-alphaA) * alphaB * (Go/255) ) * 255);
  B := round((1/alphaC) * (alphaA* (B/255) + (1-alphaA) * alphaB * (Bo/255) ) * 255);

  A := round (AlphaC * 255);
Funktioniert soweit, nur ist die Verrechnung der Farben anscheinend noch nicht ausreichend. Daher habe ich das hier noch mit meiner Ursprungslösung ergänzt:

Delphi-Quellcode:
  alphaA := A/255;
  alphaB := AO/255;
  alphaC := alphaA + (1 - alphaA )* AlphaB;

  B := (ao * (bo - b) shr 8 + b); // hier ergänzt
  G := (ao * (go - g) shr 8 + g); //
  r := (ao * (ro - r) shr 8 + r); //

  R := round((1/alphaC) * (alphaA* (R/255) + (1-alphaA) * alphaB * (Ro/255) ) * 255);
  G := round((1/alphaC) * (alphaA* (G/255) + (1-alphaA) * alphaB * (Go/255) ) * 255);
  B := round((1/alphaC) * (alphaA* (B/255) + (1-alphaA) * alphaB * (Bo/255) ) * 255);

  A := round (AlphaC * 255);
Mit dieser Ergänzung wird das Ergebnis exakt so angezeigt, wie Photoshop die Datei errechnet.

Anliegend mal ein Screenshot, wo eine PSP-Layer-Datei auf eine Ebene reduziert wurde und als PNG-Datei geladen (linke Seite).
Rechts daneben die Original-PSP-Datei, mit den geladenen Layern, wo das Ergebnis dann berechnet mit der o.g. Routine im Programm angezeigt wird (rechte Seite).

stahli 5. Jun 2017 12:50

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Beeindruckend :thumb:

Harry Stahl 5. Jun 2017 13:42

AW: Zwei transparente Bitmaps miteinader verrechnen
 
@Stahli

Danke, aber das Kompliment geht an all die netten Kollegen, die mir hier geholfen haben, alleine hätte ich das nicht hinbekommen.
Hatte zuvor bestimmt einen halben Tag lang versucht, das selbst zu backen, aber ohne Erfolg.

Da sieht man mal wieder, wie super hilfreich und unverzichtbar dieses Forum ist (insofern mal ein herzliches Danke an Daniel und die anderen Forenunterstützer)!:thumb:

Michael II 6. Jun 2017 14:36

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Hallo Harry

du hast hier mal ein Testprogramm gepostet. - Kannst du (falls dein Projekt nun nicht geheim eingestuft ;-)) dein aktualisiertes Projekt hochladen (oder mir per PN senden)? Besten Dank.


Zitat:

Funktioniert soweit, nur ist die Verrechnung der Farben anscheinend noch nicht ausreichend.
Das hängt von den Quellbildern ab.

In deinen Beispielbildern A und B wurde der weiche Rand erzeugt, indem
1. der Alphawert am Rand abnimmt UND zusätzlich
2. mit einem Farbverlauf Farbe -> schwarz

Oder etwas anders ausgedrückt: Der Originalfarbwert wurde wahrscheinlich mit dem Alphawert multipliziert - und dieser Farbwert wurde in A, B gespeichert.

Du könntest also in solchen Fällen vor dem Berechnen des Zielbilds für die Bitmaps A und B die Originalfarben [die Farben für intransparente Versionen von A und B] ermitteln.

_____
Manchmal liegen A und B mit Alphakanal aber auch in Originalfarben vor (also nicht wie bei deinen Beispielbildern "Farbe=alpha*Originalfarbe"); in solchen Fällen (grösserer Farbraum fürs Bild möglich) müsstest du natürlich auf den oben erwähnten Vorprozess verzichten.

[Wenn deine Grafikobjekte keine Auskunft (oder keine korrekte) über das AlphaFormat geben, hilft nur testen (oder raten, indem du AlphaVerläufe und Farbverläufe miteinander vergleichst).]

Gruss
Michael

Harry Stahl 6. Jun 2017 23:02

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Michael,

klar, kein Problem, habe das Demo kurzerhand mit meiner Endlösung aktualisiert und hier noch mal hochgeladen.

Ja, das Problem mit den vormultiplizierten Farbwerten basierend auf den Alphawerten ist mir bekannt und führt immer wieder zu Verwirrungen. In meinem Projekt verwende ich keine vorberechneten Farbwerte auf Alphabasis, sondern die reinen Farbwerte und berechne die Ausgabe dann auf der Basis der Alphawerte.

Wie bereits oben mal erwähnt, gibt es hier beim Demoprojekt im Ergebnis eine Farbverfälschung, weil ich die PNG-Dateien aus den TImage-Containern über einen Umweg wieder in das TBitmap holen musste. In meinem Programm tritt das nicht auf, zum Laden der Dateien verwende ich die ImageEn-Komponenten, die das soweit alles schon richtig berücksichtigen.

Nur konnte ich das hier nicht posten, weil die meisten Anwender diese Kompos wohl eher nicht haben.

Michael II 7. Jun 2017 19:27

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Hallo Harry

danke fürs Posten der angepassten Version. Ich weiss nun wieso du die Korrekturfunktion einbauen musst.

Du hast alphaA und alphaB vertauscht und auch bei der Berechnung der Farbwerte eine Vertauschung vorgenommen.

Wenn du noch einmal die Formel auf https://de.wikipedia.org/wiki/Alpha_Blending anschaust (oder den von mir geposteten Delphi Code) für das Überblenden A über B, dann findest du

alphaC := alphaA + (1-alphaA)*alphaB
und für die Berechnung des Farbwerts gilt
C=(alphaA*A + alphaB*B - alphaA*alphaB*B)/alphaC [0.]


Du hast aber berechnet:
alphaA = B/255 // in alphaA steht also bei dir der Alphakanal Wert von B (statt A)
alphaB = A/255 // und in alphaB steht der Alphakanal Wert von A (statt B)

Da die Funktion zur Berechnung von alphaC symmetrisch [f(x,y)=f(x,y)=x+y-xy) ist, spielt deine Vertauschung der alpha-Werte hier keine Rolle. Der von dir ermittelte Wert von alphaC ist korrekt.

Bei der Berechnung der Farbe rechnest du
C=(alphaA*B + (1-alphaA)*alphaB*A)/alphaC [1.]

Hier hast du nun auch noch A und B vertauscht (aber nicht so, dass sich die "AlphaVertauschung" und die "FarbVertauschung" aufheben) ;-). Du rechnest so, als wäre A unten und B oben.

Vor der Berechnung der Farbwerte korrigierst du die Farbwerte von B wie folgt:
B=alphaB*(A-B)+B [2.]

Wenn wir [2]. in [1]. einsetzen, erhalten wir:

C=(alphaA*(alphaB*(A-B)+B) + (1-alphaA)*alphaB*A)/alphaC
= alphaA*alphaB*A - alphaA*alphaB*B + alphaA*B + alphaB*A-alphaA*alphaB*A)/alphaC
= (alphaB*A + alphaA*B - alphaA*alphaB*B)/alphaC

Wenn du jetzt noch berücksichtigst, dass du alphaA und alphaB vertauscht hast, erhältst du exakt [0.]. Du hast also durch deine Korrektur [2.] die Vertauschungen korrigiert.


Deine Korrektur [2.] ist aber etwas heikel umgesetzt. Du rechnest in deinem Code
B := (ao * (bo - b) shr 8 + b); // hier ergänzt
B ist als Byte definiert und die rechte Seite kann grösser werden als 255. Die Korrektur könnte deshalb in gewissen Fällen misslingen.

Ich würde deshalb deine Vertauschunen zurücktauschen und stattdessen die Originalformel verwenden, also:

alphaC := alphaA + (1-alphaA)*alphaB
C=(alphaA*A + alphaB*B - alphaA*alphaB*B)/alphaC

also etwa so:

Delphi-Quellcode:
procedure AueberB( const a, b : TBitMap; c : TBitMap );

// 0 = "normal"
// 1 = Alpha Korrektur
// Berechne A über B - Resultat in C

var lineA, lineB, lineC : PRGB32Array;
    x, y : integer;
    alphaA, alphaB, alphaC : extended;
label weiter;

begin
  for y := 0 to a.Height-1 do
  begin
    lineA := a.ScanLine[y];
    lineB := b.ScanLine[y];
    lineC := c.ScanLine[y];

    for x := 0 to a.Width-1 do
    begin
      alphaA := lineA[x].A/255;
      alphaB := lineB[x].A/255;

      // 0 = Transparent -  1 = intransparent
      if ( alphaA + alphaB = 0 ) then
          lineC[x] := lineA[x]
      else
      if ( lineA[x].A = 255 ) then // A intransparent => A nehmen
          lineC[x] := lineA[x]
      else
      if ( lineA[x].A = 0 ) then // A transparent => B nehmen
          lineC[x] := lineB[x]
      else
      if ( lineB[x].B = 0 ) then // B transparent => A nehmen
          lineC[x] := lineA[x]
      else
      begin // A und B verrechnen:
           alphaC := alphaA + ( 1 - alphaA )*alphaB;
           lineC[x].A := round(alphaC*255);

           // falls die Farbwerte von A und von B "vormultipliziert mit Alpha" vorliegen:
          (* lineC[x].R := EnsureRange( round((lineA[x].R + (1-alphaA)*lineB[x].R )), 0, 255 );
           lineC[x].G := EnsureRange( round((lineA[x].G + (1-alphaA)*lineB[x].G )), 0, 255 );
           lineC[x].B := EnsureRange( round((lineA[x].B + (1-alphaA)*lineB[x].B )), 0, 255 );
            *)

           // falls Farbwerte nicht vormultipliziert mit Alpha vorliegen:
           lineC[x].R := round(1/alphaC*(alphaA*lineA[x].R + (1-alphaA)*alphaB*lineB[x].R ));
           lineC[x].G := round(1/alphaC*(alphaA*lineA[x].G + (1-alphaA)*alphaB*lineB[x].G ));
           lineC[x].B := round(1/alphaC*(alphaA*lineA[x].B + (1-alphaA)*alphaB*lineB[x].B ));
      end;
    end; {for x := 0 to a.Width-1 do}
  end;{for y := 0 to a.Height-1 do}
end;

Tipp: Du musst keine EnsureRange Funktion nutzen. Die Berechnungen von alpha und Farbwert sind ja genau so konstruiert, dass Sie im Intervall [0..1] (bzw. [0..255]) liegen.

Ich habe gesehen, dass du deine Bitmaps in PNGs umwandelst und dann erst die PNG ins Image lädst. Dies hat wahrscheinlich technische Gründe. TImage kann auch direkt Bitmaps mit AlphaKanal darstellen.

In deinem Code müsstest du ergänzen:
Delphi-Quellcode:
  bmres.PixelFormat := pf32bit; // bisher
  bmres.AlphaFormat := afDefined; // neu
..
..
  // Ausgabe bmRes:
  imgres.Transparent := false;  //neu
  imgres.Picture.Assign( bmres ); //neu

Gruss
Michael

Harry Stahl 9. Jun 2017 00:21

AW: Zwei transparente Bitmaps miteinader verrechnen
 
Danke für den Tipp mit dem TImage.

Ansonsten werde ich mir morgen mal in Ruhe ansehen, ob ich möglicherweise etwas vertauscht habe, könnte so sein. Ergebnis in meinem Programm stimmt jedenfalls (habe ich inzwischen mit dutzenden semitransparenten Überblendsituationen in Photoshop und bei mir verglichen). Die PSP-Datei lade ich mit der ImageEn-Komponente und übernehme daraus die Bitmaps und Alphawerte (letztere sind dort i.d.R. in einem 8-Bit-Bimtap abgelegt) für die Erstellung meiner 32-Alpha-Bitmap, mit der ich intern arbeite und die ganzen Berechnungen vornehme.


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