diff options
Diffstat (limited to 'tests/OffscreenContext.mm')
-rw-r--r-- | tests/OffscreenContext.mm | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/tests/OffscreenContext.mm b/tests/OffscreenContext.mm new file mode 100644 index 0000000..9a6a5e2 --- /dev/null +++ b/tests/OffscreenContext.mm @@ -0,0 +1,207 @@ +#include "OffscreenContext.h" + +#import <OpenGL/OpenGL.h> +#import <OpenGL/glu.h> // for gluCheckExtension +#import <AppKit/AppKit.h> // for NSOpenGL... + +// Simple error reporting macros to help keep the sample code clean +#define REPORT_ERROR_AND_EXIT(desc) { std::cout << desc << "\n"; return false; } +#define NULL_ERROR_EXIT(test, desc) { if (!test) REPORT_ERROR_AND_EXIT(desc); } + +struct OffscreenContext +{ + NSOpenGLContext *openGLContext; + NSAutoreleasePool *pool; + int width; + int height; + GLuint fbo; +}; + + +OffscreenContext *create_offscreen_context(int w, int h) +{ + OffscreenContext *ctx = new OffscreenContext; + ctx->width = w; + ctx->height = h; + + ctx->pool = [NSAutoreleasePool new]; + + /* + * Create an OpenGL context just so that OpenGL calls will work. I'm not + using it for actual rendering. + */ + + NSOpenGLPixelFormatAttribute attributes[] = { + NSOpenGLPFAPixelBuffer, + NSOpenGLPFANoRecovery, + NSOpenGLPFAAccelerated, + NSOpenGLPFADepthSize, 24, + (NSOpenGLPixelFormatAttribute) 0 + }; + NSOpenGLPixelFormat* pixFormat = [[[NSOpenGLPixelFormat + alloc] initWithAttributes:attributes] autorelease]; + // Create the OpenGL context to render with (with color and depth buffers) + ctx->openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixFormat + shareContext:nil]; + NULL_ERROR_EXIT(ctx->openGLContext, "Unable to create NSOpenGLContext"); + + [ctx->openGLContext makeCurrentContext]; + + /* + * Test if framebuffer objects are supported + */ + const GLubyte* strExt = glGetString(GL_EXTENSIONS); + GLboolean fboSupported = gluCheckExtension((const GLubyte*)"GL_EXT_framebuffer_object", strExt); + if (!fboSupported) + REPORT_ERROR_AND_EXIT("Your system does not support framebuffer extension - unable to render scene"); + /* + * Create an FBO + */ + GLuint renderBuffer = 0; + GLuint depthBuffer = 0; + // Depth buffer to use for depth testing - optional if you're not using depth testing + glGenRenderbuffersEXT(1, &depthBuffer); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthBuffer); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, w, h); + REPORTGLERROR("creating depth render buffer"); + + // Render buffer to use for imaging + glGenRenderbuffersEXT(1, &renderBuffer); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderBuffer); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h); + REPORTGLERROR("creating color render buffer"); + ctx->fbo = 0; + glGenFramebuffersEXT(1, &ctx->fbo); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ctx->fbo); + REPORTGLERROR("binding framebuffer"); + + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_RENDERBUFFER_EXT, renderBuffer); + REPORTGLERROR("specifying color render buffer"); + + if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != + GL_FRAMEBUFFER_COMPLETE_EXT) + REPORT_ERROR_AND_EXIT("Problem with OpenGL framebuffer after specifying color render buffer."); + + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, depthBuffer); + REPORTGLERROR("specifying depth render buffer"); + + if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != + GL_FRAMEBUFFER_COMPLETE_EXT) + REPORT_ERROR_AND_EXIT("Problem with OpenGL framebuffer after specifying depth render buffer."); + + return ctx; +} + +bool teardown_offscreen_context(OffscreenContext *ctx) +{ + // "un"bind my FBO + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + /* + * Cleanup + */ + [ctx->openGLContext clearDrawable]; + [ctx->openGLContext release]; + + [ctx->pool release]; + return true; +} + +bool save_framebuffer(OffscreenContext *ctx, const char *filename) +{ + /* + * Extract the resulting rendering as an image + */ + + int samplesPerPixel = 4; // R, G, B and A + int rowBytes = samplesPerPixel * ctx->width; + char* bufferData = (char*)malloc(rowBytes * ctx->height); + if (!bufferData) { + std::cerr << "Unable to allocate buffer for image extraction."; + return 1; + } + glReadPixels(0, 0, ctx->width, ctx->height, GL_BGRA, GL_UNSIGNED_BYTE, + bufferData); + REPORTGLERROR("reading pixels from framebuffer"); + + // Flip it vertically - images read from OpenGL buffers are upside-down + char* flippedBuffer = (char*)malloc(rowBytes * ctx->height); + if (!flippedBuffer) { + std::cout << "Unable to allocate flipped buffer for corrected image."; + return 1; + } + for (int i = 0 ; i < ctx->height ; i++) { + bcopy(bufferData + i * rowBytes, flippedBuffer + (ctx->height - i - 1) * + rowBytes, rowBytes); + } + + /* + * Output the image to a file + */ + CGColorSpaceRef colorSpace = + CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipFirst | + kCGBitmapByteOrder32Little; // XRGB Little Endian + int bitsPerComponent = 8; + CGContextRef contextRef = CGBitmapContextCreate(flippedBuffer, + ctx->width, ctx->height, bitsPerComponent, rowBytes, + colorSpace, bitmapInfo); + if (!contextRef) { + std::cerr << "Unable to create CGContextRef."; + return false; + } + + CGImageRef imageRef = CGBitmapContextCreateImage(contextRef); + if (!imageRef) { + std::cerr << "Unable to create CGImageRef."; + return false; + } + Boolean isDirectory = false; + CFStringRef fname = CFStringCreateWithCString(kCFAllocatorDefault, filename, kCFStringEncodingUTF8); + CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, + fname, kCFURLPOSIXPathStyle, isDirectory); + if (!fileURL) { + std::cerr << "Unable to create file URL ref."; + return false; + } + CGDataConsumerRef dataconsumer = CGDataConsumerCreateWithURL(fileURL); + + CFIndex fileImageIndex = 1; + CFMutableDictionaryRef fileDict = NULL; + CFStringRef fileUTType = kUTTypePNG; + // Create an image destination opaque reference for authoring an image file + CGImageDestinationRef imageDest = CGImageDestinationCreateWithDataConsumer(dataconsumer, + fileUTType, + fileImageIndex, + fileDict); + if (!imageDest) { + std::cerr << "Unable to create CGImageDestinationRef."; + return false; + } + CFIndex capacity = 1; + CFMutableDictionaryRef imageProps = + CFDictionaryCreateMutable(kCFAllocatorDefault, + capacity, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CGImageDestinationAddImage(imageDest, imageRef, imageProps); + CGImageDestinationFinalize(imageDest); + + free(flippedBuffer); + free(bufferData); + CFRelease(imageDest); + CFRelease(dataconsumer); + CFRelease(fileURL); + CFRelease(fname); + CFRelease(imageProps); + CGColorSpaceRelease(colorSpace); + CGImageRelease(imageRef); + return true; +} + +void bind_offscreen_context(OffscreenContext *ctx) +{ + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ctx->fbo); +} |