Click here to Skip to main content
15,887,135 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a little directX that is simply an overlapped window with a Direct2d surface on it.

In WM_PAINT, I try to fill some memory with the color purple, and then copy that to a bitmap, before writing the bitmap to a display.

Trouble is, EndDraw() succeeds, but all I get is a whole lot of black window. That tells me the surface is indeed there, and being drawn, but either the bitmap copy or the bitmap draw operation isn't working as expected, despite succeeding. There is commented code below. I've tried various combinations of it, but no dice.

The gfx:: stuff is from my embedded graphics library, and is battle tested. I'm not too concerned about that code. m_frame_buffer should indeed be filled with X11 standard purple at @BGRA8888. If the pixel format is somehow wrong, I still shouldn't be seeing all black.

Anyway, I need some eyes on this. I am stumped.

Thanks in advance.

What I have tried:

It's a bit sloppy in terms of naming and stuff. Started with a sample, and modified it. I'll clean it up later. I want to get it working first.

C++
// called from WM_PAINT
void app::render() {
    HRESULT hr = S_OK;
    if(m_frame_buffer==NULL) {
        hr = E_POINTER;
    }
   if(SUCCEEDED(hr)) {        
        // create a thin wrapper around m_frame_buffer 
        // so we can draw to it
        frame_buffer_type bmp
         ((gfx::size16)m_screen.dimensions(),m_frame_buffer);
        // fill m_frame_buffer with purple.
        gfx::draw::filled_rectangle(bmp,bmp.bounds(),color_type::purple);
        // create the bitmap and render target
        hr = create_device_resources();
        if (SUCCEEDED(hr)) {
            // RECT rc;
            // // get the window bounds
            // GetClientRect(m_hwnd, &rc);
            if(m_bitmap!=NULL) {
                // D2D1_RECT_U ru;
                // ru.top = 0;
                // ru.left = 0;
                // ru.right = m_screen.bounds().x2;
                // ru.bottom = m_screen.bounds().y2;

                // copy the frame buffer to the bitmap
                hr=m_bitmap->CopyFromMemory
                (NULL,m_frame_buffer,m_screen.dimensions().width*4);
                if(SUCCEEDED(hr)) {
                    // D2D1_RECT_F rf;
                    // rf.top = rc.top;
                    // rf.left = rc.left;
                    // rf.bottom = rc.bottom;
                    // rf.right = rc.right;
                    // D2D1_RECT_F srf;
                    // srf.top = 0;
                    // srf.left = 0;
                    // srf.bottom = rc.bottom-rc.top;
                    // srf.right = rc.right-rc.left;
                    
                    // draw the bitmap to the render target
                    m_pRenderTarget->BeginDraw();
                    m_pRenderTarget->DrawBitmap(m_bitmap,NULL, 1, 
                    D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, NULL);
                    hr = m_pRenderTarget->EndDraw();
                }
                
                // even if we do this, no dice
                //destroy_device_resources();
            }
        }
    }
}
HRESULT app::create_device_resources() {
    // Initialize device-dependent resources.
    HRESULT hr = S_OK;

    if (m_pRenderTarget == NULL) {
        RECT rc;
        GetClientRect(m_hwnd, &rc);

        D2D1_SIZE_U size = D2D1::SizeU(
            rc.right - rc.left+1,
            rc.bottom - rc.top+1);
        D2D1_RENDER_TARGET_PROPERTIES rprops = 
                           D2D1::RenderTargetProperties();
        // Create a Direct2D render target.
        hr = m_pDirect2dFactory->CreateHwndRenderTarget(
            rprops,
            D2D1::HwndRenderTargetProperties(m_hwnd, size),
            &m_pRenderTarget);

        if (SUCCEEDED(hr)) {
            D2D1_BITMAP_PROPERTIES props;
            props.dpiX = rprops.dpiX;
            props.dpiY = rprops.dpiY;
            D2D1_PIXEL_FORMAT pixelFormat = D2D1::PixelFormat(
            DXGI_FORMAT_B8G8R8A8_UNORM,
            D2D1_ALPHA_MODE_IGNORE
            );
            props.pixelFormat = pixelFormat;
            size.width = 640;
            size.height = 480;
            hr = m_pRenderTarget->CreateBitmap
                 (size,m_frame_buffer,size.width*4, props, &m_bitmap);
            
            if(m_bitmap==NULL) {
                hr = E_UNEXPECTED;
            }
        } 
    }
    return hr;
}
Posted
Updated 13-Sep-23 7:32am
v4

1 solution

You should made proper question naming as your question is about Direct2D not the DirectDraw - different technologies. Anyway here is the simple workable example implementation of what you need. It trigger repaint event each second and update buffer with the random color.
/////////////////////////////////////////////////////
#include <windows.h>
#include <conio.h>
#include <atlbase.h>
#include <assert.h>
#include <d2d1.h>
/////////////////////////////////////////////////////
#pragma comment (lib, "d2d1.lib")
/////////////////////////////////////////////////////
// Exit Flag
HANDLE g_hQuit = NULL;
// Ready to quit flag
HANDLE g_hReady = NULL;
/////////////////////////////////////////////////////
CComPtr<ID2D1HwndRenderTarget> m_pRenderTarget = nullptr;
CComPtr<ID2D1Factory> m_pFactory = nullptr;
CComPtr<ID2D1Bitmap> m_pBitmap = nullptr;
uint8_t * m_frame_buffer = nullptr;
SIZE Resolution = {640,480};
/////////////////////////////////////////////////////
// Console Handler
BOOL WINAPI ConsoleHandlerRoutine(_In_  DWORD dwCtrlType)
{
	if (dwCtrlType == CTRL_CLOSE_EVENT || dwCtrlType == CTRL_LOGOFF_EVENT 
            || dwCtrlType == CTRL_SHUTDOWN_EVENT)
	{
		SetEvent(g_hQuit);
		WaitForSingleObject(g_hReady,INFINITE);
	}
	return TRUE;
}
/////////////////////////////////////////////////////
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if (uMsg == WM_SIZE) {
		if (m_pRenderTarget) {
			D2D1_SIZE_U size = D2D1::SizeU(LOWORD(lParam), HIWORD(lParam));
			m_pRenderTarget->Resize(size);
		}
	}
	if (uMsg == WM_CLOSE) {
		SetEvent(g_hQuit);
	}
	if (uMsg == WM_PAINT) {
		if (m_pRenderTarget && m_pBitmap) {

			DWORD ticks = GetTickCount();
			DWORD color = (DWORD)(sin(ticks * 3.1456 / 180) * 0x00ffffff)
                            | 0xff000000;
			uint32_t * p = (uint32_t *)m_frame_buffer;
			for (int y = 0; y < Resolution.cy; y++) {
				for (int x = 0; x < Resolution.cx; x++) {
					*p++ = color;
				}
			}

			HRESULT hr;
			hr = m_pBitmap->CopyFromMemory(NULL,
                                    m_frame_buffer,Resolution.cx * 4);
			assert(hr == S_OK);
			m_pRenderTarget->BeginDraw();
			D2D1_RECT_F rect_dest = {
				0,
				0,
				Resolution.cx,
				Resolution.cy
			};
			m_pRenderTarget->DrawBitmap(m_pBitmap,
				rect_dest,1.0f,D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,NULL);
			hr = m_pRenderTarget->EndDraw();
			assert(hr == S_OK);
		}
	}
	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
/////////////////////////////////////////////////////
int main(int argc, char* argv[]) {

	g_hQuit  = CreateEvent(NULL, TRUE, FALSE, NULL);
	g_hReady = CreateEvent(NULL, TRUE, FALSE, NULL);
	CoInitializeEx(0, COINIT_MULTITHREADED);

	static WCHAR szClassName[100];
	WNDCLASSW wc;
	HINSTANCE hInstance = GetModuleHandle(NULL);
	swprintf_s(szClassName, _countof(szClassName), L"%s%d", 
               L"TempClass", GetTickCount());
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName = NULL;
	wc.hIcon = NULL;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.lpszClassName = szClassName;

	RegisterClassW(&wc);

	HRESULT hr = S_OK;

	HWND hwnd = CreateWindowExW(
		WS_EX_OVERLAPPEDWINDOW | WS_EX_APPWINDOW
		, szClassName, L"Rendering",
		WS_OVERLAPPEDWINDOW,
		0, 0,
		(UINT)(640),
		(UINT)(480),
		NULL, NULL, hInstance, NULL);

	assert(IsWindow(hwnd));
	if (!IsWindow(hwnd)) goto exit;
	hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pFactory);
	assert(hr == S_OK);
	if (hr != S_OK) goto exit;
	{
		m_frame_buffer = (uint8_t *)malloc(
                                    Resolution.cx * Resolution.cy * 4);
		uint32_t * p = (uint32_t *)m_frame_buffer;
		for (int y = 0; y < Resolution.cy; y++) {
			for (int x = 0; x < Resolution.cx; x++) {
				*p++ = 0xff000000;
			}
		}
	}
	{
		RECT rc;
		GetClientRect(hwnd, &rc);
		D2D1_SIZE_U size = D2D1::SizeU(
                                       rc.right - rc.left, 
                                       rc.bottom - rc.top
                                      );

		hr = m_pFactory->CreateHwndRenderTarget(
            D2D1::RenderTargetProperties(),
			D2D1::HwndRenderTargetProperties(hwnd, size),
			&m_pRenderTarget
		);
		assert(hr == S_OK);
		if (hr != S_OK) goto exit;
	}
	{
		D2D1_SIZE_U size = {0};
		D2D1_BITMAP_PROPERTIES props;
		m_pRenderTarget->GetDpi(&props.dpiX,&props.dpiY);
		D2D1_PIXEL_FORMAT pixelFormat = D2D1::PixelFormat(
			DXGI_FORMAT_B8G8R8A8_UNORM,
			D2D1_ALPHA_MODE_IGNORE
		);
		props.pixelFormat = pixelFormat;
		size.width = Resolution.cx;
		size.height = Resolution.cy;
		
		hr = m_pRenderTarget->CreateBitmap(size, 
                                           m_frame_buffer, 
                                           size.width * 4, 
                                           props, 
                                           &m_pBitmap);
		assert(hr == S_OK);
		if (hr != S_OK) goto exit;
	}
	ShowWindow(hwnd,SW_SHOWNORMAL);
	UpdateWindow(hwnd);
	SetTimer(hwnd,(UINT_PTR)m_pBitmap.p,1000,NULL);

	HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
	HANDLE hHandles[] = {hConsole,g_hQuit};
	bool bQuit = false;
	while (!bQuit) {
		DWORD result = 0;
		if (hwnd) {
			result = MsgWaitForMultipleObjects(_countof(hHandles),hHandles,
                                           FALSE,INFINITE,QS_ALLEVENTS);
		}
		else {
			result = WaitForMultipleObjects(_countof(hHandles),hHandles,
                                           FALSE,INFINITE);
		}
		if (result == WAIT_OBJECT_0 + _countof(hHandles)) {
			MSG msg = { 0 };
			while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
				if (msg.message == WM_QUIT) {
					bQuit = true;
					break;
				}
				if (msg.message == WM_TIMER) {
					InvalidateRect(hwnd,NULL,FALSE);
				}
				if (msg.message == WM_KEYDOWN) {
					if (msg.wParam == VK_ESCAPE) {
						bQuit = true;
						break;
					}
				}
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		} 
		if (result == WAIT_OBJECT_0) {
			DWORD Events = 0;
			DWORD temp;
			GetNumberOfConsoleInputEvents(hConsole,&Events);
			while (Events--) {
				INPUT_RECORD rec = { 0 };
				if (!ReadConsoleInput(hConsole,&rec,1,&temp)) break;
				if (rec.EventType == KEY_EVENT) {
					if ((rec.Event.KeyEvent.bKeyDown) 
           && (rec.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)) {
						bQuit = true;
						break;
					}
				}
			}
		}
		if (WAIT_OBJECT_0 == WaitForSingleObject(g_hQuit, 0)) {
			bQuit = true;
		}
	}

exit:
	if (IsWindow(hwnd)) {
		DestroyWindow(hwnd);
	}
	SetEvent(g_hReady);
	SetConsoleCtrlHandler(ConsoleHandlerRoutine, FALSE);

	m_pRenderTarget.Release();
	m_pBitmap.Release();
	m_pFactory.Release();
	CoUninitialize();
	if (m_frame_buffer) {
		free(m_frame_buffer);
	}
	CloseHandle(g_hQuit);
	CloseHandle(g_hReady);
}
/////////////////////////////////////////////////////
 
Share this answer
 
Comments
honey the codewitch 13-Sep-23 9:58am    
Thanks. I'll give it a shot. I meant Direct2D, and yet always type DirectDraw because I learned DirectX ages ago, and the old tech stuck. I hardly do any windows development at all. Mostly embedded so I don't have much reason to try to retrain myself.
Maxim Kartavenkov 13-Sep-23 10:21am    
I see, you probably forgot to call triggering window redraw such as InvalidateRect or InvalidateRgn or RedrawWindow once your new frame comes up, as you mentioned that drawing called from WM_PAINT. You also can draw outside of WM_PAINT once the new frame comes and buffer updated. But on WM_PAINT just draw the latest frame in case if streaming stopped
honey the codewitch 13-Sep-23 10:26am    
Oh I'm calling InvalidateRect in my message loop for now.
Maxim Kartavenkov 13-Sep-23 10:35am    
Ok, then if it does not work compare example which I made with your code. You can build this example as console application. The destination bitmap updated every repaint call with random color. Also in the output buffer you may not specify alpha value, but initialize render target which check for alpha, so your data output is fully transparency, so set alpha value to 0xff - like I do in random color preparing.
honey the codewitch 13-Sep-23 10:39am    
In my code, any pixel created with an alpha channel, such as the bgra_pixel<32> used above has it's alpha defaulted to full opacity, in this case 0xFF at 8-bits per channel.

gfx::draw::filled_rectangle(bmp,bmp.bounds(),color_type::purple);

This line should fill m_frame_buffer appropriately with like 0xFF 0x00 0xFF 0xFF like that. (or similar, I actually don't know what the X11 values for purple are off the top of my head)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900