1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2009 by Denton Woods
5 // Last modified: 01/06/2009
6 //
7 // Filename: src-IL/src/il_sun.c
8 //
9 // Description: Reads from a Sun RAS file.  Specifications available from
10 //				http://www.fileformat.info/format/sunraster/egff.htm.
11 //
12 //-----------------------------------------------------------------------------
13 
14 
15 #include "il_internal.h"
16 #ifndef IL_NO_SUN
17 #include "il_bits.h"
18 
19 ILboolean	iLoadSunInternal(void);
20 ILboolean	iIsValidSun(void);
21 ILuint		iSunGetRle(ILubyte *Data, ILuint Length);
22 
23 typedef struct SUNHEAD
24 {
25 	ILuint MagicNumber;      // Magic (identification) number
26 	ILuint Width;            // Width of image in pixels
27 	ILuint Height;           // Height of image in pixels
28 	ILuint Depth;            // Number of bits per pixel
29 	ILuint Length;           // Size of image data in bytes
30 	ILuint Type;             // Type of raster file
31 	ILuint ColorMapType;     // Type of color map
32 	ILuint ColorMapLength;   // Size of the color map in bytes
33 } SUNHEAD;
34 
35 // Data storage types
36 #define IL_SUN_OLD		0x00
37 #define IL_SUN_STANDARD	0x01
38 #define IL_SUN_BYTE_ENC	0x02
39 #define IL_SUN_RGB		0x03
40 #define IL_SUN_TIFF		0x04
41 #define IL_SUN_IFF		0x05
42 #define IL_SUN_EXPER	0xFFFF  // Experimental, not supported.
43 
44 // Colormap types
45 #define IL_SUN_NO_MAP	0x00
46 #define IL_SUN_RGB_MAP	0x01
47 #define IL_SUN_RAW_MAP	0x02
48 
49 
50 //! Checks if the file specified in FileName is a valid Sun file.
ilIsValidSun(ILconst_string FileName)51 ILboolean ilIsValidSun(ILconst_string FileName)
52 {
53 	ILHANDLE	SunFile;
54 	ILboolean	bSun = IL_FALSE;
55 
56 	if (!iCheckExtension(FileName, IL_TEXT("sun")) && !iCheckExtension(FileName, IL_TEXT("ras")) &&
57 		!iCheckExtension(FileName, IL_TEXT("im1")) && !iCheckExtension(FileName, IL_TEXT("im8")) &&
58 		!iCheckExtension(FileName, IL_TEXT("im24")) && !iCheckExtension(FileName, IL_TEXT("im32")) &&
59 		!iCheckExtension(FileName, IL_TEXT("rs"))) {  // Lots of names possible...
60 		ilSetError(IL_INVALID_EXTENSION);
61 		return bSun;
62 	}
63 
64 	SunFile = iopenr(FileName);
65 	if (SunFile == NULL) {
66 		ilSetError(IL_COULD_NOT_OPEN_FILE);
67 		return bSun;
68 	}
69 
70 	bSun = ilIsValidSunF(SunFile);
71 	icloser(SunFile);
72 
73 	return bSun;
74 }
75 
76 
77 //! Checks if the ILHANDLE contains a valid Sun file at the current position.
ilIsValidSunF(ILHANDLE File)78 ILboolean ilIsValidSunF(ILHANDLE File)
79 {
80 	ILuint		FirstPos;
81 	ILboolean	bRet;
82 
83 	iSetInputFile(File);
84 	FirstPos = itell();
85 	bRet = iIsValidSun();
86 	iseek(FirstPos, IL_SEEK_SET);
87 
88 	return bRet;
89 }
90 
91 
92 //! Checks if Lump is a valid Sun lump.
ilIsValidSunL(const void * Lump,ILuint Size)93 ILboolean ilIsValidSunL(const void *Lump, ILuint Size)
94 {
95 	iSetInputLump(Lump, Size);
96 	return iIsValidSun();
97 }
98 
99 
100 // Internal function used to get the Sun header from the current file.
iGetSunHead(SUNHEAD * Header)101 ILboolean iGetSunHead(SUNHEAD *Header)
102 {
103 	Header->MagicNumber = GetBigUInt();
104 	Header->Width = GetBigUInt();
105 	Header->Height = GetBigUInt();
106 	Header->Depth = GetBigUInt();
107 	Header->Length = GetBigUInt();
108 	Header->Type = GetBigUInt();
109 	Header->ColorMapType = GetBigUInt();
110 	Header->ColorMapLength = GetBigUInt();
111 
112 	return IL_TRUE;
113 }
114 
115 
116 // Internal function used to check if the HEADER is a valid SUN header.
iCheckSun(SUNHEAD * Header)117 ILboolean iCheckSun(SUNHEAD *Header)
118 {
119 	if (Header->MagicNumber != 0x59A66A95)  // Magic number is always 0x59A66A95.
120 		return IL_FALSE;
121 	if (Header->Width == 0 || Header->Height == 0)  // 0 dimensions are meaningless.
122 		return IL_FALSE;
123 	// These are the only valid depths that I know of.
124 	if (Header->Depth != 1 && Header->Depth != 8 && Header->Depth != 24 && Header->Depth != 32)
125 		return IL_FALSE;
126 	if (Header->Type > IL_SUN_RGB)  //@TODO: Support further types.
127 		return IL_FALSE;
128 	if (Header->ColorMapType > IL_SUN_RGB_MAP)  //@TODO: Find out more about raw map.
129 		return IL_FALSE;
130 	// Color map cannot be 0 if there is a map indicated.
131 	if (Header->ColorMapType > IL_SUN_NO_MAP && Header->ColorMapLength == 0)
132 		return IL_FALSE;
133 	//@TODO: These wouldn't make sense.  Are they valid somehow?  Find out...
134 	if ((Header->Depth == 1 || Header->Depth == 32) && Header->Type == IL_SUN_BYTE_ENC)
135 		return IL_FALSE;
136 
137 	return IL_TRUE;
138 }
139 
140 
141 // Internal function to get the header and check it.
iIsValidSun()142 ILboolean iIsValidSun()
143 {
144 	SUNHEAD Head;
145 
146 	if (!iGetSunHead(&Head))
147 		return IL_FALSE;
148 	iseek(-(ILint)sizeof(SUNHEAD), IL_SEEK_CUR);
149 
150 	return iCheckSun(&Head);
151 }
152 
153 
154 // Reads a Sun file
ilLoadSun(ILconst_string FileName)155 ILboolean ilLoadSun(ILconst_string FileName)
156 {
157 	ILHANDLE	SunFile;
158 	ILboolean	bSun = IL_FALSE;
159 
160 	SunFile = iopenr(FileName);
161 	if (SunFile == NULL) {
162 		ilSetError(IL_COULD_NOT_OPEN_FILE);
163 		return bSun;
164 	}
165 
166 	iSetInputFile(SunFile);
167 
168 	bSun = ilLoadSunF(SunFile);
169 
170 	icloser(SunFile);
171 
172 	return bSun;
173 }
174 
175 
176 //! Reads an already-opened Sun file
ilLoadSunF(ILHANDLE File)177 ILboolean ilLoadSunF(ILHANDLE File)
178 {
179 	ILuint		FirstPos;
180 	ILboolean	bRet;
181 
182 	iSetInputFile(File);
183 	FirstPos = itell();
184 	bRet = iLoadSunInternal();
185 	iseek(FirstPos, IL_SEEK_SET);
186 
187 	return bRet;
188 }
189 
190 
191 //! Reads from a memory "lump" that contains a Sun
ilLoadSunL(const void * Lump,ILuint Size)192 ILboolean ilLoadSunL(const void *Lump, ILuint Size)
193 {
194 	iSetInputLump(Lump, Size);
195 	return iLoadSunInternal();
196 }
197 
198 
iLoadSunInternal(void)199 ILboolean iLoadSunInternal(void)
200 {
201 	SUNHEAD	Header;
202 	BITFILE	*File;
203 	ILuint	i, j, Padding, Offset, BytesRead;
204 	ILubyte	PaddingData[16];
205 
206 	if (iCurImage == NULL) {
207 		ilSetError(IL_ILLEGAL_OPERATION);
208 		return IL_FALSE;
209 	}
210 
211 	//@TODO: Right now, iGetSunHead cannot fail.
212 	if (!iGetSunHead(&Header) || !iCheckSun(&Header)) {
213 		ilSetError(IL_INVALID_FILE_HEADER);
214 		return IL_FALSE;
215 	}
216 
217 	switch (Header.Depth)
218 	{
219 		case 1:  //@TODO: Find a file to test this on.
220 			File = bfile(iGetFile());
221 			if (File == NULL)
222 				return IL_FALSE;
223 
224 			if (!ilTexImage(Header.Width, Header.Height, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL))
225 				return IL_FALSE;
226 			if (Header.ColorMapLength != 0) {
227 				// Data should be an index into the color map, but the color map should only be RGB (6 bytes, 2 entries).
228 				if (Header.ColorMapLength != 6) {
229 					ilSetError(IL_INVALID_FILE_HEADER);
230 					return IL_FALSE;
231 				}
232 			}
233 			iCurImage->Pal.Palette = (ILubyte*)ialloc(6);  // Just need 2 entries in the color map.
234 			if (Header.ColorMapLength == 0) {  // Create the color map
235 				iCurImage->Pal.Palette[0] = 0x00;  // Entry for black
236 				iCurImage->Pal.Palette[1] = 0x00;
237 				iCurImage->Pal.Palette[2] = 0x00;
238 				iCurImage->Pal.Palette[3] = 0xFF;  // Entry for white
239 				iCurImage->Pal.Palette[4] = 0xFF;
240 				iCurImage->Pal.Palette[5] = 0xFF;
241 			}
242 			else {
243 				iread(iCurImage->Pal.Palette, 1, 6);  // Read in the color map.
244 			}
245 			iCurImage->Pal.PalSize = 6;
246 			iCurImage->Pal.PalType = IL_PAL_RGB24;
247 
248 			Padding = (16 - (iCurImage->Width % 16)) % 16;  // Has to be aligned on a 16-bit boundary.  The rest is padding.
249 
250 			// Reads the bits
251 			for (i = 0; i < iCurImage->Height; i++) {
252 				bread(&iCurImage->Data[iCurImage->Width * i], 1, iCurImage->Width, File);
253 				//bseek(File, BitPadding, IL_SEEK_CUR);  //@TODO: This function does not work correctly.
254 				bread(PaddingData, 1, Padding, File);  // Skip padding bits.
255 			}
256 			break;
257 
258 
259 		case 8:
260 			if (Header.ColorMapType == IL_SUN_NO_MAP) {  // Greyscale image
261 				if (!ilTexImage(Header.Width, Header.Height, 1, 1, IL_LUMINANCE, IL_UNSIGNED_BYTE, NULL))
262 					return IL_FALSE;
263 			}
264 			else {  // Colour-mapped image
265 				if (!ilTexImage(Header.Width, Header.Height, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL))
266 					return IL_FALSE;
267 				iCurImage->Pal.Palette = (ILubyte*)ialloc(Header.ColorMapLength);  // Allocate color map.
268 				if (iCurImage->Pal.Palette == NULL)
269 					return IL_FALSE;
270 				if (iread(iCurImage->Pal.Palette, 1, Header.ColorMapLength) != Header.ColorMapLength) {  // Read color map.
271 					ilSetError(IL_FILE_READ_ERROR);
272 					return IL_FALSE;
273 				}
274 
275 				iCurImage->Pal.PalSize = Header.ColorMapLength;
276 				iCurImage->Pal.PalType = IL_PAL_RGB24;
277 			}
278 
279 			if (Header.Type != IL_SUN_BYTE_ENC) {  // Regular uncompressed image data
280 				Padding = (2 - (iCurImage->Bps % 2)) % 2;  // Must be padded on a 16-bit boundary (2 bytes)
281 				for (i = 0; i < Header.Height; i++) {
282 					iread(iCurImage->Data + i * Header.Width, 1, iCurImage->Bps);
283 					if (Padding)  // Only possible for padding to be 0 or 1.
284 						igetc();
285 				}
286 			}
287 			else {  // RLE image data
288 				for (i = 0; i < iCurImage->Height; i++) {
289 					BytesRead = iSunGetRle(iCurImage->Data + iCurImage->Bps * i, iCurImage->Bps);
290 					if (BytesRead % 2)  // Each scanline must be aligned on a 2-byte boundary.
291 						igetc();  // Skip padding
292 				}
293 			}
294 			break;
295 
296 		case 24:
297 			if (Header.ColorMapLength > 0)  // Ignore any possible colormaps.
298 				iseek(Header.ColorMapLength, IL_SEEK_CUR);
299 
300 			if (Header.Type == IL_SUN_RGB) {
301 				if (!ilTexImage(Header.Width, Header.Height, 1, 3, IL_RGB, IL_UNSIGNED_BYTE, NULL))
302 					return IL_FALSE;
303 			}
304 			else {
305 				if (!ilTexImage(Header.Width, Header.Height, 1, 3, IL_BGR, IL_UNSIGNED_BYTE, NULL))
306 					return IL_FALSE;
307 			}
308 
309 			if (Header.Type != IL_SUN_BYTE_ENC) {  // Regular uncompressed image data
310 				Padding = (2 - (iCurImage->Bps % 2)) % 2;  // Must be padded on a 16-bit boundary (2 bytes)
311 				for (i = 0; i < Header.Height; i++) {
312 					iread(iCurImage->Data + i * Header.Width * 3, 1, iCurImage->Bps);
313 					if (Padding)  // Only possible for padding to be 0 or 1.
314 						igetc();
315 				}
316 			}
317 			else {  // RLE image data
318 				for (i = 0; i < iCurImage->Height; i++) {
319 					BytesRead = iSunGetRle(iCurImage->Data + iCurImage->Bps * i, iCurImage->Bps);
320 					if (BytesRead % 2)  // Each scanline must be aligned on a 2-byte boundary.
321 						igetc();  // Skip padding
322 				}
323 			}
324 
325 			break;
326 
327 		case 32:
328 			if (Header.ColorMapLength > 0)  // Ignore any possible colormaps.
329 				iseek(Header.ColorMapLength, IL_SEEK_CUR);
330 
331 			if (Header.Type == IL_SUN_RGB) {
332 				if (!ilTexImage(Header.Width, Header.Height, 1, 3, IL_RGB, IL_UNSIGNED_BYTE, NULL))
333 					return IL_FALSE;
334 			}
335 			else {
336 				if (!ilTexImage(Header.Width, Header.Height, 1, 3, IL_BGR, IL_UNSIGNED_BYTE, NULL))
337 					return IL_FALSE;
338 			}
339 
340 			// There is no padding at the end of each scanline.
341 			Offset = 0;
342 			for (i = 0; i < Header.Height; i++) {
343 				for (j = 0; j < Header.Width; j++) {
344 					igetc();  // There is a pad byte before each pixel.
345 					iCurImage->Data[Offset]   = igetc();
346 					iCurImage->Data[Offset+1] = igetc();
347 					iCurImage->Data[Offset+2] = igetc();
348 				}
349 			}
350 			break;
351 
352 
353 		default:  // Should have already been checked with iGetSunHead.
354 			return IL_FALSE;
355 	}
356 
357 	iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
358 	return ilFixImage();
359 }
360 
361 
iSunGetRle(ILubyte * Data,ILuint Length)362 ILuint iSunGetRle(ILubyte *Data, ILuint Length)
363 {
364 	ILuint	i = 0, j;
365 	ILubyte	Flag, Value;
366 	ILuint	Count;
367 
368 	for (i = 0; i < Length; ) {
369 		Flag = igetc();
370 		if (Flag == 0x80) {  // Run follows (or 1 byte of 0x80)
371 			Count = igetc();
372 			if (Count == 0) {  // 1 pixel of value (0x80)
373 				*Data = 0x80;
374 				Data++;
375 				i++;
376 			}
377 			else {  // Here we have a run.
378 				Value = igetc();
379 				Count++;  // Should really be Count+1
380 				for (j = 0; j < Count && i + j < Length; j++, Data++) {
381 					*Data = Value;
382 				}
383 				i += Count;
384 			}
385 		}
386 		else {  // 1 byte of this value (cannot be 0x80)
387 			*Data = Flag;
388 			Data++;
389 			i++;
390 		}
391 	}
392 
393 	return i;
394 }
395 
396 
397 #endif//IL_NO_SUN
398 
399