How to unlock a locked bitmap - c++

I want to unlock a locked ID2D1Bitmap I have tried m_pBitmap1->Release(); but it doesn't seem to work
hr=m_pBitmap1->CopyFromRenderTarget(nullptr, m_pRenderTarget2, nullptr); gives an access violation error:
"Unhandled exception at 0x00fb2a46 in dent_detection_sys.exe: 0xC0000005: Access violation reading location 0x00000024."
WICRect rcLock = { 0, 0, sc_bitmapWidth , sc_bitmapHeight };
IWICBitmapLock *pILock=NULL;
hr =pWICBitmap->Lock(&rcLock, WICBitmapLockWrite, &pILock);
hr=pRT->CreateSharedBitmap(
IID_IWICBitmapLock,
static_cast<void *>(pILock),
&bp2,
&m_pBitmap1
);
hr=m_pBitmap1->Release();
hr=m_pBitmap1->CopyFromRenderTarget(nullptr, m_pRenderTarget2, nullptr);

To unlock the WIC bitmap, release the IWICBitmapLock:
pILock->Release();

You should only release m_pBitmap1 when you don't want to use it anymore.
hr=m_pBitmap1->CopyFromRenderTarget(nullptr, m_pRenderTarget2, nullptr);
hr=m_pBitmap1->Release();

