Click here to Skip to main content
15,884,099 members
Articles / Desktop Programming / MFC

Scan2PDF

Rate me:
Please Sign up or sign in to vote.
4.90/5 (75 votes)
22 Apr 20053 min read 432.7K   20.9K   230  
A utility for bulk scanning, converting the scanned pages to PDF and burning them on CD/DVD for archiving.
// xImaWnd.cpp : Windows functions
/* 07/08/2001 v1.00 - Davide Pizzolato - www.xdp.it
 * CxImage version 5.99c 17/Oct/2004
 */

#include "ximage.h"

#include "ximaiter.h" 
#include "ximabmp.h"

////////////////////////////////////////////////////////////////////////////////
#if CXIMAGE_SUPPORT_WINCE
////////////////////////////////////////////////////////////////////////////////
long CxImage::Blt(HDC pDC, long x, long y)
{
	if((pDib==0)||(pDC==0)||(!info.bEnabled)) return 0;

    HBRUSH brImage = CreateDIBPatternBrushPt(pDib, DIB_RGB_COLORS);
    HBRUSH brOld = (HBRUSH) SelectObject(pDC, brImage);
    PatBlt(pDC, 0, 0, head.biWidth, head.biHeight, PATCOPY);
    SelectObject(pDC, brOld);
    DeleteObject(brImage);
	return 1;
}
#endif //CXIMAGE_SUPPORT_WINCE
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
#if CXIMAGE_SUPPORT_WINDOWS
////////////////////////////////////////////////////////////////////////////////
/**
 * Transfer the image in a global bitmap handle (clipboard copy)
 */
HANDLE CxImage::CopyToHandle()
{
	HANDLE hMem=NULL;
	if (pDib){
		hMem= GlobalAlloc(GHND, GetSize());
		if (hMem){
			BYTE* pDst=(BYTE*)GlobalLock(hMem);
			if (pDst){
				memcpy(pDst,pDib,GetSize());
			}
			GlobalUnlock(hMem);
		}
	}
	return hMem;
}
////////////////////////////////////////////////////////////////////////////////
/**
 * Global object (clipboard paste) constructor
 * \param hMem: source bitmap object, the clipboard format must be CF_DIB
 * \return true if everything is ok
 */
