Tutorial :GLPaint save image



Question:

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.


Solution: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;  }  


Solution: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;  }  


Solution: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!


Solution: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.


Solution: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.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »