1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2009 by Denton Woods
5 // Last modified: 03/07/2009
6 //
7 // Filename: src-IL/src/il_targa.c
8 //
9 // Description: Reads from and writes to a Targa (.tga) file.
10 //
11 //-----------------------------------------------------------------------------
12 
13 
14 #include "il_internal.h"
15 #ifndef IL_NO_TGA
16 #include "il_targa.h"
17 //#include <time.h>  // for ilMakeString()
18 #include <string.h>
19 #include "il_manip.h"
20 #include "il_bits.h"
21 
22 #ifdef DJGPP
23 #include <dos.h>
24 #endif
25 
26 
27 //! Checks if the file specified in FileName is a valid Targa file.
ilIsValidTga(ILconst_string FileName)28 ILboolean ilIsValidTga(ILconst_string FileName)
29 {
30 	ILHANDLE	TargaFile;
31 	ILboolean	bTarga = IL_FALSE;
32 
33 	if (!iCheckExtension(FileName, IL_TEXT("tga")) &&
34 		!iCheckExtension(FileName, IL_TEXT("vda")) &&
35 		!iCheckExtension(FileName, IL_TEXT("icb")) &&
36 		!iCheckExtension(FileName, IL_TEXT("vst"))) {
37 		ilSetError(IL_INVALID_EXTENSION);
38 		return bTarga;
39 	}
40 
41 	TargaFile = iopenr(FileName);
42 	if (TargaFile == NULL) {
43 		ilSetError(IL_COULD_NOT_OPEN_FILE);
44 		return bTarga;
45 	}
46 
47 	bTarga = ilIsValidTgaF(TargaFile);
48 	icloser(TargaFile);
49 
50 	return bTarga;
51 }
52 
53 
54 //! Checks if the ILHANDLE contains a valid Targa file at the current position.
ilIsValidTgaF(ILHANDLE File)55 ILboolean ilIsValidTgaF(ILHANDLE File)
56 {
57 	ILuint		FirstPos;
58 	ILboolean	bRet;
59 
60 	iSetInputFile(File);
61 	FirstPos = itell();
62 	bRet = iIsValidTarga();
63 	iseek(FirstPos, IL_SEEK_SET);
64 
65 	return bRet;
66 }
67 
68 
69 //! Checks if Lump is a valid Targa lump.
ilIsValidTgaL(const void * Lump,ILuint Size)70 ILboolean ilIsValidTgaL(const void *Lump, ILuint Size)
71 {
72 	iSetInputLump(Lump, Size);
73 	return iIsValidTarga();
74 }
75 
76 
77 // Internal function used to get the Targa header from the current file.
iGetTgaHead(TARGAHEAD * Header)78 ILboolean iGetTgaHead(TARGAHEAD *Header)
79 {
80 	Header->IDLen = (ILubyte)igetc();
81 	Header->ColMapPresent = (ILubyte)igetc();
82 	Header->ImageType = (ILubyte)igetc();
83 	Header->FirstEntry = GetLittleShort();
84 	Header->ColMapLen = GetLittleShort();
85 	Header->ColMapEntSize = (ILubyte)igetc();
86 
87 	Header->OriginX = GetLittleShort();
88 	Header->OriginY = GetLittleShort();
89 	Header->Width = GetLittleUShort();
90 	Header->Height = GetLittleUShort();
91 	Header->Bpp = (ILubyte)igetc();
92 	Header->ImageDesc = (ILubyte)igetc();
93 
94 	return IL_TRUE;
95 }
96 
97 
98 // Internal function to get the header and check it.
iIsValidTarga()99 ILboolean iIsValidTarga()
100 {
101 	TARGAHEAD	Head;
102 
103 	if (!iGetTgaHead(&Head))
104 		return IL_FALSE;
105 	iseek(-(ILint)sizeof(TARGAHEAD), IL_SEEK_CUR);
106 
107 	return iCheckTarga(&Head);
108 }
109 
110 
111 // Internal function used to check if the HEADER is a valid Targa header.
iCheckTarga(TARGAHEAD * Header)112 ILboolean iCheckTarga(TARGAHEAD *Header)
113 {
114 	if (Header->Width == 0 || Header->Height == 0)
115 		return IL_FALSE;
116 	if (Header->Bpp != 8 && Header->Bpp != 15 && Header->Bpp != 16
117 		&& Header->Bpp != 24 && Header->Bpp != 32)
118 		return IL_FALSE;
119 	if (Header->ImageDesc & BIT_4)	// Supposed to be set to 0
120 		return IL_FALSE;
121 
122 	// check type (added 20040218)
123 	if (Header->ImageType   != TGA_NO_DATA
124 	   && Header->ImageType != TGA_COLMAP_UNCOMP
125 	   && Header->ImageType != TGA_UNMAP_UNCOMP
126 	   && Header->ImageType != TGA_BW_UNCOMP
127 	   && Header->ImageType != TGA_COLMAP_COMP
128 	   && Header->ImageType != TGA_UNMAP_COMP
129 	   && Header->ImageType != TGA_BW_COMP)
130 		return IL_FALSE;
131 
132 	// Doesn't work well with the bitshift so change it.
133 	if (Header->Bpp == 15)
134 		Header->Bpp = 16;
135 
136 	return IL_TRUE;
137 }
138 
139 
140 //! Reads a Targa file
ilLoadTarga(ILconst_string FileName)141 ILboolean ilLoadTarga(ILconst_string FileName)
142 {
143 	ILHANDLE	TargaFile;
144 	ILboolean	bTarga = IL_FALSE;
145 
146 	TargaFile = iopenr(FileName);
147 	if (TargaFile == NULL) {
148 		ilSetError(IL_COULD_NOT_OPEN_FILE);
149 		return bTarga;
150 	}
151 
152 	bTarga = ilLoadTargaF(TargaFile);
153 	icloser(TargaFile);
154 
155 	return bTarga;
156 }
157 
158 
159 //! Reads an already-opened Targa file
ilLoadTargaF(ILHANDLE File)160 ILboolean ilLoadTargaF(ILHANDLE File)
161 {
162 	ILuint		FirstPos;
163 	ILboolean	bRet;
164 
165 	iSetInputFile(File);
166 	FirstPos = itell();
167 	bRet = iLoadTargaInternal();
168 	iseek(FirstPos, IL_SEEK_SET);
169 
170 	return bRet;
171 }
172 
173 
174 //! Reads from a memory "lump" that contains a Targa
ilLoadTargaL(const void * Lump,ILuint Size)175 ILboolean ilLoadTargaL(const void *Lump, ILuint Size)
176 {
177 	iSetInputLump(Lump, Size);
178 	return iLoadTargaInternal();
179 }
180 
181 
182 // Internal function used to load the Targa.
iLoadTargaInternal()183 ILboolean iLoadTargaInternal()
184 {
185 	TARGAHEAD	Header;
186 	ILboolean	bTarga;
187 	ILenum		iOrigin;
188 
189 	if (iCurImage == NULL) {
190 		ilSetError(IL_ILLEGAL_OPERATION);
191 		return IL_FALSE;
192 	}
193 
194 	if (!iGetTgaHead(&Header))
195 		return IL_FALSE;
196 	if (!iCheckTarga(&Header)) {
197 		ilSetError(IL_INVALID_FILE_HEADER);
198 		return IL_FALSE;
199 	}
200 
201 	switch (Header.ImageType)
202 	{
203 		case TGA_NO_DATA:
204 			ilSetError(IL_ILLEGAL_FILE_VALUE);
205 			return IL_FALSE;
206 		case TGA_COLMAP_UNCOMP:
207 		case TGA_COLMAP_COMP:
208 			bTarga = iReadColMapTga(&Header);
209 			break;
210 		case TGA_UNMAP_UNCOMP:
211 		case TGA_UNMAP_COMP:
212 			bTarga = iReadUnmapTga(&Header);
213 			break;
214 		case TGA_BW_UNCOMP:
215 		case TGA_BW_COMP:
216 			bTarga = iReadBwTga(&Header);
217 			break;
218 		default:
219 			ilSetError(IL_ILLEGAL_FILE_VALUE);
220 			return IL_FALSE;
221 	}
222 
223 	if (bTarga==IL_FALSE)
224 		return IL_FALSE;
225 
226 	// @JASON Extra Code to manipulate the image depending on
227 	// the Image Descriptor's origin bits.
228 	iOrigin = Header.ImageDesc & IMAGEDESC_ORIGIN_MASK;
229 
230 	switch (iOrigin)
231 	{
232 		case IMAGEDESC_TOPLEFT:
233 			iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
234 			break;
235 
236 		case IMAGEDESC_TOPRIGHT:
237 			iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
238 			iMirror();
239 			break;
240 
241 		case IMAGEDESC_BOTLEFT:
242 			iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
243 			break;
244 
245 		case IMAGEDESC_BOTRIGHT:
246 			iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
247 			iMirror();
248 			break;
249 	}
250 
251 	return ilFixImage();
252 }
253 
254 
iReadColMapTga(TARGAHEAD * Header)255 ILboolean iReadColMapTga(TARGAHEAD *Header)
256 {
257 	char		ID[255];
258 	ILuint		i;
259 	ILushort	Pixel;
260 
261 	if (iread(ID, 1, Header->IDLen) != Header->IDLen)
262 		return IL_FALSE;
263 
264 	if (!ilTexImage(Header->Width, Header->Height, 1, (ILubyte)(Header->Bpp >> 3), 0, IL_UNSIGNED_BYTE, NULL)) {
265 		return IL_FALSE;
266 	}
267 	if (iCurImage->Pal.Palette && iCurImage->Pal.PalSize)
268 		ifree(iCurImage->Pal.Palette);
269 
270 	iCurImage->Format = IL_COLOUR_INDEX;
271 	iCurImage->Pal.PalSize = Header->ColMapLen * (Header->ColMapEntSize >> 3);
272 
273 	switch (Header->ColMapEntSize)
274 	{
275 		case 16:
276 			iCurImage->Pal.PalType = IL_PAL_BGRA32;
277 			iCurImage->Pal.PalSize = Header->ColMapLen * 4;
278 			break;
279 		case 24:
280 			iCurImage->Pal.PalType = IL_PAL_BGR24;
281 			break;
282 		case 32:
283 			iCurImage->Pal.PalType = IL_PAL_BGRA32;
284 			break;
285 		default:
286 			// Should *never* reach here
287 			ilSetError(IL_ILLEGAL_FILE_VALUE);
288 			return IL_FALSE;
289 	}
290 
291 	iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize);
292 	if (iCurImage->Pal.Palette == NULL) {
293 		return IL_FALSE;
294 	}
295 
296 	// Do we need to do something with FirstEntry?	Like maybe:
297 	//	iread(Image->Pal + Targa->FirstEntry, 1, Image->Pal.PalSize);  ??
298 	if (Header->ColMapEntSize != 16)
299 	{
300 		if (iread(iCurImage->Pal.Palette, 1, iCurImage->Pal.PalSize) != iCurImage->Pal.PalSize)
301 			return IL_FALSE;
302 	}
303 	else {
304 		// 16 bit palette, so we have to break it up.
305 		for (i = 0; i < iCurImage->Pal.PalSize; i += 4)
306 		{
307 			Pixel = GetBigUShort();
308 			if (ieof())
309 				return IL_FALSE;
310 			iCurImage->Pal.Palette[3] = (Pixel & 0x8000) >> 12;
311 			iCurImage->Pal.Palette[0] = (Pixel & 0xFC00) >> 7;
312 			iCurImage->Pal.Palette[1] = (Pixel & 0x03E0) >> 2;
313 			iCurImage->Pal.Palette[2] = (Pixel & 0x001F) << 3;
314 		}
315 	}
316 
317 	if (Header->ImageType == TGA_COLMAP_COMP)
318 	{
319 		if (!iUncompressTgaData(iCurImage))
320 		{
321 			return IL_FALSE;
322 		}
323 	}
324 	else
325 	{
326 		if (iread(iCurImage->Data, 1, iCurImage->SizeOfData) != iCurImage->SizeOfData)
327 		{
328 			return IL_FALSE;
329 		}
330 	}
331 
332 	return IL_TRUE;
333 }
334 
335 
iReadUnmapTga(TARGAHEAD * Header)336 ILboolean iReadUnmapTga(TARGAHEAD *Header)
337 {
338 	ILubyte Bpp;
339 	char	ID[255];
340 
341 	if (iread(ID, 1, Header->IDLen) != Header->IDLen)
342 		return IL_FALSE;
343 
344 	/*if (Header->Bpp == 16)
345 		Bpp = 3;
346 	else*/
347 	Bpp = (ILubyte)(Header->Bpp >> 3);
348 
349 	if (!ilTexImage(Header->Width, Header->Height, 1, Bpp, 0, IL_UNSIGNED_BYTE, NULL)) {
350 		return IL_FALSE;
351 	}
352 
353 	switch (iCurImage->Bpp)
354 	{
355 		case 1:
356 			iCurImage->Format = IL_COLOUR_INDEX;  // wtf?  How is this possible?
357 			break;
358 		case 2:  // 16-bit is not supported directly!
359 				 //iCurImage->Format = IL_RGB5_A1;
360 			/*iCurImage->Format = IL_RGBA;
361 			iCurImage->Type = IL_UNSIGNED_SHORT_5_5_5_1_EXT;*/
362 			//iCurImage->Type = IL_UNSIGNED_SHORT_5_6_5_REV;
363 
364 			// Remove?
365 			//ilCloseImage(iCurImage);
366 			//ilSetError(IL_FORMAT_NOT_SUPPORTED);
367 			//return IL_FALSE;
368 
369 			/*iCurImage->Bpp = 4;
370 			iCurImage->Format = IL_BGRA;
371 			iCurImage->Type = IL_UNSIGNED_SHORT_1_5_5_5_REV;*/
372 
373 			iCurImage->Format = IL_BGR;
374 
375 			break;
376 		case 3:
377 			iCurImage->Format = IL_BGR;
378 			break;
379 		case 4:
380 			iCurImage->Format = IL_BGRA;
381 			break;
382 		default:
383 			ilSetError(IL_INVALID_VALUE);
384 			return IL_FALSE;
385 	}
386 
387 
388 	// @TODO:  Determine this:
389 	// We assume that no palette is present, but it's possible...
390 	//	Should we mess with it or not?
391 
392 
393 	if (Header->ImageType == TGA_UNMAP_COMP) {
394 		if (!iUncompressTgaData(iCurImage)) {
395 			return IL_FALSE;
396 		}
397 	}
398 	else {
399 		if (iread(iCurImage->Data, 1, iCurImage->SizeOfData) != iCurImage->SizeOfData) {
400 			return IL_FALSE;
401 		}
402 	}
403 
404 	// Go ahead and expand it to 24-bit.
405 	if (Header->Bpp == 16) {
406 		if (!i16BitTarga(iCurImage))
407 			return IL_FALSE;
408 		return IL_TRUE;
409 	}
410 
411 	return IL_TRUE;
412 }
413 
414 
iReadBwTga(TARGAHEAD * Header)415 ILboolean iReadBwTga(TARGAHEAD *Header)
416 {
417 	char ID[255];
418 
419 	if (iread(ID, 1, Header->IDLen) != Header->IDLen)
420 		return IL_FALSE;
421 
422 	// We assume that no palette is present, but it's possible...
423 	//	Should we mess with it or not?
424 
425 	if (!ilTexImage(Header->Width, Header->Height, 1, (ILubyte)(Header->Bpp >> 3), IL_LUMINANCE, IL_UNSIGNED_BYTE, NULL)) {
426 		return IL_FALSE;
427 	}
428 
429 	if (Header->ImageType == TGA_BW_COMP) {
430 		if (!iUncompressTgaData(iCurImage)) {
431 			return IL_FALSE;
432 		}
433 	}
434 	else {
435 		if (iread(iCurImage->Data, 1, iCurImage->SizeOfData) != iCurImage->SizeOfData) {
436 			return IL_FALSE;
437 		}
438 	}
439 
440 	return IL_TRUE;
441 }
442 
443 
iUncompressTgaData(ILimage * Image)444 ILboolean iUncompressTgaData(ILimage *Image)
445 {
446 	ILuint	BytesRead = 0, Size, RunLen, i, ToRead;
447 	ILubyte Header, Color[4];
448 	ILint	c;
449 
450 	Size = Image->Width * Image->Height * Image->Depth * Image->Bpp;
451 
452 	if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
453 		iPreCache(iCurImage->SizeOfData / 2);
454 
455 	while (BytesRead < Size) {
456 		Header = (ILubyte)igetc();
457 		if (Header & BIT_7) {
458 			ClearBits(Header, BIT_7);
459 			if (iread(Color, 1, Image->Bpp) != Image->Bpp) {
460 				iUnCache();
461 				return IL_FALSE;
462 			}
463 			RunLen = (Header+1) * Image->Bpp;
464 			for (i = 0; i < RunLen; i += Image->Bpp) {
465 				// Read the color in, but we check to make sure that we do not go past the end of the image.
466 				for (c = 0; c < Image->Bpp && BytesRead+i+c < Size; c++) {
467 					Image->Data[BytesRead+i+c] = Color[c];
468 				}
469 			}
470 			BytesRead += RunLen;
471 		}
472 		else {
473 			RunLen = (Header+1) * Image->Bpp;
474 			// We have to check that we do not go past the end of the image data.
475 			if (BytesRead + RunLen > Size)
476 				ToRead = Size - BytesRead;
477 			else
478 				ToRead = RunLen;
479 			if (iread(Image->Data + BytesRead, 1, ToRead) != ToRead) {
480 				iUnCache();  //@TODO: Error needed here?
481 				return IL_FALSE;
482 			}
483 			BytesRead += RunLen;
484 
485 			if (BytesRead + RunLen > Size)
486 				iseek(RunLen - ToRead, IL_SEEK_CUR);
487 		}
488 	}
489 
490 	iUnCache();
491 
492 	return IL_TRUE;
493 }
494 
495 
496 // Pretty damn unoptimized
i16BitTarga(ILimage * Image)497 ILboolean i16BitTarga(ILimage *Image)
498 {
499 	ILushort	*Temp1;
500 	ILubyte 	*Data, *Temp2;
501 	ILuint		x, PixSize = Image->Width * Image->Height;
502 
503 	Data = (ILubyte*)ialloc(Image->Width * Image->Height * 3);
504 	Temp1 = (ILushort*)Image->Data;
505 	Temp2 = Data;
506 
507 	if (Data == NULL)
508 		return IL_FALSE;
509 
510 	for (x = 0; x < PixSize; x++) {
511 		*Temp2++ = (*Temp1 & 0x001F) << 3;	// Blue
512 		*Temp2++ = (*Temp1 & 0x03E0) >> 2;	// Green
513 		*Temp2++ = (*Temp1 & 0x7C00) >> 7;	// Red
514 
515 		Temp1++;
516 
517 
518 		/*s = *Temp;
519 		s = SwapShort(s);
520 		a = !!(s & BIT_15);
521 
522 		s = s << 1;
523 
524 		//if (a) {
525 		SetBits(s, BIT_0);
526 		//}
527 
528 		//SetBits(s, BIT_15);
529 
530 		*Temp++ = s;*/
531 	}
532 
533 	if (!ilTexImage(Image->Width, Image->Height, 1, 3, IL_BGR, IL_UNSIGNED_BYTE, Data)) {
534 		ifree(Data);
535 		return IL_FALSE;
536 	}
537 
538 	ifree(Data);
539 
540 	return IL_TRUE;
541 }
542 
543 
544 //! Writes a Targa file
ilSaveTarga(const ILstring FileName)545 ILboolean ilSaveTarga(const ILstring FileName)
546 {
547 	ILHANDLE	TargaFile;
548 	ILuint		TargaSize;
549 
550 	if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
551 		if (iFileExists(FileName)) {
552 			ilSetError(IL_FILE_ALREADY_EXISTS);
553 			return IL_FALSE;
554 		}
555 	}
556 
557 	TargaFile = iopenw(FileName);
558 	if (TargaFile == NULL) {
559 		ilSetError(IL_COULD_NOT_OPEN_FILE);
560 		return IL_FALSE;
561 	}
562 
563 	TargaSize = ilSaveTargaF(TargaFile);
564 	iclosew(TargaFile);
565 
566 	if (TargaSize == 0)
567 		return IL_FALSE;
568 	return IL_TRUE;
569 }
570 
571 
572 //! Writes a Targa to an already-opened file
ilSaveTargaF(ILHANDLE File)573 ILuint ilSaveTargaF(ILHANDLE File)
574 {
575 	ILuint Pos;
576 	iSetOutputFile(File);
577 	Pos = itellw();
578 	if (iSaveTargaInternal() == IL_FALSE)
579 		return 0;  // Error occurred
580 	return itellw() - Pos;  // Return the number of bytes written.
581 }
582 
583 
584 //! Writes a Targa to a memory "lump"
ilSaveTargaL(void * Lump,ILuint Size)585 ILuint ilSaveTargaL(void *Lump, ILuint Size)
586 {
587 	ILuint Pos = itellw();
588 	iSetOutputLump(Lump, Size);
589 	if (iSaveTargaInternal() == IL_FALSE)
590 		return 0;  // Error occurred
591 	return itellw() - Pos;  // Return the number of bytes written.
592 }
593 
594 
595 // Internal function used to save the Targa.
iSaveTargaInternal()596 ILboolean iSaveTargaInternal()
597 {
598 	const char	*ID = iGetString(IL_TGA_ID_STRING);
599 	const char	*AuthName = iGetString(IL_TGA_AUTHNAME_STRING);
600 	const char	*AuthComment = iGetString(IL_TGA_AUTHCOMMENT_STRING);
601 	ILubyte 	IDLen = 0, UsePal, Type, PalEntSize;
602 	ILshort 	ColMapStart = 0, PalSize;
603 	ILubyte		Temp;
604 	ILenum		Format;
605 	ILboolean	Compress;
606 	ILuint		RleLen;
607 	ILubyte 	*Rle;
608 	ILpal		*TempPal = NULL;
609 	ILimage 	*TempImage = NULL;
610 	ILuint		ExtOffset, i;
611 	char		*Footer = "TRUEVISION-XFILE.\0";
612 	char		*idString = "Developer's Image Library (DevIL)";
613 	ILuint		Day, Month, Year, Hour, Minute, Second;
614 	char		*TempData;
615 
616 	if (iCurImage == NULL) {
617 		ilSetError(IL_ILLEGAL_OPERATION);
618 		return IL_FALSE;
619 	}
620 
621 	if (iGetInt(IL_TGA_RLE) == IL_TRUE)
622 		Compress = IL_TRUE;
623 	else
624 		Compress = IL_FALSE;
625 
626 	if (ID)
627 		IDLen = (ILubyte)ilCharStrLen(ID);
628 
629 	if (iCurImage->Pal.Palette && iCurImage->Pal.PalSize && iCurImage->Pal.PalType != IL_PAL_NONE)
630 		UsePal = IL_TRUE;
631 	else
632 		UsePal = IL_FALSE;
633 
634 	iwrite(&IDLen, sizeof(ILubyte), 1);
635 	iwrite(&UsePal, sizeof(ILubyte), 1);
636 
637 	Format = iCurImage->Format;
638 	switch (Format) {
639 		case IL_COLOUR_INDEX:
640 			if (Compress)
641 				Type = 9;
642 			else
643 				Type = 1;
644 			break;
645 		case IL_BGR:
646 		case IL_BGRA:
647 			if (Compress)
648 				Type = 10;
649 			else
650 				Type = 2;
651 			break;
652 		case IL_RGB:
653 		case IL_RGBA:
654 			ilSwapColours();
655 			if (Compress)
656 				Type = 10;
657 			else
658 				Type = 2;
659 			break;
660 		case IL_LUMINANCE:
661 			if (Compress)
662 				Type = 11;
663 			else
664 				Type = 3;
665 			break;
666 		default:
667 			// Should convert the types here...
668 			ilSetError(IL_INVALID_VALUE);
669 			ifree(ID);
670 			ifree(AuthName);
671 			ifree(AuthComment);
672 			return IL_FALSE;
673 	}
674 
675 	iwrite(&Type, sizeof(ILubyte), 1);
676 	SaveLittleShort(ColMapStart);
677 
678 	switch (iCurImage->Pal.PalType)
679 	{
680 		case IL_PAL_NONE:
681 			PalSize = 0;
682 			PalEntSize = 0;
683 			break;
684 		case IL_PAL_BGR24:
685 			PalSize = (ILshort)(iCurImage->Pal.PalSize / 3);
686 			PalEntSize = 24;
687 			TempPal = &iCurImage->Pal;
688 			break;
689 
690 		case IL_PAL_RGB24:
691 		case IL_PAL_RGB32:
692 		case IL_PAL_RGBA32:
693 		case IL_PAL_BGR32:
694 		case IL_PAL_BGRA32:
695 			TempPal = iConvertPal(&iCurImage->Pal, IL_PAL_BGR24);
696 			if (TempPal == NULL)
697 				return IL_FALSE;
698 				PalSize = (ILshort)(TempPal->PalSize / 3);
699 			PalEntSize = 24;
700 			break;
701 		default:
702 			ilSetError(IL_INVALID_VALUE);
703 			ifree(ID);
704 			ifree(AuthName);
705 			ifree(AuthComment);
706 			PalSize = 0;
707 			PalEntSize = 0;
708 			return IL_FALSE;
709 	}
710 	SaveLittleShort(PalSize);
711 	iwrite(&PalEntSize, sizeof(ILubyte), 1);
712 
713 	if (iCurImage->Bpc > 1) {
714 		TempImage = iConvertImage(iCurImage, iCurImage->Format, IL_UNSIGNED_BYTE);
715 		if (TempImage == NULL) {
716 			ifree(ID);
717 			ifree(AuthName);
718 			ifree(AuthComment);
719 			return IL_FALSE;
720 		}
721 	}
722 	else {
723 		TempImage = iCurImage;
724 	}
725 
726 	if (TempImage->Origin != IL_ORIGIN_LOWER_LEFT)
727 		TempData = (char*)iGetFlipped(TempImage);
728 	else
729 		TempData = (char*)TempImage->Data;
730 
731 	// Write out the origin stuff.
732 	Temp = 0;
733 	iwrite(&Temp, sizeof(ILshort), 1);
734 	iwrite(&Temp, sizeof(ILshort), 1);
735 
736 	Temp = iCurImage->Bpp << 3;  // Changes to bits per pixel
737 	SaveLittleUShort((ILushort)iCurImage->Width);
738 	SaveLittleUShort((ILushort)iCurImage->Height);
739 	iwrite(&Temp, sizeof(ILubyte), 1);
740 
741 	// Still don't know what exactly this is for...
742 	Temp = 0;
743 	iwrite(&Temp, sizeof(ILubyte), 1);
744 	iwrite(ID, sizeof(char), IDLen);
745 	ifree(ID);
746 	//iwrite(ID, sizeof(ILbyte), IDLen - sizeof(ILuint));
747 	//iwrite(&iCurImage->Depth, sizeof(ILuint), 1);
748 
749 	// Write out the colormap
750 	if (UsePal)
751 		iwrite(TempPal->Palette, sizeof(ILubyte), TempPal->PalSize);
752 	// else do nothing
753 
754 	if (!Compress)
755 		iwrite(TempData, sizeof(ILubyte), TempImage->SizeOfData);
756 	else {
757 		Rle = (ILubyte*)ialloc(TempImage->SizeOfData + TempImage->SizeOfData / 2 + 1);	// max
758 		if (Rle == NULL) {
759 			ifree(AuthName);
760 			ifree(AuthComment);
761 			return IL_FALSE;
762 		}
763 		RleLen = ilRleCompress((unsigned char*)TempData, TempImage->Width, TempImage->Height,
764 		                       TempImage->Depth, TempImage->Bpp, Rle, IL_TGACOMP, NULL);
765 
766 		iwrite(Rle, 1, RleLen);
767 		ifree(Rle);
768 	}
769 
770 	// Write the extension area.
771 	ExtOffset = itellw();
772 	SaveLittleUShort(495);	// Number of bytes in the extension area (TGA 2.0 spec)
773 	iwrite(AuthName, 1, ilCharStrLen(AuthName));
774 	ipad(41 - ilCharStrLen(AuthName));
775 	iwrite(AuthComment, 1, ilCharStrLen(AuthComment));
776 	ipad(324 - ilCharStrLen(AuthComment));
777 	ifree(AuthName);
778 	ifree(AuthComment);
779 
780 	// Write time/date
781 	iGetDateTime(&Month, &Day, &Year, &Hour, &Minute, &Second);
782 	SaveLittleUShort((ILushort)Month);
783 	SaveLittleUShort((ILushort)Day);
784 	SaveLittleUShort((ILushort)Year);
785 	SaveLittleUShort((ILushort)Hour);
786 	SaveLittleUShort((ILushort)Minute);
787 	SaveLittleUShort((ILushort)Second);
788 
789 	for (i = 0; i < 6; i++) {  // Time created
790 		SaveLittleUShort(0);
791 	}
792 	for (i = 0; i < 41; i++) {	// Job name/ID
793 		iputc(0);
794 	}
795 	for (i = 0; i < 3; i++) {  // Job time
796 		SaveLittleUShort(0);
797 	}
798 
799 	iwrite(idString, 1, ilCharStrLen(idString));	// Software ID
800 	for (i = 0; i < 41 - ilCharStrLen(idString); i++) {
801 		iputc(0);
802 	}
803 	SaveLittleUShort(IL_VERSION);  // Software version
804 	iputc(' ');  // Release letter (not beta anymore, so use a space)
805 
806 	SaveLittleUInt(0);	// Key colour
807 	SaveLittleUInt(0);	// Pixel aspect ratio
808 	SaveLittleUInt(0);	// Gamma correction offset
809 	SaveLittleUInt(0);	// Colour correction offset
810 	SaveLittleUInt(0);	// Postage stamp offset
811 	SaveLittleUInt(0);	// Scan line offset
812 	iputc(3);  // Attributes type
813 
814 	// Write the footer.
815 	SaveLittleUInt(ExtOffset);	// No extension area
816 	SaveLittleUInt(0);	// No developer directory
817 	iwrite(Footer, 1, ilCharStrLen(Footer));
818 
819 	if (TempImage->Origin != IL_ORIGIN_LOWER_LEFT) {
820 		ifree(TempData);
821 	}
822 	if (Format == IL_RGB || Format == IL_RGBA) {
823 		ilSwapColours();
824 	}
825 
826 	if (TempPal != &iCurImage->Pal && TempPal != NULL) {
827 		ifree(TempPal->Palette);
828 		ifree(TempPal);
829 	}
830 
831 	if (TempImage != iCurImage)
832 		ilCloseImage(TempImage);
833 
834 	return IL_TRUE;
835 }
836 
837 
838 // Only to be called by ilDetermineSize.  Returns the buffer size needed to save the
839 //  current image as a Targa file.
iTargaSize(void)840 ILuint iTargaSize(void)
841 {
842 	ILuint	Size, Bpp;
843 	ILubyte	IDLen = 0;
844 	const char	*ID = iGetString(IL_TGA_ID_STRING);
845 	const char	*AuthName = iGetString(IL_TGA_AUTHNAME_STRING);
846 	const char	*AuthComment = iGetString(IL_TGA_AUTHCOMMENT_STRING);
847 
848 	//@TODO: Support color indexed images.
849 	if (iGetInt(IL_TGA_RLE) == IL_TRUE || iCurImage->Format == IL_COLOUR_INDEX) {
850 		// Use the slower method, since we are using compression.  We do a "fake" write.
851 		ilSaveTargaL(NULL, 0);
852 	}
853 
854 	if (ID)
855 		IDLen = (ILubyte)ilCharStrLen(ID);
856 
857 	Size = 18 + IDLen;  // Header + ID
858 
859 	// Bpp may not be iCurImage->Bpp.
860 	switch (iCurImage->Format)
861 	{
862 		case IL_BGR:
863 		case IL_RGB:
864 			Bpp = 3;
865 			break;
866 		case IL_BGRA:
867 		case IL_RGBA:
868 			Bpp = 4;
869 			break;
870 		case IL_LUMINANCE:
871 			Bpp = 1;
872 			break;
873 		default:  //@TODO: Do not know what to do with the others yet.
874 			return 0;
875 	}
876 
877 	Size += iCurImage->Width * iCurImage->Height * Bpp;
878 	Size += 532;  // Size of the extension area
879 
880 	return Size;
881 }
882 
883 
884 /*// Makes a neat string to go into the id field of the .tga
885 void iMakeString(char *Str)
886 {
887 	char		*PSG = "Generated by Developer's Image Library: ";
888 	char		TimeStr[255];
889 
890 	time_t		Time;
891 	struct tm	*CurTime;
892 
893 	time(&Time);
894 #ifdef _WIN32
895 	_tzset();
896 #endif
897 	CurTime = localtime(&Time);
898 
899 	strftime(TimeStr, 255 - ilCharStrLen(PSG), "%#c (%z)", CurTime);
900 	//strftime(TimeStr, 255 - ilCharStrLen(PSG), "%C (%Z)", CurTime);
901 	sprintf(Str, "%s%s", PSG, TimeStr);
902 
903 	return;
904 }*/
905 
906 
907 //changed name to iGetDateTime on 20031221 to fix bug 830196
iGetDateTime(ILuint * Month,ILuint * Day,ILuint * Yr,ILuint * Hr,ILuint * Min,ILuint * Sec)908 void iGetDateTime(ILuint *Month, ILuint *Day, ILuint *Yr, ILuint *Hr, ILuint *Min, ILuint *Sec)
909 {
910 #ifdef DJGPP
911 	struct date day;
912 	struct time curtime;
913 
914 	gettime(&curtime);
915 	getdate(&day);
916 
917 	*Month = day.da_mon;
918 	*Day = day.da_day;
919 	*Yr = day.da_year;
920 
921 	*Hr = curtime.ti_hour;
922 	*Min = curtime.ti_min;
923 	*Sec = curtime.ti_sec;
924 
925 	return;
926 #else
927 
928 #ifdef _WIN32
929 	SYSTEMTIME Time;
930 
931 	GetSystemTime(&Time);
932 
933 	*Month = Time.wMonth;
934 	*Day = Time.wDay;
935 	*Yr = Time.wYear;
936 
937 	*Hr = Time.wHour;
938 	*Min = Time.wMinute;
939 	*Sec = Time.wSecond;
940 
941 	return;
942 #else
943 
944 	*Month = 0;
945 	*Day = 0;
946 	*Yr = 0;
947 
948 	*Hr = 0;
949 	*Min = 0;
950 	*Sec = 0;
951 
952 	return;
953 #endif
954 #endif
955 }
956 
957 
958 #endif//IL_NO_TGA
959