bool CxImage::CreateFromHANDLE(HANDLE hMem)
{
	if (!Destroy())
		return false;

	DWORD dwSize = GlobalSize(hMem);
	if (!dwSize) return false;

	BYTE *lpVoid;						//pointer to the bitmap
	lpVoid = (BYTE *)GlobalLock(hMem);
	BITMAPINFOHEADER *pHead;			//pointer to the bitmap header
	pHead = (BITMAPINFOHEADER *)lpVoid;
	if (lpVoid){

		//CxMemFile hFile(lpVoid,dwSize);

		//copy the bitmap header
		memcpy(&head,pHead,sizeof(BITMAPINFOHEADER));
		//create the image
		if(!Create(head.biWidth,head.biHeight,head.biBitCount)){
			GlobalUnlock(lpVoid);
			return false;
		}
		//preserve DPI
		if (head.biXPelsPerMeter) SetXDPI((long)floor(head.biXPelsPerMeter * 254.0 / 10000.0 + 0.5)); else SetXDPI(96);
		if (head.biYPelsPerMeter) SetYDPI((long)floor(head.biYPelsPerMeter * 254.0 / 10000.0 + 0.5)); else SetYDPI(96);

		/*//copy the pixels (old way)
		if((pHead->biCompression != BI_RGB) || (pHead->biBitCount == 32)){ //<J�rgen Alfredsson>
			// BITFIELD case
			// set the internal header in the dib
			memcpy(pDib,&head,sizeof(head));
			// get the bitfield masks
			DWORD bf[3];
			memcpy(bf,lpVoid+pHead->biSize,12);
			// transform into RGB
			Bitfield2RGB(lpVoid+pHead->biSize+12,(WORD)bf[0],(WORD)bf[1],(WORD)bf[2],(BYTE)pHead->biBitCount);
		} else { //normal bitmap
			memcpy(pDib,lpVoid,GetSize());
		}*/

		// <Michael Gandyra>
		// fill in color map
		bool bIsOldBmp = (head.biSize == sizeof(BITMAPCOREHEADER));
		RGBQUAD *pRgb = GetPalette();
		if (pRgb) {
			// number of colors to fill in
			int nColors = DibNumColors(pHead);
			if (bIsOldBmp) {
				/* get pointer to BITMAPCOREINFO (old style 1.x) */
				LPBITMAPCOREINFO lpbmc = (LPBITMAPCOREINFO)lpVoid;
				for (int i = nColors - 1; i >= 0; i--) {
					pRgb[i].rgbRed      = lpbmc->bmciColors[i].rgbtRed;
					pRgb[i].rgbGreen    = lpbmc->bmciColors[i].rgbtGreen;
					pRgb[i].rgbBlue     = lpbmc->bmciColors[i].rgbtBlue;
					pRgb[i].rgbReserved = (BYTE)0;
				}
			} else {
				/* get pointer to BITMAPINFO (new style 3.x) */
				LPBITMAPINFO lpbmi = (LPBITMAPINFO)lpVoid;
				for (int i = nColors - 1; i >= 0; i--) {
					pRgb[i].rgbRed      = lpbmi->bmiColors[i].rgbRed;
					pRgb[i].rgbGreen    = lpbmi->bmiColors[i].rgbGreen;
					pRgb[i].rgbBlue     = lpbmi->bmiColors[i].rgbBlue;
					pRgb[i].rgbReserved = (BYTE)0;
				}
			}
		}

		// <Michael Gandyra>
		DWORD dwCompression = pHead->biCompression;
		// compressed bitmap ?
		if(dwCompression!=BI_RGB || pHead->biBitCount==32) {
			// get the bitmap bits
			LPSTR lpDIBBits = (LPSTR)((BYTE*)pHead + *(DWORD*)pHead + (WORD)(GetNumColors() * sizeof(RGBQUAD)));
			// decode and copy them to our image
			switch (pHead->biBitCount) {
			case 32 :
				{
					// BITFIELD case
					if (dwCompression == BI_BITFIELDS || dwCompression == BI_RGB) {
						// get the bitfield masks
						DWORD bf[3];
						memcpy(bf,lpVoid+pHead->biSize,12);
						// transform into RGB
						Bitfield2RGB(lpVoid+pHead->biSize+12,(WORD)bf[0],(WORD)bf[1],(WORD)bf[2],(BYTE)pHead->biBitCount);
					} else 
						throw "unknown compression";
				}
				break;
			case 16 :
				{
					// get the bitfield masks
					long offset=0;
					DWORD bf[3];
					if (dwCompression == BI_BITFIELDS) {
						memcpy(bf,lpVoid+pHead->biSize,12);
						offset= 12;
					} else {
						bf[0] = 0x7C00;
						bf[1] = 0x3E0;
						bf[2] = 0x1F; // RGB555
					}
					// copy the pixels
					memcpy(info.pImage, lpDIBBits + offset, head.biHeight*((head.biWidth+1)/2)*4);
					// transform into RGB
					Bitfield2RGB(info.pImage, (WORD)bf[0], (WORD)bf[1], (WORD)bf[2], 16);
				}
				break;
			case 8 :
			case 4 :
			case 1 :
				{
					switch (dwCompression) {
					case BI_RLE4:
						{
							BYTE status_byte = 0;
							BYTE second_byte = 0;
							int scanline = 0;
							int bits = 0;
							BOOL low_nibble = FALSE;
							CImageIterator iter(this);

							for (BOOL bContinue = TRUE; bContinue; ) {
								status_byte = *(lpDIBBits++);
								switch (status_byte) {
								case RLE_COMMAND :
									status_byte = *(lpDIBBits++);
									switch (status_byte) {
									case RLE_ENDOFLINE :
										bits = 0;
										scanline++;
										low_nibble = FALSE;
										break;
									case RLE_ENDOFBITMAP :
										bContinue = FALSE;
										break;
									case RLE_DELTA :
										{
											// read the delta values
											BYTE delta_x;
											BYTE delta_y;
											delta_x = *(lpDIBBits++);
											delta_y = *(lpDIBBits++);
											// apply them
											bits       += delta_x / 2;
											scanline   += delta_y;
											break;
										}
									default :
										second_byte = *(lpDIBBits++);
										BYTE* sline = iter.GetRow(scanline);
										for (int i = 0; i < status_byte; i++) {
											if (low_nibble) {
												if ((DWORD)(sline + bits) < (DWORD)(info.pImage + head.biSizeImage)) {
													*(sline + bits) |=  second_byte & 0x0F;
												}
												if (i != status_byte - 1)
													second_byte = *(lpDIBBits++);
												bits++;
											} else {
												if ((DWORD)(sline + bits) <(DWORD)(info.pImage + head.biSizeImage)) {
													*(sline + bits) = (BYTE)(second_byte & 0xF0);
												}
											}
											low_nibble = !low_nibble;
										}
										if ((((status_byte+1) >> 1) & 1 )== 1)
											second_byte = *(lpDIBBits++);
										break;
									};
									break;
									default :
										{
											BYTE* sline = iter.GetRow(scanline);
											second_byte = *(lpDIBBits++);
											for (unsigned i = 0; i < status_byte; i++) {
												if (low_nibble) {
													if ((DWORD)(sline + bits) <(DWORD)(info.pImage + head.biSizeImage)) {
														*(sline + bits) |= second_byte & 0x0F;
													}
													bits++;
												} else {
													if ((DWORD)(sline + bits) <(DWORD)(info.pImage + head.biSizeImage))	{
														*(sline + bits) = (BYTE)(second_byte & 0xF0);
													}
												}
												low_nibble = !low_nibble;
											}
										}
										break;
								};
							}
						}
						break;
					case BI_RLE8 :
						{
							BYTE status_byte = 0;
							BYTE second_byte = 0;
							int scanline = 0;
							int bits = 0;
							CImageIterator iter(this);

							for (BOOL bContinue = TRUE; bContinue; ) {
								status_byte = *(lpDIBBits++);
								if (status_byte==RLE_COMMAND) {
									status_byte = *(lpDIBBits++);
									switch (status_byte) {
									case RLE_ENDOFLINE :
										bits = 0;
										scanline++;
										break;
									case RLE_ENDOFBITMAP :
										bContinue = FALSE;
										break;
									case RLE_DELTA :
										{
											// read the delta values
											BYTE delta_x;
											BYTE delta_y;
											delta_x = *(lpDIBBits++);
											delta_y = *(lpDIBBits++);
											// apply them
											bits     += delta_x;
											scanline += delta_y;
										}
										break;
									default :
										int nNumBytes = sizeof(BYTE) * status_byte;
										memcpy((void *)(iter.GetRow(scanline) + bits), lpDIBBits, nNumBytes);
										lpDIBBits += nNumBytes;
										// align run length to even number of bytes 
										if ((status_byte & 1) == 1)
											second_byte = *(lpDIBBits++);
										bits += status_byte;
										break;
									};
								} else {
									BYTE *sline = iter.GetRow(scanline);
									second_byte = *(lpDIBBits++);
									for (unsigned i = 0; i < status_byte; i++) {
										if ((DWORD)bits<info.dwEffWidth){
											*(sline + bits) = second_byte;
											bits++;
										} else {
											bContinue = FALSE;
											break;
										}
									}
								}
							}
						}
						break;
					default :
						throw "compression type not supported";
					}
				}
			}
		} else {
			//normal bitmap (not compressed)
			memcpy(pDib,lpVoid,GetSize());
		}

		GlobalUnlock(lpVoid);
		return true;
	}
	return false;
}
////////////////////////////////////////////////////////////////////////////////
/**
 * Transfer the image in a  bitmap handle
 * \param hdc: target device context (the screen, usually)
 * \return bitmap handle, or NULL if an error occurs.
 */