According to the MSDN to use a shared WIC bitmap the render target type must be D2D1_RENDER_TARGET_TYPE_SOFTWARE when the render target is created.
cdemo is a structure object with basic d2d, wic, dwrite interface pointers.
example: cdemo->d2d.factory->CreateSomething(), cdemo->wic.factory->CreateSomething(), cdemo->dwrite.factory->CreateSomething, cdemo->xaudio.factory->CreateSomething, etc.
cbmp is a pointer to a structure that has interfaces and properties related to a WIC bitmap
The following example does not work as is without some tweaks and typo fixes, but it can be used to demonstrate how to use wic for editing a bitmap and directly accessing pixels
This code assumes that the cdemo->d2d.factory and cdemo->wic.factory are already created.
#define DEBUG_FAILED_GOTO(a,b) MessageBox(m_hwnd, a, L"FAILED", MB_OK); goto b
#define DEBUG_DISPLAY(a) MessageBox(m_hwnd, a, L"DEBUG", MB_OK)
#define USING_SHARED_WIC_BITMAP
#define USING_WIC_RENDER_TARGET
#define USING_WICBMP_COPY_TO_D2DBMP
struct COMMON_WIC_BGRA { BYTE b, g, r, a };
struct COMMON_WIC_BMP
{
//// Miscelaneous variables
bool ready;
bool using_d2d_bmp;
bool using_shared_bmp;
bool using_render_tgt;
bool ready_d2d_bmp;
bool ready_shared_bmp;
bool ready_render_tgt;
UINT BPPPP; // Bit-Per-Pixel-Per-Plane
UINT stride; // cbStride = row size;
UINT buff_size; // (org_size.y * stride);
POINT org_size, clip_TpLt, padding;
POINT cur_size, clip_BtRt;
D2D1_BITMAP_PROPERTIES props_bmp; // = D2D1::BitmapProperties();
D2D1_RENDER_TARGET_PROPERTIES props_tgt; // = D2D1::RenderTargetProperties();
WICPixelFormatGUID formatGUID; // = GUID_WICPixelFormat32bppPBGRA;
WICRect rc_lock; // the lock region, usually the entire
//// Interfaces
IWICBitmap* ifc_bmp; // WIC bitmap: lock and unlock bmp data;
IWICBitmapLock* ifc_lock; // Used to access the pixels to read/write
ID2D1RenderTarget* ifc_render_tgt; // Creates a d2d render target
ID2D1Bitmap* ifc_d2d_bmp; // creates an d2d bitmap for display
ID2D1Bitmap* ifc_shared_bmp; // creates a shared bitmap for display
ID2D1SolidColorBrush* ifc_render_brush; // This is needed for the render target
//// Data pointers
BYTE* byte; // Points to a pixel's byte; 8 bits
COMMON_WIC_BGRA* wpixel; // Points to a pixel; 32 bits
};
BOOL Lock_Release (COMON_WIC_BMP *cbmp);
BOOL Lock_Start (COMMON_INTERFACE_STUFF *cdemo, COMMON_WIC_BMP *cbmp, DWORD flags);
void Create_BMP_n_Stuff (
COMMON_INTERFACE_STUFF *cdemo,
COMMON_WIC_BMP *cbmp,
int org_xsize,
int org_ysize)
{
DEBUG_DISPLAY(L"Gate 0-1 Open: started xxx process");
if (cbmp == NULL) { return E_FAIL; }
if (cdemo == NULL) { return E_FAIL; }
DEBUG_DISPLAY(L"Gate 0-2 Open: passed the sanity test");
HRESULT hr = S_OK;
ZeroMemory(cbmp, sizeof(COMMON_WIC_BMP));
// Create a Direct2D render target.
if (cdemo->d2d.hwnd_tgt == NULL)
{
RECT rc;
GetClientRect(m_hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU((rc.right - rc.left), (rc.bottom - rc.top));
cdemo->d2d.props_tgt = D2D1::HwndRenderTargetProperties(m_hwnd, size);
cdemo->d2d.props_tgt_type = D2D1_RENDER_TARGET_TYPE_SOFTWARE;
cdemo->d2d.props_bmp = D2D1::BitmapProperties();
hr = cdemo->d2d.factory->CreateHwndRenderTarget(
cdemo->d2d.props_tgt_type,
cdemo->d2d.props_tgt,
&cdemo->d2d.hwnd_tgt);
if (FAILED(hr)) { goto CleanUp; }
}
DEBUG_DISPLAY(L"Gate 1 Open: hwnd_tgt created");
cbmp->ready = false; // type is: bool
// this option is compatible to D2D bitmap without conversion
cbmp->formatGUID = GUID_WICPixelFormat32bppPBGRA; // type is: WICPixelFormatGUID
cbmp->buff_size = 0; // type is: UINT32
cbmp->stride = 0; // type is: UINT32
cbmp->clip_TpLt.x = 0; // type is: POINT or POINTS
cbmp->clip_TpLt.y = 0;
cbmp->clip_BtRt.x = cbmp->org_size.x = org_xsize; // type is: POINT or POINTS
cbmp->clip_BtRt.y = cbmp->org_size.y = org_ysize;
cbmp->byte = NULL; // type is: pointer to BYTE, BYTE*
cbmp->pixel = NULL; // type is: pointer to COMMON_WIC_BGRA, COMMON_WIC_BGRA*
cbmp->ifc_bmp = NULL; // type is: IWICBitmap*
cbmp->ifc_d2d_bmp = NULL; // type is: ID2D1Bitmap*
cbmp->ifc_lock = NULL; // type is: IWICBitmapLock*
cbmp->ifc_shared_bmp = NULL; // type is: ID2D1Bitmap*
cbmp->ifc_render_tgt = NULL; // type is: ID2D1RenderTarget*
cbmp->ifc_render_brush = NULL; // type is: ID2D1SolidColorBrush*
//D2D1_BITMAP_PROPERTIES props_bmp; = D2D1::BitmapProperties();
//D2D1_RENDER_TARGET_PROPERTIES props_tgt; = D2D1::RenderTargetProperties();
//bool ready;
//bool using_d2d_bmp;
//bool using_shared_bmp;
//bool using_render_tgt;
//bool ready_d2d_bmp;
//bool ready_shared_bmp;
//bool ready_render_tgt;
//UINT BPPPP; // Bit-Per-Pixel-Per-Plane
if (cdemo->wic.factory == NULL)
{ // (re)create the WIC factory
hr = CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWICImagingFactory,
reinterpret_cast<void **>(&cdemo->wic.factory));
if (FAILED(hr)) { goto CleanUp; }
}
hr = cdemo->wic.factory->CreateBitmap(
cbmp->org_size.x,
cbmp->org_size.y,
cbmp->formatGUID,
WICBitmapCacheOnDemand,
&cbmp->ifc_bmp);
// Experimental debug
//if (FAILED(hr)) { DEBUG_FAILED_GOTO(L"FAILED creating wic bitmap", CleanUp); }
if (FAILED(hr)) { goto CleanUp; }
DEBUG_DISPLAY(L"Gate 2 Open: created the WIC bitmap");
// type is: WICRect;
cbmp->rc_lock = { 0, 0, (UINT)cbmp->org_size.x, (UINT)cbmp->org_size.y };
hr = cbmp->ifc_bmp->Lock(&cbmp->rc_lock, WICBitmapLockWrite, &cbmp->ifc_lock);
hr = cbmp->ifc_lock->GetStride(&cbmp->stride); //row size = (xsize*BPPP) + xpadding
hr = cbmp->ifc_lock->GetDataPointer(&cbmp->buff_size, &cbmp->byte);
cbmp->wpixel = (COMMON_WIC_BRGA *)cbmp->byte;
// clear the bitmap
ZeroMemory(cbmp->byte, cbmp->buff_size);
#ifdef USING_SHARED_WIC_BITMAP
cbmp->props_bmp = D2D1:BitmapProperties();
hr = demo->d2d.hwnd_tgt->CreateSharedBitmap(
IID_IWICBitmapLock,
(void*)cbmp->ifc_lock,
&cbmp->props_bmp,
&cbmp->ifc_shared_bmp);
if (SUCCEDED(hr))
{
cbmp->using_shared_bmp = true;
DEBUG_DISPLAY(L"Gate 4-1 Open: created shared wic bitmap");
}
#endif
#ifdef USING_WICBMP_COPY_TO_D2DBMP
hr = cdemo->d2d.factory->CreateBitmapFromWicBitmap(
cbmp->ifc_bmp,
&cbmp->props_bmp,
&cbmp->ifc_d2d_bmp);
if (SUCCEDED(hr))
{
cbmp->using_d2d_bmp = true;
DEBUG_DISPLAY(L"Gate 4-2 Open: created d2d bitmap ");
}
#endif
#ifdef USING_WIC_RENDER_TARGET
cbmp->props_tgt = D2D1::RenderTargetProperties();
cbmp->props_tgt.type = D2D1_RENDER_TARGET_TYPE_SOFTWARE;
hr = cdemo->d2d.factory->CreateWicBitmapRenderTarget(
cbmp->ifc_bmp,
&cbmp->props_tgt,
&cbmp->ifc_render_tgt);
if (SUCCEDED(hr))
{
hr = cbmp->ifc_render_tgt->CreateSolidColorBrush(
{ 1.0f, 1.0f, 1.0f, 1.0f }, // Solid white
&cbmp->ifc_render_brush);
cbmp->using_shared_bmp = true;
DEBUG_DISPLAY(L"Gate 4-3 Open: created wic render target and brush");
}
#endif
if (FAILED(hr)) { goto CleanUp; }
cbmp->ready = true;
// Rules of engagement if using all possible combinations above
// 1) After SafeRelease(&cbmp->ifc_lock) you cannot use cbmp->byte or cbmp->wpixel
// 2) To use cbmp->ifc_render_tgt you must first SafeRelease(cbmp->ifc_lock) and
// SafeRelease(&cbmp->ifc_shared_bmp). Later reinitialize them as needed.
// 3) To display the wic bitmap (cbmp->ifc_bmp) onto cdemo->d2d.hwnd_tgt:
// you cannot copy the wic bitmap (cbmp->ifc_bmp) directly to an hwnd render target
// option 1: This is whole point of creating the shared bmp
// cdemo->d2d.hwnd_tgt->DrawBMP( [ &dst_rc, ] cbmp->ifc_shared_bmp);
// option 2: Copy the pixels to the d2d bitmap, copy the d2d bitmap to the target
// cbmp->ifc_d2d_bmp->CopyFromMemory(cbmp->byte, &dst_rc, cbmp->stride);
// cdemo->d2d.hwnd_tgt->DrawBMP( [ &dst_rc, ] cbmp->ifc_d2d_bmp);
// option 3: Copy from the render target to the d2d bitmap
// cbmp->ifc_d2d_bmp->CopyFromRenderTarget(&pt_dst, cbmp->ifc_render_tgt, &src_rc);
// cdemo->d2d.hwnd_tgt->DrawBMP( [ &dst_rc, ] cbmp->ifc_d2d_bmp);
// 4) When drawing directly to the wic bitmap either use cbmp->ifc_render_tgt or
// cbmp->ifc_lock + cbmp->byte + your own algorithms to draw shapes
//
// 5) for simplicty: it can get confusing when trying to use all methods
option 1: use the ifc_lock with the ifc_shared_bmp + your own algorithms
option 2: use the ifc_render_tgt with the ifc_d2d_bmp
// Example: Draw a filled 12x15 rectangle example:
int x = 20, byte_col = 0, wpixel_col = 0, sizey = 12;
int y = 35, byte_row = 0, wpixel_row = 0, sizex = 15;
D2D1_COLOR_F d2d_colr = { 0.50f, 0.10f, 0.80f, 1.00f }; //some random color
COMMON_WIC_BGRA wic_colr = { 0, 0, 0, 0 };
D2D1_POINT_2U d2d_pt_dst_f = { 0, 0 };
D2D_RECT_F d2d_outputrect = { 0.0f, 0.0f, 0.0f, 0.0f };
D2D1_RECT_F d2d_dst_rcf =
{ 0.0f, 0.0f, (FLOAT)cbmp->org_size.x, (FLOAT)cbmp->org_size.y };
D2D1_RECT_U d2d_src_rcu =
{ 0, 0, (UINT32)cbmp->org_size.x, (UINT32)cbmp->org_size.y };
WIC_RECT_U wic_dst_rcu =
{ 0, 0, (UINT32)cbmp->org_size.x, (UINT32)cbmp->org_size.y };
WIC_RECT_U wic_src_rcu = wic_dst_rcu
// must release the lock and shared bitmap before using the render target
Lock_End(cbmp);
// This should look familiar
d2d_outputrect = { (FLOAT)x, (FLOAT)y, (FLOAT)(x+sizex-1), (FLOAT)(y+sizey-1) };
cbmp->ifc_render_tgt->BeginDraw();
cbmp->ifc_render_brush->SetColor(d2d_colr);
cbmp->ifc_render_tgt->SetTransform(D2D1::IdentityMatrix());
cbmp->ifc_render_tgt->FillRectangle(&d2d_outputrect, cbmp->ifc_render_brush);
hr = cbmp->ifc_render_tgt->EndDraw();
// display the wic bitmap on the hwnd render target
hr = cbmp->ifc_d2d_bmp->CopyFromRenderTarget(
&d2d_pt_dst,
cbmp->ifc_render_tgt,
&d2d_src_rc);
hr = cdemo->d2d.hwnd_tgt->DrawBMP(&d2d_dst_rc, cbmp->ifc_d2d_bmp);
// Alternative: using the ifc_lock with the ifc_shared_bmp + home grown algorithms
if (!Lock_Start(cdemo, cbmp, WICBitmapLockWrite)) { goto CleanUp; }
// convert D2D1_COLOR_F { b, g, r, a} to BYTE { b, g, r, a }
wic_colr.b = (BYTE)ceil(d2d_colr.b * 255);
wic_colr.g = (BYTE)ceil(d2d_colr.g * 255);
wic_colr.r = (BYTE)ceil(d2d_colr.r * 255);
wic_colr.a = (BYTE)ceil(d2d_colr.a * 255);
for (int run_y = 0; run_y < sizey; ++run_y)
{
// clipping for the y values
if (((run_y + y) < cbmp->clip_TpLt.y) || ((run_y + y) >= clip_BtRt.y))
{ continue; }
// convert the y to a byte position
byte_row = ((run_y + y) * cbmp->stride);
wpixel_row = ((run_y + y) * cbmp->org_size.x) + cbmp->padding.x; //optional
for (int run_x = 0; run_x < sizex; ++run_x)
{
// clipping for the x values
if (((run_x + x) < cbmp->clip_TpLt.x) || ((run_x + x) >= clip_BtRt.x))
{ continue; }
// convert the x to an offset position
byte_col = ((run_x + x) * 4); // must multiply by 4 bytes (b, g, r, a)
wpixel_col = (run_x + x); // cbmp->wpixel points to every 4 bytes
// access the pixels by means of pointer[(y_position + x_offset)]
cbmp->byte[(byte_row + byte_col + 0)] = wic_colr.b;
cbmp->byte[(byte_row + byte_col + 1)] = wic_colr.g;
cbmp->byte[(byte_row + byte_col + 2)] = wic_colr.r;
cbmp->byte[(byte_row + byte_col + 3)] = wic_colr.a;
cbmp->wpixel[(wpixel_row + wpixel_col)] = wic_colr; // Alternative
// Another method
cbmp->wpixel[(wpixel_row + wpixel_col)].b = wic_colr.b; // Alternatives
cbmp->wpixel[(wpixel_row + wpixel_col)].g = wic_colr.g; // Alternatives
cbmp->wpixel[(wpixel_row + wpixel_col)].r = wic_colr.r; // Alternatives
cbmp->wpixel[(wpixel_row + wpixel_col)].a = wic_colr.a; // Alternatives
}
}
// display the wic bitmap on the hwnd render target
cdemo->d2d.hwnd_tgt->DrawBMP(&dst_rc, cbmp->ifc_shared_bmp);
// Optionally release the lock after every use
// Lock_Release(cbmp);
return; // commnent out if cleanup is required
CleanUp:
// SafeRelease everything that needs to be released
}
BOOL Lock_Start (
COMMON_INTERFACE_STUFF *cdemo,
COMMON_WIC_BMP *cbmp,
DWORD flags)
{
if (cdemo == NULL) { return FALSE; }
//if (!cdemo->ready) { CreateResouces(cdemo); }
if (cbmp == NULL) { return FALSE; }
if (cbmp->ifc_lock != NULL) { return TRUE; }
SafeRelease(&cbmp->ifc_lock);
hr = cbmp->ifc_bmp->Lock(&cbmp->rc_lock, flags, &cbmp->ifc_lock);
if (FAILED(hr)) { return FALSE; }
hr = cbmp->ifc_lock->GetStride(&cbmp->stride);
hr = cbmp->ifc_lock->GetDataPointer(&cbmp->buff_size, &cbmp->byte);
cbmp->wpixel = (COMMON_WIC_BGRA *)cbmp->byte;
// recreate the shared bitmap
SafeRelease(&cbmp->ifc_shared_bmp);
hr = cdemo->d2d.factory->CreateSharedBitmap(
IID_IWICBitmapLock,
(void*)cbmp->ifc_lock,
&cbmp->props_bmp,
&cbmp->ifc_shared_bmp);
return TRUE;
}
BOOL Lock_Release (COMON_WIC_BMP *cbmp)
{
if (cbmp == NULL) { return FALSE; }
if (cbmp->ifc_lock == NULL) { return TRUE; }
SafeRelease(&cbmp->ifc_lock);
SafeRelease(&cbmp->ifc_shared_bmp);
cbmp->byte = NULL;
cbmp->wpixel = NULL;
if (cbmp->using_render_tgt) { cbmp->ready_render_tgt = true; }
return TRUE;
}

Related

GetPixel via DirectX 11 efficient implementation

I have this function that get a Pixel color from the screen with GDI+ API:
RGBTRIPLE GetPixelColorGDI(int x, int y)
{
RGBTRIPLE rgb;
HDC dc = GetDC(NULL);
COLORREF color = GetPixel(dc, x, y);
ReleaseDC(NULL, dc);
rgb.rgbtRed = GetRValue(color);
rgb.rgbtGreen = GetGValue(color);
rgb.rgbtBlue = GetBValue(color);
return rgb;
}
the function works perfectly but it is too slow.
For this reason I like to try to implement the equivalent in DirectX 11 (I don't have a good experience in DirectX in general) and please excuse me if it is incorrect, this in my first attempt:
/// <summary>
/// Get pixel color from screen (24 bit) via DirextX 11
/// </summary>
/// <param name="X"></param>
/// <param name="Y"></param>
/// <param name="swapchainDescription"></param>
RGBTRIPLE GetPixelColorDX11(int X, int Y, IDXGISwapChain* pSwapChain)
{
RGBTRIPLE rgb;
HRESULT hr = 0;
using Texture2D = ID3D11Texture2D*;
IDXGIResource* backbufferptr = nullptr;
ID3D11Resource* SourceResource = nullptr;
Texture2D DestResource = nullptr;
ID3D11Device* device = nullptr;
ID3D11DeviceContext* context = nullptr;
D3D11_MAPPED_SUBRESOURCE MappedSubresource;
hr = pSwapChain->GetBuffer(0, __uuidof(IDXGIResource), (void**)&backbufferptr);
if (hr < 0) {
return;
}
hr = backbufferptr->QueryInterface(__uuidof(ID3D11Resource), (void**)&SourceResource);
if (hr < 0) {
return;
}
hr = pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&device);
if (hr < 0) {
return;
}
DXGI_SWAP_CHAIN_DESC desc;
hr = pSwapChain->GetDesc(&desc);
if (hr < 0) {
return;
}
D3D11_TEXTURE2D_DESC TextureDesciption = { };
TextureDesciption.Format = desc.BufferDesc.Format;
TextureDesciption.Width = desc.BufferDesc.Width;
TextureDesciption.Height = desc.BufferDesc.Height;
TextureDesciption.MipLevels = 1;
TextureDesciption.ArraySize = 1;
TextureDesciption.SampleDesc.Count = 1;
TextureDesciption.Usage = D3D11_USAGE_STAGING;
TextureDesciption.BindFlags = 0;
TextureDesciption.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
TextureDesciption.MiscFlags = 0;
hr = device->CreateTexture2D(&TextureDesciption, nullptr, &DestResource);
if (hr < 0) {
return;
}
device->GetImmediateContext(&context);
if (!context) {
return;
}
context->CopyResource(DestResource, SourceResource);
context->Map(DestResource, 0, D3D11_MAP_READ, 0, &MappedSubresource);
COLORREF* pPixels = (COLORREF*)MappedSubresource.pData;
rgb.rgbtRed = (pPixels[0] >> 16) & 0xff;
rgb.rgbtGreen = (pPixels[0] >> 8) & 0xff;
rgb.rgbtBlue = pPixels[0] & 0xff;
return rgb;
}
In this code is missing where I can set in X and Y coordinate in pixel because I don't known how to do it.
Apart this I don't sure if this way is enough efficient to make this more faster then the GDI++
GetPixel version.
The idea is (I don't sure if is possible) is select only one pixel (x, y) on source texture and read the color on "pPixels[0]"
Update
I explain better why I need and why.
My hooked DLL hook the DX11 "present" and draw a rectangle using shader that work as "placeholder" on a game menu.
Drawing phase is very fast becouse use DX11 and there no any FPS drop.
To do it I use a library called "imgui".
Before draw the rectangle I need to known in what position I need to place the rectangle and this depend by the result of the actual GetPixelColorGDI that is slow.
The user press a arrow key to move the rectangle on another position.
I call "GetPixelColorGDI" from 1 to 11 times for single screen.
If the next slot is filled I call GetPixelColorGDI only one time, but if the next 10 slots are empty I need to call GetPixelColorGDI 10 times.
So best solution can be create GetPixelColorDX11 like this:
RGBTRIPLE GetPixelColorDX11(int X, int Y, IDXGISwapChain* pSwapChain)
and this overloald:
struct COORDINATE
{
int x;
int y;
}
RGBTRIPLE[] GetPixelColorDX11(COORDINATE[] Pixel , IDXGISwapChain* pSwapChain)
so this can cover all cases.
here my function that I use to initialize DirectX to draw the rectangles:
bool InitDirectX(IDXGISwapChain* pChain)
{
// Get swapchain
pSwapchain = pChain;
// Get device and context
HRESULT hr = pSwapchain->GetDevice(__uuidof(ID3D11Device), (PVOID*)&pDevice);
if (FAILED(hr))
{
std::cerr << "Failed to get device from swapchain" << std::endl;
return false;
}
pDevice->GetImmediateContext(&pContext);
// Get window from swapchain description
DXGI_SWAP_CHAIN_DESC swapchainDescription;
pSwapchain->GetDesc(&swapchainDescription);
hWindow = swapchainDescription.OutputWindow;
// Use SetWindowLongPtr to modify window behaviour and get input
wndProcHandlerOriginal = (WNDPROC)SetWindowLongPtr(hWindow, GWLP_WNDPROC, (LONG_PTR)hWndProc);
std::cout << "Successfully initialised DirectX - resolution " << swapchainDescription.BufferDesc.Width << "x" << swapchainDescription.BufferDesc.Height << std::endl;
// Update the screen resolution multiplier
UpdateResolutionMultiplier(swapchainDescription.BufferDesc.Width, swapchainDescription.BufferDesc.Height);
return true;
}

Transparent glitches while drawing GIF

I wrote some solution to draw gifs, using Direct2D GIF sample:
// somewhere in render loop
IWICBitmapFrameDecode* frame = nullptr;
IWICFormatConverter* converter = nullptr;
gifDecoder->GetFrame(current_frame, &frame);
if (frame)
{
d2dWICFactory->CreateFormatConverter(&converter);
if (converter)
{
ID2D1Bitmap* temp = nullptr;
converter->Initialize(frame, GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteType::WICBitmapPaletteTypeCustom);
tar->CreateBitmapFromWicBitmap(converter, NULL, &temp);
scale->SetValue(D2D1_SCALE_PROP_SCALE, D2D1::Vector2F(1, 1));
scale->SetInput(0, temp);
target->DrawImage(scale);
SafeRelease(&temp);
}
}
SafeRelease(&frame);
SafeRelease(&converter);
Where gifDecoder is obtained this way:
d2dWICFactory->CreateDecoderFromFilename(pathToGif, NULL, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &gifDecoder);
But in my program almost with all gifs, all frames, except the first one, for some reason have horizontal transparent lines. Obviously, when I view the same gif in browser or in another program there are no that holes.
I tried to change pixel format, pallet type, but that glitches are still there.
What do I do wrong?
Basic steps:
On image load, in case of gif, for caching purposes store not all frames as ready to draw ID2D1Bitmaps but only the image's decoder. Every time decode the frame, get bitmap and draw it. At least, at first animation loop frames bitmaps and metadata (again, for each frame) can be cached.
For each gif create compatible target with the size of gif screen (obtained from metadata) for composing.
Compose animation on render: get metadata for current frame -> if current frame has disposal = true, clear compose target -> draw current frame to compose target (even if disposal true) -> draw compose target to render target,
Note, that frames may have different sizes and even offsets.
Approximate code:
ID2D1DeviceContext *renderTarget;
class GIF
{
ID2D1BitmapRenderTarget *compose;
IWICBitmapDecoder *decoder; // it seems like precache all frames costs too much time, it will be faster to obtain each frame each time (maybe cache during first loop)
float naturalWidth; // gif screen width
float naturalHeight; // gif screen height
int framesCount;
int currentFrame;
unsigned int lastFrameEpoch;
int x;
int y;
int width; // image width needed to be drawn
int height; // image height
float fw; // scale factor width;
float fh; // scale factor height
GIF(const wchar_t *path, int x, int y, int width, int height)
{
// Init, using WIC obtain here image's decoder, naturalWidth, naturalHeight
// framesCount and other optional stuff like loop information
// using IWICMetadataQueryReader obtained from the whole image,
// not from zero frame
compose = nullptr;
currentFrame = 0;
lastFrameEpoch = epoch(); // implement Your millisecond function
Resize(width, height);
}
void Resize(int width, int height)
{
this->width = width;
this->height = height;
// calculate scale factors
fw = width / naturalWidth;
fh = height / naturalHeight;
if(compose == nullptr)
{
renderTarget->CreateCompatibleRenderTarget(D2D1::SizeF(naturalWidth, naturalHeight), &compose);
compose->Clear(D2D1::ColorF(0,0,0,0));
}
}
void Render()
{
IWICBitmapFrameDecode* frame = nullptr;
decoder->GetFrame(currentFrame, &frame);
IWICFormatConverter* converter = nullptr;
d2dWICFactory->CreateFormatConverter(&converter);
converter->Initialize(frame, GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteType::WICBitmapPaletteTypeCustom);
IWICMetadataQueryReader* reader = nullptr;
frame->GetMetadataQueryReader(&reader);
PROPVARIANT propValue;
PropVariantInit(&propValue);
char disposal = 0;
float l = 0, t = 0; // frame offsets (may have)
HRESULT hr = S_OK;
reader->GetMetadataByName(L"/grctlext/Delay", &propValue);
if (SUCCEEDED((propValue.vt == VT_UI2 ? S_OK : E_FAIL)))
{
UINT frameDelay = 0;
UIntMult(propValue.uiVal, 10, &fr);
frameDelay = fr;
if (frameDelay < 16)
{
frameDelay = 16;
}
if(epoch() - lastFrameEpoch < frameDelay)
{
SafeRelease(&reader);
SafeRelease(&frame);
SafeRelease(&converter);
return;
}
}
if (SUCCEEDED(reader->GetMetadataByName(
L"/grctlext/Disposal",
&propValue)))
{
hr = (propValue.vt == VT_UI1) ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
disposal = propValue.bVal;
}
}
PropVariantClear(&propValue);
{
hr = reader->GetMetadataByName(L"/imgdesc/Left", &propValue);
if (SUCCEEDED(hr))
{
hr = (propValue.vt == VT_UI2 ? S_OK : E_FAIL);
if (SUCCEEDED(hr))
{
l = static_cast<FLOAT>(propValue.uiVal);
}
PropVariantClear(&propValue);
}
}
{
hr = reader->GetMetadataByName(L"/imgdesc/Top", &propValue);
if (SUCCEEDED(hr))
{
hr = (propValue.vt == VT_UI2 ? S_OK : E_FAIL);
if (SUCCEEDED(hr))
{
t = static_cast<FLOAT>(propValue.uiVal);
}
PropVariantClear(&propValue);
}
}
renderTarget->CreateBitmapFromWicBitmap(converter, NULL, &temp);
compose->BeginDraw();
if (disposal == 2)
compose->Clear(D2D1::ColorF(0, 0, 0, 0));
auto ss = temp->GetSize();
compose->DrawBitmap(temp, D2D1::RectF(l, t, l + ss.width, t + ss.height));
compose->EndDraw();
ID2D1Bitmap* composition = nullptr;
compose->GetBitmap(&composition);
// You need to create scale effect to have antialised resizing
scale->SetValue(D2D1_SCALE_PROP_SCALE, D2D1::Vector2F(fw, fh));
scale->SetInput(0, composition);
auto p = D2D1::Point2F(x, y);
renderTarget->DrawImage(iscale, p);
SafeRelease(&temp);
SafeRelease(&composition);
SafeRelease(&reader);
SafeRelease(&frame);
SafeRelease(&converter);
}
}*gif[100] = {nullptr};
inline void Render()
{
renderTarget->BeginDraw();
for(int i = 0; i < 100 && gif[i] != nullptr; i++)
gif[i]->Render();
renderTarget->EndDraw();
}

Alternative way to SwapChain->Present to draw a frame on DX11

I need to hook DX11 on a game to draw some rectangle.
To do it I have tried some source code but actually the only one that work in my case is ImGui:
https://github.com/ocornut/imgui
theoretically I can use it, but because in my case I need to draw only some line I like to consider other altarnative.
For this reason I have tried this example:
http://www.directxtutorial.com/Lesson.aspx?lessonid=11-4-5
that work on a test desktop project but not in my dll that hook the game.
My dll crash in this line:
pChain->Present(0, 0);
becouse I have already hook with detour this method:
HRESULT __stdcall IDXGISwapChain_Present(IDXGISwapChain* pChain, UINT SyncInterval, UINT Flags)
{
// Get device and swapchain
if (!bGraphicsInitialised)
{
// Init Direct X
if (!InitDirectXAndInput(pChain)) return fnIDXGISwapChainPresent(pChain, SyncInterval, Flags);
// Init ImGui
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
// Setup rendering and IO
ImGui_ImplWin32_Init(hWindow);
ImGui_ImplDX11_Init(pDevice, pContext);
ImGui::GetIO().ImeWindowHandle = hWindow;
// Create render target view for rendering
ID3D11Texture2D* pBackBuffer;
pChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
HRESULT hr = pDevice->CreateRenderTargetView(pBackBuffer, NULL, &pRenderTargetView);
if (FAILED(hr))
{
std::cerr << "Failed to create render target view" << std::endl;
return fnIDXGISwapChainPresent(pChain, SyncInterval, Flags);
}
pBackBuffer->Release();
bGraphicsInitialised = true;
}
LF::Log_Update("IDXGISwapChain_Present");
// Render ImGui
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
ImGui::ShowDemoWindowCustom(test);
ImGui::EndFrame();
ImGui::Render();
pContext->OMSetRenderTargets(1, &pRenderTargetView, NULL);
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
return fnIDXGISwapChainPresent(pChain, SyncInterval, Flags);
}
with this code my dll (that use ImuGui) work perfectly and not need "swapChain->Present".
Here my dll without ImuGui:
HRESULT __stdcall IDXGISwapChain_Present(IDXGISwapChain* pChain, UINT SyncInterval, UINT Flags)
{
// Get device and swapchain
if (!bGraphicsInitialised)
{
// Init Direct X
if (!InitDirectXAndInput(pChain)) return fnIDXGISwapChainPresent(pChain, SyncInterval, Flags);
// Init ImGui
////ImGui::CreateContext();
////ImGuiIO& io = ImGui::GetIO(); (void)io;
////io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
////// Setup rendering and IO
////ImGui_ImplWin32_Init(hWindow);
////ImGui_ImplDX11_Init(pDevice, pContext);
////ImGui::GetIO().ImeWindowHandle = hWindow;
// set up and initialize Direct3D
InitD3D(hWindow, pDevice, pContext);
// Create render target view for rendering
ID3D11Texture2D* pBackBuffer;
pChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
HRESULT hr = pDevice->CreateRenderTargetView(pBackBuffer, NULL, &pRenderTargetView);
if (FAILED(hr))
{
std::cerr << "Failed to create render target view" << std::endl;
return fnIDXGISwapChainPresent(pChain, SyncInterval, Flags);
}
pBackBuffer->Release();
bGraphicsInitialised = true;
}
RenderFrame();
pChain->Present(0, 0);
LF::Log_Update("pChain->Present(1, 0)");
return fnIDXGISwapChainPresent(pChain, SyncInterval, Flags);
}
and here my renderframe:
// this is the function used to render a single frame
void RenderFrame(void)
{
if (VectVertices.size() < 1)
return;
// Set transparent color
float color[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
// clear the back buffer
devcon->ClearRenderTargetView(backbuffer, color);
// select which vertex buffer to display
UINT stride = sizeof(VERTEX);
UINT offset = 0;
devcon->IASetVertexBuffers(0, 1, &pVBuffer, &stride, &offset);
// select which primtive type we are using
// draw the vertex buffer to the back buffer
devcon->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST);
devcon->Draw(VectVertices.size(), 0);
// swapchain->Present(0, 0);
}
in short my code not work becouse shouldn't use swapchain->Present that is hooked.
The here the working rendering method of imuGUI:
// Render function
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
ID3D11DeviceContext* ctx = g_pd3dDeviceContext;
// Create and grow vertex/index buffers if needed
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount)
{
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
D3D11_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert);
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0)
return;
}
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount)
{
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
D3D11_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx);
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pIB) < 0)
return;
}
// Upload vertex/index data into a single contiguous GPU buffer
D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource;
if (ctx->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
return;
if (ctx->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
return;
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
ctx->Unmap(g_pVB, 0);
ctx->Unmap(g_pIB, 0);
// Setup orthographic projection matrix into our constant buffer
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
{
D3D11_MAPPED_SUBRESOURCE mapped_resource;
if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
return;
VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData;
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
float mvp[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.5f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
};
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
ctx->Unmap(g_pVertexConstantBuffer, 0);
}
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
struct BACKUP_DX11_STATE
{
UINT ScissorRectsCount, ViewportsCount;
D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
ID3D11RasterizerState* RS;
ID3D11BlendState* BlendState;
FLOAT BlendFactor[4];
UINT SampleMask;
UINT StencilRef;
ID3D11DepthStencilState* DepthStencilState;
ID3D11ShaderResourceView* PSShaderResource;
ID3D11SamplerState* PSSampler;
ID3D11PixelShader* PS;
ID3D11VertexShader* VS;
ID3D11GeometryShader* GS;
UINT PSInstancesCount, VSInstancesCount, GSInstancesCount;
ID3D11ClassInstance *PSInstances[256], *VSInstances[256], *GSInstances[256]; // 256 is max according to PSSetShader documentation
D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology;
ID3D11Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer;
UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset;
DXGI_FORMAT IndexBufferFormat;
ID3D11InputLayout* InputLayout;
};
BACKUP_DX11_STATE old;
old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
ctx->RSGetState(&old.RS);
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
ctx->PSGetSamplers(0, 1, &old.PSSampler);
old.PSInstancesCount = old.VSInstancesCount = old.GSInstancesCount = 256;
ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount);
ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount);
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
ctx->GSGetShader(&old.GS, old.GSInstances, &old.GSInstancesCount);
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
ctx->IAGetInputLayout(&old.InputLayout);
// Setup desired DX state
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_idx_offset = 0;
int global_vtx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Apply scissor/clipping rectangle
const D3D11_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) };
ctx->RSSetScissorRects(1, &r);
// Bind texture, Draw
ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->TextureId;
ctx->PSSetShaderResources(0, 1, &texture_srv);
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
// Restore modified DX state
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release();
for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release();
ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release();
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
ctx->GSSetShader(old.GS, old.GSInstances, old.GSInstancesCount); if (old.GS) old.GS->Release();
for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release();
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
}
Can you please suggest me a way to avoid to use pChain->Present(0, 0) ?
Thank you !

Copy HICON / HCURSOR in to Byte Array

Is any way can we convert HICON or HCURSOR in to Byte array, I googled in all the way I didnt found a single generic solution, below I tried to convert HICON color and mask BITMAP to byte array and sending this through socket and creating my icon using CreateIconIndirect API but instead of doing all this stuff if I can able to send a HICON directly that will be good.
int ProcessMouse()
{
BYTE m_hbmMaskBits[70000];
BYTE m_hbmColorBits[70000];
CURSORINFO CursorInfo;
CursorInfo.cbSize = sizeof(CursorInfo);
GetCursorInfo(&CursorInfo);
ICONINFO iconInfo;
if (!GetIconInfo(CursorInfo.hCursor, &iconInfo))
{
MessageBox(NULL, _T("CreateCursor Failed"),_T("message"),MB_OK|MB_SYSTEMMODAL);
}
bool isColorShape = (iconInfo.hbmColor != NULL);
bool isMaskShape = (iconInfo.hbmMask != NULL);
LONG cbSize = 0; int nWidth = 0; int nHeight = 0; int actualHeight = 0; int bmPlanes = 0;
int bmBitsPixel = 0; int xHotspot = 0; int yHotspot = 0; int widthBytes = 0;
// Return width,height,actualheight,bmplanes,bmbitspixel,hotsopt of cursor.
if(!CopyIconInfo( CursorInfo.hCursor,
nWidth,
nHeight,
actualHeight,
bmPlanes,
bmBitsPixel,
xHotspot,
yHotspot,
widthBytes ))
{
return 0;
}
std::vector<BYTE> bColor;
std::vector<BYTE> bMask;
int sz_hbmColor = 0;
int sz_hbmMask = 0;
_tempWidth = nWidth;
_tempHeight = nHeight;
//If HCURSOR have both color and mask go with regular approach.
if(isColorShape)
{
//Convert iconInfo.hbmColor HBITMAP to Byte array.
bColor = HBIMAPtoBYTE(iconInfo.hbmColor,sz_hbmColor);
//Convert iconInfo.hbmMask HBITMAP to Byte array.
bMask = HBIMAPtoBYTE(iconInfo.hbmMask,sz_hbmMask);
}
// If HCURSOR have only mask data go with new approach(split mask bitmap to color and mask).
else if(isMaskShape)
{
std::vector<BYTE> bSrcBitmap;
int sz_hbmBitmap = 0;
//Convert iconInfo.hbmMask HBITMAP to Byte array.
bSrcBitmap = HBIMAPtoBYTE(iconInfo.hbmMask,sz_hbmBitmap);
sz_hbmColor = sz_hbmBitmap/2;
sz_hbmMask = sz_hbmBitmap/2;
bMask.resize(bMask.size() + sz_hbmBitmap/2);
memcpy(&bMask[bSrcBitmap.size() - sz_hbmBitmap], &bSrcBitmap[0], sz_hbmBitmap/2 * sizeof(BYTE));
bColor.resize(bColor.size() + sz_hbmBitmap/2);
memcpy(&bColor[bSrcBitmap.size() - sz_hbmBitmap], &bSrcBitmap[sz_hbmBitmap/2], sz_hbmBitmap/2 * sizeof(BYTE));
//Clear at end.
bSrcBitmap.clear();
}
try{
err = memcpy_s((m_hbmMaskBits), sz_hbmMask, &(bMask[0]), sz_hbmMask );
err = memcpy_s((m_hbmColorBits),sz_hbmColor,&(bColor[0]),sz_hbmColor);
//Clear at end.
bMask.clear();
bColor.clear();
return 1;
}catch(...) {
if(err) {
MessageBox(NULL, _T("memcopy failed at mask or color copy"),_T("message"),MB_OK|MB_SYSTEMMODAL);
}
}
}
I tried in below way but it doesn't support for few monochrome cursors.
PICTDESC pd = {sizeof(pd), PICTYPE_ICON};
pd.icon.hicon = CursorInfo.hCursor;
CComPtr<IPicture> pPict = NULL;
CComPtr<IStream> pStrm = NULL;
BOOL res = FALSE;
res = SUCCEEDED( ::CreateStreamOnHGlobal(NULL, TRUE, &pStrm) );
res = SUCCEEDED( ::OleCreatePictureIndirect(&pd, IID_IPicture, TRUE, (void**)&pPict) );
res = SUCCEEDED( pPict->SaveAsFile( pStrm, TRUE, &cbSize ) );
if( res )
{
// rewind stream to the beginning
LARGE_INTEGER li = {0};
pStrm->Seek(li, STREAM_SEEK_SET, NULL);
// write to file
DWORD dwWritten = 0, dwRead = 0, dwDone = 0;
while( dwDone < cbSize )
{
if( SUCCEEDED(pStrm->Read(bCursorBuff, sizeof(bCursorBuff), &dwRead)) )
{
dwDone += dwRead;
}
}
_ASSERTE(dwDone == cbSize);
}
//End of Cursor image
pStrm.Release();
pPict.Release();
HICON and HCURSOR are system handles, so they work only on the current machine.
Over network only the actual data can be sent (bitmap bytes). Then that machine can create its own handles for it.
Using the HBITMAP bytes is the correct approach. You can find some details here:
How to convert HICON to HBITMAP in VC++?
You can get the raw HBITMAP bits using GetDIBits(). More information: C++/Win32: How to get the alpha channel from an HBITMAP?
Below Code works only for color cursor for monochrome cursor use to
convert 16bpp bitmap to 32bpp bitmap and use same code its works.
bool saveToMemory(HICON hIcon, BYTE* buffer, DWORD& nSize)
{
if (hIcon == 0)
return FALSE;
int * pImageOffset;
int nNumIcons = 1;
nSize = 0;
// copy iconheader first of all
ICONHEADER iconheader;
// Setup the icon header
iconheader.idReserved = 0; // Must be 0
iconheader.idType = 1; // Type 1 = ICON (type 2 = CURSOR)
iconheader.idCount = nNumIcons; // number of ICONDIRs
// save to memory
memcpy(buffer, &iconheader, sizeof(iconheader));
nSize += sizeof(iconheader); // update
//
// Leave space for the IconDir entries
//
nSize += sizeof(ICONDIR);
pImageOffset = (int *)malloc(nNumIcons * sizeof(int));
ICONINFO iconInfo;
BITMAP bmpColor, bmpMask;
GetIconBitmapInfo(hIcon, &iconInfo, &bmpColor, &bmpMask);
// record the file-offset of the icon image for when we write the icon directories
pImageOffset[0] = nSize;
// bitmapinfoheader + colortable
//WriteIconImageHeader(hFile, &bmpColor, &bmpMask);
BITMAPINFOHEADER biHeader;
UINT nImageBytes;
// calculate how much space the COLOR and MASK bitmaps take
nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask);
// write the ICONIMAGE to disk (first the BITMAPINFOHEADER)
ZeroMemory(&biHeader, sizeof(biHeader));
// Fill in only those fields that are necessary
biHeader.biSize = sizeof(biHeader);
biHeader.biWidth = bmpColor.bmWidth;
biHeader.biHeight = bmpColor.bmHeight * 2; // height of color+mono
biHeader.biPlanes = bmpColor.bmPlanes;
biHeader.biBitCount = bmpColor.bmBitsPixel;
biHeader.biSizeImage = nImageBytes;
// write the BITMAPINFOHEADER
//WriteFile(hFile, &biHeader, sizeof(biHeader), &nWritten, 0);
memcpy(&buffer[nSize], &biHeader, sizeof(biHeader));
nSize += sizeof(biHeader);
// save color and mask bitmaps
saveIconData(buffer, nSize, iconInfo.hbmColor);
saveIconData(buffer, nSize, iconInfo.hbmMask);
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
//
// Lastly, save the icon directories.
//
DWORD size = saveIconDirectoryEntry(buffer, sizeof(ICONHEADER), pImageOffset[0], hIcon);
free(pImageOffset);
return TRUE;
}
//
// Return the number of BYTES the bitmap will take ON DISK
//
static UINT NumBitmapBytes(BITMAP *pBitmap)
{
int nWidthBytes = pBitmap->bmWidthBytes;
// bitmap scanlines MUST be a multiple of 4 bytes when stored
// inside a bitmap resource, so round up if necessary
if (nWidthBytes & 3)
nWidthBytes = (nWidthBytes + 4) & ~3;
return nWidthBytes * pBitmap->bmHeight;
}
// same as WriteIconData but save to memory
static UINT saveIconData(BYTE* buffer, DWORD& nSize, HBITMAP hBitmap)
{
BITMAP bmp;
int i;
BYTE * pIconData;
UINT nBitmapBytes;
DWORD nWritten = 0;
GetObject(hBitmap, sizeof(BITMAP), &bmp);
nBitmapBytes = NumBitmapBytes(&bmp);
pIconData = (BYTE *)malloc(nBitmapBytes);
GetBitmapBits(hBitmap, nBitmapBytes, pIconData);
// bitmaps are stored inverted (vertically) when on disk..
// so write out each line in turn, starting at the bottom + working
// towards the top of the bitmap. Also, the bitmaps are stored in packed
// in memory - scanlines are NOT 32bit aligned, just 1-after-the-other
for (i = bmp.bmHeight - 1; i >= 0; i--)
{
// Write the bitmap scanline
// save to memory
memcpy(&buffer[nSize], pIconData + (i * bmp.bmWidthBytes), bmp.bmWidthBytes);
nSize += bmp.bmWidthBytes;
nWritten += bmp.bmWidthBytes;
}
free(pIconData);
return nWritten;
}
//
// same as WriteIconDirectoryEntry but save to memory
//
static UINT saveIconDirectoryEntry(BYTE* buffer, DWORD pos, int imageOffset, HICON hIcon)
{
ICONINFO iconInfo;
ICONDIR iconDir;
BITMAP bmpColor;
BITMAP bmpMask;
DWORD nWritten = 0;
UINT nColorCount;
UINT nImageBytes;
GetIconBitmapInfo(hIcon, &iconInfo, &bmpColor, &bmpMask);
nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask);
if (bmpColor.bmBitsPixel >= 8)
nColorCount = 0;
else
nColorCount = 1 << (bmpColor.bmBitsPixel * bmpColor.bmPlanes);
// Create the ICONDIR structure
iconDir.bWidth = (BYTE)bmpColor.bmWidth;
iconDir.bHeight = (BYTE)bmpColor.bmHeight;
iconDir.bColorCount = nColorCount;
iconDir.bReserved = 0;
iconDir.wPlanes = bmpColor.bmPlanes;
iconDir.wBitCount = bmpColor.bmBitsPixel;
iconDir.dwBytesInRes = sizeof(BITMAPINFOHEADER) + nImageBytes;
iconDir.dwImageOffset = imageOffset;
// save to memory
memcpy(&buffer[pos], &iconDir, sizeof(iconDir));
nWritten += sizeof(iconDir);
// Free resources
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
return nWritten;
}
I was able to do so by calling GetDIBits() twice, once to get the actual details of the cursor images and another time to get the pixels.
You can apply this code for the color and mask, just be aware that it only returns 32x32px cursors, also only the first frame, even if the size is configured for something else.
var windowDeviceContext = User32.GetWindowDC(IntPtr.Zero);
//Initialize the bitmap header and calculate its size.
var maskHeader = new BitmapInfoHeader();
maskHeader.Size = (uint) Marshal.SizeOf(maskHeader);
//Gets the image details.
Gdi32.GetDIBits(windowDeviceContext, iconInfo.Mask, 0, 0, null, ref maskHeader, DibColorModes.RgbColors);
//If there's any data, get it.
if (maskHeader.Height != 0)
{
//To prevent the cursor image from being inverted.
maskHeader.Height *= -1;
var maskBuffer = new byte[maskHeader.SizeImage];
Gdi32.GetDIBits(windowDeviceContext, iconInfo.Mask, 0, (uint) maskHeader.Height, maskBuffer, ref maskHeader, DibColorModes.RgbColors);
}
It's C#, but easily converted to your language of choice.

