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