Emboss text and other shape on your bitmap

This is best explained with pictures. The left most image is some text and a simple shape. The image in the middle is the bitmap on which we want to emboss the first image. The result is shown in the third image. So, this topic will basically give you the code to achieve this result. The code also allows you to get a sunk-in effect rather than a raised effect.

The basic idea behind the Emboss() function given below is very simple. It creates a monochrome bitmap that represents the edges that need to be highlighted and it creates another monochrome bitmap of the edges that need a shadow. These two bitmaps are then used to draw the highlight and the shadow. The detail, however, is a little bit more involved. Let's cover the main steps, one at a time.

The raised and sunken effect is merely achieved by using a lighter color on one edge and a darker color on the other edge. If we use a light color for the edges towards the top left then we get a raised effect. Conversely, if we use a dark color for the edges towareds the top left then we get a sunken effect. So, if the function is called with the bRaised flag set to false, then we simply swap the highlight and the shadow color to get a sunken effect.

The next major step is creating the highlight bitmap. We use a monochrome bitmap for this purpose since a monochrome bitmap has just a background and a foreground and makes our task simpler.

  1. We set the entire bitmap to white using PatBlt(). White is the background color in monochrome bitmap. This assures that any portion of the bitmap not drawn to will have the background color.
  2. Next we copy the source image to the highlight bitmap using BitBlt() to get a monochrome image. We copy the image 1 pixel closer to the top-left corner. Our purpose is to get a one pixel wide highlight edge for all the edges towards the top-left corner.
  3. This is the important step. We use another BitBlt() to remove all the extra edges in the highlight bitmap. We use the MERGEPAINT raster operation for this. What MERGEPAINT does is, it converts all the pixels in the destination bitmap (the highlight bitmap) corresponding to a foreground pixel in the source to white (the background color). Any pixel in the destination bitmap is unchanged when the corresponding pixel in the source is a background pixel. This explanation applies because we are dealing with a monochrome bitmap as the destination bitmap. Note that we do not use an offset for this operation, e.i. the destination x and y coordinates as well as the source x and y coordinates are all zero. The net effect of step 2 and 3 is that we get a single pixel wide line for all the edges that need to be highlighted.

In a similar manner, we create the shadow bitmap. We then copy the the background bitmap onto the result bitmap and then draw the highlight edges using the highlight color and the shadow edges using the shadow color. To copy the highlight and shadow colors we use the raster op-code 0x00B8074A. This is also represented as PSDPxax which is reverse polish notation for ((P^D)&S)^P, where P,S and D stand for pattern brush, source and destination. This probably still doesn't make a lot of sense so I'll just tell you what the effect of this raster operation is. Given that the text color is black and the background color is white - we set this up by calling SetBkColor() and SetTextColor() - the result of the raster operation is that for every black pixel ( the foreground color in a monochrome bitmap ) in the source the brush color is copied to the destination. You will note that we do select an appropriate brush before the call to BitBlt().

//prototype for default arguments - include this in your header file
HBITMAP Emboss( HBITMAP hBitmap, HBITMAP hbmBackGnd, HPALETTE hPal, BOOL bRaised = TRUE,
			   int xDest = 0, int yDest = 0, 
			   COLORREF clrHighlight = GetSysColor( COLOR_BTNHIGHLIGHT ), 
			   COLORREF clrShadow = GetSysColor( COLOR_BTNSHADOW ));

