Transfer an image from CDC to CBitmap - c++

How can I transfer an image from CDC to CBitmap?
The problem in whole:
I have a big image into CBitmap A. I need to transfer parts of this image to a number of CBitmap for the storage into a vector, because I can't use a number of CDC for this :)
I make a prepared CDC into a cycle (get a neccessary part of CBitmap A) and then I need to transfer it to CBitmap x.
How can I do it?
Here's my code:
m_bitmaps.clear();
m_bitmaps.reserve(4);
CDC SourceDC, destDC;
SourceDC.CreateCompatibleDC(pMainDC);
destDC.CreateCompatibleDC(pMainDC);
CBitmap bmpWhole; // bmp 200*200
bmpWhole.LoadBitmap(IDB_TEST_BITMAP);
SourceDC.SelectObject(&bmpWhole);
//pMainDC->BitBlt(0,0,200,200,&SourceDC,0,0,SRCCOPY);
// It's OK - I got a source picture
for (int x=100; x>=0; x-=100)
for (int y=100; y>=0; y-=100)
{
CBitmap *destBitmap = new CBitmap();
destBitmap->CreateCompatibleBitmap(&destDC, 100, 100);
CBitmap *oldBitmap = destDC.SelectObject(destBitmap);
destDC.BitBlt(0,0,100,100,&SourceDC,x,y,SRCCOPY);
pMainDC->BitBlt(x*1.1,y*1.1,100,100,&destDC,0,0,SRCCOPY);
// I got black squares here! - something wrong with previous code
m_bitmaps.push_back(destBitmap);
destDC.SelectObject(oldBitmap);
}
bmpWhole.DeleteObject();
destDC.DeleteDC();
SourceDC.DeleteDC();
// And later CBitmaps are black squares
I found the solution!
Parsing the CBitmap and initializing the vector
m_bitmaps.clear();
m_bitmaps.reserve(4);
CDC SourceDC, destDC;
SourceDC.CreateCompatibleDC(pMainDC);
CBitmap bmpWhole;
bmpWhole.LoadBitmap(IDB_TEST_BITMAP);
SourceDC.SelectObject(&bmpWhole);
for (int x=100; x>=0; x-=100)
for (int y=100; y>=0; y-=100)
{
CImage *destImage = new CImage();
destImage->Create(100, 100, 24);
destDC.Attach(destImage->GetDC());
destDC.BitBlt(0,0,100,100,&SourceDC,x,y,SRCCOPY);
destDC.Detach();
destImage->ReleaseDC();
m_bitmaps.push_back(destImage);
}
bmpWhole.DeleteObject();
destDC.DeleteDC();
SourceDC.DeleteDC();
Draw:
WORD nShift=0;
for (auto iter = m_bitmaps.begin(); iter != m_bitmaps.end(); ++iter, nShift+=110)
{
CBitmap* pBitmap = CBitmap::FromHandle((*iter)->operator HBITMAP());
CDC memDC;
memDC.CreateCompatibleDC(pMainDC);
memDC.SelectObject(pBitmap);
pMainDC->BitBlt(nShift, 0, 100, 100, &memDC, 0, 0, SRCCOPY);
}

Create another device context and one by one create and select your target bitmaps into it and use BitBlt to copy portions of the source bitmap into them. Below is an example of how you might do this.
// Create a DC compatible with the display
// this is used to copy FROM the source bitmap
sourceDC.CreateDC(NULL);
sourceDC.SelectObject(&sourceBitmap);
// Create another device context for the destination bitmap
destDC.CreateCompatibleDC(&sourceDC);
for(int i = 0; i < numBitmaps; i++)
{
// Determine the x, y, sourceX, sourceY, with and height here
// ...
// create a new bitmap
CBitmap *destBitmap = new CBitmap();
destBitmap->CreateCompatibleBitmap(&destDC, width, height);
// Select the bitmap into the destination device context
CBitmap *oldBitmap = destDC.SelectObject(destBitmap);
// copy the bitmap
destDC.BitBlt(x, y, width, height, &sourceDC, sourceX, sourceY, SRCCOPY);
// add it to the vector
bitmapVector.push_back(destBitmap);
// Clean up
destDC.SelectObject(oldBitmap);
}
I omitted error checking for simplicity. If you are using C++11 or Boost I recommend using unique_ptr to manage the lifetime of the bitmap object. Otherwise you need to delete it at the appropriate place such as the destructor.