ID2D1Bitmap1::Map issue

I have an ID2D1BitmapBrush. What i'm trying to achieve is to get its image data. This is what I've tried:
// imageBrush - ID2D1BitmapBrush
// topLeft is a d2d1_point2u (0,0)
// uBounds is image bounding rectangle
Microsoft::WRL::ComPtr<ID2D1Bitmap> tmpBitmap;
Microsoft::WRL::ComPtr<ID2D1Bitmap1> tmpBitmap1;
D2D1_MAPPED_RECT bitmapData;
imageBrush->GetBitmap(tmpBitmap.ReleaseAndGetAddressOf());
///Creating a new bitmap
D2D1_SIZE_U dimensions;
dimensions.height = uBounds.bottom;
dimensions.width = uBounds.right;
D2D1_BITMAP_PROPERTIES1 d2dbp;
D2D1_PIXEL_FORMAT d2dpf;
FLOAT dpiX = 0;
FLOAT dpiY = 0;
d2dpf.format = DXGI_FORMAT_R8G8B8A8_UNORM;
d2dpf.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
d2dFactory->GetDesktopDpi(&dpiX, &dpiY);
d2dbp.pixelFormat = d2dpf;
d2dbp.dpiX = dpiX;
d2dbp.dpiY = dpiY;
d2dbp.bitmapOptions = D2D1_BITMAP_OPTIONS_CPU_READ | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
d2dbp.colorContext = nullptr;
HRESULT hr = d2dCtx->CreateBitmap(dimensions, nullptr, 0, d2dbp, tmpBitmap1.GetAddressOf());
/// Getting image data
tmpBitmap1.Get()->CopyFromBitmap(&topLeft, tmpBitmap.Get(), &uBounds);
tmpBitmap1.Get()->Map(D2D1_MAP_OPTIONS_READ, &bitmapData);
The problem is - at the end bitmapData.bits is empty.
Where did i get it wrong?
After starting with your code above, and providing a proper bitmap brush, only a couple modification were required (kept your variable names, etc).
ID2D1Factory1* d2dFactory = /* [ get current factory ] */;
ASSERT(d2dFactory);
ID2D1DeviceContext* d2dCtx = /* [ get current target ] */;
ASSERT(d2dCtx);
Microsoft::WRL::ComPtr<ID2D1Bitmap> tmpBitmap { };
Microsoft::WRL::ComPtr<ID2D1Bitmap1> tmpBitmap1 { };
D2D1_MAPPED_RECT bitmapData { };
imageBrush->GetBitmap(tmpBitmap.ReleaseAndGetAddressOf());
ASSERT(tmpBitmap);
D2D1_SIZE_F const bitmapSize = tmpBitmap->GetSize();
ASSERT(bitmapSize.width > 0.0f);
ASSERT(bitmapSize.height > 0.0f);
D2D1_RECT_U const uBounds
{
0u,
0u,
static_cast<UINT32>(bitmapSize.width),
static_cast<UINT32>(bitmapSize.width)
};
D2D1_SIZE_U const dimensions
{
uBounds.bottom,
uBounds.right
};
D2D1_BITMAP_PROPERTIES1 d2dbp { };
D2D1_PIXEL_FORMAT d2dpf { };
FLOAT dpiX = 0.0f;
FLOAT dpiY = 0.0f;
// This modification to the color format was required
// to correlate with my swap chain format. Otherwise
// you will receive an error [E_INVALIDARG ] when
// calling CopyFromBitmap (as you had described)
d2dpf.format = DXGI_FORMAT_B8G8R8A8_UNORM; // <-- previously DXGI_FORMAT_R8G8B8A8_UNORM;
d2dpf.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
d2dbp.pixelFormat = d2dpf;
d2dFactory->GetDesktopDpi(&dpiX, &dpiY);
d2dbp.dpiX = dpiX;
d2dbp.dpiY = dpiY;
d2dbp.bitmapOptions = D2D1_BITMAP_OPTIONS_CPU_READ | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
d2dbp.colorContext = nullptr;
HRESULT hr = S_OK;
// [ omitting the interrogation of hr ]
D2D1_POINT_2U const topLeft { uBounds.left, uBounds.top };
hr = d2dCtx->CreateBitmap(dimensions, nullptr, 0u, d2dbp, tmpBitmap1.GetAddressOf());
hr = tmpBitmap1.Get()->CopyFromBitmap(&topLeft, tmpBitmap.Get(), &uBounds);
hr = tmpBitmap1.Get()->Map(D2D1_MAP_OPTIONS_READ, &bitmapData);
ASSERT(bitmapData.pitch == /* [call funtion to determine pitch] */);
ASSERT(bitmapData.bits != nullptr);
// [ ... code to further utilize bits ]
The primary change was to ensure the pixel format correlated with the format set in the swap chain to avoid the error you described (E_INVALIDARG One or more arguments are invalid) when calling CopyFromBitmap.
Additional code was used to validate the resulting D2D1_MAPPED_RECT (pitch and bits). Hope this helps.