// Emboss		- Creates a 3D embossed effect
// Returns		- A new bitmap containing the resulting effect
// hBitmap		- Bitmap that contains the basic text & shapes
// hbmBackGnd		- Contains the color image 
// hPal			- Handle of palette associated with hbmBackGnd
// bRaised		- True if raised effect is desired. False for sunken effect
// xDest		- x coordinate - used to offset hBitmap
// yDest		- y coordinate - used to offset hBitmap
// clrHightlight	- Color used for the highlight edge
// clrShadow		- Color used for the shadow
// Note			- 1. Neither of the bitmap handles passed in should be selected 
//			  in a device context.
//			  2. The pixel at 0,0 in hBitmap is considered the background color
HBITMAP Emboss( HBITMAP hBitmap, HBITMAP hbmBackGnd, HPALETTE hPal, 
			   BOOL bRaised, int xDest, int yDest, 
			   COLORREF clrHighlight, COLORREF clrShadow )
	const DWORD PSDPxax = 0x00B8074A;
	BITMAP   bmInfo ;
	HBITMAP  hbmOld, hbmShadow, hbmHighlight, hbmResult, hbmOldMem ;
	HBRUSH   hbrPat ;
	HDC      hDC, hColorDC, hMonoDC, hMemDC ;

	if( !bRaised )
		// Swap the highlight and shadow color
		COLORREF clrTemp = clrShadow;
		clrShadow = clrHighlight;
		clrHighlight = clrTemp;
	// We create two monochrome bitmaps. One of them will contain the
	// highlighted edge and the other will contain the shadow. These
	// bitmaps are then used to paint the highlight and shadow on the
	// background image.
	hbmResult = NULL ;
	hDC = GetDC( NULL ) ;

	// Create a compatible DCs
	hMemDC = ::CreateCompatibleDC( hDC );
	hMonoDC = CreateCompatibleDC( hDC );
	hColorDC = CreateCompatibleDC( hDC );

	if( hMemDC == NULL || hMonoDC == NULL || hColorDC == NULL )
		if( hMemDC ) DeleteDC( hMemDC );
		if( hMonoDC ) DeleteDC( hMonoDC );
		if( hColorDC ) DeleteDC( hColorDC );

		return NULL;

	// Select the background image into memory DC so that we can draw it
	hbmOldMem = (HBITMAP)::SelectObject( hMemDC, hbmBackGnd );
	// Get dimensions of the background image
	::GetObject( hbmBackGnd, sizeof( bm ), &bm );
	// Create the monochrome and compatible color bitmaps 
	GetObject( hBitmap, sizeof( BITMAP ), (LPSTR) &bmInfo ) ;
	hbmShadow =
		CreateBitmap( bmInfo.bmWidth, bmInfo.bmHeight, 1, 1, NULL ) ;
	hbmHighlight =
		CreateBitmap( bmInfo.bmWidth, bmInfo.bmHeight, 1, 1, NULL ) ;
	hbmResult =
		CreateCompatibleBitmap( hDC, bm.bmWidth, bm.bmHeight ) ;
	hbmOld = (HBITMAP)SelectObject( hColorDC, hBitmap ) ;
	// Set background color of bitmap for mono conversion
	// We assume that the pixel in the top left corner has the background color
	SetBkColor( hColorDC, GetPixel( hColorDC, 0, 0 ) ) ;
	// Create the highlight bitmap.
	hbmHighlight = (HBITMAP)SelectObject( hMonoDC, (HGDIOBJ) hbmHighlight ) ;
	PatBlt( hMonoDC, 0, 0, bmInfo.bmWidth, bmInfo.bmHeight, WHITENESS ) ;
	BitBlt( hMonoDC, 0, 0, bmInfo.bmWidth - 1, bmInfo.bmHeight - 1,
		hColorDC, 1, 1, SRCCOPY ) ;
	BitBlt( hMonoDC, 0, 0, bmInfo.bmWidth, bmInfo.bmHeight,
		hColorDC, 0, 0, MERGEPAINT ) ;
	hbmHighlight = (HBITMAP)SelectObject( hMonoDC, (HGDIOBJ) hbmHighlight ) ;

	// create the shadow bitmap
	hbmShadow = (HBITMAP)SelectObject( hMonoDC, (HGDIOBJ) hbmShadow ) ;
	PatBlt( hMonoDC, 0, 0, bmInfo.bmWidth, bmInfo.bmHeight, WHITENESS ) ;
	BitBlt( hMonoDC, 1, 1, bmInfo.bmWidth-1, bmInfo.bmHeight-1,
		hColorDC, 0, 0, SRCCOPY ) ;
	BitBlt( hMonoDC, 0, 0, bmInfo.bmWidth, bmInfo.bmHeight,
		hColorDC, 0, 0, MERGEPAINT ) ;
	hbmShadow = (HBITMAP)SelectObject( hMonoDC, (HGDIOBJ) hbmShadow ) ;

	// Now let's start working on the final image
	SelectObject( hColorDC, hbmResult ) ;
	// Select and realize the palette if one is supplied
	if( hPal && GetDeviceCaps(hDC, RASTERCAPS) & RC_PALETTE )
		::SelectPalette( hColorDC, hPal, FALSE );
	// Draw the background image
	BitBlt(hColorDC, 0, 0, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0,SRCCOPY);
	// Restore the old bitmap in the hMemDC
	::SelectObject( hMemDC, hbmOldMem );
	// Set the background and foreground color for the raster operations
	SetBkColor( hColorDC, RGB(255,255,255) ) ;
	SetTextColor( hColorDC, RGB(0,0,0) ) ;
	// blt the highlight edge
	hbrPat = CreateSolidBrush( clrHighlight ) ;
	hbrPat = (HBRUSH)SelectObject( hColorDC, hbrPat ) ;
	hbmHighlight = (HBITMAP)SelectObject( hMonoDC, (HGDIOBJ) hbmHighlight ) ;
	BitBlt( hColorDC, xDest, yDest, bmInfo.bmWidth, bmInfo.bmHeight,
		hMonoDC, 0, 0, PSDPxax ) ;
	DeleteObject( SelectObject( hColorDC, hbrPat ) ) ;
	hbmHighlight = (HBITMAP)SelectObject( hMonoDC, (HGDIOBJ) hbmHighlight ) ;
	// blt the shadow edge
	hbrPat = CreateSolidBrush( clrShadow ) ;
	hbrPat = (HBRUSH)SelectObject( hColorDC, hbrPat ) ;
	hbmShadow = (HBITMAP)SelectObject( hMonoDC, (HGDIOBJ) hbmShadow ) ;
	BitBlt( hColorDC, xDest, yDest, bmInfo.bmWidth, bmInfo.bmHeight,
		hMonoDC, 0, 0, PSDPxax ) ;
	DeleteObject( SelectObject( hColorDC, hbrPat ) ) ;
	hbmShadow = (HBITMAP)SelectObject( hMonoDC, (HGDIOBJ) hbmShadow ) ;
	// select old bitmap into color DC 
	SelectObject( hColorDC, hbmOld ) ;
	DeleteObject( (HGDIOBJ) hbmShadow ) ;
	DeleteObject( (HGDIOBJ) hbmHighlight ) ;
	ReleaseDC( NULL, hDC ) ;

	return ( hbmResult ) ;


  • How can I change the context

    Posted by Legacy on 02/10/2003 12:00am

    Originally posted by: aliho

    CDC *dc = GetDC(); // get current device context
    m_pictDC.CreateCompatibleDC(dc); // create the same device to save the picture
    m_drawDC.CreateCompatibleDC(dc); // create the same device for drawing

    CRect rect;
    m_pictDC.StretchBlt(0, 0,rect.Width(), rect.Height(), dc, 0, 0, rect.Width(), rect.Height(), SRCCOPY); // save the picture
    m_drawDC.StretchBlt(0, 0,rect.Width(), rect.Height(), dc, 0, 0, rect.Width(), rect.Height(), SRCCOPY); // save the picture


    dc->StretchBlt(0, 0,rect.Width(), rect.Height(), &m_pictDC, 0, 0, rect.Width(), rect.Height(), SRCCOPY);

    don't work

  • Demo

    Posted by Legacy on 10/21/2002 12:00am

    Originally posted by: SP.Raj

    Can I have complete Demo and src of this work?

  • rkgotrala

    Posted by Legacy on 09/03/2002 12:00am

    Originally posted by: rkgotrala

    it is good can we make it simple

  • Can I get a demo of this project

    Posted by Legacy on 06/19/2002 12:00am

    Originally posted by: suguna

    Can I get a demo of this project please? 
    Is there any way to emboss another bitmap on the bitmap?

  • real time mark on bmp

    Posted by Legacy on 02/19/2002 12:00am

    Originally posted by: hong

    How to mark date and time on a bmp ? dynamically generate the date and time mark.

  • Can I get a demo of this project

    Posted by Legacy on 09/14/2001 12:00am

    Originally posted by: Richard Harris

    Can you send me a demo, so I can see how this work?


  • Please help me read an image file

    Posted by Legacy on 09/13/2001 12:00am

    Originally posted by: alvin james

    i am currently working on a program that will allow me do do some image processing. I have written code that wil allow me to load and dispaly a bitmap, but i am having trouble getting the values of the pixels. I am working with grayscale images and all i need is the code that will allow me to save the pixel values of the loaded image,x, to an array that i can process later. I would love any help that i can get. Thank you.

  • www.exontrol.com

    Posted by Legacy on 09/07/2001 12:00am

    Originally posted by: www.exontrol.com


  • Where is the demonstration?

    Posted by Legacy on 08/23/2001 12:00am

    Originally posted by: Ashok kumar

    I want to see the demonstration fot this project.
    How can I check this code for any bitmap?
    Answer awaited

  • Embose

    Posted by Legacy on 08/08/2001 12:00am

    Originally posted by: Mizan Rahman


    I wanted to know how to paint BMP as disabled so it appearss that the object is inaccessible.

    You can e-mail me the ans.

    Thank you.
    Mizan Rahman

  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • When individual departments procure cloud service for their own use, they usually don't consider the hazardous organization-wide implications. Read this paper to learn best practices for setting up an internal, IT-based cloud brokerage function that service the entire organization. Find out how this approach enables you to retain top-down visibility and control of network security and manage the impact of cloud traffic on your WAN.

  • U.S. companies are desperately trying to recruit and hire skilled software engineers and developers, but there's simply not enough quality talent to go around. In response, companies often resort to inferior solutions -- hiring substandard developers and engineers, recruiting talent on a part-time or temporary basis, poaching people from competitors, or burdening an already stressed IT staff for more of their labor. Fortunately, there's a better solution. Read this white paper to learn the business value of …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date