
Introduction
This is an example of how to create and use masked and semi-transparent bitmap buttons that can be overlaid on any background, including other bitmaps. The buttons are automatically alpha-blended (anti-aliased) with the background to create nice smooth edges, and provide region clipping to create various elliptical and other shapes.
Instead of creating a new class that duplicates all the other functionality of owner-draw buttons, the code to draw the transparency and create regions was added to the CHoverButtonEx class written by Niek Albers and modified by Frederick Ackers. If you are already using the CHoverButtonEx class, you can simply replace it with this version without affecting existing functionality, although I have also added a fourth image to the bitmaps for a disabled view, so you would need this image in your existing bitmaps, or you would need to change the references back to 3.
The demo project uses a dialog-based regioned window that displays a circular, patterned bitmap as the background. This type of bitmapped background shows how these buttons can take on a very stylistic look and feel. By pressing any of the four refresh buttons in the lower portion of the window, the background is changed to a different bitmap (there are six in all).
Additional thanks goes to Paul Nettle for his winDIB routines.
A Bit About Anti-Aliasing and Alpha-Blending
One of the trickiest parts of masking images on top of images is the rough edges, or pixelation, of the meeting places of these images when they are not straight edges. The solution to these rough edges is to blend the edges in a technique called anti-aliasing. In this demo, anti-aliasing is done using an alpha-blending technique, where the transparency of the foreground bitmap pixel determines the strength of color at any particular position on the display.
The formula for blending source and background pixels is:
displayColor = sourceColor � sourceAlpha / 255 + backgroundColor � (255 � sourceAlpha) / 255
where the alpha value range is 0 (transparent) to 255 (opaque). Therefore, any part of the button's bitmap that has an alpha value of 0 will not be seen, and areas with values greater than zero will be blended with the background at varying strengths up to 255 where the button's bitmap will be fully opaque.
static inline unsigned int alphaBlend(const unsigned
int bg, const unsigned int src)
{
unsigned int a = src >> 24;
if (0 == a) return bg;
unsigned int rb = (((src & 0x00ff00ff) * a) +
((bg & 0x00ff00ff) * (0xff - a))) & 0xff00ff00;
unsigned int g = (((src & 0x0000ff00) * a) +
((bg & 0x0000ff00) * (0xff - a))) & 0x00ff0000;
return (src & 0xff000000) | ((rb | g) >> 8);
}
Creating The Bitmaps
As explained, in order to blend the button bitmap with the background, the button bitmap must have an alpha value assigned to each pixel. This means that these bitmaps must be 32-bit-per-pixel (32bpp) images where the byte order is Alpha, Red, Green, Blue (ARGB).
One way to create this type of image is to use Adobe Photoshop. Create a new image with the color mode CMYK, making sure that the background color is set to black. Photoshop can automatically anti-alias edges for you against the black background. Save the file as a .RAW type, which only saves the actual bytes of the image and no header information. Unfortunately, the order of the .RAW image will be ABGR, so there is a bit reordering that must be done before blending, but this is no big deal.
Import the bitmaps into your project as a new resource type, "RAW". The bitmaps can now be loaded as resources for use on the buttons.
BOOL CHoverButtonEx::LoadRaw(UINT rawid, long nWidth, long nHeight)
{
UINT style = GetButtonStyle();
if (!(style & BS_OWNERDRAW))
{
style |= BS_OWNERDRAW;
SetButtonStyle(style);
}
m_pRaw = NULL;
CString resName;
resName.Format("#%d", rawid);
HGLOBAL hRaw = LoadResource(AfxGetResourceHandle(),
FindResource(AfxGetResourceHandle(), resName, "RAW"));
if (!hRaw)
return FALSE;
m_pRaw = (unsigned int*)LockResource(hRaw);
if (!m_pRaw)
return FALSE;
m_nRawWidth = nWidth;
m_nRawHeight = nHeight;
if (!m_bHorizontal)
{
m_ButtonSize.cy=nHeight/4;
m_ButtonSize.cx=nWidth;
}
else
{
m_ButtonSize.cy=nHeight;
m_ButtonSize.cx=nWidth/4;
}
SetWindowPos( NULL, 0,0, m_ButtonSize.cx,
m_ButtonSize.cy,SWP_NOMOVE | SWP_NOOWNERZORDER );
return TRUE;
}
Using the code
Adding transparent bitmapped buttons is very easy to implement.
- Create any number of owner-draw buttons on your dialog or view.
- Include the HoverButton header in your dialog or view header where the buttons are declared.
#include "HoverButton.h"
.
.
- Change the button types to
CHoverButtonEx. CHoverButtonEx m_Btn1;
CHoverButtonEx m_Btn2;
CHoverButtonEx m_Btn3;
- Create a region for the buttons, and assign parameters.
HRGN r = CreateEllipticRgn(0, 0, 48, 48);
m_Btn1.SetHorizontal(TRUE);
m_Btn1.SetRegion(r);
m_Btn1.LoadRaw(IDR_RED, 192, 48);
m_Btn1.SetToolTipText("Press Me!");
m_Btn2.SetHorizontal(TRUE);
m_Btn2.SetRegion(r);
m_Btn2.LoadRaw(IDR_PURPLE, 192, 48);
m_Btn2.SetToolTipText("Press Me!");
m_Btn3.SetHorizontal(TRUE);
m_Btn3.SetRegion(r);
m_Btn3.LoadRaw(IDR_PURPLE, 192, 48);
m_Btn3.SetToolTipText("Press Me!");
DeleteObject(r);
- If the background changes, simply call
RefreshBkgrnd. m_Btn1.RefreshBkgrnd();
m_Btn2.RefreshBkgrnd();
m_Btn3.RefreshBkgrnd();
That's it!
Points of Interest
One of the interesting routines here is how to determine what the background looks like. I decided to read the background before the button is first displayed, and save that image until the button is refreshed.
if (m_pRaw && m_nRawWidth && m_nRawHeight && !m_bLoadBkgrnd)
{
unsigned int bkPix;
COLORREF c;
int bkWidth;
int bkHeight;
if (!m_bHorizontal)
{
bkWidth = m_nRawWidth;
bkHeight = m_nRawHeight/4;
}
else
{
bkWidth = m_nRawWidth/4;
bkHeight = m_nRawHeight;
}
if (m_pBkGrnd)
delete m_pBkGrnd;
m_pBkGrnd = new unsigned int;
if (!m_pBkGrnd)
return;
unsigned int* pBkGrnd = m_pBkGrnd;
for (int iY = 0; iY < bkHeight; iY++)
{
for (int iX = 0; iX < bkWidth; iX++, pBkGrnd++)
{
c = mydc->GetPixel(iX, iY);
bkPix = (unsigned int)c;
bkPix |= 0xff000000;
bkPix = (bkPix&0xff00ff00) | ((bkPix>>16)&0xff)
| ((bkPix<<16)&0xff0000);
*pBkGrnd = bkPix;
}
}
m_bLoadBkgrnd = TRUE;
}
This method provides an accurate picture of the background, especially if the background has been stretched or tiled from the original image. Another method of determining the background might be to pass the background bitmap to the button class, and calculate the offsets, or simply pass a color reference if the background is not a bitmap. Any of these other methods might prove more efficient in selected cases.
|
|
 |
 | How to load raw image at runtime MickeyJC | 3:57 17 Jul '08 |
