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