how can I create OpenGL context using Windows memory dc (c++) - c++

In my Windows MFC application, in its View class I created an OpenGL context using View's DC:
HANDLE * hdc = GetDC()->m_hdc;
int nPixelFormat;
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // Size of this structure
1, // Version of this structure
PFD_DRAW_TO_WINDOW | // Draw to window (not bitmap)
PFD_SUPPORT_OPENGL | // Support OpenGL calls
PFD_DOUBLEBUFFER, // Double -buffered mode
PFD_TYPE_RGBA, // RGBA Color mode
24, // Want 24bit color
0,0,0,0,0,0, // Not used to select mode
0,0, // Not used to select mode
0,0,0,0,0, // Not used to select mode
32, // Size of depth buffer
0, // Not used to select mode
0, // Not used to select mode
PFD_MAIN_PLANE, // Draw in main plane
0, // Not used to select mode
0,0,0 }; // Not used to select mode
// Choose a pixel format that best matches that described in pfd
nPixelFormat = ChoosePixelFormat(hdC, &pfd);
// Set the pixel format for the device context
assert(SetPixelFormat(hdC, nPixelFormat, &pfd));
HGLRC m_hrc = wglCreateContext(hdc);
assert(m_hrc);
wglMakeCurrent(m_hdc,m_hrc);
All the code above works all right, and I can do OpenGL drawings as expected.
But , What I need now is to change the DC to memory dc instead of window DC . To be exact, how can I use the 'hmemDC' bellow to create an OpenGL context like the way I did above with window DC:
CRect rct;
GetClientRect(&rct);
HDC hmemDC = CreateCompatibleDC(pDC->m_hDC);
HBITMAP hBmp = CreateCompatibleBitmap(pDC->m_hDC,rct.Width(),rct.Height());
with the same pixel format constructed above, I came across the "Invalid pixel format" error in calling wglCreateContext() , can not success in getting the correct OpenGL context.
I googled a lot , and tryed to change some of the values of pixel format, the result was the same.
Is it possible to create OpenGL context with Windows Memory DC? and if it is how should I do it?
Edit:
This is why I need a bitmap ( or Memory DC ): I created a 2d map rendering library which uses OpenGL. The client want to use this library to render background map, and draw its own symbols on top of it . But, they prefer to use Windows GDI other than OpenGL to draw their symbols. So I thought if I can provide them with a bitmap or a Mmeory DC, they could to what they want. Any better solutions? Am I in the right direction? Or it is a total bad idea to provide such a 2d library in OpenGL backend.

This can't be done in a useful way.
You can in principle render to a bitmap by using the PFD_DRAW_TO_BITMAP flag instead of PFD_DRAW_TO_WINDOW in the PIXELFORMATDESCRIPTOR.
However, doing so will disable all hardware accelearated rendering. This will fall back to Microsofts default OpenGL 1.1 implementation.
If you want hw-accleration and/or modern GL, you either need a window or some offscreen buffer like a pbuffer, which is available via the WGL_ARB_pbuffer extension. However, in modern GL, you are probably better off creating a window which is just never shown, and using a Frambeuffer Object as the offscreen render target.
In either case, you will have to copy the data back to the CPU, if you need it as some bitmap there.

Put in few words: You can't create arbitrary OpenGL contexts for MemDCs. At least no kind of OpenGL context you'd actually want to use.
If your goal is off-screen rendering either create a PBuffer-DC; which requires to create a OpenGL context first which in turn required to create a window and setting its pixel format. Or you can just create a window and a OpenGL context for it and use a framebuffer object.

Related

Getting a window's pixel format on GLFW in linux

