X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=image.c;h=c556f3930bfbc7cd0170ceaeeb439908673d9718;hb=1b6062f6c60a2525501e1133052d87307ee02e36;hp=5059710e550f4fe157c24f08d0c794ef53d08cd8;hpb=66cfa344b8aeaaaf19c3e609032807046c906d78;p=xonotic%2Fdarkplaces.git diff --git a/image.c b/image.c index 5059710e..c556f393 100644 --- a/image.c +++ b/image.c @@ -8,6 +8,14 @@ int image_width; int image_height; +void Image_CopyAlphaFromBlueBGRA(unsigned char *outpixels, const unsigned char *inpixels, int w, int h) +{ + int i, n; + n = w * h; + for(i = 0; i < n; ++i) + outpixels[4*i+3] = inpixels[4*i]; // blue channel +} + #if 1 // written by LordHavoc in a readable way, optimized by Vic, further optimized by LordHavoc (the non-special index case), readable version preserved below this void Image_CopyMux(unsigned char *outpixels, const unsigned char *inpixels, int inputwidth, int inputheight, qboolean inputflipx, qboolean inputflipy, qboolean inputflipdiagonal, int numoutputcomponents, int numinputcomponents, int *outputinputcomponentindices) @@ -26,14 +34,14 @@ void Image_CopyMux(unsigned char *outpixels, const unsigned char *inpixels, int if (inputflipdiagonal) { for (x = 0, line = inpixels + col_ofs; x < inputwidth; x++, line += col_inc) - for (y = 0, in = line + row_ofs; y < inputheight; y++, in += row_inc, outpixels += numinputcomponents) + for (y = 0, in = line + row_ofs; y < inputheight; y++, in += row_inc, outpixels += numoutputcomponents) for (c = 0; c < numoutputcomponents; c++) outpixels[c] = ((index = outputinputcomponentindices[c]) & 0x80000000) ? index : in[index]; } else { for (y = 0, line = inpixels + row_ofs; y < inputheight; y++, line += row_inc) - for (x = 0, in = line + col_ofs; x < inputwidth; x++, in += col_inc, outpixels += numinputcomponents) + for (x = 0, in = line + col_ofs; x < inputwidth; x++, in += col_inc, outpixels += numoutputcomponents) for (c = 0; c < numoutputcomponents; c++) outpixels[c] = ((index = outputinputcomponentindices[c]) & 0x80000000) ? index : in[index]; } @@ -44,14 +52,14 @@ void Image_CopyMux(unsigned char *outpixels, const unsigned char *inpixels, int if (inputflipdiagonal) { for (x = 0, line = inpixels + col_ofs; x < inputwidth; x++, line += col_inc) - for (y = 0, in = line + row_ofs; y < inputheight; y++, in += row_inc, outpixels += numinputcomponents) + for (y = 0, in = line + row_ofs; y < inputheight; y++, in += row_inc, outpixels += numoutputcomponents) for (c = 0; c < numoutputcomponents; c++) outpixels[c] = in[outputinputcomponentindices[c]]; } else { for (y = 0, line = inpixels + row_ofs; y < inputheight; y++, line += row_inc) - for (x = 0, in = line + col_ofs; x < inputwidth; x++, in += col_inc, outpixels += numinputcomponents) + for (x = 0, in = line + col_ofs; x < inputwidth; x++, in += col_inc, outpixels += numoutputcomponents) for (c = 0; c < numoutputcomponents; c++) outpixels[c] = in[outputinputcomponentindices[c]]; } @@ -115,7 +123,7 @@ void Image_GammaRemapRGB(const unsigned char *in, unsigned char *out, int pixels } // note: pal must be 32bit color -void Image_Copy8bitRGBA(const unsigned char *in, unsigned char *out, int pixels, const unsigned int *pal) +void Image_Copy8bitBGRA(const unsigned char *in, unsigned char *out, int pixels, const unsigned int *pal) { int *iout = (int *)out; while (pixels >= 8) @@ -181,10 +189,10 @@ typedef struct pcx_s LoadPCX ============ */ -unsigned char* LoadPCX (const unsigned char *f, int filesize, int matchwidth, int matchheight) +unsigned char* LoadPCX_BGRA (const unsigned char *f, int filesize, int *miplevel) { pcx_t pcx; - unsigned char *a, *b, *image_rgba, *pbuf; + unsigned char *a, *b, *image_buffer, *pbuf; const unsigned char *palette, *fin, *enddata; int x, y, x2, dataByte; @@ -211,23 +219,21 @@ unsigned char* LoadPCX (const unsigned char *f, int filesize, int matchwidth, in image_width = pcx.xmax + 1 - pcx.xmin; image_height = pcx.ymax + 1 - pcx.ymin; - if (pcx.manufacturer != 0x0a || pcx.version != 5 || pcx.encoding != 1 || pcx.bits_per_pixel != 8 || image_width > 4096 || image_height > 4096 || image_width <= 0 || image_height <= 0) + if (pcx.manufacturer != 0x0a || pcx.version != 5 || pcx.encoding != 1 || pcx.bits_per_pixel != 8 || image_width > 32768 || image_height > 32768 || image_width <= 0 || image_height <= 0) { Con_Print("Bad pcx file\n"); return NULL; } - if ((matchwidth && image_width != matchwidth) || (matchheight && image_height != matchheight)) - return NULL; palette = f + filesize - 768; - image_rgba = (unsigned char *)Mem_Alloc(tempmempool, image_width*image_height*4); - if (!image_rgba) + image_buffer = (unsigned char *)Mem_Alloc(tempmempool, image_width*image_height*4); + if (!image_buffer) { Con_Printf("LoadPCX: not enough memory for %i by %i image\n", image_width, image_height); return NULL; } - pbuf = image_rgba + image_width*image_height*3; + pbuf = image_buffer + image_width*image_height*3; enddata = palette; for (y = 0;y < image_height && fin < enddata;y++) @@ -250,24 +256,102 @@ unsigned char* LoadPCX (const unsigned char *f, int filesize, int matchwidth, in else a[x++] = dataByte; } - fin += pcx.bytes_per_line - image_width; // the number of bytes per line is always forced to an even number while(x < image_width) a[x++] = 0; } - a = image_rgba; + a = image_buffer; b = pbuf; for(x = 0;x < image_width*image_height;x++) { y = *b++ * 3; - *a++ = palette[y]; - *a++ = palette[y+1]; *a++ = palette[y+2]; + *a++ = palette[y+1]; + *a++ = palette[y]; *a++ = 255; } - return image_rgba; + return image_buffer; +} + +/* +============ +LoadPCX +============ +*/ +qboolean LoadPCX_QWSkin(const unsigned char *f, int filesize, unsigned char *pixels, int outwidth, int outheight) +{ + pcx_t pcx; + unsigned char *a; + const unsigned char *fin, *enddata; + int x, y, x2, dataByte, pcxwidth, pcxheight; + + if (filesize < (int)sizeof(pcx) + 768) + return false; + + image_width = outwidth; + image_height = outheight; + fin = f; + + memcpy(&pcx, fin, sizeof(pcx)); + fin += sizeof(pcx); + + // LordHavoc: big-endian support ported from QF newtree + pcx.xmax = LittleShort (pcx.xmax); + pcx.xmin = LittleShort (pcx.xmin); + pcx.ymax = LittleShort (pcx.ymax); + pcx.ymin = LittleShort (pcx.ymin); + pcx.hres = LittleShort (pcx.hres); + pcx.vres = LittleShort (pcx.vres); + pcx.bytes_per_line = LittleShort (pcx.bytes_per_line); + pcx.palette_type = LittleShort (pcx.palette_type); + + pcxwidth = pcx.xmax + 1 - pcx.xmin; + pcxheight = pcx.ymax + 1 - pcx.ymin; + if (pcx.manufacturer != 0x0a || pcx.version != 5 || pcx.encoding != 1 || pcx.bits_per_pixel != 8 || pcxwidth > 4096 || pcxheight > 4096 || pcxwidth <= 0 || pcxheight <= 0) + return false; + + enddata = f + filesize - 768; + + for (y = 0;y < outheight && fin < enddata;y++) + { + a = pixels + y * outwidth; + // pad the output with blank lines if needed + if (y >= pcxheight) + { + memset(a, 0, outwidth); + continue; + } + for (x = 0;x < pcxwidth;) + { + if (fin >= enddata) + return false; + dataByte = *fin++; + if(dataByte >= 0xC0) + { + x2 = x + (dataByte & 0x3F); + if (fin >= enddata) + return false; + if (x2 > pcxwidth) + return false; + dataByte = *fin++; + for (;x < x2;x++) + if (x < outwidth) + a[x] = dataByte; + } + else + { + if (x < outwidth) // truncate to destination width + a[x] = dataByte; + x++; + } + } + while(x < outwidth) + a[x++] = 0; + } + + return true; } /* @@ -298,14 +382,20 @@ void PrintTargaHeader(TargaHeader *t) LoadTGA ============= */ -unsigned char *LoadTGA (const unsigned char *f, int filesize, int matchwidth, int matchheight) +unsigned char *LoadTGA_BGRA (const unsigned char *f, int filesize, int *miplevel) { - int x, y, pix_inc, row_inc, red, green, blue, alpha, runlen, alphabits; - unsigned char *pixbuf, *image_rgba; + int x, y, pix_inc, row_inci, runlen, alphabits; + unsigned char *image_buffer; + unsigned int *pixbufi; const unsigned char *fin, *enddata; - unsigned char *p; TargaHeader targa_header; - unsigned char palette[256*4]; + unsigned int palettei[256]; + union + { + unsigned int i; + unsigned char b[4]; + } + bgra; if (filesize < 19) return NULL; @@ -323,16 +413,15 @@ unsigned char *LoadTGA (const unsigned char *f, int filesize, int matchwidth, in targa_header.y_origin = f[10] + f[11] * 256; targa_header.width = image_width = f[12] + f[13] * 256; targa_header.height = image_height = f[14] + f[15] * 256; - if (image_width > 4096 || image_height > 4096 || image_width <= 0 || image_height <= 0) + targa_header.pixel_size = f[16]; + targa_header.attributes = f[17]; + + if (image_width > 32768 || image_height > 32768 || image_width <= 0 || image_height <= 0) { Con_Print("LoadTGA: invalid size\n"); PrintTargaHeader(&targa_header); return NULL; } - if ((matchwidth && image_width != matchwidth) || (matchheight && image_height != matchheight)) - return NULL; - targa_header.pixel_size = f[16]; - targa_header.attributes = f[17]; // advance to end of header fin = f + 18; @@ -361,21 +450,17 @@ unsigned char *LoadTGA (const unsigned char *f, int filesize, int matchwidth, in { for (x = 0;x < targa_header.colormap_length;x++) { - palette[x*4+2] = *fin++; - palette[x*4+1] = *fin++; - palette[x*4+0] = *fin++; - palette[x*4+3] = 255; + bgra.b[0] = *fin++; + bgra.b[1] = *fin++; + bgra.b[2] = *fin++; + bgra.b[3] = 255; + palettei[x] = bgra.i; } } else if (targa_header.colormap_size == 32) { - for (x = 0;x < targa_header.colormap_length;x++) - { - palette[x*4+2] = *fin++; - palette[x*4+1] = *fin++; - palette[x*4+0] = *fin++; - palette[x*4+3] = *fin++; - } + memcpy(palettei, fin, targa_header.colormap_length*4); + fin += targa_header.colormap_length * 4; } else { @@ -400,10 +485,9 @@ unsigned char *LoadTGA (const unsigned char *f, int filesize, int matchwidth, in // set up a palette to make the loader easier for (x = 0;x < 256;x++) { - palette[x*4+2] = x; - palette[x*4+1] = x; - palette[x*4+0] = x; - palette[x*4+3] = 255; + bgra.b[0] = bgra.b[1] = bgra.b[2] = x; + bgra.b[3] = 255; + palettei[x] = bgra.i; } // fall through to colormap case case 1: @@ -434,8 +518,8 @@ unsigned char *LoadTGA (const unsigned char *f, int filesize, int matchwidth, in return NULL; } - image_rgba = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4); - if (!image_rgba) + image_buffer = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4); + if (!image_buffer) { Con_Printf("LoadTGA: not enough memory for %i by %i image\n", image_width, image_height); return NULL; @@ -444,38 +528,29 @@ unsigned char *LoadTGA (const unsigned char *f, int filesize, int matchwidth, in // If bit 5 of attributes isn't set, the image has been stored from bottom to top if ((targa_header.attributes & 0x20) == 0) { - pixbuf = image_rgba + (image_height - 1)*image_width*4; - row_inc = -image_width*4*2; + pixbufi = (unsigned int*)image_buffer + (image_height - 1)*image_width; + row_inci = -image_width*2; } else { - pixbuf = image_rgba; - row_inc = 0; + pixbufi = (unsigned int*)image_buffer; + row_inci = 0; } x = 0; y = 0; - red = green = blue = alpha = 255; pix_inc = 1; if ((targa_header.image_type & ~8) == 2) - pix_inc = targa_header.pixel_size / 8; + pix_inc = (targa_header.pixel_size + 7) / 8; switch (targa_header.image_type) { case 1: // colormapped, uncompressed case 3: // greyscale, uncompressed if (fin + image_width * image_height * pix_inc > enddata) break; - for (y = 0;y < image_height;y++, pixbuf += row_inc) - { + for (y = 0;y < image_height;y++, pixbufi += row_inci) for (x = 0;x < image_width;x++) - { - p = palette + *fin++ * 4; - *pixbuf++ = p[0]; - *pixbuf++ = p[1]; - *pixbuf++ = p[2]; - *pixbuf++ = p[3]; - } - } + *pixbufi++ = palettei[*fin++]; break; case 2: // BGR or BGRA, uncompressed @@ -483,34 +558,27 @@ unsigned char *LoadTGA (const unsigned char *f, int filesize, int matchwidth, in break; if (targa_header.pixel_size == 32 && alphabits) { - for (y = 0;y < image_height;y++, pixbuf += row_inc) - { - for (x = 0;x < image_width;x++, fin += pix_inc) - { - *pixbuf++ = fin[2]; - *pixbuf++ = fin[1]; - *pixbuf++ = fin[0]; - *pixbuf++ = fin[3]; - } - } + for (y = 0;y < image_height;y++) + memcpy(pixbufi + y * (image_width + row_inci), fin + y * image_width * pix_inc, image_width*4); } else { - for (y = 0;y < image_height;y++, pixbuf += row_inc) + for (y = 0;y < image_height;y++, pixbufi += row_inci) { for (x = 0;x < image_width;x++, fin += pix_inc) { - *pixbuf++ = fin[2]; - *pixbuf++ = fin[1]; - *pixbuf++ = fin[0]; - *pixbuf++ = 255; + bgra.b[0] = fin[0]; + bgra.b[1] = fin[1]; + bgra.b[2] = fin[2]; + bgra.b[3] = 255; + *pixbufi++ = bgra.i; } } } break; case 9: // colormapped, RLE case 11: // greyscale, RLE - for (y = 0;y < image_height;y++, pixbuf += row_inc) + for (y = 0;y < image_height;y++, pixbufi += row_inci) { for (x = 0;x < image_width;) { @@ -525,18 +593,9 @@ unsigned char *LoadTGA (const unsigned char *f, int filesize, int matchwidth, in break; // error - truncated file if (x + runlen > image_width) break; // error - line exceeds width - p = palette + *fin++ * 4; - red = p[0]; - green = p[1]; - blue = p[2]; - alpha = p[3]; + bgra.i = palettei[*fin++]; for (;runlen--;x++) - { - *pixbuf++ = red; - *pixbuf++ = green; - *pixbuf++ = blue; - *pixbuf++ = alpha; - } + *pixbufi++ = bgra.i; } else { @@ -547,22 +606,23 @@ unsigned char *LoadTGA (const unsigned char *f, int filesize, int matchwidth, in if (x + runlen > image_width) break; // error - line exceeds width for (;runlen--;x++) - { - p = palette + *fin++ * 4; - *pixbuf++ = p[0]; - *pixbuf++ = p[1]; - *pixbuf++ = p[2]; - *pixbuf++ = p[3]; - } + *pixbufi++ = palettei[*fin++]; } } + + if (x != image_width) + { + // pixbufi is useless now + Con_Printf("LoadTGA: corrupt file\n"); + break; + } } break; case 10: // BGR or BGRA, RLE if (targa_header.pixel_size == 32 && alphabits) { - for (y = 0;y < image_height;y++, pixbuf += row_inc) + for (y = 0;y < image_height;y++, pixbufi += row_inci) { for (x = 0;x < image_width;) { @@ -577,18 +637,13 @@ unsigned char *LoadTGA (const unsigned char *f, int filesize, int matchwidth, in break; // error - truncated file if (x + runlen > image_width) break; // error - line exceeds width - red = fin[2]; - green = fin[1]; - blue = fin[0]; - alpha = fin[3]; + bgra.b[0] = fin[0]; + bgra.b[1] = fin[1]; + bgra.b[2] = fin[2]; + bgra.b[3] = fin[3]; fin += pix_inc; for (;runlen--;x++) - { - *pixbuf++ = red; - *pixbuf++ = green; - *pixbuf++ = blue; - *pixbuf++ = alpha; - } + *pixbufi++ = bgra.i; } else { @@ -598,20 +653,29 @@ unsigned char *LoadTGA (const unsigned char *f, int filesize, int matchwidth, in break; // error - truncated file if (x + runlen > image_width) break; // error - line exceeds width - for (;runlen--;x++, fin += pix_inc) + for (;runlen--;x++) { - *pixbuf++ = fin[2]; - *pixbuf++ = fin[1]; - *pixbuf++ = fin[0]; - *pixbuf++ = fin[3]; + bgra.b[0] = fin[0]; + bgra.b[1] = fin[1]; + bgra.b[2] = fin[2]; + bgra.b[3] = fin[3]; + fin += pix_inc; + *pixbufi++ = bgra.i; } } } + + if (x != image_width) + { + // pixbufi is useless now + Con_Printf("LoadTGA: corrupt file\n"); + break; + } } } else { - for (y = 0;y < image_height;y++, pixbuf += row_inc) + for (y = 0;y < image_height;y++, pixbufi += row_inci) { for (x = 0;x < image_width;) { @@ -626,18 +690,13 @@ unsigned char *LoadTGA (const unsigned char *f, int filesize, int matchwidth, in break; // error - truncated file if (x + runlen > image_width) break; // error - line exceeds width - red = fin[2]; - green = fin[1]; - blue = fin[0]; - alpha = 255; + bgra.b[0] = fin[0]; + bgra.b[1] = fin[1]; + bgra.b[2] = fin[2]; + bgra.b[3] = 255; fin += pix_inc; for (;runlen--;x++) - { - *pixbuf++ = red; - *pixbuf++ = green; - *pixbuf++ = blue; - *pixbuf++ = alpha; - } + *pixbufi++ = bgra.i; } else { @@ -647,15 +706,24 @@ unsigned char *LoadTGA (const unsigned char *f, int filesize, int matchwidth, in break; // error - truncated file if (x + runlen > image_width) break; // error - line exceeds width - for (;runlen--;x++, fin += pix_inc) + for (;runlen--;x++) { - *pixbuf++ = fin[2]; - *pixbuf++ = fin[1]; - *pixbuf++ = fin[0]; - *pixbuf++ = 255; + bgra.b[0] = fin[0]; + bgra.b[1] = fin[1]; + bgra.b[2] = fin[2]; + bgra.b[3] = 255; + fin += pix_inc; + *pixbufi++ = bgra.i; } } } + + if (x != image_width) + { + // pixbufi is useless now + Con_Printf("LoadTGA: corrupt file\n"); + break; + } } } break; @@ -664,55 +732,9 @@ unsigned char *LoadTGA (const unsigned char *f, int filesize, int matchwidth, in break; } - return image_rgba; -} - -/* -============ -LoadLMP -============ -*/ -unsigned char *LoadLMP (const unsigned char *f, int filesize, int matchwidth, int matchheight, qboolean loadAs8Bit) -{ - unsigned char *image_buffer; - - if (filesize < 9) - { - Con_Print("LoadLMP: invalid LMP file\n"); - return NULL; - } - - // parse the very complicated header *chuckle* - image_width = BuffLittleLong(f); - image_height = BuffLittleLong(f + 4); - if (image_width > 4096 || image_height > 4096 || image_width <= 0 || image_height <= 0) - { - Con_Printf("LoadLMP: invalid size %ix%i\n", image_width, image_height); - return NULL; - } - if ((matchwidth && image_width != matchwidth) || (matchheight && image_height != matchheight)) - return NULL; - - if (filesize < (8 + image_width * image_height)) - { - Con_Print("LoadLMP: invalid LMP file\n"); - return NULL; - } - - if (loadAs8Bit) - { - image_buffer = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height); - memcpy(image_buffer, f + 8, image_width * image_height); - } - else - { - image_buffer = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4); - Image_Copy8bitRGBA(f + 8, image_buffer, image_width * image_height, palette_transparent); - } return image_buffer; } - typedef struct q2wal_s { char name[32]; @@ -724,9 +746,9 @@ typedef struct q2wal_s int value; } q2wal_t; -unsigned char *LoadWAL (const unsigned char *f, int filesize, int matchwidth, int matchheight) +unsigned char *LoadWAL_BGRA (const unsigned char *f, int filesize, int *miplevel) { - unsigned char *image_rgba; + unsigned char *image_buffer; const q2wal_t *inwal = (const q2wal_t *)f; if (filesize < (int) sizeof(q2wal_t)) @@ -737,13 +759,11 @@ unsigned char *LoadWAL (const unsigned char *f, int filesize, int matchwidth, in image_width = LittleLong(inwal->width); image_height = LittleLong(inwal->height); - if (image_width > 4096 || image_height > 4096 || image_width <= 0 || image_height <= 0) + if (image_width > 32768 || image_height > 32768 || image_width <= 0 || image_height <= 0) { Con_Printf("LoadWAL: invalid size %ix%i\n", image_width, image_height); return NULL; } - if ((matchwidth && image_width != matchwidth) || (matchheight && image_height != matchheight)) - return NULL; if (filesize < (int) sizeof(q2wal_t) + (int) LittleLong(inwal->offsets[0]) + image_width * image_height) { @@ -751,14 +771,14 @@ unsigned char *LoadWAL (const unsigned char *f, int filesize, int matchwidth, in return NULL; } - image_rgba = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4); - if (!image_rgba) + image_buffer = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4); + if (!image_buffer) { - Con_Printf("LoadLMP: not enough memory for %i by %i image\n", image_width, image_height); + Con_Printf("LoadWAL: not enough memory for %i by %i image\n", image_width, image_height); return NULL; } - Image_Copy8bitRGBA(f + LittleLong(inwal->offsets[0]), image_rgba, image_width * image_height, palette_complete); - return image_rgba; + Image_Copy8bitBGRA(f + LittleLong(inwal->offsets[0]), image_buffer, image_width * image_height, palette_bgra_complete); + return image_buffer; } @@ -776,75 +796,111 @@ void Image_StripImageExtension (const char *in, char *out, size_t size_out) strlcpy(out, in, size_out); } +static unsigned char image_linearfromsrgb[256]; + +void Image_MakeLinearColorsFromsRGB(unsigned char *pout, const unsigned char *pin, int numpixels) +{ + int i; + // this math from http://www.opengl.org/registry/specs/EXT/texture_sRGB.txt + if (!image_linearfromsrgb[255]) + for (i = 0;i < 256;i++) + image_linearfromsrgb[i] = (unsigned char)(Image_LinearFloatFromsRGB(i) * 256.0f); + for (i = 0;i < numpixels;i++) + { + pout[i*4+0] = image_linearfromsrgb[pin[i*4+0]]; + pout[i*4+1] = image_linearfromsrgb[pin[i*4+1]]; + pout[i*4+2] = image_linearfromsrgb[pin[i*4+2]]; + pout[i*4+3] = pin[i*4+3]; + } +} + typedef struct imageformat_s { const char *formatstring; - unsigned char *(*loadfunc)(const unsigned char *f, int filesize, int matchwidth, int matchheight); + unsigned char *(*loadfunc)(const unsigned char *f, int filesize, int *miplevel); } imageformat_t; // GAME_TENEBRAE only imageformat_t imageformats_tenebrae[] = { - {"override/%s.tga", LoadTGA}, - {"override/%s.png", PNG_LoadImage}, - {"override/%s.jpg", JPEG_LoadImage}, - {"override/%s.pcx", LoadPCX}, + {"override/%s.tga", LoadTGA_BGRA}, + {"override/%s.png", PNG_LoadImage_BGRA}, + {"override/%s.jpg", JPEG_LoadImage_BGRA}, + {"override/%s.pcx", LoadPCX_BGRA}, + {"%s.tga", LoadTGA_BGRA}, + {"%s.png", PNG_LoadImage_BGRA}, + {"%s.jpg", JPEG_LoadImage_BGRA}, + {"%s.pcx", LoadPCX_BGRA}, {NULL, NULL} }; imageformat_t imageformats_nopath[] = { - {"override/%s.tga", LoadTGA}, - {"override/%s.png", PNG_LoadImage}, - {"override/%s.jpg", JPEG_LoadImage}, - {"textures/%s.tga", LoadTGA}, - {"textures/%s.png", PNG_LoadImage}, - {"textures/%s.jpg", JPEG_LoadImage}, - {"%s.tga", LoadTGA}, - {"%s.png", PNG_LoadImage}, - {"%s.jpg", JPEG_LoadImage}, - {"%s.pcx", LoadPCX}, + {"override/%s.tga", LoadTGA_BGRA}, + {"override/%s.png", PNG_LoadImage_BGRA}, + {"override/%s.jpg", JPEG_LoadImage_BGRA}, + {"textures/%s.tga", LoadTGA_BGRA}, + {"textures/%s.png", PNG_LoadImage_BGRA}, + {"textures/%s.jpg", JPEG_LoadImage_BGRA}, + {"%s.tga", LoadTGA_BGRA}, + {"%s.png", PNG_LoadImage_BGRA}, + {"%s.jpg", JPEG_LoadImage_BGRA}, + {"%s.pcx", LoadPCX_BGRA}, + {NULL, NULL} +}; + +// GAME_DELUXEQUAKE only +// VorteX: the point why i use such messy texture paths is +// that GtkRadiant can't detect normal/gloss textures +// and exclude them from texture browser +// so i just use additional folder to store this textures +imageformat_t imageformats_dq[] = +{ + {"%s.tga", LoadTGA_BGRA}, + {"%s.jpg", JPEG_LoadImage_BGRA}, + {"texturemaps/%s.tga", LoadTGA_BGRA}, + {"texturemaps/%s.jpg", JPEG_LoadImage_BGRA}, {NULL, NULL} }; imageformat_t imageformats_textures[] = { - {"%s.tga", LoadTGA}, - {"%s.png", PNG_LoadImage}, - {"%s.jpg", JPEG_LoadImage}, - {"%s.pcx", LoadPCX}, - {"%s.wal", LoadWAL}, + {"%s.tga", LoadTGA_BGRA}, + {"%s.png", PNG_LoadImage_BGRA}, + {"%s.jpg", JPEG_LoadImage_BGRA}, + {"%s.pcx", LoadPCX_BGRA}, + {"%s.wal", LoadWAL_BGRA}, {NULL, NULL} }; imageformat_t imageformats_gfx[] = { - {"%s.tga", LoadTGA}, - {"%s.png", PNG_LoadImage}, - {"%s.jpg", JPEG_LoadImage}, - {"%s.pcx", LoadPCX}, + {"%s.tga", LoadTGA_BGRA}, + {"%s.png", PNG_LoadImage_BGRA}, + {"%s.jpg", JPEG_LoadImage_BGRA}, + {"%s.pcx", LoadPCX_BGRA}, {NULL, NULL} }; imageformat_t imageformats_other[] = { - {"%s.tga", LoadTGA}, - {"%s.png", PNG_LoadImage}, - {"%s.jpg", JPEG_LoadImage}, - {"%s.pcx", LoadPCX}, + {"%s.tga", LoadTGA_BGRA}, + {"%s.png", PNG_LoadImage_BGRA}, + {"%s.jpg", JPEG_LoadImage_BGRA}, + {"%s.pcx", LoadPCX_BGRA}, {NULL, NULL} }; int fixtransparentpixels(unsigned char *data, int w, int h); -unsigned char *loadimagepixels (const char *filename, qboolean complain, int matchwidth, int matchheight, qboolean allowFixtrans) +unsigned char *loadimagepixelsbgra (const char *filename, qboolean complain, qboolean allowFixtrans, qboolean convertsRGB, int *miplevel) { fs_offset_t filesize; imageformat_t *firstformat, *format; - unsigned char *f, *data = NULL; - char basename[MAX_QPATH], name[MAX_QPATH], *c; - if (developer_memorydebug.integer) - Mem_CheckSentinelsGlobal(); + unsigned char *f, *data = NULL, *data2 = NULL; + char basename[MAX_QPATH], name[MAX_QPATH], name2[MAX_QPATH], *c; + //if (developer_memorydebug.integer) + // Mem_CheckSentinelsGlobal(); if (developer_texturelogging.integer) Log_Printf("textures.log", "%s\n", filename); Image_StripImageExtension(filename, basename, sizeof(basename)); // strip filename extensions to allow replacement by other types @@ -862,6 +918,8 @@ unsigned char *loadimagepixels (const char *filename, qboolean complain, int mat } if (gamemode == GAME_TENEBRAE) firstformat = imageformats_tenebrae; + else if (gamemode == GAME_DELUXEQUAKE) + firstformat = imageformats_dq; else if (!strcasecmp(name, "textures")) firstformat = imageformats_textures; else if (!strcasecmp(name, "gfx")) @@ -873,18 +931,36 @@ unsigned char *loadimagepixels (const char *filename, qboolean complain, int mat // now try all the formats in the selected list for (format = firstformat;format->formatstring;format++) { - sprintf (name, format->formatstring, basename); + dpsnprintf (name, sizeof(name), format->formatstring, basename); f = FS_LoadFile(name, tempmempool, true, &filesize); if (f) { - data = format->loadfunc(f, filesize, matchwidth, matchheight); + int mymiplevel = miplevel ? *miplevel : 0; + data = format->loadfunc(f, (int)filesize, &mymiplevel); Mem_Free(f); if (data) { - if (developer.integer >= 10) - Con_Printf("loaded image %s (%dx%d)\n", name, image_width, image_height); - if (developer_memorydebug.integer) - Mem_CheckSentinelsGlobal(); + if(format->loadfunc == JPEG_LoadImage_BGRA) // jpeg can't do alpha, so let's simulate it by loading another jpeg + { + dpsnprintf (name2, sizeof(name2), format->formatstring, va("%s_alpha", basename)); + f = FS_LoadFile(name2, tempmempool, true, &filesize); + if(f) + { + int mymiplevel2 = miplevel ? *miplevel : 0; + data2 = format->loadfunc(f, (int)filesize, &mymiplevel2); + if(mymiplevel != mymiplevel2) + Host_Error("loadimagepixelsbgra: miplevels differ"); + Mem_Free(f); + Image_CopyAlphaFromBlueBGRA(data, data2, image_width, image_height); + Mem_Free(data2); + } + } + if (developer_loading.integer) + Con_DPrintf("loaded image %s (%dx%d)\n", name, image_width, image_height); + if(miplevel) + *miplevel = mymiplevel; + //if (developer_memorydebug.integer) + // Mem_CheckSentinelsGlobal(); if(allowFixtrans && r_fixtrans_auto.integer) { int n = fixtransparentpixels(data, image_width, image_height); @@ -896,18 +972,17 @@ unsigned char *loadimagepixels (const char *filename, qboolean complain, int mat char outfilename[MAX_QPATH], buf[MAX_QPATH]; Image_StripImageExtension(name, buf, sizeof(buf)); dpsnprintf(outfilename, sizeof(outfilename), "fixtrans/%s.tga", buf); - Image_WriteTGARGBA(outfilename, image_width, image_height, data); + Image_WriteTGABGRA(outfilename, image_width, image_height, data); Con_Printf("- %s written.\n", outfilename); } } } + if (convertsRGB) + Image_MakeLinearColorsFromsRGB(data, data, image_width * image_height); return data; } else - { - if (developer.integer >= 1) - Con_DPrintf("Error loading image %s (file loaded but decode failed)\n", name); - } + Con_DPrintf("Error loading image %s (file loaded but decode failed)\n", name); } } if (complain) @@ -915,22 +990,28 @@ unsigned char *loadimagepixels (const char *filename, qboolean complain, int mat Con_Printf("Couldn't load %s using ", filename); for (format = firstformat;format->formatstring;format++) { - sprintf (name, format->formatstring, basename); + dpsnprintf (name, sizeof(name), format->formatstring, basename); Con_Printf(format == firstformat ? "\"%s\"" : (format[1].formatstring ? ", \"%s\"" : " or \"%s\".\n"), format->formatstring); } } - if (developer_memorydebug.integer) - Mem_CheckSentinelsGlobal(); + + // texture loading can take a while, so make sure we're sending keepalives + CL_KeepaliveMessage(false); + + //if (developer_memorydebug.integer) + // Mem_CheckSentinelsGlobal(); return NULL; } -rtexture_t *loadtextureimage (rtexturepool_t *pool, const char *filename, int matchwidth, int matchheight, qboolean complain, int flags, qboolean allowFixtrans) +extern cvar_t gl_picmip; +rtexture_t *loadtextureimage (rtexturepool_t *pool, const char *filename, qboolean complain, int flags, qboolean allowFixtrans, qboolean sRGB) { unsigned char *data; rtexture_t *rt; - if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight, allowFixtrans))) + int miplevel = R_PicmipForFlags(flags); + if (!(data = loadimagepixelsbgra (filename, complain, allowFixtrans, false, &miplevel))) return 0; - rt = R_LoadTexture2D(pool, filename, image_width, image_height, data, TEXTYPE_RGBA, flags, NULL); + rt = R_LoadTexture2D(pool, filename, image_width, image_height, data, sRGB ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, flags, miplevel, NULL); Mem_Free(data); return rt; } @@ -943,7 +1024,7 @@ int fixtransparentpixels(unsigned char *data, int w, int h) int const FIXTRANS_HAS_U = 8; int const FIXTRANS_HAS_D = 16; int const FIXTRANS_FIXED = 32; - unsigned char *fixMask = Mem_Alloc(tempmempool, w * h); + unsigned char *fixMask = (unsigned char *) Mem_Alloc(tempmempool, w * h); int fixPixels = 0; int changedPixels = 0; int x, y; @@ -983,41 +1064,41 @@ int fixtransparentpixels(unsigned char *data, int w, int h) unsigned char r, g, b, a, r0, g0, b0; if(fixMask[FIXTRANS_PIXEL] & FIXTRANS_HAS_U) { - r = data[FIXTRANS_PIXEL_U * 4 + 0]; + r = data[FIXTRANS_PIXEL_U * 4 + 2]; g = data[FIXTRANS_PIXEL_U * 4 + 1]; - b = data[FIXTRANS_PIXEL_U * 4 + 2]; + b = data[FIXTRANS_PIXEL_U * 4 + 0]; a = data[FIXTRANS_PIXEL_U * 4 + 3]; sumR += r; sumG += g; sumB += b; sumA += a; sumRA += r*a; sumGA += g*a; sumBA += b*a; ++cnt; } if(fixMask[FIXTRANS_PIXEL] & FIXTRANS_HAS_D) { - r = data[FIXTRANS_PIXEL_D * 4 + 0]; + r = data[FIXTRANS_PIXEL_D * 4 + 2]; g = data[FIXTRANS_PIXEL_D * 4 + 1]; - b = data[FIXTRANS_PIXEL_D * 4 + 2]; + b = data[FIXTRANS_PIXEL_D * 4 + 0]; a = data[FIXTRANS_PIXEL_D * 4 + 3]; sumR += r; sumG += g; sumB += b; sumA += a; sumRA += r*a; sumGA += g*a; sumBA += b*a; ++cnt; } if(fixMask[FIXTRANS_PIXEL] & FIXTRANS_HAS_L) { - r = data[FIXTRANS_PIXEL_L * 4 + 0]; + r = data[FIXTRANS_PIXEL_L * 4 + 2]; g = data[FIXTRANS_PIXEL_L * 4 + 1]; - b = data[FIXTRANS_PIXEL_L * 4 + 2]; + b = data[FIXTRANS_PIXEL_L * 4 + 0]; a = data[FIXTRANS_PIXEL_L * 4 + 3]; sumR += r; sumG += g; sumB += b; sumA += a; sumRA += r*a; sumGA += g*a; sumBA += b*a; ++cnt; } if(fixMask[FIXTRANS_PIXEL] & FIXTRANS_HAS_R) { - r = data[FIXTRANS_PIXEL_R * 4 + 0]; + r = data[FIXTRANS_PIXEL_R * 4 + 2]; g = data[FIXTRANS_PIXEL_R * 4 + 1]; - b = data[FIXTRANS_PIXEL_R * 4 + 2]; + b = data[FIXTRANS_PIXEL_R * 4 + 0]; a = data[FIXTRANS_PIXEL_R * 4 + 3]; sumR += r; sumG += g; sumB += b; sumA += a; sumRA += r*a; sumGA += g*a; sumBA += b*a; ++cnt; } if(!cnt) continue; - r0 = data[FIXTRANS_PIXEL * 4 + 0]; + r0 = data[FIXTRANS_PIXEL * 4 + 2]; g0 = data[FIXTRANS_PIXEL * 4 + 1]; - b0 = data[FIXTRANS_PIXEL * 4 + 2]; + b0 = data[FIXTRANS_PIXEL * 4 + 0]; if(sumA) { // there is a surrounding non-alpha pixel @@ -1034,9 +1115,9 @@ int fixtransparentpixels(unsigned char *data, int w, int h) } if(r != r0 || g != g0 || b != b0) ++changedPixels; - data[FIXTRANS_PIXEL * 4 + 0] = r; + data[FIXTRANS_PIXEL * 4 + 2] = r; data[FIXTRANS_PIXEL * 4 + 1] = g; - data[FIXTRANS_PIXEL * 4 + 2] = b; + data[FIXTRANS_PIXEL * 4 + 0] = b; fixMask[FIXTRANS_PIXEL] |= FIXTRANS_FIXED; } for(y = 0; y < h; ++y) @@ -1076,24 +1157,26 @@ void Image_FixTransparentPixels_f(void) Con_Printf("Processing %s... ", filename); Image_StripImageExtension(filename, buf, sizeof(buf)); dpsnprintf(outfilename, sizeof(outfilename), "fixtrans/%s.tga", buf); - if(!(data = loadimagepixels(filename, true, 0, 0, false))) + if(!(data = loadimagepixelsbgra(filename, true, false, false, NULL))) return; if((n = fixtransparentpixels(data, image_width, image_height))) { - Image_WriteTGARGBA(outfilename, image_width, image_height, data); + Image_WriteTGABGRA(outfilename, image_width, image_height, data); Con_Printf("%s written (%d pixels changed).\n", outfilename, n); } else Con_Printf("unchanged.\n"); Mem_Free(data); } + FS_FreeSearch(search); } -qboolean Image_WriteTGARGB_preflipped (const char *filename, int width, int height, const unsigned char *data, unsigned char *buffer) +qboolean Image_WriteTGABGR_preflipped (const char *filename, int width, int height, const unsigned char *data) { qboolean ret; - unsigned char *out; - const unsigned char *in, *end; + unsigned char buffer[18]; + const void *buffers[2]; + fs_offset_t sizes[2]; memset (buffer, 0, 18); buffer[2] = 2; // uncompressed type @@ -1103,26 +1186,21 @@ qboolean Image_WriteTGARGB_preflipped (const char *filename, int width, int heig buffer[15] = (height >> 8) & 0xFF; buffer[16] = 24; // pixel size - // swap rgb to bgr - in = data; - out = buffer + 18; - end = in + width*height*3; - for (;in < end;in += 3) - { - *out++ = in[2]; - *out++ = in[1]; - *out++ = in[0]; - } - ret = FS_WriteFile (filename, buffer, width*height*3 + 18 ); + buffers[0] = buffer; + sizes[0] = 18; + buffers[1] = data; + sizes[1] = width*height*3; + ret = FS_WriteFileInBlocks(filename, buffers, sizes, 2); return ret; } -void Image_WriteTGARGBA (const char *filename, int width, int height, const unsigned char *data) +qboolean Image_WriteTGABGRA (const char *filename, int width, int height, const unsigned char *data) { int y; unsigned char *buffer, *out; const unsigned char *in, *end; + qboolean ret; buffer = (unsigned char *)Mem_Alloc(tempmempool, width*height*4 + 18); @@ -1143,21 +1221,13 @@ void Image_WriteTGARGBA (const char *filename, int width, int height, const unsi buffer[16] = 32; // pixel size buffer[17] = 8; // 8 bits of alpha - // swap rgba to bgra and flip upside down + // flip upside down out = buffer + 18; for (y = height - 1;y >= 0;y--) { - in = data + y * width * 4; - end = in + width * 4; - for (;in < end;in += 4) - { - *out++ = in[2]; - *out++ = in[1]; - *out++ = in[0]; - *out++ = in[3]; - } + memcpy(out, data + y * width * 4, width * 4); + out += width*4; } - FS_WriteFile (filename, buffer, width*height*4 + 18 ); } else { @@ -1165,7 +1235,7 @@ void Image_WriteTGARGBA (const char *filename, int width, int height, const unsi buffer[16] = 24; // pixel size buffer[17] = 0; // 8 bits of alpha - // swap rgba to bgr and flip upside down + // truncate bgra to bgr and flip upside down out = buffer + 18; for (y = height - 1;y >= 0;y--) { @@ -1173,15 +1243,17 @@ void Image_WriteTGARGBA (const char *filename, int width, int height, const unsi end = in + width * 4; for (;in < end;in += 4) { - *out++ = in[2]; - *out++ = in[1]; *out++ = in[0]; + *out++ = in[1]; + *out++ = in[2]; } } - FS_WriteFile (filename, buffer, width*height*3 + 18 ); } + ret = FS_WriteFile (filename, buffer, out - buffer); Mem_Free(buffer); + + return ret; } static void Image_Resample32LerpLine (const unsigned char *in, unsigned char *out, int inwidth, int outwidth) @@ -1456,15 +1528,13 @@ void Image_MipReduce32(const unsigned char *in, unsigned char *out, int *width, } } -void Image_HeightmapToNormalmap(const unsigned char *inpixels, unsigned char *outpixels, int width, int height, int clamp, float bumpscale) +void Image_HeightmapToNormalmap_BGRA(const unsigned char *inpixels, unsigned char *outpixels, int width, int height, int clamp, float bumpscale) { int x, y, x1, x2, y1, y2; const unsigned char *b, *row[3]; int p[5]; unsigned char *out; - float iwidth, iheight, ibumpscale, n[3]; - iwidth = 1.0f / width; - iheight = 1.0f / height; + float ibumpscale, n[3]; ibumpscale = (255.0f * 6.0f) / bumpscale; out = outpixels; for (y = 0, y1 = height-1;y < height;y1 = y, y++) @@ -1490,9 +1560,9 @@ void Image_HeightmapToNormalmap(const unsigned char *inpixels, unsigned char *ou n[2] = ibumpscale; VectorNormalize(n); // turn it into a dot3 rgb vector texture - out[0] = (int)(128.0f + n[0] * 127.0f); + out[2] = (int)(128.0f + n[0] * 127.0f); out[1] = (int)(128.0f + n[1] * 127.0f); - out[2] = (int)(128.0f + n[2] * 127.0f); + out[0] = (int)(128.0f + n[2] * 127.0f); out[3] = (p[4]) / 3; out += 4; }