向图像列表添加32位位图

6
我正在尝试在运行时创建和绘制一个32位位图,然后将其添加到ImageList中。该位图具有透明度(alpha通道)。我可以创建位图并在其Canvas上绘制而不出问题,并且它可以在任何其他Canvas上正常使用透明度绘制。
问题是当我将其添加到ImageList时,图像似乎失去了使用位图的Canvas属性所做的绘制。
以下是我开始创建位图的方式:
Bitmap := TBitmap.Create;
Bitmap.PixelFormat := pf32bit;
Bitmap.Transparent := True;
Bitmap.AlphaFormat := afDefined;
SetBkMode(Bitmap.Canvas.Handle, TRANSPARENT);
Bitmap.SetSize(100, 42);

// now I can draw, let's say, an icon from an imagelist
ImageList.Draw(Bitmap.Canvas, 5, 5, 0, dsTransparent, itImage);

// and some other stuff
Bitmap.Canvas.RoundRect(0, 0, 99, 41, 5, 5);
Bitmap.Canvas.TextOut(50, 5, 'Test string');

如果我将这个位图绘制到任何控件画布上,它会正常绘制,包括来自图像列表的图像、圆角矩形和文本,具有透明背景(任何未绘制的地方都将是透明的;将保留已经存在的背景)。这意味着Form1.Canvas.Draw(0, 0, Bitmap);将在Form1上绘制位图,如果那里已经有其他图像,它将被保留为背景。
但是,如果我将这个位图添加到图像列表中,就会出现奇怪的问题。ImageList的ColorDepth设置为cd32bit,然后我调用:
BitmapImageList.Width := Bitmap.Width;
BitmapImageList.Hieght := Bitmap.Height;
BitmapImageList.Add(Bitmap, nil);

现在,如果我尝试使用imagelist绘制该图像:
BitmapImageList.Draw(Form1.Canvas, 0, 0, 0);

只有从ImageList中绘制的位图图像、圆角矩形以及在画布上绘制的文本将显示出来,其他内容都会消失。
我错过了什么?
1个回答

5

可以通过创建一个额外的位图(Intrans),将其 alpha 通道设置为0 来实现此目标。
在ImageList.Add中使用Intrans作为原始位图的Mask图像。
示例应反映您自己的情况。

type
  pRGBQuadArray = ^TRGBQuadArray;
  TRGBQuadArray = ARRAY [0 .. 0] OF TRGBQuad;

Procedure GenIntransparentBitmap(bmp, Intrans: TBitmap);
var
  pscanLine32: pRGBQuadArray;
  i, j: Integer;
begin
  Intrans.Assign(bmp);
  for i := 0 to Intrans.Height - 1 do
  begin
    pscanLine32 := Intrans.Scanline[i];
    for j := 0 to Intrans.Width - 1 do
    begin
      pscanLine32[j].rgbReserved := 0;
    end;
  end;
end;

procedure TForm3.Button1Click(Sender: TObject);
var
  Bitmap, Intransp: TBitmap;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.PixelFormat := pf32bit;
    Bitmap.Transparent := true;
    Bitmap.AlphaFormat := afIgnored;
    SetBkMode(Bitmap.Canvas.Handle, BKMODE_LAST);
    Bitmap.SetSize(100, 42);

    ImageList1.Draw(Bitmap.Canvas, 5, 5, 0, dsTransparent, itImage);

    Bitmap.Canvas.Brush.Style := bsClear;
    Bitmap.Canvas.RoundRect(0, 0, 99, 41, 5, 5);
    Bitmap.Canvas.TextOut(50, 5, 'Test string');

    BitmapImageList.Width := Bitmap.Width;
    BitmapImageList.Height := Bitmap.Height;

    // Create intransparent bitmap from transparent bitmap
    Intransp := TBitmap.Create;
    try
      GenIntransparentBitmap(Bitmap, Intransp);
      // add intransparent bitmap as image and transparent bitmap as mask
      BitmapImageList.Add(Intransp, Bitmap);
    finally
      Intransp.Free;
    end;

    BitmapImageList.Draw(Canvas, 100, 100, 0);
  finally
    Bitmap.Free;
  end;
end;

一个更简短的版本是:
Procedure GenIntransparentBitmap(bmp, Intrans: TBitmap);
begin
  Intrans.Assign(bmp);
  Intrans.PixelFormat := pf24bit;
end;

procedure TForm3.Button1Click(Sender: TObject);
var
  Bitmap, Intransp: TBitmap;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.PixelFormat := pf32bit;

    SetBkMode(Bitmap.Canvas.Handle, TRANSPARENT);
    Bitmap.SetSize(100, 42);

    ImageList1.Draw(Bitmap.Canvas, 5, 5, 0, dsTransparent, itImage);

    Bitmap.Canvas.Brush.Style := bsClear;
    Bitmap.Canvas.RoundRect(0, 0, 99, 41, 5, 5);
    Bitmap.Canvas.TextOut(50, 5, 'Test string');

    BitmapImageList.Width := Bitmap.Width;
    BitmapImageList.Height := Bitmap.Height;

    // Create intransparent bitmap from transparent bitmap
    Intransp := TBitmap.Create;
    try
      GenIntransparentBitmap(Bitmap, Intransp);
      // add intransparent bitmap as image and transparent bitmap as mask
      BitmapImageList.Add(Intransp, Bitmap);
    finally
      Intransp.Free;
    end;

    BitmapImageList.Draw(Canvas, 100, 100, 0);
  finally
    Bitmap.Free;
  end;
end;

1
做得好。我忘记了那个遮罩。我总是将图标添加到图像列表中,现在我记得当制作图标时,指定一个遮罩是必不可少的。 - David Heffernan
太棒了。我已经寻找那个问题的答案很久了,但是一直没有找到。你的解决方案完美地解决了我的问题。最后需要注意的一点是,在示例中,“BitmapImageList”的ColorDepth必须设置为cd24Bit。非常感谢你。 - Marcio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接