iDevGames Forums
Properly minimizing an OpenGL view - Printable Version

+- iDevGames Forums (http://www.idevgames.com/forums)
+-- Forum: Development Zone (/forum-3.html)
+--- Forum: Graphics & Audio Programming (/forum-9.html)
+--- Thread: Properly minimizing an OpenGL view (/thread-6611.html)

Pages: 1 2


Properly minimizing an OpenGL view - arekkusu - Dec 3, 2003 07:05 PM

(this is something to put in the FAQ)

Did you ever notice that when you minimize the window of an OpenGL game, sometimes the window turns all white while it is genie-ing into the dock? Or, the dock icon is all white?

Nearly all of the uDevGame entries had this problem, so here's the answer for Cocoa people. You need to read the GL framebuffer and draw it into the underlying Quartz view:

Code:
// window delegate methods
- (void)windowWillMiniaturize:(NSNotification *)notification {
    [self copyGLtoQuartz];    
    [[self window] setOpaque:NO];        // required to make the Quartz underlay and the window shadow appear correctly
}


- (void)windowDidMiniaturize:(NSNotification *)notification {
    [[self window] setOpaque:YES];
}

// NSOpenGLView subclass methods
- (BOOL)isFlipped {
    return YES;                            // required for proper minimization, must sync ortho view
}


- (void)copyGLtoQuartz {
    NSSize  size = [self frame].size;
    GLfloat zero = 0.0f;
    long    rowbytes = size.width * 4;
    rowbytes = (rowbytes + 3)& ~3;            // ctx rowbytes is always multiple of 4, per glGrab
    NSBitmapImageRep *minicon = [[NSBitmapImageRep alloc]
        initWithBitmapDataPlanes:nil
        pixelsWide:size.width
        pixelsHigh:size.height
        bitsPerSample:8
        samplesPerPixel:3
        hasAlpha:NO
        isPlanar:NO
        colorSpaceName:NSDeviceRGBColorSpace
        bytesPerRow:rowbytes
        bitsPerPixel:32];
    
    [ctx makeCurrentContext];
    glFinish();                                // finish any pending OpenGL commands
    glPushAttrib(GL_ALL_ATTRIB_BITS);        // reset all properties that affect glReadPixels, in case app was using them
    glDisable(GL_COLOR_TABLE);
    glDisable(GL_CONVOLUTION_1D);
    glDisable(GL_CONVOLUTION_2D);
    glDisable(GL_HISTOGRAM);
    glDisable(GL_MINMAX);
    glDisable(GL_POST_COLOR_MATRIX_COLOR_TABLE);
    glDisable(GL_POST_CONVOLUTION_COLOR_TABLE);
    glDisable(GL_SEPARABLE_2D);
    
    glPixelMapfv(GL_PIXEL_MAP_R_TO_R, 1, &zero);
    glPixelMapfv(GL_PIXEL_MAP_G_TO_G, 1, &zero);
    glPixelMapfv(GL_PIXEL_MAP_B_TO_B, 1, &zero);
    glPixelMapfv(GL_PIXEL_MAP_A_TO_A, 1, &zero);
    
    glPixelStorei(GL_PACK_SWAP_BYTES, 0);
    glPixelStorei(GL_PACK_LSB_FIRST, 0);
    glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
    glPixelStorei(GL_PACK_ALIGNMENT, 4);    // force 4-byte alignment from RGBA framebuffer
    glPixelStorei(GL_PACK_ROW_LENGTH, 0);
    glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
    glPixelStorei(GL_PACK_SKIP_ROWS, 0);
    glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
    
    glPixelTransferi(GL_MAP_COLOR, 0);
    glPixelTransferf(GL_RED_SCALE, 1.0f);
    glPixelTransferf(GL_RED_BIAS, 0.0f);
    glPixelTransferf(GL_GREEN_SCALE, 1.0f);
    glPixelTransferf(GL_GREEN_BIAS, 0.0f);
    glPixelTransferf(GL_BLUE_SCALE, 1.0f);
    glPixelTransferf(GL_BLUE_BIAS, 0.0f);
    glPixelTransferf(GL_ALPHA_SCALE, 1.0f);
    glPixelTransferf(GL_ALPHA_BIAS, 0.0f);
    glPixelTransferf(GL_POST_COLOR_MATRIX_RED_SCALE, 1.0f);
    glPixelTransferf(GL_POST_COLOR_MATRIX_RED_BIAS, 0.0f);
    glPixelTransferf(GL_POST_COLOR_MATRIX_GREEN_SCALE, 1.0f);
    glPixelTransferf(GL_POST_COLOR_MATRIX_GREEN_BIAS, 0.0f);
    glPixelTransferf(GL_POST_COLOR_MATRIX_BLUE_SCALE, 1.0f);
    glPixelTransferf(GL_POST_COLOR_MATRIX_BLUE_BIAS, 0.0f);
    glPixelTransferf(GL_POST_COLOR_MATRIX_ALPHA_SCALE, 1.0f);
    glPixelTransferf(GL_POST_COLOR_MATRIX_ALPHA_BIAS, 0.0f);
    glReadPixels(0, 0, size.width, size.height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, [minicon bitmapData]);
    glPopAttrib();

    [self lockFocus];
    [minicon drawInRect:frame];
    [minicon release];

    [self unlockFocus];
    [[self window] flushWindow];
}

Note 1: This setup requires the viewport coordinate system to have the origin in the upper left. For example in an orthographic projection:

Code:
glViewport(0, 0, frame.size.width, frame.size.height);
    gluOrtho2D(0, frame.size.width, frame.size.height, 0);        // must be flipped to match the NSView, origin at TOP left

If your origin is in the lower left, like in Quartz, then you have to flip the NSImage upside down in CopyGLtoQuartz.

Note 2: This doesn't handle a GL view which has a translucent background. This is only an issue if you are overlaying GL content on Quartz content like in the UnderlaySurface example. See the GLUT source code if you need to do this, it involves fixing up the alpha component of each pixel.

Note 3: If your app uses GLUT, then all of this is already done for you.

Note 4: You'll also have to do this if you want to print your NSOpenGLView.

(Edit: fixed typos induced by lack of sleep)


Properly minimizing an OpenGL view - codemattic - Dec 3, 2003 07:35 PM

THANK YOU. this drove me crazy.


Properly minimizing an OpenGL view - codemattic - Dec 3, 2003 07:36 PM

where is ctx defined/initialized?

thanks, Codemattic


Properly minimizing an OpenGL view - arekkusu - Dec 3, 2003 07:50 PM

Code:
// NSOpenGLView subclass header
NSOpenGLContext *ctx;

Depending how you construct your view, you either make this yourself, or you can get it from the system with [NSOpenGLContext currentContext];

Don't forget, if you have multiple contexts or use threads, you must set the current context before ANY gl calls (resize drawRect, dealloc, etc);


Properly minimizing an OpenGL view - Bachus - Dec 3, 2003 11:39 PM

Ah thank you. I keep forgetting to add code like this to Gaichu. Mental note...


Properly minimizing an OpenGL view - geezusfreeek - Dec 3, 2003 11:56 PM

Somebody should add this to the source code section of the site. It would be a good addition.


Properly minimizing an OpenGL view - DoG - Dec 4, 2003 03:03 AM

If we only had it in Carbon, too. Though I am sure there are some Apple examples on this, somebody should extract the relevant code.


Properly minimizing an OpenGL view - OneSadCookie - Dec 4, 2003 03:38 AM

It's pretty easy in Carbon, too -- Make a GWorld as if loading a texture via QuickTime, ReadPixels into the GWorld, then CopyBits the GWorld to the window.


Properly minimizing an OpenGL view - Programmer - Dec 4, 2003 09:05 AM

Strange -- I dropped this into my (non-game) app to try it and it doesn't even call those methods upon miniturization. Does this have to be a root view, or will it work with a pair of NSOpenGLView panes in a larger window?


Properly minimizing an OpenGL view - sohta - Dec 4, 2003 10:15 AM

Make sure that your glView (or whatever object you put the code in) is the window's delegate, then the code should be called when the window is minimised.


Properly minimizing an OpenGL view - arekkusu - Dec 4, 2003 10:42 AM

You'll have to do a little more work if you have multiple GLviews, or overlapping views, or etc. This simplified code is geared towards games where the window is one big GL context.

See the GLUT source for a complete recursive hierarchical copy.


Properly minimizing an OpenGL view - sohta - Dec 10, 2003 04:37 AM

I've finally added that sweet piece of code to my project (works great)

For those who don't want to change their coordinate system (like me). You can also simply flip the image as you copy it to the window. Here's a simple way to do it:

Code:
NSAffineTransform *mirror = [NSAffineTransform transform];
  [mirror  scaleXBy:1 yBy:-1];
  [mirror translateXBy:0 yBy: -size.height];
  [mirror concat];
  [minicon drawInRect:[glWindow frame]];
  [mirror invert];
  [mirror concat];

Thanks a lot!


Properly minimizing an OpenGL view - aaronsullivan - Oct 18, 2004 09:53 PM

Anybody ever try to get this to work with an SDL app? I'm assuming it would go into SDLMain.m, but I'm shaky on Cocoa and I could be wrong. Also, I'm not sure how to get the ctx from SDL. Any help on this would be great!


Properly minimizing an OpenGL view - arekkusu - Oct 19, 2004 02:29 PM

I believe SDL is built on top of CGL, not NSGL. So you can use CGLGetCurrentContext() at any time after the context is created and active. Substitute the proper SDLImage stuff for NSBitmapImageRep stuff too.


Properly minimizing an OpenGL view - DoG - Oct 19, 2004 02:51 PM

Since NSGL (and AGL, for that matter)is a layer above CGL, you can probably use CGL calls no matter how the context was created.