HBITMAP CxImage::MakeBitmap(HDC hdc)
{
	if (!pDib)
		return NULL;

	if (!hdc){
		// this call to CreateBitmap doesn't create a DIB <jaslet>
		// // Create a device-independent bitmap <CSC>
		//  return CreateBitmap(head.biWidth,head.biHeight,	1, head.biBitCount, GetBits());
		// use instead this code
		HDC hMemDC = CreateCompatibleDC(NULL);
		LPVOID pBit32;
		HBITMAP bmp = CreateDIBSection(hMemDC,(LPBITMAPINFO)pDib,DIB_RGB_COLORS, &pBit32, NULL, 0);
		if (pBit32) memcpy(pBit32, GetBits(), head.biSizeImage);
		DeleteDC(hMemDC);
		return bmp;
	}

	// this single line seems to work very well
	HBITMAP bmp = CreateDIBitmap(hdc, (LPBITMAPINFOHEADER)pDib, CBM_INIT,
		GetBits(), (LPBITMAPINFO)pDib, DIB_RGB_COLORS);

	return bmp;
}
////////////////////////////////////////////////////////////////////////////////
/**
 * Bitmap resource constructor
 * \param hbmp : bitmap resource handle
 * \param hpal : (optional) palette, useful for 8bpp DC 
 * \return true if everything is ok
 */
bool CxImage::CreateFromHBITMAP(HBITMAP hbmp, HPALETTE hpal)
{
	if (!Destroy())
		return false;

	if (hbmp) { 
        BITMAP bm;
		// get informations about the bitmap
        GetObject(hbmp, sizeof(BITMAP), (LPSTR) &bm);
		// create the image
        if (!Create(bm.bmWidth, bm.bmHeight, bm.bmBitsPixel, 0))
			return false;
		// create a device context for the bitmap
        HDC dc = ::GetDC(NULL);
		if (!dc)
			return false;

		if (hpal){
			SelectObject(dc,hpal); //the palette you should get from the user or have a stock one
			RealizePalette(dc);
		}

		// copy the pixels
        if (GetDIBits(dc, hbmp, 0, head.biHeight, info.pImage,
			(LPBITMAPINFO)pDib, DIB_RGB_COLORS) == 0){ //replace &head with pDib <Wil Stark>
            strcpy(info.szLastError,"GetDIBits failed");
			::ReleaseDC(NULL, dc);
			return false;
        }
        ::ReleaseDC(NULL, dc);
		return true;
    }
	return false;
}
////////////////////////////////////////////////////////////////////////////////
/**
 * icon resource constructor
 * \param hico : icon resource handle
 * \return true if everything is ok
 */
bool CxImage::CreateFromHICON(HICON hico)
{
	if (!Destroy())
		return false;

	if (hico) { 
		ICONINFO iinfo;
		GetIconInfo(hico,&iinfo);
		if (!CreateFromHBITMAP(iinfo.hbmColor))
			return false;
#if CXIMAGE_SUPPORT_ALPHA
		CxImage mask;
		mask.CreateFromHBITMAP(iinfo.hbmMask);
		mask.GrayScale();
		mask.Negative();
		AlphaSet(mask);
#endif
		DeleteObject(iinfo.hbmColor); //<Sims>
		DeleteObject(iinfo.hbmMask);  //<Sims>
		return true;
    }
	return false;
}
////////////////////////////////////////////////////////////////////////////////
long CxImage::Draw(HDC hdc, const RECT& rect, RECT* pClipRect, bool bSmooth)
{
	return Draw(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pClipRect,bSmooth);
}
////////////////////////////////////////////////////////////////////////////////
/**
 * Draws the image in the specified device context, with support for alpha channel, alpha palette, transparency, opacity.
 * \param hdc : destination device context
 * \param x,y : (optional) offset
 * \param cx,cy : (optional) size.
 *                 - If cx or cy are not specified (or less than 0), the normal width or height will be used
 *                 - If cx or cy are different than width or height, the image will be stretched
 *
 * \param pClipRect : limit the drawing operations inside a given rectangle in the output device context.
 * \param bSmooth : activates a bilinear filter that will enhance the appearence for zommed pictures.
 *                   Quite slow. Needs CXIMAGE_SUPPORT_INTERPOLATION.
 * \return true if everything is ok
 */
