Drawing part of image in Direct2D - c++

Since GDI+ is pretty (ridiculously) slow, I've decided to migrate to Direct2D. I've looked up many topics on many forums (including this one), but with no success (it may also be caused by the fact that's pretty late) and the Direct2D documentation is pretty slim still (and confusing, for me at least).
Anyway, I've got a .PNG that I open in Direct2D and want to draw only a part of it once every 20ms.
Initialize D2D stuff
ID2D1Factory* d2dFactory = NULL;
IWICImagingFactory* d2dWICFactory = NULL;
IWICBitmapDecoder* d2dDecoder = NULL;
IWICFormatConverter* d2dConverter = NULL;
ID2D1HwndRenderTarget* d2drender = NULL;
IWICBitmapFrameDecode* d2dBmpSrc = NULL;
ID2D1Bitmap* d2dBmp = NULL;
/* initialize all the good stuff */
HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(ID2D1Factory), NULL, (void**)&d2dFactory);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
hr = d2dFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(zgE->getWnd(), size), &d2drender);
hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
__uuidof(IWICImagingFactory), (void**)(&d2dWICFactory));
hr = d2dWICFactory->CreateDecoderFromFilename(L"tile_1.png", NULL, GENERIC_READ,
WICDecodeMetadataCacheOnLoad, &d2dDecoder);
hr = d2dWICFactory->CreateFormatConverter(&d2dConverter);
hr = d2dDecoder->GetFrame(0, &d2dBmpSrc);
hr = d2dConverter->Initialize(d2dBmpSrc, GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeMedianCut);
hr = d2drender->CreateBitmapFromWicBitmap(d2dConverter, NULL, &d2dBmp);
Drawing:
/* draw the image */
D2D1_RECT_F rect = D2D1::RectF(x, y, x + size.width, y + size.height);
d2drender->DrawBitmap(d2dBmp, &rect);
However, I can't get it to draw only a part of it, lets say 20 by 20 pixels. I've fiddled with DrawBitmap() and with differently sized rects, but the result isn't cropping the image.
Is there any way to do it besides layering, since I don't want to layer the image at each frame?

ID2D1RenderTarget::DrawBitmap ( http://msdn.microsoft.com/en-us/library/windows/desktop/dd371880(v=vs.85).aspx ) takes up to 5 parameters, the last one being the source rectangle. You're only supplying the first 2 parameters, and the default for the source rectangle is to use the entire bitmap. So, the solution is simply to supply this parameter with a D2D1::RectF(0, 0, 20, 20) if you only want to draw from that portion of it.

Mainly three steps
Create the entire bitmap from file
Get part of the entire bitmap by calling CopyFromBitmap
Draw bitmap created in step 2.

Related

Visual Studio C++ How do I stop images from being scaled wrong when I maximize window D3D11 and D2D1

So I'm trying to iron out kinks of how I'm rendering images with direct stuff. Right now it works as a DXGISwapchain with D3D11 and I make a ID2D1RenderTarget which I draw to using bitmaps. My issue is when I hit the maximize button on the window my images are off, or at least the ones using data from the window grabbed with GetClientRect (the others seem... close enough but probably still off and I want to be able to use the client space to scale and draw things later as well). I have a D2D1::RectF set with the top left at 0.0f and the bottom right as the window's height and width grabbed via GetClientRect (along with a few others just for additional fooling around). Looked around and it seems like I need to call recreate the ID2D1RenderTarget and/or resize the buffers. Calling a function to recreate the ID2D1RenderTarget before making objects which contain the bitmaps and the functions which draw them did not help with the issue at all. I tried resizing the buffers but I keep getting errors, first set were regarding parameters, but before fixing that I realized I needed to release the objects, but now my it seems since I have made the objects with ComPtr it seems how it deals with deleting them is having issues. client.h is calling an exception: "Access violation executing location " with the unsigned long InternalRelease() function. This occurs with the function to adjust the buffers and target. So right now I'm lost as to what to do in order to get the desired effect. Only other things to note is the ID3D11RenderTargetView I made is used to clear to a color since for I had errors with calling Clear(D2D1::ColorF(D2D1::ColorF::White)); on my ID2D1RenderTarget. I don't care if the solution to this resizes the ID3D11RenderTargetView unless it will improve speed for the program or prevent some sort of unforeseen issue elsewhere since I don't intend to use it aside for that. If I could call clear on the ID2D1RenderTarget and no longer need the ID311RenderTargetView and keep the swapchain while resolving the issue that would work too. Also I intend to work out fullscreen mode next so a method that works with that would also be very much desired. I'm also open to take any other advice here, while it's not polished and just in a form to get things working first, I probably will miss things even when tidying up. Anyway here's the code:
Here's the Graphics class where I make the swapchain, buffers and rendertargets and such and the function in which I try and reset the buffers. Side note I followed some tutorials on my way here to get me to understand enough of the direct stuff to get here and get to the point where I'm looking into to stuff on Microsoft and understanding it somewhat to solve problems I have. Anyway one of them went through making some exceptions and that is what stuff like Graphic_Throw_Failure() are for. (though I did it wrong or the errors are doing something and I can't see the pop up window half the time, sometimes it stops in the exception code but I can still read the message)
//not in the cpp file but just how some variables exist for clarity.
private:
Microsoft::WRL::ComPtr<ID3D11Device> pDevice = nullptr;
Microsoft::WRL::ComPtr < IDXGISwapChain> pSwapChain = nullptr;
Microsoft::WRL::ComPtr < ID3D11DeviceContext> pContext = nullptr;
Microsoft::WRL::ComPtr < ID3D11RenderTargetView> pRTarget = nullptr;
ID2D1RenderTarget* p2dRenderTarget = nullptr;
ID2D1Factory* p2DFactory = nullptr;
Graphics::Graphics(HWND hwnd) {
DXGI_SWAP_CHAIN_DESC swapchainDesc = {};
ZeroMemory(&swapchainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
swapchainDesc.Windowed = true;
swapchainDesc.BufferCount = 1;
swapchainDesc.BufferDesc.Height = 0;
swapchainDesc.BufferDesc.Width = 0;
swapchainDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swapchainDesc.SampleDesc.Count = 1;
swapchainDesc.SampleDesc.Quality = 0;
swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapchainDesc.BufferDesc.RefreshRate.Numerator = 1;
swapchainDesc.BufferDesc.RefreshRate.Denominator = 60;
swapchainDesc.OutputWindow = hwnd;
HRESULT hre;
Graphic_Throw_Failure(D3D11CreateDeviceAndSwapChain(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
levels,
4,
D3D11_SDK_VERSION,
&swapchainDesc,
&pSwapChain,
&pDevice,
nullptr,
&pContext
));
//3Dbuffer setup
wrl::ComPtr<ID3D11Resource> p3dbuffer = nullptr;
Graphic_Throw_Failure(pSwapChain->GetBuffer(0, __uuidof(ID3D11Resource), &p3dbuffer));
Graphic_Throw_Failure(pDevice->CreateRenderTargetView(p3dbuffer.Get(), nullptr, &pRTarget));
//2D buffer Setup
IDXGISurface* pBackBuffer = nullptr;
Graphic_Throw_Failure(pSwapChain->GetBuffer(0,IID_PPV_ARGS(&pBackBuffer)));
//makes 2d Factory
Graphic_Throw_Failure(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &p2DFactory));
//sets up DXGI buffer for 2d
FLOAT dpi;
dpi = GetDpiForWindow(hwnd);
//p2DFactory->GetDesktopDpi(&dpiX, &dpiY);
D2D1_RENDER_TARGET_PROPERTIES p2dRTprops =
D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
dpi,
dpi
);
Graphic_Throw_Failure(p2DFactory->CreateDxgiSurfaceRenderTarget(
pBackBuffer, &p2dRTprops, &p2dRenderTarget
));
if(pBackBuffer!=nullptr)
pBackBuffer->Release();
}
//the adjusting function I failed to make. could also be missing somethings I need to clear before
//calling ResizeBuffers
void Graphics::adjustRenderTargets(HWND hwnd) {
HRESULT hre;
pContext->ClearState();
p2dRenderTarget->Release();
pRTarget->Release();
//3Dbuffer setup
wrl::ComPtr<ID3D11Resource> p3dbuffer = nullptr;
Graphic_Throw_Failure(pSwapChain->GetBuffer(0, __uuidof(ID3D11Resource), &p3dbuffer));
Graphic_Throw_Failure(pDevice->CreateRenderTargetView(p3dbuffer.Get(), nullptr, &pRTarget));
//2D buffer Setup
IDXGISurface* pBackBuffer = nullptr;
Graphic_Throw_Failure(pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)));
//makes 2d Factory
Graphic_Throw_Failure(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &p2DFactory));
//sets up DXGI buffer for 2d
FLOAT dpi;
dpi = GetDpiForWindow(hwnd);
//p2DFactory->GetDesktopDpi(&dpiX, &dpiY);
D2D1_RENDER_TARGET_PROPERTIES p2dRTprops =
D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
dpi,
dpi
);
Graphic_Throw_Failure(p2DFactory->CreateDxgiSurfaceRenderTarget(
pBackBuffer, &p2dRTprops, &p2dRenderTarget
));
if (pBackBuffer != nullptr)
pBackBuffer->Release();
};
//and the destructor in case there is something still wrong there though
//currently not giving me issues since I set the objects in it to nullptr after releasing it.
//didn't work for the ComPtr.
Graphics::~Graphics() {
if (p2DFactory != nullptr) {
p2DFactory->Release();
}
if (p2dRenderTarget != nullptr) {
p2dRenderTarget->Release();
}
}
This is the class which holds the bitmaps and deals with them and drawing them. Once again I made some exceptions for this class
//some variables in the header file
ID2D1Bitmap* Bittmap=nullptr;
Graphics* GFX;
Sprites::Sprites(const wchar_t* filename, Graphics* gfx) {
HRESULT hre;
GFX = gfx;
//makes WIC Factory
IWICImagingFactory* WICfactory = NULL;
Sprite_Throw_Failure(CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWICImagingFactory,
(LPVOID*)&WICfactory
));
//Makes the Decoder
IWICBitmapDecoder* WICdecode = NULL;
Sprite_Throw_Failure(WICfactory->CreateDecoderFromFilename(
filename,
NULL,
GENERIC_READ,
WICDecodeMetadataCacheOnLoad,
&WICdecode
));
//Read the frame (should be only one so read the image)
IWICBitmapFrameDecode* WICframe = NULL;
Sprite_Throw_Failure(WICdecode->GetFrame(0, &WICframe));
//Format converter
IWICFormatConverter* WICconverter = NULL;
Sprite_Throw_Failure(WICfactory->CreateFormatConverter(&WICconverter));
//makes the converter set up to create a 32bpp BGRA bitmap
Sprite_Throw_Failure(WICconverter->Initialize(
WICframe,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
NULL,
0.0,
WICBitmapPaletteTypeCustom
));
//makes the bitmap
Sprite_Throw_Failure(GFX->Get2DRenderTarget()->CreateBitmapFromWicBitmap(
WICconverter,
NULL,
&Bittmap
));
if (WICfactory) WICfactory->Release();
if (WICdecode) WICdecode->Release();
if (WICconverter)WICconverter->Release();
if (WICframe)WICframe->Release();
}
//draws the sprites
void Sprites::Draw(D2D1_RECT_F location) {
HRESULT hre;
GFX->Get2DRenderTarget()->BeginDraw();
GFX->Get2DRenderTarget()->DrawBitmap(
Bittmap,
location, //destination rect
1.0f, //opacity
D2D1_BITMAP_INTERPOLATION_MODE::D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
D2D1::RectF(
1980.0f, 2850.0f, 3000.0f,
3600.0f) //source rect
);
Sprite_Throw_Failure(GFX->Get2DRenderTarget()-> EndDraw());
}
Sprites::~Sprites() {
//bitmapsheet.clear();
if (Bittmap != nullptr) {
Bittmap->Release();
}
}
This is the class which the main loop is handled wnd is the windowclass I made which makes and manages the window. I use it here to get the graphics object the window uses which has all the direct stuff. here are some variables that appear, forgive the name bob.
//in header file
private:
Window wnd;
//in cpp file
Sprites* testsprite;
Sprites* testsprite2;
D2D1_RECT_F bob;
within the function that calls over and over for the duration of the program at the part where I render:
//inefficient constant adjusting of rendering just so I can quickly assess
//the change and make sure it works so when I implement how I intend the windows to be scaled it
//will already be done
wnd.GFX().adjustRenderTargets(wnd.getwindowhandle());
//clearing the ID3D11RenderTargetView to a color
wnd.GFX().ClearBuffer(0.3f, 0.5f, 1.0f);
//drawing calls
const wchar_t* filename = L"\environmentsketches 02.png";
//this creates a Sprites object getGraphix returns the a pointer to the graphics object
//only really used to get the ID2D1RenderTarget but I may use it for other things, will
//remove later if not needed and just pass the render target unless issues arise.
testsprite = new Sprites(filename, wnd.GFX().getGraphix());
bob = D2D1::RectF(
0.0f, 0.0f, wnd.getwindowWidth(),
wnd.getwindowHeight());
//This draws the bitmap of a predetermined portion of the image but uses bob to
// to determine where to draw the bitmap
testsprite->Draw(bob);
bob = D2D1::RectF(
0.0f, 0.0f, wnd.getwindowWidth()/(16.0f/9.0f),
wnd.getwindowHeight());
testsprite->Draw(bob);
filename= L"\envrioment sketch march 1.png";
bob = D2D1::RectF(
100.0f, 100.0f, 600.f,
300.f);
testsprite2 = new Sprites(filename, wnd.GFX().getGraphix());
testsprite2->Draw(bob);
//EndFrame just calls present on the IDXGISwapChain
wnd.GFX().EndFrame();
testsprite->~Sprites();
testsprite2->~Sprites();
If you read through this thank you and thank you for any advice you have to offer.
Don't code late or tired. So first thing I realized is one when posting the code here I forgot to include the call for SwapChain->ResizeBuffers(0,width,height, DXGI_FORMAT_UNKNOWN, 0)) which is where the mistake was. I changed my pointers from smart to regular to manage their release manually for this, but the issue was more so that the last parameter wasn't 0, it was D3D11_CREATE_DEVICE_BGRA_SUPPORT instead of a proper swapchain flag and was reading as another (I believe DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY) which I couldn't use because of how I made my swapchain resulting in an error and it just not running.
In all the solution to my problem was to release my render targets (I already released by buffers after I made the targets), resize the buffers and then remake the ID2D1RenderTarget. Just don't put a wrong flag in, (or make sure the call is in when posting it for feedback to the mistake might be caught by others.)

Why can't i set a thumbnail on the Taskbar using DwmSetIconicThumbnail?

I need your help for a project at my job.
The main software, using 4D language, works as a MDI, it creates many windows included in the main window. The main issue is that we have a lot of windows and we need an easy way to switch from a window to another.
We decided to create a little c++ plugin which is a dll to resolve this issue.
This plugin will create a tab on the taskbar for each window opened like Windows Explorer. The creation and the deletion of tabs already works.
But the current problem is that no thumbnail is set on the Tab.
The parameter given is the ID of the window from the main software. The method called PA_GetHWND is the method given by 4D to obtain the handle of the window using the windowID.
I have already check where is the problem. The bitmap created from the window already exists and is good. For this test, i put the bitmap in the clipboard and paste it on Paint and the Bitmap was good.
Here is the code introducing the method to refresh the bitmap on the tab.
bool CManageTaskBar::UpdateWindow(long WindowID)
{
HRESULT res;
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
//Get the Handle from 4D
HWND nHandle = (HWND)(PA_GetHWND((PA_WindowRef)(WindowID)));
// Retrieve the handle to a display device context for the client
// area of the window.
hdcScreen = GetDC(NULL);
hdcWindow = GetDC(nHandle);
// Create a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC(hdcWindow);
// Get the client area for size calculation
RECT rcClient;
GetClientRect(nHandle, &rcClient);
// Create a compatible bitmap from the Window DC
hbmScreen = CreateCompatibleBitmap(hdcWindow,rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
// 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(nHandle, L"BitBlt has failed", L"Failed", MB_OK);
//goto done;
}
ITaskbarList3* ptbl = NULL;
HRESULT hr = CoCreateInstance(my_CLSID_TaskbarList, NULL, CLSCTX_ALL, my_IID_ITaskbarList3, (LPVOID*)&ptbl);
BOOL fForceIconic = TRUE;
BOOL fHasIconic = TRUE;
res = DwmSetWindowAttribute(nHandle, DWMWA_FORCE_ICONIC_REPRESENTATION, &fForceIconic, sizeof(fForceIconic));
res = DwmSetWindowAttribute(nHandle, DWMWA_HAS_ICONIC_BITMAP, &fHasIconic, sizeof(fHasIconic));
if (hbmScreen)
{
res = DwmSetIconicThumbnail(nHandle, hbmScreen,0);//DWM_SIT_DISPLAYFRAME);
}
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL, hdcScreen);
ReleaseDC(nHandle, hdcWindow);
return true;
}
The calls to DwmSetWindowAttribute return Invalid Handle. This handle works to get a bitmap but not to set an attribute.
And the call to DwmSetIconicThumbnail returns E_INVALIDARG maybe because the handle given is wrong.
Why cannot I set an attribute to this handle and why the call to set the thumbnail returns E_INVALIDARG ?
Thanks to everyone who will take care of my problem.
It's my first question, be friendly please :)

