/* Copyright (C) 2001-2002 A Nourai 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 included (GNU.txt) 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. */ // gl_image.h -- high quality image handling #include "quakedef.h" #include int image_width, image_height; /* ========================================================= Targa Loading ========================================================= */ #define TGA_MAXCOLORS 16384 /* Definitions for image types. */ #define TGA_Map 1 /* Uncompressed, color-mapped images. */ #define TGA_RGB 2 /* Uncompressed, RGB images. */ #define TGA_Mono 3 /* Uncompressed, black and white images. */ #define TGA_RLEMap 9 /* Runlength encoded color-mapped images. */ #define TGA_RLERGB 10 /* Runlength encoded RGB images. */ #define TGA_RLEMono 11 /* Compressed, black and white images. */ /* Definitions for interleave flag. */ #define TGA_IL_Two 1 /* two-way (even/odd) interleaving */ #define TGA_IL_Four 2 /* four way interleaving */ /* Definitions for origin flag */ #define TGA_O_UPPER 0 /* Origin in lower left-hand corner. */ 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; int fgetLittleShort (FILE *f) { byte b1, b2; b1 = fgetc(f); b2 = fgetc(f); return (short)(b1 + b2*256); } int fgetLittleLong (FILE *f) { byte b1, b2, b3, b4; b1 = fgetc(f); b2 = fgetc(f); b3 = fgetc(f); b4 = fgetc(f); return b1 + (b2<<8) + (b3<<16) + (b4<<24); } /* ============= LoadTGA ============= */ byte *LoadTGA (FILE *fin, char *name, int matchwidth, int matchheight) { int w, h, x, y, realrow, truerow, baserow, i, temp1, temp2, pixel_size, map_idx; int RLE_count, RLE_flag, size, interleave, origin; qboolean mapped, rlencoded; byte *data, *dst, r, g, b, a, j, k, l, *ColorMap; TargaHeader header; /* load file */ if (!fin && COM_FOpenFile(name, &fin, !(hipnotic || rogue)) == -1) return NULL; header.id_length = fgetc (fin); header.colormap_type = fgetc (fin); header.image_type = fgetc (fin); header.colormap_index = fgetLittleShort (fin); header.colormap_length = fgetLittleShort (fin); header.colormap_size = fgetc (fin); header.x_origin = fgetLittleShort (fin); header.y_origin = fgetLittleShort (fin); header.width = fgetLittleShort (fin); header.height = fgetLittleShort (fin); header.pixel_size = fgetc (fin); header.attributes = fgetc (fin); if (header.id_length != 0) fseek (fin, header.id_length, SEEK_CUR); if (matchwidth && header.width != matchwidth) { fclose (fin); return NULL; } else if (matchheight && header.height != matchheight) { fclose (fin); return NULL; } /* validate TGA type */ switch (header.image_type) { case TGA_Map: case TGA_RGB: case TGA_Mono: case TGA_RLEMap: case TGA_RLERGB: case TGA_RLEMono: break; default: Con_Printf ("%s : Only type 1 (map), 2 (RGB), 3 (mono), 9 (RLEmap), 10 (RLERGB), 11 (RLEmono) TGA images supported\n", name); fclose (fin); return NULL; } /* validate color depth */ switch (header.pixel_size) { case 8: case 15: case 16: case 24: case 32: break; default: Con_Printf ("%s : Only 8, 15, 16, 24 or 32 bit images (with colormaps) supported\n", name); fclose (fin); return NULL; } r = g = b = a = l = 0; /* if required, read the color map information. */ ColorMap = NULL; mapped = (header.image_type == TGA_Map || header.image_type == TGA_RLEMap) && header.colormap_type == 1; if (mapped) { /* validate colormap size */ switch (header.colormap_size) { case 8: case 15: case 16: case 32: case 24: break; default: Con_Printf ("%s : Only 8, 15, 16, 24 or 32 bit colormaps supported\n", name); fclose (fin); return NULL; } temp1 = header.colormap_index; temp2 = header.colormap_length; if ((temp1 + temp2 + 1) >= TGA_MAXCOLORS) { fclose (fin); return NULL; } ColorMap = Q_Malloc (TGA_MAXCOLORS * 4); map_idx = 0; for (i = temp1 ; i < temp1 + temp2 ; ++i, map_idx += 4) { /* read appropriate number of bytes, break into rgb & put in map. */ switch (header.colormap_size) { case 8: /* grey scale, read and triplicate. */ r = g = b = getc (fin); a = 255; break; case 15: /* 5 bits each of red green and blue. */ /* watch byte order. */ j = getc (fin); k = getc (fin); l = ((unsigned int) k << 8) + j; r = (byte) ( ((k & 0x7C) >> 2) << 3 ); g = (byte) ( (((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3 ); b = (byte) ( (j & 0x1F) << 3 ); a = 255; break; case 16: /* 5 bits each of red green and blue, 1 alpha bit. */ /* watch byte order. */ j = getc (fin); k = getc (fin); l = ((unsigned int) k << 8) + j; r = (byte) ( ((k & 0x7C) >> 2) << 3 ); g = (byte) ( (((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3 ); b = (byte) ( (j & 0x1F) << 3 ); a = (k & 0x80) ? 255 : 0; break; case 24: /* 8 bits each of blue, green and red. */ b = getc (fin); g = getc (fin); r = getc (fin); a = 255; l = 0; break; case 32: /* 8 bits each of blue, green, red and alpha. */ b = getc (fin); g = getc (fin); r = getc (fin); a = getc (fin); l = 0; break; } ColorMap[map_idx+0] = r; ColorMap[map_idx+1] = g; ColorMap[map_idx+2] = b; ColorMap[map_idx+3] = a; } } /* check run-length encoding. */ rlencoded = (header.image_type == TGA_RLEMap || header.image_type == TGA_RLERGB || header.image_type == TGA_RLEMono); RLE_count = RLE_flag = 0; image_width = w = header.width; image_height = h = header.height; size = w * h * 4; data = Q_Calloc (size, 1); /* read the Targa file body and convert to portable format. */ pixel_size = header.pixel_size; origin = (header.attributes & 0x20) >> 5; interleave = (header.attributes & 0xC0) >> 6; truerow = 0; baserow = 0; for (y=0 ; y> 2) << 3 ); g = (byte) ( (((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3 ); b = (byte) ( (j & 0x1F) << 3 ); a = 255; break; case 16: /* 5 bits each of red green and blue, 1 alpha bit. */ /* watch byte order. */ j = getc (fin); k = getc (fin); l = ((unsigned int) k << 8) + j; r = (byte) ( ((k & 0x7C) >> 2) << 3 ); g = (byte) ( (((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3 ); b = (byte) ( (j & 0x1F) << 3 ); a = (k & 0x80) ? 255 : 0; break; case 24: /* 8 bits each of blue, green and red. */ b = getc (fin); g = getc (fin); r = getc (fin); a = 255; l = 0; break; case 32: /* 8 bits each of blue, green, red and alpha. */ b = getc (fin); g = getc (fin); r = getc (fin); a = getc (fin); l = 0; break; default: fclose (fin); Con_Printf ("%s : Illegal pixel_size '%d'\n", name, pixel_size); free (data); if (mapped) free (ColorMap); return NULL; } PixEncode: if (mapped) { map_idx = l * 4; *dst++ = ColorMap[map_idx+0]; *dst++ = ColorMap[map_idx+1]; *dst++ = ColorMap[map_idx+2]; *dst++ = ColorMap[map_idx+3]; } else { *dst++ = r; *dst++ = g; *dst++ = b; *dst++ = a; } } if (interleave == TGA_IL_Four) truerow += 4; else if (interleave == TGA_IL_Two) truerow += 2; else truerow++; if (truerow >= h) truerow = ++baserow; } if (mapped) free (ColorMap); fclose (fin); return data; } /* ============= WriteTGA ============= */ int WriteTGA (char *filename, byte *pixels, int width, int height) { byte *buffer; int i, size, temp; qboolean retval = true; size = width * height * 3; buffer = Q_Malloc (size + 18); memset (buffer, 0, 18); buffer[2] = 2; buffer[12] = width & 255; buffer[13] = width >> 8; buffer[14] = height & 255; buffer[15] = height >> 8; buffer[16] = 24; memcpy (buffer + 18, pixels, size); for (i = 18 ; i < size + 18 ; i += 3) { temp = buffer[i]; buffer[i] = buffer[i+2]; buffer[i+2] = temp; } if (!COM_WriteFile(filename, buffer, size + 18)) retval = false; free (buffer); return retval; } /* ========================================================= PNG Loading ========================================================= */ /* ============= LoadPNG ============= */ byte *LoadPNG (FILE *fin, char *filename, int matchwidth, int matchheight) { byte header[8], **rowpointers, *data; png_structp png; png_infop pnginfo; int y, width, height, bitdepth, colortype; int interlace, compression, filter, bytesperpixel; unsigned long rowbytes; if (!fin && COM_FOpenFile(filename, &fin, !(hipnotic || rogue)) == -1) return NULL; fread (header, 1, 8, fin); if (png_sig_cmp(header, 0, 8)) { Con_Printf ("%s : Invalid PNG file\n", filename); fclose (fin); return NULL; } if (!(png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL))) { fclose (fin); return NULL; } if (!(pnginfo = png_create_info_struct(png))) { png_destroy_read_struct (&png, &pnginfo, NULL); fclose (fin); return NULL; } if (setjmp(png->jmpbuf)) { png_destroy_read_struct (&png, &pnginfo, NULL); fclose (fin); return NULL; } png_init_io (png, fin); png_set_sig_bytes (png, 8); png_read_info (png, pnginfo); png_get_IHDR (png, pnginfo, (png_uint_32 *)&width, (png_uint_32 *)&height, &bitdepth, &colortype, &interlace, &compression, &filter); if ((matchwidth && width != matchwidth) || (matchheight && height != matchheight)) { png_destroy_read_struct (&png, &pnginfo, NULL); fclose (fin); return NULL; } if (colortype == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb (png); png_set_filler (png, 255, PNG_FILLER_AFTER); } if (colortype == PNG_COLOR_TYPE_GRAY && bitdepth < 8) png_set_gray_1_2_4_to_8 (png); if (png_get_valid(png, pnginfo, PNG_INFO_tRNS)) png_set_tRNS_to_alpha (png); if (colortype == PNG_COLOR_TYPE_GRAY || colortype == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb (png); png_set_filler (png, 255, PNG_FILLER_AFTER); } if (colortype != PNG_COLOR_TYPE_RGBA) png_set_filler (png, 255, PNG_FILLER_AFTER); if (bitdepth < 8) png_set_expand (png); else if (bitdepth == 16) png_set_strip_16 (png); png_read_update_info (png, pnginfo); rowbytes = png_get_rowbytes (png, pnginfo); bytesperpixel = png_get_channels (png, pnginfo); bitdepth = png_get_bit_depth (png, pnginfo); if (bitdepth != 8 || bytesperpixel != 4) { Con_Printf ("%s : Bad PNG color depth and/or bpp\n", filename); fclose (fin); png_destroy_read_struct (&png, &pnginfo, NULL); return NULL; } data = Q_Malloc (height * rowbytes); rowpointers = Q_Malloc (height * 4); for (y=0 ; yjmpbuf)) { png_destroy_write_struct (&png_ptr, &info_ptr); fclose (fp); return false; } png_init_io (png_ptr, fp); png_set_compression_level (png_ptr, bound(Z_NO_COMPRESSION, compression, Z_BEST_COMPRESSION)); png_set_IHDR (png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info (png_ptr, info_ptr); rowpointers = Q_Malloc (4 * height); for (i=0 ; i