AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Zwei transparente Bitmaps miteinader verrechnen

Ein Thema von Harry Stahl · begonnen am 4. Jun 2017 · letzter Beitrag vom 9. Jun 2017
Antwort Antwort
Seite 3 von 3     123   
Michael II

Registriert seit: 1. Dez 2012
Ort: CH BE Eriswil
763 Beiträge
 
Delphi 11 Alexandria
 
#21

AW: Zwei transparente Bitmaps miteinader verrechnen

  Alt 7. Jun 2017, 20:27
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
Michael Gasser
  Mit Zitat antworten Zitat
Benutzerbild von Harry Stahl
Harry Stahl

Registriert seit: 2. Apr 2004
Ort: Bonn
2.538 Beiträge
 
Delphi 11 Alexandria
 
#22

AW: Zwei transparente Bitmaps miteinader verrechnen

  Alt 9. Jun 2017, 01:21
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.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 3 von 3     123   


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:42 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