我有一个类似的问题,并找到了一个更优雅的解决方案。基本上,您将 CAEAGLLayer 子类化,并添加您自己的 renderInContext 实现,它只是要求 OpenGL 视图使用 glReadPixels 呈现内容。美妙之处在于,现在您可以在层次结构中的任何层上调用 renderInContext,结果是一个完整的、完美的屏幕截图,其中包括您的 OpenGL 视图!
我们在子类 CAEAGLLayer 中的 renderInContext 是:
- (void)renderInContext:(CGContextRef)ctx
{
[super renderInContext: ctx];
[self.delegate renderInContext: ctx];
}
然后,在 OpenGL 视图中,我们替换 layerClass 以便它返回我们的子类而不是普通的 CAEAGLLayer:
+ (Class)layerClass
{
return [MyCAEAGLLayer class];
}
我们在视图中添加一个方法来实际将视图的内容渲染到上下文中。请注意,此代码必须在您的 GL 视图被渲染之后,但在您调用之前运行,presentRenderbuffer
以便渲染缓冲区将包含您的帧。否则,生成的图像很可能是空的(您可能会在此特定问题上看到设备和模拟器之间的不同行为)。
- (void) renderInContext: (CGContextRef) context
{
GLint backingWidth, backingHeight;
// Bind the color renderbuffer used to render the OpenGL ES view
// If your application only creates a single color renderbuffer which is already bound at this point,
// this call is redundant, but it is needed if you're dealing with multiple renderbuffers.
// Note, replace "_colorRenderbuffer" with the actual name of the renderbuffer object defined in your class.
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
// Get the size of the backing CAEAGLLayer
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
NSInteger x = 0, y = 0, width = backingWidth, height = backingHeight;
NSInteger dataLength = width * height * 4;
GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte));
// Read pixel data from the framebuffer
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
// Create a CGImage with the pixel data
// If your OpenGL ES content is opaque, use kCGImageAlphaNoneSkipLast to ignore the alpha channel
// otherwise, use kCGImageAlphaPremultipliedLast
CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGImageRef iref = CGImageCreate(width, height, 8, 32, width * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast,
ref, NULL, true, kCGRenderingIntentDefault);
CGFloat scale = self.contentScaleFactor;
NSInteger widthInPoints, heightInPoints;
widthInPoints = width / scale;
heightInPoints = height / scale;
// UIKit coordinate system is upside down to GL/Quartz coordinate system
// Flip the CGImage by rendering it to the flipped bitmap context
// The size of the destination area is measured in POINTS
CGContextSetBlendMode(context, kCGBlendModeCopy);
CGContextDrawImage(context, CGRectMake(0.0, 0.0, widthInPoints, heightInPoints), iref);
// Clean up
free(data);
CFRelease(ref);
CFRelease(colorspace);
CGImageRelease(iref);
}
最后,为了获取屏幕截图,您可以使用通常的方式使用 renderInContext。当然,美妙之处在于您不需要直接抓取 OpenGL 视图。您可以抓取 OpenGL 视图的其中一个超级视图,并获得一个组合屏幕截图,其中包括 OpenGL 视图以及它旁边或顶部的任何其他内容:
UIGraphicsBeginImageContextWithOptions(superviewToGrab.bounds.size, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
[superviewToGrab.layer renderInContext: context]; // This recursively calls renderInContext on all the sublayers, including your OpenGL layer(s)
CGImageRef screenShot = UIGraphicsGetImageFromCurrentImageContext().CGImage;
UIGraphicsEndImageContext();