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