WIC Direct2D CreateBitmapFromMemory: limitations on width and height?

CreateBitmapFromMemory executes successfully when _nWidth is equal to or less than 644.
If the value exceeds this value, the HRESULT value is -2003292276
Do limits exist on the width and height?
#include <d2d1.h>
#include <d2d1helper.h>
#include <wincodecsdk.h> // Use this for WIC Direct2D functions
void test()
{
IWICImagingFactory *m_pIWICFactory;
ID2D1Factory *m_pD2DFactory;
IWICBitmap *m_pEmbeddedBitmap;
ID2D1Bitmap *m_pD2DBitmap;
unsigned char *pImageBuffer = new unsigned char[1024*1024];
HRESULT hr = S_OK;
int _nHeight = 300;
int _nWidth = 644;
If nWidth exceeds 644, CreateBitmapFromMemory returns an Error.
//_nWidth = 648;
if (m_pIWICFactory == 0 )
{
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
// Create WIC factory
hr = CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_pIWICFactory)
);
if (SUCCEEDED(hr))
{
// Create D2D factory
hr = D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory );
}
}
hr = m_pIWICFactory->CreateBitmapFromMemory(
_nHeight, // height
_nWidth, // width
GUID_WICPixelFormat24bppRGB, // pixel format of the NEW bitmap
_nWidth*3, // calculated from width and bpp information
1024*1024, // height x width
pImageBuffer, // name of the .c array
&m_pEmbeddedBitmap // pointer to pointer to whatever an IWICBitmap is.
);
if (!SUCCEEDED(hr)) {
char *buffer = "Error in CreateBitmapFromMemory\n";
}
}
Error code is 0x88982F8C WINCODEC_ERR_INSUFFICIENTBUFFER and the reason is now obvious?
The first parameter is width, and the second is height. You have them in wrong order. All in all you provide incorrect arguments resulting in bad buffer.
Are you sure you passed in the correct pixelFormat for function CreateBitmapFromMemory? you hard code it to GUID_WICPixelFormat24bppRGB, I think this is the root cause, you should make sure this format same as the format with the source bitmap which you are copy the data from. try use the GetPixelFormat function to get the correct format instead of hard code.
There is an upper limit on the dimensions of images on the GPU.
Call GetMaximumBitmapSize on the render target.
http://msdn.microsoft.com/query/dev11.query?appId=Dev11IDEF1&l=EN-US&k=k(GetMaximumBitmapSize);k(DevLang-C%2B%2B);k(TargetOS-Windows)&rd=true
What you get back is the max pixels of either vertical or horiz.
For larger images you'd have to load them into a software render target such as a bitmap render target and then render what you want from that.

