HDC Question

Hi

Pre-stream yesterday I asked a question regarding the definition of HDC which Casey kindly responded to but unfortunately my 5 year old interrupted my listening and I missed the full answer.

My question was what was the definition of HDC and where can the related information be found as according to MSDN, HDC seems to be a part of Direct X that should not be used directly. I basically would like to understand how it works.....

I got the bit from Casey that said its basically a Handle to a Device Context but even that escapes me somehow....e.g. what exactly is a device context and how does windows create it/establish it?

Can anyone help by providing the answer that Casey provided or hopefully, Casey won't mind answering this again :-)

Thanks in advance
You can watch it again for another 2 weeks in the twitch past broadcasts section;
http://www.twitch.tv/handmade_hero/profile/past_broadcasts
Fair point...didnt think of that. Thanks. But if anyone feels like elaborating more on the answer to the question, feel free :-)

Edited by Gav on
I think this page does an adequate job of explaining device contexts. To paraphrase, a device context is Windows' way of shielding you from the differences in drawing interfaces. A device context can be for a screen plugged into the computer, or for a printer or (I believe) even for a bitmap that only exists in memory. So in principle, if you write a program that produces a picture, you can just change where you acquire your device context from and voila, you are displaying the image on screen, print it out or write it to memory and subsequently save it as a BMP file.

Before you can use a device context, you need to acquire a handle to it. This handle is opaque, meaning that the Windows API manages the actual data associated with a device context handle internally, with no way for you to directly access that data. You need to use the functions provided by Windows to extract information like the underlying device's DPI, color capabilities and so on.

The API also offers functions for directly drawing to a device context, e.g. drawing a line or filling an ellipsis. In Casey's platform layer, he mostly uses the StretchDIBits function to draw a previously prepared bitmap to the device.

[quote=Gavzooka]
HDC seems to be a part of Direct X that should not be used directly

I haven't worked with DirectX much, but I think it uses its own device context internally (maybe you can also pass it one). It's probably not a good idea to use this device context directly, e.g. telling it to draw a line somewhere on it, because DirectX assumes it has exclusive access to this context. Instead, you should tell DirectX to draw a line and it will issue the correct commands to the device context.
Thanks.

So Casey said its a Handle to a Device Context which is opaque to the user. So we should think of it as a pointer where we set stuff on and execute....

So I can see how this works with StretchDIBits. Assuming I read it correctly, we tell the system which Device Context to use, tell it about what its copying and how and the function basically executes using that detail.

To help me play and understand, can anyone provide details of some additional functions that I could try with? I know when Casey was setting up the back buffer he mentioned there was several methods that could be used but opted for StretchDIBits. What other functions could have been used instead for example?

Edited by Gav on
The microsoft documentation is really not that bad if you follow the link that Nimbal posted you will see a list of operations which is an overview and there's a link just the draw functions there. If your intereset is all the bitmap function then look at this page.

Edited by Bigpet on
Historically, the device context was a handle Windows used to allow you to set the state of the graphics code in Windows. Graphics is tricky that way, and always has been - there are typically so many possible parameters to an operation that you cannot pass them all every time, and many have consequences in terms of preparation time such that you may want to set them once and let dependent values be cached, etc.

So in the original Windows graphics subsystem, you had device contexts, and these device context stored the state of the graphics system. You would set things on them like the pattern fill you wanted solid shapes to use, the pen color for lines, the line stipple pattern, the scaling, the origin of the coordinate system, the font for text rendering, the clipping region, etc., etc., etc. You would then issue graphics commands that used these things, like MoveTo/LineTo or FillRect.

This would directly invoke the Windows low-level graphics routines which, using the state in the device context, would actually go fill the pixels! That is how it used to work.

Nowadays, the HDC is still around as a handle to the DC, but you don't see it used very much (especially not in games) because typically all it is used for is to tell Windows what display surface you're talking about. But instead of actually using that context as it was originally intended (to issue lots of high-level drawing commands), you typically just use it to open an OpenGL or Direct3D handle and you do all your drawing through the 3D API. Even in our case, where we are temporarily going old-school and drawing all the pixels ourselves, we basically don't use the HDC for anything other than copying our bitmap onto the screen.

But old Windows programs used to use the device context more more, so it was less of a useless abstraction then.

- Casey
I think OP was trying to do something similar to me in my Windows/Debugger post today. Just trying to draw some simple things for some instant gratification/experimentation early on.

If you check out my Windows/Debugger post, I had problems with this. I know I'm doing something really wrong, but I get different results in debugger versus running code at full speed. Trying to figure out why that should ever happen.
Thanks all

@jloiterman - No, afraid not. I just like to know the finite detail of how things work the way they do and why. Casey explanation above answers that in sufficient detail for me to move on now :-) but before I was struggling to accept the purpose of a HDC. Until I fully understand the ins and outs of each days code, I don't and won't move on...understand of HDC was evading me!
One thing while I think of it.

A "handle" is, in data structure parlance, a doubly indirect reference. Somewhere, behind the scenes, is a structure which holds the data:

1
2
3
struct device_context {
    // some stuff
};


An indirect reference to one of these structures might be a pointer. A doubly indirect reference could be a pointer to a pointer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
typedef struct device_context** HDC;

void DoSomethingWithDC(HDC hDC)
{
    struct device_context* dc = *hDC;
    if (!dc) {
      // Null pointer
      return;
    }
    // Do stuff
}


Or, more commonly, an index into an array (or other collection) of pointers:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#define MAX_HANDLES some_number

struct device_context* DCHandleTable[MAX_HANDLES];

typedef uint32_t HDC;

void DoSomethingWithDC(HDC hDC)
{
    struct device_context* dc = DCHandleTable[hDC];
    if (!dc) {
      // Null pointer
      return;
    }
    // Do stuff
}


So why would you bother using a doubly-indirect reference, as opposed to just a pointer?

One reason is safety. If you're in the habit of freeing objects (Casey isn't, but Windows is), then this gives you a pointer which points to free memory. Attempting to write to it would corrupt the heap.

Using handles lets you free an object, but still ensure that nobody knows about a bad pointer:

1
2
3
4
void DeleteDC(HDC hDC) {
    free(DCHandleTable[hDC]);
    DCHandleTable[hDC] = 0;
}


(It's possible that handles could get reused, and there are steps you can take to mitigate that, but it will still point to a valid object even if it's not the right valid object.)

Another reason is security, and this is why operating systems use them a lot. You don't want the user to be able to manufacture a handle to an object for which it has no permissions. All sufficiently modern operating systems have them; Unix-like operating systems, for example, have process ids and file handles.

Managing handles and the objects they point to is the central thing that the Windows kernel does. To ntoskrnl, pretty much everything is an object referenced by a handle.

I mention all this because "big" game engines often use handles to manage game entities, especially for multiplayer games where you can't transport pointers across a network. Handmade Hero very likely won't, of course. But handles are ubiquitous, and it's worth understanding what they are.