|
 |
I have been succesfully using this control with RAW images added to ResourceView (VC6). Now I wonder if I can dynamically LoadRaw to make the app more flexible when trying to change the RAW image. Eg. LoadRaw(_T("abc.raw")) Pls advice.
|
|
|
|
 |
 | Transparent Button with Region and Anti-Aliased Edges CGroff | 2:05 8 Jul '08 |
|
 |
How can I do that in Access?Is it possible? Thanks!
|
|
|
|
 |
 | Thanks a lot! Alex Cohn | 5:35 23 Oct '07 |
|
 |
I have borrowed the alphaBlend() function of yours, it's pleasure to use it.
|
|
|
|
 |
 | Why not use straight png files [modified] Mark Hagers | 6:28 30 Aug '07 |
|
 |
I always use the png file format for export of images with alpha channel support. These can be imported as resources in VS2005. They can then be assigned to a button or pictureBox control's Image property, or drawn in code using 'Properties.Resources.imagename' (without quotes). The image is then automatically alphablended to whatever is behind it.
One caveat: don't store png files in ImageList controls, their quality deteriorates with every save of your project (don't ask me why).
To create a png image with alpha channel in Photoshop: First create an RGB image with a transparent background. (You can also change an existing "flat" image by doubleclicking the background layer in the layers palette).
Next use the PS tools to edit your image. You can use as many layers as you like to create your image. The combined transparency of all layers will become the alpha channel in the exported file. You can put a temporary background layer below your image layers to see how things will look on a background image, and delete the temp layer before exporting to png.
Finally choose File->Save for Web... choose png 24 as a file format and make sure "transparency" is checked. Click Save and you're done. Note: I always create Images in PS CS2 on a Mac, but this should work identically on Windows.
-- modified at 11:37 Thursday 30th August, 2007
|
|
|
|
 |
 | little bug grandmasta1 | 0:59 12 Jul '07 |
