从iOS上的两个jpeg加载RGBA图像 - OpenGL ES 2.0

时间:2014-03-08 01:37:37

标签: ios iphone objective-c uiimage opengl-es-2.0

基于我的问题here中的概念以及我在其他问题here中概述的PNG文件的问题,我将尝试从两个jpeg加载RGBA图像。一个将包含RGB,另一个将包含Alpha。我可以将第二个保存为灰度jpeg或RGB,并从红色组件中提取alpha数据。

在第二步中,我将原始图像数据保存到缓存中的文件中。然后,我将进行测试,以确定加载原始数据或解压缩jpeg并构建原始数据的速度更快。如果我确定它更快,在后续加载时我可以检查缓存中是否存在原始文件。如果不是,我将跳过该文件保存。

我知道如何将两个jpeg加载到两个UIImages中。我不确定的是将来自一个UIImage的rgb与我用于alpha的其他UIImage的任何通道交错的最快或最有效的方式。

我看到两种可能性。一个将在下面的注释B中。迭代所有像素并将红色从“alpha jpeg”复制到imageData steam的alpha。

另一个是,可能有一些神奇的UIImage命令将一个频道从一个频道复制到另一个频道。如果我这样做,它就会在评论A附近。

有什么想法吗?

编辑 - 此外..该过程不能破坏任何RGB信息。我需要这个过程的全部原因是来自photoshop的PNG预先将rgb与alpha相关联,从而破坏了rgb信息。我在自定义的openGL着色器中使用alpha而不是alpha。所以我正在寻找原始RGBA数据,我可以将alpha设置为任何用作镜面反射贴图或照明贴图或高度贴图或alpha以外的其他内容。

这是我的入门代码减去我的错误检查和其他专有废话。我有一系列纹理,用于管理纹理的所有内容:

if (textureInfo[texIndex].generated==NO) {
    glGenTextures(1, &textureInfo[texIndex].texture);
    textureInfo[texIndex].generated=YES;
}

glBindTexture(GL_TEXTURE_2D, textureInfo[texIndex].texture);

// glTexParameteri commands are here based on options for this texture

NSString *path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@_RGB",name] ofType:type];

NSData *texData = [[NSData alloc] initWithContentsOfFile:path];
UIImage *imageRGB = [[UIImage alloc] initWithData:texData];

path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@_A",name] ofType:type];

texData = [[NSData alloc] initWithContentsOfFile:path];
UIImage *imageAlpha = [[UIImage alloc] initWithData:texData];

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
void *imageDataRGB = malloc( heightRGB * widthRGB * 4 );
void *imageDataAlpha = malloc( heightA * widthA * 4 );
CGContextRef thisContextRGB = CGBitmapContextCreate( imageDataRGB, widthRGB, heightRGB, 8, 4 * widthRGB, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
CGContextRef thisContextA = CGBitmapContextCreate( imageDataAlpha, widthA, heightA, 8, 4 * widthA, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );

// **** A. In here I want to mix the two.. take the R of imageA and stick it in the Alpha of imageRGB.

CGColorSpaceRelease( colorSpace );
CGContextClearRect( thisContextRGB, CGRectMake( 0, 0, widthRGB, heightRGB ) );
CGContextDrawImage( thisContextRGB, CGRectMake( 0, 0, widthRGB, heightRGB ), imageRGB.CGImage );

// **** B. OR maybe repeat the above 3 lines for the imageA.CGImage and then
// **** It could be done in here by iterating through the data and copying the R byte of imageDataA on to the A byte of the imageDataRGB

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, widthRGB, heightRGB, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageDataRGB);

// **** In here I could save off the merged imageData to a binary file and load that later if it's faster

glBindTexture(GL_TEXTURE_2D, textureInfo[texIndex].texture);

// Generates a full MipMap chain to the current bound texture.
if (useMipmap) {
    glGenerateMipmap(GL_TEXTURE_2D);
}

CGContextRelease(thisContextRGB);
CGContextRelease(thisContextA);

free(imageDataRGB);
free(imageDataAlpha);

2 个答案:

答案 0 :(得分:1)

我在上面的评论中提出的建议是使用单独的纹理并在着色器中组合。然而,我会解释为什么这可能会更快......

texture2D调用本身的数量不应该与速度有很大关系。影响速度的重要因素是:(1)需要将多少数据从CPU复制到GPU? (你可以很容易地测试,在每个调用中用N / 2个像素调用texture2D两次几乎和用其它相同的N个像素调用一次一样快),以及(2)实现是否需要重新排列CPU内存中的数据在texture2D之前(如果是,则调用可能非常慢)。一些纹理格式需要重新排列,有些则不需要;通常至少RGBA,RGB565和YUV420或YUYV的某些变体不需要重新排列。步幅和宽度/高度是2的幂可能也很重要。

我认为,如果不需要重新排列数据,使用RGB进行一次通话,使用A进行一次通话,与使用RGBA的通话一样快。

由于重新排列比副本慢得多,因此复制RGBX(忽略第四个通道)然后A可能更快,而不是在CPU上重新排列RGB和A然后复制。

P.S。 “在第二步中,我将原始图像数据保存到缓存中的文件中。然后我将进行测试,以确定加载原始数据或解压缩jpegs并构建原始数据的速度更快。” - 从内存以外的任何内容读取原始数据可能比解压缩要慢很多。 100万像素图像从jpeg解压缩需要几十毫秒,或者从闪存驱动器读取原始数据需要几百毫秒。

