1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22
23 #include "tr_local.h"
24
25 /*
26 ========================================================================
27
28 TGA files are used for 24/32 bit images
29
30 ========================================================================
31 */
32
33 typedef struct _TargaHeader {
34 unsigned char id_length, colormap_type, image_type;
35 unsigned short colormap_index, colormap_length;
36 unsigned char colormap_size;
37 unsigned short x_origin, y_origin, width, height;
38 unsigned char pixel_size, attributes;
39 } TargaHeader;
40
R_LoadTGA(const char * name,byte ** pic,int * width,int * height)41 void R_LoadTGA ( const char *name, byte **pic, int *width, int *height)
42 {
43 unsigned columns, rows, numPixels;
44 byte *pixbuf;
45 int row, column;
46 byte *buf_p;
47 byte *end;
48 union {
49 byte *b;
50 void *v;
51 } buffer;
52 TargaHeader targa_header;
53 byte *targa_rgba;
54 int length;
55
56 *pic = NULL;
57
58 if(width)
59 *width = 0;
60 if(height)
61 *height = 0;
62
63 //
64 // load the file
65 //
66 length = ri.FS_ReadFile ( ( char * ) name, &buffer.v);
67 if (!buffer.b || length < 0) {
68 return;
69 }
70
71 if(length < 18)
72 {
73 ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name );
74 }
75
76 buf_p = buffer.b;
77 end = buffer.b + length;
78
79 targa_header.id_length = buf_p[0];
80 targa_header.colormap_type = buf_p[1];
81 targa_header.image_type = buf_p[2];
82
83 memcpy(&targa_header.colormap_index, &buf_p[3], 2);
84 memcpy(&targa_header.colormap_length, &buf_p[5], 2);
85 targa_header.colormap_size = buf_p[7];
86 memcpy(&targa_header.x_origin, &buf_p[8], 2);
87 memcpy(&targa_header.y_origin, &buf_p[10], 2);
88 memcpy(&targa_header.width, &buf_p[12], 2);
89 memcpy(&targa_header.height, &buf_p[14], 2);
90 targa_header.pixel_size = buf_p[16];
91 targa_header.attributes = buf_p[17];
92
93 targa_header.colormap_index = LittleShort(targa_header.colormap_index);
94 targa_header.colormap_length = LittleShort(targa_header.colormap_length);
95 targa_header.x_origin = LittleShort(targa_header.x_origin);
96 targa_header.y_origin = LittleShort(targa_header.y_origin);
97 targa_header.width = LittleShort(targa_header.width);
98 targa_header.height = LittleShort(targa_header.height);
99
100 buf_p += 18;
101
102 if (targa_header.image_type!=2
103 && targa_header.image_type!=10
104 && targa_header.image_type != 3 )
105 {
106 ri.Error (ERR_DROP, "LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported");
107 }
108
109 if ( targa_header.colormap_type != 0 )
110 {
111 ri.Error( ERR_DROP, "LoadTGA: colormaps not supported" );
112 }
113
114 if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 )
115 {
116 ri.Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)");
117 }
118
119 columns = targa_header.width;
120 rows = targa_header.height;
121 numPixels = columns * rows * 4;
122
123 if(!columns || !rows || numPixels > 0x7FFFFFFF || numPixels / columns / 4 != rows)
124 {
125 ri.Error (ERR_DROP, "LoadTGA: %s has an invalid image size", name);
126 }
127
128
129 targa_rgba = ri.Z_Malloc (numPixels);
130
131 if (targa_header.id_length != 0)
132 {
133 if (buf_p + targa_header.id_length > end)
134 ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name );
135
136 buf_p += targa_header.id_length; // skip TARGA image comment
137 }
138
139 if ( targa_header.image_type==2 || targa_header.image_type == 3 )
140 {
141 if(buf_p + columns*rows*targa_header.pixel_size/8 > end)
142 {
143 ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
144 }
145
146 // Uncompressed RGB or gray scale image
147 for(row=rows-1; row>=0; row--)
148 {
149 pixbuf = targa_rgba + row*columns*4;
150 for(column=0; column<columns; column++)
151 {
152 unsigned char red,green,blue,alphabyte;
153 switch (targa_header.pixel_size)
154 {
155
156 case 8:
157 blue = *buf_p++;
158 green = blue;
159 red = blue;
160 *pixbuf++ = red;
161 *pixbuf++ = green;
162 *pixbuf++ = blue;
163 *pixbuf++ = 255;
164 break;
165
166 case 24:
167 blue = *buf_p++;
168 green = *buf_p++;
169 red = *buf_p++;
170 *pixbuf++ = red;
171 *pixbuf++ = green;
172 *pixbuf++ = blue;
173 *pixbuf++ = 255;
174 break;
175 case 32:
176 blue = *buf_p++;
177 green = *buf_p++;
178 red = *buf_p++;
179 alphabyte = *buf_p++;
180 *pixbuf++ = red;
181 *pixbuf++ = green;
182 *pixbuf++ = blue;
183 *pixbuf++ = alphabyte;
184 break;
185 default:
186 ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name );
187 break;
188 }
189 }
190 }
191 }
192 else if (targa_header.image_type==10) { // Runlength encoded RGB images
193 unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
194
195 for(row=rows-1; row>=0; row--) {
196 pixbuf = targa_rgba + row*columns*4;
197 for(column=0; column<columns; ) {
198 if(buf_p + 1 > end)
199 ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
200 packetHeader= *buf_p++;
201 packetSize = 1 + (packetHeader & 0x7f);
202 if (packetHeader & 0x80) { // run-length packet
203 if(buf_p + targa_header.pixel_size/8 > end)
204 ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
205 switch (targa_header.pixel_size) {
206 case 24:
207 blue = *buf_p++;
208 green = *buf_p++;
209 red = *buf_p++;
210 alphabyte = 255;
211 break;
212 case 32:
213 blue = *buf_p++;
214 green = *buf_p++;
215 red = *buf_p++;
216 alphabyte = *buf_p++;
217 break;
218 default:
219 ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name );
220 break;
221 }
222
223 for(j=0;j<packetSize;j++) {
224 *pixbuf++=red;
225 *pixbuf++=green;
226 *pixbuf++=blue;
227 *pixbuf++=alphabyte;
228 column++;
229 if (column==columns) { // run spans across rows
230 column=0;
231 if (row>0)
232 row--;
233 else
234 goto breakOut;
235 pixbuf = targa_rgba + row*columns*4;
236 }
237 }
238 }
239 else { // non run-length packet
240
241 if(buf_p + targa_header.pixel_size/8*packetSize > end)
242 ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
243 for(j=0;j<packetSize;j++) {
244 switch (targa_header.pixel_size) {
245 case 24:
246 blue = *buf_p++;
247 green = *buf_p++;
248 red = *buf_p++;
249 *pixbuf++ = red;
250 *pixbuf++ = green;
251 *pixbuf++ = blue;
252 *pixbuf++ = 255;
253 break;
254 case 32:
255 blue = *buf_p++;
256 green = *buf_p++;
257 red = *buf_p++;
258 alphabyte = *buf_p++;
259 *pixbuf++ = red;
260 *pixbuf++ = green;
261 *pixbuf++ = blue;
262 *pixbuf++ = alphabyte;
263 break;
264 default:
265 ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name );
266 break;
267 }
268 column++;
269 if (column==columns) { // pixel packet run spans across rows
270 column=0;
271 if (row>0)
272 row--;
273 else
274 goto breakOut;
275 pixbuf = targa_rgba + row*columns*4;
276 }
277 }
278 }
279 }
280 breakOut:;
281 }
282 }
283
284 #if 0
285 // TTimo: this is the chunk of code to ensure a behavior that meets TGA specs
286 // bit 5 set => top-down
287 if (targa_header.attributes & 0x20) {
288 unsigned char *flip = (unsigned char*)malloc (columns*4);
289 unsigned char *src, *dst;
290
291 for (row = 0; row < rows/2; row++) {
292 src = targa_rgba + row * 4 * columns;
293 dst = targa_rgba + (rows - row - 1) * 4 * columns;
294
295 memcpy (flip, src, columns*4);
296 memcpy (src, dst, columns*4);
297 memcpy (dst, flip, columns*4);
298 }
299 free (flip);
300 }
301 #endif
302 // instead we just print a warning
303 if (targa_header.attributes & 0x20) {
304 ri.Printf( PRINT_DEVELOPER, "WARNING: '%s' TGA file header declares top-down image, ignoring\n", name);
305 }
306
307 if (width)
308 *width = columns;
309 if (height)
310 *height = rows;
311
312 *pic = targa_rgba;
313
314 ri.FS_FreeFile (buffer.v);
315 }
316