|
 |
Hello,
very nice work, but i found a little bug... if i maximize my dialog the bkgnd behind the button is lighter than the rest of my bkgnd-image.
has anyone an idea?
is it the alphablend-function???
|
|
|
|
 |
 | How to create RAW ffrom PNG Sir.Costy | 23:06 19 Jun '07 |
|
 |
Hallo,
I have some PNG image of for state button (in fact only 3 meter), and I try to create some RAW file with them, but seams to not work. Even the PNG has transparency, after converting some transparency it is lost. Does know haw may I create a RAW which looks/ behave exactly like this PNG? Here is the PNG image https://mx1.im-c.de/exchange/IacobescuC/Posteingang/AW:%20Button%20images:%20new%20try.EML/1_multipart_xF8FF_2_icon_show_presentation_toolbar.png/C58EA28C-18C0-4a97-9AF2-036E93DDAFB3/icon_show_presentation_toolbar.png?attach=1
Thank a lot.
|
|
|
|
 |
|
 |
You can use PNG directly with GDI+. Look for other CodeProject articles using PNG. If you want to convert to RAW, it seems you will have to adjust transparency with alpha channel in PS to get the right levels. However, I cannot access your PNG example to test.
|
|
|
|
 |
 | Can I use Bitmaps and not raw images? Sir.Costy | 23:46 30 May '07 |
|
 |
I look in you code and I saw that you have a function LoadBitmap(....). Thats mean that I can use 32BPP bitmaps and not RAW images?
Thanks ...
|
|
|
|
 |
 | How can I change the image?
| 15:37 7 Mar '07 |
|
 |
Great source~ i want to change the image, but i failed. (just call LoadRaw again)
how can i change the image during the execution?
thanks~
|
|
|
|
 |
|
 |
If you want to change the image on the button during execution, you should be able to just call LoadRaw. I added this call as follows:
void CTransparentButtonDemoDlg::OnBnClickedNextBg() . . . m_Btn1.LoadRaw(IDR_PURPLE, 192, 48); // add before refresh background
Worked fine for me.
|
|
|
|
 |
 | Would you Please send me the bmp before raw? hongming6662 | 23:59 1 Feb '07 |
|
 |
I have try many times ,but fail.Would you Please send me the bmp before raw? My Email_address is 4201154011@163.com.Looking forward to your writing! Thank You Very much! Best regards! Love from hongming
|
|
|
|
 |
|
 |
The .RAW files can be opened in Photoshop by specifying 4 channels interleaved. The size of the Purple, Red, and Exit buttons are 192x48. The size of the Open and Save buttons are 300x48.
The .BMP version of these .RAW files can be downloaded here:
Purple Button
Red Button
Exit Button
Open Button
Save Button
Some browsers (i.e. Firefox) may not display these bitmaps correctly. You should use Photoshop or Irfanview to view.
|
|
|
|
 |
 | Transparency dosen't work with buttons on toolbars :( Sir.Costy | 6:27 15 Jan '07 |