Related

How to use CreateCompatibleDC(), SetPixel(), and BitBlt() to display an image?

I'm trying to draw and display an image(s) on a device context (variable: dc) by using CreateCompatibleDC(), SetPixel(), and BitBlt() as seen in the code below:
HDC Layout = CreateCompatibleDC(0);
HBITMAP image = CreateCompatibleBitmap(Layout, symbol->bitmap_width, symbol->bitmap_height);
// Draw the image
int bit = 0;
for (int j = 0; j < symbol->bitmap_height; j++) {
for (int k = 0; k < symbol->bitmap_width; k++) {
if (symbol->bitmap[bit] == '1')
SetPixel(Layout, j, k, rgbBlue);
else
SetPixel(Layout, j, k, rgbGreen);
bit++;
}
}
BOOL success = BitBlt(dc, 1000, 1000, 1000, 1000, BCLayout, 0, 0, SRCCOPY);
I expected the image to be displayed in said device context but the image does not display in the end. Does anyone know why that is?
A few things I should clarify:
the variable "symbol" is a struct variable that holds all the information for the image
the symbol->bitmap array is a character array that has characters that denote the color of a pixel on the bitmap representation of the image (why it's one-dimensional, I don't know. It was designed that way by a third party)
CreateCompatibleDC() creates an in-memory HDC with a 1x1 monochrome HBITMAP assigned to it by default. You need to use SelectObject() to replace that default HBITMAP with your own HBITMAP before you then use SetPixel() to change the HDC's pixels, eg:
// create an HDC...
HDC Layout = CreateCompatibleDC(0);
// create a bitmap for the HDC...
HBITMAP image = CreateCompatibleBitmap(Layout, symbol->bitmap_width, symbol->bitmap_height);
// replace the default bitmap with the new one...
// remember the old bitmap for later...
HBITMAP oldBmp = (HBITMAP) SelectObject(Layout, image);
// Draw the image as needed...
// restore the previous bitmap...
SelectObject(Layout, oldBmp);
// destroy the new bitmap...
DeleteObject(image);
// destroy the HDC...
DeleteDC(Layout);

Anti aliasing in MFC