I want to get a GLFW window's pixel format. I'm using ubuntu so win32 functions are out of the picture. I've stumbled upon this question but there are only win32 answers and there is an answer that uses HDC and PIXELFORMATDESCRIPTOR which I don't have access to (Since I will not be using the function permanently I rather not install a new library for this.)
I want to get the format in the form of YUV420P or RGB24.
Conceptually the closest thing to a pixelformat in X11 is a so called Visual (X11 core) or FBConfig (through GLX extension).
First you need the native window handle. You can retrieve this from GLFW using
Window x11win = glfwGetX11Window(glfwwin);
A window's visual can be queried using XGetWindowAttributes
XWindowAttributs winattr;
XGetWindowAttributes(display, x11win, &winattr);
// winattr->visual
// use it to query a XVisualInfo which can then be
// passed to glXGetConfig
The FBConfig can be queried using glXQueryDrawable
unsigned fbconfigid;
glXQueryDrawable(display, x11win, GLX_FBCONFIG_ID, &fbconfigid);
unsigned n_fbconfigs;
GLXFBConfig glxfbconfigs = glXGetFBConfigs(display, screen, &n_fbconfigs);
if( fbconfigid >= n_fbconfigs ){ raise_error(...); }
int attribute_value;
glXGetFBConfigAttrib(display, glxfbconfigs[fbconfigid], GLX_…, &attribute_value);
XFree(fbconfigs);
That is outside the scope of GLFW as can be read here:
Framebuffer related attributes
GLFW does not expose attributes of the default framebuffer (i.e. the framebuffer attached to the window) as these can be queried directly with either OpenGL, OpenGL ES or Vulkan.
If you are using version 3.0 or later of OpenGL or OpenGL ES, the glGetFramebufferAttachmentParameteriv function can be used to retrieve the number of bits for the red, green, blue, alpha, depth and stencil buffer channels. Otherwise, the glGetIntegerv function can be used.
Hint:
Don't rely on (if you've created the window with the videomode of the specified monitor and didn't tinkered with framebuffers):
GLFWvidmode *vid_mode = glfwGetVideoMode(glfwGetWindowMonitor(win));
vid_mode->redBits;
vid_mode->greenBits;
vid_mode->blueBits;
because in glfwCreateWindow we read the following:
The created window, framebuffer and context may differ from what you requested, as not all parameters and hints are hard constraints. This includes the size of the window, especially for full screen windows. To query the actual attributes of the created window, framebuffer and context, see glfwGetWindowAttrib, glfwGetWindowSize and glfwGetFramebufferSize.

OpenGL get Device Context

I am trying to create an OpenGL application on windows. As far as I can understand, one of the first things I must acquire is a Device Context, which must be passed on to a couple of functions that choose and set a pixel format and create a rendering context. I used the OpenGL wiki to get a rough idea about what I should do.
My code is something like:
#include <iostream>
#include <windef.h>
#include <wingdi.h>
HDC hdc;
int main() {
hdc = wglGetCurrentDC();
std::cout << "HDC: " << hdc << std::endl;
return 0;
}
This prints
HDC: 0
I assumed a Device Context refers to a physical device, but I read somewhere that it refers to any drawable "surface". In both cases is my question: how can I obtain a non-null DC? Or should I perform a completely different set of steps in order to set up this whole OpenGL system?
I found a lot of tutorials online, but they all use GLUT, GLEW, GLFW, X11, SDL etc. which are libraries. Libraries make certain things easier, but they usually do not perform tasks that are impossible without using them. This time, I want to try to do things the hard way and therefore use no libraries, just plain OpenGL.
I found, at last, a tutorial that only used the windows libraries for creating a window.
You did not state your OS but I assume Windows from the function names. The problem is exactly as Reto Koradi stated in the comment. To set up OpenGL you need to do this:
Obtain OS handle to object with valid device context
It can be OS window or OS bitmap. If you have just console app then you need to create a valid OS window first and use its handle (to my knowledge console does not have Canvas).
you can use GLUT for the window creation or If your compiler IDE has an window App you can use that. You can also combine OpenGL and Window components. VCL is also not a problem (I am using it for years with OpenGL)
In windows you can use CreateWindowEx so google an example for it...
Anyway you should have your handle in a variable like:
HWND hwin=NULL;
If you have no experience with windows applications then use GLUT for this. Otherwise you would need to learn a lot of stuff just to cover window creation, message handling of events and user/app interaction which can be really overwhelming for a rookie without guide.
Get Device context for that handle
HDC hdc = GetDC(hwin);
Set pixel format you need of device context
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory( &pfd, sizeof( pfd ) ); // set the pixel format for the DC
pfd.nSize = sizeof( pfd );
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 24;
pfd.iLayerType = PFD_MAIN_PLANE;
SetPixelFormat(hdc,ChoosePixelFormat(hdc, &pfd),&pfd);
Create OpenGL rendering context for device context
HGLRC hrc = wglCreateContext(hdc);
Set it as default OpenGL context
wglMakeCurrent(hdc, hrc);
This is absolute minimum without any error checking , additional buffers etc. For more info and actual code see related QA's:
How to render an openGL frame in C++ builder? for oldstyle GL
simple complete GL+VAO/VBO+GLSL+shaders example in C++ with the new stuff
You can use GLUT for all of this. This is first hit I found by quick search:
How can I set an OpenGL display (Window created by OpenGL) to maximized?
Or follow OpenGL tutorials there are tons of them out there ...

How can I draw inside two separate 3D windows within the same application on Windows using OpenGL?

I am implementing a plug-in inside a 3rd party program in C++ on Windows.
The 3rd party program has a window that displays 3D graphics using OpenGL.
However I need the plug-in to create another window that also displays 3D graphics using OpenGL.
Do I need to create a new OpenGL rendering context for my window or is there some way that I can "reuse" the OpenGL rendering context used by the 3rd party program?
I assumed that I had to create a new OpenGL rendering context and tried the following:
// create a rendering context
hglrc = wglCreateContext (hdc);
// make it the calling thread's current rendering context
wglMakeCurrent (hdc, hglrc);
However the last function failed.
Reading the documentation of wglMakeCurrent I notice that
A thread can have one current rendering context. A process can have multiple rendering contexts by means of multithreading.
Does this mean that my window need to run in a separate thread from the 3rd party program?
You didn't post error code generated by wglMakeCurrent(), so I won't be guessing the reason. It's not the binding itself, however. Sentence 'A thread can have one current rendering context' means, that new context will 'replace' the old one and become the current. I don't know why are you trying to set two contexts as current (or run another thread), but it's not the way to go. Avoid multithreading in rendering unless it's absolutely necessary.
So, answering your question:
Yes, you CAN 'reuse' OpenGL rendering context.
Why, you may ask? Rendering context is created for specific device context (HDC), which is exclusive property of each window (HWND)! How is this possible, then?!
Well, it seems somehow impossible because of function prototypes:
HWND my_window = CreateWindow(...);
HDC my_dc = GetDC(my_new_window);
//Setup pixel format for 'my_dc'...
HGLRC my_rc = wglCreateContext(my_dc);
wglMakeCurrent(my_dc, my_rc);
This really lets you think that rendering context is bound to this specific device context and valid only for it. But it's not.
The critical part is the comment (setup pixel format). Rendering context is created for specific CLASS of DCs, to be more precise: for DCs with the same pixel format. So code below is perfectly valid:
//window_1 = main window, window_2 = your window
HDC dc_1 = GetDC(window_1);
Set_pixel_format_for_dc_1(); //Usual stuff
HGLRC rc = wglCreateContext(dc_1);
wglMakeCurrent(dc_1, rc);
ultra_super_draw();
//.....
HDC dc_2 = GetDC(window_2);
//Get dc_1's PF to make sure it's compatible with rc.
int pf_index = GetPixelFormat(dc_1);
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR));
DescribePixelFormat(dc_1, pf_index, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
SetPixelFormat(dc_2, pf_index, &pfd);
wglMakeCurrent(dc_2, rc);
another_awesome_render();
wglMakeCurrent(NULL, NULL);
If you are still not convinced, MSDN:
wglMakeCurrent(hdc, hglrc): The hdc parameter must refer to a drawing surface supported by OpenGL. It need not be the same hdc that was passed to wglCreateContext when hglrc was created, but it must be on the same device and have the same pixel format.
I guess you are already familiar with these calls. Now, I don't know what are the conditions that your rendering must meet, but without additional requirements, I don't see any difficulties from this point:
HDC my_dc = Create_my_DC();
//...
void my_new_render
{
//Probably you want to save current binding:
HDC current_dc = wglGetCurrentDC();
HGLRC current_context = wglGetCurrentContext();
wglMakeCurrent(my_dc, current_context);
MyUltraSuperRender(...);
wglMakeCurrent(current_dc, current_context);
}
Hope this helps :)
First things first, you actually should create a separate OpenGL context for your plugin, for the simple reason that it gives you a separate state space that doesn't interfere with the main programs OpenGL context.
You misunderstood the part about multiple rendering contexts though. It's perfectly possible to have an arbitrary number of OpenGL contexts for a process. But each thread of the process can bind only one context at a time. That one binding also includes the window DC the context is bound to. It is however perfectly legal change a context binding at any time. Either you change the window a given context is bound to, or you switch the context or you do both at the same time.
So in your situation I suggest you create a custom context for your plug-in, that you use for all the windows your plug-in creates.
That your simple context "creation" code fails has one simple reason: Your window will most likely not have a pixel format descriptor set.
I suggest you use the following method to create your new windows and contexts:
/* first get hold of the HDC/HRC of the parent */
HDC parentDC = wglGetCurrentDC();
HRC parentRC = wglGetCurrentContext();
int pixelformatID = GetPixelFormat(parentDC);
/* we use the same PFD as the parent */
PIXELFORMATDESCRIPTOR pixelformat;
memset(pixelformat, 0, sizeof(pixelformat);
DescribePixelFormat(parentDC, pixelformatID, sizeof(pixelformat), &pixelformat);
/* create a window and set it's pixelformat to the parent one's */
HWND myWND = create_my_window();
HDC myDC = GetDC(myWND);
SetPixelFormat(myDC, pixelformatID, &pixelformat);
/* finally we can create a rendering context
* it doesn't matter if we create it against
* the parent or our own DC.
*/
HRC myRC = wglCreateContext(myDC);
/* we're done here... */
Now whenever your plugin wants to render something it should bind its own context, do its thing and bind the context that was bound before:
HDC prevDC = wglGetCurrentDC();
HRC prevRC = wglGetCurrentContext();
wglMakeCurrent(myDC, myRC);
/* do OpenGL stuff */
wglMakeCurrent(prevDC, prevRC);

Using CreateCompatibleDC with mapping modes other than MM_TEXT

I have a visual C++ application that uses a CView derived class to render its display, which is primarily 3d vector data and true type text. The mapping mode used is either MM_ANISOTROPIC or MM_LOMETRIC. I can't use MM_TEXT as I use the same code for printing and plotting of the data and also have to overcome non-square screen pixel issues.
The drawing code currently draws directly onto the screen using the CViews OnDraw method and the CDC object provided. I am trying to replace this with drawing to a bitmap and blitting the bitmap to screen, using a CreateCompatibleDC / CreateCompatibleBitmap combination, as described in the MS documentation and elsewhere.
The problem is that the DCs are not compatible for mapping modes other than MM_TEXT, such that my view is rendered upside down and at the wrong scale. Investigation shows the following;
void CMyView::OnDraw(CDC *pDC)
{
CDC MyDC = CreateCompatibleDC(pDC); // Create a new memory DC;
int a = pDC->GetMapMode(),b = MyDC.GetMapMode();
'
'
'
}
a = 2
b = 1
Calling a SetMapMode on MyDC causes the display to be drawn entirely in black. Do I have to rewrite my code to suit MM_TEXT for drawing to a bitmap, or is there another way to overcome this problem.
You probably need to also call SetWindowExt and SetViewportExt. I have definitely used MM_ISOTROPIC with bitmap DCs before and it worked OK (don't have the code to hand as it was since ported to GDI+)

How to Convert a gdi+ Bitmap-like struct into an HDC?

How to Convert a Bitmap-like struct into an HDC?
I am now writting image processing program in c++, gdi.
If I got a HDC.
I can draw whatever I like on the HDC in gdi by the following code.
// HDC is handy.
HDC dc;
dc.DrawXXX // I can draw using gdi method.
Graphics gr(dc); // now I can also draw on the dc using gdi+ method.
My Application is based on FreeImage.
I make of fipImage. ( use data struct like Bitmap )
However if I want to draw on fipWinImage, now I have to copy fipWinImageto Bitmap, then draw on the Bitmap, and finally convert the bitmap into fipImage again, which is time comsuming and memory comsuming.
Convert fipImage to Bitmap -> Draw on the bitmap -> convert bitmap to fipWinImage
fipWinImage imagefip;
Bitmap* tempImg = new Bitmap(imagefip->GetWidth(), imagefip.GetHeigt(), PixelFormat24bppRGB); // memory comsuming is image is large
Graphics *pGr = Graphics::FromImage(tempImg);
HDC dc = pGr->GetHDC();
RECT rec;
rec.left = 0;
rec.top = 0;
rec.right = imagefip.GetWidth();
rec.bottom = imagefip.GetHeight();
fipImage.draw(dc, rec); // using stretchdibits()
pGr->ReleaseHDC(dc);
Graphics gr(tempImg);
HDC dc = gr.GetHDC(); // Get an Hdc, draw using gdi method
gr.ReleaseHDC(tempDC); //
gr.drawXXX // Draw using gdi+ method.
fipWinImage fipImg; // final result fipWinImage.
HBITMAP temp;
Color color;
tempImg->GetHBITMAP(color, &temp);
fipImg->copyFromBitmap(temp);
I want to construct a HDC directly from fipImage. and draw directly on fipWinImage
How can I do this?
First a few clarifications:
A Device Context is basically a structure that remembers things like foreground and background colors, brushes, font info, and the physical drawing surface (bitmap).
This is a handy thing, so that you don't have to keep specifying all of these things when you're doing one graphics operation after another. You can also pass all of these settings around more easily this way. That's all that a DC really is - just a collection of drawing parameters, including the surface to draw upon.
An "HDC" is just a handle (reference) to one of these structs. Being a "Handle" lets window move the struct around in memory to manage free space without your pointers to it getting messed up.
If you have access to the source code for the library you're using, examine the fipWinImage::draw(...) method. If they're using StretchDIBits, then they must get their raw bitmap data into a compatible format at some point. It's also possible that the fipWinImage class is wrapping an underlying BITMAP or DIB, etc.
The final step to getting your own HDC...
A bitmap is "SELECTED" into a device context, and can only be selected into a single DC at one time. If you can get the internal HBITMAP from fipWinImage, you can select it into another DC (assuming that it isn't still selected into another HC).
When you create a DC, windows automatically creates a 1x1 bitmap for it (since a DC must have a selected bitmap at all times). When you select in a new bitmap, you get the handle to the previously selected bitmap returned to you. Hang on to that, because you're going to need to put it back when you're done.
Hope that helps.
I don't know FreeImage, but if you can get a pointer to the actual pixel data (DIB section) out of it, you could just create a HBITMAP that shares it without having to copy the data every time.