Full-Featured 24-bit Color Toolbar

Environment: VC6.0 SP4, Win95/NT3.51 or later

Creating a CToolbar with 16 or 24 bit buttons is easy if you aren’t worried about disabled buttons and your buttons don’t have any pixels that should be set to the toolbar’s background color. Things quickly get more complicated
if you want to correctly handle those other cases. This sample program attaches 24 bit images to the standard
MFC main frame toolbar and handles both disabled and hot buttons as well
as transparent button backgrounds. It can easily be extended to handle
16 bit images or different toolbar configurations.

If your application can be run with the display set to less than 16 bits
per pixel, you should add code to check the display depth and not load
the high color toolbars in those cases, since they won’t look good.

Specifying separate bitmaps for the enabled, disabled, and hot buttons isn’t too hard–CToolbar::GetToolbarCtrl() gets the toolbar control,
then CToolbarCtrl::SetImageList(), CToolbarCtrl::SetDisabledImageList(),
and CToolbarCtrl::SetHotImageList() are used to assign the bitmaps.

Making the image lists 24 bits deep requires creating the
CImageLists with no images, then loading the images as CBitmaps, and finally copying the
CBitmaps into the CImageLists.

Loading the bitmaps as 24 bits per pixel even when the user’s screen is set
to a different bit depth requires extra work, too. CBitmap::LoadBitmap()
converts the bitmap to the screen’s bit depth, so the Win32 function
::LoadImage() is used instead to create a 24 bit DIBSECTION, which is
then attached to a CBitmap so it can be passed to CImageList::Add().

When a toolbar is created in the Visual Studio toolbar editor, any pixel
that is light gray (RGB (192, 192, 192)) is replaced with the user’s
chosen button color at runtime, making those pixels effectively
transparent. For some reason this doesn’t happen to 24 bit CImageLists.
Specifying a mask color when the CImageList is created just ends up
replacing that color with black, not with the system button color. This
code does that color subsitution “manually.” Before a bitmap is added
to an image list, the code iterates over the pixels in the bitmap,
replacing each RGB (192, 192, 192) pixel with the system button color
(::GetSysColor (COLOR_BTNFACE)). Since the bitmaps are DIBSECTIONs,
the code can access the pixels directly for maximum efficiency.

Since the Visual Studio graphics editor can’t handle 24 bit images, you must edit the images in another program that can, such as GIMP or Adobe Photoshop. Save them as 24 bit .bmp files in the project’s “res” folder. You can then import them into the project using the “import” command in the Developer Studio “Resource” tab.


// these constants represent the dimensions and number of buttons in
// the default MFC-generated toolbar. If you need something different,
// feel free to change them. For extra credit, you can load the
// toolbar’s existing image list at runtime and copy the parameters from
// there.
static const int kImageWidth (16);
static const int kImageHeight (15);
static const int kNumImages (8);

static const UINT kToolBarBitDepth (ILC_COLOR24);

// this color will be treated as transparent in the loaded bitmaps —
// in other words, any pixel of this color will be set at runtime to
// the user’s button color. The Visual Studio toolbar editor defaults
// to 192, 192, 192 (light gray).
static const RGBTRIPLE kBackgroundColor = {192, 192, 192};

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;

if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0(“Failed to create toolbar\n”);
return -1; // fail to create
}

// attach the hicolor bitmaps to the toolbar
AttachToolbarImages (IDB_HICOLOR_TOOLBAR,
IDB_HICOLOR_TOOLBAR_DISABLED,
IDB_HICOLOR_TOOLBAR_HOT);

// the rest of your CMainFrame::OnCreate() code goes here
}

// find every pixel of the default background color in the specified
// bitmap and set each one to the user’s button color.
static void ReplaceBackgroundColor (CBitmap& ioBM)
{
// figure out how many pixels there are in the bitmap
BITMAP bmInfo;

VERIFY (ioBM.GetBitmap (&bmInfo));

// add support for additional bit depths if you choose
VERIFY (bmInfo.bmBitsPixel == 24);
VERIFY (bmInfo.bmWidthBytes == (bmInfo.bmWidth * 3));

const UINT numPixels (bmInfo.bmHeight * bmInfo.bmWidth);

// get a pointer to the pixels
DIBSECTION ds;

VERIFY (ioBM.GetObject (sizeof (DIBSECTION), &ds) == sizeof (DIBSECTION));

RGBTRIPLE* pixels = reinterpret_cast<RGBTRIPLE*>(ds.dsBm.bmBits);
VERIFY (pixels != NULL);

// get the user’s preferred button color from the system
const COLORREF buttonColor (::GetSysColor (COLOR_BTNFACE));
const RGBTRIPLE userBackgroundColor = {
GetBValue (buttonColor), GetGValue (buttonColor), GetRValue (buttonColor)};

// search through the pixels, substituting the user’s button
// color for any pixel that has the magic background color
for (UINT i = 0; i < numPixels; ++i) { if (pixels [i].rgbtBlue == kBackgroundColor.rgbtBlue && pixels [i].rgbtGreen == kBackgroundColor.rgbtGreen && pixels [i].rgbtRed == kBackgroundColor.rgbtRed) { pixels [i] = userBackgroundColor; } } } // create an image list for the specified BMP resource
static void MakeToolbarImageList (UINT inBitmapID,
CImageList& outImageList)
{
CBitmap bm;

// if we use CBitmap::LoadBitmap() to load the bitmap, the colors
// will be reduced to the bit depth of the main screen and we won’t
// be able to access the pixels directly. To avoid those problems,
// we’ll load the bitmap as a DIBSection instead and attach the
// DIBSection to the CBitmap.
VERIFY (bm.Attach (::LoadImage (::AfxFindResourceHandle(
MAKEINTRESOURCE (inBitmapID), RT_BITMAP),
MAKEINTRESOURCE (inBitmapID), IMAGE_BITMAP, 0, 0,
(LR_DEFAULTSIZE | LR_CREATEDIBSECTION))));

// replace the specified color in the bitmap with the user’s
// button color
::ReplaceBackgroundColor (bm);

// create a 24 bit image list with the same dimensions and number
// of buttons as the toolbar
VERIFY (outImageList.Create (
kImageWidth, kImageHeight, kToolBarBitDepth, kNumImages, 0));

// attach the bitmap to the image list
VERIFY (outImageList.Add (&bm, RGB (0, 0, 0)) != -1);
}

// load the high color toolbar images and attach them to m_wndToolBar
void CMainFrame::AttachToolbarImages (UINT inNormalImageID,
UINT inDisabledImageID,
UINT inHotImageID)
{
// make high-color image lists for each of the bitmaps
::MakeToolbarImageList (inNormalImageID, m_ToolbarImages);
::MakeToolbarImageList (inDisabledImageID, m_ToolbarImagesDisabled);
::MakeToolbarImageList (inHotImageID, m_ToolbarImagesHot);

// get the toolbar control associated with the CToolbar object
CToolBarCtrl& barCtrl = m_wndToolBar.GetToolBarCtrl();

// attach the image lists to the toolbar control
barCtrl.SetImageList (&m_ToolbarImages);
barCtrl.SetDisabledImageList (&m_ToolbarImagesDisabled);
barCtrl.SetHotImageList (&m_ToolbarImagesHot);
}

Downloads

Download demo project – 37 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read