I'm trying to implement anti-aliasing in my MFC app, I'm using the technique described in this tutorial.
Create a bitmap (2x, 4x, 8x) the size of the original bitmap.
Draw on the resized bitmap (I'm only using simple figures (lines, circles and etc)).
Set StretchBlt Mode to HalfTone.
And Resize with StretchBlt to the original size.
Using this way, drawing in the resized bitmap it works, but I want to create a more generic function that receives a bitmap with the drawing already made and return with the anti-aliasing, I tried this:
static HBITMAP AntiAliasing(HBITMAP hBitmap)
{
int escala = 4;
HBITMAP bmp = __copia(hBitmap); // Copy the bitmap.
HDC hMemDC = CreateCompatibleDC(NULL);
HBITMAP bmpAntigo1 = (HBITMAP)::SelectObject(hMemDC, bmp);
BITMAP bitmap;
::GetObject(hBitmap, sizeof(BITMAP), &bitmap);
// Create a bitmap (2x, 4x, 8x) the size of the original bitmap.
HDC hDCDimensionado = ::CreateCompatibleDC(hMemDC);
HBITMAP bmpDimensionado = ::CreateCompatibleBitmap(hDCDimensionado,
bitmap.bmWidth * escala,
bitmap.bmHeight * escala);
HBITMAP hBmpVelho = (HBITMAP)::SelectObject(hDCDimensionado, bmpDimensionado);
// I also tried with {BLACKONWHITE, HALFTONE, WHITEONBLACK}
int oldStretchBltMode2 = ::SetStretchBltMode(hDCDimensionado, COLORONCOLOR);
// Resize the bitmap to the new size.
::StretchBlt(hDCDimensionado,
0, 0, bitmap.bmWidth * escala, bitmap.bmHeight * escala,
hMemDC,
0, 0, bitmap.bmWidth, bitmap.bmHeight,
SRCCOPY);
/*
* Here the bitmap has lost his colors and became black and white.
*/
::SetStretchBltMode(hDCDimensionado, oldStretchBltMode2);
// Set StretchBltMode to halfTone so can mimic the anti aliasing effect.
int oldStretchBltMode = ::SetStretchBltMode(hMemDC, HALFTONE);
// resize to the original size.
::StretchBlt(hMemDC,
0, 0, bitmap.bmWidth, bitmap.bmHeight,
hDCDimensionado,
0, 0, escala * bitmap.bmWidth, escala * bitmap.bmHeight,
SRCCOPY);
::SetStretchBltMode(hMemDC, oldStretchBltMode);
::SelectObject(hMemDC, bmpAntigo1);
::DeleteDC(hMemDC);
::SelectObject(hDCDimensionado, hBmpVelho);
DeleteDC(hDCDimensionado);
return bmp;
}
But this function doesn't work, the result loses its colors (all drawings became black) and there isn't anti aliasing.
Any help will be appreciated!
From documentation for CreateCompatibleBitmap:
Note: When a memory device context is created, it initially has a
1-by-1 monochrome bitmap selected into it. If this memory device
context is used in CreateCompatibleBitmap, the bitmap that is created
is a monochrome bitmap. To create a color bitmap, use the HDC that was
used to create the memory device context, as shown in the following
code:
Change the code and supply hdc for the desktop as show below:
HDC hdc = ::GetDC(0);
HBITMAP bmpDimensionado = ::CreateCompatibleBitmap(hdc, ...)
::ReleaseDC(0, hdc);
This will show the image, however this method will not produce the desired effect because it simply magnifies each pixel to larger size and reduces it back to the original pixel. There is no blending with neighboring pixels.
Use other methods such Direct2D with Gaussian blur effect, or use GDI+ instead with interpolation mode:
Gdiplus::GdiplusStartup...
void foo(HDC hdc)
{
Gdiplus::Bitmap bitmap(L"file.bmp");
if(bitmap.GetLastStatus() != 0)
return 0;
auto w = bitmap.GetWidth();
auto h = bitmap.GetHeight();
auto maxw = w * 2;
auto maxh = h * 2;
Gdiplus::Bitmap membmp(maxw, maxh);
Gdiplus::Graphics memgr(&membmp);
memgr.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBilinear);
memgr.DrawImage(&bitmap, 0, 0, maxw, maxh);
Gdiplus::Graphics gr(hdc);
gr.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBilinear);
gr.DrawImage(&membmp, 0, 0, w, h);
}
If target window is at least Vista, use GDI+ version 1.1 with blur effect. See also How to turn on GDI+ 1.1 in MFC project
#define GDIPVER 0x0110 //add this to precompiled header file
void blur(HDC hdc)
{
Gdiplus::Graphics graphics(hdc);
Gdiplus::Bitmap bitmap(L"file.bmp");
if(bitmap.GetLastStatus() != 0)
return;
Gdiplus::Blur blur;
Gdiplus::BlurParams blur_param;
blur_param.radius = 3; //change the radius for different result
blur_param.expandEdge = TRUE;
blur.SetParameters(&blur_param);
bitmap.ApplyEffect(&blur, NULL);
graphics.DrawImage(&bitmap, 0, 0);
}

Using TransparentBlt for drawing in MFC

