I’ve just spent many hours trying to get something that is essentially extremely simple working with Opengl and Objective C
Exporting an image in something like Processing is easy, you just say beginRaw() and endRaw() and just put your drawing content in between, being used to this I’m amazed the folks at Apple haven’t come up with something similar for OpenGL subviews.
Anyway the problem with OpenGL is that its a raster based system, which basically means exporting vector based images is a no go unless you fancy a headache, so I’ll settle for lousy bitmaps.
Just to help people I’ve shared the code to return NSData from a pixel buffer of your OpenGL view, this works quite well and renders out a PNG, although you can specify more formats if you so please, heres the basics on using this code.
First create an action, say that is generated from a button, mine looks like this in code:
-(IBAction)savePDF:(id)sender
{
__block NSSavePanel *panel = [NSSavePanel savePanel];
[panel setAllowedFileTypes:[NSArray arrayWithObject:@”png”]];
[panel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) {
if(result == NSOKButton){
//NSRect r = [self bounds];
NSData *data = [self glToUIImage];
NSError *error;
BOOL successful = [data writeToURL:[panel URL] options:0 error:&error];
if(!successful){
NSAlert *a = [NSAlert alertWithError:error];
[a runModal];
}
}
panel = nil;
}];
}
Now notice theres a call to [self glToUIImage]; in there, implement this method like so.
-(NSData *) glToUIImage {
NSRect baseRect = [self convertRectToBase:[self bounds]];
NSInteger myDataLength = baseRect.size.width * baseRect.size.height * 4;
int width = baseRect.size.width;
int height = baseRect.size.height;
// allocate array and read pixels into it.
GLubyte *buffer = (GLubyte *) malloc(myDataLength);
glReadPixels(0, 0, baseRect.size.width, baseRect.size.height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
// gl renders “upside down” so swap top to bottom into new array.
// there’s gotta be a better way, but this works.
GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
for(int y = 0; y <height; y++)
{
for(int x = 0; x <width * 4; x++)
{
buffer2[((height - 1) - y) * width * 4 + x] = buffer[y * 4 * width + x];
}
}
// make data provider with data.
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL);
// prep the ingredients
int bitsPerComponent = 8;
int bitsPerPixel = 32;
int bytesPerRow = 4 * width;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
// make the cgimage
CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
// then make the uiimage from that
CIImage *myImage = [CIImage imageWithCGImage:imageRef];
NSBitmapImageRep* rep = [[NSBitmapImageRep alloc]
initWithCIImage:myImage];
NSData* PNGData = [rep representationUsingType:NSPNGFileType
properties:nil];
return PNGData;
}
Thats it! you should be okies to export from an OpenGL view, make sure you connect the action to a component and your good to go!