Help painting pixels to the screen - handmade hero day 004

dont want to take you guys time so gonna go straight to the point, i've tried everything i could to try and paint pixels to the window in the day 004 video and failed, i really
dont know what else to do, the window just wont change color no matter what, if anyone could shine a light here i'd be thankful, looking forward to you guys response and thx a lot!

#include <windows.h>
#include <winnt.h>
#include <stdint.h>
#define internalscope static // static variable cant be accessed by other files
#define localscope static // refer to static local variables as localscope
#define globalscope static //refer to static global variables as globalscope


/* typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO, *PBITMAPINFO; */

typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;

typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
globalscope bool running;
globalscope BITMAPINFO bitmapinfo; // static vars are automatically initialized to zero
globalscope void *bitmapmemory;
globalscope int bitmapwidth;
globalscope int bitmapheight;

internalscope void win32resizeDIBsection(int width, int height) //DIB device independent bitmap
{

if(bitmapmemory){
VirtualFree(bitmapmemory,0 ,MEM_RELEASE);
}

bitmapwidth = width;
bitmapheight= height;

bitmapinfo.bmiHeader.biSize = sizeof(bitmapinfo.bmiHeader);
bitmapinfo.bmiHeader.biWidth = bitmapwidth;
bitmapinfo.bmiHeader.biHeight = -bitmapheight;
bitmapinfo.bmiHeader.biPlanes = 1;
bitmapinfo.bmiHeader.biBitCount = 32;
bitmapinfo.bmiHeader.biCompression = BI_RGB;
bitmapinfo.bmiHeader.biSizeImage = 0;
bitmapinfo.bmiHeader.biXPelsPerMeter = 0;
bitmapinfo.bmiHeader.biYPelsPerMeter = 0;
bitmapinfo.bmiHeader.biClrUsed = 0;
bitmapinfo.bmiHeader.biClrImportant = 0;

int bytesperpixel = 4; // number of bytes per each pixel on the screen
int bitmapmemorysize = (bitmapwidth*bitmapheight) * bytesperpixel;

bitmapmemory = VirtualAlloc(0,bitmapmemorysize, MEM_COMMIT, PAGE_READWRITE);

//TODO:draw pixels to the screen, day 004

int pitch = width*bytesperpixel;
uint8 *row = (uint8 *)bitmapmemory;

for(int y=0; y < bitmapheight;++y){

uint8 *pixel = (uint8 *)row;

for(int x=0; x < bitmapwidth;++x){

*pixel = 255;
++pixel;

*pixel = 0;
++pixel;

*pixel = 0;
++pixel;

*pixel = 0;
++pixel;


}
row += pitch;

}
}


internalscope void win32updatewindow(HDC DeviceContext, RECT *windowrect,
int x, int y, int width, int height)
{

int windowwidth = windowrect -> left - windowrect -> right;
int windowheight = windowrect -> top - windowrect -> bottom;

StretchDIBits(
DeviceContext,
/*x, y, width, height,
x, y, width, height,*/
0, 0 , bitmapwidth, bitmapheight,
0, 0 , windowwidth, windowheight,
bitmapmemory, &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);



}