I want do draw a vector of CRect into a device context. The CRects that overlap should add up in a way that the intersection of all turn to a brighter green. Therefore I came up with the following code:
void Grid::tag(CDC* pDC){
CBrush brushGreen;
brushGreen.CreateSolidBrush(RGB(0, 100, 0));
CDC dcMemory;
dcMemory.SelectObject(&brushGreen);
dcMemory.CreateCompatibleDC(pDC);
for (size_t i = 0; i < taglist.size(); i++){
dcMemory.FillRect(taglist[i], &brushGreen);
pDC->TransparentBlt(frame.left, frame.top, frame.Width(), frame.Height(), &dcMemory, taglist[i].left, taglist[i].top, taglist[i].Width(), taglist[i].Height(),RGB(0,100,0));
}
DeleteObject(brushGreen);
}
Unfortunately, it turns out black. It seems like nothing is drawn into pDC. What am I doing wrong? Is this a valid approach to begin with?
In your example you have to fill memory dc with a transparent color. This will initialize the background color, so to speak. Then draw on memory dc and use TransparentBlt with that transparent color.
void CMyWnd::OnPaint()
{
CWnd::OnPaint();
CClientDC dc(this);
CRect rc;
GetClientRect(&rc);
//paint any custom background
dc.FillSolidRect(&rc, RGB(200,200,255));
//choose a color which you don't otherwise need, use it for transparency
COLORREF transparent_color = RGB(1, 1, 1);
//create memory dc and initialize with transparent_color:
CDC memdc;
memdc.CreateCompatibleDC(&dc);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc, rc.right, rc.bottom);
memdc.SelectObject(bitmap);
memdc.FillSolidRect(&rc, transparent_color);
//start custom drawing on memeory dc:
CBrush brushGreen;
brushGreen.CreateSolidBrush(RGB(0, 100, 0));
CRect small_rc(10, 10, rc.right - 10, 20);
memdc.FillRect(small_rc, &brushGreen);
//end custom drawing
//Finish by copying memeory dc to destination dc:
dc.TransparentBlt(0, 0, rc.Width(), rc.Height(),
&memdc, 0, 0, rc.Width(), rc.Height(), transparent_color);
}
Instead of TransparentBlt - which does color keying during blt, you can use AlphaBlend, you have also other issues in your code. Below are some fixes, and ideas how to correct your code (I have not tested if this compiles).
CBrush brushGreen;
brushGreen.CreateSolidBrush(RGB(0, 100, 0));
CDC dcMemory;
//SO: what is the use of this? Also before creating DC
//dcMemory.SelectObject(&brushGreen);
dcMemory.CreateCompatibleDC(pDC);
//SO: for memory DC you need also a bitmap to be selected (dont forget to release it):
HBITMAP hbmp = CreateCompatibleBitmap((HDC)dc, 500, 500);
auto oldDcMemoryBmp = dcMemory.SelectObject(hbmp);
for (size_t i = 0; i < taglist.size(); i++){
dcMemory.FillRect(taglist[i], &brushGreen);
// SO: this is not needed
//pDC->TransparentBlt(frame.left, frame.top, frame.Width(), frame.Height(), &dcMemory, taglist[i].left, taglist[i].top, taglist[i].Width(), taglist[i].Height(),RGB(0,100,0));
// SO: Instead use albhaBlt
BLENDFUNCTION BlendFunction;
BlendFunction.AlphaFormat = AC_SRC_ALPHA;
BlendFunction.BlendFlags = 0;
BlendFunction.BlendOp = AC_SRC_OVER;
BlendFunction.SourceConstantAlpha = 15; // value 0 (transparent) to 255 (opaque)
dc.AlphaBlend(taglist[i].left, taglist[i].top, taglist[i].Width(), taglist[i].Height(), &dcMemory, 0, 0, taglist[i].Width(), taglist[i].Height(), BlendFunction);
}
//DeleteObject(brushGreen);

Creating, displaying, and then accessing bitmap/DIB data (w/o GetBitmapBits())

