Introduction
Every once in a while, you need the capability to do something in code that requires a an amount of research that is usually disproportionate to the final number of lines required to implement the code. Manipulating bitmaps is one of those things. For most of us, doing GDI stuff is one of the most distasteful (and mysterious) coding tasks we can imagine.
In the course our current development efforts, I decided that I wanted to dynamically change the contents of an CImageList
. By "dynamically", I mean building the bitmaps and then replacing a given image in an existing image list. I know - insaity, pure insanity. Well, no great idea goes unpunished, and mine was no exception. After figuring out how to do it, circumstances prevented me from actually using the code. However, that doesn't mean you (the audience) can't benefit from my experience.
The core idea here was to take an existing bitmap, and replace part of that bitmap with another (smaller?) bitmap, and this is what this article describes. What you do with the resulting bitmap is limited only by your imagination. The following is a description of the function in the demo app that does all of the related work.
How It's Done - Partially Replacing an Existing Bitmap
First, we have to establish some parameters. Since the demo works with images whose sizes are static and already known, we have it pretty simple. The width and height of our small bitmaps are 20x20. The "target" bitmap is 60x20.
int nWidth = 20;
int nHeight = 20;
int nTargetWidth = 60;
Next, we establish where we want to copy the selected source bitmap on the target bitmap.
int nCoordX = m_nSection * 20;
int nCoordY = 0;
Here, we call a dialog box function to retrieve the appropriate (and already loaded) bitmap.
CBitmap* pBmpSource = GetSourceBitmap(nImgNumb);
Then, we want to define and initialize some necessary objects. Since we'll be working with device contexts, we need a CBitmap
pointer for both the source and target bitmaps, as well as device context objects.
CBitmap* pOldTargetBmp = NULL;
CBitmap* pOldSourceBmp = NULL;
CDC targetDC;
CDC sourceDC;
CDC* pDC = this->GetDC();
Now comes the interesting part. We need to create a device context on which we can render the bitmaps.
targetDC.CreateCompatibleDC(pDC);
sourceDC.CreateCompatibleDC(pDC);
Then, we select the target bitmap into the target DC, and the source bitmap into the source DC.
pOldTargetBmp = targetDC.SelectObject(&m_BmpTarget);
pOldSourceBmp = sourceDC.SelectObject(pBmpSource);
This is where the magic occurs. We us BitBlt
to copy the contents of the source DC into the target DC at the specified coordinates. Remember, we set values for the first four parameters at the top of the function.
targetDC.BitBlt(nCoordX, nCoordY, nTargetWidth, nHeight, &sourceDC,
0, 0, SRCCOPY);
Now that we've done everything we need to do with the device contexts, we can free up the resources and clean up.
sourceDC.SelectObject(pOldSourceBmp);
targetDC.SelectObject(pOldTargetBmp);
sourceDC.DeleteDC();
targetDC.DeleteDC();
ReleaseDC(pDC);
The last step is to set the bitmap control on the dialog to use our new bitmap.
m_ctrlTargetImage.SetBitmap(HBITMAP(m_BmpTarget));
And there you have it - copying a bitmap to another bitmap in leas than 20 lines of code (not counting the code that got us here, of course).
If you have questions, I'll do my best to answer them, but I'm not the best person to ask about the esoteric bitmap mangling theory. You might be better off asking questions in the MFC/VC++ forum here on CodeProject.
Legacy Compilers
The demo project was created with VS2005, but you should be able to copy/paste the desired code directly into any project using VC5 through VS2003 without any problems. If you do have problems, you should be perfectly capable of figuring out how to fix them. Afterall, you're a programmer, right?
Credits
Many thanks to PJ Arends and Christian Graus for their assistance in getting the code above to work.
I used PJ's bitmap debugger utility to make sure everything was working as it should.
Another honorable mention goes to Chris Richardson for pointing out an omission where releasing a DC is concerned.
Updates
- 07/31/2006 - Corrected some spelling errors, and updated the code and article body to reflect a missing call to
ReleaseDC
.
- 07/28/2006 - Original Article