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