ref: c909a88a89b43d5555f3090c0a54de31b067fe7c
dir: /ref/r_image.c/
/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "r_local.h" #define MAX_RIMAGES 1024 image_t r_images[MAX_RIMAGES]; int numr_images; /* =============== R_ImageList_f =============== */ void R_ImageList_f (void) { int i; image_t *image; int texels; ri.Con_Printf (PRINT_ALL, "------------------\n"); texels = 0; for (i=0, image=r_images ; i<numr_images ; i++, image++) { if (image->registration_sequence <= 0) continue; texels += image->width*image->height; switch (image->type) { case it_skin: ri.Con_Printf (PRINT_ALL, "M"); break; case it_sprite: ri.Con_Printf (PRINT_ALL, "S"); break; case it_wall: ri.Con_Printf (PRINT_ALL, "W"); break; case it_pic: ri.Con_Printf (PRINT_ALL, "P"); break; default: ri.Con_Printf (PRINT_ALL, " "); break; } ri.Con_Printf (PRINT_ALL, " %3i %3i : %s\n", image->width, image->height, image->name); } ri.Con_Printf (PRINT_ALL, "Total texel count: %i\n", texels); } /* ================================================================= PCX LOADING ================================================================= */ /* ============== LoadPCX ============== */ void LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height) { byte *raw; pcx_t *pcx; int x, y; int len; int dataByte, runLength; byte *out, *pix; *pic = NULL; // // load the file // len = ri.FS_LoadFile (filename, (void **)&raw); if (!raw) { ri.Con_Printf (PRINT_DEVELOPER, "Bad pcx file %s\n", filename); return; } // // parse the PCX file // pcx = (pcx_t *)raw; pcx->xmin = LittleShort(pcx->xmin); pcx->ymin = LittleShort(pcx->ymin); pcx->xmax = LittleShort(pcx->xmax); pcx->ymax = LittleShort(pcx->ymax); 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); raw = &pcx->data; if (pcx->manufacturer != 0x0a || pcx->version != 5 || pcx->encoding != 1 || pcx->bits_per_pixel != 8 || pcx->xmax >= 640 || pcx->ymax >= 480) { ri.Con_Printf (PRINT_ALL, "Bad pcx file %s\n", filename); return; } out = malloc ( (pcx->ymax+1) * (pcx->xmax+1) ); *pic = out; pix = out; if (palette) { *palette = malloc(768); memcpy (*palette, (byte *)pcx + len - 768, 768); } if (width) *width = pcx->xmax+1; if (height) *height = pcx->ymax+1; for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1) { for (x=0 ; x<=pcx->xmax ; ) { dataByte = *raw++; if((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; dataByte = *raw++; } else runLength = 1; while(runLength-- > 0) pix[x++] = dataByte; } } if ( raw - (byte *)pcx > len) { ri.Con_Printf (PRINT_DEVELOPER, "PCX file %s was malformed", filename); free (*pic); *pic = NULL; } ri.FS_FreeFile (pcx); } /* ========================================================= TARGA LOADING ========================================================= */ typedef struct _TargaHeader { unsigned char id_length, colormap_type, image_type; unsigned short colormap_index, colormap_length; unsigned char colormap_size; unsigned short x_origin, y_origin, width, height; unsigned char pixel_size, attributes; } TargaHeader; /* ============= LoadTGA ============= */ void LoadTGA (char *name, byte **pic, int *width, int *height) { int columns, rows, numPixels; byte *pixbuf; int row, column; byte *buf_p; byte *buffer; int length; TargaHeader targa_header; byte *targa_rgba; *pic = NULL; // // load the file // length = ri.FS_LoadFile (name, (void **)&buffer); if (!buffer) { ri.Con_Printf (PRINT_DEVELOPER, "Bad tga file %s\n", name); return; } buf_p = buffer; targa_header.id_length = *buf_p++; targa_header.colormap_type = *buf_p++; targa_header.image_type = *buf_p++; targa_header.colormap_index = LittleShort ( *((short *)buf_p) ); buf_p+=2; targa_header.colormap_length = LittleShort ( *((short *)buf_p) ); buf_p+=2; targa_header.colormap_size = *buf_p++; targa_header.x_origin = LittleShort ( *((short *)buf_p) ); buf_p+=2; targa_header.y_origin = LittleShort ( *((short *)buf_p) ); buf_p+=2; targa_header.width = LittleShort ( *((short *)buf_p) ); buf_p+=2; targa_header.height = LittleShort ( *((short *)buf_p) ); buf_p+=2; targa_header.pixel_size = *buf_p++; targa_header.attributes = *buf_p++; if (targa_header.image_type!=2 && targa_header.image_type!=10) ri.Sys_Error (ERR_DROP, "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)) ri.Sys_Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n"); columns = targa_header.width; rows = targa_header.height; numPixels = columns * rows; if (width) *width = columns; if (height) *height = rows; targa_rgba = malloc (numPixels*4); *pic = targa_rgba; if (targa_header.id_length != 0) buf_p += targa_header.id_length; // skip TARGA image comment if (targa_header.image_type==2) { // Uncompressed, RGB images for(row=rows-1; row>=0; row--) { pixbuf = targa_rgba + row*columns*4; for(column=0; column<columns; column++) { unsigned char red,green,blue,alphabyte; switch (targa_header.pixel_size) { case 24: blue = *buf_p++; green = *buf_p++; red = *buf_p++; *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = 255; break; case 32: blue = *buf_p++; green = *buf_p++; red = *buf_p++; alphabyte = *buf_p++; *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alphabyte; break; } } } } else if (targa_header.image_type==10) { // Runlength encoded RGB images unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j; for(row=rows-1; row>=0; row--) { pixbuf = targa_rgba + row*columns*4; for(column=0; column<columns; ) { packetHeader= *buf_p++; packetSize = 1 + (packetHeader & 0x7f); if (packetHeader & 0x80) { // run-length packet switch (targa_header.pixel_size) { case 24: blue = *buf_p++; green = *buf_p++; red = *buf_p++; alphabyte = 255; break; case 32: blue = *buf_p++; green = *buf_p++; red = *buf_p++; alphabyte = *buf_p++; break; } for(j=0;j<packetSize;j++) { *pixbuf++=red; *pixbuf++=green; *pixbuf++=blue; *pixbuf++=alphabyte; column++; if (column==columns) { // run spans across rows column=0; if (row>0) row--; else goto breakOut; pixbuf = targa_rgba + row*columns*4; } } } else { // non run-length packet for(j=0;j<packetSize;j++) { switch (targa_header.pixel_size) { case 24: blue = *buf_p++; green = *buf_p++; red = *buf_p++; *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = 255; break; case 32: blue = *buf_p++; green = *buf_p++; red = *buf_p++; alphabyte = *buf_p++; *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alphabyte; break; } column++; if (column==columns) { // pixel packet run spans across rows column=0; if (row>0) row--; else goto breakOut; pixbuf = targa_rgba + row*columns*4; } } } } breakOut:; } } ri.FS_FreeFile (buffer); } //======================================================= image_t *R_FindFreeImage (void) { image_t *image; int i; // find a free image_t for (i=0, image=r_images ; i<numr_images ; i++,image++) { if (!image->registration_sequence) break; } if (i == numr_images) { if (numr_images == MAX_RIMAGES) ri.Sys_Error (ERR_DROP, "MAX_RIMAGES"); numr_images++; } image = &r_images[i]; return image; } /* ================ GL_LoadPic ================ */ image_t *GL_LoadPic (char *name, byte *pic, int width, int height, imagetype_t type) { image_t *image; int i, c, b; image = R_FindFreeImage (); if (strlen(name) >= sizeof(image->name)) ri.Sys_Error (ERR_DROP, "Draw_LoadPic: \"%s\" is too long", name); strcpy (image->name, name); image->registration_sequence = registration_sequence; image->width = width; image->height = height; image->type = type; c = width*height; image->pixels[0] = malloc (c); image->transparent = false; for (i=0 ; i<c ; i++) { b = pic[i]; if (b == 255) image->transparent = true; image->pixels[0][i] = b; } return image; } /* ================ R_LoadWal ================ */ image_t *R_LoadWal (char *name) { miptex_t *mt; int ofs; image_t *image; int size; ri.FS_LoadFile (name, (void **)&mt); if (!mt) { ri.Con_Printf (PRINT_ALL, "R_LoadWal: can't load %s\n", name); return r_notexture_mip; } image = R_FindFreeImage (); strcpy (image->name, name); image->width = LittleLong (mt->width); image->height = LittleLong (mt->height); image->type = it_wall; image->registration_sequence = registration_sequence; size = image->width*image->height * (256+64+16+4)/256; image->pixels[0] = malloc (size); image->pixels[1] = image->pixels[0] + image->width*image->height; image->pixels[2] = image->pixels[1] + image->width*image->height/4; image->pixels[3] = image->pixels[2] + image->width*image->height/16; ofs = LittleLong (mt->offsets[0]); memcpy ( image->pixels[0], (byte *)mt + ofs, size); ri.FS_FreeFile ((void *)mt); return image; } /* =============== R_FindImage Finds or loads the given image =============== */ image_t *R_FindImage (char *name, imagetype_t type) { image_t *image; int i, len; byte *pic, *palette; int width, height; if (!name) return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: NULL name"); len = strlen(name); if (len<5) return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: bad name: %s", name); // look for it for (i=0, image=r_images ; i<numr_images ; i++,image++) { if (!strcmp(name, image->name)) { image->registration_sequence = registration_sequence; return image; } } // // load the pic from disk // pic = NULL; palette = NULL; if (!strcmp(name+len-4, ".pcx")) { LoadPCX (name, &pic, &palette, &width, &height); if (!pic) return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: can't load %s", name); image = GL_LoadPic (name, pic, width, height, type); } else if (!strcmp(name+len-4, ".wal")) { image = R_LoadWal (name); } else if (!strcmp(name+len-4, ".tga")) return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: can't load %s in software renderer", name); else return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: bad extension on: %s", name); if (pic) free(pic); if (palette) free(palette); return image; } /* =============== R_RegisterSkin =============== */ struct image_s *R_RegisterSkin (char *name) { return R_FindImage (name, it_skin); } /* ================ R_FreeUnusedImages Any image that was not touched on this registration sequence will be freed. ================ */ void R_FreeUnusedImages (void) { int i; image_t *image; for (i=0, image=r_images ; i<numr_images ; i++, image++) { if (image->registration_sequence == registration_sequence) { Com_PageInMemory ((byte *)image->pixels[0], image->width*image->height); continue; // used this sequence } if (!image->registration_sequence) continue; // free texture if (image->type == it_pic) continue; // don't free pics // free it free (image->pixels[0]); // the other mip levels just follow memset (image, 0, sizeof(*image)); } } /* =============== R_InitImages =============== */ void R_InitImages (void) { registration_sequence = 1; } /* =============== R_ShutdownImages =============== */ void R_ShutdownImages (void) { int i; image_t *image; for (i=0, image=r_images ; i<numr_images ; i++, image++) { if (!image->registration_sequence) continue; // free texture // free it free (image->pixels[0]); // the other mip levels just follow memset (image, 0, sizeof(*image)); } }