I want to display a raw BGRA data (so I have an unsigned int *, with width and height) on a window so that the alpha component is taken into account to have per-pixel transparency. The final purpose is to integrate this in a graphic toolkit.
after reading Kenny Kerr's article (https://learn.microsoft.com/en-us/archive/msdn-magazine/2014/june/windows-with-c-high-performance-window-layering-using-the-windows-composition-engine) I think that it can solve my problem.
I have already written a complete code that does what the article described (transparent window using Windows Composition engine in c++) : transparent window with a translucent disk
So, basically, what I am doing now is :
get the buffer (a surface) from the swapchain
get a bitmap with CreateBitmapFromDxgiSurface
I allocate a BGRA test data
use CopyFromMemory to copy my BGRA data onto the bitmap
between BeginDraw and EndDraw, I call DrawBitmap
But whatever flag I use for the properties' bitmapOptions passed to CreateBitmapFromDxgiSurface, the app crashes and the D2D debug layer mentions an error like : "warning: D2D DEBUG ERROR - The bitmap options [0x1] must be a subset of the flags associated with the DXGI surface."
the complete code is here :
#include <iostream>
#ifdef _WIN32_WINNT
# undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0A00
#include <guiddef.h>
#include <d2d1_1.h>
#include <d3d11.h>
#include <dxgi1_3.h>
#include <dcomp.h>
static HINSTANCE instance = NULL;
static HWND win = NULL;
LRESULT CALLBACK _window_procedure(HWND window,
UINT message,
WPARAM window_param,
LPARAM data_param);
typedef struct
{
ID3D11Device *d3d_device;
IDXGIDevice *dxgi_device;
IDXGIFactory2 *dxgi_factory;
ID2D1Factory1 *d2d_factory;
ID2D1Device *d2d_device;
ID2D1DeviceContext *d2d_device_ctx;
IDCompositionDevice *dcomp_device;
IDCompositionVisual *dcomp_visual;
IDCompositionTarget *dcomp_target;
IDXGISwapChain1 *dxgi_swapchain;
} D2d;
int d2d_init(HWND window, D2d *d2d)
{
const D3D_FEATURE_LEVEL levels[] =
{
D3D_FEATURE_LEVEL_12_1,
D3D_FEATURE_LEVEL_12_0,
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0
};
D2D1_FACTORY_OPTIONS opt;
DXGI_SWAP_CHAIN_DESC1 desc;
RECT r;
HRESULT res;
/* direct3d device */
res = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL,
D3D11_CREATE_DEVICE_BGRA_SUPPORT |
D3D11_CREATE_DEVICE_DEBUG,
levels, sizeof(levels) / sizeof(D3D_FEATURE_LEVEL),
D3D11_SDK_VERSION, &d2d->d3d_device, NULL, NULL);
if (FAILED(res))
return 0;
/* dxgi device */
res = d2d->d3d_device->QueryInterface(&d2d->dxgi_device);
if (FAILED(res))
goto release_d3d_device;
/* dxgi factory */
res = CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG,
__uuidof(d2d->dxgi_factory),
(void **)&d2d->dxgi_factory);
if (FAILED(res))
goto release_dxgi_device;
/* d2d factory */
opt.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(d2d->d2d_factory), &opt,
(void **)&d2d->d2d_factory);
if (FAILED(res))
goto release_dxgi_factory;
/* d2d device */
res = d2d->d2d_factory->CreateDevice(d2d->dxgi_device, &d2d->d2d_device);
if (FAILED(res))
goto release_d2d_factory;
/* d2d device context */
// FIXME : D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS
res = d2d->d2d_device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
&d2d->d2d_device_ctx);
if (FAILED(res))
goto release_d2d_device;
/* dcomp device */
res = DCompositionCreateDevice(d2d->dxgi_device,
__uuidof(d2d->dcomp_device),
(void **)&d2d->dcomp_device);
if (FAILED(res))
goto release_d2d_device_ctx;
/* dcomp visual */
res = d2d->dcomp_device->CreateVisual(&d2d->dcomp_visual);
if (FAILED(res))
goto release_dcomp_device;
/* dcomp target */
res = d2d->dcomp_device->CreateTargetForHwnd(window, TRUE,
&d2d->dcomp_target);
if (FAILED(res))
goto release_dcomp_visual;
/* dxgi swapchain */
if (!GetClientRect(window, &r))
goto release_dcomp_target;
desc.Width = r.right - r.left; /* width of client area */
desc.Height = r.bottom - r.top; /* height of client area */
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.Stereo = FALSE;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = 2;
desc.Scaling = DXGI_SCALING_STRETCH;
desc.SwapEffect= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
desc.Flags = 0;
res = d2d->dxgi_factory->CreateSwapChainForComposition(d2d->dxgi_device,
&desc,
NULL,
&d2d->dxgi_swapchain);
if (FAILED(res))
goto release_dcomp_target;
return 1;
release_dcomp_target:
d2d->dcomp_target->Release();
release_dcomp_visual:
d2d->dcomp_visual->Release();
release_dcomp_device:
d2d->dcomp_device->Release();
release_d2d_device_ctx:
d2d->d2d_device_ctx->Release();
release_d2d_device:
d2d->d2d_device->Release();
release_d2d_factory:
d2d->d2d_factory->Release();
release_dxgi_factory:
d2d->dxgi_factory->Release();
release_dxgi_device:
d2d->dxgi_device->Release();
release_d3d_device:
d2d->d3d_device->Release();
return 0;
}
void d2d_shutdown(D2d *d2d)
{
d2d->dxgi_swapchain->Release();
d2d->dcomp_target->Release();
d2d->dcomp_visual->Release();
d2d->dcomp_device->Release();
d2d->d2d_device_ctx->Release();
d2d->d2d_device->Release();
d2d->d2d_factory->Release();
d2d->dxgi_factory->Release();
d2d->dxgi_device->Release();
d2d->d3d_device->Release();
}
void render(D2d *d2d)
{
IDXGISurface *surface;
ID2D1Bitmap1 *bitmap;
D2D1_BITMAP_PROPERTIES1 properties;
D2D1_COLOR_F c;
HRESULT res;
std::cout << "render" << std::endl;
/* swapchain buffer in a IDXGISurface */
res = d2d->dxgi_swapchain->GetBuffer(0, __uuidof(surface),
(void **)&surface);
if (FAILED(res))
return;
std::cout << "render 1" << std::endl;
properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
properties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
properties.dpiX = 96;
properties.dpiY = 96;
// which value to set here ??
properties.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
properties.colorContext = NULL;
res = d2d->d2d_device_ctx->CreateBitmapFromDxgiSurface(surface,
&properties,
&bitmap);
if (FAILED(res))
goto release_surface;
std::cout << "render 2" << std::endl;
d2d->d2d_device_ctx->SetTarget(bitmap);
std::cout << "render 3" << std::endl;
unsigned int *data, *iter;
unsigned int i, j;
D2D1_SIZE_U s;
D2D1_RECT_U r;
// BGRA test data
s = bitmap->GetPixelSize();
data = (unsigned int *)malloc(s.width * s.height * sizeof(unsigned int));
if (!data)
{
std::cout << "malloc fails" << std::endl;
goto bitmap_release;
}
iter = data;
for (i = 0; i < s.height; i++)
{
for (j = 0; j < s.width; j++, iter++)
{
if (i >= 50 && j>= 50)
*iter = 0xff0000ff;
else
*iter = 0xffff0000;
}
}
r.left = 0;
r.top = 0;
r.right = s.width;
r.bottom = s.height;
std::cout << "render 4" << std::endl;
res = bitmap->CopyFromMemory(&r, data, 4 * s.width);
if (FAILED(res))
{
std::cout << "copy failed" << std::endl;
goto data_free;
}
std::cout << "render 5" << std::endl;
d2d->d2d_device_ctx->BeginDraw();
// c.r = 0.18f;
// c.g = 0.55f;
// c.b = 0.34f;
// c.a = 0.75f;
c.r = 0.0f;
c.g = 0.0f;
c.b = 0.0f;
c.a = 0.0f;
d2d->d2d_device_ctx->Clear(&c);
std::cout << "render 6" << std::endl;
D2D1_RECT_F rect;
rect.left = 0.0f;
rect.top = 0.0f;
rect.right = (float)s.width;
rect.bottom = (float)s.height;
d2d->d2d_device_ctx->DrawBitmap(bitmap, rect, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, rect);
std::cout << "render 7" << std::endl;
d2d->d2d_device_ctx->EndDraw(NULL, NULL);
d2d->dxgi_swapchain->Present(1, 0);
d2d->dcomp_visual->SetContent(d2d->dxgi_swapchain);
d2d->dcomp_target->SetRoot(d2d->dcomp_visual);
d2d->dcomp_device->Commit();
data_free:
free(data);
bitmap_release:
bitmap->Release();
release_surface:
surface->Release();
}
LRESULT CALLBACK
_window_procedure(HWND window,
UINT message,
WPARAM window_param,
LPARAM data_param)
{
switch (message)
{
case WM_CLOSE:
PostQuitMessage(0);
return 0;
case WM_KEYUP:
if (window_param == 'Q')
{
PostQuitMessage(0);
}
if (window_param == 'T')
{
std::cout << "transp" << std::endl;
}
return 0;
/* GDI notifications */
case WM_CREATE:
return 0;
case WM_PAINT:
{
RECT rect;
D2d *d2d;
d2d = (D2d *)GetWindowLongPtr(window, GWLP_USERDATA);
std::cout << "paint" << std::endl;
if (GetUpdateRect(window, &rect, FALSE))
{
PAINTSTRUCT ps;
BeginPaint(window, &ps);
render(d2d);
EndPaint(window, &ps);
}
return 0;
}
default:
return DefWindowProc(window, message, window_param, data_param);
}
}
int main()
{
/* class */
WNDCLASS wc;
instance = GetModuleHandle(NULL);
if (!instance)
return 1;
memset (&wc, 0, sizeof (WNDCLASS));
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = _window_procedure;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = instance;
wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(1 + COLOR_BTNFACE);
wc.lpszMenuName = NULL;
wc.lpszClassName = "D2D";
if(!RegisterClass(&wc))
goto free_library;
/* Window */
int w;
int h;
RECT r;
DWORD style;
DWORD exstyle;
w = 640;
h = 480;
style = WS_OVERLAPPEDWINDOW | WS_SIZEBOX;
exstyle = WS_EX_NOREDIRECTIONBITMAP;
r.left = 0;
r.top = 0;
r.right = w;
r.bottom = h;
if (!AdjustWindowRectEx(&r, style, FALSE, exstyle))
goto unregister_class;
win = CreateWindowEx(exstyle,
"D2D", "Test",
style,
100, 100,
r.right - r.left,
r.bottom - r.top,
NULL,
NULL, instance, NULL);
if (!win)
goto unregister_class;
/* d2d init */
D2d d2d;
if (!d2d_init(win, &d2d))
{
std::cout << "d2d init failed" << std::endl;
goto destroy_win;
}
SetWindowLongPtr(win, GWLP_USERDATA, (LONG_PTR)&d2d);
ShowWindow(win, SW_SHOWNORMAL);
UpdateWindow(win);
/* msg loop */
while(1)
{
MSG msg;
BOOL ret;
ret = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
if (ret)
{
do
{
if (msg.message == WM_QUIT)
goto beach;
TranslateMessage(&msg);
DispatchMessageW(&msg);
} while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
}
}
beach:
d2d_shutdown(&d2d);
DestroyWindow(win);
UnregisterClass("D2D", instance);
FreeLibrary(instance);
std::cout << "exiting..." << std::endl;
return 0;
destroy_win:
DestroyWindow(win);
unregister_class:
UnregisterClass("D2D", instance);
free_library:
FreeLibrary(instance);
std::cout << "exiting 2..." << std::endl;
return 1;
}
after 1 week of search on internet, especially on msdn, i couldn't found a solution. I have tried several combination of flags, but always a crash of this program.
Does someone know what is wrong ?
thank you
Bitmaps (D2D1 or others in GPU context) are "just" GPU resources that can serve very different purposes.
Your error comes from the fact you're confusing the target bitmap for the device context with a bitmap you want to write on the device context.
The first bitmap is just here to express the target for the device context is in fact the DXGI surface.
The second bitmap is used to write some pixels on the device context (which in turn will land on the DXGI surface).
So you need to change your code. First, just connect the device context (=render target) to the DXGI surface, like this:
void setBackBuffer(D2d* d2d)
{
IDXGISurface* surface;
ID2D1Bitmap1* bitmap;
HRESULT res = d2d->dxgi_swapchain->GetBuffer(0, __uuidof(surface), (void**)&surface);
if (FAILED(res))
return;
res = d2d->d2d_device_ctx->CreateBitmapFromDxgiSurface(surface, NULL, &bitmap);
if (FAILED(res))
goto release_surface;
d2d->d2d_device_ctx->SetTarget(bitmap);
bitmap->Release();
release_surface:
surface->Release();
}
And add that just after your call to CreateSwapChainForComposition.
After that, modify your render method like this for example:
void render(D2d* d2d)
{
ID2D1Bitmap1* bitmap;
D2D1_BITMAP_PROPERTIES1 properties;
DXGI_SWAP_CHAIN_DESC1 desc;
D2D1_COLOR_F c;
HRESULT res;
std::cout << "render" << std::endl;
// get size from swapchain (for example)
d2d->dxgi_swapchain->GetDesc1(&desc);
D2D1_SIZE_U s = { desc.Width, desc.Height };
unsigned int* data, * iter;
unsigned int i, j;
// BGRA test data
data = (unsigned int*)malloc(s.width * s.height * sizeof(unsigned int));
if (!data)
{
std::cout << "malloc fails" << std::endl;
return;
}
iter = data;
for (i = 0; i < s.height; i++)
{
for (j = 0; j < s.width; j++, iter++)
{
if (i >= 50 && j >= 50)
*iter = 0xff0000ff;
else
*iter = 0xffff0000;
}
}
std::cout << "render 4" << std::endl;
// create a bitmap from properties & pixel buffer
// hint: in general for most structures, it's much easier to use ZeroMemory or memset(0) so by default values are automatically set
ZeroMemory(&properties, sizeof(properties));
properties.pixelFormat.format = desc.Format;
properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
res = d2d->d2d_device_ctx->CreateBitmap(s, data, 4 * s.width, properties, &bitmap);
if (FAILED(res))
{
std::cout << "create failed" << std::endl;
goto data_free;
}
std::cout << "render 5" << std::endl;
d2d->d2d_device_ctx->BeginDraw();
c.r = 0.0f;
c.g = 0.0f;
c.b = 0.0f;
c.a = 0.0f;
d2d->d2d_device_ctx->Clear(&c);
std::cout << "render 6" << std::endl;
D2D1_RECT_F rect;
rect.left = 0.0f;
rect.top = 0.0f;
rect.right = (float)s.width;
rect.bottom = (float)s.height;
d2d->d2d_device_ctx->DrawBitmap(bitmap, rect, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, rect);
std::cout << "render 7" << std::endl;
d2d->d2d_device_ctx->EndDraw(NULL, NULL);
d2d->dxgi_swapchain->Present(1, 0);
d2d->dcomp_visual->SetContent(d2d->dxgi_swapchain);
d2d->dcomp_target->SetRoot(d2d->dcomp_visual);
d2d->dcomp_device->Commit();
bitmap->Release();
data_free:
free(data);
}
Related
I am receiving those screenshots in WM_PAINT, and then save it to memory. The screenshots have around 8-10kB size. And I am trying to display those screenshots in a 15 FPS, but the problem is that its displaying sometimes glitched screenshots like this:
[the gray square with dots inside it, that should not be there, its covering the half of the screenshot :c ]
Image displayed like this, is like every second or third screenshot. I have no idea how to fix this im stuck a couple days at this.
Here is the server code (window procedure) where i am trying to display these screenshots
LRESULT CALLBACK ClientWndProc(HWND Chwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
std::vector<char> buffer(50000);
switch (uMsg)
{
case WM_CREATE:
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
SetTimer(Chwnd, SCREEN_TIMER, 1000/15, NULL);
break;
}
case WM_TIMER:
{
if (wParam == SCREEN_TIMER)
{
SOCKET selectedSocket = clientSockets[itemIndex];
auto it = std::find(clientSockets.begin(), clientSockets.end(), selectedSocket);
send(selectedSocket, "capscr", strlen("capscr"), 0);
InvalidateRect(Chwnd, NULL, TRUE);
}
break;
}
case WM_PAINT:
{
SOCKET selectedSocket = clientSockets[itemIndex];
auto it = std::find(clientSockets.begin(), clientSockets.end(), selectedSocket);
if (it != clientSockets.end())
{
int bytesReceived = 0;
int totalBytesReceived = 0;
int expectedBytes = buffer.size();
Sleep(1000/15);
do
{
bytesReceived = recv(selectedSocket, buffer.data() + totalBytesReceived, buffer.size() - totalBytesReceived, 0);
totalBytesReceived += bytesReceived;
} while (totalBytesReceived < expectedBytes && bytesReceived > 0);
//MessageBoxA(NULL, buffer.data(), "s", MB_OK);
HGLOBAL hGlobal = GlobalAlloc(GHND, expectedBytes);
void* pData = GlobalLock(hGlobal);
memcpy(pData, buffer.data(), buffer.size());
GlobalUnlock(hGlobal);
IStream* pStream = NULL;
CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);
Gdiplus::Bitmap bitmap(pStream);
Gdiplus::Status status = bitmap.GetLastStatus();
PAINTSTRUCT ps;
HDC hdc = BeginPaint(Chwnd, &ps);
int imgWidth = bitmap.GetWidth();
int imgHeight = bitmap.GetHeight();
Gdiplus::Graphics graphics(hdc);
RECT clientRect;
GetClientRect(Chwnd, &clientRect);
graphics.DrawImage(&bitmap, 0, 0, imgWidth, imgHeight);
EndPaint(Chwnd, &ps);
GlobalFree(hGlobal);
}
break;
}
case WM_ERASEBKGND:
return TRUE;
case WM_CLOSE:
{
DestroyWindow(hwndClient);
break;
}
case WM_DESTROY:
DestroyWindow(hwndClient);
return 0;
default:
return DefWindowProc(Chwnd, uMsg, wParam, lParam);
}
return 0;
}
client code sending screenshots:
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return 0;
}
void shutdownGdiPlus()
{
Gdiplus::GdiplusShutdown(gdiPlusToken);
gdiPlusToken = NULL;
}
bool initGdiPlusIfNeeded()
{
// If already initialized then return true
if (gdiPlusToken != NULL)
return true;
static Gdiplus::GdiplusStartupInput gdiPlusStartupInput;
return (success = GdiplusStartup(&gdiPlusToken, &gdiPlusStartupInput, NULL)) == Gdiplus::Status::Ok;
}
std::pair<ULONGLONG, char*> getScreenShotAsByteArray()
{
if (!initGdiPlusIfNeeded())
return {};
IStream* iStream;
HRESULT res = CreateStreamOnHGlobal(NULL, true, &iStream);
const HDC srcDC = ::GetDC(NULL);
const int screenHeight = GetSystemMetrics(SM_CYSCREEN);
const int screenWidth = GetSystemMetrics(SM_CXSCREEN);
const HDC memDC = CreateCompatibleDC(srcDC);
const HBITMAP membit = CreateCompatibleBitmap(srcDC, screenWidth, screenHeight);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(memDC, membit);
BitBlt(memDC, 0, 0, screenWidth, screenHeight, srcDC, 0, 0, SRCCOPY);
// Create a bitmap to store the previous screenshot
HBITMAP prevBitmap = CreateCompatibleBitmap(srcDC, screenWidth, screenHeight);
HDC prevDC = CreateCompatibleDC(srcDC);
SelectObject(prevDC, prevBitmap);
// Get the size of the bitmaps in bytes
BITMAP bmp;
GetObject(prevBitmap, sizeof(BITMAP), &bmp);
int prevBmpSize = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
GetObject(membit, sizeof(BITMAP), &bmp);
int currBmpSize = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
// Allocate memory for the bitmap data
char* prevBmpData = new char[prevBmpSize];
char* currBmpData = new char[currBmpSize];
// Get the raw pixel data of the bitmaps
GetBitmapBits(prevBitmap, prevBmpSize, prevBmpData);
GetBitmapBits(membit, currBmpSize, currBmpData);
// Compare the bitmap data
bool isDifferent = memcmp(prevBmpData, currBmpData, currBmpSize) != 0;
// Free the allocated memory
delete[] prevBmpData;
delete[] currBmpData;
// Check if the current screenshot is different from the previous screenshot
if (isDifferent)
{
// Screenshot is different, take a new screenshot
int newScreenWidth = 700;
int newScreenHeight = 500;
Gdiplus::Bitmap fullScreenBitmap(membit, NULL);
Gdiplus::Bitmap newBitmap(newScreenWidth, newScreenHeight);
Gdiplus::Graphics graphics(&newBitmap);
graphics.DrawImage(&fullScreenBitmap, 0, 0, newScreenWidth, newScreenHeight);
CLSID clsid;
GetEncoderClsid(L"image/jpeg", &clsid);
ULONG quality = 0;
EncoderParameters encoderParams;
encoderParams.Count = 1;
encoderParams.Parameter[0].Guid = EncoderQuality;
encoderParams.Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParams.Parameter[0].NumberOfValues = 1;
encoderParams.Parameter[0].Value = &quality;
newBitmap.Save(iStream, &clsid, &encoderParams);
ULARGE_INTEGER pos{ 0, 0 };
const LARGE_INTEGER pos2{ 0, 0 };
iStream->Seek(pos2, STREAM_SEEK_SET, &pos);
ULONG bytesWritten = 0;
STATSTG statstg;
iStream->Stat(&statstg, STATFLAG_DEFAULT);
const ULONGLONG bufferLen = statstg.cbSize.QuadPart;
char* imageBuffer = new char[bufferLen];
iStream->Read(imageBuffer, bufferLen, &bytesWritten);
iStream->Release();
DeleteObject(memDC);
DeleteObject(membit);
::ReleaseDC(NULL, srcDC);
std::pair<ULONGLONG, char*> result(bufferLen, imageBuffer);
return result;
}
else
{
return {};
}
}
void sendScreens() {
if (clientsocket == INVALID_SOCKET) {
// handle error, the socket is not valid or not connected
return;
}
std::pair<ULONGLONG, char*> image = getScreenShotAsByteArray();
ULONGLONG bufferLen = image.first;
char* imageBuffer = image.second;
int bytesSent = send(clientsocket, imageBuffer, bufferLen, 0);
if(bytesSent < 0)
{
return;
}
std::cout << "Sent Bytes: " << bytesSent << "\n";
// wait for the desired interval before sending the next screenshot
std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::time_point end = start + std::chrono::milliseconds(FPS);
std::this_thread::sleep_until(end);
}
I am using this code to capture screen and cursor.
// ConsoleApplication1.cpp : This file contains the 'main' function. Program execution begins and ends there.
#include <Windows.h>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace std;
using namespace cv;
HWND hwndDesktop = NULL;
bool flg;
CURSORINFO cursor;
ICONINFOEXW info;
BITMAP bmpCursor;
HDC hwindowDC, hwindowCompatibleDC;
HBITMAP hbwindow;
int height, width, srcheight, srcwidth;
Mat src;
BITMAPINFOHEADER bi;
RECT windowsize;
Mat hwnd2mat(HWND hwnd)
{
src.create(height, width, CV_8UC4);
int val = 0;
// use the previously created device context with the bitmap
HGDIOBJ hPrevBitmap = SelectObject(hwindowCompatibleDC, hbwindow);
// copy from the window device context to the bitmap device context
val = StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, 0, 0, srcwidth, srcheight, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !
if (val <= 0) {
DWORD error_ = GetLastError();
cout << "error " << error_ << endl;
}
cursor = { sizeof(cursor) };
::GetCursorInfo(&cursor);
if (cursor.flags == CURSOR_SHOWING and flg) {
info = { sizeof(info) };
::GetIconInfoExW(cursor.hCursor, &info);
const int x = cursor.ptScreenPos.x - windowsize.left - info.xHotspot;
const int y = cursor.ptScreenPos.y - windowsize.top - info.yHotspot;
bmpCursor = { 0 };
val = ::GetObject(info.hbmColor, sizeof(bmpCursor), &bmpCursor);
val = ::DrawIconEx(hwindowCompatibleDC, x, y, cursor.hCursor, bmpCursor.bmWidth, bmpCursor.bmHeight,
0, NULL, DI_NORMAL);
if (val <= 0) {
DWORD error_ = GetLastError();
cout << "error " << error_ << endl;
flg = false;
}
}
val = GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, src.data, (BITMAPINFO *)&bi, DIB_RGB_COLORS); //copy from hwindowCompatibleDC to hbwindow
if (val <= 0) {
DWORD error_ = GetLastError();
cout << "error " << error_ << endl;
GdiFlush();
flg = false;
}
SelectObject(hwindowCompatibleDC, hPrevBitmap);
return src;
}
int main(int argc, char **argv)
{
flg = true;
hwndDesktop = GetDesktopWindow();
namedWindow("output", WINDOW_NORMAL);
int key = 0;
hwindowDC = GetDC(hwndDesktop);
if (hwindowDC == NULL) {
DWORD error_ = GetLastError();
cout << "error " << error_ << endl;
}
hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
if (hwindowCompatibleDC == NULL) {
DWORD error_ = GetLastError();
cout << "error " << error_ << endl;
}
SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);
GetClientRect(hwndDesktop, &windowsize);
srcheight = windowsize.bottom;
srcwidth = windowsize.right;
height = windowsize.bottom / 1; //change this to whatever size you want to resize to
width = windowsize.right / 1;
hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
if (hbwindow == NULL) {
DWORD error_ = GetLastError();
cout << "error " << error_ << endl;
}
bi.biSize = sizeof(BITMAPINFOHEADER); //http://msdn.microsoft.com/en-us/library/windows/window/dd183402%28v=vs.85%29.aspx
bi.biWidth = width;
bi.biHeight = -height; //this is the line that makes it draw upside down or not
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;
while (key != 27)
{
Mat src = hwnd2mat(hwndDesktop);
// you can do some image processing here
imshow("output", src);
key = waitKey(1); // you can change wait time
}
}
The Screen Capture works perfectly , but always fails after screen capture of just more than 8 mins and 30 seconds.
After 8 min and 30 seconds GetDIBits starts to return 0 .
I am running this on a 64bit machine with windows 10 professional ediiton.
This Issue seems to not apear if i remove the cursor capturing code .
DeleteObject(info.hbmColor);
DeleteObject(info.hbmMask);
Adding these lines did the trick.
Thanks to Raymond Chen and IInspectable
I am extending an old console application. Trying to add a window( for an animation ). Are able to display first image, but unable to update the window with later images. Have created a minimum application to demonstrate the problem. In this app I have a red window that should change at each update of the window( it is not ). What am I doing wrong ?
#include "pch.h"
#include <iostream>
#include <Windows.h>
using namespace std;
HWND hWindow;
LRESULT __stdcall MyWindowProcedure(HWND hWnd, unsigned int msg, WPARAM wp, LPARAM lp)
{
PAINTSTRUCT ps;
HDC hdc;
HBITMAP hBitmap;
VOID * pvBits;
RGBQUAD *bmiColors;
static int j = 128;
HBITMAP hOldBmp;
BITMAPINFO MyBitmap;
BOOL qRetBlit;
switch (msg)
{
case WM_DESTROY:
std::cout << "\ndestroying window\n";
PostQuitMessage(0);
return 0L;
case WM_LBUTTONDOWN:
std::cout << "\nmouse left button down at (" << LOWORD(lp)
<< ',' << HIWORD(lp) << ")\n";
// fall thru
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
#if 1
// Create a device context that is compatible with the window
HDC hLocalDC;
hLocalDC = ::CreateCompatibleDC(hdc);
// Verify that the device context was created
if (hLocalDC == NULL) {
printf("CreateCompatibleDC Failed\n");
// return false;
break;
}
MyBitmap.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
MyBitmap.bmiHeader.biWidth = 256;
MyBitmap.bmiHeader.biHeight = -256;
MyBitmap.bmiHeader.biPlanes = 1;
MyBitmap.bmiHeader.biBitCount = 32;
MyBitmap.bmiHeader.biCompression = BI_RGB;
MyBitmap.bmiHeader.biSizeImage = 256 * 256 * 4;
MyBitmap.bmiHeader.biYPelsPerMeter = 1000;
MyBitmap.bmiHeader.biXPelsPerMeter = 1000;
MyBitmap.bmiHeader.biClrUsed = 0;
MyBitmap.bmiHeader.biClrImportant = 0;
hBitmap = CreateDIBSection(NULL, &MyBitmap, DIB_RGB_COLORS, &pvBits, NULL, NULL);
bmiColors = (RGBQUAD*)pvBits;
// note: BGR
for (int i = 0; i < 256 * 256; i++)
{
bmiColors[i].rgbBlue = 0;
bmiColors[i].rgbGreen = 0;
bmiColors[i].rgbRed = j % 256;
bmiColors[i].rgbReserved = 255;
}
j += 10;
bmiColors[1000].rgbRed;
// Select the bitmap into the device context
hOldBmp = (HBITMAP)::SelectObject(hLocalDC, hBitmap);
if (hOldBmp == NULL) {
printf("SelectObject Failed");
// return false;
break;
}
// Blit the dc which holds the bitmap onto the window's dc
qRetBlit = ::BitBlt(hdc, 0, 0, 256, 256, hLocalDC, 0, 0, SRCCOPY);
if (!qRetBlit) {
printf("Blit Failed");
// return false;
break;
}
// Unitialize and deallocate resources
SelectObject(hLocalDC, hOldBmp);
DeleteDC(hLocalDC);
DeleteObject(hBitmap);
#endif
EndPaint(hWnd, &ps);
break;
default:
std::cout << '.';
break;
}
return DefWindowProc(hWnd, msg, wp, lp);
}
int main()
{
WNDCLASSEX wndclass = { sizeof(WNDCLASSEX), CS_DBLCLKS | CS_HREDRAW, MyWindowProcedure,
0, 0, GetModuleHandle(0), LoadIcon(0,IDI_APPLICATION),
LoadCursor(0,IDC_ARROW), HBRUSH(COLOR_WINDOW + 1),
0, L"myclass", LoadIcon(0,IDI_APPLICATION) };
if (RegisterClassEx(&wndclass))
{
hWindow = CreateWindowEx(0, L"myclass", L"myclass", WS_OVERLAPPEDWINDOW, 1200, 600, 256 + 15, 256 + 38, 0, 0, GetModuleHandle(0), 0);
ShowWindow(hWindow, SW_SHOWDEFAULT);
UpdateWindow(hWindow);
MSG msg;
byte bRet;
while ((bRet = GetMessage(&msg, hWindow, 0, 0)) != 0)
{
if (bRet == -1)
{
return 1;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
}
As pointed out. You do not want to call WM_PAINT your self. Let the system handle it. So in WM_LBUTTONDOWN do:
case WM_LBUTTONDOWN:
std::cout << "\nmouse left button down at (" << LOWORD(lp)
<< ',' << HIWORD(lp) << ")\n";
InvalidateRect(hWnd, nullptr, FALSE);
return 0;
Do not fall through. Wnen you call RedrawWindow the window is invalidated and then needs to be repainted. I just tested this, the window lightens up on each click.
UPDATE:
Thanks to Remy Lebeau
Difference between InvalidateRect and RedrawWindow
And yes, it is better to let windows use its own schedule for this kind of stuff.
I've put together this code that takes a screenshot of the desktop and maps it for raw pixel data access, but the output is all zeros. I have no idea what i've done wrong. After looking at many examples of the Desktop Duplication Api online I don't see any differences between them and mine.
This is my method to initialize everything, and no errors are raised.
BOOL init()
{
CHECKHR(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, gFeatureLevels, gNumFeatureLevels, D3D11_SDK_VERSION, &lDevice, &FeatureLevel, &lImmediateContext))
IDXGIDevice* lDxgiDevice;
CHECKHR(lDevice->QueryInterface(IID_PPV_ARGS(&lDxgiDevice)))
IDXGIAdapter* lDxgiAdapter;
CHECKHR(lDxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&lDxgiAdapter))
lDxgiDevice->Release();
IDXGIOutput* lDxgiOutput;
CHECKHR(lDxgiAdapter->EnumOutputs(0, &lDxgiOutput))
lDxgiAdapter->Release();
CHECKHR(lDxgiOutput->GetDesc(&OutputDesc))
IDXGIOutput1* lDxgiOutput1;
CHECKHR(lDxgiOutput->QueryInterface(IID_PPV_ARGS(&lDxgiOutput1)))
lDxgiOutput->Release();
CHECKHR(lDxgiOutput1->DuplicateOutput(lDevice, &lDeskDupl))
lDxgiOutput1->Release();
lDeskDupl->GetDesc(&OutputDuplDesc);
D3D11_TEXTURE2D_DESC desc;
desc.Width = OutputDuplDesc.ModeDesc.Width;
desc.Height = OutputDuplDesc.ModeDesc.Height;
desc.Format = OutputDuplDesc.ModeDesc.Format;
desc.ArraySize = 1;
desc.BindFlags = 0;
desc.MiscFlags = 0;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.MipLevels = 1;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
desc.Usage = D3D11_USAGE_STAGING;
CHECKHR(lDevice->CreateTexture2D(&desc, NULL, &lDestImage))
return TRUE;
}
the CHECKHR macro i'm using was tested by me and works, just to clarify that it is not the problem.
This is the code that I use to actually grab the frame:
int main()
{
init();
HRESULT hr;
IDXGIResource* lDesktopResource;
ID3D11Texture2D* lAcquiredDesktopImage;
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
while (true)
{
if (SUCCEEDED(lDeskDupl->AcquireNextFrame(INFINITE, &FrameInfo, &lDesktopResource)))
{
break;
}
}
hr = lDesktopResource->QueryInterface(IID_PPV_ARGS(&lAcquiredDesktopImage));
if (FAILED(hr))
{
cout << "QueryInterface failed!" << endl;
system("pause");
}
lDesktopResource->Release();
lImmediateContext->CopyResource(lDestImage, lAcquiredDesktopImage);
lAcquiredDesktopImage->Release();
lDeskDupl->ReleaseFrame();
D3D11_MAPPED_SUBRESOURCE resource;
UINT subresource = D3D11CalcSubresource(0, 0, 0);
hr = lImmediateContext->Map(lDestImage, subresource, D3D11_MAP_READ_WRITE, 0, &resource);
if (FAILED(hr))
{
cout << "Map failed!" << endl;
system("pause");
}
BYTE* pb = (BYTE*)(resource.pData);
for (int i = 0; i < 2000000; i++)
{
cout << (int)pb[i] << endl;
}
system("pause");
return 0;
}
All that happens is 2000000 zeros get printed to the console. Is there something i'm missing or that I cant see?
First, call D3D11CreateDevice against specific adapter which output you are about to duplicate:
BOOL init()
{
IDXGIFactory* lDxgiFactory;
CHECKHR(CreateDXGIFactory1(__uuidof(IDXGIFactory), (void**)&lDxgiFactory))
IDXGIAdapter* lDxgiAdapter;
CHECKHR(lDxgiFactory->EnumAdapters(0, &lDxgiAdapter))
lDxgiFactory->Release();
CHECKHR(D3D11CreateDevice(lDxgiAdapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, gFeatureLevels, gNumFeatureLevels, D3D11_SDK_VERSION, &lDevice, &FeatureLevel, &lImmediateContext))
IDXGIDevice* lDxgiDevice;
CHECKHR(lDevice->QueryInterface(IID_PPV_ARGS(&lDxgiDevice)))
//IDXGIAdapter* lDxgiAdapter;
//CHECKHR(lDxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&lDxgiAdapter))
lDxgiDevice->Release();
IDXGIOutput* lDxgiOutput;
CHECKHR(lDxgiAdapter->EnumOutputs(0, &lDxgiOutput))
lDxgiAdapter->Release();
CHECKHR(lDxgiOutput->GetDesc(&OutputDesc))
IDXGIOutput1* lDxgiOutput1;
CHECKHR(lDxgiOutput->QueryInterface(IID_PPV_ARGS(&lDxgiOutput1)))
lDxgiOutput->Release();
CHECKHR(lDxgiOutput1->DuplicateOutput(lDevice, &lDeskDupl))
// ...
Then, your code is not quite accurate around AcquireNextFrame. You need to wait for actual frame and do ReleaseFrame in the loop while you are skipping.
// ...
while (true)
{
if (SUCCEEDED(lDeskDupl->AcquireNextFrame(INFINITE, &FrameInfo, &lDesktopResource)) && FrameInfo.LastPresentTime.QuadPart)
{
break;
}
lDeskDupl->ReleaseFrame();
}
// ...
i have this code, Screen Capture, it should work but i dont know where is the issue
it says "No Target Architecture" i think its about the bit thing .. im running windows 7 32bit, and im using visual studio 2012. thank you stackoverflow in advance
#include <windef.h>
#include <wingdi.h>
#include <fileapi.h>
#include <Windows.h>
#include <handleapi.h>
#include <iostream>
using namespace std;
HWND Window;
int main()
{
char file[] = "c:\\Users\\Mt\Desktop\\j.jpg";
if(Capture(file) == true)
cout << "Screen shot successful at " << file << endl;
else
cout << "Unknow Error " << endl;
}
HWND Window;
BOOL Capture(char *file)
{
HDC hdc;
HBITMAP bitmap;
BITMAPINFO bmpinfo;
LPVOID pBits;
HDC hdc2;
DWORD dwWidth, dwHeight, dwBPP, dwNumColors;
HGDIOBJ gdiobj;
HANDLE hfile;
DWORD dwBytes;
hdc=CreateDC("DISPLAY", NULL, NULL, NULL);
if(hdc==NULL) {
return FALSE;
}
dwWidth = GetDeviceCaps(hdc, HORZRES);
dwHeight = GetDeviceCaps(hdc, VERTRES);
dwBPP = GetDeviceCaps(hdc, BITSPIXEL);
if(dwBPP<=8) {
dwNumColors = GetDeviceCaps(hdc, NUMCOLORS);
dwNumColors = 256;
} else {
dwNumColors = 0;
}
hdc2=CreateCompatibleDC(hdc);
if(hdc2==NULL) {
DeleteDC(hdc);
return FALSE;
}
bmpinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpinfo.bmiHeader.biWidth = dwWidth;
bmpinfo.bmiHeader.biHeight = dwHeight;
bmpinfo.bmiHeader.biPlanes = 1;
bmpinfo.bmiHeader.biBitCount = (WORD) dwBPP;
bmpinfo.bmiHeader.biCompression = BI_RGB;
bmpinfo.bmiHeader.biSizeImage = 0;
bmpinfo.bmiHeader.biXPelsPerMeter = 0;
bmpinfo.bmiHeader.biYPelsPerMeter = 0;
bmpinfo.bmiHeader.biClrUsed = dwNumColors;
bmpinfo.bmiHeader.biClrImportant = dwNumColors;
bitmap = CreateDIBSection(hdc, &bmpinfo, DIB_PAL_COLORS, &pBits, NULL, 0);
if(bitmap==NULL) {
DeleteDC(hdc);
DeleteDC(hdc2);
return FALSE;
}
gdiobj = SelectObject(hdc2, (HGDIOBJ)bitmap);
if((gdiobj==NULL) || (gdiobj==(void *)GDI_ERROR)) {
DeleteDC(hdc);
DeleteDC(hdc2);
return FALSE;
}
if (!BitBlt(hdc2, 0,0, dwWidth, dwHeight, hdc, 0,0, SRCCOPY)) {
DeleteDC(hdc);
DeleteDC(hdc2);
return FALSE;
}
RGBQUAD colors[256];
if(dwNumColors!=0) {
dwNumColors = GetDIBColorTable(hdc2, 0, dwNumColors, colors);
}
BITMAPFILEHEADER bitmapfileheader;
BITMAPINFOHEADER bitmapinfoheader;
bitmapfileheader.bfType = 0x4D42;
bitmapfileheader.bfSize = ((dwWidth * dwHeight * dwBPP)/8) + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (dwNumColors * sizeof(RGBQUAD));
bitmapfileheader.bfReserved1 = 0;
bitmapfileheader.bfReserved2 = 0;
bitmapfileheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (dwNumColors * sizeof(RGBQUAD));
bitmapinfoheader.biSize = sizeof(BITMAPINFOHEADER);
bitmapinfoheader.biWidth = dwWidth;
bitmapinfoheader.biHeight = dwHeight;
bitmapinfoheader.biPlanes = 1;
bitmapinfoheader.biBitCount = (WORD)dwBPP;
bitmapinfoheader.biCompression = BI_RGB;
bitmapinfoheader.biSizeImage = 0;
bitmapinfoheader.biXPelsPerMeter = 0;
bitmapinfoheader.biYPelsPerMeter = 0;
bitmapinfoheader.biClrUsed = dwNumColors;
bitmapinfoheader.biClrImportant = 0;
hfile=CreateFile(file,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
if(hfile==INVALID_HANDLE_VALUE) {
DeleteObject(bitmap);
DeleteDC(hdc2);
DeleteDC(hdc);
return FALSE;
}
WriteFile(hfile,&bitmapfileheader,sizeof(BITMAPFILEHEADER), &dwBytes, NULL);
WriteFile(hfile,&bitmapinfoheader,sizeof(BITMAPINFOHEADER), &dwBytes, NULL);
if(dwNumColors!=0)
WriteFile(hfile,colors,sizeof(RGBQUAD)*dwNumColors,&dwBytes,NULL);
WriteFile(hfile,pBits,(dwWidth*dwHeight*dwBPP)/8,&dwBytes,NULL);
CloseHandle(hfile);
DeleteObject(bitmap);
DeleteDC(hdc2);
DeleteDC(hdc);
return TRUE;
}
and this is what i get .. i don't know what i do wrong
Error 1 error C1189: #error : "No Target Architecture" C:\Program Files\Windows Kits\8.0\Include\um\winnt.h 146 1 capture it 2
You need to include windows.h before windef.h
You need to go into the configuration for your project, and set a target architecture. In your case I assume that is x86.