|
 |
Great things, but if you try to put this buttonST on a toll bar you will have some unexpected disapointments.
It can't take parent background for creating transparency, so it take image from under screen application.
do you know how can i create transparent image buttons on toolbars? better will be if there are allsow round buttons
10x
|
|
|
|
 |
|
 |
You could try to modify the background pixel array and fill it with the solid color of the toolbar. Otherwise, you might have to use PNG's.
|
|
|
|
 |
 | Anti - aliased background ? Virtual Reality | 9:47 15 Dec '06 |
|
 |
I ran your project ... very cool.
I see only smooth edged buttons but the background edges look jagged. Is there a way to anti alias the border of the background circle.
Thanks .. VR
|
|
|
|
 |
|
 |
Good question! Since the buttons draw themselves with the alpha-blend, we would have to add this same functionality to the dialog class. I'm not sure how well this would work against the desktop background, but in theory it is the same.
I would probably start by adding the alphaBlend and DrawTransparentBitmap functions to CTransparentButtonDemoDlg class, and then call DrawTransparentBitmap in DrawTheBackground.
|
|
|
|
 |
 | How can I do that with VB.net? alexmbra | 7:05 17 May '06 |
|
 |
Is it possible? Can anybody help me? Thanks.
|
|
|
|
 |
|
|
 |
 | CMYK Black is different from RGB Black otterrrr | 9:48 11 Dec '05 |
|
 |
when RGB black(#000000) is applied to CMYK black, it has a slightly effect to alphachannel. maybe cause of K(BLACK) = 90%
therefore if you want to fill the background with black (in CMYK mode) check the value of K is 100%. the default black color of Photoshop 7.0 is not appropriate.
Thanks for reading.
|
|
|
|
 |
 | Nice but question on button response CosmoNova | 22:00 4 Sep '05 |
|
 |
Very nice work, thanks for sharing! I have a project which needs similar bitmap buttons, but I have a difficulty in my code as well as in this example that: The buttons aren't responding fast enough when I do fast clicking. Let say I click the change background three times really quick, I can only chance the background twice instead of three times. How can I improve this? as it's causing serious "miss-key" in the users' point of view. Thanks a lot!
|
|
|
|
 |
 | Does not work with window alway on top daniel_zy | 4:53 18 May '05 |
|
 |
You cannot save the background in case you have some window on top of this window (you save only the part of the button that is visible). You can try it with the task manager.
|
|
|
|
 |
 | How to use it in WTL ? icer.L | 1:07 27 Feb '05 |
|
 |
When I see this for the first time. I thought that's cool, that what I want. But my app is using WTL. How can I do the same thing on WTL, do I have to do much change at you code? Thank you.
|
|
|
|
 |
|
 |
I do not use WTL, but if there is a button class you can probably just derive CHoverButtonEx from that class.
|
|
|
|
 |
 | How to create the .Raw files durbin | 12:52 21 Dec '04 |
|
 |
Hi,
can any body tell me how to create the .Raw files? I followed the steps that OP provided, doesn't work!
|
|
|
|
 |
|
 |
In Photoshop:
1. Set background color to black. 2. Create a new file and set Mode to CMYK Color, or open existing file and change Mode to CMYK Color under Image menu if not in that format. 3. Create four images to represent the four button states. Use photoshop tools to blend edges (feather, etc.) Darker areas within the image will be more transparent. I usually create each button as a separate file and copy/paste into final image as a new layer. This makes it easier to move/change each button independently. 4. Save file as type .RAW.
In Visual Studio:
1. Right-click on your project's resources tree. 2. Select Import... 3. Change Files of Type: to All Files (*.*) 4. Select the RAW file and press Import 5. Enter the resource type as RAW and press OK 6. Change the resource name from IDR_RAW1 if desired.
You can also check out the .Net version for more info: http://www.codeproject.com/cs/miscctrl/TransButtonNetDemo.asp[^]
|
|
|
|
 |
|
|
Last Updated 17 Aug 2004 |
Advertise |
Privacy |
Terms of Use |
Copyright ©
CodeProject, 1999-2010