arekkusu
2003.12.03, 10: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:
// 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:
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)
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:
// 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:
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)