Introduction
This article describes a simple
calendar that sits on
your desktop. I have created this application to just
study about alpha blending, desktop wallpapers, and how
a device context can be saved as a bitmap. I am planning
to make a more refined version including a time
scheduler and reminder also.
Using the code
Alpha Blending
Collapse
HBITMAP Create24BPPDIBSection(HDC hDC, int iWidth, int iHeight)
{
BITMAPINFO bmi;
HBITMAP hbm;
LPBYTE pBits;
// Initialize header to 0s.
ZeroMemory(&bmi, sizeof(bmi));
// Fill out the fields you care about.
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = iWidth;
bmi.bmiHeader.biHeight = iHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 24;
bmi.bmiHeader.biCompression = BI_RGB;
// Create the surface.
hbm = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS,(void **)&pBits, NULL, 0);
return(hbm);
}
BOOL BitmapsCompatible(LPBITMAP lpbm1, LPBITMAP lpbm2)
{
if (lpbm1->bmBitsPixel != lpbm2->bmBitsPixel)
return FALSE;
if (lpbm1->bmPlanes != lpbm2->bmPlanes)
return FALSE;
if (lpbm1->bmWidth != lpbm2->bmWidth)
return FALSE;
if (lpbm1->bmHeight != lpbm2->bmHeight)
return FALSE;
return TRUE;
}
BOOL BlendImages(HBITMAP hbmSrc1, HBITMAP hbmSrc2, HBITMAP hbmDst, DWORD dwWeight1)
{
BITMAP bmSrc1, bmSrc2, bmDst;
RGBTRIPLE *lprgbSrc1, *lprgbSrc2, *lprgbDst;
DWORD dwWidthBytes, dwWeight2;
int x, y;
// Only values between 0 and 255 are valid.
if (dwWeight1 > 255) return FALSE;
// Get weighting value for second source image.
dwWeight2 = 255-dwWeight1;
// Get information about the surfaces you were passed.
if (!GetObject(hbmSrc1, sizeof(BITMAP), &bmSrc1)) return FALSE;
if (!GetObject(hbmSrc2, sizeof(BITMAP), &bmSrc2)) return FALSE;
if (!GetObject(hbmDst, sizeof(BITMAP), &bmDst)) return FALSE;
// Make sure you have data that meets your requirements.
if (!BitmapsCompatible(&bmSrc1, &bmSrc2))
return FALSE;
if (!BitmapsCompatible(&bmSrc1, &bmDst))
return FALSE;
if (bmSrc1.bmBitsPixel != 24)
return FALSE;
if (bmSrc1.bmPlanes != 1)
return FALSE;
if (!bmSrc1.bmBits || !bmSrc2.bmBits || !bmDst.bmBits)
return FALSE;
dwWidthBytes = bmDst.bmWidthBytes;
// Initialize the surface pointers.
lprgbSrc1 = (RGBTRIPLE *)bmSrc1.bmBits;
lprgbSrc2 = (RGBTRIPLE *)bmSrc2.bmBits;
lprgbDst = (RGBTRIPLE *)bmDst.bmBits;
for (y=0; y<bmDst.bmHeight; y++) {
for (x=0; x<bmDst.bmWidth; x++) {
lprgbDst[x].rgbtRed =
(BYTE)((((DWORD)lprgbSrc1[x].rgbtRed * dwWeight1) +
((DWORD)lprgbSrc2[x].rgbtRed * dwWeight2)) >> 8);
lprgbDst[x].rgbtGreen =
(BYTE)((((DWORD)lprgbSrc1[x].rgbtGreen * dwWeight1) +
((DWORD)lprgbSrc2[x].rgbtGreen * dwWeight2)) >> 8);
lprgbDst[x].rgbtBlue =
(BYTE)((((DWORD)lprgbSrc1[x].rgbtBlue * dwWeight1) +
((DWORD)lprgbSrc2[x].rgbtBlue * dwWeight2)) >> 8);
}
// Move to next scan line.
lprgbSrc1 = (RGBTRIPLE *)((LPBYTE)lprgbSrc1 + dwWidthBytes);
lprgbSrc2 = (RGBTRIPLE *)((LPBYTE)lprgbSrc2 + dwWidthBytes);
lprgbDst = (RGBTRIPLE *)((LPBYTE)lprgbDst + dwWidthBytes);
}
return TRUE;
}
BOOL DoAlphaBlend(
HDC hdcDest, // Handle to destination DC.
int nXOriginDest, // X-coord of upper-left corner.
int nYOriginDest, // Y-coord of upper-left corner.
int nWidthDest, // Destination width.
int nHeightDest, // Destination height.
HDC hdcSrc, // Handle to source DC.
int nXOriginSrc, // X-coord of upper-left corner.
int nYOriginSrc, // Y-coord of upper-left corner.
int nWidthSrc, // Source width.
int nHeightSrc, // Source height.
DWORD dwSourceWeight) // Source weighting (between 0 and 255).
{
HDC hdcSrc1 = NULL;
HDC hdcSrc2 = NULL;
HDC hdcDst = NULL;
HBITMAP hbmSrc1 = NULL;
HBITMAP hbmSrc2 = NULL;
HBITMAP hbmDst = NULL;
BOOL bReturn;
// Create surfaces for sources and destination images.
hbmSrc1 = Create24BPPDIBSection(hdcDest, nWidthDest,nHeightDest);
if (!hbmSrc1) goto HANDLEERROR;
hbmSrc2 = Create24BPPDIBSection(hdcDest, nWidthDest,nHeightDest);
if (!hbmSrc2) goto HANDLEERROR;
hbmDst = Create24BPPDIBSection(hdcDest, nWidthDest,nHeightDest);
if (!hbmDst) goto HANDLEERROR;
// Create HDCs to hold our surfaces.
hdcSrc1 = CreateCompatibleDC(hdcDest);
if (!hdcSrc1) goto HANDLEERROR;
hdcSrc2 = CreateCompatibleDC(hdcDest);
if (!hdcSrc2) goto HANDLEERROR;
hdcDst = CreateCompatibleDC(hdcDest);
if (!hdcDst) goto HANDLEERROR;
// Prepare the surfaces for drawing.
SelectObject(hdcSrc1, hbmSrc1);
SelectObject(hdcSrc2, hbmSrc2);
SelectObject(hdcDst, hbmDst);
SetStretchBltMode(hdcSrc1, COLORONCOLOR);
SetStretchBltMode(hdcSrc2, COLORONCOLOR);
// Capture a copy of the source area.
if (!StretchBlt(hdcSrc1, 0,0,nWidthDest,nHeightDest,
hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
SRCCOPY))
goto HANDLEERROR;
// Capture a copy of the destination area.
if (!StretchBlt(hdcSrc2, 0,0,nWidthDest,nHeightDest,
hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
SRCCOPY))
goto HANDLEERROR;
// Blend the two source areas to create the destination image.
bReturn = BlendImages(hbmSrc1, hbmSrc2, hbmDst, dwSourceWeight);
// Clean up objects you do not need any longer.
// You cannot delete an object that's selected into an
// HDC so delete the HDC first.
DeleteDC(hdcSrc1);
DeleteDC(hdcSrc2);
DeleteObject(hbmSrc1);
DeleteObject(hbmSrc2);
// Display the blended (destination) image to the target HDC.
if (bReturn) {
BitBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
hdcDst, 0,0, SRCCOPY);
}
// Clean up the rest of the objects you created.
DeleteDC(hdcDst);
DeleteObject(hbmDst);
return bReturn;
HANDLEERROR:
if (hdcSrc1) DeleteDC(hdcSrc1);
if (hdcSrc2) DeleteDC(hdcSrc2);
if (hdcDst) DeleteDC(hdcDst);
if (hbmSrc1) DeleteObject(hbmSrc1);
if (hbmSrc2) DeleteObject(hbmSrc2);
if (hbmDst) DeleteObject(hbmDst);
return FALSE;
}
Save Device Context into an uncompressed bitmap
Collapse
// Function to save bitmap
BOOL SaveBitmap(HDC hDC,HBITMAP hBitmap,CString szPath)
{
FILE * fp= NULL;
fp = fopen(szPath.GetBuffer(1),"wb");
if(fp == NULL)
return false;
BITMAP Bm;
BITMAPINFO BitInfo;
ZeroMemory(&BitInfo, sizeof(BITMAPINFO));
BitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitInfo.bmiHeader.biBitCount = 0;
if(!::GetDIBits(hDC, hBitmap, 0, 0, NULL, &BitInfo, DIB_RGB_COLORS))
return (false);
Bm.bmHeight = BitInfo.bmiHeader.biHeight;
Bm.bmWidth = BitInfo.bmiHeader.biWidth;
BITMAPFILEHEADER BmHdr;
BmHdr.bfType = 0x4d42; // 'BM' WINDOWS_BITMAP_SIGNATURE
BmHdr.bfSize = (((3 * Bm.bmWidth + 3) & ~3) * Bm.bmHeight)
+ sizeof(BITMAPFILEHEADER)
+ sizeof(BITMAPINFOHEADER);
BmHdr.bfReserved1 = BmHdr.bfReserved2 = 0;
BmHdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER)
+ sizeof(BITMAPINFOHEADER);
BitInfo.bmiHeader.biCompression = 0;
// Writing Bitmap File Header ////
fwrite(&BmHdr,sizeof(BITMAPFILEHEADER),1,fp);
fwrite(&BitInfo.bmiHeader,sizeof(BITMAPINFOHEADER),1,fp);
BYTE *pData = new BYTE[BitInfo.bmiHeader.biSizeImage + 5];
if(!::GetDIBits(hDC, hBitmap, 0, Bm.bmHeight,
pData, &BitInfo, DIB_RGB_COLORS))
return (false);
if(pData != NULL)
fwrite(pData,1,BitInfo.bmiHeader.biSizeImage,fp);
fclose(fp);
delete (pData);
return (true);
}
// here is how to use the above function
//Get the device context
HDC hDC = ::GetDC(hWnd);
// Create a memory device context
HDC hMemDC = ::CreateCompatibleDC(hDC);
// Create a compatible bitmap
HBITMAP hBitmap = CreateCompatibleBitmap(hDC,
nScreenWidth,nScreenHeight);
// Select the bitmap into the memory context
::SelectObject(hMemDC,hBitmap);
// Do what ever drawing you want into the Device Context
...
...
SaveBitmap(hMemDC, hBitmap,"Sample.bmp");
Points of Interest
SetStretchBltMode(hDC,HALFTONE)
Try out this function, this improves the quality of StretchBlt()
.
I think with our using this mode, SretchBlt()
function cannot give you the required quality if you are
dealing with big bitmaps.