From e2cab23c62e497b1f06192e6aeeef7f828fa6fd9 Mon Sep 17 00:00:00 2001 From: Matthias Bentrup Date: Tue, 31 Jan 2012 17:24:59 +0100 Subject: [PATCH 2/5] New R_CreateImage API The new function doesn't get just width, height and an array of RGBA pixels, but an array of textureLevel_t, each describing one mipmap level of the texture. If there are fewer levels than required, the remaining levels are computed, so image loaders may pass just the base level as usual. The textureLevel_t has a format field to specify the format of the pixel data, but this patch allows only GL_RGBA8. The new image loader accepts also non-power-of-two sized textures if all mipmaps are provided. If the mipmaps have to be computed, the texture is still rescaled to a power-of-two size. --- code/renderer/tr_bsp.c | 11 +++- code/renderer/tr_image.c | 160 ++++++++++++++++++++++++++++++++---------- code/renderer/tr_image_bmp.c | 24 +++---- code/renderer/tr_image_jpg.c | 15 ++-- code/renderer/tr_image_pcx.c | 25 +++---- code/renderer/tr_image_png.c | 45 +++--------- code/renderer/tr_image_tga.c | 23 +++---- code/renderer/tr_local.h | 27 +++++-- 8 files changed, 201 insertions(+), 129 deletions(-) diff --git a/code/renderer/tr_bsp.c b/code/renderer/tr_bsp.c index 17afd7e..9164686 100644 --- a/code/renderer/tr_bsp.c +++ b/code/renderer/tr_bsp.c @@ -139,6 +139,7 @@ static void R_LoadLightmaps( lump_t *l ) { int i, j; float maxIntensity = 0; double sumIntensity = 0; + textureLevel_t pic; len = l->filelen; if ( !len ) { @@ -202,8 +203,14 @@ static void R_LoadLightmaps( lump_t *l ) { image[j*4+3] = 255; } } - tr.lightmaps[i] = R_CreateImage( va("*lightmap%d",i), image, - LIGHTMAP_SIZE, LIGHTMAP_SIZE, qfalse, qfalse, GL_CLAMP_TO_EDGE ); + + pic.format = GL_RGBA8; + pic.width = LIGHTMAP_SIZE; + pic.height = LIGHTMAP_SIZE; + pic.size = sizeof(image); + pic.data = image; + tr.lightmaps[i] = R_CreateImage( va("*lightmap%d",i), 1, &pic, + qfalse, qfalse, GL_CLAMP_TO_EDGE ); } if ( r_lightmap->integer == 2 ) { diff --git a/code/renderer/tr_image.c b/code/renderer/tr_image.c index 6b74bc0..f3478bb 100644 --- a/code/renderer/tr_image.c +++ b/code/renderer/tr_image.c @@ -469,20 +469,44 @@ color4ub_t mipBlendColors[16] = { }; +static qboolean UploadOneTexLevel( int level, const textureLevel_t *pic ) +{ + GLsizei w; + if( pic->format != GL_RGBA8 ) { + return qfalse; + } else { + qglTexImage2D( GL_PROXY_TEXTURE_2D, level, + pic->format, + pic->width, pic->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, + NULL ); + qglGetTexLevelParameteriv( GL_PROXY_TEXTURE_2D, level, + GL_TEXTURE_WIDTH, &w); + if( !w ) + return qfalse; + + qglTexImage2D( GL_TEXTURE_2D, level, + pic->format, + pic->width, pic->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, + pic->data ); + } + + return qtrue; +} + /* =============== Upload32 =============== */ -extern qboolean charSet; -static void Upload32( color4ub_t *data, - int width, int height, - qboolean mipmap, - qboolean picmip, - qboolean lightMap, - int *format, - int *pUploadWidth, int *pUploadHeight ) +static void Upload32( int numTexLevels, const textureLevel_t *pics, + qboolean mipmap, + qboolean picmip, + qboolean lightMap, + int *format, + int *pUploadWidth, int *pUploadHeight ) { int samples; color4ub_t *scaledBuffer = NULL; @@ -492,6 +516,33 @@ static void Upload32( color4ub_t *data, color4ub_t *scan; GLenum internalFormat = GL_RGB; float rMax = 0, gMax = 0, bMax = 0; + int baseLevel = 0; + int width = 0, height = 0; + color4ub_t *data; + + // we may skip some textureLevels, if r_picmip is set + if( picmip ) + baseLevel = r_picmip->integer; + if( baseLevel >= numTexLevels ) + baseLevel = numTexLevels - 1; + + width = pics[baseLevel].width; + height = pics[baseLevel].height; + + if( pics[0].format != GL_RGBA8 ) { + // compressed texture + for( i = baseLevel; i < numTexLevels; i++ ) { + if( !UploadOneTexLevel( i - baseLevel, &pics[i] ) ) + break; + } + if( i >= numTexLevels ) + goto done; + + // failed to upload all levels + ri.Error(ERR_DROP, "Unsupported Texture format: %x", pics[0].format); + } else { + data = pics[baseLevel].data; + } // // convert to exact power of 2 sizes @@ -517,8 +568,8 @@ static void Upload32( color4ub_t *data, // perform optional picmip operation // if ( picmip ) { - scaled_width >>= r_picmip->integer; - scaled_height >>= r_picmip->integer; + scaled_width >>= r_picmip->integer - baseLevel; + scaled_height >>= r_picmip->integer - baseLevel; } // @@ -765,8 +816,9 @@ R_CreateImage This is the only way any image_t are created ================ */ -image_t *R_CreateImage( const char *name, const byte *pic, int width, int height, - qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) { +image_t *R_CreateImage( const char *name, int numTexLevels, + const textureLevel_t *pic, qboolean mipmap, + qboolean allowPicmip, int glWrapClampMode ) { image_t *image; qboolean isLightmap = qfalse; long hash; @@ -791,8 +843,8 @@ image_t *R_CreateImage( const char *name, const byte *pic, int width, int height strcpy (image->imgName, name); - image->width = width; - image->height = height; + image->width = pic[0].width; + image->height = pic[0].height; image->wrapClampMode = glWrapClampMode; // lightmaps are always allocated on TMU 1 @@ -808,13 +860,13 @@ image_t *R_CreateImage( const char *name, const byte *pic, int width, int height GL_Bind(image); - Upload32( (color4ub_t *)pic, image->width, image->height, - image->mipmap, - allowPicmip, - isLightmap, - &image->internalFormat, - &image->uploadWidth, - &image->uploadHeight ); + Upload32( numTexLevels, pic, + image->mipmap, + allowPicmip, + isLightmap, + &image->internalFormat, + &image->uploadWidth, + &image->uploadHeight ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode ); qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode ); @@ -837,7 +889,7 @@ image_t *R_CreateImage( const char *name, const byte *pic, int width, int height typedef struct { char *ext; - void (*ImageLoader)( const char *, unsigned char **, int *, int * ); + void (*ImageLoader)( const char *, int *, textureLevel_t ** ); } imageExtToLoaderMap_t; // Note that the ordering indicates the order of preference used @@ -862,7 +914,7 @@ Loads any of the supported image types into a cannonical 32 bit format. ================= */ -void R_LoadImage( const char *name, byte **pic, int *width, int *height ) +static void R_LoadImage( const char *name, int *numLevels, textureLevel_t **pic ) { qboolean orgNameFailed = qfalse; int orgLoader = -1; @@ -872,8 +924,7 @@ void R_LoadImage( const char *name, byte **pic, int *width, int *height ) char *altName; *pic = NULL; - *width = 0; - *height = 0; + *numLevels = 0; Q_strncpyz( localName, name, MAX_QPATH ); @@ -887,7 +938,7 @@ void R_LoadImage( const char *name, byte **pic, int *width, int *height ) if( !Q_stricmp( ext, imageLoaders[ i ].ext ) ) { // Load - imageLoaders[ i ].ImageLoader( localName, pic, width, height ); + imageLoaders[ i ].ImageLoader( localName, numLevels, pic ); break; } } @@ -921,7 +972,7 @@ void R_LoadImage( const char *name, byte **pic, int *width, int *height ) altName = va( "%s.%s", localName, imageLoaders[ i ].ext ); // Load - imageLoaders[ i ].ImageLoader( altName, pic, width, height ); + imageLoaders[ i ].ImageLoader( altName, numLevels, pic ); if( *pic ) { @@ -947,8 +998,8 @@ Returns NULL if it fails, not a default image. */ image_t *R_FindImageFile( const char *name, qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) { image_t *image; - int width, height; - byte *pic; + int numLevels; + textureLevel_t *pic; long hash; if (!name) { @@ -981,12 +1032,13 @@ image_t *R_FindImageFile( const char *name, qboolean mipmap, qboolean allowPicmi // // load the pic from disk // - R_LoadImage( name, &pic, &width, &height ); + R_LoadImage( name, &numLevels, &pic ); if ( pic == NULL ) { return NULL; } - image = R_CreateImage( ( char * ) name, pic, width, height, mipmap, allowPicmip, glWrapClampMode ); + image = R_CreateImage( ( char * ) name, numLevels, pic, mipmap, + allowPicmip, glWrapClampMode ); ri.Free( pic ); return image; } @@ -1002,6 +1054,7 @@ static void R_CreateDlightImage( void ) { int x,y; color4ub_t data[DLIGHT_SIZE][DLIGHT_SIZE]; int b; + textureLevel_t pic; // make a centered inverse-square falloff blob for dynamic lighting for (x=0 ; xformat = GL_RGBA8; + (*pic)->width = columns; + (*pic)->height = rows; + (*pic)->size = numPixels * sizeof(color4ub_t); + (*pic)->data = bmpRGBA = (byte *)(*pic + 1); + *numTexLevels = 1; for ( row = rows-1; row >= 0; row-- ) { diff --git a/code/renderer/tr_image_jpg.c b/code/renderer/tr_image_jpg.c index 028be6d..f0c4ff7 100644 --- a/code/renderer/tr_image_jpg.c +++ b/code/renderer/tr_image_jpg.c @@ -65,7 +65,7 @@ static void R_JPGOutputMessage(j_common_ptr cinfo) ri.Printf(PRINT_ALL, "%s\n", buffer); } -void R_LoadJPG(const char *filename, unsigned char **pic, int *width, int *height) +void R_LoadJPG(const char *filename, int *numTexLevels, textureLevel_t **pic) { /* This struct contains the JPEG decompression parameters and pointers to * working space (which is allocated as needed by the JPEG library). @@ -176,10 +176,13 @@ void R_LoadJPG(const char *filename, unsigned char **pic, int *width, int *heigh memcount = pixelcount * 4; row_stride = cinfo.output_width * cinfo.output_components; - out = ri.Malloc(memcount); - - *width = cinfo.output_width; - *height = cinfo.output_height; + *pic = (textureLevel_t *)ri.Malloc(sizeof(textureLevel_t) + memcount); + (*pic)->format = GL_RGBA8; + (*pic)->width = cinfo.output_width; + (*pic)->height = cinfo.output_height; + (*pic)->size = memcount; + (*pic)->data = out = (byte *)(*pic + 1); + *numTexLevels = 1; /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ @@ -211,8 +214,6 @@ void R_LoadJPG(const char *filename, unsigned char **pic, int *width, int *heigh buf[--dindex] = buf[--sindex]; } while(sindex); - *pic = out; - /* Step 7: Finish decompression */ jpeg_finish_decompress(&cinfo); diff --git a/code/renderer/tr_image_pcx.c b/code/renderer/tr_image_pcx.c index d1b1a5e..f80666f 100644 --- a/code/renderer/tr_image_pcx.c +++ b/code/renderer/tr_image_pcx.c @@ -48,7 +48,7 @@ typedef struct { unsigned char data[]; } pcx_t; -void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height) +void R_LoadPCX ( const char *filename, int *numTexLevels, textureLevel_t **pic) { union { byte *b; @@ -58,18 +58,15 @@ void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height) pcx_t *pcx; int len; unsigned char dataByte = 0, runLength = 0; - byte *out, *pix; + byte *pix; unsigned short w, h; byte *pic8; byte *palette; int i; unsigned size = 0; - if (width) - *width = 0; - if (height) - *height = 0; *pic = NULL; + *numTexLevels = 0; // // load the file @@ -152,7 +149,14 @@ void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height) palette = end-768; - pix = out = ri.Malloc(4 * size ); + *pic = ri.Malloc( sizeof(textureLevel_t) + 4 * size ); + (*pic)->format = GL_RGBA8; + (*pic)->width = w; + (*pic)->height = h; + (*pic)->size = size * sizeof(color4ub_t); + (*pic)->data = pix = (byte *)(*pic + 1); + *numTexLevels = 1; + for (i = 0 ; i < size ; i++) { unsigned char p = pic8[i]; @@ -163,13 +167,6 @@ void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height) pix += 4; } - if (width) - *width = w; - if (height) - *height = h; - - *pic = out; - ri.FS_FreeFile (pcx); ri.Free (pic8); } diff --git a/code/renderer/tr_image_png.c b/code/renderer/tr_image_png.c index 4dc9d9b..f8a114c 100644 --- a/code/renderer/tr_image_png.c +++ b/code/renderer/tr_image_png.c @@ -1901,7 +1901,7 @@ static qboolean DecodeImageInterlaced(struct PNG_Chunk_IHDR *IHDR, * The PNG loader */ -void R_LoadPNG(const char *name, byte **pic, int *width, int *height) +void R_LoadPNG(const char *name, int *numTexLevels, textureLevel_t **pic) { struct BufferedFile *ThePNG; byte *OutBuffer; @@ -1945,16 +1945,7 @@ void R_LoadPNG(const char *name, byte **pic, int *width, int *height) */ *pic = NULL; - - if(width) - { - *width = 0; - } - - if(height) - { - *height = 0; - } + *numTexLevels = 0; /* * Read the file. @@ -2399,8 +2390,9 @@ void R_LoadPNG(const char *name, byte **pic, int *width, int *height) * Allocate output buffer. */ - OutBuffer = ri.Malloc(IHDR_Width * IHDR_Height * Q3IMAGE_BYTESPERPIXEL); - if(!OutBuffer) + *pic = (textureLevel_t *)ri.Malloc( sizeof(textureLevel_t) + IHDR_Width * IHDR_Height * Q3IMAGE_BYTESPERPIXEL ); + + if(!(*pic)) { ri.Free(DecompressedData); CloseBufferedFile(ThePNG); @@ -2408,6 +2400,13 @@ void R_LoadPNG(const char *name, byte **pic, int *width, int *height) return; } + (*pic)->format = GL_RGBA8; + (*pic)->width = IHDR_Width; + (*pic)->height = IHDR_Height; + (*pic)->size = IHDR_Width * IHDR_Height * Q3IMAGE_BYTESPERPIXEL; + (*pic)->data = OutBuffer = (byte *)(*pic + 1); + *numTexLevels = 1; + /* * Interlaced and Non-interlaced images need to be handled differently. */ @@ -2453,26 +2452,6 @@ void R_LoadPNG(const char *name, byte **pic, int *width, int *height) } /* - * update the pointer to the image data - */ - - *pic = OutBuffer; - - /* - * Fill width and height. - */ - - if(width) - { - *width = IHDR_Width; - } - - if(height) - { - *height = IHDR_Height; - } - - /* * DecompressedData is not needed anymore. */ diff --git a/code/renderer/tr_image_tga.c b/code/renderer/tr_image_tga.c index 5eebde2..f642a5b 100644 --- a/code/renderer/tr_image_tga.c +++ b/code/renderer/tr_image_tga.c @@ -38,7 +38,7 @@ typedef struct _TargaHeader { unsigned char pixel_size, attributes; } TargaHeader; -void R_LoadTGA ( const char *name, byte **pic, int *width, int *height) +void R_LoadTGA ( const char *name, int *numTexLevels, textureLevel_t **pic) { unsigned columns, rows, numPixels; byte *pixbuf; @@ -54,11 +54,7 @@ void R_LoadTGA ( const char *name, byte **pic, int *width, int *height) int length; *pic = NULL; - - if(width) - *width = 0; - if(height) - *height = 0; + *numTexLevels = 0; // // load the file @@ -126,7 +122,13 @@ void R_LoadTGA ( const char *name, byte **pic, int *width, int *height) } - targa_rgba = ri.Malloc (numPixels); + *pic = (textureLevel_t *)ri.Malloc( sizeof(textureLevel_t) + numPixels ); + (*pic)->format = GL_RGBA8; + (*pic)->width = columns; + (*pic)->height = rows; + (*pic)->size = numPixels; + (*pic)->data = targa_rgba = (byte *)(*pic + 1); + *numTexLevels = 1; if (targa_header.id_length != 0) { @@ -309,12 +311,5 @@ void R_LoadTGA ( const char *name, byte **pic, int *width, int *height) ri.Printf( PRINT_WARNING, "WARNING: '%s' TGA file header declares top-down image, ignoring\n", name); } - if (width) - *width = columns; - if (height) - *height = rows; - - *pic = targa_rgba; - ri.FS_FreeFile (buffer.v); } diff --git a/code/renderer/tr_local.h b/code/renderer/tr_local.h index 58c3e2a..8eb39cc 100644 --- a/code/renderer/tr_local.h +++ b/code/renderer/tr_local.h @@ -1247,8 +1247,10 @@ model_t *R_AllocModel( void ); void R_Init( void ); image_t *R_FindImageFile( const char *name, qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ); -image_t *R_CreateImage( const char *name, const byte *pic, int width, int height, qboolean mipmap - , qboolean allowPicmip, int wrapClampMode ); +typedef struct textureLevel_s textureLevel_t; +image_t *R_CreateImage( const char *name, int numTexLevels, + const textureLevel_t *pic, qboolean mipmap, + qboolean allowPicmip, int wrapClampMode ); qboolean R_GetModeInfo( int *width, int *height, float *windowAspect, int mode ); void R_SetColorMappings( void ); @@ -1540,11 +1542,22 @@ IMAGE LOADERS ============================================================= */ -void R_LoadBMP( const char *name, byte **pic, int *width, int *height ); -void R_LoadJPG( const char *name, byte **pic, int *width, int *height ); -void R_LoadPCX( const char *name, byte **pic, int *width, int *height ); -void R_LoadPNG( const char *name, byte **pic, int *width, int *height ); -void R_LoadTGA( const char *name, byte **pic, int *width, int *height ); +// pic points to an array of numLevels textureLevel_t structs that describe +// the individual mip levels, the format is either GL_RGBA8 or a compressed +// internalformat if the data is precompressed. +struct textureLevel_s { + GLenum format; + int width, height; + GLsizei size; + void *data; +}; + +void R_LoadBMP( const char *name, int *numLevels, textureLevel_t **pic ); +void R_LoadDDS( const char *name, int *numLevels, textureLevel_t **pic ); +void R_LoadJPG( const char *name, int *numLevels, textureLevel_t **pic ); +void R_LoadPCX( const char *name, int *numLevels, textureLevel_t **pic ); +void R_LoadPNG( const char *name, int *numLevels, textureLevel_t **pic ); +void R_LoadTGA( const char *name, int *numLevels, textureLevel_t **pic ); /* ============================================================= -- 1.7.5.4