1

I need to draw a transparent bitmap on a TMenuItem. Despite trying for many hours with different methods I could not succeed:

var
  NewItem: TMenuItem;
  ThisBmp: TBitmap;
begin
  NewItem := TMenuItem.Create(pmSendToCustomTool);
  NewItem.Caption := ThisCaption;
  NewItem.Bitmap.SetSize(16,16);
  NewItem.Bitmap.PixelFormat := pf32bit;
  NewItem.Bitmap.Transparent := True;
  NewItem.Bitmap.TransparentColor := clFuchsia;
  ThisBmp := TBitmap.Create;
  try
    ThisBmp.SetSize(16,16);
    ThisBmp.PixelFormat := pf32bit;
    ThisBmp.Transparent := True;
    ThisBmp.Canvas.Brush.Color := clFuchsia;
    ThisBmp.TransparentColor := clFuchsia; 
    MySystemImageList1.GetBitmap(AIndex, ThisBmp);
    CodeSite.Send('ThisBmp', ThisBmp);
    NewItem.Bitmap.Assign(ThisBmp);
    CodeSite.Send('NewItem.Bitmap', NewItem.Bitmap);
  finally
    ThisBmp.Free;
  end;

This is how ThisBmp looks in CodeSite after GetBitmap: enter image description here

And this how the resulting menu item looks: enter image description here

user1580348
  • 5,721
  • 4
  • 43
  • 105
  • 2
    Is there a reason why you are not simply using the [`TMenuItem.ImageIndex`](http://docwiki.embarcadero.com/Libraries/en/Vcl.Menus.TMenuItem.ImageIndex) property instead? Assign your ImageList to the parent menu's [`Images`](http://docwiki.embarcadero.com/Libraries/en/Vcl.Menus.TMenu.Images) property, or the parent menu item's [`SubMenuImages`](http://docwiki.embarcadero.com/Libraries/en/Vcl.Menus.TMenuItem.SubMenuImages) property (depending on your UI design), and then set the menu item's `ImageIndex` to the desired index. Let the ImageList handle the transparent drawing for you. – Remy Lebeau Jan 15 '19 at 18:39
  • Yes, there is a reason. I MUST do it this way. Reason: In the popup menu, I have some items which get their glyph from a SystemImageList (as above) and some other items which get their glyph from a standard TImageList. Since I can only use ONE Imagelist for the whole popup menu I must pull part of the glyphs this way. – user1580348 Jan 15 '19 at 18:49
  • Related: [*.bmp loses transparent background if using v6 ImageList Control](https://stackoverflow.com/questions/22174341/) – Remy Lebeau Jan 15 '19 at 18:51
  • 1
    Actually, you don't. You could either 1) copy all of the images to a single TImageList, or 2) owner-draw the menu items, then you can draw from any source you want. You don't HAVE to use the `Bitmap` property at all. – Remy Lebeau Jan 15 '19 at 18:56
  • Why are you setting the transparent color manually? If you set `TBitmap.Transparent` to `True` and don't set any value to `TransparentColor` TBitmap Will use of lower left image pixel as transparent color. Also why are you setting `Brush.color` at all? – SilverWarior Jan 16 '19 at 06:35

1 Answers1

1

Your code doesn't work because you lost all transparency info when using GetBitmap(). You will have to draw the bitmap manually instead, eg:

uses
  ..., Winapi.CommCtrl;

procedure GetTransparentBitmapFromImageList(ImageList: TCustomImageList; Index: Integer; Bitmap: TBitmap);
var
  i: integer;
begin
  // make sure your ImageList is set to ColorDepth=cd32bit and DrawingStyle=dsTransparant beforehand...
  Bitmap.SetSize(ImageList.Width, ImageList.Height);
  Bitmap.PixelFormat := pf32bit;
  if (ImageList.ColorDepth = cd32Bit) then
  begin
    Bitmap.Transparent := False;
    Bitmap.AlphaFormat := afDefined;
  end
  else
    Bitmap.Transparent := True;
  for i := 0 to Bitmap.Height-1 do
    FillChar(Bitmap.ScanLine[i]^, Bitmap.Width*SizeOf(DWORD), $00);
  ImageList_Draw(ImageList.Handle, Index, Bitmap.Canvas.Handle, 0, 0, ILD_TRANSPARENT);
end;

Alternatively:

procedure GetTransparentBitmapFromImageList(ImageList: TCustomImageList; Index: Integer; Bitmap: TBitmap);
begin
  Bitmap.PixelFormat := pf32bit;
  Bitmap.Canvas.Brush.Color := clFuschia;
  Bitmap.SetSize(ImageList.Width, ImageList.Height);
  ImageList.Draw(Bitmap.Canvas, 0, 0, AIndex, dsTransparent, itImage);
  Bitmap.Transparent := True;
  Bitmap.TransParentColor := clFuchsia;
  Bitmap.TransparentMode := tmAuto;
end;

Then you can do this:

var
  NewItem: TMenuItem;
begin
  NewItem := TMenuItem.Create(pmSendToCustomTool);
  NewItem.Caption := ThisCaption;
  GetTransparentBitmapFromImageList(MySystemImageList1, AIndex, NewItem.Bitmap);
  CodeSite.Send('NewItem.Bitmap', NewItem.Bitmap);
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thanks Remy, but when I use the method under *Alternatively* then I get this result: https://i.imgur.com/YPTlgVM.png – user1580348 Jan 15 '19 at 20:52
  • ... and also the first version does not work. This is the result of the first version (the non-transparent color now is black instead of Fuchsia): https://i.imgur.com/XVFqnsn.png – user1580348 Jan 15 '19 at 21:03
  • The only remaining possibility would be to copy the image from the SystemImageList to the imagelist which is associated with the popupmenu. How could this be done while preserving transparency? Then I could easily set the `ImageIndex` property for all popup menu items. – user1580348 Jan 15 '19 at 21:37