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_sgi.c
8 //
9 // Description: Reads and writes Silicon Graphics Inc. (.sgi) files.
10 //
11 //-----------------------------------------------------------------------------
12 
13 #include "il_internal.h"
14 #ifndef IL_NO_SGI
15 #include "il_sgi.h"
16 #include "il_manip.h"
17 #include <limits.h>
18 
19 static char *FName = NULL;
20 
21 /*----------------------------------------------------------------------------*/
22 
23 /*! Checks if the file specified in FileName is a valid .sgi file. */
ilIsValidSgi(ILconst_string FileName)24 ILboolean ilIsValidSgi(ILconst_string FileName)
25 {
26 	ILHANDLE	SgiFile;
27 	ILboolean	bSgi = IL_FALSE;
28 
29 	if (!iCheckExtension(FileName, IL_TEXT("sgi"))) {
30 		ilSetError(IL_INVALID_EXTENSION);
31 		return bSgi;
32 	}
33 
34 	FName = (char*)FileName;
35 
36 	SgiFile = iopenr(FileName);
37 	if (SgiFile == NULL) {
38 		ilSetError(IL_COULD_NOT_OPEN_FILE);
39 		return bSgi;
40 	}
41 
42 	bSgi = ilIsValidSgiF(SgiFile);
43 	icloser(SgiFile);
44 
45 	return bSgi;
46 }
47 
48 /*----------------------------------------------------------------------------*/
49 
50 /*! Checks if the ILHANDLE contains a valid .sgi file at the current position.*/
ilIsValidSgiF(ILHANDLE File)51 ILboolean ilIsValidSgiF(ILHANDLE File)
52 {
53 	ILuint		FirstPos;
54 	ILboolean	bRet;
55 
56 	iSetInputFile(File);
57 	FirstPos = itell();
58 	bRet = iIsValidSgi();
59 	iseek(FirstPos, IL_SEEK_SET);
60 
61 	return bRet;
62 }
63 
64 /*----------------------------------------------------------------------------*/
65 
66 //! Checks if Lump is a valid .sgi lump.
ilIsValidSgiL(const void * Lump,ILuint Size)67 ILboolean ilIsValidSgiL(const void *Lump, ILuint Size)
68 {
69 	FName = NULL;
70 	iSetInputLump(Lump, Size);
71 	return iIsValidSgi();
72 }
73 
74 /*----------------------------------------------------------------------------*/
75 
76 // Internal function used to get the .sgi header from the current file.
iGetSgiHead(iSgiHeader * Header)77 ILboolean iGetSgiHead(iSgiHeader *Header)
78 {
79 	Header->MagicNum = GetBigUShort();
80 	Header->Storage = (ILbyte)igetc();
81 	Header->Bpc = (ILbyte)igetc();
82 	Header->Dim = GetBigUShort();
83 	Header->XSize = GetBigUShort();
84 	Header->YSize = GetBigUShort();
85 	Header->ZSize = GetBigUShort();
86 	Header->PixMin = GetBigInt();
87 	Header->PixMax = GetBigInt();
88 	Header->Dummy1 = GetBigInt();
89 	iread(Header->Name, 1, 80);
90 	Header->ColMap = GetBigInt();
91 	iread(Header->Dummy, 1, 404);
92 
93 	return IL_TRUE;
94 }
95 
96 /*----------------------------------------------------------------------------*/
97 
98 /* Internal function to get the header and check it. */
iIsValidSgi()99 ILboolean iIsValidSgi()
100 {
101 	iSgiHeader	Head;
102 
103 	if (!iGetSgiHead(&Head))
104 		return IL_FALSE;
105 	iseek(-(ILint)sizeof(iSgiHeader), IL_SEEK_CUR);  // Go ahead and restore to previous state
106 
107 	return iCheckSgi(&Head);
108 }
109 
110 /*----------------------------------------------------------------------------*/
111 
112 /* Internal function used to check if the HEADER is a valid .sgi header. */
iCheckSgi(iSgiHeader * Header)113 ILboolean iCheckSgi(iSgiHeader *Header)
114 {
115 	if (Header->MagicNum != SGI_MAGICNUM)
116 		return IL_FALSE;
117 	if (Header->Storage != SGI_RLE && Header->Storage != SGI_VERBATIM)
118 		return IL_FALSE;
119 	if (Header->Bpc == 0 || Header->Dim == 0)
120 		return IL_FALSE;
121 	if (Header->XSize == 0 || Header->YSize == 0 || Header->ZSize == 0)
122 		return IL_FALSE;
123 
124 	return IL_TRUE;
125 }
126 
127 /*----------------------------------------------------------------------------*/
128 
129 /*! Reads a SGI file */
ilLoadSgi(ILconst_string FileName)130 ILboolean ilLoadSgi(ILconst_string FileName)
131 {
132 	ILHANDLE	SgiFile;
133 	ILboolean	bSgi = IL_FALSE;
134 
135 	SgiFile = iopenr(FileName);
136 	if (SgiFile == NULL) {
137 		ilSetError(IL_COULD_NOT_OPEN_FILE);
138 		return bSgi;
139 	}
140 
141 	bSgi = ilLoadSgiF(SgiFile);
142 	icloser(SgiFile);
143 
144 	return bSgi;
145 }
146 
147 /*----------------------------------------------------------------------------*/
148 
149 /*! Reads an already-opened SGI file */
ilLoadSgiF(ILHANDLE File)150 ILboolean ilLoadSgiF(ILHANDLE File)
151 {
152 	ILuint		FirstPos;
153 	ILboolean	bRet;
154 
155 	iSetInputFile(File);
156 	FirstPos = itell();
157 	bRet = iLoadSgiInternal();
158 	iseek(FirstPos, IL_SEEK_SET);
159 
160 	return bRet;
161 }
162 
163 /*----------------------------------------------------------------------------*/
164 
165 /*! Reads from a memory "lump" that contains a SGI image */
ilLoadSgiL(const void * Lump,ILuint Size)166 ILboolean ilLoadSgiL(const void *Lump, ILuint Size)
167 {
168 	iSetInputLump(Lump, Size);
169 	return iLoadSgiInternal();
170 }
171 
172 /*----------------------------------------------------------------------------*/
173 
174 /* Internal function used to load the SGI image */
iLoadSgiInternal()175 ILboolean iLoadSgiInternal()
176 {
177 	iSgiHeader	Header;
178 	ILboolean	bSgi;
179 
180 	if (iCurImage == NULL) {
181 		ilSetError(IL_ILLEGAL_OPERATION);
182 		return IL_FALSE;
183 	}
184 
185 	if (!iGetSgiHead(&Header))
186 		return IL_FALSE;
187 	if (!iCheckSgi(&Header)) {
188 		ilSetError(IL_INVALID_FILE_HEADER);
189 		return IL_FALSE;
190 	}
191 
192 	// Bugfix for #1060946.
193 	//  The ZSize should never really be 2 by the specifications.  Some
194 	//  application is outputting these, and it looks like the ZSize
195 	//  should really be 1.
196 	if (Header.ZSize == 2)
197 		Header.ZSize = 1;
198 
199 	if (Header.Storage == SGI_RLE) {  // RLE
200 		bSgi = iReadRleSgi(&Header);
201 	}
202 	else {  // Non-RLE  //(Header.Storage == SGI_VERBATIM)
203 		bSgi = iReadNonRleSgi(&Header);
204 	}
205 
206 	if (!bSgi)
207 		return IL_FALSE;
208 	return ilFixImage();
209 }
210 
211 /*----------------------------------------------------------------------------*/
212 
iReadRleSgi(iSgiHeader * Head)213 ILboolean iReadRleSgi(iSgiHeader *Head)
214 {
215 	#if BYTE_ORDER == LITTLE_ENDIAN
216 	ILuint ixTable;
217 	#endif
218 	ILuint 		ChanInt = 0;
219 	ILuint  	ixPlane, ixHeight,ixPixel, RleOff, RleLen;
220 	ILuint		*OffTable=NULL, *LenTable=NULL, TableSize, Cur;
221 	ILubyte		**TempData=NULL;
222 
223 	if (!iNewSgi(Head))
224 		return IL_FALSE;
225 
226 	TableSize = Head->YSize * Head->ZSize;
227 	OffTable = (ILuint*)ialloc(TableSize * sizeof(ILuint));
228 	LenTable = (ILuint*)ialloc(TableSize * sizeof(ILuint));
229 	if (OffTable == NULL || LenTable == NULL)
230 		goto cleanup_error;
231 	if (iread(OffTable, TableSize * sizeof(ILuint), 1) != 1)
232 		goto cleanup_error;
233 	if (iread(LenTable, TableSize * sizeof(ILuint), 1) != 1)
234 		goto cleanup_error;
235 
236 #if BYTE_ORDER == LITTLE_ENDIAN
237 	// Fix the offset/len table (it's big endian format)
238 	for (ixTable = 0; ixTable < TableSize; ixTable++) {
239 		iSwapUInt(OffTable + ixTable);
240 		iSwapUInt(LenTable + ixTable);
241 	}
242 #endif //__LITTLE_ENDIAN__
243 
244 	// We have to create a temporary buffer for the image, because SGI
245 	//	images are plane-separated.
246 	TempData = (ILubyte**)ialloc(Head->ZSize * sizeof(ILubyte*));
247 	if (TempData == NULL)
248 		goto cleanup_error;
249 	imemclear(TempData, Head->ZSize * sizeof(ILubyte*));  // Just in case ialloc fails then cleanup_error.
250 	for (ixPlane = 0; ixPlane < Head->ZSize; ixPlane++) {
251 		TempData[ixPlane] = (ILubyte*)ialloc(Head->XSize * Head->YSize * Head->Bpc);
252 		if (TempData[ixPlane] == NULL)
253 			goto cleanup_error;
254 	}
255 
256 	// Read the Planes into the temporary memory
257 	for (ixPlane = 0; ixPlane < Head->ZSize; ixPlane++) {
258 		for (ixHeight = 0, Cur = 0;	ixHeight < Head->YSize;
259 			ixHeight++, Cur += Head->XSize * Head->Bpc) {
260 
261 			RleOff = OffTable[ixHeight + ixPlane * Head->YSize];
262 			RleLen = LenTable[ixHeight + ixPlane * Head->YSize];
263 
264 			// Seeks to the offset table position
265 			iseek(RleOff, IL_SEEK_SET);
266 			if (iGetScanLine((TempData[ixPlane]) + (ixHeight * Head->XSize * Head->Bpc),
267 				Head, RleLen) != Head->XSize * Head->Bpc) {
268 					ilSetError(IL_ILLEGAL_FILE_VALUE);
269 					goto cleanup_error;
270 			}
271 		}
272 	}
273 
274 	// DW: Removed on 05/25/2002.
275 	/*// Check if an alphaplane exists and invert it
276 	if (Head->ZSize == 4) {
277 		for (ixPixel=0; (ILint)ixPixel<Head->XSize * Head->YSize; ixPixel++) {
278  			TempData[3][ixPixel] = TempData[3][ixPixel] ^ 255;
279  		}
280 	}*/
281 
282 	// Assemble the image from its planes
283 	for (ixPixel = 0; ixPixel < iCurImage->SizeOfData;
284 		ixPixel += Head->ZSize * Head->Bpc, ChanInt += Head->Bpc) {
285 		for (ixPlane = 0; (ILint)ixPlane < Head->ZSize * Head->Bpc;	ixPlane += Head->Bpc) {
286 			iCurImage->Data[ixPixel + ixPlane] = TempData[ixPlane][ChanInt];
287 			if (Head->Bpc == 2)
288 				iCurImage->Data[ixPixel + ixPlane + 1] = TempData[ixPlane][ChanInt + 1];
289 		}
290 	}
291 
292 	#if BYTE_ORDER == LITTLE_ENDIAN
293 	if (Head->Bpc == 2)
294 		sgiSwitchData(iCurImage->Data, iCurImage->SizeOfData);
295 	#endif
296 
297 	ifree(OffTable);
298 	ifree(LenTable);
299 
300 	for (ixPlane = 0; ixPlane < Head->ZSize; ixPlane++) {
301 		ifree(TempData[ixPlane]);
302 	}
303 	ifree(TempData);
304 
305 	return IL_TRUE;
306 
307 cleanup_error:
308 	ifree(OffTable);
309 	ifree(LenTable);
310 	if (TempData) {
311 		for (ixPlane = 0; ixPlane < Head->ZSize; ixPlane++) {
312 			ifree(TempData[ixPlane]);
313 		}
314 		ifree(TempData);
315 	}
316 
317 	return IL_FALSE;
318 }
319 
320 /*----------------------------------------------------------------------------*/
321 
iGetScanLine(ILubyte * ScanLine,iSgiHeader * Head,ILuint Length)322 ILint iGetScanLine(ILubyte *ScanLine, iSgiHeader *Head, ILuint Length)
323 {
324 	ILushort Pixel, Count;  // For current pixel
325 	ILuint	 BppRead = 0, CurPos = 0, Bps = Head->XSize * Head->Bpc;
326 
327 	while (BppRead < Length && CurPos < Bps)
328 	{
329 		Pixel = 0;
330 		if (iread(&Pixel, Head->Bpc, 1) != 1)
331 			return -1;
332 
333 #if BYTE_ORDER != LITTLE_ENDIAN
334 		iSwapUShort(&Pixel);
335 #endif
336 
337 		if (!(Count = (Pixel & 0x7f)))  // If 0, line ends
338 			return CurPos;
339 		if (Pixel & 0x80) {  // If top bit set, then it is a "run"
340 			if (iread(ScanLine, Head->Bpc, Count) != Count)
341 				return -1;
342 			BppRead += Head->Bpc * Count + Head->Bpc;
343 			ScanLine += Head->Bpc * Count;
344 			CurPos += Head->Bpc * Count;
345 		}
346 		else {
347 			if (iread(&Pixel, Head->Bpc, 1) != 1)
348 				return -1;
349 #if BYTE_ORDER != LITTLE_ENDIAN
350 			iSwapUShort(&Pixel);
351 #endif
352 			if (Head->Bpc == 1) {
353 				while (Count--) {
354 					*ScanLine = (ILubyte)Pixel;
355 					ScanLine++;
356 					CurPos++;
357 				}
358 			}
359 			else {
360 				while (Count--) {
361 					*(ILushort*)ScanLine = Pixel;
362 					ScanLine += 2;
363 					CurPos += 2;
364 				}
365 			}
366 			BppRead += Head->Bpc + Head->Bpc;
367 		}
368 	}
369 
370 	return CurPos;
371 }
372 
373 
374 /*----------------------------------------------------------------------------*/
375 
376 // Much easier to read - just assemble from planes, no decompression
iReadNonRleSgi(iSgiHeader * Head)377 ILboolean iReadNonRleSgi(iSgiHeader *Head)
378 {
379 	ILuint		i, c;
380 	// ILint		ChanInt = 0; Unused
381 	ILint 		ChanSize;
382 	ILboolean	Cache = IL_FALSE;
383 
384 	if (!iNewSgi(Head)) {
385 		return IL_FALSE;
386 	}
387 
388 	if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST) {
389 		Cache = IL_TRUE;
390 		ChanSize = Head->XSize * Head->YSize * Head->Bpc;
391 		iPreCache(ChanSize);
392 	}
393 
394 	for (c = 0; c < iCurImage->Bpp; c++) {
395 		for (i = c; i < iCurImage->SizeOfData; i += iCurImage->Bpp) {
396 			if (iread(iCurImage->Data + i, 1, 1) != 1) {
397 				if (Cache)
398 					iUnCache();
399 				return IL_FALSE;
400 			}
401 		}
402 	}
403 
404 	if (Cache)
405 		iUnCache();
406 
407 	return IL_TRUE;
408 }
409 
410 /*----------------------------------------------------------------------------*/
411 
sgiSwitchData(ILubyte * Data,ILuint SizeOfData)412 void sgiSwitchData(ILubyte *Data, ILuint SizeOfData)
413 {
414 	ILubyte	Temp;
415 	ILuint	i;
416 	#ifdef ALTIVEC_GCC
417 		i = 0;
418 		union {
419 			vector unsigned char vec;
420 			vector unsigned int load;
421 		}inversion_vector;
422 
423 		inversion_vector.load  = (vector unsigned int)\
424 			{0x01000302,0x05040706,0x09080B0A,0x0D0C0F0E};
425 		while( i <= SizeOfData-16 ) {
426 			vector unsigned char data = vec_ld(i,Data);
427 			vec_perm(data,data,inversion_vector.vec);
428 			vec_st(data,i,Data);
429 			i+=16;
430 		}
431 		SizeOfData -= i;
432 	#endif
433 	for (i = 0; i < SizeOfData; i += 2) {
434 		Temp = Data[i];
435 		Data[i] = Data[i+1];
436 		Data[i+1] = Temp;
437 	}
438 	return;
439 }
440 
441 /*----------------------------------------------------------------------------*/
442 
443 // Just an internal convenience function for reading SGI files
iNewSgi(iSgiHeader * Head)444 ILboolean iNewSgi(iSgiHeader *Head)
445 {
446 	if (!ilTexImage(Head->XSize, Head->YSize, Head->Bpc, (ILubyte)Head->ZSize, 0, IL_UNSIGNED_BYTE, NULL)) {
447 		return IL_FALSE;
448 	}
449 	iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
450 
451 	switch (Head->ZSize)
452 	{
453 		case 1:
454 			iCurImage->Format = IL_LUMINANCE;
455 			break;
456 		/*case 2:
457 			iCurImage->Format = IL_LUMINANCE_ALPHA;
458 			break;*/
459 		case 3:
460 			iCurImage->Format = IL_RGB;
461 			break;
462 		case 4:
463 			iCurImage->Format = IL_RGBA;
464 			break;
465 		default:
466 			ilSetError(IL_ILLEGAL_FILE_VALUE);
467 			return IL_FALSE;
468 	}
469 
470 	switch (Head->Bpc)
471 	{
472 		case 1:
473 			if (Head->PixMin < 0)
474 				iCurImage->Type = IL_BYTE;
475 			else
476 				iCurImage->Type = IL_UNSIGNED_BYTE;
477 			break;
478 		case 2:
479 			if (Head->PixMin < 0)
480 				iCurImage->Type = IL_SHORT;
481 			else
482 				iCurImage->Type = IL_UNSIGNED_SHORT;
483 			break;
484 		default:
485 			ilSetError(IL_ILLEGAL_FILE_VALUE);
486 			return IL_FALSE;
487 	}
488 
489 	iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
490 
491 	return IL_TRUE;
492 }
493 
494 /*----------------------------------------------------------------------------*/
495 
496 //! Writes a SGI file
ilSaveSgi(const ILstring FileName)497 ILboolean ilSaveSgi(const ILstring FileName)
498 {
499 	ILHANDLE	SgiFile;
500 	ILuint		SgiSize;
501 
502 	if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
503 		if (iFileExists(FileName)) {
504 			ilSetError(IL_FILE_ALREADY_EXISTS);
505 			return IL_FALSE;
506 		}
507 	}
508 
509 	SgiFile = iopenw(FileName);
510 	if (SgiFile == NULL) {
511 		ilSetError(IL_COULD_NOT_OPEN_FILE);
512 		return IL_FALSE;
513 	}
514 
515 	SgiSize = ilSaveSgiF(SgiFile);
516 	iclosew(SgiFile);
517 
518 	if (SgiSize == 0)
519 		return IL_FALSE;
520 	return IL_TRUE;
521 }
522 
523 
524 //! Writes a Sgi to an already-opened file
ilSaveSgiF(ILHANDLE File)525 ILuint ilSaveSgiF(ILHANDLE File)
526 {
527 	ILuint Pos;
528 	iSetOutputFile(File);
529 	Pos = itellw();
530 	if (iSaveSgiInternal() == IL_FALSE)
531 		return 0;  // Error occurred
532 	return itellw() - Pos;  // Return the number of bytes written.
533 }
534 
535 
536 //! Writes a Sgi to a memory "lump"
ilSaveSgiL(void * Lump,ILuint Size)537 ILuint ilSaveSgiL(void *Lump, ILuint Size)
538 {
539 	ILuint Pos;
540 	iSetOutputLump(Lump, Size);
541 	Pos = itellw();
542 	if (iSaveSgiInternal() == IL_FALSE)
543 		return 0;  // Error occurred
544 	return itellw() - Pos;  // Return the number of bytes written.
545 }
546 
547 
DetermineSgiType(ILenum Type)548 ILenum DetermineSgiType(ILenum Type)
549 {
550 	if (Type > IL_UNSIGNED_SHORT) {
551 		if (iCurImage->Type == IL_INT)
552 			return IL_SHORT;
553 		return IL_UNSIGNED_SHORT;
554 	}
555 	return Type;
556 }
557 
558 /*----------------------------------------------------------------------------*/
559 
560 // Rle does NOT work yet.
561 
562 // Internal function used to save the Sgi.
iSaveSgiInternal()563 ILboolean iSaveSgiInternal()
564 {
565 	ILuint		i, c;
566 	ILboolean	Compress;
567 	ILimage		*Temp = iCurImage;
568 	ILubyte		*TempData;
569 
570 	if (iCurImage == NULL) {
571 		ilSetError(IL_ILLEGAL_OPERATION);
572 		return IL_FALSE;
573 	}
574 
575 	if (iCurImage->Format != IL_LUMINANCE
576 	    //while the sgi spec doesn't directly forbid rgb files with 2
577 	    //channels, they are quite uncommon and most apps don't support
578 	    //them. so convert lum_a images to rgba before writing.
579 	    //&& iCurImage->Format != IL_LUMINANCE_ALPHA
580 	    && iCurImage->Format != IL_RGB
581 	    && iCurImage->Format != IL_RGBA) {
582 		if (iCurImage->Format == IL_BGRA || iCurImage->Format == IL_LUMINANCE_ALPHA)
583 			Temp = iConvertImage(iCurImage, IL_RGBA, DetermineSgiType(iCurImage->Type));
584 		else
585 			Temp = iConvertImage(iCurImage, IL_RGB, DetermineSgiType(iCurImage->Type));
586 	}
587 	else if (iCurImage->Type > IL_UNSIGNED_SHORT) {
588 		Temp = iConvertImage(iCurImage, iCurImage->Format, DetermineSgiType(iCurImage->Type));
589 	}
590 
591 	//compression of images with 2 bytes per channel doesn't work yet
592 	Compress = iGetInt(IL_SGI_RLE) && Temp->Bpc == 1;
593 
594 	if (Temp == NULL)
595 		return IL_FALSE;
596 
597 	SaveBigUShort(SGI_MAGICNUM);  // 'Magic' number
598 	if (Compress)
599 		iputc(1);
600 	else
601 		iputc(0);
602 
603 	if (Temp->Type == IL_UNSIGNED_BYTE)
604 		iputc(1);
605 	else if (Temp->Type == IL_UNSIGNED_SHORT)
606 		iputc(2);
607 	// Need to error here if not one of the two...
608 
609 	if (Temp->Format == IL_LUMINANCE || Temp->Format == IL_COLOUR_INDEX)
610 		SaveBigUShort(2);
611 	else
612 		SaveBigUShort(3);
613 
614 	SaveBigUShort((ILushort)Temp->Width);
615 	SaveBigUShort((ILushort)Temp->Height);
616 	SaveBigUShort((ILushort)Temp->Bpp);
617 
618 	switch (Temp->Type)
619 	{
620 		case IL_BYTE:
621 			SaveBigInt(SCHAR_MIN);	// Minimum pixel value
622 			SaveBigInt(SCHAR_MAX);	// Maximum pixel value
623 			break;
624 		case IL_UNSIGNED_BYTE:
625 			SaveBigInt(0);			// Minimum pixel value
626 			SaveBigInt(UCHAR_MAX);	// Maximum pixel value
627 			break;
628 		case IL_SHORT:
629 			SaveBigInt(SHRT_MIN);	// Minimum pixel value
630 			SaveBigInt(SHRT_MAX);	// Maximum pixel value
631 			break;
632 		case IL_UNSIGNED_SHORT:
633 			SaveBigInt(0);			// Minimum pixel value
634 			SaveBigInt(USHRT_MAX);	// Maximum pixel value
635 			break;
636 	}
637 
638 	SaveBigInt(0);  // Dummy value
639 
640 	if (FName) {
641 		c = ilCharStrLen(FName);
642 		c = c < 79 ? 79 : c;
643 		iwrite(FName, 1, c);
644 		c = 80 - c;
645 		for (i = 0; i < c; i++) {
646 			iputc(0);
647 		}
648 	}
649 	else {
650 		for (i = 0; i < 80; i++) {
651 			iputc(0);
652 		}
653 	}
654 
655 	SaveBigUInt(0);  // Colormap
656 
657 	// Padding
658 	for (i = 0; i < 101; i++) {
659 		SaveLittleInt(0);
660 	}
661 
662 
663 	if (iCurImage->Origin == IL_ORIGIN_UPPER_LEFT) {
664 		TempData = iGetFlipped(Temp);
665 		if (TempData == NULL) {
666 			if (Temp!= iCurImage)
667 				ilCloseImage(Temp);
668 			return IL_FALSE;
669 		}
670 	}
671 	else {
672 		TempData = Temp->Data;
673 	}
674 
675 
676 	if (!Compress) {
677 		for (c = 0; c < Temp->Bpp; c++) {
678 			for (i = c; i < Temp->SizeOfData; i += Temp->Bpp) {
679 				iputc(TempData[i]);  // Have to save each colour plane separately.
680 			}
681 		}
682 	}
683 	else {
684 		iSaveRleSgi(TempData, Temp->Width, Temp->Height, Temp->Bpp, Temp->Bps);
685 	}
686 
687 
688 	if (TempData != Temp->Data)
689 		ifree(TempData);
690 	if (Temp != iCurImage)
691 		ilCloseImage(Temp);
692 
693 	return IL_TRUE;
694 }
695 
696 /*----------------------------------------------------------------------------*/
697 
iSaveRleSgi(ILubyte * Data,ILuint w,ILuint h,ILuint numChannels,ILuint bps)698 ILboolean iSaveRleSgi(ILubyte *Data, ILuint w, ILuint h, ILuint numChannels,
699 		ILuint bps)
700 {
701 	//works only for sgi files with only 1 bpc
702 
703 	ILuint	c, i, y, j;
704 	ILubyte	*ScanLine = NULL, *CompLine = NULL;
705 	ILuint	*StartTable = NULL, *LenTable = NULL;
706 	ILuint	TableOff, DataOff = 0;
707 
708 	ScanLine = (ILubyte*)ialloc(w);
709 	CompLine = (ILubyte*)ialloc(w * 2 + 1);  // Absolute worst case.
710 	StartTable = (ILuint*)ialloc(h * numChannels * sizeof(ILuint));
711 	LenTable = (ILuint*)icalloc(h * numChannels, sizeof(ILuint));
712 	if (!ScanLine || !CompLine || !StartTable || !LenTable) {
713 		ifree(ScanLine);
714 		ifree(CompLine);
715 		ifree(StartTable);
716 		ifree(LenTable);
717 		return IL_FALSE;
718 	}
719 
720 	// These just contain dummy values at this point.
721 	TableOff = itellw();
722 	iwrite(StartTable, sizeof(ILuint), h * numChannels);
723 	iwrite(LenTable, sizeof(ILuint), h * numChannels);
724 
725 	DataOff = itellw();
726 	for (c = 0; c < numChannels; c++) {
727 		for (y = 0; y < h; y++) {
728 			i = y * bps + c;
729 			for (j = 0; j < w; j++, i += numChannels) {
730 				ScanLine[j] = Data[i];
731 			}
732 
733 			ilRleCompressLine(ScanLine, w, 1, CompLine, LenTable + h * c + y, IL_SGICOMP);
734 			iwrite(CompLine, 1, *(LenTable + h * c + y));
735 		}
736 	}
737 
738 	iseekw(TableOff, IL_SEEK_SET);
739 
740 	j = h * numChannels;
741 	for (y = 0; y < j; y++) {
742 		StartTable[y] = DataOff;
743 		DataOff += LenTable[y];
744 #if BYTE_ORDER == LITTLE_ENDIAN
745 		iSwapUInt(&StartTable[y]);
746  		iSwapUInt(&LenTable[y]);
747 #endif
748 	}
749 
750 	iwrite(StartTable, sizeof(ILuint), h * numChannels);
751 	iwrite(LenTable, sizeof(ILuint), h * numChannels);
752 
753 	ifree(ScanLine);
754 	ifree(CompLine);
755 	ifree(StartTable);
756 	ifree(LenTable);
757 
758 	return IL_TRUE;
759 }
760 
761 /*----------------------------------------------------------------------------*/
762 
763 #endif//IL_NO_SGI
764