Pregunta

Esta pregunta está relacionada con mi Pregunta anterior ON SO.

Quiero combinar dos capas con alfa aplicado solo a una parte específica de la capa de origen.Una forma en que lo intenté fue establecer SourceConstantalpha a $ FF (y tener la función usar el canal alfa en la capa de origen).

Este tipo de obras, aunque lento (supongo que puedo acelerarlo usando escáneres), el tipo de parte es que no puedo averiguar qué configurar el canal alfa.La documentación sugiere que el cálculo es:

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

He intentado algunos valores diferentes por suponer el trabajo, pero mi primera pregunta es: ¿Cómo puedo calcular el valor alfa?

Después de leer algunas otras preguntas, me encontré con la función Transparentblt, que hace el pozo de enmascaramiento (y rápido), pero no la transparencia, ¿hay una manera de combinar? Estas dos llamadas juntas (tal vez usando una tercera capa)?

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.

¿Fue útil?

Solución

Truco: mezclar los mismos colores en cualquier relación da como resultado ese mismo color.

Entonces, la forma más sencilla (y quizás también la más eficiente) es primero dibujar primero el resultado transparentado en un mapa de bits temporal, y alphablend ese mapa de bits en el lienzo de destino.

Con el acceso al lienzo de destino durante el dibujo:

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;

y sin acceso al lienzo de destino durante el dibujo:

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;

Otros consejos

Solo por completo de la integridad ( "¿Cómo puedo calcular el valor 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;

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top