5

I am using the following code to take a screenshot of the pixels in a GLView. The problem is, it returns a completely black UIImage. This code is being called in LineDrawer.m which is the heart of the GLView code - so it is being called from the right .m file. How can I save the actual screenshot and not a black image?

- (UIImage*) getGLScreenshot {

NSLog(@"1");

float scale = 0.0;
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)])
{
    // scale value should be 1.0 on 3G and 3GS, and 2.0 on iPhone 4.
    scale = [[UIScreen mainScreen] scale]; 
}

// these are swapped since the screen is rotatey
float h = 768 * scale;
float w = 924 * scale;

NSInteger myDataLength = w * h * 4;

// allocate array and read pixels into it.
GLubyte *buffer = (GLubyte *) malloc(myDataLength);
glReadPixels(0, 0, w, h, 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 <h; y++)
{
    for(int x = 0; x <w * 4; x++)
    {
        buffer2[(((int)h-1) - y) * (int)w * 4 + x] = buffer[y * 4 * (int)w + 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 * w;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

// make the cgimage
CGImageRef imageRef = CGImageCreate(w, h, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);

// then make the uiimage from that
UIImage *myImage = [UIImage imageWithCGImage:imageRef];
return myImage;

}

- (void)saveGLScreenshotToPhotosAlbum {
UIImageWriteToSavedPhotosAlbum([self getGLScreenshot], nil, nil, nil);
}
genpfault
  • 51,148
  • 11
  • 85
  • 139
James Anderson
  • 556
  • 3
  • 16
  • 41
  • 1
    You need to free your two buffers and release the `provider`, `colorSpaceRef`, and `imageRef`. Also you can probably do the vertical flip by supplying a negative `stride` value somewhere. – jhabbott Jun 23 '12 at 12:35
  • How can I do this? Sorry, but I'm not too experienced with OpenGL and this is a drop-in library! I'd really appreciate an example :-) – James Anderson Jun 23 '12 at 13:00
  • My comment is not an answer to your question, I'm pointing out your memory leaks. You should read up about reference counting in CoreFoundation and Objective-C in general. Also you need to be aware of memory management in C when using malloc. – jhabbott Jun 23 '12 at 13:10
  • It's possible that your OpenGL ES context is not associated with the current thread when running the above capture code. Try to add `[EAGLContext setCurrentContext:self.context];` at the beginning of that method. Also, `glReadPixels()` will only capture what's currently in the visible in the framebuffer, so if you're calling this within `-drawRect:` for your GLKView, that content may not have been rendered yet. I'm not sure of the proper way to force this with a GLKView, but a `glFinish()` before `glReadPixels()` might be sufficient. – Brad Larson Jun 24 '12 at 22:28
  • Did you try it without the image flipping in your loop? Did you output your pixels on the console? Are they really 0 (black)? – tomk Jul 03 '12 at 15:25
  • To free your buffers after you used them just put a ```free(buffer); free(buffer2);```to the end of your code. – tomk Jul 03 '12 at 15:26
  • I had a similar issue. See my answer for this question to see how I finally figured it out. http://stackoverflow.com/questions/11036288/xcode-screenshot-eaglcontext/11868013#11868013 – Nate_Hirsch Aug 08 '12 at 15:34

3 Answers3

1

I had to do something similar in the Sparrow Framework a while back, you should be able to pull the parts you need out of the code in this forum reply: http://forum.sparrow-framework.org/topic/spdisplayobjectscreenshot

EDIT: Also this post http://forum.sparrow-framework.org/topic/taking-screenshots

Workshed
  • 236
  • 1
  • 8
0

Change your drawable properties

eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:

            [NSNumber numberWithBool:YES],  
                kEAGLDrawablePropertyRetainedBacking, 
                kEAGLColorFormatRGB565, 
                kEAGLDrawablePropertyColorFormat, nil];

kEAGLDrawablePropertyRetainedBacking to YES

Saad Ur Rehman
  • 798
  • 1
  • 10
  • 19
0

Try this I went through a lot of things and finally found a solution.

-(UIImage*)renderImg{
        GLint backingWidth = 0;
        GLint backingHeight = 0;
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
        GLubyte *buffer = (GLubyte *) malloc(backingWidth * backingHeight * 4);
        GLubyte *buffer2 = (GLubyte *) malloc(backingWidth * backingHeight * 4);

        glReadPixels(0, 0, backingWidth, backingHeight, GL_RGBA, GL_UNSIGNED_BYTE,
                     (GLvoid *)buffer);
        for (int y=0; y<backingHeight; y++) {
            for (int x=0; x<backingWidth*4; x++) {
                buffer2[y * 4 * backingWidth + x] =
                buffer[(backingHeight - y - 1) * backingWidth * 4 + x];
            }
        }
        free(buffer);
        CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2,
                                                                  backingWidth * backingHeight * 4,
                                                                  myProviderReleaseData);
        // set up for CGImage creation
        int bitsPerComponent = 8;
        int bitsPerPixel = 32;
        int bytesPerRow = 4 * backingWidth;
        CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
        // Use this to retain alpha
        CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
        CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
        CGImageRef imageRef = CGImageCreate(backingWidth, backingHeight,
                                            bitsPerComponent, bitsPerPixel,
                                            bytesPerRow, colorSpaceRef,
                                            bitmapInfo, provider,
                                            NULL, NO,
                                            renderingIntent);
        // this contains our final image.
        UIImage *newUIImage = [UIImage imageWithCGImage:imageRef];
        CGImageRelease(imageRef);
        CGColorSpaceRelease(colorSpaceRef);
        CGDataProviderRelease(provider);

        return newUIImage;
    }

I think it should work perfectly.

Kranthi Samala
  • 171
  • 1
  • 6