2012-10-03 9 views
5

To pytanie jest związane z moim earlier question na SO.Alphablend i TransparentBlt

Chcę połączyć dwie warstwy z alfa zastosowane tylko do określonej części warstwy źródłowej. Jednym ze sposobów, w jaki próbowałem, było ustawienie SourceConstantAlpha na $ ff (i funkcja ta używa kanału alfa w warstwie źródłowej).

Ten rodzaj prac - choć powolny (myślę, że mogę go przyspieszyć za pomocą ScanLines), część jest taka, że ​​nie wiem, w jaki sposób ustawić kanał alfa. Dokumentacja sugeruje, że wyliczenie to:

st.Red = Src.Red + (1 - Src.Alpha) * Dst.Red 

Próbowałem kilka różnych wartości przez zgadywanie, ale moje pierwsze pytanie brzmi: W jaki sposób obliczyć wartość alfa?

Po przeczytaniu kilku innych pytań dotyczących SO, natknąłem się na funkcję TransparentBlt, która wykonuje maskę dobrze (i szybko), ale nie przezroczystość, czy istnieje sposób połączenia tych dwóch połączeń razem (może za pomocą trzeciej warstwy)?

unit MainWnd; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, ExtCtrls, ControlsEx; 

type 
{------------------------------------------------------------------------------} 
    TfrmMain = class(TForm) 
    PaintBox1: TPaintBox; 
    procedure PaintBox1Paint(Sender: TObject); 
    private 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    frmMain: TfrmMain; 

implementation 

{$R *.dfm} 

{..............................................................................} 
procedure copyToAlpha(const in_bitmap : TBitmap; const in_transparentColor : TColor; 
     const in_transparency : integer); 
var 
    x : integer; 
    y : integer; 
    p : integer; 
begin 
    ASSERT(in_bitmap.PixelFormat = pf32bit); 

    for x := 0 to in_bitmap.Width - 1 do 
    begin 
    for y := 0 to in_bitmap.Height - 1 do 
    begin 
     p := in_bitmap.Canvas.Pixels[x, y]; 
     if TColor(p) <> in_transparentColor then 
     begin 
     in_bitmap.Canvas.Pixels[x, y] := p or (in_transparency shl 24); 
     end 
     else 
     in_bitmap.Canvas.Pixels[x, y] := p or ($ff shl 24); 
    end; 
    end; 
end; 

{..............................................................................} 
procedure alphaBlendTest(
     const in_target : TCanvas; 
     const in_width : integer; 
     const in_height : integer); 
const 
    BARSIZE = 30; 
var 
    bitmap : TBitmap; 
    r  : TRect; 
    blendFn : BLENDFUNCTION; 
    ret  : Boolean; 
begin 
    blendFn.BlendOp    := AC_SRC_OVER; 
    blendFn.SourceConstantAlpha := $ff; 
    blendFn.BlendFlags   := 0; 
    blendFn.alphaFormat   := AC_SRC_ALPHA; 

    bitmap := TBitmap.Create; 
    try 
    bitmap.Width    := in_width; 
    bitmap.Height    := in_height; 
    bitmap.PixelFormat  := pf32bit; 
    bitmap.HandleType   := bmDIB; 
    bitmap.TransparentColor := clFuchsia; 
    bitmap.Transparent  := true; 
    bitmap.Canvas.Brush.Color := clFuchsia; 
    bitmap.Canvas.FillRect(Bounds(0, 0, in_width, in_height)); 
    bitmap.Canvas.Brush.Color := clGreen; 

    r := Bounds(
     in_width div 2 - (in_width div 3) div 2, 
     0, 
     (in_width div 3) + 1, 
     BARSIZE   + 1); 

    bitmap.Canvas.Rectangle(r); 
    // done drawing 

    //copyToAlpha(bitmap, clFuchsia, 1); 
    ret := Windows.TransparentBlt(
     in_target.Handle, 
     0, 
     0, 
     in_width, 
     in_height, 
     bitmap.Canvas.Handle, 
     0, 
     0, 
     in_width, 
     in_height, 
     clFuchsia); 
     //blendFn); 

    ASSERT(ret); 
    finally 
    bitmap.Free; 
    end; 
