使用 GL_RGB 格式抓取 Mac OS 屏幕
Posted
技术标签:
【中文标题】使用 GL_RGB 格式抓取 Mac OS 屏幕【英文标题】:Grab Mac OS Screen using GL_RGB format 【发布时间】:2010-01-25 12:33:57 【问题描述】:我正在使用 glgrab 代码尝试抓取 Mac 屏幕的全屏屏幕截图。但是,我希望位图数据采用 GL_RGB 格式。也就是说,每个像素的格式应该是:
0x00RRGGBB
原始代码指定GL_BGRA
格式。但是,将其更改为 GL_RGB
会给我一个完全空白的结果。我正在使用的总源代码是:
CGImageRef grabViaOpenGL(CGDirectDisplayID display, CGRect srcRect)
CGContextRef bitmap;
CGImageRef image;
void * data;
long bytewidth;
GLint width, height;
long bytes;
CGColorSpaceRef cSpace = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB);
CGLContextObj glContextObj;
CGLPixelFormatObj pixelFormatObj ;
GLint numPixelFormats ;
//CGLPixelFormatAttribute
int attribs[] =
// kCGLPFAClosestPolicy,
kCGLPFAFullScreen,
kCGLPFADisplayMask,
NULL, /* Display mask bit goes here */
kCGLPFAColorSize, 24,
kCGLPFAAlphaSize, 0,
kCGLPFADepthSize, 32,
kCGLPFASupersample,
NULL
;
if ( display == kCGNullDirectDisplay )
display = CGMainDisplayID();
attribs[2] = CGDisplayIDToOpenGLDisplayMask(display);
/* Build a full-screen GL context */
CGLChoosePixelFormat( (CGLPixelFormatAttribute*) attribs, &pixelFormatObj, &numPixelFormats );
if ( pixelFormatObj == NULL ) // No full screen context support
// GL didn't find any suitable pixel formats. Try again without the supersample bit:
attribs[10] = NULL;
CGLChoosePixelFormat( (CGLPixelFormatAttribute*) attribs, &pixelFormatObj, &numPixelFormats );
if (pixelFormatObj == NULL)
qDebug("Unable to find an openGL pixel format that meets constraints");
return NULL;
CGLCreateContext( pixelFormatObj, NULL, &glContextObj ) ;
CGLDestroyPixelFormat( pixelFormatObj ) ;
if ( glContextObj == NULL )
qDebug("Unable to create OpenGL context");
return NULL;
CGLSetCurrentContext( glContextObj ) ;
CGLSetFullScreen( glContextObj ) ;
glReadBuffer(GL_FRONT);
width = srcRect.size.width;
height = srcRect.size.height;
bytewidth = width * 4; // Assume 4 bytes/pixel for now
bytewidth = (bytewidth + 3) & ~3; // Align to 4 bytes
bytes = bytewidth * height; // width * height
/* Build bitmap context */
data = malloc(height * bytewidth);
if ( data == NULL )
CGLSetCurrentContext( NULL );
CGLClearDrawable( glContextObj ); // disassociate from full screen
CGLDestroyContext( glContextObj ); // and destroy the context
qDebug("OpenGL drawable clear failed");
return NULL;
bitmap = CGBitmapContextCreate(data, width, height, 8, bytewidth,
cSpace, kCGImageAlphaNoneSkipFirst /* XRGB */);
CFRelease(cSpace);
/* Read framebuffer into our bitmap */
glFinish(); /* Finish all OpenGL commands */
glPixelStorei(GL_PACK_ALIGNMENT, 4); /* Force 4-byte alignment */
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
/*
* Fetch the data in XRGB format, matching the bitmap context.
*/
glReadPixels((GLint)srcRect.origin.x, (GLint)srcRect.origin.y, width, height,
GL_RGB,
#ifdef __BIG_ENDIAN__
GL_UNSIGNED_INT_8_8_8_8_REV, // for PPC
#else
GL_UNSIGNED_INT_8_8_8_8, // for Intel! http://lists.apple.com/archives/quartz-dev/2006/May/msg00100.html
#endif
data);
/*
* glReadPixels generates a quadrant I raster, with origin in the lower left
* This isn't a problem for signal processing routines such as compressors,
* as they can simply use a negative 'advance' to move between scanlines.
* CGImageRef and CGBitmapContext assume a quadrant III raster, though, so we need to
* invert it. Pixel reformatting can also be done here.
*/
swizzleBitmap(data, bytewidth, height);
/* Make an image out of our bitmap; does a cheap vm_copy of the bitmap */
image = CGBitmapContextCreateImage(bitmap);
/* Get rid of bitmap */
CFRelease(bitmap);
free(data);
/* Get rid of GL context */
CGLSetCurrentContext( NULL );
CGLClearDrawable( glContextObj ); // disassociate from full screen
CGLDestroyContext( glContextObj ); // and destroy the context
/* Returned image has a reference count of 1 */
return image;
我对 OpenGL 完全陌生,所以我很感激一些正确方向的指示。干杯!
更新:
经过一些实验,我设法缩小了我的问题范围。我的问题是,虽然我不想要 alpha 组件,但我确实希望将每个像素打包到 4 字节边界。现在,当我为 glReadPixels
调用指定 GL_RGB 或 GL_BGR 格式时,我将位图数据打包成 3 字节块。当我指定GL_RGBA
或GL_BGRA
时,我得到四个字节块,但总是最后一个alpha 通道组件。
然后我尝试更改传递给的值
bitmap = CGBitmapContextCreate(data, width, height, 8, bytewidth,cSpace, kCGImageAlphaNoneSkipFirst /* XRGB */);
但是,AlphaNoneSkipFirst
或 AlphaNoneSkipLast
的任何变体都不会将 Alpha 通道放在像素字节块的开头。
有什么想法吗?
【问题讨论】:
【参考方案1】:我不是 Mac 用户,但如果您可以获得 RGBA 数据并想要 XRGB,您不能将每个像素向下移动 8 位吗?
foreach( unsigned int* RGBA_pixel, pixbuf )
(*RGBA_pixel) = (*RGBA_pixel) >> 8;
【讨论】:
是的,但这肯定会非常非常慢吗?如果屏幕数据是 1440x900,即 1296000 像素,每个像素都必须单独移动。一个更聪明和讨厌的 hack 是在像素数组的开头添加一个字节并将所有内容都视为 XRGB.... 并非如此。粗略地说,假设单周期位移,在 2GHz CPU 上它可能需要不到一毫秒的时间。【参考方案2】:尝试使用GL_UNSIGNED_BYTE
而不是GL_UNSIGNED_INT_8_8_8_8_REV
/ GL_UNSIGNED_INT_8_8_8_8
。
虽然您似乎想要 GL_RGBA 代替 - 但它应该与 8_8_8_8_REV 或 8_8_8_8 一起使用。
【讨论】:
【参考方案3】:当我使用 GL_BGRA 时,返回的数据是预先混合的,这得到了确认,因为当我在窗口中显示结果时颜色看起来正确。
如果您需要我创建的项目,请与我联系。希望这会有所帮助。
【讨论】:
以上是关于使用 GL_RGB 格式抓取 Mac OS 屏幕的主要内容,如果未能解决你的问题,请参考以下文章
在 glCopyTexImage1D() OpenGL 3.3 中使用 GL_RGB10_A2UI 内部格式
Swift 4 - 在 mac os 上使用 AVAssetWriter 进行 avfoundation 屏幕和音频录制 - 视频冻结