The code from this video runs on my PC (CPU i7 3770k) at around 2100 FPS. The footage of the program running at the end of the video is slightly modified - I believe I just made a simple loop to draw 20 pixels per frame instead of 1. I should have gone back and edited the code shown in the video accordingly, to avoid causing confusion when you run the code and see the animation run much slower. With 640x480 = 307k pixels, at 2000 FPS you should expect it to take about 2.5 minutes to fill the window with pixels, so don't be surprised when it doesn't appear to go as fast as in the video. You can speed up the animation by putting a simple for loop around the 2 lines that draw pixels to the screen. Sorry for the confusion; I'll do my best to avoid such mistakes in my future videos.
Hey, just a small correction for Win32 geeks, the HDC doesn't point to the HBITMAP object actually. The SelectObject sets the HBITMAP object you give to it as the default bitmap object that will be affected by the drawing functions on that HDC. That means, you select that bitmap into the HDC. And, every HDC has a default bitmap selected into it, otherwise you cannot draw on that HDC.
Hello! Thanks for the amazing tutorial, I actually ported it successfully to MASM64 assembly! One thing though, Im drawing rectangles and when I move them around the pixels don't redraw so the rects end up leaving a persistent trail, this is because the drawing pixels array is persistent from frame to frame. Is there a way to zero it out at the end of every frame or do I have to do it manually?
Really a very good tutorial! Thank you very much! I really like your style of programming! Besides explicitly casting to (void**)&frame.pixels in line 84, you should look at line 48 again: static unsigned int p = 0; frame.pixels[(p++)%(frame.width*frame.height)] = border32(); The idea would be elegant, however you are willfully letting p overflow with it. Are you sure that the overflow of an unsigned int is not a UB? For signed int the overflow would be UB for sure.
Did you get that error running the code exactly as provided? I am not able to reproduce it. During CreateWindow, the WindowProcessMessage function is called with several messages. Since the WS_VISIBLE flag is passed, this includes the WM_WINDOWPOSCHANGED message, which then through DefWindowProc sends a WM_SIZE message. Therefore, a WM_SIZE message is handled before the CreateWindow function returns and the main loop is entered. So frame_bitmap gets initialized with CreateDIBSection before the first main loop ever calls PeekMessage().
@@UltimaN3rd Kinda. :) I've changed variables names to my style, and organized global vars into one structure, and moved buffer-filling code into separate function for future experiments. Everything else is the same. I ran it through debagger step-by-step and there was jump straight to buffer-filling function without getting WM_SIZE message and thus skipping that branch. It caused Access violation (obviously, we didn't allocate buffer memory at that moment yet). So I initialized buffer with the code from WM-SIZE message branch right after window creation. And everything worked. Actually, now I think, overall it's a safer way to allocate memory without any conditions before trying to write something into it. Because if somehow (like in my case) condition will not be met, there may be an error. I use Windows 7 (may be this is the cause of not getting WM_SIZE message without resizing window?) and Pelles C as IDE/compiler.
@@1kvolt1978 Ah I see, most likely it's a difference in the Windows 7 Win32 API. When I started these tutorials I had intended to write my code such that it would work as far back as possible - probably Windows XP. However I quickly found an important feature of the Win32 API related to high resolution timing that was improved in Windows 10 2004, and eventually decided to just target Windows 10 at a minimum. Perhaps I should have made that clear though in my Windows programming tutorials.
Yes I sped it up to show the full effect. If you want to test how fast it's actually going you can use the code from my recent tutorial on high precision timing. In my upcoming series of tutorials on making a complete game I'll be showing how to put the rendering code on a separate thread to keep it running at a high refresh rate.
There's an error on line 91 when you pass frame.pixels to CreateDIBSection. What you have to do is: (void*)&frame.pixels Because the function takes a void pointer.
Yes, you're right. When I wrote this code I was compiling with Microsoft's C compiler which didn't throw an error or warning, but with GCC you do need to cast frame.pixels to a void pointer.
Yes. But its ja (void**) type not a (void*). So: (void**)&frame.pixels msdn CreateDIBSection: HBITMAP CreateDIBSection( [in] HDC hdc, [in] const BITMAPINFO *pbmi, [in] UINT usage, [out] VOID **ppvBits, [in] HANDLE hSection, [in] DWORD offset ); VOID **ppvBits is type (void**)
@@shankar4510 A void pointer is a pointer to an "unknown type". The address to which the pointer points could be anything. For this reason, the following things are not allowed with a void pointer and lead to undefined behavior: - Dereferencing a void pointer - Pointer arithmetic on void pointers. However, void pointers may be compared with each other and they may also be checked for NULL. Note: ppvBits is not a void pointer, but a pointer to a void pointer, i.e. void**.
@Nick Walton Hey great tutorials! I'm having a little trouble regarding the CreateDIBSection() function within the WindowProcessMessage() function. In the CreateDIBSection() the fourth argument takes a "void**" pointer to a pointer type, but then we are passing in a "uint32_t**" type. When I try to use this, visual studio gives an error stating " 'uint32_t **' is incompatible with parameter of type 'void **' " I do not know what to do beyond this point. I'm trying to do this tutorial in c++ so maybe that's the issue. (I mainly wanted to make my own game engine with GDI but there aren't really any good tutorials in c++ or even GDI in general, and then yours showed up, so thanks btw).
Could you make a code sample for cheking if a particular pixel on the screen changed color and initiate a mouse click on the same coordinate in case of that event?
@@UltimaN3rd Anywhere on the screen e.g if a pixel changes color inside a browser window or on the desktop (E.g because I hit the meta key and the windows start menu popped up causing the pixels near that area to change color )
@@billkillernic I don't have the time right now to write that code, but I believe you could put it together from these two links: docs.microsoft.com/en-us/windows/win32/gdi/capturing-an-image docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput You could continually capture screenshots and compare them to detect updated pixels, then send the mouse clicks you want. Very inefficient but should get the job done.
hey man, great explanation, but i tried the code, got a couple problems that i resolved, only one i am not sure if i resolved right. Using visula studio 2019 it is complaining the entry point is not main. I changed it to WinMain to main and deleted WINAPI next to it. Now it runs, i get a console but nothing else. should i have solved it differently ?
In the text version of this tutorial, you can download the code and build script directly: croakingkero.com/tutorials/drawing_pixels_win32_gdi/ Try following the instructions there (everthing before the "Code Walkthrough") and see if it compiles with the original code and build script downloaded from my site. Cheers.
@@UltimaN3rd Hey man, thanks for the answer, i actually decided to take a step back as it seemed a bit too complicated. I am familiarizing myself with the topic first using Wxwidgets, then it should become easier to take a deeper dive into Windowsapi. Thanks again!
I've got two problems with this tutorial... The first is that GCC is warning me that casting from uint32_t * to void ** in CreateDIBSection is a bad idea.. But that's just a warning. The part that is confusing me is that even though I've linked both gdi32.lib and user32.lib in the command line, it still gives me undefined reference errors for CreateCompatibleDC, BitBlt, DeleteObject, CreateDIBSection, and SelectObject. What's extra weird is that I know wingdi is being included since at the same time CreateDIBSection is being declared as an undefined reference, it's also warning me about incompatible pointer types. Any ideas?
What is the exact command you use to build the code with GCC? I was able to compile with this: "gcc main.c -lgdi32" Perhaps your version of GCC/MinGW is no good in some way. If the build command I pasted above doesn't work, what is the output of "gcc -v"?
I don't plan on using many libraries for these tutorials; I'd rather code as low level as possible for now (except for audio). However I've never used Allegro before so I'll take a good look at it.
seems not compatible with MinGW: pixelsbuf.c: In function `LRESULT WindowProcessMessage(HWND__*, UINT, WPARAM, LPARAM)': pixelsbuf.c:84: error: invalid conversion from `uint32_t**' to `void**' pixelsbuf.c:84: error: initializing argument 4 of `HBITMAP__* CreateDIBSection(HDC__*, const BITMAPINFO*, UINT, void**, void*, DWORD)'