create iphone uikit uiview opengl-es eaglview

iphone - create - uiimageview set image swift



¿Cómo obtener UIImage de EAGLView? (6)

Estoy tratando de obtener un UIImage de lo que se muestra en mi EAGLView. ¿Alguna sugerencia sobre cómo hacer esto?


Aquí hay una versión limpia del código de Quakeboy. Lo probé en iPad, y funciona muy bien. Las mejoras incluyen:

  • funciona con cualquier tamaño EAGLView
  • funciona con pantalla Retina (escala de puntos 2)
  • reemplazó el bucle anidado con memcpy
  • limpios fugas de memoria
  • guarda el UIImage en el álbum de fotos como una bonificación.

Use esto como un método en su EAGLView:

-(void)snapUIImage { int s = 1; UIScreen* screen = [ UIScreen mainScreen ]; if ( [ screen respondsToSelector:@selector(scale) ] ) s = (int) [ screen scale ]; const int w = self.frame.size.width; const int h = self.frame.size.height; const NSInteger myDataLength = w * h * 4 * s * s; // allocate array and read pixels into it. GLubyte *buffer = (GLubyte *) malloc(myDataLength); glReadPixels(0, 0, w*s, h*s, 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*s; y++) { memcpy( buffer2 + (h*s - 1 - y) * w * 4 * s, buffer + (y * 4 * w * s), w * 4 * s ); } free(buffer); // work with the flipped buffer, so get rid of the original one. // 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 * s; CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault; CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; // make the cgimage CGImageRef imageRef = CGImageCreate(w*s, h*s, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent); // then make the uiimage from that UIImage *myImage = [ UIImage imageWithCGImage:imageRef scale:s orientation:UIImageOrientationUp ]; UIImageWriteToSavedPhotosAlbum( myImage, nil, nil, nil ); CGImageRelease( imageRef ); CGDataProviderRelease(provider); CGColorSpaceRelease(colorSpaceRef); free(buffer2); }


-(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]; CGImageRelease( imageRef ); CGDataProviderRelease(provider); CGColorSpaceRelease(colorSpaceRef); free(buffer2); return myImage; }


No pude obtener las otras respuestas aquí para que funcionen correctamente para mí.

Después de unos días, finalmente obtuve una solución funcional para esto. Hay un código proporcionado por Apple que produce un UIImage desde un EAGLView. Entonces simplemente necesita voltear la imagen verticalmente ya que UIkit está boca abajo.

Método provisto por Apple: modificado para que esté dentro de la vista que desea convertir en una imagen.

-(UIImage *) drawableToCGImage { GLint backingWidth2, backingHeight2; //Bind the color renderbuffer used to render the OpenGL ES view // If your application only creates a single color renderbuffer which is already bound at this point, // this call is redundant, but it is needed if you''re dealing with multiple renderbuffers. // Note, replace "_colorRenderbuffer" with the actual name of the renderbuffer object defined in your class. glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); // Get the size of the backing CAEAGLLayer glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth2); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight2); NSInteger x = 0, y = 0, width2 = backingWidth2, height2 = backingHeight2; NSInteger dataLength = width2 * height2 * 4; GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte)); // Read pixel data from the framebuffer glPixelStorei(GL_PACK_ALIGNMENT, 4); glReadPixels(x, y, width2, height2, GL_RGBA, GL_UNSIGNED_BYTE, data); // Create a CGImage with the pixel data // If your OpenGL ES content is opaque, use kCGImageAlphaNoneSkipLast to ignore the alpha channel // otherwise, use kCGImageAlphaPremultipliedLast CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL); CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); CGImageRef iref = CGImageCreate(width2, height2, 8, 32, width2 * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast, ref, NULL, true, kCGRenderingIntentDefault); // OpenGL ES measures data in PIXELS // Create a graphics context with the target size measured in POINTS NSInteger widthInPoints, heightInPoints; if (NULL != UIGraphicsBeginImageContextWithOptions) { // On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration // Set the scale parameter to your OpenGL ES view''s contentScaleFactor // so that you get a high-resolution snapshot when its value is greater than 1.0 CGFloat scale = self.contentScaleFactor; widthInPoints = width2 / scale; heightInPoints = height2 / scale; UIGraphicsBeginImageContextWithOptions(CGSizeMake(widthInPoints, heightInPoints), NO, scale); } else { // On iOS prior to 4, fall back to use UIGraphicsBeginImageContext widthInPoints = width2; heightInPoints = height2; UIGraphicsBeginImageContext(CGSizeMake(widthInPoints, heightInPoints)); } CGContextRef cgcontext = UIGraphicsGetCurrentContext(); // UIKit coordinate system is upside down to GL/Quartz coordinate system // Flip the CGImage by rendering it to the flipped bitmap context // The size of the destination area is measured in POINTS CGContextSetBlendMode(cgcontext, kCGBlendModeCopy); CGContextDrawImage(cgcontext, CGRectMake(0.0, 0.0, widthInPoints, heightInPoints), iref); // Retrieve the UIImage from the current context UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // Clean up free(data); CFRelease(ref); CFRelease(colorspace); CGImageRelease(iref); return image;

}

Y aquí hay un método para voltear la imagen

- (UIImage *) flipImageVertically:(UIImage *)originalImage { UIImageView *tempImageView = [[UIImageView alloc] initWithImage:originalImage]; UIGraphicsBeginImageContext(tempImageView.frame.size); CGContextRef context = UIGraphicsGetCurrentContext(); CGAffineTransform flipVertical = CGAffineTransformMake( 1, 0, 0, -1, 0, tempImageView.frame.size.height ); CGContextConcatCTM(context, flipVertical); [tempImageView.layer renderInContext:context]; UIImage *flippedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); //[tempImageView release]; return flippedImage;

}

Y aquí hay un enlace a la página de desarrollo de Apple donde encontré el primer método de referencia. http://developer.apple.com/library/ios/#qa/qa1704/_index.html


Con este código anterior de Brad Larson, debe editar su EAGLView.m

- (id)initWithCoder:(NSCoder*)coder{ self = [super initWithCoder:coder]; if (self) { CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer; eaglLayer.opaque = TRUE; eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; } return self; }

Tienes que cambiar el valor de numberWithBool YES


CGDataProviderCreateWithData viene con una devolución de llamada de versión para liberar los datos, donde debe hacer la versión:

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

Luego haga esto como otros ejemplos, pero NO para liberar datos aquí:

GLubyte *bufferData = (GLubyte *) malloc(bufferDataSize); CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bufferData, bufferDataSize, releaseBufferData); .... CGDataProviderRelease(provider);

O simplemente use CGDataProviderCreateWithCFData sin CGDataProviderCreateWithCFData cosas de devolución de llamada:

GLubyte *bufferData = (GLubyte *) malloc(bufferDataSize); NSData *data = [NSData dataWithBytes:bufferData length:bufferDataSize]; CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)data); .... CGDataProviderRelease(provider); free(bufferData); // Remember to free it

Para obtener más información, consulte esta discusión:

¿Cuál es el patrón de gestión de memoria correcto para buffer-> CGImageRef-> UIImage?


EDITAR: como notas de demianturner a continuación, ya no necesita renderizar la capa, puede (y debería) ahora usar el nivel superior [UIView drawViewHierarchyInRect:] . Aparte de eso; esto debería funcionar igual.

Un EAGLView es solo un tipo de vista, y su CAEAGLLayer subyacente es solo una especie de capa. Eso significa que el enfoque estándar para convertir una vista / capa en un UIImage funcionará. (El hecho de que la pregunta vinculada sea UIWebview no importa; eso es simplemente otro tipo de vista).