LRESULT CALLBACK win32mainwindowcallback( //MainWindowProc function <-- main window procedure
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{

LRESULT Result = 0;

switch(uMsg)
{

case WM_ACTIVATEAPP:
{

}
break;

case WM_CLOSE:
{
running = false;
}
break;

case WM_DESTROY:
{

running = false;
}
break;

case WM_SIZE:
{

RECT clientrect;
GetClientRect(hwnd, &clientrect);
int width = clientrect.right - clientrect.left;
int height = clientrect.bottom - clientrect.top;
win32resizeDIBsection(width, height);

}
break;

case WM_PAINT:
{


PAINTSTRUCT Paint;
HDC DeviceContext = BeginPaint(hwnd,&Paint);

int x = Paint.rcPaint.left;
int y = Paint.rcPaint.top;
int width = Paint.rcPaint.right - Paint.rcPaint.left;
int height = Paint.rcPaint.bottom - Paint.rcPaint.top;

RECT clientrect;
GetClientRect(hwnd, &clientrect);

win32updatewindow(DeviceContext, &clientrect, x, y, width, height);
//PatBlt(DeviceContext,x,y,width,height,WHITENESS);
EndPaint(hwnd,&Paint);

}
break;

default:
{
Result = DefWindowProc(hwnd,uMsg,wParam,lParam);//assigns the function DefWindowProc to Result
}
break;
}
return(Result);

}



int CALLBACK WinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow)
{


WNDCLASS windowclass = {CS_OWNDC|CS_HREDRAW|CS_VREDRAW,win32mainwindowcallback,0,0,hInstance,0,0,0,0,"Handmadeherowindowclass"};

if (RegisterClassA(&windowclass)) { RegisterClass


HWND windowhandle = CreateWindowExA(0,windowclass.lpszClassName,"Handmade Hero",
WS_OVERLAPPEDWINDOW|WS_VISIBLE,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,0,0,hInstance,0);


if(windowhandle){

running = true;

while(running){

MSG Message;
BOOL MessageResult = GetMessageA(&Message,0,0,0);


if(MessageResult > 0){
TranslateMessage(&Message);
DispatchMessage(&Message); //sends the message to the Main Window Callback
}

else
{
break;
}
}
}
else
{
// logging
}
}
else
{

//logging
}
return 0;
};
Please use the code tag for code in your post.

The problem is that you are passing negative width and height to StretchDIBits.

To know if StretchDIBits fails, you can "capture" its return value in a variable and set a breakpoint in visual studio (F9 on the line you want) so see the value. The msdn documentation will give you the expected return value. In this case, if it returns 0 it has failed.

Once you know it has failed, the next step would be to check if the parameters are valid. You can set a breakpoint in visual studio to inspect the values before calling the function.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
internalscope void win32updatewindow(HDC DeviceContext, RECT *windowrect,
                                     int x, int y, int width, int height)
{
    
    // int windowwidth = windowrect -> left - windowrect -> right;
    int windowwidth = windowrect -> right - windowrect -> left;
    // int windowheight = windowrect -> top - windowrect -> bottom;
    int windowheight = windowrect -> bottom - windowrect -> top;
    
    int result = StretchDIBits(
        DeviceContext,
        /*x, y, width, height,
        x, y, width, height,*/
        0, 0 , bitmapwidth, bitmapheight,
        0, 0 , windowwidth, windowheight,
        bitmapmemory, &bitmapinfo, DIB_RGB_COLORS, SRCCOPY);
    
    if ( result == 0 ) {
        int x = 5; // Just to be able to set a break point.
    }   
}


