2012-10-03 4 views
5

이 질문은 내에서 earlier question과 관련이 있습니다.Alphablend 및 TransparentBlt

소스 레이어의 특정 부분에만 적용되는 알파로 두 개의 레이어를 결합하고 싶습니다. 필자가 시도한 한 가지 방법은 SourceConstantAlpha를 $ ff로 설정하는 것 (그리고 소스 레이어에서 알파 채널을 사용하는 기능을 가짐)입니다.

이런 종류의 작품 - 느리지 만 (내가 ScanLines를 사용하여 속도를 높일 수있을 것 같지만), 알파 채널을 설정할 대상을 알 수 없다는 것이 부분입니다.

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

내가 추측 작업에 의해 몇 가지 다른 값을 시도,하지만 내 첫 번째 질문은 다음과 같습니다 : 나는 알파 값을 계산하려면 어떻게 문서는 계산이 제안?

몇 가지 다른 질문을 읽은 후에 투명성이 아니라 잘 마스킹하는 TransparentBlt 기능을 사용하여이 두 가지 호출을 함께 사용하는 방법이 있습니다 (어쩌면 세 번째 레이어 사용).)?

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

y 각 픽셀의 r, g, b를 원하는 알파로 채우고 AlphaBlend를 255로 나눕니다. 물론 픽셀의 알파를 알파 값으로 설정하십시오. 나는 TransparentBlt에 대한 질문을 이해하지 못한다. 어떻게 투명성이 좋지 않은가? –

+3

@Sertac OP에는 부분적으로 투명한 색으로 구성된 비트 맵이 있습니다. 투명한 부분은 대상 캔버스에서 그대로 유지해야하며 그려진 부분은 대상 캔버스로 알파 블 레이되어야합니다. – NGLN

답변

8

트릭 : 어떤 비율이든 동일한 색상을 혼합하면 동일한 색상이됩니다.

가장 간단한 방법은 투명화 된 결과를 임시 비트 맵에 먼저 그려서 대상 캔버스에서 해당 비트 맵을 알파 블렌드하는 것입니다. 그리는 동안 대상 캔버스에 접근 할 수있는

:
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; 

그리고 대상 캔버스에 액세스하지 않고

동안 그리기 :

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

"대상 비트 맵에 액세스하지 않고"* 당신이 의미하는 것을 얻지 못했지만, 첫 번째 버전을 좋아합니다. :) –

+0

'blt'가 픽셀을 걷는 것보다 빠르지는 모르겠지만 아마 그럴 것 같아요. –

+1

@Sertac @Sertac 웹 서비스 ... – NGLN

4

을 그냥 완전성을 위해서 ("나는 어떻게 계산합니까 알파 값? ") :

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;