Ever used 32bit images stored in TImageList in your Delphi application? Toolbars and some other VCL controls have DisabledImages property which is automatically used to get images for disabled toolbar buttons. But what about menu components? They don't have this property and drawing of disabled images is handled by TImageList with original enabled images (TMainMenu.Images property). And the results are really abysmal. How can this be fixed?
One way is to override DoDraw method of TImageList and change the code that draws disabled images. You can do regular RGB to grayscale conversion here or let Windows draw it for you in grayscale with nearly no work on your part. You can do this by using ImageList_DrawIndirect with ILS_SATURATE parameter. Note that this works only on Windows XP and newer and for 32bit images only. For older targets or color depths doing your own RGB->grayscale conversion is an option (good idea would probably be to cache converted grayscale images somewhere so they won't need to be converted on every draw call).
Here's the code of DoDraw method using ILS_SATURATE:
type // Descendant of regular TImageList TSIImageList = class(TImageList) protected procedure DoDraw(Index: Integer; Canvas: TCanvas; X, Y: Integer; Style: Cardinal; Enabled: Boolean = True); override; end; procedure TSIImageList.DoDraw(Index: Integer; Canvas: TCanvas; X, Y: Integer; Style: Cardinal; Enabled: Boolean); var Options: TImageListDrawParams; function GetRGBColor(Value: TColor): Cardinal; begin Result := ColorToRGB(Value); case Result of clNone: Result := CLR_NONE; clDefault: Result := CLR_DEFAULT; end; end; begin if Enabled or (ColorDepth <> cd32Bit) then inherited else if HandleAllocated then begin FillChar(Options, SizeOf(Options), 0); Options.cbSize := SizeOf(Options); Options.himl := Self.Handle; Options.i := Index; Options.hdcDst := Canvas.Handle; Options.x := X; Options.y := Y; Options.cx := 0; Options.cy := 0; Options.xBitmap := 0; Options.yBitmap := 0; Options.rgbBk := GetRGBColor(BkColor); Options.rgbFg := GetRGBColor(BlendColor); Options.fStyle := Style; Options.fState := ILS_SATURATE; // Grayscale for 32bit images ImageList_DrawIndirect(@Options); end; end;
Important note: For ILS_SATURATE to work correctly source image files must be 32bit with proper alpha channel data, setting color depth of TImageList to 32bit is not enough! If you don't see any images drawn this is probably the cause: 8/24bit image is loaded from file and then inserted into 32bit TImageList. As there is no alpha channel data in source image it is drawn as fully transparent so you don't see anything.