I have inherited an old-school MFC Windows CE program, and am having to make some modifications to it. As part of this I have to create a monochrome image with text on it, and both display it on a screen as well as send each row of the image to a printer one at a time.
I originally used a bitmap, and had success using DrawText() and getting a test string (Hello World) to display on the screen (this code is in Figure 1). However, I hit a wall at the stage where I am looking to extract the wrap data from the bitmap. What I am trying to get is an array with 1s or 0s representing black or white. I had first thought I would use GetBitmapBits() but unfortunately the code I am working with is so old that function is not supported yet. I thought I could get around this issue by using GetBitmap() and then accessing the bmBits parameter. However this appears to always be null which was confirmed when I found the following link: Why does GetObject return an BITMAP with null bmBits?.
My next attempt was to follow the advice in the link and use CreateDIBSection() instead of CreateCompatibleBitmap(). This seems like the right path, and I should have access to the data I want, but unfortunately I cannot get the DIB to display (code is in Figure 2). I suspect I am doing something wrong in creating the header of the DIB, but I cannot figure out what my mistake is.
If anyone has suggestions for a way to access the data in the bitmap, or can see what I am doing wrong with the DIB, I would greatly appreciate the help!
*** FIGURE 1: Code to create and display a bitmap
void CRunPage::OnPaint()
{
CPaintDC dc(this); // property page device context for painting
CBitmap mBmp; // CBitmap object for displaying built-in bitmaps
CDC mDCMem; // CDC object to handle built-in bitmap
int iWidth, iHeight; // dimension to draw on the screen
int icurLabel, // current label index of open print file
iLabelNum; // number of labels in open print file
LPBITMAPINFOHEADER pBMIH; // bitmap header object for current label
LPBYTE pImage; // bitmap data for current label
CSize size; // size of label
int PreviewLeft,PreviewTop,PreviewWidth,PreviewHeight;
CRect Rect;
BITMAP bm;
LPVOID bmBits=NULL;
// Calculate the preview area
PreviewLeft=5;
PreviewTop=5;
GetDlgItem(IDC_RUN_NEXT)->GetWindowRect(&Rect);
ScreenToClient(&Rect);
PreviewWidth=Rect.left-PreviewLeft*2;
GetDlgItem(IDC_RUN_WRAPTEXT)->GetWindowRect(&Rect);
ScreenToClient(&Rect);
PreviewHeight=Rect.top-PreviewTop*2;
CRect textRect;
CString testText(_T("Hello World"));
CBitmap * pOldBitmap;
CBrush whiteBrush, *pOldBrush;
CPen blackPen, *pOldPen;
mDCMem.CreateCompatibleDC(&dc);
mBmp.CreateCompatibleBitmap(&dc, PreviewWidth+PreviewLeft*2, PreviewHeight+PreviewTop*2);
//mBmp.CreateCompatibleBitmap(&dc, PreviewWidth, PreviewHeight);
pOldBitmap = mDCMem.SelectObject(&mBmp);
blackPen.CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
whiteBrush.CreateSolidBrush(RGB(255,255,255));
textRect.SetRect(0,0,PreviewWidth, PreviewHeight);
// this means behind the text will be a white box w/ a black boarder
pOldBrush = mDCMem.SelectObject(&whiteBrush);
pOldPen = mDCMem.SelectObject(&blackPen);
//these commands draw on the memory-only context (mDCMem)
mDCMem.Rectangle(&textRect);
mDCMem.DrawText((LPCTSTR)testText, 11, &textRect, DT_CENTER|DT_VCENTER);
mDCMem.SelectObject(pOldBrush);
mDCMem.SelectObject(pOldPen);
dc.StretchBlt(PreviewLeft,PreviewTop, PreviewWidth, PreviewHeight, & mDCMem, 0, 0, PreviewWidth, PreviewHeight, SRCCOPY);
mDCMem.SelectObject(pOldBitmap);
}
*** FIGURE 2: Trying to use a DIB instead of a bitmap
void CRunPage::OnPaint()
{
CPaintDC dc(this); // property page device context for painting
CBitmap mBmp; // CBitmap object for displaying built-in bitmaps
CDC mDCMem; // CDC object to handle built-in bitmap
int iWidth, iHeight; // dimension to draw on the screen
int icurLabel, // current label index of open print file
iLabelNum; // number of labels in open print file
LPBITMAPINFOHEADER pBMIH; // bitmap header object for current label
LPBYTE pImage; // bitmap data for current label
CSize size; // size of label
int PreviewLeft,PreviewTop,PreviewWidth,PreviewHeight;
CRect Rect;
BITMAP bm;
// Calculate the preview area
PreviewLeft=5;
PreviewTop=5;
GetDlgItem(IDC_RUN_NEXT)->GetWindowRect(&Rect);
ScreenToClient(&Rect);
PreviewWidth=Rect.left-PreviewLeft*2;
GetDlgItem(IDC_RUN_WRAPTEXT)->GetWindowRect(&Rect);
ScreenToClient(&Rect);
PreviewHeight=Rect.top-PreviewTop*2;
CRect textRect;
CString testText(_T("Hello World"));
CBitmap * pOldBitmap;
CBrush whiteBrush, *pOldBrush;
CPen blackPen, *pOldPen;
LPBYTE pFWandImageMem=NULL, pImageMem=NULL, pTemp=NULL;
int i=0,j=0, buffSize=0, numBytesPerRow=0, bitmapWidthPix,bitmapHeightPix;
char *numBytesPerRowString;
char temp;
void ** ppvBits;
BITMAPINFOHEADER bmif;
BITMAPINFO bmi;
HBITMAP myDIB, myOldDIB;
mDCMem.CreateCompatibleDC(&dc);
//this rect is the area in which I can draw (its x,y location is set by BitBlt or StretchBlt
//mBmp.CreateCompatibleBitmap(&dc, PreviewWidth+PreviewLeft*2, PreviewHeight+PreviewTop*2);
bmif.biSize = sizeof(BITMAPINFOHEADER);
bmif.biWidth = PreviewWidth+PreviewLeft*2;
bmif.biHeight = -(PreviewHeight+PreviewTop*2);//- means top down (I think? I tried both ways and neither worked)
bmif.biPlanes = 1;
bmif.biBitCount = 1;
bmif.biCompression = BI_RGB; // no compression
bmif.biSizeImage = 0; // Size (bytes) if image - this can be set to 0 for uncompressed images
bmif.biXPelsPerMeter = 0;
bmif.biYPelsPerMeter = 0;
bmif.biClrUsed =0;
bmif.biClrImportant = 0;
bmi.bmiColors[0].rgbBlue=0;
bmi.bmiColors[0].rgbGreen=0;
bmi.bmiColors[0].rgbRed=0;
bmi.bmiColors[0].rgbReserved=0;
bmi.bmiColors[1].rgbBlue=255;
bmi.bmiColors[1].rgbGreen=255;
bmi.bmiColors[1].rgbRed=255;
bmi.bmiColors[1].rgbReserved=0;
bmi.bmiHeader=bmif;
myDIB = CreateDIBSection(dc.GetSafeHdc(), &bmi, DIB_RGB_COLORS, ppvBits, NULL, 0);
myOldDIB = (HBITMAP)mDCMem.SelectObject(myDIB);//SelectObject(mDCMem, myDIB);
blackPen.CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
whiteBrush.CreateSolidBrush(RGB(255,255,255));
textRect.SetRect(0,0,PreviewWidth, PreviewHeight);
// this means behind the text will be a white box w/ a black boarder
pOldBrush = mDCMem.SelectObject(&whiteBrush);
pOldPen = mDCMem.SelectObject(&blackPen);
//these commands draw on the memory-only context (mDCMem)
mDCMem.Rectangle(&textRect);
mDCMem.DrawText((LPCTSTR)testText, 11, &textRect, DT_CENTER|DT_VCENTER);
mDCMem.SelectObject(pOldBrush);
mDCMem.SelectObject(pOldPen);
dc.StretchBlt(PreviewLeft,PreviewTop, PreviewWidth, PreviewHeight, & mDCMem, 0, 0, PreviewWidth, PreviewHeight, SRCCOPY);
mDCMem.SelectObject(myOldDIB);
}
So I made two minor changes to the DIB code, and it is displaying the image correctly now.
First, I changed the way I passed in my pointer to the CreateDIBSection():
void ** ppvBits;
to
LPBYTE pBits;
And then I had to change how I passed that into CreateDIBSection. I also explicitly casted the return of CreateDIBSection() to an HBITMAP:
myDIB = CreateDIBSection(dc.GetSafeHdc(), &bmi, DIB_RGB_COLORS, (void**)&pBits, NULL, 0);
to
myDIB = (HBITMAP) CreateDIBSection(dc.GetSafeHdc(), &bmi, DIB_RGB_COLORS, ppvBits, NULL, 0);
I have not had a chance to see if I can access the image data, but I am past the initial issues now. Thanks to anyone who looked at this, and if people know how to do the first (device dependent bitmap) method I would be interested to know.

