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