2011-10-28 46 views
4

所以我正在處理一堆2048x2048精靈表,它們很快填滿了內存。原樣,我使用下面的方法(通過雷Wenderlich)加載紋理:關於iOS紋理加載的OpenGL ES - 我如何從RGBA8888 .png文件獲取RGB565紋理?

- (GLuint)setupTexture:(NSString *)fileName 
{  
    CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage; 
    if (!spriteImage) 
    { 
     NSLog(@"Failed to load image %@", fileName); 
     exit(1); 
    } 
    size_t width = CGImageGetWidth(spriteImage); 
    size_t height = CGImageGetHeight(spriteImage); 
    GLubyte * spriteData = (GLubyte *) calloc(width*height*4, sizeof(GLubyte)); 
    CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8,  width*4,CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);  
    CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage); 
    CGContextRelease(spriteContext); 
    GLuint texName; 
    glGenTextures(1, &texName); 
    glBindTexture(GL_TEXTURE_2D, texName); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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); 
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData); 
    GLenum err = glGetError(); 
    if (err != GL_NO_ERROR) 
     NSLog(@"Error uploading texture. glError: 0x%04X", err);  
    free(spriteData);   
    return texName;  
} 

所以我的問題是,我如何丟棄來自CGImageα信息,然後在「下采樣」圖像以更少的每像素位,最後告訴OpenGL呢?

回答

7

我使用此代碼將CGContextDrawImage寫入的數據轉換爲RGB565。它使用優化的NEON代碼在支持NEON的設備上每次處理8個像素(每個運行armv7代碼的iOS設備都有這樣的芯片)。您看到的指針data與您的spriteData指針相同,我只是懶得重命名它。

void *temp = malloc(width * height * 2); 
uint32_t *inPixel32 = (uint32_t *)data; 
uint16_t *outPixel16 = (uint16_t *)temp; 
uint32_t pixelCount = width * height; 

#ifdef __ARM_NEON__ 
for(uint32_t i=0; i<pixelCount; i+=8, inPixel32+=8, outPixel16+=8) 
{ 
    uint8x8x4_t rgba = vld4_u8((const uint8_t *)inPixel32); 

    uint8x8_t r = vshr_n_u8(rgba.val[0], 3); 
    uint8x8_t g = vshr_n_u8(rgba.val[1], 2); 
    uint8x8_t b = vshr_n_u8(rgba.val[2], 3); 

    uint16x8_t r16 = vmovl_u8(r); 
    uint16x8_t g16 = vmovl_u8(g); 
    uint16x8_t b16 = vmovl_u8(b); 

    r16 = vshlq_n_u16(r16, 11); 
    g16 = vshlq_n_u16(g16, 5); 

    uint16x8_t rg16 = vorrq_u16(r16, g16); 
    uint16x8_t result = vorrq_u16(rg16, b16); 

    vst1q_u16(outPixel16, result); 
} 
#else     
for(uint32_t i=0; i<pixelCount; i++, inPixel32++) 
{ 
    uint32_t r = (((*inPixel32 >> 0) & 0xFF) >> 3); 
    uint32_t g = (((*inPixel32 >> 8) & 0xFF) >> 2); 
    uint32_t b = (((*inPixel32 >> 16) & 0xFF) >> 3); 

    *outPixel16++ = (r << 11) | (g << 5) | (b << 0);    
} 
#endif 

free(data); 
data = temp; 
+1

'uint8x8_t'類型在'arm_neon.h'中定義# – brigadir

+0

精彩!不得不改變glTexImage2D線以下參數:'glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,寬度,高度,0,GL_RGB,GL_UNSIGNED_SHORT_5_6_5,spriteData);' – Jakob

+0

偉大的答案,你看不到太多NEON代碼在那裏。爲了更加硬件無關,你有沒有考慮過使用Accelerate框架來實現它? –

0

看看internalFormat參數。也許你可以指定GL_R3_G3_B2每個像素只使用8位,或者GL_RGB5可能有用。有幾個可能有用的選項。

查看Texture Internal Formats瞭解更多信息。

3

而不是簡單地改變色彩空間,我可能會建議在這裏使用PVRTC壓縮紋理。這應該比使用RGB565版本在GPU上使用更少的內存,因爲這些紋理保持壓縮而不是擴展到未壓縮的位圖。

在OpenGL ES的編程指南適用於iOS的「Best Practices for Working with Texture Data」一節中,蘋果表示

紋理壓縮通常提供的內存 儲蓄和質量的最佳平衡。通過實施 GL_IMG_texture_compression_pvrtc擴展,OpenGL ES for iOS支持PowerVR紋理 壓縮(PVRTC)格式。 PVRTC壓縮有兩個級別,每個通道4位和每個通道2位,其中 分別提供8:1和16:1的壓縮比,分別比未壓縮的32位 紋理格式高。壓縮後的PVRTC紋理仍然提供了不錯的質量水平,尤其是在4位的水平。

如果您的應用程序無法使用壓縮的紋理,可以考慮使用 精度較低的像素格式。

這表明壓縮紋理的內存大小比使用較小色彩空間的內存大小要小。

蘋果在他們的PVRTextureLoader示例應用程序中有一個很好的例子,我重新使用了其中一些代碼來加載我在一起的textured cube sample application中的PVRTC紋理。您可以查看PVRTextureLoader中的「編碼圖像」編譯階段,瞭解如何在編譯時將PNG紋理轉換爲PVRTC文件。

+0

你的答案一直鼓勵我在PVRTexturLoader例子再看看。我曾嘗試在前一天晚上使用我的紋理,但失敗了,但我現在已經開始工作了。巨大的內存佔用改進!謝謝拉爾森先生 – Jakob