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
	BITMAP bm;
	::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 );
		::RealizePalette(hColorDC);
	}
	// 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 ) ;
}



Comments

  • 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;
    GetClientRect(&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

    Reply
  • 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?
    Thanks,
    SP.Raj

    Reply
  • rkgotrala

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

    Originally posted by: rkgotrala

    it is good can we make it simple
    

    Reply
  • 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?

    Reply
  • 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.

    Reply
  • 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?

    Thank-you

    Reply
  • 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.

    Reply
  • www.exontrol.com

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

    Originally posted by: www.exontrol.com

    Nice.

    Reply
  • 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
    Thanks

    Reply
  • Embose

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

    Originally posted by: Mizan Rahman

    Hi,
    
    

    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

    Reply
  • Loading, Please Wait ...

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

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • Webinar on September 23, 2014, 2 p.m. ET / 11 a.m. PT Mobile commerce presents an array of opportunities for any business -- from connecting with your customers through mobile apps to enriching operations with mobile enterprise solutions. Join guest speaker, Michael Facemire, Forrester Research, Inc. Principal Analyst, as he discusses the new demands of mobile engagement and how application program interfaces (APIs) play a crucial role. Check out this upcoming webinar to learn about the new set of …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds