IPhone RGBA в ARGB - PullRequest
       86

IPhone RGBA в ARGB

2 голосов
/ 16 октября 2010

Я использую glReadPixels для захвата снимков экрана моей сцены opengl, а затем превращаю их в видео с помощью AVAssetWriter на IOS 4. Моя проблема заключается в том, что мне нужно передать альфа-канал видео, которое принимает только kCVPixelFormatType_32ARGB и glReadPixels при получении RGBA.Поэтому в основном мне нужен способ конвертировать мой RGBA в ARGB, иными словами, сначала нужно поместить альфа-байты.

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

Я думал, что выложу весь код, чтобы помочь кому-то другому.

Приветствия

Ответы [ 6 ]

6 голосов
/ 16 октября 2010

Примечание: я предполагаю 8 бит на канал.Отрегулируйте соответственно, если это не так.

Чтобы переместить альфа-биты последними, вам нужно выполнить вращение .Обычно это легче всего выражается с помощью сдвига битов.

В этом случае вы хотите переместить биты RGB на 8 бит вправо, а биты A на 24 бита влево.Эти два значения должны затем быть объединены с использованием побитового ИЛИ, так что получается argb = (rgba >> 8) | (rgba << 24).

2 голосов
/ 26 марта 2013

Я понимаю, что на этот вопрос был дан ответ, но я хотел убедиться, что люди знают о vImage, части платформы Accelerate и доступной в iOS и OSX. Насколько я понимаю, vImage используется Core Graphics для выполнения векторных операций с привязкой к ЦП для растровых изображений.

Конкретным API, для которого вы хотите конвертировать ARGB в RGBA, является vImagePermuteChannels_ARGB8888. Существуют также API для преобразования RGB в ARGB / XRGB, для переворачивания изображения, для перезаписи канала и многое другое. Это своего рода скрытый драгоценный камень!

Обновление: Брэд Ларсон написал отличный ответ на практически тот же вопрос здесь .

2 голосов
/ 26 апреля 2012

Более того, не кодируйте видео с помощью ARGB, отправляйте кадры AVAssetWriter BGRA.Как я опишу в этом ответе , это позволяет вам кодировать видео 640x480 со скоростью 30 кадров в секунду на iPhone 4 и до 20 кадров в секунду для видео 720p.IPhone 4S может использовать эту функцию вплоть до видео 1080p с частотой 30 кадров в секунду.

Кроме того, вам нужно будет убедиться, что вы используете пул буферов пикселей вместо того, чтобы каждый раз заново создавать буфер пикселей.Скопировав код из этого ответа, вы конфигурируете AVAssetWriter, используя это:

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];

, затем используйте этот код для захвата каждого визуализированного кадра, используя 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);

При использовании glReadPixels(), вам нужно поменять цвета вашего кадра, поэтому я использовал для этого закадровое FBO и фрагментный шейдер со следующим кодом:

 varying highp vec2 textureCoordinate;

 uniform sampler2D inputImageTexture;

 void main()
 {
     gl_FragColor = texture2D(inputImageTexture, textureCoordinate).bgra;
 }

Однако на еще более быстром маршрутеiOS 5.0 для захвата контента OpenGL ES, чем glReadPixels(), который я описываю в этого ответа .Хорошая особенность этого процесса заключается в том, что текстуры уже хранят контент в пиксельном формате BGRA, поэтому вы можете просто передавать инкапсулирующие пиксельные буферы прямо в AVAssetWriter без какого-либо преобразования цвета и по-прежнему видеть отличные скорости кодирования.

0 голосов
/ 14 декабря 2011

Я уверен, что альфа-значения можно игнорировать.Таким образом, вы можете просто сделать memcpy с массивом буферов пикселей, смещенным на один байт:

0 голосов
/ 18 января 2011
+ (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;
}
0 голосов
/ 16 октября 2010

Да, его 8 бит на канал, так что это что-то вроде:

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

Не могу заставить его работать

...