long CxImage::Draw(HDC hdc, long x, long y, long cx, long cy, RECT* pClipRect, bool bSmooth)
{
	if((pDib==0)||(hdc==0)||(cx==0)||(cy==0)||(!info.bEnabled)) return 0;

	if (cx < 0) cx = head.biWidth;
	if (cy < 0) cy = head.biHeight;
	bool bTransparent = info.nBkgndIndex != -1;
	bool bAlpha = pAlpha != 0;

	RECT mainbox; // (experimental) 
	if (pClipRect){
		GetClipBox(hdc,&mainbox);
		HRGN rgn = CreateRectRgnIndirect(pClipRect);
		ExtSelectClipRgn(hdc,rgn,RGN_AND);
		DeleteObject(rgn);
	}

	//find the smallest area to paint
	RECT clipbox,paintbox;
	GetClipBox(hdc,&clipbox);

	paintbox.top = min(clipbox.bottom,max(clipbox.top,y));
	paintbox.left = min(clipbox.right,max(clipbox.left,x));
	paintbox.right = max(clipbox.left,min(clipbox.right,x+cx));
	paintbox.bottom = max(clipbox.top,min(clipbox.bottom,y+cy));

	long destw = paintbox.right - paintbox.left;
	long desth = paintbox.bottom - paintbox.top;

	if (!(bTransparent || bAlpha || info.bAlphaPaletteEnabled)){
		if (cx==head.biWidth && cy==head.biHeight){ //NORMAL
			SetStretchBltMode(hdc,COLORONCOLOR);	
			SetDIBitsToDevice(hdc, x, y, cx, cy, 0, 0, 0, cy,
						info.pImage,(BITMAPINFO*)pDib,DIB_RGB_COLORS);
		} else { //STRETCH
			//pixel informations
			RGBQUAD c={0,0,0,0};
			//Preparing Bitmap Info
			BITMAPINFO bmInfo;
			memset(&bmInfo.bmiHeader,0,sizeof(BITMAPINFOHEADER));
			bmInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
			bmInfo.bmiHeader.biWidth=destw;
			bmInfo.bmiHeader.biHeight=desth;
			bmInfo.bmiHeader.biPlanes=1;
			bmInfo.bmiHeader.biBitCount=24;
			BYTE *pbase;	//points to the final dib
			BYTE *pdst;		//current pixel from pbase
			BYTE *ppix;		//current pixel from image
			//get the background
			HDC TmpDC=CreateCompatibleDC(hdc);
			HBITMAP TmpBmp=CreateDIBSection(hdc,&bmInfo,DIB_RGB_COLORS,(void**)&pbase,0,0);
			HGDIOBJ TmpObj=SelectObject(TmpDC,TmpBmp);

			if (pbase){
				long xx,yy;
				long sx,sy;
				float dx,dy;
				BYTE *psrc;

				long ew = ((((24 * destw) + 31) / 32) * 4);
				long ymax = paintbox.bottom;
				long xmin = paintbox.left;
				float fx=(float)head.biWidth/(float)cx;
				float fy=(float)head.biHeight/(float)cy;

				for(yy=0;yy<desth;yy++){
					dy = head.biHeight-(ymax-yy-y)*fy;
					sy = max(0L,(long)floor(dy));
					psrc = info.pImage+sy*info.dwEffWidth;
					pdst = pbase+yy*ew;
					for(xx=0;xx<destw;xx++){
						dx = (xx+xmin-x)*fx;
						sx = max(0L,(long)floor(dx));
#if CXIMAGE_SUPPORT_INTERPOLATION
						if (bSmooth){
							c = GetPixelColorInterpolated(dx - 0.5f, dy - 0.5f, CxImage::IM_BILINEAR, CxImage::OM_REPEAT);
						} else
#endif //CXIMAGE_SUPPORT_INTERPOLATION
						{
							if (head.biClrUsed){
								c=GetPaletteColor(GetPixelIndex(sx,sy));
							} else {
								ppix = psrc + sx*3;
								c.rgbBlue = *ppix++;
								c.rgbGreen= *ppix++;
								c.rgbRed  = *ppix;
							}
						}
						*pdst++=c.rgbBlue;
						*pdst++=c.rgbGreen;
						*pdst++=c.rgbRed;
					}
				}
			}
			//paint the image & cleanup
			SetDIBitsToDevice(hdc,paintbox.left,paintbox.top,destw,desth,0,0,0,desth,pbase,&bmInfo,0);
			DeleteObject(SelectObject(TmpDC,TmpObj));
			DeleteDC(TmpDC);
		}
	} else {	// draw image with transparent/alpha blending
	//////////////////////////////////////////////////////////////////
		//Alpha blend - Thanks to Florian Egel

		//pixel informations
		RGBQUAD c={0,0,0,0};
		RGBQUAD ct = GetTransColor();
		long* pc = (long*)&c;
		long* pct= (long*)&ct;
		long cit = GetTransIndex();
		long ci;

		//Preparing Bitmap Info
		BITMAPINFO bmInfo;
		memset(&bmInfo.bmiHeader,0,sizeof(BITMAPINFOHEADER));
		bmInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
		bmInfo.bmiHeader.biWidth=destw;
		bmInfo.bmiHeader.biHeight=desth;
		bmInfo.bmiHeader.biPlanes=1;
		bmInfo.bmiHeader.biBitCount=24;

		BYTE *pbase;	//points to the final dib
		BYTE *pdst;		//current pixel from pbase
		BYTE *ppix;		//current pixel from image

		//get the background
		HDC TmpDC=CreateCompatibleDC(hdc);
		HBITMAP TmpBmp=CreateDIBSection(hdc,&bmInfo,DIB_RGB_COLORS,(void**)&pbase,0,0);
		HGDIOBJ TmpObj=SelectObject(TmpDC,TmpBmp);
		BitBlt(TmpDC,0,0,destw,desth,hdc,paintbox.left,paintbox.top,SRCCOPY);

		if (pbase){
			long xx,yy,alphaoffset,ix,iy;
			BYTE a,a1,*psrc;
			long ew = ((((24 * destw) + 31) / 32) * 4);
			long ymax = paintbox.bottom;
			long xmin = paintbox.left;

			if (cx!=head.biWidth || cy!=head.biHeight){
				//STRETCH
				float fx=(float)head.biWidth/(float)cx;
				float fy=(float)head.biHeight/(float)cy;
				float dx,dy;
				long sx,sy;
				
				for(yy=0;yy<desth;yy++){
					dy = head.biHeight-(ymax-yy-y)*fy;
					sy = max(0L,(long)floor(dy));

					alphaoffset = sy*head.biWidth;
					pdst = pbase + yy*ew;
					psrc = info.pImage + sy*info.dwEffWidth;

					for(xx=0;xx<destw;xx++){
						dx = (xx+xmin-x)*fx;
						sx = max(0L,(long)floor(dx));

						if (bAlpha) a=pAlpha[alphaoffset+sx]; else a=255;
						a =(BYTE)((a*(1+info.nAlphaMax))>>8);

						if (head.biClrUsed){
							ci = GetPixelIndex(sx,sy);
#if CXIMAGE_SUPPORT_INTERPOLATION
							if (bSmooth){
								c = GetPixelColorInterpolated(dx - 0.5f, dy - 0.5f, CxImage::IM_BILINEAR, CxImage::OM_REPEAT);
							} else
#endif //CXIMAGE_SUPPORT_INTERPOLATION
							{
								c = GetPaletteColor(GetPixelIndex(sx,sy));
							}
							if (info.bAlphaPaletteEnabled){
								a = (BYTE)((a*(1+c.rgbReserved))>>8);
							}
						} else {
#if CXIMAGE_SUPPORT_INTERPOLATION
							if (bSmooth){
								c = GetPixelColorInterpolated(dx - 0.5f, dy - 0.5f, CxImage::IM_BILINEAR, CxImage::OM_REPEAT);
							} else
#endif //CXIMAGE_SUPPORT_INTERPOLATION
							{
								ppix = psrc + sx*3;
								c.rgbBlue = *ppix++;
								c.rgbGreen= *ppix++;
								c.rgbRed  = *ppix;
							}
						}
						//if (*pc!=*pct || !bTransparent){
						//if ((head.biClrUsed && ci!=cit) || ((!head.biClrUsed||bSmooth) && *pc!=*pct) || !bTransparent){
						if ((head.biClrUsed && ci!=cit) || (!head.biClrUsed && *pc!=*pct) || !bTransparent){
							// DJT, assume many pixels are fully transparent or opaque and thus avoid multiplication
							if (a == 0) {			// Transparent, retain dest 
								pdst+=3; 
							} else if (a == 255) {	// opaque, ignore dest 
								*pdst++= c.rgbBlue; 
								*pdst++= c.rgbGreen; 
								*pdst++= c.rgbRed; 
							} else {				// semi transparent 
								a1=(BYTE)~a;
								*pdst++=(BYTE)((*pdst * a1 + a * c.rgbBlue)>>8); 
								*pdst++=(BYTE)((*pdst * a1 + a * c.rgbGreen)>>8); 
								*pdst++=(BYTE)((*pdst * a1 + a * c.rgbRed)>>8); 
							} 
						} else {
							pdst+=3;
						}
					}
				}
			} else {
				//NORMAL
				iy=head.biHeight-ymax+y;
				for(yy=0;yy<desth;yy++,iy++){
					alphaoffset=iy*head.biWidth;
					ix=xmin-x;
					pdst=pbase+yy*ew;
					ppix=info.pImage+iy*info.dwEffWidth+ix*3;
					for(xx=0;xx<destw;xx++,ix++){

						if (bAlpha) a=pAlpha[alphaoffset+ix]; else a=255;
						a = (BYTE)((a*(1+info.nAlphaMax))>>8);

						if (head.biClrUsed){
							ci = GetPixelIndex(ix,iy);
							c = GetPaletteColor((BYTE)ci);
							if (info.bAlphaPaletteEnabled){
								a = (BYTE)((a*(1+c.rgbReserved))>>8);
							}
						} else {
							c.rgbBlue = *ppix++;
							c.rgbGreen= *ppix++;
							c.rgbRed  = *ppix++;
						}

						//if (*pc!=*pct || !bTransparent){
						if ((head.biClrUsed && ci!=cit) || (!head.biClrUsed && *pc!=*pct) || !bTransparent){
							// DJT, assume many pixels are fully transparent or opaque and thus avoid multiplication
							if (a == 0) {			// Transparent, retain dest 
								pdst+=3; 
							} else if (a == 255) {	// opaque, ignore dest 
								*pdst++= c.rgbBlue; 
								*pdst++= c.rgbGreen; 
								*pdst++= c.rgbRed; 
							} else {				// semi transparent 
								a1=(BYTE)~a;
								*pdst++=(BYTE)((*pdst * a1 + a * c.rgbBlue)>>8); 
								*pdst++=(BYTE)((*pdst * a1 + a * c.rgbGreen)>>8); 
								*pdst++=(BYTE)((*pdst * a1 + a * c.rgbRed)>>8); 
							} 
						} else {
							pdst+=3;
						}
					}
				}
			}
		}
		//paint the image & cleanup
		SetDIBitsToDevice(hdc,paintbox.left,paintbox.top,destw,desth,0,0,0,desth,pbase,&bmInfo,0);
		DeleteObject(SelectObject(TmpDC,TmpObj));
		DeleteDC(TmpDC);
	}

	if (pClipRect){  // (experimental)
		HRGN rgn = CreateRectRgnIndirect(&mainbox);
		ExtSelectClipRgn(hdc,rgn,RGN_OR);
		DeleteObject(rgn);
	}

	return 1;
}
////////////////////////////////////////////////////////////////////////////////
long CxImage::Draw2(HDC hdc, const RECT& rect)
{
	return Draw2(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
}
////////////////////////////////////////////////////////////////////////////////
/**
 * Draws (stretch) the image with single transparency support
 * \param hdc : destination device context
 * \param x,y : (optional) offset
 * \param cx,cy : (optional) size.
 *                 - If cx or cy are not specified (or less than 0), the normal width or height will be used
 *                 - If cx or cy are different than width or height, the image will be stretched
 *
 * \return true if everything is ok
 */
long CxImage::Draw2(HDC hdc, long x, long y, long cx, long cy)
{
	if((pDib==0)||(hdc==0)||(cx==0)||(cy==0)||(!info.bEnabled)) return 0;
	if (cx < 0) cx = head.biWidth;
	if (cy < 0) cy = head.biHeight;
	bool bTransparent = (info.nBkgndIndex != -1);

	if (!bTransparent){
		SetStretchBltMode(hdc,COLORONCOLOR);	
		StretchDIBits(hdc, x, y, cx, cy, 0, 0, head.biWidth, head.biHeight,
						info.pImage,(BITMAPINFO*)pDib, DIB_RGB_COLORS,SRCCOPY);
	} else {
		// draw image with transparent background
		const int safe = 0; // or else GDI fails in the following - sometimes 
		RECT rcDst = {x+safe, y+safe, x+cx, y+cy};
		if (RectVisible(hdc, &rcDst)){
		/////////////////////////////////////////////////////////////////
			// True Mask Method - Thanks to Paul Reynolds and Ron Gery
			int nWidth = head.biWidth;
			int nHeight = head.biHeight;
			// Create two memory dcs for the image and the mask
			HDC dcImage=CreateCompatibleDC(hdc);
			HDC dcTrans=CreateCompatibleDC(hdc);
			// Select the image into the appropriate dc
			HBITMAP bm = CreateCompatibleBitmap(hdc, nWidth, nHeight);
			HBITMAP pOldBitmapImage = (HBITMAP)SelectObject(dcImage,bm);
			SetStretchBltMode(dcImage,COLORONCOLOR);
			StretchDIBits(dcImage, 0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight,
							info.pImage,(BITMAPINFO*)pDib,DIB_RGB_COLORS,SRCCOPY);

			// Create the mask bitmap
			HBITMAP bitmapTrans = CreateBitmap(nWidth, nHeight, 1, 1, NULL);
			// Select the mask bitmap into the appropriate dc
			HBITMAP pOldBitmapTrans = (HBITMAP)SelectObject(dcTrans, bitmapTrans);
			// Build mask based on transparent colour
			RGBQUAD rgbBG;
			if (head.biBitCount<24) rgbBG = GetPaletteColor((BYTE)info.nBkgndIndex);
			else rgbBG = info.nBkgndColor;
			COLORREF crColour = RGB(rgbBG.rgbRed, rgbBG.rgbGreen, rgbBG.rgbBlue);
			COLORREF crOldBack = SetBkColor(dcImage,crColour);
			BitBlt(dcTrans,0, 0, nWidth, nHeight, dcImage, 0, 0, SRCCOPY);

			// Do the work - True Mask method - cool if not actual display
			StretchBlt(hdc,x, y,cx,cy, dcImage, 0, 0, nWidth, nHeight, SRCINVERT);
			StretchBlt(hdc,x, y,cx,cy, dcTrans, 0, 0, nWidth, nHeight, SRCAND);
			StretchBlt(hdc,x, y,cx,cy, dcImage, 0, 0, nWidth, nHeight, SRCINVERT);

			// Restore settings
			SelectObject(dcImage,pOldBitmapImage);
			SelectObject(dcTrans,pOldBitmapTrans);
			SetBkColor(hdc,crOldBack);
			DeleteObject( bitmapTrans );  // RG 29/01/2002
			DeleteDC(dcImage);
			DeleteDC(dcTrans);
			DeleteObject(bm);
		}
	}
	return 1;
}
////////////////////////////////////////////////////////////////////////////////
long CxImage::Stretch(HDC hdc, const RECT& rect, DWORD dwRop)
{
	return Stretch(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, dwRop);
}
////////////////////////////////////////////////////////////////////////////////
/**
 * Stretch the image. Obsolete: use Draw() or Draw2()
 * \param hdc : destination device context
 * \param xoffset,yoffset : (optional) offset
 * \param xsize,ysize : size.
 * \param dwRop : raster operation code (see BitBlt documentation)
 * \return true if everything is ok
 */
long CxImage::Stretch(HDC hdc, long xoffset, long yoffset, long xsize, long ysize, DWORD dwRop)
{
	if((pDib)&&(hdc)) {
		//palette must be correctly filled
		SetStretchBltMode(hdc,COLORONCOLOR);	
		StretchDIBits(hdc, xoffset, yoffset,
					xsize, ysize, 0, 0, head.biWidth, head.biHeight,
					info.pImage,(BITMAPINFO*)pDib,DIB_RGB_COLORS,dwRop);
		return 1;
	}
	return 0;
}
////////////////////////////////////////////////////////////////////////////////
/**
 * Tiles the device context in the specified rectangle with the image.
 * \param hdc : destination device context
 * \param rc : tiled rectangle in the output device context
 * \return true if everything is ok
 */
long CxImage::Tile(HDC hdc, RECT *rc)
{
	if((pDib)&&(hdc)&&(rc)) {
		int w = rc->right - rc->left;
		int h = rc->bottom - rc->top;
		int x,y,z;
		int bx=head.biWidth;
		int by=head.biHeight;
		for (y = 0 ; y < h ; y += by){
			if ((y+by)>h) by=h-y;
			z=bx;
			for (x = 0 ; x < w ; x += z){
				if ((x+z)>w) z=w-x;
				RECT r = {rc->left + x,rc->top + y,rc->left + x + z,rc->top + y + by};
				Draw(hdc,rc->left + x, rc->top + y,-1,-1,&r);
			}
		}
		return 1;
	}
	return 0;
}
////////////////////////////////////////////////////////////////////////////////
// For UNICODE support: char -> TCHAR
long CxImage::DrawString(HDC hdc, long x, long y, const TCHAR* text, RGBQUAD color, const TCHAR* font, long lSize, long lWeight, BYTE bItalic, BYTE bUnderline, bool bSetAlpha)
//long CxImage::DrawString(HDC hdc, long x, long y, const char* text, RGBQUAD color, const char* font, long lSize, long lWeight, BYTE bItalic, BYTE bUnderline, bool bSetAlpha)
{
	if (IsValid()){
		//get the background
		HDC TmpDC=CreateCompatibleDC(hdc);
		//choose the font
		HFONT m_Font;
		LOGFONT* m_pLF;
		m_pLF=(LOGFONT*)calloc(1,sizeof(LOGFONT));
		_tcsncpy(m_pLF->lfFaceName,font,31);	// For UNICODE support
		//strncpy(m_pLF->lfFaceName,font,31);
		m_pLF->lfHeight=lSize;
		m_pLF->lfWeight=lWeight;
		m_pLF->lfItalic=bItalic;
		m_pLF->lfUnderline=bUnderline;
		m_Font=CreateFontIndirect(m_pLF);
		//select the font in the dc
		HFONT pOldFont=NULL;
		if (m_Font)
			pOldFont = (HFONT)SelectObject(TmpDC,m_Font);
		else
			pOldFont = (HFONT)SelectObject(TmpDC,GetStockObject(DEFAULT_GUI_FONT));

		//Set text color
		SetTextColor(TmpDC,RGB(255,255,255));
		SetBkColor(TmpDC,RGB(0,0,0));
		//draw the text
		SetBkMode(TmpDC,OPAQUE);
		//Set text position;
		RECT pos = {0,0,0,0};
		//long len = (long)strlen(text);
		long len = (long)_tcslen(text);	// For UNICODE support
		::DrawText(TmpDC,text,len,&pos,DT_CALCRECT);
		pos.right+=pos.bottom; //for italics

		//Preparing Bitmap Info
		long width=pos.right;
		long height=pos.bottom;
		BITMAPINFO bmInfo;
		memset(&bmInfo.bmiHeader,0,sizeof(BITMAPINFOHEADER));
		bmInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
		bmInfo.bmiHeader.biWidth=width;
		bmInfo.bmiHeader.biHeight=height;
		bmInfo.bmiHeader.biPlanes=1;
		bmInfo.bmiHeader.biBitCount=24;
		BYTE *pbase; //points to the final dib

		HBITMAP TmpBmp=CreateDIBSection(TmpDC,&bmInfo,DIB_RGB_COLORS,(void**)&pbase,0,0);
		HGDIOBJ TmpObj=SelectObject(TmpDC,TmpBmp);
		memset(pbase,0,height*((((24 * width) + 31) / 32) * 4));

		::DrawText(TmpDC,text,len,&pos,0);

		CxImage itext;
		itext.CreateFromHBITMAP(TmpBmp);

		y=head.biHeight-y-1;
		for (long ix=0;ix<width;ix++){
			for (long iy=0;iy<height;iy++){
				if (itext.GetPixelColor(ix,iy).rgbBlue) SetPixelColor(x+ix,y+iy,color,bSetAlpha);
			}
		}

		//cleanup
		if (pOldFont) SelectObject(TmpDC,pOldFont);
		DeleteObject(m_Font);
		free(m_pLF);
		DeleteObject(SelectObject(TmpDC,TmpObj));
		DeleteDC(TmpDC);
	}

	return 1;
}
////////////////////////////////////////////////////////////////////////////////
// <VATI>
long CxImage::DrawStringEx(HDC hdc, long x, long y, CXTEXTINFO *pTextType, bool bSetAlpha )
{
	if (!IsValid())
        return -1;
    
	//get the background
	HDC pDC;
	if (hdc) pDC=hdc; else pDC = ::GetDC(0);
	HDC TmpDC=CreateCompatibleDC(pDC);
   	
    //choose the font
	HFONT m_Font;
    m_Font=CreateFontIndirect( &pTextType->lfont );
    
    // get colors in RGBQUAD
    RGBQUAD p_forecolor = RGBtoRGBQUAD(pTextType->fcolor);
    RGBQUAD p_backcolor = RGBtoRGBQUAD(pTextType->bcolor);

    // check alignment and re-set default if necessary
    if ( pTextType->align != DT_CENTER &&
         pTextType->align != DT_LEFT &&
         pTextType->align != DT_RIGHT )
        pTextType->align = DT_CENTER;

    // check rounding radius and re-set default if necessary
    if ( pTextType->b_round > 50 || pTextType->b_round < 0 )
        pTextType->b_round = 10;

    // check opacity and re-set default if necessary
    if ( pTextType->b_opacity > 1. || pTextType->b_opacity < .0 )
        pTextType->b_opacity = 0.;

    //select the font in the dc
	HFONT pOldFont=NULL;
	if (m_Font)
		pOldFont = (HFONT)SelectObject(TmpDC,m_Font);
	else
		pOldFont = (HFONT)SelectObject(TmpDC,GetStockObject(DEFAULT_GUI_FONT));

	//Set text color
    SetTextColor(TmpDC,RGB(255,255,255));
	SetBkColor(TmpDC,RGB(0,0,0));
	SetBkMode(TmpDC,OPAQUE);
	//Set text position;
	RECT pos = {0,0,0,0};
	
    // get text length and number of lines
    long i=0, numlines=1, len=(long)_tcsclen(pTextType->text);
    while (i<len)
    {
        if ( pTextType->text[i++]==13 )
            numlines++;
    }

	::DrawText(TmpDC, pTextType->text, len, &pos, /*DT_EDITCONTROL|DT_EXTERNALLEADING|*/DT_NOPREFIX | DT_CALCRECT );

    // increase only if it's really italics, and only one line height
	if ( pTextType->lfont.lfItalic ) 
        pos.right += pos.bottom/2/numlines; 

    // background frame and rounding radius
	int frame = 0, roundR = 0;
    if ( pTextType->opaque )
    {
        roundR= (int)(pos.bottom/numlines * pTextType->b_round / 100 ) ;
        frame = (int)(/*3.5 + */0.29289*roundR ) ;
        pos.right += pos.bottom/numlines/3 ; // JUST FOR BEAUTY
    }

	//Preparing Bitmap Info
	long width=pos.right +frame*2;
	long height=pos.bottom +frame*2;
	BITMAPINFO bmInfo;
	memset(&bmInfo.bmiHeader,0,sizeof(BITMAPINFOHEADER));
	bmInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
	bmInfo.bmiHeader.biWidth=width;
	bmInfo.bmiHeader.biHeight=height;
	bmInfo.bmiHeader.biPlanes=1;
	bmInfo.bmiHeader.biBitCount=24;
	BYTE *pbase; //points to the final dib

	HBITMAP TmpBmp=CreateDIBSection(TmpDC,&bmInfo,DIB_RGB_COLORS,(void**)&pbase,0,0);
	HGDIOBJ TmpObj=SelectObject(TmpDC,TmpBmp);
	memset(pbase,0,height*((((24 * width) + 31) / 32) * 4));

	::DrawText(TmpDC,pTextType->text,len, &pos, /*DT_EDITCONTROL|DT_EXTERNALLEADING|*/DT_NOPREFIX| pTextType->align );
    
	CxImage itext;
	itext.CreateFromHBITMAP(TmpBmp);
    y=head.biHeight-y-1;

    //move the insertion point according to alignment type
    // DT_CENTER: cursor points to the center of text rectangle
    // DT_RIGHT:  cursor points to right side end of text rectangle
    // DT_LEFT:   cursor points to left end of text rectangle
    if ( pTextType->align == DT_CENTER )
        x -= width/2;
    else if ( pTextType->align == DT_RIGHT )
        x -= width;
    if (x<0) x=0;
    
    //draw the background first, if it exists
    long ix,iy;
    if ( pTextType->opaque )
    {
        int ixf=0; 
        for (ix=0;ix<width;ix++)
        {
            if ( ix<=roundR )
                ixf = (int)(.5+roundR-sqrt((float)(roundR*roundR-(ix-roundR)*(ix-roundR))));
            else if ( ix>=width-roundR-1 )
                ixf = (int)(.5+roundR-sqrt((float)(roundR*roundR-(width-1-ix-roundR)*(width-1-ix-roundR))));
            else
                ixf=0;

            for (iy=0;iy<height;iy++)
            {
                if ( (ix<=roundR && ( iy > height-ixf-1 || iy < ixf )) ||
                     (ix>=width-roundR-1 && ( iy > height-ixf-1 || iy < ixf )) )
                    continue;
                else
                    if ( pTextType->b_opacity > 0.0 && pTextType->b_opacity < 1.0 )
                    {
                        RGBQUAD bcolor, pcolor;
                        // calculate a transition color from original image to background color:
                        pcolor = GetPixelColor(x+ix,y+iy);
						bcolor.rgbBlue = (unsigned char)(pTextType->b_opacity * pcolor.rgbBlue + (1.0-pTextType->b_opacity) * p_backcolor.rgbBlue );
                        bcolor.rgbRed = (unsigned char)(pTextType->b_opacity * pcolor.rgbRed + (1.0-pTextType->b_opacity) * p_backcolor.rgbRed ) ;
                        bcolor.rgbGreen = (unsigned char)(pTextType->b_opacity * pcolor.rgbGreen + (1.0-pTextType->b_opacity) * p_backcolor.rgbGreen ) ;
                        bcolor.rgbReserved = 0;
                        SetPixelColor(x+ix,y+iy,bcolor,bSetAlpha);
                    }
                    else
                        SetPixelColor(x+ix,y+iy,p_backcolor,bSetAlpha);
			}
		}
    }
	
    // draw the text itself
    for (ix=0;ix<width;ix++)
    {
		for (iy=0;iy<height;iy++)
        {
            if (itext.GetPixelColor(ix,iy).rgbBlue )
                SetPixelColor(x+ix+frame,y+iy-frame,p_forecolor,bSetAlpha);
		}
	}

	//cleanup
    if (pOldFont) SelectObject(TmpDC,pOldFont);
	DeleteObject(m_Font);
	DeleteObject(SelectObject(TmpDC,TmpObj));
	DeleteDC(TmpDC);
	return 1;
}

//////////////////////////////////////////////////////////////////////////////
void CxImage::InitTextInfo( CXTEXTINFO *txt )
{

    memset( txt, 0, sizeof(CXTEXTINFO));
    
    // LOGFONT defaults
    txt->lfont.lfHeight        = -36; 
    txt->lfont.lfCharSet       = EASTEUROPE_CHARSET; // just for Central-European users 
    txt->lfont.lfWeight        = FW_NORMAL;
    txt->lfont.lfWidth         = 0; 
    txt->lfont.lfEscapement    = 0; 
    txt->lfont.lfOrientation   = 0; 
    txt->lfont.lfItalic        = FALSE; 
    txt->lfont.lfUnderline     = FALSE; 
    txt->lfont.lfStrikeOut     = FALSE; 
    txt->lfont.lfOutPrecision  = OUT_DEFAULT_PRECIS; 
    txt->lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; 
    txt->lfont.lfQuality       = PROOF_QUALITY; 
    txt->lfont.lfPitchAndFamily= DEFAULT_PITCH | FF_DONTCARE ; 
    _stprintf( txt->lfont.lfFaceName, _T("Arial")); //use TCHAR mappings <Cesar M>

    // initial colors
    txt->fcolor = RGB( 255,255,160 );  // default foreground: light goldyellow
    txt->bcolor = RGB( 32, 96, 0 );    // default background: deep green

    // background
    txt->opaque    = TRUE;  // text has a non-transparent background;
    txt->b_opacity = 0.0;   // default: opaque background
    txt->b_outline = 0;     // default: no outline (OUTLINE NOT IMPLEMENTED AT THIS TIME)
    txt->b_round   = 20;    // default: rounding radius is 20% of the rectangle height
    // the text 
    _stprintf( txt->text, _T("Sample Text 01234��")); // text use TCHAR mappings <Cesar M>
    txt->align = DT_CENTER;
    return;
}
////////////////////////////////////////////////////////////////////////////////
#endif //CXIMAGE_SUPPORT_WINDOWS
////////////////////////////////////////////////////////////////////////////////

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
CEO Solaris Electronics LLC
United Arab Emirates United Arab Emirates
I was born in Shiraz, a very beautiful famous city in Iran. I started programming when I was 12 years old with GWBASIC. Since now, I worked with various programming languages from Basic, Foxpro, C/C++, Visual Basic, Pascal to MATLAB and now Visual C++.
I graduated from Iran University of Science & Technology in Communication Eng., and now work as a system programmer for a telecommunication industry.
I wrote several programs and drivers for Synthesizers, Power Amplifiers, GPIB, GPS devices, Radio cards, Data Acquisition cards and so many related devices.
I'm author of several books like Learning C (primary and advanced), Learning Visual Basic, API application for VB, Teach Yourself Object Oriented Programming (OOP) and etc.
I'm winner of January, May, August 2003 and April 2005 best article of month competition, my articles are:


You can see list of my articles, by clicking here


Comments and Discussions