You're also missing the comment '//' on the line:
1
if (RegisterClassA(&windowclass)) { // RegisterClass

Edited by Simon Anciaux on Reason: Typos
Hi, I also have this problem too.

I checked the return value for StrechDIBits, and its 720 (which I expected), so it works but I can only see 1 pixel at top (EDIT:I can see 1 row).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
internal void
Win32DisplayBufferInWindow(HDC DeviceContext, int WindowWidth, int WindowHeight,
	win32_offscreen_buffer Buffer,
	int X, int Y, int Width, int Height)
{

	int result = StretchDIBits(DeviceContext,
		0, 0, WindowWidth, WindowHeight,
		0, 0, Buffer.Width, Buffer.Height, 
		Buffer.Memory,
		&Buffer.Info,
		DIB_RGB_COLORS, SRCCOPY);

}


and this is the weird function in case I typed something wrong:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
internal void 
RenderWeirdGradient(win32_offscreen_buffer Buffer, int BlueOffset, int GreenOffset)
{

	uint8 *Row = (uint8 *)Buffer.Memory;

	for (int Y = 0; Y < Buffer.Height; ++Y)
	{
		uint32 *Pixel = (uint32 *)Row;
		for (int X = 0; X < Buffer.Width; ++X)
		{

			uint8 Blue = (X + BlueOffset);
			uint8 Green = (Y + GreenOffset);

			*Pixel++ = ((Green << 0) | Blue);
		}
		Row += Buffer.Pitch;
	}

}


Btw, Win10, Visual Studio 2015, if that's matter.

Thanks.

Edited by Muhammed Yaman on
Does WindowWidth, WindowHeight and Buffer.Width and Buffer.Height have good values? Is Bitmap.Info set up properly?
mmozeiko
Does WindowWidth, WindowHeight and Buffer.Width and Buffer.Height have good values? Is Bitmap.Info set up properly?


Window width, height and Buffer width,heigth
http://prnt.sc/drijsv

Buffer settings here.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
internal void
Win32ResizeDIBSection(win32_offscreen_buffer *Buffer, int Width, int Height)
{
	if (Buffer->Memory)
	{
		VirtualFree(Buffer->Memory, 0, MEM_RELEASE);
	}

	Buffer->Width = Width;
	Buffer->Height = Height;
	Buffer->BytesPerPixel = 4;
	 
	Buffer->Info.bmiHeader.biSize = sizeof(Buffer->Info.bmiHeader);
	Buffer->Info.bmiHeader.biWidth = Buffer->Width;
	Buffer->Info.bmiHeader.biHeight = -(Buffer->Height);
	Buffer->Info.bmiHeader.biPlanes = 1;
	Buffer->Info.bmiHeader.biBitCount = 32;
	Buffer->Info.bmiHeader.biCompression = BI_RGB;

	int BitmapMemorySize = ((Buffer->Width)*(Buffer->Height))*(Buffer->BytesPerPixel);

	Buffer->Memory = VirtualAlloc(0, BitmapMemorySize, MEM_COMMIT, PAGE_READWRITE);

	int Pitch = ((Buffer->Width) * (Buffer->BytesPerPixel));

}

Edited by Muhammed Yaman on
Your prnt dot sc link points to this topic. That doesn't help much.


Edited by Mārtiņš Možeiko on
Can you check again? should be fixed now. Sorry about that
Ok, the values are reasonable inside Win32DisplayBufferInWindow function. What about inside RenderWeirdGradient function? No need to post screenshot, just check the values yourself with debugger.
Where do you call RenderWeirdGradient function? Is it called at all? Do you call Win32DisplayBufferInWindow afterwards?
mmozeiko
Ok, the values are reasonable inside Win32DisplayBufferInWindow function. What about inside RenderWeirdGradient function?


Checked they are ok too.

mmozeiko
Where do you call RenderWeirdGradient function? Is it called at all? Do you call Win32DisplayBufferInWindow afterwards?


in while(Running) loop,

and yep I call Win32DisplayBufferInWindow.



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
				
                                //After Peek Message loop

                                RenderWeirdGradient(&GlobalBackbuffer, XOffset, YOffset);


				HDC DeviceContent = GetDC(Window);
				
				win32_window_dimension Dimension = Win32GetWindowDimension(Window);

				Win32DisplayBufferInWindow(DeviceContent, Dimension.Width, Dimension.Height,
					&GlobalBackbuffer, 0, 0, 
					Dimension.Width, Dimension.Height);
				ReleaseDC(Window, DeviceContent);



				++XOffset;
				++YOffset;


JFYI I can only see 1 row of pixels at top and they are moving.

Edited by Muhammed Yaman on
Hmm, what about Buffer.Pitch value inside RenderWeirdGradient function?
mmozeiko
Hmm, what about Buffer.Pitch value inside RenderWeirdGradient function?


Thank you soooooooooooooooo much!


Instead of Buffer->Pitch I wrote this

1
int Pitch = ((Buffer->Width) * (Buffer->BytesPerPixel));


Thanks again!
No problem :)
Just learn to use debugger more - examine all the values to see if they are what you expect them to be!