Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/VCL/Standard Controls/TMainMenu    [ Add a report in this area ]  
Report #:  86879   Status: Open
Disabled glyphs in menus look poor
Project:  Delphi Build #:  14.0.3593.25826
Version:    14.0 Submitted By:   David Heffernan
Report Type:  Basic functionality failure Date Reported:  8/3/2010 8:02:01 AM
Severity:    Serious / Highly visible problem Last Updated: 6/18/2013 6:48:28 PM
Platform:    All platforms Internal Tracking #:   20334
Resolution: None (Resolution Comments) Resolved in Build: : None
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: 32
Description
I would respectfully offer the opinion that, for apps built with Delphi, disabled glyphs in menus look rather poor.

This is perhaps especially so with the advent of system drawn themed menus in Vista/7.  With alpha blended 32bpp glyphs being the norm, the use of TCustomImageList.DoDraw to draw disabled greyed glyphs doesn't seem like a good choice.  If you compare with apps which use system drawn menus (i.e. not owner drawn) with glyphs you will see that Windows converts the glyph to gray scale and draws that.

It would be better to use system drawn menus when glyphs are present, but this would need quite a bit of support from TImageList because it would need to provide PARGB32 bitmaps to stuff into MenuItemInfo.hbmpItem which is the magic incantation that tells Windows to draw glyphs in system drawn menus.  If you did it this way then you could just sit back and let Windows take the strain of Vista/7 menu drawing and get rid of all the owner draw themed code that lives in VistaDraw!

At the very least I think you'd be better off avoiding TCustomImageList.DoDraw and you could use something along these lines:

procedure DrawDisabledImage(DC: HDC; Images: HIMAGELIST; Index, X, Y: Integer);

type
  TBGRA = packed record
    B,G,R,A: Byte;
  end;

var
  i: Integer;
  Icon: HICON;
  MemDC: HDC;
  bmi: BITMAPINFO;
  bmp, bmpPrev: HBITMAP;
  bits: Pointer;
  p: ^TBGRA;
  Width, Height: Integer;
  BlendFunction: TBlendFunction;

begin
  if not ImageList_GetIconSize(Images, Width, Height) then begin
    exit;
  end;
  Icon := ImageList_GetIcon(Images, Index, LR_DEFAULTCOLOR);
  if Icon=0 then begin
    exit;
  end;
  Try
    ZeroMemory(@bmi, SizeOf(bmi));
    bmi.bmiHeader.biSize := SizeOf(bmi.bmiHeader);
    bmi.bmiHeader.biWidth := Width;
    bmi.bmiHeader.biHeight := Height;
    bmi.bmiHeader.biPlanes := 1;
    bmi.bmiHeader.biBitCount := 32;
    bmi.bmiHeader.biCompression := BI_RGB;
    bmi.bmiHeader.biSizeImage := Width*Height*4;
    MemDC := CreateCompatibleDC(DC);
    Try
      if MemDC=0 then begin
        exit;
      end;
      bmp := CreateDIBSection(MemDC, bmi, DIB_RGB_COLORS, bits, 0, 0);
      Try
        if bmp=0 then begin
          exit;
        end;
        bmpPrev := SelectObject(MemDC, bmp);
        Try
          DrawIconEx(MemDC, 0, 0, Icon, Width, Height, 0, 0, DI_NORMAL);
          p := bits;
          for i := 1 to Width*Height do begin
            // Grayscale = 0.30*R + 0.59*G + 0.11*B
            p.B := MulDiv(p.R, 30, 100) + MulDiv(p.G, 59, 100) + MulDiv(p.B, 11, 100);
            p.G := p.B;
            p.R := p.B;
            inc(p);
          end;
          BlendFunction.BlendOp := AC_SRC_OVER;
          BlendFunction.BlendFlags := 0;
          BlendFunction.AlphaFormat := AC_SRC_ALPHA;
          BlendFunction.SourceConstantAlpha := $60;
          AlphaBlend(
            DC,
            X,
            Y,
            Width,
            Height,
            MemDC,
            0,
            0,
            Width,
            Height,
            BlendFunction
          );
        Finally
          SelectObject(MemDC, bmpPrev);
        End;
      Finally
        DeleteObject(bmp);
      End;
    Finally
      DeleteDC(MemDC);
    End;
  Finally
    DestroyIcon(Icon);
  End;
end;


Whatever, you get the idea.

Even if you feel disinclined to change the default use of TCustomImageList.DoDraw, how about putting in a hook to allow app developers to customise the drawing of disabled glyphs?  Then I could use my routine above without having to hack the Delphi source!!
Steps to Reproduce:
None
Workarounds
None, other than linking in a modified version of Menus.pas
Attachment
D2010MenuGlyphProblems.zip
Comments

Tomohiro Takahashi at 8/3/2010 6:25:43 PM -
Could you please attach sample project and some screenshots to reproduce/confirm your issue?

David Heffernan at 8/25/2010 9:01:28 AM -
Hi, I'm sorry for the delay, but I've been away on vacation.

I've added a simple project which illustrates the issue.  There are two versions of the project: one uses the version of Menus.pas supplied with Delphi and the other uses a version with a couple of modifications to deal with some of the issues I have encountered.  I think you should be able to see from the two apps what I'm referring to.

I trust that you will agree that the disabled icons do look terrible if I use the standard Delphi approach to drawing them.

As I said in my original report, if you could find a way to get a PARGB32 bitmap into MenuItemInfo.hbmpItem then that would be the best solution of all because you could just throw away all the Vista owner draw menu code!

David Heffernan at 11/24/2010 7:36:00 AM -
Actually I have recently learnt (http://galfar.vevb.net/wp/2010/04/ugly-images-of-disabled-items-in-delphi/) that the workaround code I suggest is severe overkill.  Instead you can use the following:

procedure DrawDisabledImage(DC: HDC; ImageList: TCustomImageList; Index, X, Y: Integer);
var
  Options: TImageListDrawParams;
begin
  ZeroMemory(@Options, SizeOf(Options));
  Options.cbSize := SizeOf(Options);
  Options.himl := ImageList.Handle;
  Options.i := Index;
  Options.hdcDst := DC;
  Options.x := X;
  Options.y := Y;
  Options.fState := ILS_SATURATE;
  ImageList_DrawIndirect(@Options);
end;

Server Response from: ETNACODE01