Print transparent bitmaps via regions

This is a modification of Mr Lachand-Robert's BitmapToRegion() function.

The new routine, SetBitmapPixels(), lets you print a CBitmap transparently, even on an HP printer, where you can't use the normal raster operation codes. It is too slow for display, but can be used for printing in place of the normal "blitting" of the bits to the DC.

I have compiled it clean on VC5.0, warning level 4, and have used it in my own code. It has not been thoroughly checked for all pathological logic cases, but does not (seem to) leak any memory.

Here is a sample of calling the routine:


////////////////////////////////////////////
//This sample assumes a mapping mod
//of MM_LOENGLISH (100 pts/inch)
//with the viewport origin in the upper lef
//corner of the DC. It assumes the DC has bee
//built and prepared, and is referenced vi
//a printer DC ptr named pDC
/
//The bitmap is initialized with a resourc
//built and included in the project workspace
/
//The bitmap will be drawn into the D
//centered on a point 2" over and 2" down
/////////////////////////////////////////////
CBitmap bmStamp;

bmStamp.LoadBitmap(IDB_BITMAP_WOODS);

BOOL bOk = SetBitmapPixels(
           pDC,
	       HBITMAP(bmStamp),
	       RGB(255,255,255), // Transparency color = whit
		   200,
		  -200);



And here is the routine:

///////////////////////////////////////////
//Set pixels in a DC drawing surfac
//which are not transparent in a bitmap
/
//If you have a CBitmap object, you can us
//this function to print it transparentl
//on an HP printer
/
//pDC:             DC pointe
//hBmp:            Source bitma
//rgbTransparent:  Color base for the "transparent" pixel
//nXOffset:        Offset to bitmap UL corner, in logical unit
//nYOffset:        Offset to bitmap UL corner, in logical unit
//                 (usually negative
/
//Returns true if OK, else false
/
//This code is based on the BitmapToRegion(
//function contributed by Mr. Lachand-Robert
/
//It is a modification of the original function
//which does the following
/
//1) Given a DC which is being drawn int
//   (such as a printer), the functio
//   makes sure that the regions draw
//   will be centered on the X,Y offsets
/
//2) The function then loops thru the bit
//   in the bitmap. When it encounters 
//   block of bits which are all of the sam
//   color, and which are not the transparenc
//   color, it creates a small region an
//   fills it with a brush of the pixel color
/
/
//The function assumes a mapping mode othe
//than MM_TEXT for the passed-in Devic
//Context. I used it successfully wit
//MM_ANISOTROPIC, for example, and was abl
//to "zoom" the bitmap correctly
///////////////////////////////////////////
BOOL SetBitmapPixels(CDC*     pDC,
                    HBITMAP  hBmp,
                    COLORREF rgbTransparent,
					int      nXOffset,
					int      nYOffset)
{
	//Sanity..
	if (hBmp    == NULL || pDC     == NULL)
		return FALSE;

	// Create a memory DC inside which we will scan the bitmap conten
	HDC hMemDC = CreateCompatibleDC(NULL);
	ASSERT(hMemDC);

	if (hMemDC)
	{
		// Get bitmap siz
		BITMAP bm;
		GetObject(hBmp, sizeof(bm), &bm);

		//Make sure x and Y offset are relativ
		//to center of the bitmap; thi
		//way the bitmap will appear centere
		//on (nXOffset,nYOffset
		nXOffset -= bm.bmWidth /2;
		nYOffset -= bm.bmHeight/2;

		// Create a 32 bits depth bitmap and select it into the memory DC
		BITMAPINFOHEADER RGB32BITSBITMAPINFO =
		{	
			sizeof(BITMAPINFOHEADER),// biSize
			bm.bmWidth,				 // biWidth;
           -bm.bmHeight,			 // biHeight
			1,						 // biPlanes;
			32,						 // biBitCount
			BI_RGB,					 // biCompression;
			0,						 // biSizeImage;
			0,						 // biXPelsPerMeter;
			0,						 // biYPelsPerMeter;
			0,						 // biClrUsed;
			0						 // biClrImportant;
		};

		VOID * pbits32; 

		HBITMAP hbm32 = CreateDIBSection(
		                hMemDC,
		                (BITMAPINFO *)&RGB32BITSBITMAPINFO,
							 DIB_RGB_COLORS,
							 &pbits32,
							 NULL,
							 0);

		if (hbm32)
		{
			HBITMAP holdBmp = (HBITMAP)SelectObject(hMemDC, hbm32);

			// Create a DC just to copy the bitmap into the memory D
			HDC hDC = CreateCompatibleDC(hMemDC);

			if (hDC)
			{
				// Get how many bytes per row w
				//have for the bitmap bit
				//(rounded up to 32 bits
				BITMAP bm32;
				VERIFY(GetObject(hbm32, sizeof(bm32), &bm32));

				while (bm32.bmWidthBytes % 4)
					bm32.bmWidthBytes++;

				// Copy the bitmap into the memory D
				HBITMAP holdBmp = (HBITMAP)SelectObject(hDC, hBmp);
				VERIFY(BitBlt(hMemDC,
					           0,
								  0,
								  bm.bmWidth,
								  bm.bmHeight,
								  hDC,
								  0,
								  0,
								  SRCCOPY));

				//Scan each bitmap row from bottom to to
				//(the bitmap is inverted vertically
				BYTE *p32 = (BYTE *)bm32.bmBits +
				            (bm32.bmHeight - 1) * bm32.bmWidthBytes;

				//Scan each bitmap pixel from left to right
				//Search for a continuous lin
				//of non transparent pixel
				for (int y = 0; y < bm.bmHeight; y++)
				{
					for (int x = 0; x < bm.bmWidth; x++)
					{
						int  x0 = x; //Ptr to start of region to fil
						LONG *p = (LONG *)p32 + x;

						COLORREF
						rgbBase =RGB(GetBValue((DWORD)(*p)),
								   	 GetGValue((DWORD)(*p)),
										 GetRValue((DWORD)(*p)));

						COLORREF rgbCurr = rgbBase;

						if (rgbBase != rgbTransparent)
						{
							while(rgbCurr == rgbBase &&
							      x        < bm.bmWidth)
							{
								p++;
								x++;
  								rgbCurr = RGB(GetBValue((DWORD)(*p)),
							  	   			  GetGValue((DWORD)(*p)),
												  GetRValue((DWORD)(*p)));
							}

							CRgn rgnTemp;

							rgnTemp.CreateRectRgn(
									  x0 + nXOffset,
									  y  + nYOffset,
									  x  + nXOffset,
									  y+1+ nYOffset);

							CBrush  brTemp(rgbBase);
							pDC->FillRgn(&rgnTemp,&brTemp);

							x--; //Decrement to account for x for loo
						}	//end if not transparent
					}	//end for 

					// Go to next ro
					//(remember, the bitmap is inverted vertically
					p32 -= bm32.bmWidthBytes;
				}	//end for 

				// Clean u
				SelectObject(hDC, holdBmp);
				DeleteDC(hDC);
			}

			DeleteObject(SelectObject(hMemDC, holdBmp));
		}

		DeleteDC(hMemDC);
	}	

	return TRUE;
}
///////////////////////////////