1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Last modified: 03/0/2009
5 //
6 // Filename: src-IL/src/il_iff.c
7 //
8 // Description: Reads from an .iff file. Contribution from GAIA:
9 // http://gaia.fdi.ucm.es/grupo/projects/javy/devzone.html#DevILNotes.
10 //
11 //-----------------------------------------------------------------------------
12
13 #include "il_internal.h"
14
15 #ifndef IL_NO_IFF
16
17 // Chunk type, data and functions:
18 typedef struct _iff_chunk {
19 ILuint tag;
20 ILuint start;
21 ILuint size;
22 ILuint chunkType;
23 } iff_chunk;
24
25 #define CHUNK_STACK_SIZE (32)
26 static iff_chunk chunkStack[CHUNK_STACK_SIZE];
27 static int chunkDepth = -1;
28
29 iff_chunk iff_begin_read_chunk();
30 void iff_end_read_chunk();
31 char *iff_read_data(int size);
32 ILboolean iLoadIffInternal(void);
33
34
35 /* Define the IFF tags we are looking for in the file. */
36 const ILuint IFF_TAG_CIMG = ('C' << 24) | ('I' << 16) | ('M' << 8) | ('G');
37 const ILuint IFF_TAG_FOR4 = ('F' << 24) | ('O' << 16) | ('R' << 8) | ('4');
38 const ILuint IFF_TAG_TBHD = ('T' << 24) | ('B' << 16) | ('H' << 8) | ('D');
39 const ILuint IFF_TAG_TBMP = ('T' << 24) | ('B' << 16) | ('M' << 8) | ('P');
40 const ILuint IFF_TAG_RGBA = ('R' << 24) | ('G' << 16) | ('B' << 8) | ('A');
41 const ILuint IFF_TAG_CLPZ = ('C' << 24) | ('L' << 16) | ('P' << 8) | ('Z');
42 const ILuint IFF_TAG_ESXY = ('E' << 24) | ('S' << 16) | ('X' << 8) | ('Y');
43 const ILuint IFF_TAG_ZBUF = ('Z' << 24) | ('B' << 16) | ('U' << 8) | ('F');
44 const ILuint IFF_TAG_BLUR = ('B' << 24) | ('L' << 16) | ('U' << 8) | ('R');
45 const ILuint IFF_TAG_BLRT = ('B' << 24) | ('L' << 16) | ('R' << 8) | ('T');
46 const ILuint IFF_TAG_HIST = ('H' << 24) | ('I' << 16) | ('S' << 8) | ('T');
47
48 // Flags
49 #define RGB_FLAG (1)
50 #define ALPHA_FLAG (2)
51 #define ZBUFFER_FLAG (4)
52
53 // Function for decompress the file.
54 char *iff_decompress_rle(ILuint numBytes, char *compressedData,
55 ILuint compressedDataSize,
56 ILuint *compressedStartIndex);
57
58 char *iffReadUncompressedTile(ILushort width, ILushort height, ILbyte depth);
59 char *iff_decompress_tile_rle(ILushort width, ILushort height, ILushort depth,
60 char *compressedData, ILuint compressedDataSize);
61
62
63 //! Reads an IFF file
ilLoadIff(const ILstring FileName)64 ILboolean ilLoadIff(const ILstring FileName)
65 {
66 ILHANDLE iffFile;
67 ILboolean ret = IL_FALSE;
68
69 iffFile = iopenr(FileName);
70 if (iffFile == NULL) {
71 ilSetError(IL_COULD_NOT_OPEN_FILE);
72 return ret;
73 }
74 ret = ilLoadIffF(iffFile);
75 icloser(iffFile);
76 return ret;
77 }
78
79
80 //! Reads an already-opened IFF file
ilLoadIffF(ILHANDLE File)81 ILboolean ilLoadIffF(ILHANDLE File)
82 {
83 ILuint FirstPos;
84 ILboolean bRet;
85
86 iSetInputFile(File);
87 FirstPos = itell();
88 bRet = iLoadIffInternal();
89 iseek(FirstPos, IL_SEEK_SET);
90
91 // Lbm files can have the .iff extension as well, so if Iff-loading failed,
92 // try to load it as a Lbm.
93 if (bRet == IL_FALSE)
94 return ilLoadIlbmF(File);
95
96 return bRet;
97 }
98
99
100 //! Reads from a memory "lump" that contains an IFF
ilLoadIffL(const void * Lump,ILuint Size)101 ILboolean ilLoadIffL(const void *Lump, ILuint Size)
102 {
103 ILuint FirstPos;
104 ILboolean bRet;
105
106 iSetInputLump(Lump, Size);
107 FirstPos = itell();
108 bRet = iLoadIffInternal();
109 iseek(FirstPos, IL_SEEK_SET);
110
111 // Lbm files can have the .iff extension as well, so if Iff-loading failed,
112 // try to load it as a Lbm.
113 if (bRet == IL_FALSE)
114 return ilLoadIlbmL(Lump, Size);
115
116 return IL_TRUE;
117 }
118
iLoadIffInternal(void)119 ILboolean iLoadIffInternal(void)
120 {
121 iff_chunk chunkInfo;
122
123 // -- Header info.
124 ILuint width, height;
125 ILuint flags, compress;
126 ILushort tiles;
127
128 ILenum format;
129 ILubyte bpp;
130
131 ILboolean tileImageDataFound;
132
133 // -- Initialize the top of the chunk stack.
134 chunkDepth = -1;
135
136 // -- File should begin with a FOR4 chunk of type CIMG
137 chunkInfo = iff_begin_read_chunk();
138 if (chunkInfo.chunkType != IFF_TAG_CIMG) {
139 ilSetError(IL_ILLEGAL_FILE_VALUE);
140 return IL_FALSE;
141 }
142
143
144 /*
145 * Read the image header
146 * OK, we have a FOR4 of type CIMG, look for the following tags
147 * FVER
148 * TBHD bitmap header, definition of size, etc.
149 * AUTH
150 * DATE
151 */
152 while (1) {
153
154 chunkInfo = iff_begin_read_chunk();
155
156 // -- Right now, the only info we need about the image is in TBHD
157 // -- so search this level until we find it.
158 if( chunkInfo.tag == IFF_TAG_TBHD ) {
159 // -- Header chunk found
160 width = GetBigUInt();
161 height = GetBigUInt();
162 GetBigShort(); // -- Don't support
163 GetBigShort(); // -- Don't support
164 flags = GetBigUInt();
165 GetBigShort(); // -- Don't support
166 tiles = GetBigUShort();
167 compress = GetBigUInt();
168
169 iff_end_read_chunk();
170
171 if( compress > 1 ) {
172 ilSetError(IL_ILLEGAL_FILE_VALUE);
173 return IL_FALSE;
174 }
175 break;
176 } else
177 iff_end_read_chunk();
178 } /* END find TBHD while loop */
179
180 if (!(flags & RGB_FLAG)) {
181 ilSetError(IL_ILLEGAL_FILE_VALUE);
182 return IL_FALSE;
183 }
184
185 if (flags & ALPHA_FLAG) {
186 format = IL_RGBA; bpp = 4;
187 } else {
188 format = IL_RGB; bpp = 3;
189 }
190
191 if (!ilTexImage(width, height, 1, bpp, format, IL_UNSIGNED_BYTE, NULL))
192 return IL_FALSE;
193
194 iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
195
196 tileImageDataFound = IL_FALSE;
197
198 while (!tileImageDataFound) {
199 ILuint tileImage;
200 ILuint tileZ;
201
202 chunkInfo = iff_begin_read_chunk();
203
204 /*
205 * OK, we have a FOR4 of type TBMP, (embedded FOR4)
206 * look for the following tags
207 * RGBA color data, RLE compressed tiles of 32 bbp data
208 * ZBUF z-buffer data, 32 bit float values
209 * CLPZ depth map specific, clipping planes, 2 float values
210 * ESXY depth map specific, eye x-y ratios, 2 float values
211 * HIST
212 * VERS
213 * FOR4 <size> BLUR (twice embedded FOR4)
214 */
215 if (chunkInfo.chunkType != IFF_TAG_TBMP) {
216 iff_end_read_chunk();
217 continue;
218 }
219 tileImageDataFound = IL_TRUE;
220 tileImage = 0; // Si no RGBA, tileImage = tiles...
221 if (flags & ZBUFFER_FLAG)
222 tileZ = 0;
223 else
224 tileZ = tiles;
225
226 // Read tiles
227 while ( (tileImage < tiles) || (tileZ < tiles)) {
228 char *tileData;
229 ILushort x1, x2, y1, y2, tile_width, tile_height;
230 ILuint remainingDataSize;
231 ILushort tile_area;
232 ILuint tileCompressed;
233
234 chunkInfo = iff_begin_read_chunk();
235 if ((chunkInfo.tag != IFF_TAG_RGBA) && (chunkInfo.tag != IFF_TAG_ZBUF)) {
236 ilSetError(IL_ILLEGAL_FILE_VALUE);
237 return IL_FALSE;
238 }
239 x1 = GetBigUShort(); y1 = GetBigUShort();
240 x2 = GetBigUShort(); y2 = GetBigUShort();
241
242 remainingDataSize = chunkInfo.size - 4*sizeof(ILushort);
243 tile_width = x2 - x1 + 1;
244 tile_height = y2 - y1 + 1;
245 tile_area = tile_width * tile_height;
246
247 if ((ILint)remainingDataSize >= (tile_width * tile_height * bpp))
248 tileCompressed = 0;
249 else
250 tileCompressed = 1;
251
252 if (chunkInfo.tag == IFF_TAG_RGBA) {
253 if (tileCompressed) {
254 char *data = iff_read_data(remainingDataSize);
255 if (data) {
256 tileData = iff_decompress_tile_rle(tile_width, tile_height,
257 bpp, data, remainingDataSize);
258 ifree(data);
259 }
260 } else {
261 tileData = iffReadUncompressedTile(tile_width, tile_height, bpp);
262 }
263
264 if (tileData) {
265 // Dump RGBA data to our data structure
266 ILushort i;
267 ILuint base;
268 base = bpp*(width * y1 + x1);
269 for (i = 0; i < tile_height; i++) {
270 memcpy(&iCurImage->Data[base + bpp*i*width],
271 &tileData[bpp*i*tile_width],
272 tile_width*bpp*sizeof(char));
273 }
274 ifree(tileData);
275 tileData = NULL;
276
277 iff_end_read_chunk();
278 tileImage++;
279 } else
280 return IL_FALSE;
281 } else if (chunkInfo.tag == IFF_TAG_ZBUF) {
282 tileZ++;
283 iff_end_read_chunk();
284 }
285
286 }
287 }
288 //ilConvertImage(IL_RGB, IL_UNSIGNED_BYTE); // Why was this here?
289 return ilFixImage();
290 }
291
292 /*
293 * IFF Chunking Routines.
294 *
295 */
296
iff_begin_read_chunk()297 iff_chunk iff_begin_read_chunk()
298 {
299 chunkDepth++;
300 if (chunkDepth >= CHUNK_STACK_SIZE){
301 ilSetError(IL_STACK_OVERFLOW);
302 return chunkStack[0];
303 }
304 if (chunkDepth < 0) {
305 ilSetError(IL_STACK_UNDERFLOW);
306 return chunkStack[0];
307 }
308
309 chunkStack[chunkDepth].start = itell();
310 chunkStack[chunkDepth].tag = GetBigInt();
311 chunkStack[chunkDepth].size = GetBigInt();
312
313 if (chunkStack[chunkDepth].tag == IFF_TAG_FOR4) {
314 // -- We have a form, so read the form type tag as well.
315 chunkStack[chunkDepth].chunkType = GetBigInt();
316 } else {
317 chunkStack[chunkDepth].chunkType = 0;
318 }
319
320 return chunkStack[chunkDepth];
321 }
322
iff_end_read_chunk()323 void iff_end_read_chunk()
324 {
325 ILuint end;
326 int part;
327
328 end = chunkStack[chunkDepth].start + chunkStack[chunkDepth].size + 8;
329
330 if (chunkStack[chunkDepth].chunkType != 0) {
331 end += 4;
332 }
333 // Add padding
334 part = end % 4;
335 if (part != 0) {
336 end += 4 - part;
337 }
338
339 iseek(end, IL_SEEK_SET);
340
341 chunkDepth--;
342 }
343
iff_read_data(int size)344 char * iff_read_data(int size)
345 {
346 char *buffer = ialloc(size * sizeof(char));
347 if (buffer == NULL)
348 return NULL;
349
350 if (iread(buffer, size*sizeof(char), 1) != 1) {
351 ifree(buffer);
352 return NULL;
353 }
354
355 return buffer;
356 }
357
358 /*
359 IFF decompress functions
360 */
361
iffReadUncompressedTile(ILushort width,ILushort height,ILbyte depth)362 char *iffReadUncompressedTile(ILushort width, ILushort height, ILbyte depth)
363 {
364
365 char *data = NULL;
366 char *iniPixel;
367 char *finPixel;
368 int i, j;
369 int tam = width* height * depth * sizeof(char);
370
371 data = ialloc(tam);
372 if (data == NULL)
373 return NULL;
374
375 if (iread(data, tam, 1) != 1) {
376 ifree(data);
377 return NULL;
378 }
379
380 iniPixel = data;
381 for (i = 0; i < tam / depth; i++) {
382 finPixel = iniPixel + depth;
383 for (j = 0; j < (depth /2); j++) {
384 char aux;
385 aux = *iniPixel;
386 *(finPixel--) = *iniPixel;
387 *(iniPixel++) = aux;
388 }
389 }
390 return data;
391 }
392
393
iff_decompress_tile_rle(ILushort width,ILushort height,ILushort depth,char * compressedData,ILuint compressedDataSize)394 char *iff_decompress_tile_rle(ILushort width, ILushort height, ILushort depth,
395 char *compressedData, ILuint compressedDataSize)
396 {
397
398 char *channels[4];
399 char *data;
400 int i, k, row, column;
401 ILuint compressedStart = 0;
402
403 // Decompress only in RGBA.
404 if (depth != 4) {
405 ilSetError(IL_ILLEGAL_FILE_VALUE);
406 return NULL;
407 }
408
409 for (i = depth-1; i >= 0; --i) {
410 channels[i] = iff_decompress_rle(width * height, compressedData,
411 compressedDataSize, &compressedStart);
412 if (channels[i] == NULL)
413 return NULL;
414 }
415
416 // Build all the channels from the decompression into an RGBA array.
417 data = ialloc(width * height * depth * sizeof(char));
418 if (data == NULL)
419 return NULL;
420
421 for (row = 0; row < height; row++)
422 for (column = 0; column < width; column++)
423 for (k = 0; k < depth; k++)
424 data[depth*(row*width + column) + k] =
425 channels[k][row*width + column];
426
427 ifree(channels[0]); ifree(channels[1]);
428 ifree(channels[2]); ifree(channels[3]);
429
430 return data;
431 }
432
iff_decompress_rle(ILuint numBytes,char * compressedData,ILuint compressedDataSize,ILuint * compressedStartIndex)433 char *iff_decompress_rle(ILuint numBytes, char *compressedData,
434 ILuint compressedDataSize,
435 ILuint *compressedStartIndex)
436 {
437
438 char *data = ialloc(numBytes * sizeof(char));
439 unsigned char nextChar, count;
440 int i;
441 ILuint byteCount = 0;
442
443 if (data == NULL)
444 return NULL;
445
446 memset(data, 0, numBytes*sizeof(char));
447
448 while (byteCount < numBytes) {
449 if (*compressedStartIndex >= compressedDataSize)
450 break;
451 nextChar = compressedData[*compressedStartIndex];
452 (*compressedStartIndex)++;
453 count = (nextChar & 0x7f) + 1;
454 if ((byteCount + count) > numBytes) break;
455 if (nextChar & 0x80) {
456 // Duplication run
457 nextChar = compressedData[*compressedStartIndex];
458 (*compressedStartIndex)++;
459 // assert ((byteCount + count) <= numBytes);
460 for (i = 0; i < count; i++) {
461 data[byteCount]= nextChar;
462 byteCount++;
463 }
464 } else {
465 // Verbatim run
466 for (i = 0; i < count; i++) {
467 data[byteCount] = compressedData[*compressedStartIndex];
468 (*compressedStartIndex)++;
469 byteCount++;
470 }
471 }
472 //assert(byteCount <= numBytes);
473 }
474
475 return data;
476
477 }
478
479 #endif //IL_NO_IFF
480