Why is GetClientRect including the window border and title bar?

I'm writing some code that will take a screenshot of another application, given its' window handle, in C++. The method I am using is to use BitBlt. My application is successfully taking the screenshot and I have a function that will save that image data out to a bmp file.
The screenshot contains the window's chrome though. That is, the border and the title bar. Based on my understanding, GetClientRect is supposed to exclude the window's border and title bar. I understand that GetWindowRect returns the coordinates within the user's desktop, and GetClientRect returns the coordinates relative to the application itself.
I notice in my screenshots that the title bar and left border are visible, but the right border and bottom of the application are cut off. So, I'm thinking that if I want to exclude the title and borders then I need to be doing some sort of combination of GetWindowRect and GetClientRect, and using information about the window itself to offset the GetClientRect dimensions by whatever the height of the window's title bar is, for example.
Does this sound accurate, or is my code below doing something wrong?
#include <Windows.h>
#include "ScreenshotManager.h"
namespace Managers {
ScreenshotManager::ScreenshotManager(HWND gameHandle) {
// get a device context for the window
m_gameContext = GetWindowDC(gameHandle);
// create a compatible device context for bitblt
m_bitmapContext = CreateCompatibleDC(m_gameContext);
// get window client area dimensions
GetClientRect(gameHandle, &m_gameClientArea);
}
bool ScreenshotManager::TakeScreenshot() {
// create a compatible bitmap for the game screenshots
m_bitmap = CreateCompatibleBitmap(m_gameContext, m_gameClientArea.right, m_gameClientArea.bottom);
// select the bitmap into the compatible device context
SelectObject(m_bitmapContext, m_bitmap);
// perform bit block transfer
if (BitBlt(m_bitmapContext, 0, 0, m_gameClientArea.right, m_gameClientArea.bottom, m_gameContext, 0, 0, SRCCOPY) == false)
return false;
// get information about the taken screenshot
GetObject(m_bitmap, sizeof(BITMAP), &m_bitmapInformation);
return true;
}
void ScreenshotManager::SaveScreenshot(LPCWSTR outputPath) {
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = m_bitmapInformation.bmWidth;
bi.biHeight = m_bitmapInformation.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 = ((m_bitmapInformation.bmWidth * bi.biBitCount + 31) / 32) * 4 * m_bitmapInformation.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(m_gameContext, m_bitmap, 0, (UINT)m_bitmapInformation.bmHeight, lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
// A file is created, this is where we will save the screen capture.
HANDLE hFile = CreateFile(outputPath, 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);
}
}
GetClientRect() does not include the border and title bar. All it does is tell you the dimensions of the client area.
BitBlt() copies a rectangular region of pixels from one device context to another. In this example the source DC is a window DC, so the origin coordinates are relative to that window.
What your code is doing is copying a client-sized rectangle from the window’s origin. (That’s why the right and bottom edges go missing.)
You might be interested in AdjustWindowRectEx() to help identify the coordinates of the region you want to copy.

Windows 7 and ScreenShot.cpp GDI+ PNG problemo

was using XP without issue for a long time. switched to 7 and trying to capture screenshots with my previously functioning code no longer works. simple concept and relatively generic code...just find the window that i call and save it as a .png. any ideas what might make this bad boy run again? can't debug with my current setup, but it makes it all the way and spits out the error message after bmp->save(...) ...couldn't save image file. edit: also a file does get created/saved, but it is blank and not written to. perhaps the bitmap encoding or GDI is screwed up?
bool CScreenShot::Snap(CString wintitle, CString file, CString& ermsg)
{
ermsg = ""; // no error message
// create screen shot bitmap
EnumWinProcStruct prm = {0, (LPSTR)(LPCTSTR)wintitle, 0};
// Find the descriptor of the window with the caption wintitle
EnumDesktopWindows(0, EnumWindowsProc, (LPARAM)&prm);
if(!prm.hwnd)
{
ermsg.Format("couldn't find window \"%s\"", wintitle);
return false;
}
// Make the window the topmost window
SetWindowPos(prm.hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
Sleep(300);
// Get device context for the top-level window and client rect
HDC hDC = GetDC(prm.hwnd);
RECT rc;
GetClientRect(prm.hwnd, &rc);
HDC memDC = CreateCompatibleDC(hDC);
// Set the size and color depth for the screen shot image
BITMAPINFO bmpInfo;
memset(&bmpInfo, 0, sizeof(bmpInfo));
bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);
bmpInfo.bmiHeader.biWidth = rc.right - rc.left;
bmpInfo.bmiHeader.biHeight = rc.bottom - rc.top;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biBitCount = 24;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biSizeImage = bmpInfo.bmiHeader.biWidth * bmpInfo.bmiHeader.biHeight * 3;
// Create memory buffer and perform a bit-block transfer of the color data from the window to the memory
LPVOID addr;
HBITMAP memBM = CreateDIBSection(memDC, &bmpInfo, DIB_RGB_COLORS, &addr, 0, 0);
HGDIOBJ stdBM = SelectObject(memDC, memBM);
BOOL OK = BitBlt(memDC, 0, 0, bmpInfo.bmiHeader.biWidth, bmpInfo.bmiHeader.biHeight, hDC, 0, 0, SRCCOPY);
ReleaseDC(prm.hwnd, hDC);
SetWindowPos(prm.hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
if(GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) != Ok)
{
ermsg.Format("couldn't start GDI+");
return false;
}
// Create a Bitmap object for work with images defined by pixel data from the GDI HBitmap and the GDI HPalette.
Bitmap* bmp = ::new Bitmap(memBM, DIB_RGB_COLORS);
SelectObject(memDC, stdBM);
DeleteObject(memBM);
DeleteDC(memDC);
// Find the encoder for "image/png" mime type
CLSID encoderClsid;
EncoderParameters encoderParameters;
GetEncoderClsid(L"image/png", &encoderClsid);
encoderParameters.Count = 0;
// Convert file name to Unicode (wide-char) string.
WCHAR fn[_MAX_PATH];
MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, file, file.GetLength() + 1, fn, _MAX_PATH);
// Save the screen shot into the specified file using image encoder with the mime style "image/png"
if(bmp->Save(fn, &encoderClsid, &encoderParameters) != Ok)
{
ermsg.Format("couldn't save image file \"%s\"", file);
return false;
}
::delete bmp;
GdiplusShutdown(gdiplusToken);
return true;
}
The error message implies that you're trying to save the file to a folder that you don't have permission to write to. Many folders such as Program Files are now protected. Since you didn't include the path in your sample code I'm unable to determine if this is the actual problem.
Edit: Another possibility is that the Bitmap is improperly constructed which causes the Save to fail. The second parameter to the constructor is supposed to be a handle to a palette, I think DIB_RGB_COLORS would be invalid here and you should use NULL. Also there are a couple of caveats noted in the Microsoft documentation and perhaps the different OS versions react differently when you break the rules:
You are responsible for deleting the GDI bitmap and the GDI palette. However, you should not delete the GDI bitmap or the GDI palette until after the GDI+ Bitmap::Bitmap object is deleted or goes out of scope.
Do not pass to the GDI+ Bitmap::Bitmap constructor a GDI bitmap or a GDI palette that is currently (or was previously) selected into a device context.
win7 won't accept encoderParameters.Count == 0 for some reason. Set that == 1 and you should be all set.
you probably could also just remove that parameter from Save() (overloaded)