1#include "OffscreenContext.h" 2#include "imageutils.h" 3#include "fbo.h" 4#include <iostream> 5#include <sstream> 6 7#import <AppKit/AppKit.h> // for NSOpenGL... 8#include <CoreServices/CoreServices.h> 9#include <sys/utsname.h> 10 11#define REPORTGLERROR(task) { GLenum tGLErr = glGetError(); if (tGLErr != GL_NO_ERROR) { std::cout << "OpenGL error " << tGLErr << " while " << task << "\n"; } } 12 13struct OffscreenContext 14{ 15 NSOpenGLContext *openGLContext; 16 NSAutoreleasePool *pool; 17 int width; 18 int height; 19 fbo_t *fbo; 20}; 21 22#include "OffscreenContextAll.hpp" 23 24std::string offscreen_context_getinfo(OffscreenContext *) 25{ 26 struct utsname name; 27 uname(&name); 28 29 SInt32 majorVersion,minorVersion,bugFixVersion; 30 31 Gestalt(gestaltSystemVersionMajor, &majorVersion); 32 Gestalt(gestaltSystemVersionMinor, &minorVersion); 33 Gestalt(gestaltSystemVersionBugFix, &bugFixVersion); 34 35 const char *arch = "unknown"; 36 if (sizeof(int*) == 4) arch = "32-bit"; 37 else if (sizeof(int*) == 8) arch = "64-bit"; 38 39 std::ostringstream out; 40 out << "GL context creator: Cocoa / CGL\n" 41 << "PNG generator: Core Foundation\n" 42 << "OS info: Mac OS X " << majorVersion << "." << minorVersion << "." << bugFixVersion << " (" << name.machine << " kernel)\n" 43 << "Machine: " << arch << "\n"; 44 return out.str(); 45} 46 47OffscreenContext *create_offscreen_context(int w, int h) 48{ 49 OffscreenContext *ctx = new OffscreenContext; 50 ctx->width = w; 51 ctx->height = h; 52 53 ctx->pool = [NSAutoreleasePool new]; 54 55 // Create an OpenGL context just so that OpenGL calls will work. 56 // Will not be used for actual rendering. 57 58 NSOpenGLPixelFormatAttribute attributes[] = { 59 NSOpenGLPFANoRecovery, 60 NSOpenGLPFADepthSize, 24, 61 NSOpenGLPFAStencilSize, 8, 62// Enable this to force software rendering 63// NSOpenGLPFARendererID, kCGLRendererGenericID, 64// Took out the acceleration requirement to be able to run the tests 65// in a non-accelerated VM. 66// NSOpenGLPFAAccelerated, 67 (NSOpenGLPixelFormatAttribute) 0 68 }; 69 NSOpenGLPixelFormat *pixFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes] autorelease]; 70 71 // Create and make current the OpenGL context to render with (with color and depth buffers) 72 ctx->openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixFormat shareContext:nil]; 73 if (!ctx->openGLContext) { 74 std::cerr << "Unable to create NSOpenGLContext\n"; 75 return nullptr; 76 } 77 78 [ctx->openGLContext makeCurrentContext]; 79 80 // glewInit must come after Context creation and before FBO calls. 81 GLenum err = glewInit(); 82 if (GLEW_OK != err) { 83 std::cerr << "Unable to init GLEW: " << glewGetErrorString(err) << std::endl; 84 return nullptr; 85 } 86 glew_dump(); 87 88 ctx->fbo = fbo_new(); 89 if (!fbo_init(ctx->fbo, w, h)) { 90 return nullptr; 91 } 92 93 return ctx; 94} 95 96bool teardown_offscreen_context(OffscreenContext *ctx) 97{ 98 fbo_unbind(ctx->fbo); 99 fbo_delete(ctx->fbo); 100 101 /* 102 * Cleanup 103 */ 104 [ctx->openGLContext clearDrawable]; 105 [ctx->openGLContext release]; 106 107 [ctx->pool release]; 108 return true; 109} 110 111/*! 112 Capture framebuffer from OpenGL and write it to the given ostream 113*/ 114bool save_framebuffer(const OffscreenContext *ctx, std::ostream &output) 115{ 116 if (!ctx) return false; 117 // Read pixels from OpenGL 118 int samplesPerPixel = 4; // R, G, B and A 119 int rowBytes = samplesPerPixel * ctx->width; 120 unsigned char *bufferData = (unsigned char *)malloc(rowBytes * ctx->height); 121 if (!bufferData) { 122 std::cerr << "Unable to allocate buffer for image extraction."; 123 return 1; 124 } 125 glReadPixels(0, 0, ctx->width, ctx->height, GL_RGBA, GL_UNSIGNED_BYTE, 126 bufferData); 127 REPORTGLERROR("reading pixels from framebuffer"); 128 129 // Flip it vertically - images read from OpenGL buffers are upside-down 130 unsigned char *flippedBuffer = (unsigned char *)malloc(rowBytes * ctx->height); 131 if (!flippedBuffer) { 132 std::cout << "Unable to allocate flipped buffer for corrected image."; 133 return 1; 134 } 135 flip_image(bufferData, flippedBuffer, samplesPerPixel, ctx->width, ctx->height); 136 137 bool writeok = write_png(output, flippedBuffer, ctx->width, ctx->height); 138 139 free(flippedBuffer); 140 free(bufferData); 141 142 return writeok; 143} 144 145