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 byte *buffer = NULL;
49 TargaHeader targa_header;
50 byte *targa_rgba;
51 int length;
52
53 *pic = NULL;
54
55 if(width)
56 *width = 0;
57 if(height)
58 *height = 0;
59
60 //
61 // load the file
62 //
63 length = ri.FS_ReadFile ( ( char * ) name, (void **)&buffer);
64 if (!buffer || length < 0) {
65 return;
66 }
67
68 if(length < 18)
69 {
70 ri.Error( ERR_DROP, "LoadTGA: header too short (%s)\n", name );
71 }
72
73 buf_p = buffer;
74 end = buffer + length;
75
76 targa_header.id_length = buf_p[0];
77 targa_header.colormap_type = buf_p[1];
78 targa_header.image_type = buf_p[2];
79
80 memcpy(&targa_header.colormap_index, &buf_p[3], 2);
81 memcpy(&targa_header.colormap_length, &buf_p[5], 2);
82 targa_header.colormap_size = buf_p[7];
83 memcpy(&targa_header.x_origin, &buf_p[8], 2);
84 memcpy(&targa_header.y_origin, &buf_p[10], 2);
85 memcpy(&targa_header.width, &buf_p[12], 2);
86 memcpy(&targa_header.height, &buf_p[14], 2);
87 targa_header.pixel_size = buf_p[16];
88 targa_header.attributes = buf_p[17];
89
90 targa_header.colormap_index = LittleShort(targa_header.colormap_index);
91 targa_header.colormap_length = LittleShort(targa_header.colormap_length);
92 targa_header.x_origin = LittleShort(targa_header.x_origin);
93 targa_header.y_origin = LittleShort(targa_header.y_origin);
94 targa_header.width = LittleShort(targa_header.width);
95 targa_header.height = LittleShort(targa_header.height);
96
97 buf_p += 18;
98
99 if (targa_header.image_type!=2
100 && targa_header.image_type!=10
101 && targa_header.image_type != 3 )
102 {
103 ri.Error (ERR_DROP, "LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n");
104 }
105
106 if ( targa_header.colormap_type != 0 )
107 {
108 ri.Error( ERR_DROP, "LoadTGA: colormaps not supported\n" );
109 }
110
111 if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 )
112 {
113 ri.Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
114 }
115
116 columns = targa_header.width;
117 rows = targa_header.height;
118 numPixels = columns * rows * 4;
119
120 if(!columns || !rows || numPixels > 0x7FFFFFFF || numPixels / columns / 4 != rows)
121 {
122 ri.Error (ERR_DROP, "LoadTGA: %s has an invalid image size\n", name);
123 }
124
125
126 targa_rgba = ri.Malloc (numPixels);
127
128 if (targa_header.id_length != 0)
129 {
130 if (buf_p + targa_header.id_length > end)
131 ri.Error( ERR_DROP, "LoadTGA: header too short (%s)\n", name );
132
133 buf_p += targa_header.id_length; // skip TARGA image comment
134 }
135
136 if ( targa_header.image_type==2 || targa_header.image_type == 3 )
137 {
138 if(buf_p + columns*rows*targa_header.pixel_size/8 > end)
139 {
140 ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)\n", name);
141 }
142
143 // Uncompressed RGB or gray scale image
144 for(row=rows-1; row>=0; row--)
145 {
146 pixbuf = targa_rgba + row*columns*4;
147 for(column=0; column<columns; column++)
148 {
149 unsigned char red,green,blue,alphabyte;
150 switch (targa_header.pixel_size)
151 {
152
153 case 8:
154 blue = *buf_p++;
155 green = blue;
156 red = blue;
157 *pixbuf++ = red;
158 *pixbuf++ = green;
159 *pixbuf++ = blue;
160 *pixbuf++ = 255;
161 break;
162
163 case 24:
164 blue = *buf_p++;
165 green = *buf_p++;
166 red = *buf_p++;
167 *pixbuf++ = red;
168 *pixbuf++ = green;
169 *pixbuf++ = blue;
170 *pixbuf++ = 255;
171 break;
172 case 32:
173 blue = *buf_p++;
174 green = *buf_p++;
175 red = *buf_p++;
176 alphabyte = *buf_p++;
177 *pixbuf++ = red;
178 *pixbuf++ = green;
179 *pixbuf++ = blue;
180 *pixbuf++ = alphabyte;
181 break;
182 default:
183 ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
184 break;
185 }
186 }
187 }
188 }
189 else if (targa_header.image_type==10) { // Runlength encoded RGB images
190 unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
191
192 red = 0;
193 green = 0;
194 blue = 0;
195 alphabyte = 0xff;
196
197 for(row=rows-1; row>=0; row--) {
198 pixbuf = targa_rgba + row*columns*4;
199 for(column=0; column<columns; ) {
200 if(buf_p + 1 > end)
201 ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)\n", name);
202 packetHeader= *buf_p++;
203 packetSize = 1 + (packetHeader & 0x7f);
204 if (packetHeader & 0x80) { // run-length packet
205 if(buf_p + targa_header.pixel_size/8 > end)
206 ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)\n", name);
207 switch (targa_header.pixel_size) {
208 case 24:
209 blue = *buf_p++;
210 green = *buf_p++;
211 red = *buf_p++;
212 alphabyte = 255;
213 break;
214 case 32:
215 blue = *buf_p++;
216 green = *buf_p++;
217 red = *buf_p++;
218 alphabyte = *buf_p++;
219 break;
220 default:
221 ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
222 break;
223 }
224
225 for(j=0;j<packetSize;j++) {
226 *pixbuf++=red;
227 *pixbuf++=green;
228 *pixbuf++=blue;
229 *pixbuf++=alphabyte;
230 column++;
231 if (column==columns) { // run spans across rows
232 column=0;
233 if (row>0)
234 row--;
235 else
236 goto breakOut;
237 pixbuf = targa_rgba + row*columns*4;
238 }
239 }
240 }
241 else { // non run-length packet
242
243 if(buf_p + targa_header.pixel_size/8*packetSize > end)
244 ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)\n", name);
245 for(j=0;j<packetSize;j++) {
246 switch (targa_header.pixel_size) {
247 case 24:
248 blue = *buf_p++;
249 green = *buf_p++;
250 red = *buf_p++;
251 *pixbuf++ = red;
252 *pixbuf++ = green;
253 *pixbuf++ = blue;
254 *pixbuf++ = 255;
255 break;
256 case 32:
257 blue = *buf_p++;
258 green = *buf_p++;
259 red = *buf_p++;
260 alphabyte = *buf_p++;
261 *pixbuf++ = red;
262 *pixbuf++ = green;
263 *pixbuf++ = blue;
264 *pixbuf++ = alphabyte;
265 break;
266 default:
267 ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
268 break;
269 }
270 column++;
271 if (column==columns) { // pixel packet run spans across rows
272 column=0;
273 if (row>0)
274 row--;
275 else
276 goto breakOut;
277 pixbuf = targa_rgba + row*columns*4;
278 }
279 }
280 }
281 }
282 breakOut:;
283 }
284 }
285
286 #if 0
287 // TTimo: this is the chunk of code to ensure a behavior that meets TGA specs
288 // bit 5 set => top-down
289 if (targa_header.attributes & 0x20) {
290 unsigned char *flip = (unsigned char*)malloc (columns*4);
291 unsigned char *src, *dst;
292
293 for (row = 0; row < rows/2; row++) {
294 src = targa_rgba + row * 4 * columns;
295 dst = targa_rgba + (rows - row - 1) * 4 * columns;
296
297 memcpy (flip, src, columns*4);
298 memcpy (src, dst, columns*4);
299 memcpy (dst, flip, columns*4);
300 }
301 free (flip);
302 }
303 #endif
304 // instead we just print a warning
305 if (targa_header.attributes & 0x20) {
306 ri.Printf( PRINT_WARNING, "WARNING: '%s' TGA file header declares top-down image, ignoring\n", name);
307 }
308
309 if (width)
310 *width = columns;
311 if (height)
312 *height = rows;
313
314 *pic = targa_rgba;
315
316 ri.FS_FreeFile (buffer);
317 }
318