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