6
votes

I'm trying to create and paint at runtime a 32 bit bitmap, and then add it to an ImageList. The bitmap has transparency (alpha-channel). I can create the bitmap and draw on it's Canvas with no problem, and it draws normaly with transparency over any other canvas.

The problem is when I add it to an ImageList, the image seems to lose the drawings made with the Canvas property of the bitmap.

Here is how I start the Bitmap:

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');

If I draw this Bitmap to any control canvas, it draws normaly, with the image from the imagelist, the rounded rectangle and the text, with transparent background (anywhere that nothing was painted will be transparent; will retain the background that was already there). It means that Form1.Canvas.Draw(0, 0, Bitmap); will draw the bitmap over the Form1 and if there was any other image there, it will be retained as background.

BUT, if I add this bitmap to an imagelist, a strange problem occurs. The ImageList has it's ColorDepth set to cd32bit, and then I call:

BitmapImageList.Width := Bitmap.Width;
BitmapImageList.Hieght := Bitmap.Height;
BitmapImageList.Add(Bitmap, nil);

Now, If I try to draw that image from imagelist with:

BitmapImageList.Draw(Form1.Canvas, 0, 0, 0);

The only thing that will be displayed is the image that was drawn in Bitmap from the ImageList, the rounded rect and the text that was drawn in the Canvas vanishes.

What am I missing?

1

1 Answers

5
votes

This can be done be creating an additional bitmap (Intrans) whose alpha chanel is set to 0.
Intrans is used for ImageList.Add as image the original bitmap as Mask.
The example should reflect yours.

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;

A shorter version would be

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;