end; 



{..............................................................................} 
procedure TfrmMain.PaintBox1Paint(Sender: TObject); 
var 
    r: TRect; 
begin 
    PaintBox1.Canvas.Brush.Color := clBlue; 
    r := Bounds(0, 0, PaintBox1.ClientWidth, PaintBox1.ClientHeight); 
    PaintBox1.Canvas.FillRect(r); 
    PaintBox1.Canvas.Brush.Color := clRed; 
    PaintBox1.Canvas.Ellipse(0, 0, PaintBox1.ClientWidth, PaintBox1.ClientHeight); 

    alphaBlendTest(PaintBox1.Canvas, PaintBox1.ClientWidth, PaintBox1.ClientHeight); 
end; 

end. 
+0

Mnożysz y r, g, b każdego piksela r z pożądaną alfą i dzielą przez 255 dla 'AlphaBlend', oczywiście również ustaw alfa piksela na wartość alfa. Nie rozumiem pytania na temat 'TransparentBlt', w jaki sposób przejrzystość nie jest dobra? –

+3

@Sertac OP ma bitmapę, która częściowo składa się z przezroczystego koloru. Przezroczyste części mają pozostać nienaruszone na płótnie docelowym, narysowane części mają być alfablended do płótna docelowego. – NGLN

Odpowiedz

8

Sztuczka: mieszanie tych samych kolorów w dowolnej proporcji powoduje uzyskanie tego samego koloru.

Najprostszym sposobem (a może i najbardziej efektywnym) jest najpierw narysowanie przezroczystego wyniku w tymczasowej bitmapie i alfablendowanie tej mapy bitowej na płótnie docelowym.

Dzięki dostępowi do docelowego płótnie podczas rysowania:

procedure TfrmMain.PaintBox1Paint(Sender: TObject); 
const 
    BarSize = 30; 
var 
    R: TRect; 
    Bmp: TBitmap; 
    BlendFunc: TBlendFunction; 
begin 
    with PaintBox1 do 
    begin 
    R := ClientRect; 
    Canvas.Brush.Color := clBlue; 
    Canvas.FillRect(R); 
    Canvas.Brush.Color := clRed; 
    Canvas.Ellipse(R); 
    Bmp := TBitmap.Create; 
    try 
     Bmp.Width := Width; 
     Bmp.Height := Height; 
     BitBlt(Bmp.Canvas.Handle, 0, 0, Width, Height, Canvas.Handle, 0, 0, 
     SRCCOPY); 
     Bmp.Canvas.Brush.Color := clGreen; 
     R := Bounds(Width div 3, 0, Width div 3 + 1, BarSize + 1); 
     Bmp.Canvas.Rectangle(R); 
     BlendFunc.BlendOp := AC_SRC_OVER; 
     BlendFunc.BlendFlags := 0; 
     BlendFunc.SourceConstantAlpha := 80; 
     BlendFunc.AlphaFormat := 0; 
     Windows.AlphaBlend(Canvas.Handle, 0, 0, Width, Height, Bmp.Canvas.Handle, 
     0, 0, Width, Height, BlendFunc); 
    finally 
     Bmp.Free; 
    end; 
    end; 
end; 

A bez dostępu do docelowego płótnie podczas rysowania:

procedure GetRemoteBitmap(Bmp: TBitmap; Width, Height: Integer); 
const 
    BarSize = 30; 
var 
    R: TRect; 
begin 
    Bmp.Canvas.Brush.Color := clFuchsia; 
    Bmp.Width := Width; 
    Bmp.Height := Height; 
    Bmp.TransparentColor := clFuchsia; 
    Bmp.Transparent := True; 
    Bmp.Canvas.Brush.Color := clGreen; 
    R := Bounds(Width div 3, 0, Width div 3 + 1, BarSize + 1); 
    Bmp.Canvas.Rectangle(R); 
end; 

procedure TfrmMain.PaintBox1Paint(Sender: TObject); 
var 
    R: TRect; 
    Bmp: TBitmap; 
    Tmp: TBitmap; 
    BlendFunc: TBlendFunction; 