C++ MFC: Draw a bitmap into a CFrame

I am trying to draw a bitmap (stored as an array of bytes) to a window.
Typical procedure follows:
Get the device context in the OnPaint() handler.
CPaintDC dc(this);
Create a compatible device context from it,
CDC pMemDC->CreateCompatibleDC(&dc);
Create a compatible bitmap that is the size of the client area (GetClientRect(&WinRect)).
CBitmap pNewBitmap->CreateCompatibleBitmap(&dc, WinRect.Width(), WinRect.Height());
Now, if the window client size is exactly the same size as the bitmap, I can simply make a call to pNewBitmap->SetBitmapBits to "feed" my array into the bitmap.
Follow this with a BitBlt and the bitmap appears on the window.
dc->BitBlt(0, 0, WinRect.Width(), WinRect.Height(), pMemDC, 0, 0, SRCCOPY);
If you want the window size to vary independent of the image size, then first you must make sure pNewBitmap is the right size (the client rectangle), but now you cannot simply shove the array into the bitmap.
In this situation, I have found a solution by repeating the above procedure to create a bitmap the exact size of the image so I can "shove" my bits into it, and then BitBlt that into the larger, client-sized bitmap, which then gets BitBlt'ed into the window.
Is there another way to do this? Actual code follows.
void CAnimateWnd::OnPaint()
{
CPaintDC dc(this); // device context for painting
DrawScene(&dc);
// Do not call CWnd::OnPaint() for painting messages
}
void CAnimateWnd::DrawScene(CDC *pDrawDC)
{
CRect WinRect;
GetClientRect(&WinRect);
if (pNewBitmap == NULL)
{
pMemDC = new CDC();
pMemDC->CreateCompatibleDC(pDrawDC);
pNewBitmap = new CBitmap();
pNewBitmap->CreateCompatibleBitmap(pDrawDC, WinRect.Width(), WinRect.Height());
pMemDC->SelectObject(pNewBitmap);
}
CRect BMPRect;
GetBitmapDrawSize(&BMPRect);
if (BMPRect != NULL)
{
if (!(BMPRect.Width() >= WinRect.Width() && BMPRect.Height() >= WinRect.Height()))
{
//The bitmap is smaller than the window, so fill the window with the background color.
CBrush BackBrush(BackGndColor);
CPen BackPen(PS_SOLID, 1, BackGndColor);
pMemDC->SelectObject(&BackBrush);
pMemDC->SelectObject(&BackPen);
pMemDC->Rectangle(&WinRect);
BackBrush.DeleteObject();
BackPen.DeleteObject();
}
}
OverrideAndDrawInHere(pMemDC, resize);
pDrawDC->BitBlt(0,0,WinRect.right,WinRect.bottom,pMemDC,0,0,SRCCOPY);
}
template <class T>
void ImageWindow<T>::OverrideAndDrawInHere(CDC *pDC, int resize)
{
if (this->sourceImage == NULL) return;
CRect clientRect;
GetClientRect(&clientRect);
if (this->dispBMP == NULL)
{
this->dispDC = new CDC();
this->dispDC->CreateCompatibleDC(pDC);
this->dispBMP = new CBitmap();
this->dispBMP->CreateCompatibleBitmap(pDC, this->sourceImage->GetWidth(), this->sourceImage->GetHeight());
this->dispDC->SelectObject(this->dispBMP);
}
this->dispBMP->SetBitmapBits(this->sourceImage->GetArea() * 4, this->translatedImage);
pDC->BitBlt(0, 0, this->sourceImage->GetWidth(), this->sourceImage->GetHeight(), this->dispDC, 0, 0, SRCCOPY);
}
Instead of using BitBlt try StretchBlt if you want to paint a bitmap to a window that isn't the same size as source image:
From MSDN:
Copies a bitmap from a source rectangle into a destination rectangle,
stretching or compressing the bitmap if necessary to fit the
dimensions of the destination rectangle.
If you want the same results as you've shown then you're probably doing it the best way. You could use something like FillRect to draw directly to the Frame (or handle OnEraseBkgnd) and then BitBlt the original sized image but you'd get probably get some flickering, which your double-buffered solution solves.