GLPaint save image

时光总嘲笑我的痴心妄想 提交于 2019-12-03 05:23:46

问题


I'm trying to develop a complex painting application on the iPhone. I'm currently drawing using Quartz (e.g. CGContext...). Unfortunately the Quartz overhead is just too slow for the type of drawing I'm doing, and I'm porting to OpenGL calls using the GLPaint example as a reference point.

Is there a way to get a UIImage/CGImage from the EAGLview class (the equivalent of Quartz's UIGraphicsGetImageFromCurrentImageContext)? Basically I need to save the pictures drawn by the GLPaint application.


回答1:


-(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;
}



回答2:


Same as @Quakeboy's answer, but passing in the view so that the size can be dynamically determined (I used this for my universal app):

- (UIImage *)saveImageFromGLView:(UIView *)glView {
    int width = glView.frame.size.width;
    int height = glView.frame.size.height;

    NSInteger myDataLength = width * height * 4;
    // allocate array and read pixels into it.
    GLubyte *buffer = (GLubyte *) malloc(myDataLength);
    glReadPixels(0, 0, width, 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
    UIImage *myImage = [UIImage imageWithCGImage:imageRef];
    return myImage;
}



回答3:


It's definitely possible. The trick is to use glReadPixels to pull the image data out of the OpenGL framebuffer into memory you can use. Once you have a pointer to the image data, you can use CGDataProviderCreateWithData and CGImageCreate to create a CGImage from the data. I'm working on an OpenGL-based drawing app that uses this technique a lot!




回答4:


This code will not leak memory like the above solution and accounts for dynamic view size as well as retina vs standard displays:

-(BOOL)iPhoneRetina{
    return ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0))?YES:NO;
}

void releasePixels(void *info, const void *data, size_t size) {
    free((void*)data);
}

-(UIImage *) glToUIImage{

    int imageWidth, imageHeight;

    int scale = [self iPhoneRetina]?2:1;

    imageWidth = self.frame.size.width*scale;
    imageHeight = self.frame.size.height*scale;

    NSInteger myDataLength = imageWidth * imageHeight * 4;

    // allocate array and read pixels into it.
    GLubyte *buffer = (GLubyte *) malloc(myDataLength);
    glReadPixels(0, 0, imageWidth, imageHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

    // make data provider with data.
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, myDataLength, releasePixels);

    // prep the ingredients
    int bitsPerComponent = 8;
    int bitsPerPixel = 32;
    int bytesPerRow = 4 * imageWidth;
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo =  kCGImageAlphaPremultipliedLast;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

    // make the cgimage

    CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);

    UIImage *myImage = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationDownMirrored]; //Render image flipped, since OpenGL's data is mirrored

    CGImageRelease(imageRef);
    CGColorSpaceRelease(colorSpaceRef);

    CGDataProviderRelease(provider);

    return myImage;
}

The others leak memory because the last parameter to CGDataProviderCreateWithData is supposed to be a function to free memory, and they also leave out the CGRelease functions.




回答5:


void SaveScreenImage()

{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CGImageRef cgImage = UIGetScreenImage();
void *imageBytes = NULL;
if (cgImage == NULL) {
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
imageBytes = malloc(320 * 480 * 4);
CGContextRef context = CGBitmapContextCreate(imageBytes, 320, 480, 8, 320 * 4, colorspace, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorspace);
for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
        CGRect bounds = [window bounds];
        CALayer *layer = [window layer];
        CGContextSaveGState(context);
        if ([layer contentsAreFlipped]) {
            CGContextTranslateCTM(context, 0.0f, bounds.size.height);
            CGContextScaleCTM(context, 1.0f, -1.0f);
        }
[layer renderInContext:(CGContextRef)context];
        CGContextRestoreGState(context);
    }
    cgImage = CGBitmapContextCreateImage(context);
    CGContextRelease(context);
}
UIImage *image=[UIImage imageWithCGImage:cgImage];
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil); 
[pool release];
}

This code will save as you see on the screen.But it maybe private api.



来源:https://stackoverflow.com/questions/946700/glpaint-save-image

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!