begin 
    with PaintBox1 do 
    begin 
    R := ClientRect; 
    Canvas.Brush.Color := clBlue; 
    Canvas.FillRect(R); 
    Canvas.Brush.Color := clRed; 
    Canvas.Ellipse(R); 
    Bmp := TBitmap.Create; 
    Tmp := TBitmap.Create; 
    try 
     GetRemoteBitmap(Bmp, Width, Height); 
     Tmp.Width := Width; 
     Tmp.Height := Height; 
     BitBlt(Tmp.Canvas.Handle, 0, 0, Width, Height, Canvas.Handle, 0, 0, 
     SRCCOPY); 
     TransparentBlt(Tmp.Canvas.Handle, 0, 0, Width, Height, Bmp.Canvas.Handle, 
     0, 0, Width, Height, ColorToRGB(clFuchsia)); 
     BlendFunc.BlendOp := AC_SRC_OVER; 
     BlendFunc.BlendFlags := 0; 
     BlendFunc.SourceConstantAlpha := 80; 
     BlendFunc.AlphaFormat := 0; 
     Windows.AlphaBlend(Canvas.Handle, 0, 0, Width, Height, Tmp.Canvas.Handle, 
     0, 0, Width, Height, BlendFunc); 
    finally 
     Tmp.Free; 
     Bmp.Free; 
    end; 
    end; 
end; 
+1

Nie rozumiem, co masz na myśli przez * "bez dostępu do docelowej mapy bitowej" *, ale podoba mi się twoja pierwsza wersja. :) –

+0

Zastanawiam się, czy "blt" jest szybszy niż chodzenie pikseli, ale myślę, że to prawdopodobnie .. –

+1

@ Sertac Jeśli dostanie swoją mapę bitową z np. a webservice ... – NGLN

4

Tylko dla kompletności wywodu („Jak mam obliczyć wartość alfa? "):

procedure alphaBlendTest(
     const in_target : TCanvas; 
     const in_width : integer; 
     const in_height : integer); 
const 
    BARSIZE = 30; 
var 
    bitmap : TBitmap; 
    r  : TRect; 
    blendFn : BLENDFUNCTION; 
    ret  : Boolean; 

    x, y: Integer; 
    px : PRGBQuad; 
begin 
    blendFn.BlendOp    := AC_SRC_OVER; 
    blendFn.SourceConstantAlpha := $ff; 
    blendFn.BlendFlags   := 0; 
    blendFn.alphaFormat   := AC_SRC_ALPHA; 

    bitmap := TBitmap.Create; 
    try 
    bitmap.Width    := in_width; 
    bitmap.Height    := in_height; 
    bitmap.PixelFormat  := pf32bit; 
    bitmap.Canvas.Brush.Color := clGreen; 

    r := Bounds(
     in_width div 2 - (in_width div 3) div 2, 
     0, 
     (in_width div 3) + 1, 
     BARSIZE   + 1); 

    bitmap.Canvas.Rectangle(r); 

    for y := 0 to bitmap.Height - 1 do begin 
     px := bitmap.ScanLine[y]; 
     for x := 0 to Bitmap.Width - 1 do begin 
     if PtInRect(r, Point(x, y)) then begin 
      px.rgbBlue := MulDiv(px.rgbBlue, $A0, $FF); 
      px.rgbGreen := MulDiv(px.rgbGreen, $A0, $FF); 
      px.rgbRed := MulDiv(px.rgbRed, $A0, $FF); 
      px.rgbReserved := $A0; 
     end else 
      px.rgbReserved := $00; // fully transparent 
     Inc(px); 
     end; 
    end; 
    // done drawing 

    ret := Windows.AlphaBlend(
     in_target.Handle, 
     0, 
     0, 
     in_width, 
     in_height, 
     bitmap.Canvas.Handle, 
     0, 
     0, 
     in_width, 
     in_height, 
     blendFn); 

    ASSERT(ret); 
    finally 
    bitmap.Free; 
    end; 
end;