1 /*
2 PLIB - A Suite of Portable Game Libraries
3 Copyright (C) 1998,2002 Steve Baker
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 For further information visit http://plib.sourceforge.net
20
21 $Id: ssgLoadTGA.cxx 1749 2003-01-30 00:51:59Z stromberg $
22 */
23
24
25 #include "ssgLocal.h"
26
27 #ifdef SSG_LOAD_TGA_SUPPORTED
28
29 //#include <sys/stat.h>
30
31 /*
32 * Submitted by Sam Stickland : sam@spacething.org
33 * Targe loading code based on code written Dave Gay : f00Dave@bigfoot.com, http://personal.nbnet.nb.ca/daveg/
34 *
35 * Jan 29, 2003:
36 * Added support for other image types, as well as run-length encoding.
37 * /marten (stromberg@users.sf.net)
38 */
39
40
get16u(const GLubyte * ptr)41 inline int get16u(const GLubyte *ptr) // little endian
42 {
43 return (ptr[0] | (ptr[1] << 8));
44 }
45
46
ssgLoadTGA(const char * fname,ssgTextureInfo * info)47 bool ssgLoadTGA ( const char *fname, ssgTextureInfo* info )
48 {
49
50 #if 1 // new loader:
51
52
53 /* TGA file layout:
54 *
55 * =============================
56 * Header:
57 * Ofs Sz Desc
58 * ----------------
59 * 0 1 Image ID Length
60 * 1 1 Colormap Type
61 * 2 1 Image Type
62 * 3 2 Colormap Origin
63 * 5 2 Colormap Length
64 * 7 1 Colormap Entry Size
65 * 8 2 X Origin of Image
66 * 10 2 Y Origin of Image
67 * 12 2 Image Width
68 * 14 2 Image Height
69 * 16 1 Image Pixel Size
70 * 17 1 Image Descriptor
71 * =============================
72 * Image Identification
73 * =============================
74 * Colormap Data
75 * =============================
76 * Image Data
77 * =============================
78 */
79
80
81 GLubyte header[18];
82
83 FILE *f = fopen(fname, "rb");
84
85 if ( f == NULL )
86 {
87 ulSetError( UL_WARNING, "ssgLoadTGA: Failed to open '%s' for reading.", fname );
88 return false;
89 }
90
91 if ( fread(header, 18, 1, f) != 1 )
92 {
93 ulSetError( UL_WARNING, "ssgLoadTGA: Failed to read header of '%s'.", fname );
94 fclose(f);
95 return false;
96 }
97
98 // colormap info
99 int cm_type = header[1];
100 int cm_first = get16u(header + 3);
101 int cm_count = get16u(header + 5);
102 int cm_bits = header[7];
103
104 // image info
105 int type = header[2];
106 int xsize = get16u(header + 12);
107 int ysize = get16u(header + 14);
108 int bits = header[16];
109
110 /* image types:
111 *
112 * 0 - no image
113 * 1 - colormap
114 * 2 - RGB
115 * 3 - grayscale
116 * 9 - colormap, RLE
117 * 10 - RGB, RLE
118 * 11 - grayscale, RLE
119 *
120 */
121
122 if ( cm_type > 1 || (type & ~(8+3)) != 0 || (type & 3) == 0 || ((type & 3) == 1 && cm_type == 0) )
123 {
124 ulSetError( UL_WARNING, "ssgLoadTGA: '%s' is not a TGA image.", fname );
125 fclose(f);
126 return false;
127 }
128
129 if ( (type & 3) == 1 && cm_bits != 8 && cm_bits != 16 && cm_bits != 24 && cm_bits != 32 )
130 {
131 ulSetError( UL_WARNING, "ssgLoadTGA: Unsupported colormap depth %d.", cm_bits );
132 fclose(f);
133 return false;
134 }
135
136 if ( ((type & 3) != 2 && bits != 8) || // colormap and grayscale must be 8-bit
137 ((type & 3) == 2 && bits != 16 && bits != 24 && bits != 32) ) // RGB may not be 8-bit
138 {
139 ulSetError( UL_WARNING, "ssgLoadTGA: Unsupported depth %d for image type %d.", bits, type );
140 fclose(f);
141 return false;
142 }
143
144 ulSetError( UL_DEBUG, "ssgLoadTGA: Loading '%s', %s %dx%d-%d%s.",
145 fname,
146 (type & 3) == 1 ? "colormap" : (type & 3) == 2 ? "RGB" : "grayscale",
147 xsize, ysize, bits,
148 (type & 8) != 0 ? " RLE" : "");
149
150
151 bool eof = false;
152
153
154 // skip image identification
155
156 if (fseek(f, header[0], SEEK_CUR) != 0)
157 eof = true;
158
159
160 // read colormap
161
162 GLubyte *cm_data = 0;
163
164 if (cm_type != 0)
165 {
166 cm_data = new GLubyte [ (cm_bits / 8) * cm_count ];
167 if (fread(cm_data, (cm_bits / 8) * cm_count, 1, f) != 1)
168 eof = true;
169 }
170
171
172 // read image data
173
174 GLubyte *image = new GLubyte [ (bits / 8) * xsize * ysize ];
175
176 if ((type & 8) != 0)
177 {
178 // unpack RLE data
179
180 int comp = (bits / 8);
181 int current = 0;
182
183 for (;;)
184 {
185 int code = getc(f);
186
187 if (code == EOF) {
188 eof = true;
189 break;
190 }
191
192 int length = (code & 0x7f) + 1;
193
194 if (current + length > xsize * ysize)
195 {
196 ulSetError( UL_WARNING, "ssgLoadTGA: RLE unpack problems." );
197 fclose(f);
198 delete [] cm_data;
199 delete [] image;
200 return false;
201 }
202
203 if ((code & 0x80) == 0)
204 {
205 // raw packet
206 if (fread(image + current * comp, length * comp, 1, f) != 1) {
207 eof = true;
208 break;
209 }
210 current += length;
211 }
212 else
213 {
214 // run-length packet
215 GLubyte data[4];
216 if (fread(data, comp, 1, f) != 1) {
217 eof = true;
218 break;
219 }
220 for (int i = 0; i < length; i++)
221 memcpy(image + (current++) * comp, data, comp);
222 }
223
224 if (current == xsize * ysize)
225 break;
226 }
227 }
228 else
229 {
230 if (fread(image, (bits / 8) * xsize * ysize, 1, f) != 1)
231 eof = true;
232 }
233
234 if (eof || ferror(f))
235 {
236 ulSetError( UL_WARNING, "ssgLoadTGA: %s.",
237 ferror(f) ? "Read error" : "Unexpected end of file" );
238 fclose(f);
239 delete [] cm_data;
240 delete [] image;
241 return false;
242 }
243
244 fclose(f);
245
246
247 // apply colormap
248
249 if ((type & 3) == 1)
250 {
251 int comp = (cm_bits / 8);
252
253 GLubyte *source = image;
254 GLubyte *target = new GLubyte [ comp * xsize * ysize ];
255
256 for (int i = 0; i < xsize * ysize; i++)
257 {
258 int index = source[i] - cm_first;
259 if (index < 0 || index > cm_count)
260 memset(target + i * comp, 0, comp);
261 else
262 memcpy(target + i * comp, cm_data + index * comp, comp);
263 }
264
265 delete [] source;
266
267 image = target;
268 bits = cm_bits;
269 }
270
271 delete [] cm_data;
272
273
274 // convert image to plain GL_LUMINANCE, GL_RGB or GL_RGBA
275
276 int zsize = (bits == 8) ? 1 : (bits == 24) ? 3 : 4;
277
278 GLubyte *pixels = new GLubyte [ xsize * ysize * zsize ];
279
280 for (int i = 0; i < xsize * ysize; i++)
281 {
282 switch (bits) {
283
284 case 8:
285 pixels[i] = image[i];
286 break;
287
288 case 16:
289 {
290 int temp = get16u(image + 2*i); // ARRR RRGG GGGB BBBB
291 pixels[4*i + 0] = (temp & 0x7c00) >> 7;
292 pixels[4*i + 1] = (temp & 0x03e0) >> 2;
293 pixels[4*i + 2] = (temp & 0x001f) << 3;
294 pixels[4*i + 3] = (temp & 0x8000) ? 255 : 0;
295 break;
296 }
297
298 case 24:
299 pixels[3*i + 0] = image[3*i + 2];
300 pixels[3*i + 1] = image[3*i + 1];
301 pixels[3*i + 2] = image[3*i + 0];
302 break;
303
304 case 32:
305 pixels[4*i + 0] = image[4*i + 2];
306 pixels[4*i + 1] = image[4*i + 1];
307 pixels[4*i + 2] = image[4*i + 0];
308 pixels[4*i + 3] = image[4*i + 3];
309 break;
310 }
311 }
312
313 delete [] image;
314
315 #if 0
316 if (zsize == 3)
317 {
318 printf("writing 'out.ppm'...\n");
319 FILE *f = fopen("out.ppm", "wb");
320 fprintf(f, "P6\n%d %d\n255\n", xsize, ysize);
321 fwrite(pixels, 3 * xsize * ysize, 1, f);
322 fclose(f);
323 }
324 #endif
325
326 if ( info != NULL )
327 {
328 info -> width = xsize;
329 info -> height = ysize;
330 info -> depth = zsize;
331 info -> alpha = (zsize == 4);
332 }
333
334 return ssgMakeMipMaps ( pixels, xsize, ysize, zsize );
335
336
337 #else // old loader:
338
339
340 #define DEF_targaHeaderLength 12
341 #define DEF_targaHeaderContent "\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00"
342
343 struct stat fileinfo;
344 int bytesRead, width, height, maxLen;
345 char *pData = NULL;
346
347 if ( stat(fname, &fileinfo) == -1 ) {
348 ulSetError ( UL_WARNING, "ssgLoadTexture: Failed to load '%s'.", fname);
349 return false ;
350 }
351
352 FILE *tfile;
353 if( (tfile = fopen(fname, "rb")) == NULL) {
354 ulSetError ( UL_WARNING, "ssgLoadTexture: Failed to load '%s'.", fname);
355 return false ;
356 }
357
358 maxLen = fileinfo.st_size;
359 pData = new char [maxLen];
360 fread (pData, maxLen, 1, tfile);
361 fclose (tfile);
362 pData[0] = 0x00;
363
364 if( memcmp( pData, DEF_targaHeaderContent, DEF_targaHeaderLength ) != 0 ) {
365 ulSetError ( UL_WARNING, "ssgLoadTexture: Failed to load '%s'. Not a targa (apparently).", fname);
366 delete[] pData;
367 return false ;
368 }
369
370 unsigned char smallArray[ 2 ];
371
372 memcpy( smallArray, pData + DEF_targaHeaderLength + 0, 2 );
373 width = smallArray[ 0 ] + smallArray[ 1 ] * 0x0100;
374
375 memcpy( smallArray, pData + DEF_targaHeaderLength + 2, 2 );
376 height = smallArray[ 0 ] + smallArray[ 1 ] * 0x0100;
377
378 memcpy( smallArray, pData + DEF_targaHeaderLength + 4, 2 );
379 int depth = smallArray[ 0 ];
380 // + smallArray[ 1 ] * 0x0100;
381
382 if( ( width <= 0 ) || ( height <= 0 ) )
383 {
384 ulSetError ( UL_WARNING, "ssgLoadTexture: Failed to load '%s'. Width and height < 0.", fname);
385 delete[] pData;
386 return false ;
387 }
388
389 // Only allow 24-bit and 32-bit!
390 bool is24Bit( depth == 24 );
391 bool is32Bit( depth == 32 );
392
393 if( !( is24Bit || is32Bit ) )
394 {
395 ulSetError ( UL_WARNING, "ssgLoadTexture: Failed to load '%s'. Not 24 or 32 bit.", fname);
396 delete[] pData;
397 return false ;
398 }
399
400 // Make it a BGRA array for now.
401 int bodySize( width * height * 4 );
402 unsigned char * texels = new unsigned char[ bodySize ];
403 if( is32Bit )
404 {
405 // Texture is 32 bit
406 // Easy, just copy it.
407 memcpy( texels, pData + DEF_targaHeaderLength + 6, bodySize );
408 }
409 else if( is24Bit )
410 {
411 // Texture is 24 bit
412 bytesRead = DEF_targaHeaderLength + 6;
413 for( int loop = 0; loop < bodySize; loop += 4, bytesRead += 3 )
414 {
415 memcpy( texels + loop, pData + bytesRead, 3 );
416 texels[ loop + 3 ] = 255; // Force alpha to max.
417 }
418 }
419
420 // Swap R & B (convert to RGBA).
421 for( int loop = 0; loop < bodySize; loop += 4 )
422 {
423 unsigned char tempC = texels[ loop + 0 ];
424 texels[ loop + 0 ] = texels[ loop + 2 ];
425 texels[ loop + 2 ] = tempC;
426 }
427
428 delete[] pData;
429
430 if ( info != NULL )
431 {
432 info -> width = width ;
433 info -> height = height ;
434 info -> depth = 4 ;
435 info -> alpha = is32Bit? 1: 0 ;
436 }
437 return ssgMakeMipMaps ( texels, width, height, 4) ;
438
439
440 #endif
441 }
442
443 #else
444
ssgLoadTGA(const char * fname,ssgTextureInfo * info)445 bool ssgLoadTGA ( const char *fname, ssgTextureInfo* info )
446 {
447 ulSetError ( UL_WARNING,
448 "ssgLoadTexture: '%s' - TGA support not configured", fname ) ;
449 return false ;
450 }
451
452 #endif
453