I am having an issue setting class attributes with a pointer, here is what my code looks like: (this version will compiles, but you need the have minesweeper window open).
WindowObject.h:
#ifndef WINDOWOBJECT_H_INCLUDED
#define WINDOWOBJECT_H_INCLUDED
#include <windows.h>
#include <stdio.h>
class WindowObject
{
public:
WindowObject();
BYTE* getPixelMap() const;
void setPixelMap(BYTE* bitPointer);
void getPixelMap(HWND hwnd);
private:
BYTE* mPixelMap;
};
#endif
WindowObject.cpp:
#include "WindowObject.h"
WindowObject::WindowObject()
{
}
BYTE* WindowObject::getPixelMap() const
{
printf("getPixelMap: %d\n", mPixelMap[0]); // it crashes, bad pointer!!
return mPixelMap;
}
void WindowObject::setPixelMap(BYTE *bitPointer)
{
printf("setPixelMap 1: %d\n", bitPointer[0]); // It works!
mPixelMap = bitPointer;
printf("setPixelMap 2: %d\n", mPixelMap [0]); // Why does this works and getPixelMap doesn't???
}
void WindowObject::getPixelMap(HWND hwnd)
{
HDC hdcWindow, hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
// Retrieve the handle to a display device context for the client
// area of the window.
hdcWindow = GetDC(hwnd);
// Create a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC(hdcWindow);
if(!hdcMemDC)
{
MessageBoxA(hwnd, "CreateCompatibleDC has failed", "Failed", MB_OK);
goto done;
}
// Get the client area for size calculation
RECT rcClient;
GetClientRect(hwnd, &rcClient);
// Create a compatible bitmap from the Window DC
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top);
if(!hbmScreen)
{
MessageBoxA(hwnd, "CreateCompatibleBitmap Failed", "Failed", MB_OK);
goto done;
}
//Get the BITMAP from the HBITMAP
GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
BYTE* bitPointer;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
// Create a device independent bitmap that so that we can write to it directly
hbmScreen = CreateDIBSection(hdcMemDC, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL);
// Select the compatible bitmap into the compatible memory DC.
SelectObject(hdcMemDC, hbmScreen);
// Bit block transfer into our compatible memory DC.
if ( !BitBlt(hdcMemDC, 0, 0, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top, hdcWindow, 0, 0, SRCCOPY) )
{
MessageBox(hwnd, "BitBlt has failed", "Failed", MB_OK);
goto done;
}
// Create PixelMap
int width = (rcClient.right-rcClient.left),
height = (rcClient.bottom-rcClient.top);
printf("Inside getPixelMap: %d\n", bitPointer[0]); // it works!
setPixelMap(bitPointer);
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(hwnd, hdcWindow);
}
main.cpp:
#include "WindowObject.h"
int main()
{
WindowObject wObj;
HWND hwnd;
hwnd = FindWindow(NULL, "Minesweeper");
if (hwnd)
{
wObj.getPixelMap(hwnd);
BYTE* bPointer = wObj.getPixelMap();
printf("After getPixelMap: %d", bPointer[0]);
}
}
The program crashes, and it gives me an "access violation reading location". I'm almost 100% sure the problem comes from my pointer but I find it wierd that it works in every stage of my tests, except when I get it with my getPixelMap method. Any help would be much appreciated!
The problem is that this data member either is not initialized (if an object of the class is default initialized) or has value 0. So using member function getPixelMap() is unsafe. Also as I think the life of an argument of function setPixelMap may be less than the life of an object of the class. So the behaviour of the program is undefined.
I think it would be better to use either a smart pointer or to allocate a memory for using by the data member yourself.
Inside the function getPixelMap(HWND hwnd) you are calling DeleteObject on the returned bitmap, after saving the pointer but before returning. So by the time you access the bitmap later, it is no longer a valid pointer, since according to the CreateDIBSection docs it says that the bitmap will be deallocated when you call DeleteObject.
You will need to keep the bitmap object (hbmScreen) and delete it later, perhaps in the destructor.
Related
I'm trying to work with both Direct2D and Direct3D. Right now, I'm having Direct2D draw content to a separate Device Context (with an HDC) and then copy the contents of that Device Context into my window. I could show the code I use to set that up in an edit to this post if requested, but before Direct3D gets involved, that part works.
Here is a simplified version of the Window Drawing code I use.
if (d3dEngine.Get()) // Object used to hold Direct3D Resources (.Get() returns a pointer for a null check)
{
// d3dEngine->PrepareScene(D2D1::ColorF(D2D1::ColorF::Wheat));
}
// Drawing Board holds the Direct 2D Render Target
drawingBoard->GetRenderer()->BeginDraw();
drawingBoard->GetRenderer()->Clear(D2D1::ColorF(1.0f,1.0f,1.0f,1.0f));
mainPage->Draw(); // Main Page Holds various objects that draw to Direct2D
if (d3dEngine.Get())
d3dEngine->FinalizeScene();
drawingBoard->GetRenderer()->EndDraw();
// Get the Secondary Device Context that Direct2D draws to
HDC dc = drawingBoard->GetDc();
RECT r{ 0,0,0,0 };
int err = 0;
// Retrieve the Rectangle for the window (currentWindow is the window handle used)
if(!GetClientRect(currentWindow, &r))
err = GetLastError();
// Use the BitBlt function to copy Direct2D content into a window
if (!BitBlt(GetDC(currentWindow), r.left, r.top, r.right - r.left, r.bottom - r.top, dc, 0, 0, SRCCOPY))
err = GetLastError();
Before any Direct3D resources are created (and the d3dEngine.Get() call returns null), this code runs to my satisfaction.
However, following the creation of Direct3D resources, the code fails:
RECT r{ 0,0,0,0 };
int err = 0;
// Retrieve the Rectangle for the window (currentWindow is the window handle used)
if(!GetClientRect(currentWindow, &r))
err = GetLastError();
The window handle currentWindow becomes invalid as GetLastError() returns 1400 after the call to GetClientRect. I suspect that the Swap Chain in Direct3D 11 may play a role due to the following code used to activate Direct3D.
GetClientRect(window, &Location);
unsigned int width = Location.right - Location.left,
height = Location.bottom - Location.top;
D3D_DRIVER_TYPE dTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE//, D3D_DRIVER_TYPE_WARP
};
int tTypes = ARRAYSIZE(dTypes);
D3D_FEATURE_LEVEL dLevels[] =
{
D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0
};
int tLevels = ARRAYSIZE(dLevels);
DXGI_SWAP_CHAIN_DESC swapChainDescription;
// Initialize the swap cahin
swapChainDescription.BufferCount = 2;
swapChainDescription.BufferDesc.Width = Location.right - Location.left;
swapChainDescription.BufferDesc.Height = Location.bottom - Location.top;
swapChainDescription.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swapChainDescription.BufferDesc.RefreshRate.Numerator = 30;
swapChainDescription.BufferDesc.RefreshRate.Denominator = 1;
swapChainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDescription.OutputWindow = window;
swapChainDescription.Windowed = true;
swapChainDescription.SampleDesc.Count = 1;
swapChainDescription.SampleDesc.Quality = 0;
swapChainDescription.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
swapChainDescription.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapChainDescription.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE;
swapChainDescription.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;// DXGI_SWAP_EFFECT_DISCARD;
unsigned int flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
/// Other initialization Code
HRESULT results = 0;
// Initialize ID3D11Device "graphicsDevice"
IDXGISwapChain* sc = nullptr;
results = dxFact->CreateSwapChain(graphicsDevice, &swapChainDescription, &sc);
This code is in a different class than the Window drawing code towards the beginning.
The window variable in the Direct3D code holds the same value that currentWindow holds in the Window Code.
Is there anyone who could provide insight as to what is going on and why the window handle stops working? And perhaps suggest a workaround?
While I'm still unsure why the window handle seemed to be useless when I checked, I was able to develop a workaround.
Basically, I have my Window class hold a RECT indicating the size of the Window and I use that instead of getting the RECT from the Window every time.
RECT r{ 0,0,0,0 };
int err = 0;
// Retrieve the Rectangle for the window (currentWindow is the window handle used)
if(!GetClientRect(currentWindow, &r))
err = GetLastError();
// Use the BitBlt function to copy Direct2D content into a window
if (!BitBlt(GetDC(currentWindow), r.left, r.top, r.right - r.left, r.bottom - r.top, dc, 0, 0, SRCCOPY))
err = GetLastError();
// Now, it uses a 'size' attribute
int err = 0;
if (!BitBlt(GetTWindowDc(), size.left, size.top, size.right - size.left, size.bottom - size.top, dc, 0, 0, SRCCOPY))
err = GetLastError();
When it comes to getting the Window Device Context, the new GetTWindowDc() method takes care of that:
if (d3dEngine.Get())
return d3dEngine->GetDC();
return GetDC(currentWindow);
Basically, if Direct3D is activated, the 3D manager object (which holds a DXGISurface) retrieves the HDC from Direct3D to use.
HDC TWindowEngine::GetDC()
{
if (!surface.Get()) // My Unique Smart pointer to a DXGISurface1 object
return 0;
HDC dc;
assert(SUCCEEDED(surface->GetDC(FALSE, &dc)));
return dc;
}
According to Surface-GetDc Documentation, you need to call the corresponding ReleaseDc on the DXGISurface1 object. So this is how I do it.
In the Window Draw code:
if (!BitBlt(GetTWindowDc(), size.left, size.top, size.right - size.left, size.bottom - size.top, dc, 0, 0, SRCCOPY))
err = GetLastError();
FlushDc();
Here is the FlushDc method:
void TWindow::FlushDc()
{
if (d3dEngine.Get())
d3dEngine->ClearDC();
}
And the ClearDC method is impelented.
void TWindowEngine::ClearDC()
{
if (surface.Get())
surface->ReleaseDC(nullptr);
}
I am searching for the memory leak(s) in this code.
I am new to GDI+ and I am not sure what I am doing wrong.
The class you see below gets called in a loop in my main function.
Each loop iteration I push an other vector to the function.
Everything is working fine except there is a memory leak.
I tried the program cppCheck to find the leak but it found no memory leaks :/
My last chance to fix the problem is to ask someone who has more experience than me with GDI+
Thank you very much for the help and sorry for the long code :)
#include "helper.h"
Gui::Gui(const TCHAR* fileName) {
this->fileName = fileName;
}
void Gui::drawGui(Gdiplus::Bitmap* image, std::vector<std::wstring> &vec) {
// Init graphics
Gdiplus::Graphics* graphics = Gdiplus::Graphics::FromImage(image);
Gdiplus::Pen penWhite (Gdiplus::Color::White);
Gdiplus::Pen penRed (Gdiplus::Color::Red);
Gdiplus::SolidBrush redBrush(Gdiplus::Color(255, 255, 0, 0));
penRed.SetWidth(8);
unsigned short marginTop = 15;
unsigned short marginLeft = 5;
unsigned short horizontalBarsizeStart = marginLeft + 60;
for (unsigned short iter = 0; iter < 8; iter++) {
// Draw text
std::wstring coreLabel = L"Core " + std::to_wstring(iter) + L':';
Gdiplus::Font myFont(L"Arial", 12);
Gdiplus::PointF origin(marginLeft, marginTop - 10);
graphics->DrawString(coreLabel.c_str(), coreLabel.length(), &myFont, origin, &redBrush);
// Draw CPU lines
unsigned short horizontalBarsizeEnd = horizontalBarsizeStart + std::stoi(vec.at(iter)); // 100 == Max cpu load
graphics->DrawLine(&penRed, horizontalBarsizeStart, marginTop, horizontalBarsizeEnd, marginTop);
// Draw border
Gdiplus::Rect rect(horizontalBarsizeStart, marginTop - 5, 100, 8);
graphics->DrawRectangle(&penWhite, rect);
// Next element
marginTop += 17;
}
}
bool Gui::SetColorBackgroundFromFile(std::vector<std::wstring> &vec) {
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
// Initialize GDI+.
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
HDC hdc = GetDC(NULL);
// Load the image. Any of the following formats are supported: BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF
Gdiplus::Bitmap* image = Gdiplus::Bitmap::FromFile(this->fileName, false);
if (image == NULL) {
return false;
}
// Draw the gui
this->drawGui(image, vec);
// Get the bitmap handle
HBITMAP hBitmap = NULL;
Gdiplus::Status status = image->GetHBITMAP(RGB(0, 0, 0), &hBitmap);
if (status != Gdiplus::Ok) {
return false;
}
BITMAPINFO bitmapInfo = { 0 };
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
// Check what we got
int ret = GetDIBits(hdc, hBitmap, 0, 0, NULL, &bitmapInfo, DIB_RGB_COLORS);
if (LOGI_LCD_COLOR_WIDTH != bitmapInfo.bmiHeader.biWidth || LOGI_LCD_COLOR_HEIGHT != bitmapInfo.bmiHeader.biHeight) {
std::cout << "Oooops. Make sure to use a 320 by 240 image for color background." << std::endl;
return false;
}
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biHeight = -bitmapInfo.bmiHeader.biHeight; // this value needs to be inverted, or else image will show up upside/down
BYTE byteBitmap[LOGI_LCD_COLOR_WIDTH * LOGI_LCD_COLOR_HEIGHT * 4]; // we have 32 bits per pixel, or 4 bytes
// Gets the "bits" from the bitmap and copies them into a buffer
// which is pointed to by byteBitmap.
ret = GetDIBits(hdc, hBitmap, 0,
-bitmapInfo.bmiHeader.biHeight, // height here needs to be positive. Since we made it negative previously, let's reverse it again.
&byteBitmap,
(BITMAPINFO *)&bitmapInfo, DIB_RGB_COLORS);
LogiLcdColorSetBackground(byteBitmap); // Send image to LCD
// delete the image when done
if (image) {
delete image;
image = NULL;
Gdiplus::GdiplusShutdown(gdiplusToken); // Shutdown GDI+
}
return true;
}
In drawGui() you're leaking the graphics object. This line creates a new Gdiplus::Graphics object:
Gdiplus::Graphics* graphics = Gdiplus::Graphics::FromImage(image);
But nowhere do you call delete graphics to delete it once you're done with it.
In SetColorBackgroundFromFile, you're leaking the DC.
HDC hdc = GetDC(NULL);
This gets a DC for the screen, but nowhere do you call ReleaseDC(NULL, hdc); to free it.
In the same function, you are creating an HBITMAP using the following call:
Gdiplus::Status status = image->GetHBITMAP(RGB(0, 0, 0), &hBitmap);
But nowhere do you call DeleteObject(hBitmap); to free it.
You also have the problem that in case of errors, your code can return without doing necessary cleanup. E.g. if the GetHBITMAP call fails, you return immediately and will leak the image object that you created a few lines above.
I would like to get the context of my window into a bitmap. I use the window to draw basic lines with touch. The problem that I'm having is my bitmap is black. Which it is probably due to the fact that I'm not grabbing the device context properly or doing something else wrong.
The functions CreateBitmapInfoStruct and CreateBMPFile are from this MSDN example.
Also note that g_hWnd is a global variable that has the handle of the window for which I want to save the picture.
My end goal is to be able to save the bitmap into a mysql field (BLOB) that I have. This is what my original problem was. Anyway, I started by first trying to create a BMP to file.
I have searched here and in other places. The best solution I found was recommended here following this MSDN example. However, it is not working.
Any help for this specific problem and/or in writing to the bitmap into a blob into mysql table, will be greatly appreciated.
Here is my code:
HDC hDC = GetDC(g_hWnd);
LPRECT rect = (LPRECT)malloc(sizeof(RECT));
GetWindowRect(g_hWnd,rect);
int h = rect->right - rect->left;
int w = rect->bottom - rect->top;
LPRECT rect = (LPRECT)malloc(sizeof(RECT));
GetWindowRect(g_hWnd,rect);
HBITMAP hBmp = CreateCompatibleBitmap(hDC,w,h);
PBITMAPINFO pbmi;
pbmi = CreateBitmapInfoStruct(g_hWnd,hBmp);
CreateBMPFile(g_hWnd, TEXT("c:\\TEMPO\\TestG2.bmp"), pbmi,
hBmp, hDC) ;
ReleaseDC(g_hWnd,hDC);
DeleteObject(hBmp);
DeleteObject(pbmi);
if (rect != nullptr)
free(rect);
EDIT:
The actual answer for capturing the screen (getDC) is to modify this sample in MSDN.
I have modified the sample here (remove the stretch) Note that still uses the goto, which I will remove.
A note about the GOTO... WHile I don't use it, I don't find it to be a problem either. I think too much has been made about the GOTO statement... like if in assembly, we wouldn't do goto (JUMPS)
Here is the code:
void saveBitmap()
{
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
// Retrieve the handle to a display device context for the client
// area of the window.
hdcScreen = GetDC(NULL);
hdcWindow = GetDC(g_hWnd);
// Create a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC(hdcWindow);
if(!hdcMemDC)
{
goto done;
}
RECT rcClient;
GetClientRect(g_hWnd, &rcClient);
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right-rcClient.left,
rcClient.bottom-rcClient.top);
if(!hbmScreen)
{
goto done;
}
SelectObject(hdcMemDC,hbmScreen);
if(!BitBlt(hdcMemDC,
0,0,
rcClient.right-rcClient.left, rcClient.bottom-rcClient.top,
hdcWindow,
0,0,
SRCCOPY))
{
goto done;
}
GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 *
bmpScreen.bmHeight;
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize);
char *lpbitmap = (char *)GlobalLock(hDIB);
// Gets the "bits" from the bitmap and copies them into a buffer
// which is pointed to by lpbitmap.
GetDIBits(hdcWindow, hbmScreen, 0,
(UINT)bmpScreen.bmHeight,
lpbitmap,
(BITMAPINFO *)&bi, DIB_RGB_COLORS);
// A file is created, this is where we will save the screen capture.
HANDLE hFile = CreateFile(L"c:\\tempo\\captureqwsx.bmp",
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
// Add the size of the headers to the size of the bitmap to get the total file size
DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER);
//Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) +
(DWORD)sizeof(BITMAPINFOHEADER);
//Size of the file
bmfHeader.bfSize = dwSizeofDIB;
//bfType must always be BM for Bitmaps
bmfHeader.bfType = 0x4D42; //BM
DWORD dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
//Unlock and Free the DIB from the heap
GlobalUnlock(hDIB);
GlobalFree(hDIB);
//Close the handle for the file that was created
CloseHandle(hFile);
//Clean up
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL,hdcScreen);
ReleaseDC(g_hWnd,hdcWindow);
}
You can use GetDIBits().
This function copies the image pixels data into your own allocated memory.
See GetDIBits and loop through pixels using X, Y for some more details and explanation.
I wish to create a transparent window over the desktop.
For that purpose I've created an HDC with a background of the desktop (created HBITMAP of the desktop and applied it to my HDC), and invoked UpdateLayeredWindow.
So far, so good.
for performance issues i need to keep a persistent GDI+ object, meaning my HDC and HBITMAP need to stay the same handles between paintings (assuming the desktop DC didn't change), same as in this question.
In the first painting iteration all goes well. in the second painting iteration, since the HDC and HBITMAP haven't changed, I repaint on the existing HDC, meaning i get double images (the background is not erased).
Here's a code example of what I'm doing:
bool SomeUI::Draw()
{
BLENDFUNCTION blend = {0};
POINT ptPos = {0};
SIZE sizeWnd = {0};
POINT ptSrc = {0};
BOOL bUpdate = FALSE;
// Get the client rect
RECT rctWindow;
bool bGot = GetWindowRect(rctWindow);
if (!bGot)
return false;
// Get the desktop's device context
HDC hDCDesktop = GetDC(NULL);
if (!hDCDesktop)
return false;
int nWidth = abs(rctWindow.right - rctWindow.left);
int nHeight = abs(rctWindow.bottom - rctWindow.top);
// Create 32Bit bitmap to apply PNG transparency
VOID *ppvBits = NULL;
BITMAPINFO BitmapInfo = {0};
BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfo.bmiHeader.biWidth = nWidth;
BitmapInfo.bmiHeader.biHeight = nHeight;
BitmapInfo.bmiHeader.biPlanes = 1;
BitmapInfo.bmiHeader.biBitCount = 32;
BitmapInfo.bmiHeader.biCompression = BI_RGB;
HBITMAP hBmp = CreateDIBSection(hDCDesktop, &BitmapInfo, DIB_RGB_COLORS, &ppvBits, NULL, 0);
if (!hBmp || hBmp==(HBITMAP)ERROR_INVALID_PARAMETER)
goto releaseHandles;
// Create a compatible DC and select the newly created bitmap
if (!m_hDC)
{
m_hDC = CreateCompatibleDC(hDCDesktop);
if (!m_hDC)
goto releaseHandles;
SelectObject(m_hDC, hBmp);
}
else
{
///////////////////////////////////////////////////////////////////////
//
// The problem lies here, this is where I need to reset the HBITMAP
// according to the desktop here (to have a transparent DC to work on)
//
///////////////////////////////////////////////////////////////////////
}
// The drawing logic
bool bInnerDraw = Draw(m_hDC);
if (!bInnerDraw)
goto releaseHandles;
// Call UpdateLayeredWindow
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
sizeWnd.cx = nWidth;
sizeWnd.cy = nHeight;
ptPos.x = rctWindow.left;
ptPos.y = rctWindow.top;
bUpdate = UpdateLayeredWindow(m_hWnd, hDCDesktop, &ptPos, &sizeWnd, m_hDC, &ptSrc, 0, &blend, ULW_ALPHA);
if (!bUpdate)
goto releaseHandles;
releaseHandles:
// releasing handles
}
Any ideas?
Found the Answer:
In order to reset the persistent HBITMAP, (reminder: it needs to stay the same handle), we'll set the desktop background of that area to a temporary HBITMAP and copy it to the persistent HBITMAP.
To achieve that (copying from one HBITMAP to the other), We'll create a temporary HDC and select the temporary HBITMAP to it, and copy the temporary HDC to the persistent HDC, usint BitBlt
Here's the code:
hBmpTemp = CreateDIBSection(hDCDesktop, &BitmapInfo, DIB_RGB_COLORS, &ppvBits, NULL, 0);
if (!hBmpTemp || hBmpTemp==(HBITMAP)ERROR_INVALID_PARAMETER)
goto releaseHandles;
HDC hTempDC = CreateCompatibleDC(NULL);
if (!hTempDC)
goto releaseHandles;
SelectObject(hTempDC, hBmpTemp);
::BitBlt(m_hPersistentDC, 0, 0, nWidth, nHeight, hTempDC, rctWindow.left, rctWindow.top, SRCCOPY);
::DeleteDC(hTempDC);
In my code I quickly generate images on the fly, and I want to display them as quickly as possible. So the first time I create my image, I create a new BITMAP, but instead of deleting the old one and creating a new one for every subsequent image, I just want to copy my data back into the existing one. Here is my code to do both the initial creation and the updating. The creation works just fine, but the updating one doesn't work.
BITMAPINFO bi;
HBITMAP Frame::CreateBitmap(HWND hwnd, int tol1, int tol2, bool useWhite, bool useBackground)
{
ZeroMemory(&bi.bmiHeader, sizeof(BITMAPINFOHEADER));
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = width;
bi.bmiHeader.biHeight = height;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 24;
bi.bmiHeader.biCompression = BI_RGB;
ZeroMemory(bi.bmiColors, sizeof(RGBQUAD));
// Allocate memory for bitmap bits
int size = height * width;
Pixel* newPixels = new Pixel[size];
// Recompute the output
//memcpy(newPixels, pixels, size*3);
ComputeOutput(newPixels, tol1, tol2, useWhite, useBackground);
HBITMAP bitmap = CreateDIBitmap(GetDC(hwnd), &bi.bmiHeader, CBM_INIT, newPixels, &bi, DIB_RGB_COLORS);
delete newPixels;
return bitmap;
}
and
void Frame::UpdateBitmap(HWND hwnd, HBITMAP bitmap, int tol1, int tol2, bool useWhite, bool useBackground)
{
ZeroMemory(&bi.bmiHeader, sizeof(BITMAPINFOHEADER));
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
HDC hdc = GetDC(hwnd);
if(!GetDIBits(hdc, bitmap, 0, bi.bmiHeader.biHeight, NULL, &bi, DIB_RGB_COLORS))
MessageBox(NULL, "Can't get base image info!", "Error!", MB_ICONEXCLAMATION | MB_OK);
// Allocate memory for bitmap bits
int size = height * width;
Pixel* newPixels = new Pixel[size];
// Recompute the output
//memcpy(newPixels, pixels, size*3);
ComputeOutput(newPixels, tol1, tol2, useWhite, useBackground);
// Push back to windows
if(!SetDIBits(hdc, bitmap, 0, bi.bmiHeader.biHeight, newPixels, &bi, DIB_RGB_COLORS))
MessageBox(NULL, "Can't set pixel data!", "Error!", MB_ICONEXCLAMATION | MB_OK);
delete newPixels;
}
where the Pixel struct is just this:
struct Pixel { unsigned char b, g, r; };
Why does my update function not work. I always get the MessageBox for "Can't set pixel data!" I used code similar to this when I was loading in the original bitmap from file, then editing the data, but now when I manually create it, it doesn't work.
to write to a bitmap you have to Select it into a DeviceContext:
hdcMemDC = CreateCompatibleDC(hdc);
... // adjust hdcMem to right sizes (see MSDN : Capturing an Image)
SelectObject(hdcMemDC,bitmap);
... // your Bitmap drawing
BitBlt( hdcMemDC, ... ) // to copy to hdc.
ReleaseDC(hWnd, hdcMemDC);
Also CreateDIBitmap( GetDC(hwnd),... ) needs a ReleaseDC(hWnd ,??? );
You must assign GetDC(...) to a variable so you can release it.
Please, create your bitmap with CreateDIBSection.
This creates a HBITMAP to a bitmap and returns a pointer to a usermode accessible buffer. In the format you specify.