From 2754cbcb5212fd350289131a93051a3381868533 Mon Sep 17 00:00:00 2001 From: Matthias Bentrup Date: Tue, 7 Feb 2012 11:22:35 +0100 Subject: [PATCH 5/5] Add DXTn decoder When the OpenGL can't process a compressed image, e.g. because the format or size is not supported natively, it is decoded to RGBA8 and uploaded uncompressed. --- code/renderer/tr_image.c | 232 +++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 230 insertions(+), 2 deletions(-) diff --git a/code/renderer/tr_image.c b/code/renderer/tr_image.c index d5426ec..af6d9c0 100644 --- a/code/renderer/tr_image.c +++ b/code/renderer/tr_image.c @@ -516,6 +516,225 @@ Upload32 =============== */ +static ID_INLINE void R5G6B5toRGBA8( unsigned short in, color4ub_t *out ) { + // the leading bits are duplicated, so that the full + // 0x00 - 0xff range is used ! + (*out)[0] = ((in >> 8) & 0xf8) | ((in >> 13) & 0x07); + (*out)[1] = ((in >> 3) & 0xfc) | ((in >> 9) & 0x03); + (*out)[2] = ((in << 3) & 0xf8) | (in & 0x07); + (*out)[3] = 255; +} +static ID_INLINE void L8toRGBA8( byte in, color4ub_t *out ) { + (*out)[0] = in; + (*out)[1] = in; + (*out)[2] = in; + (*out)[3] = 255; +} + +static void UnpackTexture( int width, int height, int format, + byte *in, color4ub_t *out ) { + int x, y, blockw, blockh; + unsigned short col0, col1; + color4ub_t palette[16]; + unsigned int colorbits0, colorbits1, colorsize, colormask; + unsigned int alphabits0, alphabits1, alphasize, alphamask; + + switch( format ) { + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: +#ifdef GL_COMPRESSED_LUMINANCE_LATC1_EXT + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: +#endif + blockw = blockh = 4; + break; + default: + ri.Error( ERR_DROP, "Unknown texture format %d", format ); + return; + } + + for( x = 0; x < width; x += blockw ) { + for( y = 0; x < height; y += blockh ) { + // setup color palette + switch( format ) { + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + // two R5G6B5 colors, two bits per pixel + col0 = in[0] | (in[1] << 8); + col1 = in[2] | (in[3] << 8); + + R5G6B5toRGBA8(col0, &palette[0]); + R5G6B5toRGBA8(col1, &palette[1]); + + if( col0 <= col1 ) { + palette[2][0] = (palette[0][0] + palette[1][0]) >> 1; + palette[2][1] = (palette[0][1] + palette[1][1]) >> 1; + palette[2][2] = (palette[0][2] + palette[1][2]) >> 1; + palette[2][3] = 255; + palette[3][0] = 0; + palette[3][1] = 0; + palette[3][2] = 0; + palette[3][3] = 0; // will be overwritten for non-DXT1 formats + } else { + palette[2][0] = (2*palette[0][0] + palette[1][0]) / 3; + palette[2][1] = (2*palette[0][1] + palette[1][1]) / 3; + palette[2][2] = (2*palette[0][2] + palette[1][2]) / 3; + palette[2][3] = 255; + palette[3][0] = (palette[0][0] + 2*palette[1][0]) / 3; + palette[3][1] = (palette[0][1] + 2*palette[1][1]) / 3; + palette[3][2] = (palette[0][2] + 2*palette[1][2]) / 3; + palette[3][3] = 255; + } + colorsize = 2; colormask = 0x03; + colorbits0 = in[4] + (in[5] << 8); + colorbits1 = in[6] + (in[7] << 8); + break; +#ifdef GL_COMPRESSED_LUMINANCE_LATC1_EXT + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: + // two L8 "colors", 3 bits per pixel + L8toRGBA8( in[0], &palette[0] ); + L8toRGBA8( in[1], &palette[1] ); + + if( in[0] > in[1] ) { + palette[2][0] = palette[2][1] = palette[2][2] = + (6*in[0] + 1*in[1]) / 7; + palette[3][0] = palette[3][1] = palette[3][2] = + (5*in[0] + 2*in[1]) / 7; + palette[4][0] = palette[4][1] = palette[4][2] = + (4*in[0] + 3*in[1]) / 7; + palette[5][0] = palette[5][1] = palette[5][2] = + (3*in[0] + 4*in[1]) / 7; + palette[6][0] = palette[6][1] = palette[6][2] = + (2*in[0] + 5*in[1]) / 7; + palette[7][0] = palette[7][1] = palette[7][2] = + (1*in[0] + 6*in[1]) / 7; + } else { + palette[2][0] = palette[2][1] = palette[2][2] = + (4*in[0] + 1*in[1]) / 5; + palette[3][0] = palette[3][1] = palette[3][2] = + (3*in[0] + 2*in[1]) / 5; + palette[4][0] = palette[4][1] = palette[4][2] = + (2*in[0] + 3*in[1]) / 5; + palette[5][0] = palette[5][1] = palette[5][2] = + (1*in[0] + 4*in[1]) / 5; + palette[6][0] = palette[6][1] = palette[6][2] = + 0; + palette[7][0] = palette[7][1] = palette[7][2] = + 255; + } + colorsize = 3; colormask = 0x07; + colorbits0 = in[2] + (in[3] << 8) + (in[4] << 16); + colorbits1 = in[5] + (in[6] << 8) + (in[7] << 16); + break; +#endif + } + + // setup alpha palette + switch( format ) { + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: +#ifdef GL_COMPRESSED_LUMINANCE_LATC1_EXT + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: +#endif + // no alpha data + alphasize = 0; alphamask = 0; + alphabits0 = alphabits1 = 0; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + // fixed alpha values + palette[0][3] = 0x00; + palette[1][3] = 0x11; + palette[2][3] = 0x22; + palette[3][3] = 0x33; + palette[4][3] = 0x44; + palette[5][3] = 0x55; + palette[6][3] = 0x66; + palette[7][3] = 0x77; + palette[8][3] = 0x88; + palette[9][3] = 0x99; + palette[10][3] = 0xaa; + palette[11][3] = 0xbb; + palette[12][3] = 0xcc; + palette[13][3] = 0xdd; + palette[14][3] = 0xee; + palette[15][3] = 0xff; + + alphasize = 4; alphamask = 0x0f; + alphabits0 = in[8] + (in[9] << 8) + (in[10] << 16) + (in[11] << 24); + alphabits1 = in[12] + (in[13] << 8) + (in[14] << 16) + (in[15] << 24); + break; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: +#ifdef GL_COMPRESSED_LUMINANCE_LATC1_EXT + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: +#endif + palette[0][3] = in[8]; + palette[1][3] = in[9]; + if( in[8] > in[9] ) { + palette[2][3] = (6*in[8] + 1*in[9]) / 7; + palette[3][3] = (5*in[8] + 2*in[9]) / 7; + palette[4][3] = (4*in[8] + 3*in[9]) / 7; + palette[5][3] = (3*in[8] + 4*in[9]) / 7; + palette[6][3] = (2*in[8] + 5*in[9]) / 7; + palette[7][3] = (1*in[8] + 6*in[9]) / 7; + } else { + palette[2][3] = (4*in[8] + 1*in[9]) / 5; + palette[3][3] = (3*in[8] + 2*in[9]) / 5; + palette[4][3] = (2*in[8] + 3*in[9]) / 5; + palette[5][3] = (1*in[8] + 4*in[9]) / 5; + palette[6][3] = 0; + palette[7][3] = 255; + } + + alphasize = 3; alphamask = 0x07; + alphabits0 = in[10] + (in[11] << 8) + (in[12] << 16); + alphabits1 = in[13] + (in[14] << 8) + (in[15] << 16); + break; + } + + // decode 16 pixels +#define decodePixel(xoff, yoff, wordidx) \ + if( x + xoff < width && y + yoff < height ) { \ + int off = xoff + width * yoff; \ + int colIdx = colorbits##wordidx & colormask; \ + colorbits##wordidx >>= colorsize; \ + out[off][0] = palette[colIdx][0]; \ + out[off][1] = palette[colIdx][1]; \ + out[off][2] = palette[colIdx][2]; \ + if( alphasize ) { \ + int aIdx = alphabits##wordidx & alphamask; \ + alphabits##wordidx >>= alphasize; \ + out[off][3] = palette[aIdx][3]; \ + } else { \ + out[off][3] = palette[colIdx][3]; \ + } \ + } + + decodePixel( 0, 0, 0 ); + decodePixel( 0, 1, 0 ); + decodePixel( 0, 2, 0 ); + decodePixel( 0, 3, 0 ); + decodePixel( 1, 0, 0 ); + decodePixel( 1, 1, 0 ); + decodePixel( 1, 2, 0 ); + decodePixel( 1, 3, 0 ); + decodePixel( 2, 0, 1 ); + decodePixel( 2, 1, 1 ); + decodePixel( 2, 2, 1 ); + decodePixel( 2, 3, 1 ); + decodePixel( 3, 0, 1 ); + decodePixel( 3, 1, 1 ); + decodePixel( 3, 2, 1 ); + decodePixel( 3, 3, 1 ); +#undef decodePixel + + out += (width - x > 4 ? 4 : width - x); + } + out += 3 * width; + } +} + static void Upload32( int numTexLevels, const textureLevel_t *pics, qboolean mipmap, qboolean picmip, @@ -524,6 +743,7 @@ static void Upload32( int numTexLevels, const textureLevel_t *pics, int *pUploadWidth, int *pUploadHeight ) { int samples; + color4ub_t *unpackedBuffer = NULL; color4ub_t *scaledBuffer = NULL; color4ub_t *resampledBuffer = NULL; int scaled_width, scaled_height; @@ -553,8 +773,14 @@ static void Upload32( int numTexLevels, const textureLevel_t *pics, if( i >= numTexLevels ) goto done; - // failed to upload all levels - ri.Error(ERR_DROP, "Unsupported Texture format: %x", pics[0].format); + // failed to upload all levels, so uncompress + // the root level and fall back to RGBA uploader + unpackedBuffer = ri.Hunk_AllocateTempMemory( width * height * 4 ); + UnpackTexture( width, height, + pics[baseLevel].format, + pics[baseLevel].data, + unpackedBuffer ); + data = unpackedBuffer; } else { data = pics[baseLevel].data; } @@ -821,6 +1047,8 @@ done: ri.Hunk_FreeTempMemory( scaledBuffer ); if ( resampledBuffer != NULL ) ri.Hunk_FreeTempMemory( resampledBuffer ); + if ( unpackedBuffer != NULL ) + ri.Hunk_FreeTempMemory( unpackedBuffer ); } -- 1.7.5.4