From 99840421861586931314b2ae54c87de372ded5df Mon Sep 17 00:00:00 2001 From: Spoike Date: Mon, 14 Oct 2019 02:36:13 +0000 Subject: [PATCH] XCF support now works with 16bit+32bit linear float source files, and 16bit int files too. Added a couple extra pixel formats to the dds loader. Format conversions are handled via a table. Additional conversions added. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5565 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/image.c | 1379 ++++++++++++++++++++++++++++++---------- engine/client/render.h | 1 + imgtool.c | 367 ++++++++--- 3 files changed, 1347 insertions(+), 400 deletions(-) diff --git a/engine/client/image.c b/engine/client/image.c index b7f8d346..7ba352c4 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -198,8 +198,6 @@ static image_t *imagelist; #define DYNAMIC_LIBJPEG #endif -static void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int flags, uploadfmt_t origfmt, const char *imagename); - #ifdef DECOMPRESS_ASTC #define ASTC_PUBLIC #include "image_astc.h" @@ -213,6 +211,8 @@ const float rgb9e5tab[32] = { 1.0/(1<<8), 1.0/(1<<7), 1.0/(1<<6), 1.0/(1<<5), 1.0/(1<<4), 1.0/(1<<3), 1.0/(1<<2), 1.0/(1<<1), 1.0, 1.0*(1<<1), 1.0*(1<<2), 1.0*(1<<3), 1.0*(1<<4), 1.0*(1<<5), 1.0*(1<<6), 1.0*(1<<7), }; +static float HalfToFloat(unsigned short val); +static unsigned short FloatToHalf(float val); #if defined(AVAIL_JPEGLIB) || defined(AVAIL_PNGLIB) @@ -1284,13 +1284,7 @@ qboolean WriteTGA(const char *filename, enum fs_relative fsroot, const qbyte *ft #undef channels #ifndef PNG_SUCKS_WITH_SETJMP - #if defined(MINGW) - #include "./mingw-libs/png.h" - #elif defined(_WIN32) - #include "png.h" - #else - #include - #endif + #include "png.h" #endif #ifdef DYNAMIC_LIBPNG @@ -1592,7 +1586,7 @@ error: qpng_set_filler(png, ~0u, PNG_FILLER_AFTER); if (colortype == PNG_COLOR_TYPE_GRAY || colortype == PNG_COLOR_TYPE_GRAY_ALPHA) - { + { //FIXME: skip this if !format qpng_set_gray_to_rgb( png ); qpng_set_filler(png, ~0u, PNG_FILLER_AFTER); } @@ -1612,7 +1606,16 @@ error: if (bitdepth == 8 && channels == 4) { if (format) - *format = PTI_RGBA8; + { + if (colortype == PNG_COLOR_TYPE_GRAY) + *format = PTI_LLLX8; + else if (colortype == PNG_COLOR_TYPE_GRAY_ALPHA) + *format = PTI_LLLA8; + else if (colortype == PNG_COLOR_TYPE_RGB) + *format = PTI_RGBX8; + else //if (colortype == PNG_COLOR_TYPE_RGB_ALPHA) + *format = PTI_RGBA8; + } } else if (bitdepth == 16 && channels == 4) *format = PTI_RGBA16; @@ -1623,16 +1626,49 @@ error: return NULL; } - data = BZF_Malloc(*height * rowbytes); - rowpointers = BZF_Malloc(*height * sizeof(*rowpointers)); +#if 0//defined(PNG_READ_APNG_SUPPORTED) && !defined(DYNAMIC_LIBPNG) + if (depth) + *depth = 1; + if (depth && png_get_valid(png, pnginfo, PNG_INFO_acTL)) //looks like an apng + { + png_uint_32 numframes, numplays; + png_uint_32 f; + png_uint_32 framewidth, frameheight, framex, framey; + png_uint_16 framedelay, framedelaydiv; + png_byte framedispose, frameblend; + png_get_acTL(png, pnginfo, &numframes, &numplays); + data = BZF_Malloc(*height * rowbytes * numframes); + rowpointers = BZF_Malloc(*height * sizeof(*rowpointers)); + if (!data || !rowpointers || !numframes) + goto error; + for (f = 0; f < numframes; f++) + { + png_read_frame_head(png, pnginfo); + png_get_next_frame_fcTL(png, pnginfo, &framewidth, &frameheight, &framex, &framey, &framedelay, &framedelaydiv, &framedispose, &frameblend); - if (!data || !rowpointers) - goto error; + for (y = 0; y < *height; y++) + rowpointers[y] = data + f**height*rowbytes + y * rowbytes; - for (y = 0; y < *height; y++) - rowpointers[y] = data + y * rowbytes; + qpng_read_image(png, rowpointers); + //FIXME: merge in the preceding frame as appropriate... + } - qpng_read_image(png, rowpointers); + *depth = numframes; + } + else +#endif + { + data = BZF_Malloc(*height * rowbytes); + rowpointers = BZF_Malloc(*height * sizeof(*rowpointers)); + + if (!data || !rowpointers) + goto error; + + for (y = 0; y < *height; y++) + rowpointers[y] = data + y * rowbytes; + + qpng_read_image(png, rowpointers); + } qpng_read_end(png, NULL); qpng_destroy_read_struct(&png, &pnginfo, NULL); @@ -2147,7 +2183,7 @@ qbyte *ReadJPEGFile(qbyte *infile, int length, int *width, int *height) struct my_error_mgr jerr; /* More stuff */ JSAMPARRAY buffer; /* Output row buffer */ - int size_stride; /* physical row width in output buffer */ + size_t size_stride; /* physical row width in output buffer */ memset(&cinfo, 0, sizeof(cinfo)); @@ -2213,51 +2249,60 @@ badjpeg: Con_DPrintf("Bad number of components in JPEG: '%d', should be '3'.\n",cinfo.output_components); goto badjpeg; } + if (cinfo.output_height > ~0u / (cinfo.output_width*4)) + { //even 64bit processes can suffer when oom. + Con_Printf("Refusing to load excessively large jpeg of %u * %u.\n",cinfo.output_width, cinfo.output_width); + goto badjpeg; + } size_stride = cinfo.output_width * cinfo.output_components; /* Make a one-row-high sample array that will go away when done with image */ buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, size_stride, 1); - out=mem=BZ_Malloc(cinfo.output_height*cinfo.output_width*4); - memset(out, 0, cinfo.output_height*cinfo.output_width*4); - - if (cinfo.output_components == 1) - { - while (cinfo.output_scanline < cinfo.output_height) - { - #ifdef DYNAMIC_LIBJPEG - (void) qjpeg_read_scanlines(&cinfo, buffer, 1); - #else - (void) jpeg_read_scanlines(&cinfo, buffer, 1); - #endif - - in = buffer[0]; - for (i = 0; i < cinfo.output_width; i++) - {//rgb to rgba - *out++ = *in; - *out++ = *in; - *out++ = *in; - *out++ = 255; - in++; - } - } - } + out=mem=BZF_Malloc(cinfo.output_height*(size_t)cinfo.output_width*4u); + if (!mem) + Con_Printf("Malloc failure on %u * %u jpeg image.\n", cinfo.output_height, cinfo.output_width); else { - while (cinfo.output_scanline < cinfo.output_height) +// memset(out, 0, cinfo.output_height*(size_t)cinfo.output_width*4u); + if (cinfo.output_components == 1) { - #ifdef DYNAMIC_LIBJPEG - (void) qjpeg_read_scanlines(&cinfo, buffer, 1); - #else - (void) jpeg_read_scanlines(&cinfo, buffer, 1); - #endif + while (cinfo.output_scanline < cinfo.output_height) + { + #ifdef DYNAMIC_LIBJPEG + (void) qjpeg_read_scanlines(&cinfo, buffer, 1); + #else + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + #endif - in = buffer[0]; - for (i = 0; i < cinfo.output_width; i++) - {//rgb to rgba - *out++ = *in++; - *out++ = *in++; - *out++ = *in++; - *out++ = 255; + in = buffer[0]; + for (i = 0; i < cinfo.output_width; i++) + {//luminance to rgba + *out++ = *in; + *out++ = *in; + *out++ = *in; + *out++ = 255; + in++; + } + } + } + else + { + while (cinfo.output_scanline < cinfo.output_height) + { + #ifdef DYNAMIC_LIBJPEG + (void) qjpeg_read_scanlines(&cinfo, buffer, 1); + #else + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + #endif + + in = buffer[0]; + for (i = 0; i < cinfo.output_width; i++) + {//rgb to rgba + *out++ = *in++; + *out++ = *in++; + *out++ = *in++; + *out++ = 255; + } } } } @@ -3640,6 +3685,7 @@ struct xcf_s size_t height; int basetype, precision; + enum uploadfmt outformat; size_t numlayers; size_t *layeroffsets; qbyte *flat; @@ -3671,6 +3717,18 @@ static unsigned int XCF_Read32(struct xcf_s *f) XCF_ReadData(f, w, sizeof(w)); return (w[0]<<24)|(w[1]<<16)|(w[2]<<8)|(w[3]<<0); } +static float XCF_ReadFloat(struct xcf_s *f) +{ //XCF is natively big-endian. + union + { + unsigned int u; + float f; + } u; + qbyte w[4]; + XCF_ReadData(f, w, sizeof(w)); + u.u = (w[0]<<24)|(w[1]<<16)|(w[2]<<8)|(w[3]<<0); + return u.f; +} static size_t XCF_ReadOffset(struct xcf_s *f) { //XCF is natively big-endian. qbyte w[8]; @@ -3838,7 +3896,7 @@ static struct xcf_heirachy_s XCF_ReadHeirachy(struct xcf_s *f) lh = XCF_Read32(f); if (lw == ctx.width && lh == ctx.height) { - ctx.data = BZ_Malloc(ctx.width*ctx.bpp*ctx.height); + ctx.data = BZ_Malloc(ctx.width*(size_t)ctx.bpp*ctx.height); for (y = 0; y < ctx.height; y+=lh) { @@ -3901,6 +3959,29 @@ static struct xcf_heirachy_s XCF_ReadChannel(struct xcf_s *f) } return h; } + +static float XCF_BigFloat(void *in) +{ + union + { + qbyte b[4]; + float f; + } u; + u.b[0] = ((qbyte*)in)[3]; + u.b[1] = ((qbyte*)in)[2]; + u.b[2] = ((qbyte*)in)[1]; + u.b[3] = ((qbyte*)in)[0]; + return u.f; +} +static float XCF_BigShort(void *in) +{ + return (((qbyte*)in)[0]<<8)|(((qbyte*)in)[1]<<0); +} +static float XCF_HalfFloat(void *in) +{ + return HalfToFloat(XCF_BigShort(in)); +} + static qboolean XCF_CombineLayer(struct xcf_s *f) { struct xcf_heirachy_s h, l={0}; @@ -3912,6 +3993,7 @@ static qboolean XCF_CombineLayer(struct xcf_s *f) qboolean unsupported = false; uint32_t x,y, ofsx=0,ofsy=0; uint32_t visible = true; + float opacity = 1; width = XCF_Read32(f); height = XCF_Read32(f); type = XCF_Read32(f); @@ -3939,6 +4021,14 @@ static qboolean XCF_CombineLayer(struct xcf_s *f) return true; XCF_ReadOffset(f); } + else if (proptype == 6) //prop_opacity + opacity = XCF_Read32(f)/255.0; + else if (proptype == 33) //prop_float_opacity + opacity = XCF_ReadFloat(f); + else if (proptype == 34) //prop_colour_tag + /*tag = */XCF_Read32(f); + else if (proptype == 32) //prop_lock_content + /*lockcontent = */XCF_Read32(f); else if (proptype == 29) //prop_group_item { Con_DPrintf("Unsupported layer property: prop_group_item\n"); @@ -3955,6 +4045,8 @@ static qboolean XCF_CombineLayer(struct xcf_s *f) } else if (proptype == 31) //prop_group_item_flags XCF_Read32(f); + else if (proptype == 9) //prop_linked + /*layerislinked = */ XCF_Read32(f); //ui data, uninteresting else if (proptype == 10) //prop_lock_alpha XCF_Read32(f); //ui state else if (proptype == 7) //prop_mode @@ -3970,9 +4062,15 @@ static qboolean XCF_CombineLayer(struct xcf_s *f) XCF_Read32(f); //ui data, uninteresting else if (proptype == 26) //prop_text_layer_flags XCF_Read32(f); //ui data, uninteresting + else if (proptype == 28) //prop_lock_content + XCF_Read32(f); //ui data, uninteresting + else if (proptype == 20) //prop_tattoo + XCF_Read32(f); //ui data, uninteresting + else if (proptype == 37) //prop_blend_space + /*blendspace = */XCF_Read32(f); else { - Con_DPrintf("Unknown layer property %i\n", proptype); + Con_DPrintf("Unknown layer(\"%s\") property %i\n", name, proptype); f->offset += propsize; } } @@ -4011,44 +4109,177 @@ static qboolean XCF_CombineLayer(struct xcf_s *f) height = min(h.height, f->height-ofsy); if (ofsx < f->width && ofsy < f->height) { - qbyte *in=h.data, *out = f->flat + ofsx*4+ofsy*f->width*4; + qbyte *in=h.data; vec4_t px; float sa, da, k; - for (y = 0; y < height; y++, out += 4*(f->width-width), in += h.bpp*(h.width-width)) + if (f->outformat == PTI_RGBA32F) { - for (x = 0; x < width; x++, out+=4, in+=h.bpp) + float *out = (float*)f->flat + ofsx*4 + ofsy*f->width*4; + for (y = 0; y < height; y++, out += 4*(f->width-width), in += h.bpp*(h.width-width)) { - switch(h.bpp) + for (x = 0; x < width; x++, out+=4, in+=h.bpp) { - case 4: Vector4Set(px, in[0], in[1], in[2], in[3]); break; - case 3: Vector4Set(px, in[0], in[1], in[2], 0xff); break; - case 2: Vector4Set(px, in[0], in[0], in[0], in[1]); break; - case 1: Vector4Set(px, in[0], in[0], in[0], 0xff); break; - default: - case 0: Vector4Set(px, 0xff, 0xff, 0xff, 0xff); break; - } - if (applylayermask) - px[3] *= l.data[x+y*width]/(float)0xff; - switch(blendmode) - { - case 0: //normal(legacy) - case 28: //normal - da = out[3]/255.0; - sa = px[3]/255; - k = 1-(1-da)*(1-sa); - out[3] = k*255; - k = sa/k; - out[0] = out[0]*(1-k)+px[0]*k; - out[1] = out[1]*(1-k)+px[1]*k; - out[2] = out[2]*(1-k)+px[2]*k; - break; - default: - Con_Printf("xcf: blend mode %i is not supported\n", blendmode); - unsupported = true; - goto parseerror; + switch(h.bpp) + { + case 16: Vector4Set(px, XCF_BigFloat(in+0), XCF_BigFloat(in+4), XCF_BigFloat(in+8), XCF_BigFloat(in+12)); break; + case 12: Vector4Set(px, XCF_BigFloat(in+0), XCF_BigFloat(in+4), XCF_BigFloat(in+8), 1); break; + case 8: Vector4Set(px, XCF_BigFloat(in+0), XCF_BigFloat(in+0), XCF_BigFloat(in+0), XCF_BigFloat(in+4)); break; + case 4: Vector4Set(px, XCF_BigFloat(in+0), XCF_BigFloat(in+0), XCF_BigFloat(in+0), 1); break; + //other bpp are invalid + default: + case 0: Vector4Set(px, 1, 1, 1, 1); break; + } + if (applylayermask) + px[3] *= l.data[x+y*width]/(float)0xff; //always bytes. + px[3] *= opacity; + switch(blendmode) + { + case 0: //normal(legacy) + case 28: //normal + da = out[3]; + sa = px[3]; + k = 1-(1-da)*(1-sa); + out[3] = k; + k = sa/k; + out[0] = out[0]*(1-k)+px[0]*k; + out[1] = out[1]*(1-k)+px[1]*k; + out[2] = out[2]*(1-k)+px[2]*k; + break; + default: + Con_Printf("xcf: blend mode %i is not supported\n", blendmode); + unsupported = true; + goto parseerror; + } } } } + else if (f->outformat == PTI_RGBA16F) + { + unsigned short *out = (unsigned short*)f->flat + ofsx*4 + ofsy*f->width*4; + for (y = 0; y < height; y++, out += 4*(f->width-width), in += h.bpp*(h.width-width)) + { + for (x = 0; x < width; x++, out+=4, in+=h.bpp) + { + switch(h.bpp) + { + case 8: Vector4Set(px, XCF_HalfFloat(in+0), XCF_HalfFloat(in+2), XCF_HalfFloat(in+4), XCF_HalfFloat(in+6)); break; + case 6: Vector4Set(px, XCF_HalfFloat(in+0), XCF_HalfFloat(in+2), XCF_HalfFloat(in+4), 1); break; + case 4: Vector4Set(px, XCF_HalfFloat(in+0), XCF_HalfFloat(in+0), XCF_HalfFloat(in+0), XCF_HalfFloat(in+2)); break; + case 2: Vector4Set(px, XCF_HalfFloat(in+0), XCF_HalfFloat(in+0), XCF_HalfFloat(in+0), 1); break; + //other bpp are invalid + default: + case 0: Vector4Set(px, 1, 1, 1, 1); break; + } + if (applylayermask) + px[3] *= l.data[x+y*width]/(float)0xff; //always bytes. + px[3] *= opacity; + switch(blendmode) + { + case 0: //normal(legacy) + case 28: //normal + da = HalfToFloat(out[3]); + sa = px[3]; + k = 1-(1-da)*(1-sa); + out[3] = FloatToHalf(k); + k = sa/k; + out[0] = FloatToHalf(HalfToFloat(out[0])*(1-k)+px[0]*k); + out[1] = FloatToHalf(HalfToFloat(out[1])*(1-k)+px[1]*k); + out[2] = FloatToHalf(HalfToFloat(out[2])*(1-k)+px[2]*k); + break; + default: + Con_Printf("xcf: blend mode %i is not supported\n", blendmode); + unsupported = true; + goto parseerror; + } + } + } + } + else if (f->outformat == PTI_RGBA16) + { + unsigned short *out = (unsigned short*)f->flat + ofsx*4 + ofsy*f->width*4; + for (y = 0; y < height; y++, out += 4*(f->width-width), in += h.bpp*(h.width-width)) + { + for (x = 0; x < width; x++, out+=4, in+=h.bpp) + { + switch(h.bpp) + { + case 8: Vector4Set(px, XCF_BigShort(in+0), XCF_BigShort(in+2), XCF_BigShort(in+4), XCF_BigShort(in+6)); break; + case 6: Vector4Set(px, XCF_BigShort(in+0), XCF_BigShort(in+2), XCF_BigShort(in+4), 0xffff); break; + case 4: Vector4Set(px, XCF_BigShort(in+0), XCF_BigShort(in+0), XCF_BigShort(in+0), XCF_BigShort(in+2)); break; + case 2: Vector4Set(px, XCF_BigShort(in+0), XCF_BigShort(in+0), XCF_BigShort(in+0), 0xffff); break; + default: + case 0: Vector4Set(px, 0xffff, 0xffff, 0xffff, 0xffff); break; + } + if (applylayermask) + px[3] *= l.data[x+y*width]/(float)0xff; + px[3] *= opacity; + switch(blendmode) + { + case 0: //normal(legacy) + case 28: //normal + da = out[3]/(float)0xffff; + sa = px[3]/0xffff; + k = 1-(1-da)*(1-sa); + out[3] = k*0xffff; + k = sa/k; + out[0] = out[0]*(1-k)+px[0]*k; + out[1] = out[1]*(1-k)+px[1]*k; + out[2] = out[2]*(1-k)+px[2]*k; + break; + default: + Con_Printf("xcf: blend mode %i is not supported\n", blendmode); + unsupported = true; + goto parseerror; + } + } + } + } + else if (f->outformat == PTI_RGBA8) + { + qbyte *out = f->flat + ofsx*4 + ofsy*f->width*4; + for (y = 0; y < height; y++, out += 4*(f->width-width), in += h.bpp*(h.width-width)) + { + for (x = 0; x < width; x++, out+=4, in+=h.bpp) + { + switch(h.bpp) + { + case 4: Vector4Set(px, in[0], in[1], in[2], in[3]); break; + case 3: Vector4Set(px, in[0], in[1], in[2], 0xff); break; + case 2: Vector4Set(px, in[0], in[0], in[0], in[1]); break; + case 1: Vector4Set(px, in[0], in[0], in[0], 0xff); break; + default: + case 0: Vector4Set(px, 0xff, 0xff, 0xff, 0xff); break; + } + if (applylayermask) + px[3] *= l.data[x+y*width]/(float)0xff; + px[3] *= opacity; + switch(blendmode) + { + case 0: //normal(legacy) + case 28: //normal + da = out[3]/255.0; + sa = px[3]/255; + k = 1-(1-da)*(1-sa); + out[3] = k*255; + k = sa/k; + out[0] = out[0]*(1-k)+px[0]*k; + out[1] = out[1]*(1-k)+px[1]*k; + out[2] = out[2]*(1-k)+px[2]*k; + break; + default: + Con_Printf("xcf: blend mode %i is not supported\n", blendmode); + unsupported = true; + goto parseerror; + } + } + } + } + else + { + Con_Printf("xcf: colour precision %i is not supported\n", f->precision); + unsupported = true; + goto parseerror; + } } } @@ -4065,6 +4296,7 @@ static qbyte *ReadXCFFile(const qbyte *filedata, size_t len, const char *fname, { size_t offs; struct xcf_s ctx; + unsigned int bb,bw,bh; if (len < 14 || strncmp(filedata, "gimp xcf ", 9) || filedata[13]) return NULL; memset(&ctx, 0, sizeof(ctx)); @@ -4073,35 +4305,36 @@ static qbyte *ReadXCFFile(const qbyte *filedata, size_t len, const char *fname, ctx.filesize = len; ctx.offset = 14; + ctx.precision = 150; + ctx.outformat = PTI_RGBA8; ctx.width = XCF_Read32(&ctx); ctx.height = XCF_Read32(&ctx); ctx.basetype = XCF_Read32(&ctx); if (ctx.basetype != 0/*rgb*/ && ctx.basetype != 1/*grey*/) - return false; //doesn't really matter what format it is, we're going to output rgba regardless. we can just do it based upon the bytes per pixel. + { + Con_Printf("%s: xcf paletted mode is not supported\n", fname); + return NULL; //doesn't really matter what format it is, we're going to output rgba regardless. we can just do it based upon the bytes per pixel. + } if (ctx.version >= 4) { ctx.precision = XCF_Read32(&ctx); if (ctx.version < 7) - ctx.precision = 150; //hack + ctx.precision = 150; //dev versions have different interpretations, just ignore it so that we don't have to handle that mess. switch(ctx.precision) { - case 150: //usually this one. - // *format = PTI_RGBA8_SRGB; - // break; - case 100: - *format = PTI_RGBA8; - break; - /*case 200: - *format = PTI_RGBA16; - break; - case 500: - *format = PTI_RGBA16F; - break; - case 600: - *format = PTI_RGBA32F; - break;*/ + case 100: ctx.outformat = PTI_RGBA8; break; + case 150: ctx.outformat = PTI_RGBA8/*_SRGB*/; break; //usually this one... but we don't care too much about srgb... for some reason. + case 200: ctx.outformat = PTI_RGBA16; break; + case 250: ctx.outformat = PTI_RGBA16/*_SRGB*/; break; + //case 300: ctx.outformat = PTI_RGBA32; break; + //case 350: ctx.outformat = PTI_RGBA32/*_SRGB*/; break; + case 500: ctx.outformat = PTI_RGBA16F; break; + case 550: ctx.outformat = PTI_RGBA16F/*_SRGB*/; break; + case 600: ctx.outformat = PTI_RGBA32F; break; + case 650: ctx.outformat = PTI_RGBA32F/*_SRGB*/; break; default: - return false; + Con_Printf("%s: xcf colour precision is not supported\n", fname); + return NULL; } } XCF_ReadHeaderProperties(&ctx); @@ -4113,7 +4346,9 @@ static qbyte *ReadXCFFile(const qbyte *filedata, size_t len, const char *fname, //channels //without any layers, its fully transparent - ctx.flat = Z_Malloc(ctx.width*ctx.height*4); + Image_BlockSizeForEncoding(ctx.outformat, &bb,&bw,&bh); //just for the bb... + ctx.flat = Z_Malloc(ctx.width*ctx.height*bb); + *format = ctx.outformat; *width = ctx.width; *height = ctx.height; @@ -4234,8 +4469,8 @@ static void *ReadEXRFile(qbyte *buf, size_t len, const char *fname, int *outwidt exr.HeaderDataWindow(hdr, &xmin,&ymin, &xmax,&ymax); *outwidth = (xmax-xmin)+1; *outheight = (ymax-ymin)+1; - result = BZ_Malloc(sizeof(short)*4**outwidth**outheight); - exr.InputSetFrameBuffer(ctx, (char*)result-xmin*8-ymin**outwidth*8, 1, *outwidth); + result = BZ_Malloc(sizeof(short)*4u*(size_t)*outwidth**outheight); + exr.InputSetFrameBuffer(ctx, (char*)result-xmin*8-ymin*(size_t)*outwidth*8, 1, *outwidth); exr.InputReadPixels(ctx, ymin, ymax); exr.CloseInputFile(ctx); *outformat = PTI_RGBA16F; //output is always half-floats. @@ -4748,13 +4983,16 @@ static struct pendingtextureinfo *Image_ReadKTXFile(unsigned int flags, const ch case 0x8051/*GL_RGB8*/: //other sized RGB formats are treated based upon the data format rather than the sized format, in case they were meant to be converted by the driver... case 0x8C3D/*GL_RGB9_E5*/: case 0x8D62/*GL_RGB565*/: + case 0x8C3A/*GL_R11F_G11F_B10F*/: if (header->glformat == 0x80E0/*GL_BGR*/) encoding = PTI_BGR8; else if (header->glformat == 0x80E1/*GL_BGRA*/) encoding = PTI_BGRX8; else if (header->glformat == 0x1907/*GL_RGB*/) { - if (header->gltype == 0x8C3E/*GL_UNSIGNED_INT_5_9_9_9_REV*/) + if (header->gltype == 0x8C3B/*GL_UNSIGNED_INT_10F_11F_11F_REV*/) + encoding = PTI_B10G11R11F; + else if (header->gltype == 0x8C3E/*GL_UNSIGNED_INT_5_9_9_9_REV*/) encoding = PTI_E5BGR9; else if (header->gltype == 0x8363/*GL_UNSIGNED_SHORT_5_6_5*/) encoding = PTI_RGB565; @@ -5165,76 +5403,126 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch // pad = 8; switch(fmt10header.dxgiformat) { - case 2/*DXGI_FORMAT_R32G32B32A32_FLOAT*/: - encoding = PTI_RGBA32F; - break; - case 6/*DXGI_FORMAT_R32G32B32_FLOAT*/: - encoding = PTI_RGB32F; - break; - case 10/*DXGI_FORMAT_R16G16B16A16_FLOAT*/: - encoding = PTI_RGBA16F; - break; - case 24/*DXGI_FORMAT_R10G10B10A2_UNORM*/: - encoding = PTI_A2BGR10; - break; - case 28/*DXGI_FORMAT_R8G8B8A8_UNORM*/: - encoding = PTI_RGBA8; - break; - case 67/*DXGI_FORMAT_R9G9B9E5_SHAREDEXP*/: - encoding = PTI_E5BGR9; - break; - case 71/*DXGI_FORMAT_BC1_UNORM*/: - encoding = PTI_BC1_RGBA; - break; - case 72/*DXGI_FORMAT_BC1_UNORM_SRGB*/: - encoding = PTI_BC1_RGBA_SRGB; - break; - case 74/*DXGI_FORMAT_BC2_UNORM*/: - encoding = PTI_BC2_RGBA; - break; - case 75/*DXGI_FORMAT_BC2_UNORM_SRGB*/: - encoding = PTI_BC2_RGBA_SRGB; - break; - case 77/*DXGI_FORMAT_BC3_UNORM*/: - encoding = PTI_BC3_RGBA; - break; - case 78/*DXGI_FORMAT_BC3_UNORM_SRGB*/: - encoding = PTI_BC3_RGBA_SRGB; - break; - case 80/*DXGI_FORMAT_BC4_UNORM*/: - encoding = PTI_BC4_R8; - break; - case 81/*DXGI_FORMAT_BC4_SNORM*/: - encoding = PTI_BC4_R8_SNORM; - break; - case 83/*DXGI_FORMAT_BC5_UNORM*/: - encoding = PTI_BC5_RG8; - break; - case 84/*DXGI_FORMAT_BC5_SNORM*/: - encoding = PTI_BC5_RG8_SNORM; - break; - case 85/*DXGI_FORMAT_B5G6R5_UNORM*/: - encoding = PTI_RGB565; - break; - case 86/*DXGI_FORMAT_B5G5R5A1_UNORM*/: - encoding = PTI_ARGB1555; - break; - case 87/*DXGI_FORMAT_B8G8R8A8_UNORM*/: - encoding = PTI_BGRA8; - break; - case 95/*DXGI_FORMAT_BC6H_UF16*/: - encoding = PTI_BC6_RGB_UFLOAT; - break; - case 96/*DXGI_FORMAT_BC6H_SF16*/: - encoding = PTI_BC6_RGB_SFLOAT; - break; - case 98/*DXGI_FORMAT_BC7_UNORM*/: - encoding = PTI_BC7_RGBA; - break; - case 99/*DXGI_FORMAT_BC7_UNORM_SRGB*/: - encoding = PTI_BC7_RGBA_SRGB; - break; + case 0x0/*DXGI_FORMAT_UNKNOWN*/: encoding = PTI_INVALID; break; + case 0x1/*DXGI_FORMAT_R32G32B32A32_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x2/*DXGI_FORMAT_R32G32B32A32_FLOAT*/: encoding = PTI_RGBA32F; break; +// case 0x3/*DXGI_FORMAT_R32G32B32A32_UINT*/: encoding = PTI_INVALID; break; +// case 0x4/*DXGI_FORMAT_R32G32B32A32_SINT*/: encoding = PTI_INVALID; break; +// case 0x5/*DXGI_FORMAT_R32G32B32_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x6/*DXGI_FORMAT_R32G32B32_FLOAT*/: encoding = PTI_RGB32F; break; +// case 0x7/*DXGI_FORMAT_R32G32B32_UINT*/: encoding = PTI_INVALID; break; +// case 0x8/*DXGI_FORMAT_R32G32B32_SINT*/: encoding = PTI_INVALID; break; +// case 0x9/*DXGI_FORMAT_R16G16B16A16_TYPELESS*/: encoding = PTI_INVALID; break; + case 0xa/*DXGI_FORMAT_R16G16B16A16_FLOAT*/: encoding = PTI_RGBA16F; break; + case 0xb/*DXGI_FORMAT_R16G16B16A16_UNORM*/: encoding = PTI_RGBA16; break; +// case 0xc/*DXGI_FORMAT_R16G16B16A16_UINT*/: encoding = PTI_INVALID; break; +// case 0xd/*DXGI_FORMAT_R16G16B16A16_SNORM*/: encoding = PTI_INVALID; break; +// case 0xe/*DXGI_FORMAT_R16G16B16A16_SINT*/: encoding = PTI_INVALID; break; +// case 0xf/*DXGI_FORMAT_R32G32_TYPELESS*/: encoding = PTI_INVALID; break; +// case 0x10/*DXGI_FORMAT_R32G32_FLOAT*/: encoding = PTI_INVALID; break; +// case 0x11/*DXGI_FORMAT_R32G32_UINT*/: encoding = PTI_INVALID; break; +// case 0x12/*DXGI_FORMAT_R32G32_SINT*/: encoding = PTI_INVALID; break; +// case 0x13/*DXGI_FORMAT_R32G8X24_TYPELESS*/: encoding = PTI_INVALID; break; +// case 0x14/*DXGI_FORMAT_D32_FLOAT_S8X24_UINT*/: encoding = PTI_DEPTH32_8; break; +// case 0x15/*DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS*/:encoding = PTI_INVALID; break; +// case 0x16/*DXGI_FORMAT_X32_TYPELESS_G8X24_UINT*/:encoding = PTI_INVALID; break; +// case 0x17/*DXGI_FORMAT_R10G10B10A2_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x18/*DXGI_FORMAT_R10G10B10A2_UNORM*/: encoding = PTI_A2BGR10; break; +// case 0x19/*DXGI_FORMAT_R10G10B10A2_UINT*/: encoding = PTI_INVALID; break; + case 0x1a/*DXGI_FORMAT_R11G11B10_FLOAT*/: encoding = PTI_B10G11R11F; break; +// case 0x1b/*DXGI_FORMAT_R8G8B8A8_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x1c/*DXGI_FORMAT_R8G8B8A8_UNORM*/: encoding = PTI_RGBA8; break; + case 0x1d/*DXGI_FORMAT_R8G8B8A8_UNORM_SRGB*/: encoding = PTI_RGBA8_SRGB; break; +// case 0x1e/*DXGI_FORMAT_R8G8B8A8_UINT*/: encoding = PTI_INVALID; break; +// case 0x1f/*DXGI_FORMAT_R8G8B8A8_SNORM*/: encoding = PTI_INVALID; break; +// case 0x20/*DXGI_FORMAT_R8G8B8A8_SINT*/: encoding = PTI_INVALID; break; +// case 0x21/*DXGI_FORMAT_R16G16_TYPELESS*/: encoding = PTI_INVALID; break; +// case 0x22/*DXGI_FORMAT_R16G16_FLOAT*/: encoding = PTI_INVALID; break; +// case 0x23/*DXGI_FORMAT_R16G16_UNORM*/: encoding = PTI_INVALID; break; +// case 0x24/*DXGI_FORMAT_R16G16_UINT*/: encoding = PTI_INVALID; break; +// case 0x25/*DXGI_FORMAT_R16G16_SNORM*/: encoding = PTI_INVALID; break; +// case 0x26/*DXGI_FORMAT_R16G16_SINT*/: encoding = PTI_INVALID; break; +// case 0x27/*DXGI_FORMAT_R32_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x28/*DXGI_FORMAT_D32_FLOAT*/: encoding = PTI_DEPTH32; break; + case 0x29/*DXGI_FORMAT_R32_FLOAT*/: encoding = PTI_R32F; break; +// case 0x2a/*DXGI_FORMAT_R32_UINT*/: encoding = PTI_INVALID; break; +// case 0x2b/*DXGI_FORMAT_R32_SINT*/: encoding = PTI_INVALID; break; +// case 0x2c/*DXGI_FORMAT_R24G8_TYPELESS*/: encoding = PTI_INVALID; break; +// case 0x2d/*DXGI_FORMAT_D24_UNORM_S8_UINT*/: encoding = PTI_DEPTH24_8; break; +// case 0x2e/*DXGI_FORMAT_R24_UNORM_X8_TYPELESS*/: encoding = PTI_INVALID; break; +// case 0x2f/*DXGI_FORMAT_X24_TYPELESS_G8_UINT*/: encoding = PTI_INVALID; break; +// case 0x30/*DXGI_FORMAT_R8G8_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x31/*DXGI_FORMAT_R8G8_UNORM*/: encoding = PTI_RG8; break; +// case 0x32/*DXGI_FORMAT_R8G8_UINT*/: encoding = PTI_INVALID; break; + case 0x33/*DXGI_FORMAT_R8G8_SNORM*/: encoding = PTI_RG8_SNORM; break; +// case 0x34/*DXGI_FORMAT_R8G8_SINT*/: encoding = PTI_INVALID; break; +// case 0x35/*DXGI_FORMAT_R16_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x36/*DXGI_FORMAT_R16_FLOAT*/: encoding = PTI_R16F; break; + case 0x37/*DXGI_FORMAT_D16_UNORM*/: encoding = PTI_DEPTH16; break; + case 0x38/*DXGI_FORMAT_R16_UNORM*/: encoding = PTI_R16; break; +// case 0x39/*DXGI_FORMAT_R16_UINT*/: encoding = PTI_INVALID; break; +// case 0x3a/*DXGI_FORMAT_R16_SNORM*/: encoding = PTI_INVALID; break; +// case 0x3b/*DXGI_FORMAT_R16_SINT*/: encoding = PTI_INVALID; break; +// case 0x3c/*DXGI_FORMAT_R8_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x3d/*DXGI_FORMAT_R8_UNORM*/: encoding = PTI_R8; break; +// case 0x3e/*DXGI_FORMAT_R8_UINT*/: encoding = PTI_INVALID; break; + case 0x3f/*DXGI_FORMAT_R8_SNORM*/: encoding = PTI_R8_SNORM; break; +// case 0x40/*DXGI_FORMAT_R8_SINT*/: encoding = PTI_INVALID; break; +// case 0x41/*DXGI_FORMAT_A8_UNORM*/: encoding = PTI_A8; break; +// case 0x42/*DXGI_FORMAT_R1_UNORM*/: encoding = PTI_INVALID; break; + case 0x43/*DXGI_FORMAT_R9G9B9E5_SHAREDEXP*/: encoding = PTI_E5BGR9; break; +// case 0x44/*DXGI_FORMAT_R8G8_B8G8_UNORM*/: encoding = PTI_INVALID; break; +// case 0x45/*DXGI_FORMAT_G8R8_G8B8_UNORM*/: encoding = PTI_INVALID; break; +// case 0x46/*DXGI_FORMAT_BC1_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x47/*DXGI_FORMAT_BC1_UNORM*/: encoding = PTI_BC1_RGBA; break; + case 0x48/*DXGI_FORMAT_BC1_UNORM_SRGB*/: encoding = PTI_BC1_RGBA_SRGB; break; +// case 0x49/*DXGI_FORMAT_BC2_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x4a/*DXGI_FORMAT_BC2_UNORM*/: encoding = PTI_BC2_RGBA; break; + case 0x4b/*DXGI_FORMAT_BC2_UNORM_SRGB*/: encoding = PTI_BC2_RGBA_SRGB; break; +// case 0x4c/*DXGI_FORMAT_BC3_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x4d/*DXGI_FORMAT_BC3_UNORM*/: encoding = PTI_BC3_RGBA; break; + case 0x4e/*DXGI_FORMAT_BC3_UNORM_SRGB*/: encoding = PTI_BC3_RGBA_SRGB; break; +// case 0x4f/*DXGI_FORMAT_BC4_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x50/*DXGI_FORMAT_BC4_UNORM*/: encoding = PTI_BC4_R8; break; + case 0x51/*DXGI_FORMAT_BC4_SNORM*/: encoding = PTI_BC4_R8_SNORM; break; +// case 0x52/*DXGI_FORMAT_BC5_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x53/*DXGI_FORMAT_BC5_UNORM*/: encoding = PTI_BC5_RG8; break; + case 0x54/*DXGI_FORMAT_BC5_SNORM*/: encoding = PTI_BC5_RG8_SNORM; break; + case 0x55/*DXGI_FORMAT_B5G6R5_UNORM*/: encoding = PTI_RGB565; break; + case 0x56/*DXGI_FORMAT_B5G5R5A1_UNORM*/: encoding = PTI_ARGB1555; break; + case 0x57/*DXGI_FORMAT_B8G8R8A8_UNORM*/: encoding = PTI_BGRA8; break; + case 0x58/*DXGI_FORMAT_B8G8R8X8_UNORM*/: encoding = PTI_BGRX8; break; +// case 0x59/*DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM*/:encoding = PTI_INVALID; break; +// case 0x5a/*DXGI_FORMAT_B8G8R8A8_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x5b/*DXGI_FORMAT_B8G8R8A8_UNORM_SRGB*/: encoding = PTI_BGRA8_SRGB; break; +// case 0x5c/*DXGI_FORMAT_B8G8R8X8_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x5d/*DXGI_FORMAT_B8G8R8X8_UNORM_SRGB*/: encoding = PTI_BGRX8_SRGB; break; +// case 0x5e/*DXGI_FORMAT_BC6H_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x5f/*DXGI_FORMAT_BC6H_UF16*/: encoding = PTI_BC6_RGB_UFLOAT; break; + case 0x60/*DXGI_FORMAT_BC6H_SF16*/: encoding = PTI_BC6_RGB_SFLOAT; break; +// case 0x61/*DXGI_FORMAT_BC7_TYPELESS*/: encoding = PTI_INVALID; break; + case 0x62/*DXGI_FORMAT_BC7_UNORM*/: encoding = PTI_BC7_RGBA; break; + case 0x63/*DXGI_FORMAT_BC7_UNORM_SRGB*/: encoding = PTI_BC7_RGBA_SRGB; break; +// case 0x64/*DXGI_FORMAT_AYUV*/: encoding = PTI_INVALID; break; +// case 0x65/*DXGI_FORMAT_Y410*/: encoding = PTI_INVALID; break; +// case 0x66/*DXGI_FORMAT_Y416*/: encoding = PTI_INVALID; break; +// case 0x67/*DXGI_FORMAT_NV12*/: encoding = PTI_INVALID; break; +// case 0x68/*DXGI_FORMAT_P010*/: encoding = PTI_INVALID; break; +// case 0x69/*DXGI_FORMAT_P016*/: encoding = PTI_INVALID; break; +// case 0x6a/*DXGI_FORMAT_420_OPAQUE*/: encoding = PTI_INVALID; break; +// case 0x6b/*DXGI_FORMAT_YUY2*/: encoding = PTI_INVALID; break; +// case 0x6c/*DXGI_FORMAT_Y210*/: encoding = PTI_INVALID; break; +// case 0x6d/*DXGI_FORMAT_Y216*/: encoding = PTI_INVALID; break; +// case 0x6e/*DXGI_FORMAT_NV11*/: encoding = PTI_INVALID; break; +// case 0x6f/*DXGI_FORMAT_AI44*/: encoding = PTI_INVALID; break; +// case 0x70/*DXGI_FORMAT_IA44*/: encoding = PTI_INVALID; break; +// case 0x71/*DXGI_FORMAT_P8*/: encoding = PTI_INVALID; break; +// case 0x72/*DXGI_FORMAT_A8P8*/: encoding = PTI_INVALID; break; + case 0x73/*DXGI_FORMAT_B4G4R4A4_UNORM*/: encoding = PTI_ARGB4444; break; +// case 0x82/*DXGI_FORMAT_P208*/: encoding = PTI_INVALID; break; +// case 0x83/*DXGI_FORMAT_V208*/: encoding = PTI_INVALID; break; +// case 0x84/*DXGI_FORMAT_V408*/: encoding = PTI_INVALID; break; case 134: encoding = PTI_ASTC_4X4_LDR; break; case 135: encoding = PTI_ASTC_4X4_SRGB; break; case 138: encoding = PTI_ASTC_5X4_LDR; break; @@ -6373,6 +6661,48 @@ static void Image_MipMap4X16 (unsigned short *in, int inwidth, int inheight, uns } } +//unsigned normalised floats, for gl_ext_packed_float. +static float SmallToFloat(unsigned int val, unsigned int size) +{ + unsigned int mantissabits = size-5; + union + { + float f; + unsigned int u; + } u; + unsigned int b; + unsigned int mmask = (1<>mantissabits)&0x1f)-15+127)<<23; //read exponent, rebias it, and reshift. + + //fold the mantissa multiple times, to try to preserve as much precision as we can. + for (b = 23; b >= mantissabits; b-= mantissabits) + u.u |= (val&mmask)<<(b-mantissabits); + if (b) + u.u |= (val&mmask)>>(mantissabits-b); + return u.f; +} +//unsigned normalised floats, for gl_ext_packed_float. +static unsigned int FloatToSmall(float val, unsigned int size) +{ + unsigned int mantissabits = size-5; + union + { + float f; + unsigned int u; + } u = {val}; + int e = 0; + int m; + + e = ((u.u>>23)&0xff) - 127; + if (e < -15) + return 0; //too small exponent, treat it as a 0 denormal + if (e > 15) + m = 0; //infinity instead of a nan + else + m = (u.u&((1<<23)-1))>>(23-mantissabits); + return ((e+15)<mipcount; mip++) + { + qbyte l, a; + qbyte *in = mips->mip[mip].data; + qbyte *out = mips->mip[mip].data; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h; + unsigned short tmp; + if (!mips->mip[mip].needfree && !mips->extrafree) + { + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p*channels); + } + mips->mip[mip].datasize = p*sizeof(*out)*channels; + + for(; p-->0; in += 4) + { + l = (in[0]+in[1]+in[2])/3; + a = in[3]; + + *out++ = l; + if (channels == 2) + *out++ = a; + } + } +} +//may operate in place +static void Image_Tr_8888to565(struct pendingtextureinfo *mips, int bgra) { unsigned int mip; for (mip = 0; mip < mips->mipcount; mip++) @@ -7269,7 +7635,7 @@ static void Image_8888to565(struct pendingtextureinfo *mips, qboolean bgra) } } -static void Image_8888to1555(struct pendingtextureinfo *mips, qboolean bgra) +static void Image_Tr_8888to1555(struct pendingtextureinfo *mips, int bgra) { unsigned int mip; for (mip = 0; mip < mips->mipcount; mip++) @@ -7312,7 +7678,7 @@ static void Image_8888to1555(struct pendingtextureinfo *mips, qboolean bgra) } } -static void Image_8888to5551(struct pendingtextureinfo *mips, qboolean bgra) +static void Image_Tr_8888to5551(struct pendingtextureinfo *mips, int bgra) { unsigned int mip; for (mip = 0; mip < mips->mipcount; mip++) @@ -7355,7 +7721,7 @@ static void Image_8888to5551(struct pendingtextureinfo *mips, qboolean bgra) } } -static void Image_8888to4444(struct pendingtextureinfo *mips, qboolean bgra) +static void Image_Tr_8888to4444(struct pendingtextureinfo *mips, int bgra) { unsigned int mip; for (mip = 0; mip < mips->mipcount; mip++) @@ -7398,7 +7764,7 @@ static void Image_8888to4444(struct pendingtextureinfo *mips, qboolean bgra) } } //may operate in place -static void Image_8888toARGB4444(struct pendingtextureinfo *mips, qboolean bgra) +static void Image_Tr_8888toARGB4444(struct pendingtextureinfo *mips, int bgra) { unsigned int mip; for (mip = 0; mip < mips->mipcount; mip++) @@ -7441,7 +7807,7 @@ static void Image_8888toARGB4444(struct pendingtextureinfo *mips, qboolean bgra) } } -static void Image_4X16to8888(struct pendingtextureinfo *mips) +static void Image_Tr_4X16to8888(struct pendingtextureinfo *mips, int unused) { unsigned int mip; for (mip = 0; mip < mips->mipcount; mip++) @@ -7464,9 +7830,11 @@ static void Image_4X16to8888(struct pendingtextureinfo *mips) } //in place: E5BGR9->RGBA8 -static void Image_E5BGR9ToByte(struct pendingtextureinfo *mips) +static void Image_Tr_E5BGR9ToByte(struct pendingtextureinfo *mips, int bgr) { unsigned int mip; + int rs = bgr?18:0; + int bs = bgr?0:18; for (mip = 0; mip < mips->mipcount; mip++) { unsigned int *in = mips->mip[mip].data; @@ -7486,26 +7854,144 @@ static void Image_E5BGR9ToByte(struct pendingtextureinfo *mips) unsigned int l = *in++; float e = rgb9e5tab[l>>27]; e *= 255; //prescale to bytes. - *out++ = bound(0, e * ((l>> 0)&0x1ff), 255); + *out++ = bound(0, e * ((l>>rs)&0x1ff), 255); *out++ = bound(0, e * ((l>> 9)&0x1ff), 255); - *out++ = bound(0, e * ((l>>18)&0x1ff), 255); + *out++ = bound(0, e * ((l>>bs)&0x1ff), 255); *out++ = 255; } } } - -//in place: RGBA16F->RGBA8, or R16F->R8 -static void Image_HalfToByte(struct pendingtextureinfo *mips, int channels) +static void Image_Tr_E5BGR9ToFloat(struct pendingtextureinfo *mips, int dummy) { unsigned int mip; for (mip = 0; mip < mips->mipcount; mip++) + { + unsigned int *in = mips->mip[mip].data; + float *out = mips->mip[mip].data; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h; + if (!mips->mip[mip].needfree && !mips->extrafree) + { + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*4*p); + } + mips->mip[mip].datasize = p*sizeof(*out)*4; + + while(p-->0) + { + unsigned int l = *in++; + float e = rgb9e5tab[l>>27]; + *out++ = e * ((l>> 0)&0x1ff); + *out++ = e * ((l>> 9)&0x1ff); + *out++ = e * ((l>>18)&0x1ff); + *out++ = 1.0; + } + } +} +//always out of place +static void Image_Tr_FloatToE5BGR9(struct pendingtextureinfo *mips, int dummy) +{ + unsigned int mip; + for (mip = 0; mip < mips->mipcount; mip++) + { + float *in = mips->mip[mip].data; + unsigned int *out = mips->mip[mip].data; + float *dofree = mips->mip[mip].needfree?in:NULL; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h; + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p); + mips->mip[mip].datasize = p*sizeof(*out); + for (; p-->0; out++, in+=4) + { + int e = 0; + float m = max(max(in[0], in[1]), in[2]); + float scale; + if (m >= 0.5) + { //positive exponent + while (m >= (1<<(e)) && e < 30-15) //don't do nans. + e++; + } + else + { //negative exponent... + while (m < 1/(1<<-e) && e > -15) //don't do denormals. + e--; + } + scale = pow(2, e-9); + *out = ((e+15)<<27); + *out |= bound(0, (int)(in[0]/scale + 0.5), 0x1ff)<<0; + *out |= bound(0, (int)(in[1]/scale + 0.5), 0x1ff)<<9; + *out |= bound(0, (int)(in[2]/scale + 0.5), 0x1ff)<<18; + } + BZ_Free(dofree); + } +} + +static void Image_Tr_PackedToFloat(struct pendingtextureinfo *mips, int dummy) +{ + unsigned int mip; + for (mip = 0; mip < mips->mipcount; mip++) + { + unsigned int *in = mips->mip[mip].data; + float *out = mips->mip[mip].data; + void *dofree = mips->mip[mip].needfree?in:NULL; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h; + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4); + mips->mip[mip].datasize = p*sizeof(*out)*4; + while(p-->0) + { + unsigned int l = *in++; + *out++ = SmallToFloat(l>> 0, 11); + *out++ = SmallToFloat(l>>11, 11); + *out++ = SmallToFloat(l>>22, 10); + *out++ = 1.0; + } + BZ_Free(dofree); + } +} +//always out of place +static void Image_Tr_FloatToPacked(struct pendingtextureinfo *mips, int dummy) +{ + unsigned int mip; + for (mip = 0; mip < mips->mipcount; mip++) + { + float *in = mips->mip[mip].data; + unsigned int *out = mips->mip[mip].data; + float *dofree = mips->mip[mip].needfree?in:NULL; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h; + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p); + mips->mip[mip].datasize = p*sizeof(*out); + for (; p-->0; out++, in+=4) + { + *out = FloatToSmall(in[0], 11)<< 0; + *out |= FloatToSmall(in[1], 11)<<11; + *out |= FloatToSmall(in[2], 10)<<22; + } + BZ_Free(dofree); + } +} + +//in place: RGBA16F->RGBA8, or R16F->R8 +static void Image_Tr_HalfToByte(struct pendingtextureinfo *mips, int channels) +{ + unsigned int mip; + qboolean bs = channels == -4; + for (mip = 0; mip < mips->mipcount; mip++) { int v; unsigned short *in = mips->mip[mip].data; qbyte *out = mips->mip[mip].data; unsigned int w = mips->mip[mip].width; unsigned int h = mips->mip[mip].height; - unsigned int p = w*h*channels; + unsigned int p = w*h*abs(channels); if (!mips->mip[mip].needfree && !mips->extrafree) { mips->mip[mip].needfree = true; @@ -7513,16 +7999,33 @@ static void Image_HalfToByte(struct pendingtextureinfo *mips, int channels) } mips->mip[mip].datasize = p*sizeof(*out); - while(p-->0) + if (bs) + { //to bgra + for (; p>0; p-=4, out+=4, in+=4) + { + v = HalfToFloat(in[2])*255; + out[0] = bound(0, v, 255); + v = HalfToFloat(in[1])*255; + out[1] = bound(0, v, 255); + v = HalfToFloat(in[0])*255; + out[2] = bound(0, v, 255); + v = HalfToFloat(in[3])*255; + out[3] = bound(0, v, 255); + } + } + else { - v = HalfToFloat(*in++)*255; - *out++ = bound(0, v, 255); + while(p-->0) + { + v = HalfToFloat(*in++)*255; + *out++ = bound(0, v, 255); + } } } } //always out of place -static void Image_RGB32FToFloat(struct pendingtextureinfo *mips) +static void Image_Tr_RGB32FToFloat(struct pendingtextureinfo *mips, int dummy) { unsigned int mip; for (mip = 0; mip < mips->mipcount; mip++) @@ -7547,15 +8050,35 @@ static void Image_RGB32FToFloat(struct pendingtextureinfo *mips) } } -//in place: RGBA32F->RGBA8, or R32F->R8 -static void Image_FloatToByte(struct pendingtextureinfo *mips, int channels) +//always out of place +static void Image_Tr_HalfToFloat(struct pendingtextureinfo *mips, int channels) { unsigned int mip; for (mip = 0; mip < mips->mipcount; mip++) { - int v; float *in = mips->mip[mip].data; - qbyte *out = mips->mip[mip].data; + float *out = mips->mip[mip].data; + float *dofree = mips->mip[mip].needfree?in:NULL; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h; + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4); + mips->mip[mip].datasize = p*sizeof(*out)*4; + while(p-->0) + *out++ = HalfToFloat(*in++); + BZ_Free(dofree); + } +} + +//in place: RGBA32F->RGBA16F +static void Image_Tr_FloatToHalf(struct pendingtextureinfo *mips, int channels) +{ + unsigned int mip; + for (mip = 0; mip < mips->mipcount; mip++) + { + float *in = mips->mip[mip].data; + unsigned short *out = mips->mip[mip].data; unsigned int w = mips->mip[mip].width; unsigned int h = mips->mip[mip].height; unsigned int p = w*h*channels; @@ -7567,17 +8090,61 @@ static void Image_FloatToByte(struct pendingtextureinfo *mips, int channels) mips->mip[mip].datasize = p*sizeof(*out); while(p-->0) + *out++ = FloatToHalf(*in++); + } +} + +//in place: RGBA32F->RGBA8, or R32F->R8 +static void Image_Tr_FloatToByte(struct pendingtextureinfo *mips, int channels) +{ + unsigned int mip; + int bs = channels == -4; + for (mip = 0; mip < mips->mipcount; mip++) + { + int v; + float *in = mips->mip[mip].data; + qbyte *out = mips->mip[mip].data; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h*abs(channels); + if (!mips->mip[mip].needfree && !mips->extrafree) { - v = *in++*255; - *out++ = bound(0, v, 255); + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p); + } + mips->mip[mip].datasize = p*sizeof(*out); + + if (bs) + { //to bgra + for (; p>0; p-=4, out+=4, in+=4) + { + v = in[2]*255; + out[0] = bound(0, v, 255); + v = in[1]*255; + out[1] = bound(0, v, 255); + v = in[0]*255; + out[2] = bound(0, v, 255); + v = in[3]*255; + out[3] = bound(0, v, 255); + } + } + else + { + while(p-->0) + { + v = *in++*255; + *out++ = bound(0, v, 255); + } } } } -//in place: R8,G8,B8,X8 (aligned) -> R8,G8,B8 (tightly packed). -static void Image_32To24(struct pendingtextureinfo *mips) +//in place, drops trailing bytes +static void Image_Tr_DropBytes(struct pendingtextureinfo *mips, int srcbitsdstbits) { - int mip; + int srcbytes=srcbitsdstbits>>16; + int dstbytes=srcbitsdstbits&0xffff; + int mip, b; for (mip = 0; mip < mips->mipcount; mip++) { qbyte *in = mips->mip[mip].data; @@ -7588,31 +8155,132 @@ static void Image_32To24(struct pendingtextureinfo *mips) if (!mips->mip[mip].needfree && !mips->extrafree) { mips->mip[mip].needfree = true; - mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*3); + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*dstbytes); } - mips->mip[mip].datasize = p*sizeof(*out)*3; + mips->mip[mip].datasize = p*sizeof(*out)*dstbytes; while(p-->0) { - *out++ = *in++; - *out++ = *in++; - *out++ = *in++; - in++; + for (b = 0; b < dstbytes; b++) + *out++ = *in++; + in += srcbytes-dstbytes; } } } -//may operate in place -static void Image_8_BGR_RGB_Swap(qbyte *data, unsigned int w, unsigned int h) +static void Image_Tr_RG8ToRGXX8(struct pendingtextureinfo *mips, int dummy) { - unsigned int p = w*h; - qbyte tmp; - while(p-->0) + int mip; + for (mip = 0; mip < mips->mipcount; mip++) { - tmp = data[0]; - data[0] = data[2]; - data[2] = tmp; - data += 4; + qbyte *in = mips->mip[mip].data; + qbyte *out = mips->mip[mip].data; + void *dofree = mips->mip[mip].needfree?in:NULL; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h; + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4); + mips->mip[mip].datasize = p*sizeof(*out)*4; + while(p-->0) + { + *out++ = in[0]; + *out++ = in[1]; + *out++ = 0; + *out++ = 0xff; + in+=2; + } + BZ_Free(dofree); + } +} + +static void Image_Tr_8To10(struct pendingtextureinfo *mips, int dummy) +{ + int mip; + for (mip = 0; mip < mips->mipcount; mip++) + { + qbyte *in = mips->mip[mip].data; + unsigned int *out = mips->mip[mip].data; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h; + if (!mips->mip[mip].needfree && !mips->extrafree) + { + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p); + } + mips->mip[mip].datasize = p*sizeof(*out); + + while(p-->0) + { + *out++= (((in[0]<<2)|(in[0]>>6))<< 0) | + (((in[1]<<2)|(in[1]>>6))<<10) | + (((in[2]<<2)|(in[2]>>6))<<20) | + ((in[3]>>6)<<30); + in+=4; + } + } +} +static void Image_Tr_10To8(struct pendingtextureinfo *mips, int dummy) +{ + int mip; + for (mip = 0; mip < mips->mipcount; mip++) + { + unsigned int *in = mips->mip[mip].data, v; + qbyte *out = mips->mip[mip].data; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h; + if (!mips->mip[mip].needfree && !mips->extrafree) + { + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4); + } + mips->mip[mip].datasize = p*sizeof(*out)*4; + + while(p-->0) + { + v = *in++; + + *out++ = (v>>2)&0xff; + *out++ = (v>>12)&0xff; + *out++ = (v>>22)&0xff; + *out++ = ((v>>30)&0x3)*0x55; + } + } +} + +static void Image_Tr_Swap8888(struct pendingtextureinfo *mips, int dummy) +{ + int mip; + for (mip = 0; mip < mips->mipcount; mip++) + { + qbyte rb, g, br, a; + qbyte *in = mips->mip[mip].data; + qbyte *out = mips->mip[mip].data; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h; + if (!mips->mip[mip].needfree && !mips->extrafree) + { + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4); + } + mips->mip[mip].datasize = p*sizeof(*out)*4; + + while(p-->0) + { + a = in[3]; + rb = in[2]; + g = in[1]; + br = in[0]; + in+=4; + + *out++ = rb; + *out++ = g; + *out++ = br; + *out++ = a; + } } } @@ -9916,7 +10584,99 @@ static qboolean Image_DecompressFormat(struct pendingtextureinfo *mips, const ch return false; } -static void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int flags, uploadfmt_t origfmt, const char *imagename) +static struct +{ + uploadfmt_t src; + uploadfmt_t dest; + void (*dotransform) (struct pendingtextureinfo *mips, int arg); + int arg; + qboolean onebitalpha; +} formattransforms[] = +{ //more preferable ones should be first. + //lossy transforms will hopefully not be automatically used, but are relevant for the imgtool. + {PTI_LLLX8, PTI_RGBX8, Image_Tr_NoTransform}, + {PTI_LLLA8, PTI_RGBA8, Image_Tr_NoTransform}, + {PTI_LLLX8, PTI_BGRX8, Image_Tr_NoTransform}, + {PTI_LLLA8, PTI_BGRA8, Image_Tr_NoTransform}, + {PTI_RGBA8, PTI_BGRA8, Image_Tr_Swap8888}, + {PTI_BGRA8, PTI_RGBA8, Image_Tr_Swap8888}, + {PTI_RGBX8, PTI_BGRX8, Image_Tr_Swap8888}, + {PTI_BGRX8, PTI_RGBX8, Image_Tr_Swap8888}, + + {PTI_RGBA16, PTI_RGBA8, Image_Tr_4X16to8888}, + + //float transforms + {PTI_RGB32F, PTI_RGBA32F, Image_Tr_RGB32FToFloat}, + {PTI_RGBA32F, PTI_RGBA16F, Image_Tr_FloatToHalf, 4}, + {PTI_R32F, PTI_R16F, Image_Tr_FloatToHalf, 1}, + {PTI_RGBA16F, PTI_RGBA32F, Image_Tr_HalfToFloat, 4}, + {PTI_R16F, PTI_R32F, Image_Tr_HalfToFloat, 1}, + {PTI_RGBA16F, PTI_BGRA8, Image_Tr_HalfToByte, -4}, + {PTI_RGBA16F, PTI_RGBA8, Image_Tr_HalfToByte, 4}, + {PTI_R16F, PTI_R8, Image_Tr_HalfToByte, 1}, + {PTI_RGBA32F, PTI_BGRA8, Image_Tr_FloatToByte, -4}, + {PTI_RGBA32F, PTI_RGBA8, Image_Tr_FloatToByte, 4}, + {PTI_R32F, PTI_R8, Image_Tr_FloatToByte, 1}, + {PTI_E5BGR9, PTI_RGBX8, Image_Tr_E5BGR9ToByte, false}, + {PTI_E5BGR9, PTI_BGRX8, Image_Tr_E5BGR9ToByte, true}, + {PTI_E5BGR9, PTI_RGBA32F, Image_Tr_E5BGR9ToFloat}, + {PTI_RGBA32F, PTI_E5BGR9, Image_Tr_FloatToE5BGR9}, + {PTI_B10G11R11F,PTI_RGBA32F, Image_Tr_PackedToFloat}, + {PTI_RGBA32F, PTI_B10G11R11F, Image_Tr_FloatToPacked}, + + {PTI_LLLA8, PTI_RGBA5551, Image_Tr_8888to5551, false, true}, + {PTI_RGBA8, PTI_RGBA5551, Image_Tr_8888to5551, false, true}, + {PTI_BGRA8, PTI_RGBA5551, Image_Tr_8888to5551, true, true}, + {PTI_LLLA8, PTI_ARGB1555, Image_Tr_8888to1555, false, true}, + {PTI_RGBA8, PTI_ARGB1555, Image_Tr_8888to1555, false, true}, + {PTI_BGRA8, PTI_ARGB1555, Image_Tr_8888to1555, true, true}, + {PTI_LLLA8, PTI_RGBA4444, Image_Tr_8888to4444, false}, + {PTI_RGBA8, PTI_RGBA4444, Image_Tr_8888to4444, false}, + {PTI_BGRA8, PTI_RGBA4444, Image_Tr_8888to4444, true}, + {PTI_LLLA8, PTI_ARGB4444, Image_Tr_8888toARGB4444, false}, + {PTI_RGBA8, PTI_ARGB4444, Image_Tr_8888toARGB4444, false}, + {PTI_BGRA8, PTI_ARGB4444, Image_Tr_8888toARGB4444, true}, + + {PTI_LLLX8, PTI_RGB565, Image_Tr_8888to565, false, true}, + {PTI_RGBX8, PTI_RGB565, Image_Tr_8888to565, false, true}, + {PTI_BGRX8, PTI_RGB565, Image_Tr_8888to565, true, true}, + {PTI_LLLX8, PTI_RGB565, Image_Tr_8888to565, false, true}, + {PTI_RGBX8, PTI_RGB565, Image_Tr_8888to565, false, true}, + {PTI_BGRX8, PTI_RGB565, Image_Tr_8888to565, true, true}, + + {PTI_A2BGR10, PTI_RGBA8, Image_Tr_10To8}, + {PTI_RGBA8, PTI_A2BGR10, Image_Tr_8To10, false, true}, + + //24bit formats are probably slow + {PTI_LLLX8, PTI_RGB8, Image_Tr_DropBytes, (4<<16)|3}, + {PTI_RGBX8, PTI_RGB8, Image_Tr_DropBytes, (4<<16)|3}, + {PTI_BGRX8, PTI_BGR8, Image_Tr_DropBytes, (4<<16)|3}, + //these are last-resort. and will result in the alpha channel getting lost. + {PTI_LLLA8, PTI_RGB8, Image_Tr_DropBytes, (4<<16)|3}, + {PTI_RGBA8, PTI_RGB8, Image_Tr_DropBytes, (4<<16)|3}, + {PTI_BGRA8, PTI_BGR8, Image_Tr_DropBytes, (4<<16)|3}, + {PTI_LLLA8, PTI_RGBX8, Image_Tr_NoTransform}, + {PTI_RGBA8, PTI_RGBX8, Image_Tr_NoTransform}, + {PTI_BGRA8, PTI_BGRX8, Image_Tr_NoTransform}, + {PTI_LLLA8, PTI_RGB565, Image_Tr_8888to565, false, true}, + {PTI_RGBA8, PTI_RGB565, Image_Tr_8888to565, false, true}, + {PTI_BGRA8, PTI_RGB565, Image_Tr_8888to565, true, true}, + {PTI_LLLA8, PTI_RGB565, Image_Tr_8888to565, false, true}, + {PTI_RGBA8, PTI_RGB565, Image_Tr_8888to565, false, true}, + {PTI_BGRA8, PTI_RGB565, Image_Tr_8888to565, true, true}, + {PTI_RGBX8, PTI_L8, Image_Tr_8888toLuminence, 1, true}, + {PTI_RGBA8, PTI_L8A8, Image_Tr_8888toLuminence, 2, true}, + + {PTI_RGBX8, PTI_R8, Image_Tr_DropBytes, (4<<16)|1, true}, + {PTI_RGBX8, PTI_RG8, Image_Tr_DropBytes, (4<<16)|2, true}, //for small normalmaps (b can be inferred, a isn't available so avoid offsetmapping) + {PTI_RGBA16, PTI_R16, Image_Tr_DropBytes, (8<<16)|2, true}, + {PTI_RGBA16F, PTI_R16F, Image_Tr_DropBytes, (8<<16)|2, true}, + {PTI_RGBA32F, PTI_R32F, Image_Tr_DropBytes, (16<<16)|4, true}, + {PTI_RGBA32F, PTI_RGB32F, Image_Tr_DropBytes, (16<<16)|12, true}, + + {PTI_RG8, PTI_RGBX8, Image_Tr_RG8ToRGXX8}, +}; +void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int flags, uploadfmt_t origfmt, const char *imagename) { int mip; @@ -9937,6 +10697,8 @@ static void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int fla void *needfree = NULL; in = mips->mip[mip].data; + if (!in) + continue; if (mips->mip[mip].needfree) out = in; else @@ -9981,119 +10743,65 @@ static void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int fla if (sh_config.texfmt[mips->encoding]) return; //okay, that got it. - if (mips->encoding == PTI_E5BGR9) - { //assume that the gpu also doesn't support float formats when it doesn't support this stuff. - Image_E5BGR9ToByte(mips); - mips->encoding = PTI_RGBX8; - if (sh_config.texfmt[mips->encoding]) - return; //okay, that got it. - } - if (mips->encoding == PTI_RGBA16F || mips->encoding == PTI_R16F) - { //just convert straight to bytes, if half doesn't work then floats probably won't either. - Image_HalfToByte(mips, (mips->encoding==PTI_RGBA16F)?4:1); - mips->encoding = (mips->encoding==PTI_RGBA16F)?PTI_RGBA8:PTI_R8; - if (sh_config.texfmt[mips->encoding]) - return; //okay, that got it. - } - if (mips->encoding == PTI_RGB32F) - { - Image_RGB32FToFloat(mips); - mips->encoding = PTI_RGBA32F; - if (sh_config.texfmt[mips->encoding]) - return; //okay, that got it. - } - if (mips->encoding == PTI_RGBA32F || mips->encoding == PTI_R32F) - { - Image_FloatToByte(mips, (mips->encoding==PTI_RGBA32F)?4:1); - mips->encoding = (mips->encoding==PTI_RGBA32F)?PTI_RGBA8:PTI_R8; - if (sh_config.texfmt[mips->encoding]) - return; //okay, that got it. - } - if (mips->encoding == PTI_RGBA16) { - Image_4X16to8888(mips); - mips->encoding = PTI_RGBA8; + qboolean onebitokay = (origfmt == TF_TRANS8 || origfmt == TF_TRANS8_FULLBRIGHT || origfmt == TF_H2_TRANS8_0 || !(sh_config.texfmt[PTI_RGBA4444] || sh_config.texfmt[PTI_ARGB4444])); + uploadfmt_t src = mips->encoding; + int i, j, first = -1, sec = -1; + for (i = 0; i < countof(formattransforms); i++) + { + if (formattransforms[i].src == src) + { + if (sh_config.texfmt[formattransforms[i].dest]) + { + if (formattransforms[i].onebitalpha && !onebitokay) + { + if (first < 0) //as a last resort perhaps. + { + first = i; + sec = -1; + } + continue; + } + + //this is a direct conversion. yay. + first = i; + sec = -1; + break; + } + + //check if we can chain it to a second transform + if (first != -1) + continue; + for (j = 0; j < countof(formattransforms); j++) + { + if (formattransforms[j].src == formattransforms[i].dest) + if (sh_config.texfmt[formattransforms[j].dest]) + { + first = i; + sec = j; + break; + } + } + + //FIXME: add a third transform, for final byteswaps?... + } + } + + if (first >= 0) + { + formattransforms[first].dotransform(mips, formattransforms[first].arg); + mips->encoding = formattransforms[first].dest; + } + if (sec >= 0) + { + formattransforms[sec].dotransform(mips, formattransforms[sec].arg); + mips->encoding = formattransforms[sec].dest; + } + if (sh_config.texfmt[mips->encoding]) return; //okay, that got it. } - - if ((mips->encoding == PTI_RGBX8 && sh_config.texfmt[PTI_BGRX8]) || - (mips->encoding == PTI_BGRX8 && sh_config.texfmt[PTI_BGRX8])) - { - Image_32To24(mips); - mips->encoding = (mips->encoding == PTI_RGBX8)?PTI_RGB8:PTI_BGR8; - } - else if ((mips->encoding == PTI_RGBX8 && sh_config.texfmt[PTI_BGRX8]) || - (mips->encoding == PTI_BGRX8 && sh_config.texfmt[PTI_RGBX8]) || - (mips->encoding == PTI_RGBA8 && sh_config.texfmt[PTI_BGRA8]) || - (mips->encoding == PTI_BGRA8 && sh_config.texfmt[PTI_RGBA8])) - { - for (mip = 0; mip < mips->mipcount; mip++) - Image_8_BGR_RGB_Swap(mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height); - if (mips->encoding == PTI_RGBA8) - mips->encoding = PTI_BGRA8; - else if (mips->encoding == PTI_BGRA8) - mips->encoding = PTI_RGBA8; - else if (mips->encoding == PTI_RGBX8) - mips->encoding = PTI_BGRX8; - else// if (mips->encoding == PTI_BGRX8) - mips->encoding = PTI_RGBX8; - } - //should we just use 5551 always? - else if (mips->encoding == PTI_RGBX8 || mips->encoding == PTI_BGRX8) - { - /*if (0) - { //prevent discolouration. - if (sh_config.texfmt[PTI_RGBA5551]) - { - for (mip = 0; mip < mips->mipcount; mip++) - Image_8888to5551(mips, mip, mips->encoding == PTI_BGRX8); - mips->encoding = PTI_RGBA5551; - } - else - { - for (mip = 0; mip < mips->mipcount; mip++) - Image_8888to1555(mips, mip, mips->encoding == PTI_BGRX8); - mips->encoding = PTI_ARGB1555; - } - } - else*/ - if (sh_config.texfmt[PTI_RGB565]) - { - Image_8888to565(mips, mips->encoding == PTI_BGRX8); - mips->encoding = PTI_RGB565; - } - } - else if (mips->encoding == PTI_RGBA8 || mips->encoding == PTI_BGRA8) - { - if (origfmt == TF_TRANS8 || origfmt == TF_TRANS8_FULLBRIGHT || origfmt == TF_H2_TRANS8_0 || !(sh_config.texfmt[PTI_RGBA4444] || sh_config.texfmt[PTI_ARGB4444])) - { //1-bit alpha is okay for these textures. - if (sh_config.texfmt[PTI_RGBA5551]) - { - Image_8888to5551(mips, mips->encoding == PTI_BGRA8); - mips->encoding = PTI_RGBA5551; - } - else - { - Image_8888to1555(mips, mips->encoding == PTI_BGRA8); - mips->encoding = PTI_ARGB1555; - } - } - else - { - if (sh_config.texfmt[PTI_RGBA4444]) - { - Image_8888to4444(mips, mips->encoding == PTI_BGRA8); - mips->encoding = PTI_RGBA4444; - } - else - { - Image_8888toARGB4444(mips, mips->encoding == PTI_BGRA8); - mips->encoding = PTI_ARGB4444; - } - } - } } //resamples and depalettes as required @@ -10873,6 +11581,19 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag } } break; + case PTI_A2BGR10: + { + unsigned int *fte_restrict premul = (unsigned int*)mips->mip[0].data, r,g,b,a; + for (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++) + { + a = (*premul>>30)&0x3; + b = (((*premul>>20)&0x3ff)*a)>>2; + g = (((*premul>>10)&0x3ff)*a)>>2; + r = (((*premul>> 0)&0x3ff)*a)>>2; + *premul++ = (a<<30)|(b<<20)|(g<<20)|(r<<0); + } + } + break; case PTI_LLLX8: //FIXME: why the Xs? case PTI_LLLA8: case PTI_RGBA8: diff --git a/engine/client/render.h b/engine/client/render.h index ef85b9c4..83406fb4 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -476,6 +476,7 @@ const char *Image_FormatName(uploadfmt_t encoding); qboolean Image_FormatHasAlpha(uploadfmt_t encoding); image_t *Image_LoadTexture (const char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags); struct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char *iname, const char *fname, qbyte *filedata, int filesize); +void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int flags, uploadfmt_t origfmt, const char *imagename); #ifdef D3D8QUAKE void D3D8_Set2D (void); diff --git a/imgtool.c b/imgtool.c index f3449b12..2c0bf440 100644 --- a/imgtool.c +++ b/imgtool.c @@ -215,6 +215,18 @@ static int QDECL ImgFile_WriteBytes(struct vfsfile_s *file, const void *buffer, struct imgfile_s *f = (struct imgfile_s*)file; return fwrite(buffer, 1, bytestowrite, f->f); } +static qboolean QDECL ImgFile_Seek(struct vfsfile_s *file, qofs_t newofs) +{ + struct imgfile_s *f = (struct imgfile_s*)file; + if (fseek(f->f, newofs, SEEK_SET)==0) + return true; //success + return false; +} +static qofs_t QDECL ImgFile_Tell(struct vfsfile_s *file) +{ + struct imgfile_s *f = (struct imgfile_s*)file; + return ftell(f->f); +} vfsfile_t *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_relative relativeto) { if (!strcmp(mode, "wb")) @@ -224,6 +236,8 @@ vfsfile_t *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_rela r->pub.seekstyle = SS_UNSEEKABLE; r->pub.Close = ImgFile_Close; r->pub.WriteBytes = ImgFile_WriteBytes; + r->pub.Seek = ImgFile_Seek; + r->pub.Tell = ImgFile_Tell; if (r->f) return &r->pub; free(r); @@ -272,12 +286,59 @@ void VARGS Q_snprintfz (char *dest, size_t size, const char *fmt, ...) va_end (argptr); } +//palette data is used in lmps, as well as written into pcxes or wads, probably some other things. qbyte *host_basepal; unsigned int d_8to24rgbtable[256]; unsigned int d_8to24bgrtable[256]; +static qbyte default_quakepal[768] = +{ //the quake palette was released into the public domain (or at least gpl) to ease development of tools writing quake-format data. +0,0,0,15,15,15,31,31,31,47,47,47,63,63,63,75,75,75,91,91,91,107,107,107,123,123,123,139,139,139,155,155,155,171,171,171,187,187,187,203,203,203,219,219,219,235,235,235,15,11,7,23,15,11,31,23,11,39,27,15,47,35,19,55,43,23,63,47,23,75,55,27,83,59,27,91,67,31,99,75,31,107,83,31,115,87,31,123,95,35,131,103,35,143,111,35,11,11,15,19,19,27,27,27,39,39,39,51,47,47,63,55,55,75,63,63,87,71,71,103,79,79,115,91,91,127,99,99, +139,107,107,151,115,115,163,123,123,175,131,131,187,139,139,203,0,0,0,7,7,0,11,11,0,19,19,0,27,27,0,35,35,0,43,43,7,47,47,7,55,55,7,63,63,7,71,71,7,75,75,11,83,83,11,91,91,11,99,99,11,107,107,15,7,0,0,15,0,0,23,0,0,31,0,0,39,0,0,47,0,0,55,0,0,63,0,0,71,0,0,79,0,0,87,0,0,95,0,0,103,0,0,111,0,0,119,0,0,127,0,0,19,19,0,27,27,0,35,35,0,47,43,0,55,47,0,67, +55,0,75,59,7,87,67,7,95,71,7,107,75,11,119,83,15,131,87,19,139,91,19,151,95,27,163,99,31,175,103,35,35,19,7,47,23,11,59,31,15,75,35,19,87,43,23,99,47,31,115,55,35,127,59,43,143,67,51,159,79,51,175,99,47,191,119,47,207,143,43,223,171,39,239,203,31,255,243,27,11,7,0,27,19,0,43,35,15,55,43,19,71,51,27,83,55,35,99,63,43,111,71,51,127,83,63,139,95,71,155,107,83,167,123,95,183,135,107,195,147,123,211,163,139,227,179,151, +171,139,163,159,127,151,147,115,135,139,103,123,127,91,111,119,83,99,107,75,87,95,63,75,87,55,67,75,47,55,67,39,47,55,31,35,43,23,27,35,19,19,23,11,11,15,7,7,187,115,159,175,107,143,163,95,131,151,87,119,139,79,107,127,75,95,115,67,83,107,59,75,95,51,63,83,43,55,71,35,43,59,31,35,47,23,27,35,19,19,23,11,11,15,7,7,219,195,187,203,179,167,191,163,155,175,151,139,163,135,123,151,123,111,135,111,95,123,99,83,107,87,71,95,75,59,83,63, +51,67,51,39,55,43,31,39,31,23,27,19,15,15,11,7,111,131,123,103,123,111,95,115,103,87,107,95,79,99,87,71,91,79,63,83,71,55,75,63,47,67,55,43,59,47,35,51,39,31,43,31,23,35,23,15,27,19,11,19,11,7,11,7,255,243,27,239,223,23,219,203,19,203,183,15,187,167,15,171,151,11,155,131,7,139,115,7,123,99,7,107,83,0,91,71,0,75,55,0,59,43,0,43,31,0,27,15,0,11,7,0,0,0,255,11,11,239,19,19,223,27,27,207,35,35,191,43, +43,175,47,47,159,47,47,143,47,47,127,47,47,111,47,47,95,43,43,79,35,35,63,27,27,47,19,19,31,11,11,15,43,0,0,59,0,0,75,7,0,95,7,0,111,15,0,127,23,7,147,31,7,163,39,11,183,51,15,195,75,27,207,99,43,219,127,59,227,151,79,231,171,95,239,191,119,247,211,139,167,123,59,183,155,55,199,195,55,231,227,87,127,191,255,171,231,255,215,255,255,103,0,0,139,0,0,179,0,0,215,0,0,255,0,0,255,243,147,255,247,199,255,255,255,159,91,83 +}; qbyte GetPaletteIndexNoFB(int red, int green, int blue) { - return 0; + int i; + int best=0; + int bestdist=INT_MAX; + int dist; + for (i = 0; i < 256-32; i++) + { + dist = + abs(host_basepal[i*3+0]-red)+ + abs(host_basepal[i*3+1]-green)+ + abs(host_basepal[i*3+2]-blue); + if (dist < bestdist) + { + bestdist = dist; + best = i; + } + } + return best; +} +static void ImgTool_SetupPalette(void) +{ + int i; + //we ought to try to read gfx/palette.lmp, but its probably in a pak + host_basepal = default_quakepal; + for (i = 0; i < 256; i++) + { + d_8to24rgbtable[i] = (host_basepal[i*3+0]<<0)|(host_basepal[i*3+1]<<8)|(host_basepal[i*3+2]<<16); + d_8to24bgrtable[i] = (host_basepal[i*3+0]<<16)|(host_basepal[i*3+1]<<8)|(host_basepal[i*3+2]<<0); + } +} +static void ImgTool_FreeMips(struct pendingtextureinfo *mips) +{ + size_t i; + for (i = 0; i < mips->mipcount; i++) + if (mips->mip[i].needfree) + BZ_Free(mips->mip[i].data); + if (mips->extrafree) + BZ_Free(mips->extrafree); + BZ_Free(mips); } sh_config_t sh_config; @@ -373,7 +434,28 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna else if (targfmt == PTI_BC7_RGBA) Q_snprintfz(command, sizeof(command), "nvcompress -bc7"); else + { + if (mips->encoding != targfmt) + { + for (m = 0; m < PTI_MAX; m++) + sh_config.texfmt[m] = (m == targfmt); + Image_ChangeFormat(mips, args->flags, PTI_INVALID, inname); + if (mips->encoding == targfmt) + return true; + + //switch to common formats... + for (m = 0; m < PTI_MAX; m++) + sh_config.texfmt[m] = (m == targfmt) || (m==PTI_RGBA8); + Image_ChangeFormat(mips, args->flags, PTI_INVALID, inname); + //and try again... + for (m = 0; m < PTI_MAX; m++) + sh_config.texfmt[m] = (m == targfmt); + Image_ChangeFormat(mips, args->flags, PTI_INVALID, inname); + + return (mips->encoding == targfmt); + } return false; + } if (canktx) FS_MakeTempName(raw, sizeof(raw), "itr", ".ktx"); else @@ -397,6 +479,27 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna Q_strncatz(command, " -normal", sizeof(command)); //looks like a normalmap... tweak metrics to favour normalised results. Q_strncatz(command, ">> /dev/null", sizeof(command)); + + if (!canktx) + { + //make sure the source pixel format is acceptable if we're forced to write a png + for (m = 0; m < PTI_MAX; m++) + sh_config.texfmt[m] = + (m == PTI_RGBA8) || (m == PTI_RGBX8) || + (m == PTI_BGRA8) || (m == PTI_BGRX8) || + (m == PTI_LLLA8) || (m == PTI_LLLX8) || + (m == PTI_RGBA16) || + (m == PTI_L8) || (m == PTI_L8A8) || + /*(m == PTI_L16) ||*/ + (m == PTI_BGR8) || (m == PTI_BGR8) || + 0; + Image_ChangeFormat(mips, args->flags&~IF_PREMULTIPLYALPHA, PTI_INVALID, inname); + + //make sure we properly read whatever is given back to us + for (m = 1; m < countof(sh_config.texfmt); m++) + sh_config.texfmt[m] = true; + } + Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh); for (m = 0; m < mips->mipcount; m++) { @@ -467,7 +570,7 @@ const char *COM_GetFileExtension (const char *in, const char *term) static void ImgTool_Convert(struct opts_s *args, const char *inname, const char *outname) { qbyte *indata; - size_t fsize; + size_t fsize, k; struct pendingtextureinfo *in; const char *outext = COM_GetFileExtension(outname, NULL); qboolean allowcompressed = false; @@ -482,19 +585,30 @@ static void ImgTool_Convert(struct opts_s *args, const char *inname, const char if (in) { printf("%s: %s, %i*%i, %i mips\n", inname, Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height, in->mipcount); - /*if (imgtool_convertto >= PTI_BC1_RGB && imgtool_convertto <= PTI_BC5_RG8_SNORM) - ImgTool_ConvertToBCn(imgtool_convertto, in); - else*/ - { - if (!(args->flags & IF_NOMIPMAP) && in->mipcount == 1) - Image_GenerateMips(in, args->flags); - if (args->newpixelformat != PTI_INVALID && allowcompressed && ImgTool_ConvertPixelFormat(args, inname, in)) - printf("\t(Converted to %s)\n", Image_FormatName(in->encoding)); + if (!(args->flags & IF_NOMIPMAP) && in->mipcount == 1) + Image_GenerateMips(in, args->flags); + + if (args->mipnum >= in->mipcount) + { + ImgTool_FreeMips(in); + Con_Printf("%s: Requested output mip number was out of bounds %i >= %i\n", outname, args->mipnum, in->mipcount); + return; } + for (k = 0; k < args->mipnum; k++) + { + if (in->mip[k].needfree) + BZ_Free(in->mip[k].data); + } + in->mipcount -= k; + memmove(in->mip, &in->mip[k], sizeof(in->mip[0])*in->mipcount); + + if (args->newpixelformat != PTI_INVALID && (args->newpixelformat < PTI_BC1_RGB || allowcompressed) && ImgTool_ConvertPixelFormat(args, inname, in)) + printf("\t(Converted to %s)\n", Image_FormatName(in->encoding)); if (!in->mipcount) { + ImgTool_FreeMips(in); printf("%s: unable to convert any mips\n", inname); return; } @@ -503,47 +617,83 @@ static void ImgTool_Convert(struct opts_s *args, const char *inname, const char ; #ifdef IMAGEFMT_KTX else if (!strcmp(outext, ".ktx")) - Image_WriteKTXFile(outname, FS_SYSTEM, in); + { + if (!Image_WriteKTXFile(outname, FS_SYSTEM, in)) + Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); + } #endif #ifdef IMAGEFMT_DDS else if (!strcmp(outext, ".dds")) - Image_WriteDDSFile(outname, FS_SYSTEM, in); + { + if (!Image_WriteDDSFile(outname, FS_SYSTEM, in)) + Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); + } #endif else { int bb,bw,bh; - Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh); - if (args->mipnum < in->mipcount) - { - if (0) - ; + + if (0) + ; #ifdef IMAGEFMT_PNG - else if (!strcmp(outext, ".png")) - { + else if (!strcmp(outext, ".png")) + { #ifdef AVAIL_PNGLIB - if (!Image_WritePNG(outname, FS_SYSTEM, 0, &in->mip[args->mipnum].data, 1, in->mip[args->mipnum].width*bb, in->mip[args->mipnum].width, in->mip[args->mipnum].height, in->encoding, false)) -#endif - Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); + //force the format, because we can. + for (k = 0; k < PTI_MAX; k++) + sh_config.texfmt[k] = + (k == PTI_RGBA8) || (k == PTI_RGBX8) || + (k == PTI_BGRA8) || (k == PTI_BGRX8) || + (k == PTI_LLLA8) || (k == PTI_LLLX8) || + (k == PTI_RGBA16) || + (k == PTI_L8) || (k == PTI_L8A8) || + /*(k == PTI_L16) ||*/ + (k == PTI_BGR8) || (k == PTI_BGR8) || + 0; + if (!sh_config.texfmt[in->encoding]) + { + Image_ChangeFormat(in, args->flags&~IF_PREMULTIPLYALPHA, PTI_INVALID, inname); + printf("\t(Exporting as %s)\n", Image_FormatName(in->encoding)); } + Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh); + if (!Image_WritePNG(outname, FS_SYSTEM, 0, &in->mip[0].data, 1, in->mip[0].width*bb, in->mip[0].width, in->mip[0].height, in->encoding, false)) +#endif + Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); + } #endif #ifdef IMAGEFMT_TGA - else if (!strcmp(outext, ".tga")) + else if (!strcmp(outext, ".tga")) + { + for (k = 0; k < PTI_MAX; k++) + sh_config.texfmt[k] = + (k == PTI_RGBA8) || (k == PTI_RGBX8) || + (k == PTI_BGRA8) || (k == PTI_BGRX8) || + (k == PTI_LLLA8) || (k == PTI_LLLX8) || + (k == PTI_RGBA16F) || (k == PTI_R16F) || //half-float tgas is a format extension, but allow it. + (k == PTI_L8) || (k == PTI_L8A8) || + /*(k == PTI_L16) ||*/ + (k == PTI_BGR8) || (k == PTI_BGR8) || + 0; + if (!sh_config.texfmt[in->encoding]) { - if (!WriteTGA(outname, FS_SYSTEM, in->mip[args->mipnum].data, in->mip[args->mipnum].width*bb, in->mip[args->mipnum].width, in->mip[args->mipnum].height, in->encoding)) - Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); + Image_ChangeFormat(in, args->flags&~IF_PREMULTIPLYALPHA, PTI_INVALID, inname); + printf("\t(Exporting as %s)\n", Image_FormatName(in->encoding)); } -#endif - else - Con_Printf("%s: Unknown output file format\n", outname); + Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh); + if (!WriteTGA(outname, FS_SYSTEM, in->mip[0].data, in->mip[0].width*bb, in->mip[0].width, in->mip[0].height, in->encoding)) + Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); } +#endif else - Con_Printf("%s: Requested output mip number was out of bounds %i >= %i\n", outname, args->mipnum, in->mipcount); + Con_Printf("%s: Unknown output file format\n", outname); } // printf("%s: %s, %i*%i, %i mips\n", outname, Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height, in->mipcount); // for (m = 0; m < in->mipcount; m++) // printf("\t%u: %i*%i*%i, %u\n", (unsigned)m, in->mip[m].width, in->mip[m].height, in->mip[m].depth, (unsigned)in->mip[m].datasize); + + ImgTool_FreeMips(in); } else printf("%s: unsupported format\n", inname); @@ -571,6 +721,8 @@ static void ImgTool_Info(struct opts_s *args, const char *inname) printf("%s: %s, %i*%i, %i mips\n", inname, Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height, in->mipcount); for (m = 0; m < in->mipcount; m++) printf("\t%u: %i*%i*%i, %u\n", (unsigned)m, in->mip[m].width, in->mip[m].height, in->mip[m].depth, (unsigned)in->mip[m].datasize); + + ImgTool_FreeMips(in); } } fflush(stdout); @@ -731,27 +883,26 @@ static void ImgTool_TreeConvert(struct opts_s *args, const char *srcpath, const -/* typedef struct { - long offset; // Position of the entry in WAD - long dsize; // Size of the entry in WAD file - long size; // Size of the entry in memory - char type; // type of entry - char cmprs; // Compression. 0 if none. - short dummy; // Not used - char name[16]; // we use only first 8 + unsigned int offset; // Position of the entry in WAD + unsigned int dsize; // Size of the entry in WAD file + unsigned int size; // Size of the entry in memory + char type; // type of entry + char cmprs; // Compression. 0 if none. + short dummy; // Not used + char name[16]; // we use only first 8 } wad2entry_t; typedef struct { - char magic[4]; //should be WAD2 - long num; //number of entries - long offset; //location of directory + char magic[4]; //should be WAD2 + unsigned int num; //number of entries + unsigned int offset; //location of directory } wad2_t; -static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const char *destpath) +static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const char *destpath, int wadtype/*x,2,3*/) { char file[MAX_OSPATH]; - const char *exts[] = {".png", ".bmp", ".tga", ".jpg", ".exr", ".hdr", NULL}; + const char *exts[] = {".png", ".bmp", ".tga", ".exr", ".hdr", ".dds", ".ktx", ".xcf", ".pcx", ".jpg", NULL}; struct filelist_s list = {exts}; size_t i, u; vfsfile_t *f; @@ -760,7 +911,7 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c size_t fsize; wad2_t wad2; wad2entry_t *wadentries = NULL, *entry; - size_t maxentries; + size_t maxentries = 0; miptex_t mip; ImgTool_TreeScan(&list, srcpath, NULL); @@ -768,7 +919,9 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c wad2.magic[0] = 'W'; wad2.magic[1] = 'A'; wad2.magic[2] = 'D'; - wad2.magic[3] = '3'; //wad3 instead of 2, so we can include a palette for tools to validate against + wad2.magic[3] = (wadtype==2)?'3':'2'; //wad3 instead of 2, so we can include a palette for tools to validate against + wad2.num = 0; + wad2.offset = 0; VFS_WRITE(f, &wad2, 12); //try to decompress everything to a nice friendly palletizable range. @@ -787,7 +940,7 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c indata = FS_LoadMallocFile(file, &fsize); if (indata) { - struct pendingtextureinfo *in = Image_LoadMipsFromMemory(args->flags|IF_PALETTIZE, inname, file, indata, fsize); + struct pendingtextureinfo *in = Image_LoadMipsFromMemory(args->flags, inname, file, indata, fsize); Image_GenerateMips(in, args->flags); if (in) { @@ -796,17 +949,56 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c if (!in->mipcount) { - printf("%s: unable to load any mips\n", inname); + ImgTool_FreeMips(in); + Con_Printf("%s: unable to load any mips\n", inname); continue; } } + if (args->mipnum >= in->mipcount) { - printf("%s: not enough mips\n", inname); + ImgTool_FreeMips(in); + Con_Printf("%s: not enough mips\n", inname); + continue; + } + + //strip out all but the 4 mip levels we care about. + for (u = 0; u < in->mipcount; u++) + { + if (u >= args->mipnum && u < args->mipnum+4) + { + if (!wadtype) + { //if we're stripping out the wad data (so that the engine ends up requiring external textures) then do it now before palettizing, for efficiency. + if (in->mip[u].needfree) + BZ_Free(in->mip[u].data); + in->mip[u].data = NULL; + in->mip[u].datasize = 0; + } + } + else + { + if (in->mip[u].needfree) + BZ_Free(in->mip[u].data); + memset(&in->mip[u], 0, sizeof(in->mip[u])); + } + } + in->mipcount -= args->mipnum; + if (in->mipcount > 4) + in->mipcount = 4; + memmove(&in->mip[0], &in->mip[args->mipnum], sizeof(in->mip[0])*in->mipcount); + memset(&in->mip[in->mipcount], 0, sizeof(in->mip[0])*((args->mipnum+4)-in->mipcount)); //null it out, just in case. + if (!in->mip[0].width || (in->mip[0].width & 15)) + Con_Printf("%s(%i): WARNING: miptex width is not a multiple of 16 - %i*%i\n", inname, args->mipnum, in->mip[0].width, in->mip[0].height); + if (!in->mip[0].height || (in->mip[0].height & 15)) + Con_Printf("%s(%i): WARNING: miptex height is not a not multiple of 16 - %i*%i\n", inname, args->mipnum, in->mip[0].width, in->mip[0].height); + + if (in->encoding != PTI_P8) + Image_ChangeFormat(in, IF_PALETTIZE, (*inname=='{')?TF_TRANS8:PTI_INVALID, inname); + if (in->encoding != PTI_P8) + { //erk! we failed to palettize... + ImgTool_FreeMips(in); continue; } - if ((in->mip[args->mipnum].width|in->mip[args->mipnum].height) & 15) - printf("%s(%i): WARNING: not multiple of 16 - %i*%i\n", inname, args->mipnum, in->mip[args->mipnum].width, in->mip[args->mipnum].height); if (wad2.num == maxentries) { @@ -816,24 +1008,28 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c entry = &wadentries[wad2.num++]; Q_strncpyz(entry->name, inname, 16); entry->name[list.file[i].baselen] = 0; //kill any .tga + if (*entry->name == '#') + *entry->name = '*'; //* is not valid in a filename, yet needed for turbs, so by convention # is used instead. this is only relevant for the first char. entry->type = TYP_MIPTEX; entry->cmprs = 0; entry->dummy = 0; entry->offset = VFS_TELL(f); memcpy(mip.name, entry->name, sizeof(mip.name)); - mip.width = in->mip[args->mipnum].width; - mip.height = in->mip[args->mipnum].height; - mip.offsets[0] = in->mip[args->mipnum+0].datasize?sizeof(mip):0; - mip.offsets[1] = in->mip[args->mipnum+1].datasize?mip.offsets[args->mipnum+0]+in->mip[args->mipnum+0].datasize:0; - mip.offsets[2] = in->mip[args->mipnum+2].datasize?mip.offsets[args->mipnum+1]+in->mip[args->mipnum+1].datasize:0; - mip.offsets[3] = in->mip[args->mipnum+3].datasize?mip.offsets[args->mipnum+2]+in->mip[args->mipnum+2].datasize:0; + mip.width = in->mip[0].width; + mip.height = in->mip[0].height; + mip.offsets[0] = in->mip[0].datasize?sizeof(mip):0; + mip.offsets[1] = in->mip[1].datasize?mip.offsets[0]+in->mip[0].datasize:0; + mip.offsets[2] = in->mip[2].datasize?mip.offsets[1]+in->mip[1].datasize:0; + mip.offsets[3] = in->mip[3].datasize?mip.offsets[2]+in->mip[2].datasize:0; + + Con_Printf("%s: %ix%i\n", mip.name, mip.width, mip.height); VFS_WRITE(f, &mip, sizeof(mip)); - VFS_WRITE(f, in->mip[args->mipnum+0].data, in->mip[args->mipnum+0].datasize); - VFS_WRITE(f, in->mip[args->mipnum+1].data, in->mip[args->mipnum+1].datasize); - VFS_WRITE(f, in->mip[args->mipnum+2].data, in->mip[args->mipnum+2].datasize); - VFS_WRITE(f, in->mip[args->mipnum+3].data, in->mip[args->mipnum+3].datasize); + VFS_WRITE(f, in->mip[0].data, in->mip[0].datasize); + VFS_WRITE(f, in->mip[1].data, in->mip[1].datasize); + VFS_WRITE(f, in->mip[2].data, in->mip[2].datasize); + VFS_WRITE(f, in->mip[3].data, in->mip[3].datasize); if (wad2.magic[3] == '3') { VFS_WRITE(f, "\x00\x01", 2); @@ -841,6 +1037,7 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c } entry->size = entry->dsize = VFS_TELL(f)-entry->offset; + ImgTool_FreeMips(in); } } wad2.offset = VFS_TELL(f); @@ -851,7 +1048,7 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *srcpath, const c FileList_Release(&list); } -*/ + int main(int argc, const char **argv) { @@ -860,7 +1057,9 @@ int main(int argc, const char **argv) mode_info, mode_convert, mode_autotree, - mode_genwad + mode_genwadx, + mode_genwad2, + mode_genwad3, } mode = mode_info; size_t u, f; struct opts_s args; @@ -881,6 +1080,7 @@ int main(int argc, const char **argv) sh_config.npot_rounddown = true; //shouldn't be relevant sh_config.havecubemaps = true; //I don't think this matters. + ImgTool_SetupPalette(); Image_Init(); if (argc==1) @@ -897,22 +1097,43 @@ showhelp: Con_Printf("compress : %s --astc_6x6_ldr [--nomips] in.png out.ktx [in2.png out2.ktx]\n", argv[0]); Con_Printf("compress : %s --bc3_rgba [--premul] [--nomips] in.png out.dds\n\tConvert pixel format (to bc3 aka dxt5) before writing to output file.\n", argv[0]); Con_Printf("convert : %s --convert in.exr out.dds\n\tConvert to different file format, while trying to preserve pixel formats.\n", argv[0]); + Con_Printf("recursive : %s --astc_6x6_ldr -r srcdir destdir\n", argv[0]); Con_Printf("decompress: %s --decompress [--exportmip 0] [--nomips] in.ktx out.png\n\tDecompresses any block-compressed pixel data.\n", argv[0]); -// Con_Printf("gen wad : %s --genwad [--exportmip 2] srcdir out.wad\n", argv[0]); -// Con_Printf("auto : %s --astc_6x6_ldr -r _postfix.png srcdir destdir\n", argv[0]); + Con_Printf("gen wad : %s --genwad3 [--exportmip 2] srcdir out.wad\n", argv[0]); Image_PrintInputFormatVersions(); - Con_Printf("Supported compressed pixelformats are:\n"); + Con_Printf("Supported compressed/interesting pixelformats are:\n"); for (f = 0; f < PTI_MAX; f++) { int bb,bw,bh; Image_BlockSizeForEncoding(f, &bb,&bw,&bh); if (f >= PTI_ASTC_FIRST && f <= PTI_ASTC_LAST) - Con_Printf(" --%-15s %.2fbpp (requires astcenc)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); + Con_Printf(" --%-16s %5.3g-bpp (requires astcenc)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); else if (f==PTI_BC1_RGB||f==PTI_BC1_RGBA||f==PTI_BC2_RGBA||f==PTI_BC3_RGBA||f==PTI_BC4_R8||f==PTI_BC5_RG8) - Con_Printf(" --%-15s %.2fbpp (requires nvcompress)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); + Con_Printf(" --%-16s %5.3g-bpp (requires nvcompress)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); else if (f==PTI_BC6_RGB_UFLOAT || f==PTI_BC6_RGB_SFLOAT || f==PTI_BC7_RGBA) - Con_Printf(" --%-15s %.2fbpp (requires nvcompress 2.1+)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); + Con_Printf(" --%-16s %5.3g-bpp (requires nvcompress 2.1+)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); + else if ( f==PTI_RGBA16F || + f==PTI_RGBA32F || + f==PTI_E5BGR9 || + f==PTI_B10G11R11F || + f==PTI_RGB565 || + f==PTI_RGBA4444 || + f==PTI_ARGB4444 || + f==PTI_RGBA5551 || + f==PTI_ARGB1555 || + f==PTI_A2BGR10 || +// f==PTI_R8 || +// f==PTI_R16 || +// f==PTI_R16F || +// f==PTI_R32F || + f==PTI_RG8 || + f==PTI_L8 || + f==PTI_L8A8 || + 0) + Con_Printf(" --%-16s %5.3g-bpp\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); +// else +// Con_DPrintf(" --%-16s %5.3g-bpp (unsupported)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); } break; } @@ -928,8 +1149,12 @@ showhelp: mode = mode_autotree; else if (!strcmp(argv[u], "-i") || !strcmp(argv[u], "--info")) mode = mode_info; - else if (!strcmp(argv[u], "-w") || !strcmp(argv[u], "--genwad")) - mode = mode_genwad; + else if (!strcmp(argv[u], "-w") || !strcmp(argv[u], "--genwad3")) + mode = mode_genwad3; + else if (!strcmp(argv[u], "-w") || !strcmp(argv[u], "--genwad2")) + mode = mode_genwad2; + else if (!strcmp(argv[u], "-w") || !strcmp(argv[u], "--genwadx")) + mode = mode_genwadx; else if (!strcmp(argv[u], "--nomips") ) args.flags |= IF_NOMIPMAP; else if (!strcmp(argv[u], "--mips")) @@ -989,11 +1214,11 @@ showhelp: u++; } } - else if (mode == mode_genwad) + else if (mode == mode_genwad2 || mode == mode_genwad3 || mode == mode_genwadx) { if (u+1 < argc) { - //ImgTool_WadConvert(&args, argv[u], argv[u+1]); + ImgTool_WadConvert(&args, argv[u], argv[u+1], mode-mode_genwadx); u++; } }