views:

617

answers:

4

I am trying to get a UIImage from what is displayed in my EAGLView. Any suggestions on how to do this?

+1  A: 

An EAGLView is just a kind of view, and its underlying CAEAGLLayer is just a kind of layer. That means, that the standard approach for converting a view/layer into a UIImage will work. (The fact that the linked question is UIWebview doesn't matter; that's just yet another kind of view.)

Rob Napier
Thanks for the answer Rob, but I tried the "Standard Method" and it did not work. Even though my EAGLView is correctly displaying a texture loaded to it, the only UIImage I have been able to extract from it using the STANDARD APPROACH is one with a blank white color, which is exactly how the EAGLView appears in IB. This is strange, indeed, seeing how EAGLView is just a kind of UIView. I think maybe I need to use glReadPixels or something instead? I am working with an EAGLView from the Apple Sample Code GLImageProcessing example.
RexOnRoids
Eh... I really did speak too fast. OpenGL doesn't render the same way as Quartz. Sorry about that. I believe this thread will address your problem. Read through all the messages; he works out several bugs along the way. I'm assuming you already know how to deal with a CGContext and get a CGImage out of it (and a UIImage from that). http://lists.apple.com/archives/mac-opengl/2006//jan/msg00085.html
Rob Napier
+2  A: 
-(UIImage *) saveImageFromGLView
{
    NSInteger myDataLength = 320 * 480 * 4;
    // allocate array and read pixels into it.
    GLubyte *buffer = (GLubyte *) malloc(myDataLength);
    glReadPixels(0, 0, 320, 480, 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 <480; y++)
    {
        for(int x = 0; x <320 * 4; x++)
        {
            buffer2[(479 - y) * 320 * 4 + x] = buffer[y * 4 * 320 + 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 * 320;
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
    // make the cgimage
    CGImageRef imageRef = CGImageCreate(320, 480, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
    // then make the uiimage from that
    UIImage *myImage = [UIImage imageWithCGImage:imageRef];
    return myImage;
}
Quakeboy
A: 

Although the code above works perfectly, it is a bit leaky.

In the saveImageFromGLView method, buffer, which was allocated using malloc(), must be free()'d eventually! Don't free buffer2 though - apparently it is used by the UIImage which is created and will be released whenever the UIImage gets released.

Furthermore provider, colorSpaceRef, and imageRef must get released. That is the lines

CGDataProviderRelease(provider);

CGColorSpaceRelease(colorSpaceRef);

CGImageRelease(imageRef);

must be added. The rule of thumb here is that any method that contains "Create" in its name returns an object with a retain count of 1, meaning that the object must be released at some point in time so that it can be freed from memory.

Hope this helps!

bgh
A: 

Yes, need to clean up memory.

How about the return myImage, still need to release it ? autorelease can be used here ?

Forrest