答案 1 :(得分:1)

将alpha复制到具有干净rgb的版本非常简单。尽管关于我的着色器是否更快地调用每个片段2或4次纹理2D或4次,但下面的方法可以作为一种方法将未预乘的RGBA放入我的glTexImage2D(GL_TEXTURE_2D...

这是我的整个方法:

-(BOOL)quickLoadTexPartsToNumber:(int)texIndex imageNameBase:(NSString *)name ofType:(NSString *)type  flipImage:(bool)flipImage clamp:(bool)clamp  mipmap:(bool)useMipmap
{
    //NSLog(@"loading image: %@ into %i",name, texIndex);

    // generate a new texture for that index number..  if it hasn't already been done
    if (textureInfo[texIndex].generated==NO) {
        glGenTextures(1, &textureInfo[texIndex].texture);
        textureInfo[texIndex].generated=YES;
    }

    glBindTexture(GL_TEXTURE_2D, textureInfo[texIndex].texture);

    if (useMipmap) {
        if (clamp) {
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_NEAREST);

            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
        }

        if (hardwareLimitions==HARDWARE_LIMITS_NONE) {
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);
        }
        else {
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST_MIPMAP_LINEAR);
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST_MIPMAP_LINEAR);
        }
    }
    else {
        if (clamp) {
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);

            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
        }

        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    }

    NSString *path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@_RGB",name] ofType:type];

    NSData *texData = [[NSData alloc] initWithContentsOfFile:path];
    UIImage *imageRGB = [[UIImage alloc] initWithData:texData];

    path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@_A",name] ofType:type];

    texData = [[NSData alloc] initWithContentsOfFile:path];
    UIImage *imageAlpha = [[UIImage alloc] initWithData:texData];


    if (imageRGB == nil) {
        NSLog(@"************* Image %@ is nil - there's a problem", [NSString stringWithFormat:@"%@_RGB",name]);
        return NO;
    }
    if (imageAlpha == nil) {
        NSLog(@"************* Image %@ is nil - there's a problem", [NSString stringWithFormat:@"%@_A",name]);
        return NO;
    }


    GLuint widthRGB = CGImageGetWidth(imageRGB.CGImage);
    GLuint heightRGB = CGImageGetHeight(imageRGB.CGImage);

    GLuint widthAlpha = CGImageGetWidth(imageAlpha.CGImage);
    GLuint heightAlpha = CGImageGetHeight(imageAlpha.CGImage);

    if (widthRGB != widthAlpha || heightRGB!=heightAlpha) {
        NSLog(@"************* Image %@ - RBG and Alpha sizes don't match", name);
    }

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    // unsigned char is an 8 bit unsigned integer
    unsigned char *imageDataRGB = malloc( heightRGB * widthRGB * 4 );
    unsigned char *imageDataAlpha = malloc( heightAlpha * widthAlpha * 4 );
    CGContextRef thisContextRGB = CGBitmapContextCreate( imageDataRGB, widthRGB, heightRGB, 8, 4 * widthRGB, colorSpace, kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big );
    CGContextRef thisContextAlpha = CGBitmapContextCreate( imageDataAlpha, widthAlpha, heightAlpha, 8, 4 * widthAlpha, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );

    if (flipImage)
    {
        // Flip the Y-axis  - don't want to do this because in this game I made all my vertex squares upside down.
        CGContextTranslateCTM (thisContextRGB, 0, heightRGB);
        CGContextScaleCTM (thisContextRGB, 1.0, -1.0);

        CGContextTranslateCTM (thisContextAlpha, 0, heightAlpha);
        CGContextScaleCTM (thisContextAlpha, 1.0, -1.0);
    }

    CGColorSpaceRelease( colorSpace );
    CGContextClearRect( thisContextRGB, CGRectMake( 0, 0, widthRGB, heightRGB ) );
    // draw the RGB version and skip the alpha
    CGContextDrawImage( thisContextRGB, CGRectMake( 0, 0, widthRGB, heightRGB ), imageRGB.CGImage );

    CGColorSpaceRelease( colorSpace );
    CGContextClearRect( thisContextAlpha, CGRectMake( 0, 0, widthAlpha, heightAlpha ) );
    CGContextDrawImage( thisContextAlpha, CGRectMake( 0, 0, widthAlpha, heightAlpha ), imageAlpha.CGImage );

    int count = 4 * widthRGB * heightRGB;
    for(int i=0; i < count; i+=4)
    {
        // copying the alpha (one byte) on to a non-premultiplied rgb is faster than copying the rgb over (3 bytes)
       imageDataRGB[i+3] = imageDataAlpha[i];
    }

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, widthRGB, heightRGB, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageDataRGB);

    glBindTexture(GL_TEXTURE_2D, textureInfo[texIndex].texture);

    // Generates a full MipMap chain to the current bound texture.
    if (useMipmap) {
        glGenerateMipmap(GL_TEXTURE_2D);
    }

    CGContextRelease(thisContextRGB);
    CGContextRelease(thisContextAlpha);

    free(imageDataRGB);
    free(imageDataAlpha);

    return YES;
}

我确实尝试使用TIF而不是PNG,无论我在过程中尝试了什么,rgb都会与alpha进行预乘,从而摧毁rgb。

这种方法可能被认为是丑陋的,但它对我来说在很多层面都有效,并且是我能够将完整的RGBA8888非预乘图像转换为OpenGL的唯一方法。