X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=image.c;h=23b69ba7781640ad0a6c0d5313ae0a814b1af5a2;hb=8d2e156ec85d83e0db23cc7d13939b231ddfdb52;hp=a32d58a40599b498b156a612e48136450fd01aaf;hpb=8dcce44300385b12c46d494c06aadcfa35a8bc14;p=xonotic%2Fdarkplaces.git diff --git a/image.c b/image.c index a32d58a4..23b69ba7 100644 --- a/image.c +++ b/image.c @@ -4,6 +4,58 @@ int image_width; int image_height; +// note: pal must be 32bit color +void Image_Copy8bitRGBA(byte *in, byte *out, int pixels, int *pal) +{ + int *iout = (void *)out; + while (pixels >= 8) + { + iout[0] = pal[in[0]]; + iout[1] = pal[in[1]]; + iout[2] = pal[in[2]]; + iout[3] = pal[in[3]]; + iout[4] = pal[in[4]]; + iout[5] = pal[in[5]]; + iout[6] = pal[in[6]]; + iout[7] = pal[in[7]]; + in += 8; + iout += 8; + pixels -= 8; + } + if (pixels & 4) + { + iout[0] = pal[in[0]]; + iout[1] = pal[in[1]]; + iout[2] = pal[in[2]]; + iout[3] = pal[in[3]]; + in += 4; + iout += 4; + } + if (pixels & 2) + { + iout[0] = pal[in[0]]; + iout[1] = pal[in[1]]; + in += 2; + iout += 2; + } + if (pixels & 1) + iout[0] = pal[in[0]]; +} + +void Image_CopyRGBAGamma(byte *in, byte *out, int pixels) +{ + while (pixels--) + { + out[0] = texgamma[in[0]]; + out[1] = texgamma[in[1]]; + out[2] = texgamma[in[2]]; + out[3] = in[3] ; + in += 4; + out += 4; + } +} + + /* ================================================================= @@ -34,7 +86,7 @@ typedef struct LoadPCX ============ */ -byte* LoadPCX (FILE *f, int matchwidth, int matchheight) +byte* LoadPCX (QFile *f, int matchwidth, int matchheight) { pcx_t *pcx, pcxbuf; byte palette[768]; @@ -46,16 +98,21 @@ byte* LoadPCX (FILE *f, int matchwidth, int matchheight) // // parse the PCX file // - fread (&pcxbuf, 1, sizeof(pcxbuf), f); + Qread (f, &pcxbuf, sizeof(pcxbuf)); pcx = &pcxbuf; - if (pcx->manufacturer != 0x0a - || pcx->version != 5 - || pcx->encoding != 1 - || pcx->bits_per_pixel != 8 - || pcx->xmax >= 320 - || pcx->ymax >= 256) + // 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); + + if (pcx->manufacturer != 0x0a || pcx->version != 5 || pcx->encoding != 1 || pcx->bits_per_pixel != 8 || pcx->xmax > 320 || pcx->ymax > 256) { Con_Printf ("Bad pcx file\n"); return NULL; @@ -67,40 +124,51 @@ byte* LoadPCX (FILE *f, int matchwidth, int matchheight) return NULL; // seek to palette - fseek (f, -768, SEEK_END); - fread (palette, 1, 768, f); + Qseek (f, -768, SEEK_END); + Qread (f, palette, 768); - fseek (f, sizeof(pcxbuf) - 4, SEEK_SET); + Qseek (f, sizeof(pcxbuf) - 4, SEEK_SET); count = (pcx->xmax+1) * (pcx->ymax+1); - image_rgba = malloc( count * 4); + image_rgba = qmalloc( count * 4); for (y=0 ; y<=pcx->ymax ; y++) { pix = image_rgba + 4*y*(pcx->xmax+1); for (x=0 ; x<=pcx->xmax ; ) { - dataByte = fgetc(f); + dataByte = Qgetc(f); if((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; - dataByte = fgetc(f); + dataByte = Qgetc(f); + if (runLength) + { + x += runLength; + while(runLength--) + { + pix[0] = palette[dataByte*3]; + pix[1] = palette[dataByte*3+1]; + pix[2] = palette[dataByte*3+2]; + pix[3] = 255; + pix += 4; + } + } } else - runLength = 1; - - while(runLength-- > 0) { + x++; pix[0] = palette[dataByte*3]; pix[1] = palette[dataByte*3+1]; pix[2] = palette[dataByte*3+2]; pix[3] = 255; pix += 4; - x++; } + } } + Qclose(f); image_width = pcx->xmax+1; image_height = pcx->ymax+1; return image_rgba; @@ -125,24 +193,24 @@ typedef struct _TargaHeader { TargaHeader targa_header; -int fgetLittleShort (FILE *f) +int fgetLittleShort (QFile *f) { byte b1, b2; - b1 = fgetc(f); - b2 = fgetc(f); + b1 = Qgetc(f); + b2 = Qgetc(f); return (short)(b1 + b2*256); } -int fgetLittleLong (FILE *f) +int fgetLittleLong (QFile *f) { byte b1, b2, b3, b4; - b1 = fgetc(f); - b2 = fgetc(f); - b3 = fgetc(f); - b4 = fgetc(f); + b1 = Qgetc(f); + b2 = Qgetc(f); + b3 = Qgetc(f); + b4 = Qgetc(f); return b1 + (b2<<8) + (b3<<16) + (b4<<24); } @@ -153,20 +221,20 @@ int fgetLittleLong (FILE *f) LoadTGA ============= */ -byte* LoadTGA (FILE *fin, int matchwidth, int matchheight) +byte* LoadTGA (QFile *fin, int matchwidth, int matchheight) { int columns, rows, numPixels; byte *pixbuf; int row, column; byte *image_rgba; - targa_header.id_length = fgetc(fin); - targa_header.colormap_type = fgetc(fin); - targa_header.image_type = fgetc(fin); + targa_header.id_length = Qgetc(fin); + targa_header.colormap_type = Qgetc(fin); + targa_header.image_type = Qgetc(fin); targa_header.colormap_index = fgetLittleShort(fin); targa_header.colormap_length = fgetLittleShort(fin); - targa_header.colormap_size = fgetc(fin); + targa_header.colormap_size = Qgetc(fin); targa_header.x_origin = fgetLittleShort(fin); targa_header.y_origin = fgetLittleShort(fin); targa_header.width = fgetLittleShort(fin); @@ -175,47 +243,47 @@ byte* LoadTGA (FILE *fin, int matchwidth, int matchheight) return NULL; if (matchheight && targa_header.height != matchheight) return NULL; - targa_header.pixel_size = fgetc(fin); - targa_header.attributes = fgetc(fin); + targa_header.pixel_size = Qgetc(fin); + targa_header.attributes = Qgetc(fin); if (targa_header.image_type!=2 && targa_header.image_type!=10) - Sys_Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n"); + Host_Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n"); if (targa_header.colormap_type !=0 || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24)) - Sys_Error ("Texture_LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n"); + Host_Error ("LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n"); columns = targa_header.width; rows = targa_header.height; numPixels = columns * rows; - image_rgba = malloc (numPixels*4); + image_rgba = qmalloc(numPixels*4); if (targa_header.id_length != 0) - fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment + Qseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment if (targa_header.image_type==2) { // Uncompressed, RGB images for(row=rows-1; row>=0; row--) { pixbuf = image_rgba + row*columns*4; for(column=0; column=0; row--) { pixbuf = image_rgba + row*columns*4; for(column=0; column 4096 || (unsigned) height > 4096) + Host_Error("LoadLMP: invalid size\n"); + if (matchwidth && width != matchwidth) + return NULL; + if (matchheight && height != matchheight) + return NULL; + + image_rgba = qmalloc(width*height*4); + Qread(f, image_rgba + width*height*3, width*height); + Qclose(f); + + Image_Copy8bitRGBA(image_rgba + width*height*3, image_rgba, width*height, d_8to24table); + image_width = width; + image_height = height; + return image_rgba; +} + +void Image_StripImageExtension (char *in, char *out) +{ + char *end, *temp; + end = in + strlen(in); + if ((end - in) >= 4) + { + temp = end - 4; + if (strcmp(temp, ".tga") == 0 || strcmp(temp, ".pcx") == 0 || strcmp(temp, ".lmp") == 0) + end = temp; + while (in < end) + *out++ = *in++; + *out++ = 0; + } + else + strcpy(out, in); +} + +byte* loadimagepixels (char* filename, qboolean complain, int matchwidth, int matchheight) +{ + QFile *f; + char basename[256], name[256]; + byte *c; + Image_StripImageExtension(filename, basename); // strip .tga, .pcx and .lmp extensions to allow replacement by other types + // replace *'s with #, so commandline utils don't get confused when dealing with the external files + for (c = basename;*c;c++) + if (*c == '*') + *c = '#'; sprintf (name, "textures/%s.tga", basename); - COM_FOpenFile (name, &f, true); + COM_FOpenFile (name, &f, true, true); if (f) return LoadTGA (f, matchwidth, matchheight); sprintf (name, "textures/%s.pcx", basename); - COM_FOpenFile (name, &f, true); + COM_FOpenFile (name, &f, true, true); if (f) return LoadPCX (f, matchwidth, matchheight); sprintf (name, "%s.tga", basename); - COM_FOpenFile (name, &f, true); + COM_FOpenFile (name, &f, true, true); if (f) return LoadTGA (f, matchwidth, matchheight); sprintf (name, "%s.pcx", basename); - COM_FOpenFile (name, &f, true); + COM_FOpenFile (name, &f, true, true); if (f) return LoadPCX (f, matchwidth, matchheight); - if (image_rgba = W_GetTexture(basename, matchwidth, matchheight)) - return image_rgba; + sprintf (name, "%s.lmp", basename); + COM_FOpenFile (name, &f, true, true); + if (f) + return LoadLMP (f, matchwidth, matchheight); if (complain) Con_Printf ("Couldn't load %s.tga or .pcx\n", filename); return NULL; } -int loadtextureimage (int texnum, char* filename, qboolean complain, int matchwidth, int matchheight) +int image_makemask (byte *in, byte *out, int size) +{ + int i, count; + count = 0; + for (i = 0;i < size;i++) + { + out[0] = out[1] = out[2] = 255; + out[3] = in[3]; + if (in[3] != 255) + count++; + in += 4; + out += 4; + } + return count; +} + +byte* loadimagepixelsmask (char* filename, qboolean complain, int matchwidth, int matchheight) +{ + byte *in, *data; + in = data = loadimagepixels(filename, complain, matchwidth, matchheight); + if (!data) + return NULL; + if (image_makemask(data, data, image_width * image_height)) + return data; // some transparency + else + { + qfree(data); + return NULL; // all opaque + } +} + +rtexture_t *loadtextureimage (char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache) { byte *data; + rtexture_t *rt; if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight))) return 0; - if (texnum >= 0) // specific texnum, not cached + rt = R_LoadTexture (filename, image_width, image_height, data, TEXF_ALPHA | TEXF_RGBA | (mipmap ? TEXF_MIPMAP : 0) | (mipmap ? TEXF_PRECACHE : 0)); + qfree(data); + return rt; +} + +rtexture_t *loadtextureimagemask (char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache) +{ + byte *data; + rtexture_t *rt; + if (!(data = loadimagepixelsmask (filename, complain, matchwidth, matchheight))) + return 0; + rt = R_LoadTexture (filename, image_width, image_height, data, TEXF_ALPHA | TEXF_RGBA | (mipmap ? TEXF_MIPMAP : 0) | (mipmap ? TEXF_PRECACHE : 0)); + qfree(data); + return rt; +} + +rtexture_t *image_masktex; +rtexture_t *loadtextureimagewithmask (char* filename, int matchwidth, int matchheight, qboolean complain, qboolean mipmap, qboolean precache) +{ + int count; + byte *data; + char *filename2; + rtexture_t *rt; + image_masktex = NULL; + if (!(data = loadimagepixels (filename, complain, matchwidth, matchheight))) + return 0; + rt = R_LoadTexture (filename, image_width, image_height, data, TEXF_ALPHA | TEXF_RGBA | (mipmap ? TEXF_MIPMAP : 0) | (mipmap ? TEXF_PRECACHE : 0)); + count = image_makemask(data, data, image_width * image_height); + if (count) + { + filename2 = qmalloc(strlen(filename) + 6); + sprintf(filename2, "%s_mask", filename); + image_masktex = R_LoadTexture (filename2, image_width, image_height, data, TEXF_ALPHA | TEXF_RGBA | (mipmap ? TEXF_MIPMAP : 0) | (mipmap ? TEXF_PRECACHE : 0)); + qfree(filename2); + } + qfree(data); + return rt; +} + +void Image_WriteTGARGB_preflipped (char *filename, int width, int height, byte *data) +{ + byte *buffer, *in, *out, *end; + + buffer = qmalloc(width*height*3 + 18); + + memset (buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = (width >> 0) & 0xFF; + buffer[13] = (width >> 8) & 0xFF; + buffer[14] = (height >> 0) & 0xFF; + 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]; + } + COM_WriteFile (filename, buffer, width*height*3 + 18 ); + + qfree(buffer); +} + +void Image_WriteTGARGB (char *filename, int width, int height, byte *data) +{ + int y; + byte *buffer, *in, *out, *end; + + buffer = qmalloc(width*height*3 + 18); + + memset (buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = (width >> 0) & 0xFF; + buffer[13] = (width >> 8) & 0xFF; + buffer[14] = (height >> 0) & 0xFF; + buffer[15] = (height >> 8) & 0xFF; + buffer[16] = 24; // pixel size + + // swap rgb to bgr and flip upside down + out = buffer + 18; + for (y = height - 1;y >= 0;y--) + { + in = data + y * width * 3; + end = in + width * 3; + for (;in < end;in += 3) + { + *out++ = in[2]; + *out++ = in[1]; + *out++ = in[0]; + } + } + COM_WriteFile (filename, buffer, width*height*3 + 18 ); + + qfree(buffer); +} + +void Image_WriteTGARGBA (char *filename, int width, int height, byte *data) +{ + int y; + byte *buffer, *in, *out, *end; + + buffer = qmalloc(width*height*4 + 18); + + memset (buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = (width >> 0) & 0xFF; + buffer[13] = (width >> 8) & 0xFF; + buffer[14] = (height >> 0) & 0xFF; + buffer[15] = (height >> 8) & 0xFF; + buffer[16] = 32; // pixel size + + // swap rgba to bgra and 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]; + } + } + COM_WriteFile (filename, buffer, width*height*4 + 18 ); + + qfree(buffer); +} + +qboolean Image_CheckAlpha(byte *data, int size, qboolean rgba) +{ + byte *end; + if (rgba) { - glBindTexture(GL_TEXTURE_2D, texnum); - GL_Upload32 (data, image_width, image_height, true, true); - free(data); - return texnum; + // check alpha bytes + for (end = data + size * 4, data += 3;data < end;data += 4) + if (*data < 255) + return 1; } - else // any texnum, cached + else { - texnum = GL_LoadTexture (filename, image_width, image_height, data, true, true, 4); - free(data); - return texnum; + // color 255 is transparent + for (end = data + size;data < end;data++) + if (*data == 255) + return 1; } + return 0; }