IPhone RGBA a ARGB

Estoy usando glReadPixels para capturar capturas de pantalla de mi escena opengl y luego convertirlas en un video usando AVAssetWriter en iOS 4. Mi problema es que necesito pasar el canal alfa al video que solo acepta kCVPixelFormatType_32ARGB y glReadPixels en recupera RGBA. Entonces, básicamente, necesito una forma de convertir mi RGBA a ARGB, en otras palabras, poner los alfa bytes primero.

int depth = 4; unsigned char buffer[width * height * depth]; glReadPixels(0,0,width, height, GL_RGBA, GL_UNSIGNED_BYTE, &buffer); CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, &buffer), width*height*depth, NULL ); CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast; CGImageRef image = CGImageCreate(width, height, 8, 32, width*depth, CGColorSpaceCreateDeviceRGB(), bitmapInfo, ref, NULL, true, kCGRenderingIntentDefault); UIWindow* parentWindow = [self window]; NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey, [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil]; CVPixelBufferRef pxbuffer = NULL; CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_32ARGB, (CFDictionaryRef) options, &pxbuffer); NSParameterAssert(status == kCVReturnSuccess); NSParameterAssert(pxbuffer != NULL); CVPixelBufferLockBaseAddress(pxbuffer, 0); void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer); NSParameterAssert(pxdata != NULL); CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(pxdata, width, height, 8, depth*width, rgbColorSpace, kCGImageAlphaPremultipliedFirst); NSParameterAssert(context); CGContextConcatCTM(context, parentWindow.transform); CGContextDrawImage(context, CGRectMake(0, 0, width, height), image); CGColorSpaceRelease(rgbColorSpace); CGContextRelease(context); CVPixelBufferUnlockBaseAddress(pxbuffer, 0); return pxbuffer; // chuck pixel buffer into AVAssetWriter 

Pensé en publicar todo el código, ya que puedo ayudar a alguien más.

Aclamaciones

Nota: Estoy asumiendo 8 bits por canal. Ajuste en consecuencia si este no es el caso.

Para mover los últimos bits alfa, debe realizar la rotation . Esto generalmente se expresa con mayor facilidad a través del cambio de bit.

En este caso, quiere mover los bits RGB 8 bits a la derecha y los bits A 24 bits restantes. Estos dos valores se deben juntar usando OR bit a bit, de modo que se convierte en argb = (rgba >> 8) | (rgba << 24) argb = (rgba >> 8) | (rgba << 24) .

Aún mejor, no codifique su video con ARGB, envíe sus cuadros AVAssetWriter BGRA. Como describo en esta respuesta , hacerlo le permite codificar 640×480 video a 30 FPS en un iPhone 4, y hasta 20 FPS para video de 720p. Un iPhone 4S puede llegar hasta 1080p a 30 FPS usando esto.

Además, deseará asegurarse de usar un grupo de búferes de píxeles en lugar de volver a crear un búfer de píxeles cada vez. Al copyr el código de esa respuesta, configure AVAssetWriter con esto:

 NSError *error = nil; assetWriter = [[AVAssetWriter alloc] initWithURL:movieURL fileType:AVFileTypeAppleM4V error:&error]; if (error != nil) { NSLog(@"Error: %@", error); } NSMutableDictionary * outputSettings = [[NSMutableDictionary alloc] init]; [outputSettings setObject: AVVideoCodecH264 forKey: AVVideoCodecKey]; [outputSettings setObject: [NSNumber numberWithInt: videoSize.width] forKey: AVVideoWidthKey]; [outputSettings setObject: [NSNumber numberWithInt: videoSize.height] forKey: AVVideoHeightKey]; assetWriterVideoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:outputSettings]; assetWriterVideoInput.expectsMediaDataInRealTime = YES; // You need to use BGRA for the video in order to get realtime encoding. I use a color-swizzling shader to line up glReadPixels' normal RGBA output with the movie input's BGRA. NSDictionary *sourcePixelBufferAttributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey, [NSNumber numberWithInt:videoSize.width], kCVPixelBufferWidthKey, [NSNumber numberWithInt:videoSize.height], kCVPixelBufferHeightKey, nil]; assetWriterPixelBufferInput = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:assetWriterVideoInput sourcePixelBufferAttributes:sourcePixelBufferAttributesDictionary]; [assetWriter addInput:assetWriterVideoInput]; 

luego usa este código para tomar cada marco renderizado usando glReadPixels() :

 CVPixelBufferRef pixel_buffer = NULL; CVReturn status = CVPixelBufferPoolCreatePixelBuffer (NULL, [assetWriterPixelBufferInput pixelBufferPool], &pixel_buffer); if ((pixel_buffer == NULL) || (status != kCVReturnSuccess)) { return; } else { CVPixelBufferLockBaseAddress(pixel_buffer, 0); GLubyte *pixelBufferData = (GLubyte *)CVPixelBufferGetBaseAddress(pixel_buffer); glReadPixels(0, 0, videoSize.width, videoSize.height, GL_RGBA, GL_UNSIGNED_BYTE, pixelBufferData); } // May need to add a check here, because if two consecutive times with the same value are added to the movie, it aborts recording CMTime currentTime = CMTimeMakeWithSeconds([[NSDate date] timeIntervalSinceDate:startTime],120); if(![assetWriterPixelBufferInput appendPixelBuffer:pixel_buffer withPresentationTime:currentTime]) { NSLog(@"Problem appending pixel buffer at time: %lld", currentTime.value); } else { // NSLog(@"Recorded pixel buffer at time: %lld", currentTime.value); } CVPixelBufferUnlockBaseAddress(pixel_buffer, 0); CVPixelBufferRelease(pixel_buffer); 

Cuando use glReadPixels() , debe swizzle los colors de su marco, por lo que he empleado un FBO fuera de pantalla y un shader de fragment con el siguiente código para hacer esto:

  varying highp vec2 textureCoordinate; uniform sampler2D inputImageTexture; void main() { gl_FragColor = texture2D(inputImageTexture, textureCoordinate).bgra; } 

Sin embargo, hay una ruta aún más rápida en iOS 5.0 para get contenido de OpenGL ES que glReadPixels() , que describo en esta respuesta . Lo bueno de ese process es que las texturas ya almacenan contenido en formatting de píxel BGRA, por lo que puede alimentar los buffers de píxel encapsulantes directamente a AVAssetWriter sin ninguna conversión de color y aún así observar velocidades de encoding excelentes.

Me doy count de que esta pregunta ha sido respondida, pero quería asegurarme de que la gente conozca vImage, parte del marco de Accelerate y disponible en iOS y OSX. Entiendo que vImage es utilizado por Core Graphics para realizar operaciones vectoriales en bitmaps.

La API específica que desea para convertir ARGB a RGBA es vImagePermuteChannels_ARGB8888. También hay API para convertir RGB a ARGB / XRGB, para voltear una image, para sobrescribir un canal y mucho más. ¡Es una especie de joya escondida!

Actualización: Brad Larson escribió una gran respuesta para esencialmente la misma pregunta aquí .

Sí, sus 8 bits por canal, así que es algo así como:

 int depth = 4; int width = 320; int height = 480; unsigned char buffer[width * height * depth]; glReadPixels(0,0,width, height, GL_RGBA, GL_UNSIGNED_BYTE, &buffer); for(int i = 0; i < width; i++){ for(int j = 0; j < height; j++){ buffer[i*j] = (buffer[i*j] >> 8) | (buffer[i*j] << 24); } } 

No puedo hacerlo funcionar

Estoy seguro de que se pueden ignorar los valores alfa. Por lo que puede hacer memcpy con la matriz de píxeles-buffer desplazada por un byte:

 void *buffer = malloc(width*height*4); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, &buffer); … memcpy(pxdata + 1, buffer, width*height*4 - 1); 
 + (UIImage *) createARGBImageFromRGBAImage: (UIImage *)image { CGSize dimensions = [image size]; NSUInteger bytesPerPixel = 4; NSUInteger bytesPerRow = bytesPerPixel * dimensions.width; NSUInteger bitsPerComponent = 8; unsigned char *rgba = malloc(bytesPerPixel * dimensions.width * dimensions.height); unsigned char *argb = malloc(bytesPerPixel * dimensions.width * dimensions.height); CGColorSpaceRef colorSpace = NULL; CGContextRef context = NULL; colorSpace = CGColorSpaceCreateDeviceRGB(); context = CGBitmapContextCreate(rgba, dimensions.width, dimensions.height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault); // kCGBitmapByteOrder32Big CGContextDrawImage(context, CGRectMake(0, 0, dimensions.width, dimensions.height), [image CGImage]); CGContextRelease(context); CGColorSpaceRelease(colorSpace); for (int x = 0; x < dimensions.width; x++) { for (int y = 0; y < dimensions.height; y++) { NSUInteger offset = ((dimensions.width * y) + x) * bytesPerPixel; argb[offset + 0] = rgba[offset + 3]; argb[offset + 1] = rgba[offset + 0]; argb[offset + 2] = rgba[offset + 1]; argb[offset + 3] = rgba[offset + 2]; } } colorSpace = CGColorSpaceCreateDeviceRGB(); context = CGBitmapContextCreate(argb, dimensions.width, dimensions.height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrderDefault); // kCGBitmapByteOrder32Big CGImageRef imageRef = CGBitmapContextCreateImage(context); image = [UIImage imageWithCGImage: imageRef]; CGImageRelease(imageRef); CGContextRelease(context); CGColorSpaceRelease(colorSpace); free(rgba); free(argb); return image; } 
Intereting Posts