1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2008 by Denton Woods
5 // Last modified: 03/07/2009
6 //
7 // Filename: src-IL/src/il_gif.c
8 //
9 // Description: Reads from a Graphics Interchange Format (.gif) file.
10 //
11 // The LZW decompression code is based on code released to the public domain
12 // by Javier Arevalo and can be found at
13 // http://www.programmersheaven.com/zone10/cat452
14 //
15 //-----------------------------------------------------------------------------
16
17
18 #include "il_internal.h"
19 #ifndef IL_NO_GIF
20
21 #include "il_gif.h"
22
23
24 ILenum GifType;
25
26 //! Checks if the file specified in FileName is a valid Gif file.
ilIsValidGif(ILconst_string FileName)27 ILboolean ilIsValidGif(ILconst_string FileName)
28 {
29 ILHANDLE GifFile;
30 ILboolean bGif = IL_FALSE;
31
32 if (!iCheckExtension(FileName, IL_TEXT("gif"))) {
33 ilSetError(IL_INVALID_EXTENSION);
34 return bGif;
35 }
36
37 GifFile = iopenr(FileName);
38 if (GifFile == NULL) {
39 ilSetError(IL_COULD_NOT_OPEN_FILE);
40 return bGif;
41 }
42
43 bGif = ilIsValidGifF(GifFile);
44 icloser(GifFile);
45
46 return bGif;
47 }
48
49
50 //! Checks if the ILHANDLE contains a valid Gif file at the current position.
ilIsValidGifF(ILHANDLE File)51 ILboolean ilIsValidGifF(ILHANDLE File)
52 {
53 ILuint FirstPos;
54 ILboolean bRet;
55
56 iSetInputFile(File);
57 FirstPos = itell();
58 bRet = iIsValidGif();
59 iseek(FirstPos, IL_SEEK_SET);
60
61 return bRet;
62 }
63
64
65 //! Checks if Lump is a valid Gif lump.
ilIsValidGifL(const void * Lump,ILuint Size)66 ILboolean ilIsValidGifL(const void *Lump, ILuint Size)
67 {
68 iSetInputLump(Lump, Size);
69 return iIsValidGif();
70 }
71
72
73 // Internal function to get the header and check it.
iIsValidGif()74 ILboolean iIsValidGif()
75 {
76 char Header[6];
77
78 if (iread(Header, 1, 6) != 6)
79 return IL_FALSE;
80 iseek(-6, IL_SEEK_CUR);
81
82 if (!strnicmp(Header, "GIF87A", 6))
83 return IL_TRUE;
84 if (!strnicmp(Header, "GIF89A", 6))
85 return IL_TRUE;
86
87 return IL_FALSE;
88 }
89
90
91 //! Reads a Gif file
ilLoadGif(ILconst_string FileName)92 ILboolean ilLoadGif(ILconst_string FileName)
93 {
94 ILHANDLE GifFile;
95 ILboolean bGif = IL_FALSE;
96
97 GifFile = iopenr(FileName);
98 if (GifFile == NULL) {
99 ilSetError(IL_COULD_NOT_OPEN_FILE);
100 return bGif;
101 }
102
103 bGif = ilLoadGifF(GifFile);
104 icloser(GifFile);
105
106 return bGif;
107 }
108
109
110 //! Reads an already-opened Gif file
ilLoadGifF(ILHANDLE File)111 ILboolean ilLoadGifF(ILHANDLE File)
112 {
113 ILuint FirstPos;
114 ILboolean bRet;
115
116 iSetInputFile(File);
117 FirstPos = itell();
118 bRet = iLoadGifInternal();
119 iseek(FirstPos, IL_SEEK_SET);
120
121 return bRet;
122 }
123
124
125 //! Reads from a memory "lump" that contains a Gif
ilLoadGifL(const void * Lump,ILuint Size)126 ILboolean ilLoadGifL(const void *Lump, ILuint Size)
127 {
128 iSetInputLump(Lump, Size);
129 return iLoadGifInternal();
130 }
131
132
133 // Internal function used to load the Gif.
iLoadGifInternal()134 ILboolean iLoadGifInternal()
135 {
136 GIFHEAD Header;
137 ILpal GlobalPal;
138
139 if (iCurImage == NULL) {
140 ilSetError(IL_ILLEGAL_OPERATION);
141 return IL_FALSE;
142 }
143
144 GlobalPal.Palette = NULL;
145 GlobalPal.PalSize = 0;
146
147 //read header
148 iread(&Header.Sig, 1, 6);
149 Header.Width = GetLittleUShort();
150 Header.Height = GetLittleUShort();
151 Header.ColourInfo = igetc();
152 Header.Background = igetc();
153 Header.Aspect = igetc();
154
155 if (!strnicmp(Header.Sig, "GIF87A", 6)) {
156 GifType = GIF87A;
157 }
158 else if (!strnicmp(Header.Sig, "GIF89A", 6)) {
159 GifType = GIF89A;
160 }
161 else {
162 ilSetError(IL_INVALID_FILE_HEADER);
163 return IL_FALSE;
164 }
165
166 if (!ilTexImage(Header.Width, Header.Height, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL))
167 return IL_FALSE;
168 iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
169
170 // Check for a global colour map.
171 if (Header.ColourInfo & (1 << 7)) {
172 if (!iGetPalette(Header.ColourInfo, &GlobalPal, IL_FALSE, NULL)) {
173 return IL_FALSE;
174 }
175 }
176
177 if (!GetImages(&GlobalPal, &Header))
178 return IL_FALSE;
179
180 if (GlobalPal.Palette && GlobalPal.PalSize)
181 ifree(GlobalPal.Palette);
182 GlobalPal.Palette = NULL;
183 GlobalPal.PalSize = 0;
184
185 return ilFixImage();
186 }
187
188
iGetPalette(ILubyte Info,ILpal * Pal,ILboolean UsePrevPal,ILimage * PrevImage)189 ILboolean iGetPalette(ILubyte Info, ILpal *Pal, ILboolean UsePrevPal, ILimage *PrevImage)
190 {
191 ILuint PalSize, PalOffset = 0;
192
193 // If we have a local palette and have to use the previous frame as well,
194 // we have to copy the palette from the previous frame, in addition
195 // to the data, which we copy in GetImages.
196
197 // The ld(palettes bpp - 1) is stored in the lower
198 // 3 bits of Info (weird gif format ... :) )
199 PalSize = (1 << ((Info & 0x7) + 1)) * 3;
200 if (UsePrevPal) {
201 if (PrevImage == NULL) { // Cannot use the previous palette if it does not exist.
202 ilSetError(IL_ILLEGAL_FILE_VALUE);
203 return IL_FALSE;
204 }
205 PalSize = PalSize + PrevImage->Pal.PalSize;
206 PalOffset = PrevImage->Pal.PalSize;
207 }
208 if (PalSize > 256 * 3) {
209 ilSetError(IL_ILLEGAL_FILE_VALUE);
210 return IL_FALSE;
211 }
212 Pal->PalSize = PalSize;
213
214 Pal->PalType = IL_PAL_RGB24;
215 //Pal->Palette = (ILubyte*)ialloc(Pal->PalSize);
216 Pal->Palette = (ILubyte*)ialloc(256 * 3);
217 if (Pal->Palette == NULL)
218 return IL_FALSE;
219 if (UsePrevPal)
220 memcpy(Pal->Palette, PrevImage->Pal.Palette, PrevImage->Pal.PalSize); // Copy the old palette over.
221 if (iread(Pal->Palette + PalOffset, 1, Pal->PalSize) != Pal->PalSize) { // Read the new palette.
222 ifree(Pal->Palette);
223 Pal->Palette = NULL;
224 return IL_FALSE;
225 }
226
227 return IL_TRUE;
228 }
229
230
GetImages(ILpal * GlobalPal,GIFHEAD * GifHead)231 ILboolean GetImages(ILpal *GlobalPal, GIFHEAD *GifHead)
232 {
233 IMAGEDESC ImageDesc, OldImageDesc;
234 GFXCONTROL Gfx;
235 ILboolean BaseImage = IL_TRUE;
236 ILimage *Image = iCurImage, *TempImage = NULL, *PrevImage = NULL;
237 ILuint NumImages = 0, i;
238 ILint input;
239 ILuint PalOffset;
240
241 OldImageDesc.ImageInfo = 0; // to initialize the data with an harmless value
242 Gfx.Used = IL_TRUE;
243
244 while (!ieof()) {
245 ILubyte DisposalMethod = 1;
246
247 i = itell();
248 if (!SkipExtensions(&Gfx))
249 goto error_clean;
250 i = itell();
251
252 if (!Gfx.Used)
253 DisposalMethod = (Gfx.Packed & 0x1C) >> 2;
254
255 //read image descriptor
256 ImageDesc.Separator = igetc();
257 if (ImageDesc.Separator != 0x2C) //end of image
258 break;
259 ImageDesc.OffX = GetLittleUShort();
260 ImageDesc.OffY = GetLittleUShort();
261 ImageDesc.Width = GetLittleUShort();
262 ImageDesc.Height = GetLittleUShort();
263 ImageDesc.ImageInfo = igetc();
264
265 if (ieof()) {
266 ilGetError(); // Gets rid of the IL_FILE_READ_ERROR that inevitably results.
267 break;
268 }
269
270
271 if (!BaseImage) {
272 NumImages++;
273 Image->Next = ilNewImage(iCurImage->Width, iCurImage->Height, 1, 1, 1);
274 if (Image->Next == NULL)
275 goto error_clean;
276 //20040612: DisposalMethod controls how the new images data is to be combined
277 //with the old image. 0 means that it doesn't matter how they are combined,
278 //1 means keep the old image, 2 means set to background color, 3 is
279 //load the image that was in place before the current (this is not implemented
280 //here! (TODO?))
281 if (DisposalMethod == 2 || DisposalMethod == 3)
282 //Note that this is actually wrong, too: If the image has a local
283 //color table, we should really search for the best fit of the
284 //background color table and use that index (?). Furthermore,
285 //we should only memset the part of the image that is not read
286 //later (if we are sure that no parts of the read image are transparent).
287 if (!Gfx.Used && Gfx.Packed & 0x1)
288 memset(Image->Next->Data, Gfx.Transparent, Image->SizeOfData);
289 else
290 memset(Image->Next->Data, GifHead->Background, Image->SizeOfData);
291 else if (DisposalMethod == 1 || DisposalMethod == 0)
292 memcpy(Image->Next->Data, Image->Data, Image->SizeOfData);
293 //Interlacing has to be removed after the image was copied (line above)
294 if (OldImageDesc.ImageInfo & (1 << 6)) { // Image is interlaced.
295 if (!RemoveInterlace(Image))
296 goto error_clean;
297 }
298
299 PrevImage = Image;
300 Image = Image->Next;
301 Image->Format = IL_COLOUR_INDEX;
302 Image->Origin = IL_ORIGIN_UPPER_LEFT;
303 } else {
304 BaseImage = IL_FALSE;
305 if (!Gfx.Used && Gfx.Packed & 0x1)
306 memset(Image->Data, Gfx.Transparent, Image->SizeOfData);
307 else
308 memset(Image->Data, GifHead->Background, Image->SizeOfData);
309 }
310
311 Image->OffX = ImageDesc.OffX;
312 Image->OffY = ImageDesc.OffY;
313 PalOffset = 0;
314
315 // Check to see if the image has its own palette.
316 if (ImageDesc.ImageInfo & (1 << 7)) {
317 ILboolean UsePrevPal = IL_FALSE;
318 if (DisposalMethod == 1 && NumImages != 0) { // Cannot be the first image for this.
319 PalOffset = PrevImage->Pal.PalSize;
320 UsePrevPal = IL_TRUE;
321 }
322 if (!iGetPalette(ImageDesc.ImageInfo, &Image->Pal, UsePrevPal, PrevImage)) {
323 goto error_clean;
324 }
325 } else {
326 if (!iCopyPalette(&Image->Pal, GlobalPal)) {
327 goto error_clean;
328 }
329 }
330
331 if (!GifGetData(Image, Image->Data + ImageDesc.OffX + ImageDesc.OffY*Image->Width, Image->SizeOfData,
332 ImageDesc.Width, ImageDesc.Height, Image->Width, PalOffset, &Gfx)) {
333 memset(Image->Data, 0, Image->SizeOfData); //@TODO: Remove this. For debugging purposes right now.
334 ilSetError(IL_ILLEGAL_FILE_VALUE);
335 goto error_clean;
336 }
337
338 // See if there was a valid graphics control extension.
339 if (!Gfx.Used) {
340 Gfx.Used = IL_TRUE;
341 Image->Duration = Gfx.Delay * 10; // We want it in milliseconds.
342
343 // See if a transparent colour is defined.
344 if (Gfx.Packed & 1) {
345 if (!ConvertTransparent(Image, Gfx.Transparent)) {
346 goto error_clean;
347 }
348 }
349 }
350 i = itell();
351 // Terminates each block.
352 if((input = igetc()) == IL_EOF)
353 goto error_clean;
354
355 if (input != 0x00)
356 iseek(-1, IL_SEEK_CUR);
357 // break;
358
359 OldImageDesc = ImageDesc;
360 }
361
362 //Deinterlace last image
363 if (OldImageDesc.ImageInfo & (1 << 6)) { // Image is interlaced.
364 if (!RemoveInterlace(Image))
365 goto error_clean;
366 }
367
368 if (BaseImage) // Was not able to load any images in...
369 return IL_FALSE;
370
371 return IL_TRUE;
372
373 error_clean:
374 Image = iCurImage->Next;
375 /* while (Image) {
376 TempImage = Image;
377 Image = Image->Next;
378 ilCloseImage(TempImage);
379 }*/
380 return IL_FALSE;
381 }
382
383
SkipExtensions(GFXCONTROL * Gfx)384 ILboolean SkipExtensions(GFXCONTROL *Gfx)
385 {
386 ILint Code;
387 ILint Label;
388 ILint Size;
389
390 // DW (06-03-2002): Apparently there can be...
391 //if (GifType == GIF87A)
392 // return IL_TRUE; // No extensions in the GIF87a format.
393
394 do {
395 if((Code = igetc()) == IL_EOF)
396 return IL_FALSE;
397
398 if (Code != 0x21) {
399 iseek(-1, IL_SEEK_CUR);
400 return IL_TRUE;
401 }
402
403 if((Label = igetc()) == IL_EOF)
404 return IL_FALSE;
405
406 switch (Label)
407 {
408 case 0xF9:
409 Gfx->Size = igetc();
410 Gfx->Packed = igetc();
411 Gfx->Delay = GetLittleUShort();
412 Gfx->Transparent = igetc();
413 Gfx->Terminator = igetc();
414 if (ieof())
415 return IL_FALSE;
416 Gfx->Used = IL_FALSE;
417 break;
418 /*case 0xFE:
419 break;
420
421 case 0x01:
422 break;*/
423 default:
424 do {
425 if((Size = igetc()) == IL_EOF)
426 return IL_FALSE;
427 iseek(Size, IL_SEEK_CUR);
428 } while (!ieof() && Size != 0);
429 }
430
431 // @TODO: Handle this better.
432 if (ieof()) {
433 ilSetError(IL_FILE_READ_ERROR);
434 return IL_FALSE;
435 }
436 } while (1);
437
438 return IL_TRUE;
439 }
440
441
442 #define MAX_CODES 4096
443
444 ILint curr_size, clear, ending, newcodes, top_slot, slot, navail_bytes = 0, nbits_left = 0;
445 ILubyte b1;
446 ILubyte byte_buff[257];
447 ILubyte *pbytes;
448 ILubyte *stack;
449 ILubyte *suffix;
450 ILshort *prefix;
451
452 ILboolean success;
453
454 ILuint code_mask[13] =
455 {
456 0L,
457 0x0001L, 0x0003L,
458 0x0007L, 0x000FL,
459 0x001FL, 0x003FL,
460 0x007FL, 0x00FFL,
461 0x01FFL, 0x03FFL,
462 0x07FFL, 0x0FFFL
463 };
464
465
get_next_code(void)466 ILint get_next_code(void) {
467 ILint i, t;
468 ILuint ret;
469
470 //20050102: Tests for IL_EOF were added because this function
471 //crashed sometimes if igetc() returned IL_EOF
472 //(for example "table-add-column-before-active.gif" included in the
473 //mozilla source package)
474
475 if (!nbits_left) {
476 if (navail_bytes <= 0) {
477 pbytes = byte_buff;
478 navail_bytes = igetc();
479
480 if(navail_bytes == IL_EOF) {
481 success = IL_FALSE;
482 return ending;
483 }
484
485 if (navail_bytes) {
486 for (i = 0; i < navail_bytes; i++) {
487 if((t = igetc()) == IL_EOF) {
488 success = IL_FALSE;
489 return ending;
490 }
491 byte_buff[i] = t;
492 }
493 }
494 }
495 b1 = *pbytes++;
496 nbits_left = 8;
497 navail_bytes--;
498 }
499
500 ret = b1 >> (8 - nbits_left);
501 while (curr_size > nbits_left) {
502 if (navail_bytes <= 0) {
503 pbytes = byte_buff;
504 navail_bytes = igetc();
505
506 if(navail_bytes == IL_EOF) {
507 success = IL_FALSE;
508 return ending;
509 }
510
511 if (navail_bytes) {
512 for (i = 0; i < navail_bytes; i++) {
513 if((t = igetc()) == IL_EOF) {
514 success = IL_FALSE;
515 return ending;
516 }
517 byte_buff[i] = t;
518 }
519 }
520 }
521 b1 = *pbytes++;
522 ret |= b1 << nbits_left;
523 nbits_left += 8;
524 navail_bytes--;
525 }
526 nbits_left -= curr_size;
527
528 return (ret & code_mask[curr_size]);
529 }
530
cleanUpGifLoadState()531 static void cleanUpGifLoadState()
532 {
533 ifree(stack);
534 ifree(suffix);
535 ifree(prefix);
536
537 }
538
GifGetData(ILimage * Image,ILubyte * Data,ILuint ImageSize,ILuint Width,ILuint Height,ILuint Stride,ILuint PalOffset,GFXCONTROL * Gfx)539 ILboolean GifGetData(ILimage *Image, ILubyte *Data, ILuint ImageSize, ILuint Width, ILuint Height, ILuint Stride, ILuint PalOffset, GFXCONTROL *Gfx)
540 {
541 ILubyte *sp;
542 ILint code, fc, oc;
543 ILubyte DisposalMethod = 0;
544 ILint c, size;
545 ILuint i = 0, Read = 0, j = 0;
546 ILubyte *DataPtr = Data;
547
548 if (!Gfx->Used)
549 DisposalMethod = (Gfx->Packed & 0x1C) >> 2;
550 if((size = igetc()) == IL_EOF)
551 return IL_FALSE;
552
553 if (size < 2 || 9 < size) {
554 return IL_FALSE;
555 }
556
557 stack = (ILubyte*)ialloc(MAX_CODES + 1);
558 suffix = (ILubyte*)ialloc(MAX_CODES + 1);
559 prefix = (ILshort*)ialloc(sizeof(*prefix) * (MAX_CODES + 1));
560 if (!stack || !suffix || !prefix)
561 {
562 cleanUpGifLoadState();
563 return IL_FALSE;
564 }
565
566 curr_size = size + 1;
567 top_slot = 1 << curr_size;
568 clear = 1 << size;
569 ending = clear + 1;
570 slot = newcodes = ending + 1;
571 navail_bytes = nbits_left = 0;
572 oc = fc = 0;
573 sp = stack;
574
575 while ((c = get_next_code()) != ending && Read < Height) {
576 if (c == clear)
577 {
578 curr_size = size + 1;
579 slot = newcodes;
580 top_slot = 1 << curr_size;
581 while ((c = get_next_code()) == clear);
582 if (c == ending)
583 break;
584 if (c >= slot)
585 c = 0;
586 oc = fc = c;
587
588 if (DisposalMethod == 1 && !Gfx->Used && Gfx->Transparent == c && (Gfx->Packed & 0x1) != 0)
589 i++;
590 else if (i < Width)
591 DataPtr[i++] = c + PalOffset;
592
593 if (i == Width)
594 {
595 DataPtr += Stride;
596 i = 0;
597 Read += 1;
598 ++j;
599 if (j >= Height) {
600 cleanUpGifLoadState();
601 return IL_FALSE;
602 }
603 }
604 }
605 else
606 {
607 code = c;
608 //BG-2007-01-10: several fixes for incomplete GIFs
609 if (code >= slot)
610 {
611 code = oc;
612 if (sp >= stack + MAX_CODES) {
613 cleanUpGifLoadState();
614 return IL_FALSE;
615 }
616 *sp++ = fc;
617 }
618
619 if (code >= MAX_CODES)
620 return IL_FALSE;
621 while (code >= newcodes)
622 {
623 if (sp >= stack + MAX_CODES)
624 {
625 cleanUpGifLoadState();
626 return IL_FALSE;
627 }
628 *sp++ = suffix[code];
629 code = prefix[code];
630 }
631
632 if (sp >= stack + MAX_CODES) {
633 cleanUpGifLoadState();
634 return IL_FALSE;
635 }
636
637 *sp++ = (ILbyte)code;
638 if (slot < top_slot)
639 {
640 fc = code;
641 suffix[slot] = fc;
642 prefix[slot++] = oc;
643 oc = c;
644 }
645 if (slot >= top_slot && curr_size < 12)
646 {
647 top_slot <<= 1;
648 curr_size++;
649 }
650 while (sp > stack)
651 {
652 sp--;
653 if (DisposalMethod == 1 && !Gfx->Used && Gfx->Transparent == *sp && (Gfx->Packed & 0x1) != 0)
654 i++;
655 else if (i < Width)
656 DataPtr[i++] = *sp + PalOffset;
657
658 if (i == Width)
659 {
660 i = 0;
661 Read += 1;
662 j = (j+1) % Height;
663 // Needs to start from Data, not Image->Data.
664 DataPtr = Data + j * Stride;
665 }
666 }
667 }
668 }
669 cleanUpGifLoadState();
670 return IL_TRUE;
671 }
672
673
674 /*From the GIF spec:
675
676 The rows of an Interlaced images are arranged in the following order:
677
678 Group 1 : Every 8th. row, starting with row 0. (Pass 1)
679 Group 2 : Every 8th. row, starting with row 4. (Pass 2)
680 Group 3 : Every 4th. row, starting with row 2. (Pass 3)
681 Group 4 : Every 2nd. row, starting with row 1. (Pass 4)
682 */
683
RemoveInterlace(ILimage * image)684 ILboolean RemoveInterlace(ILimage *image)
685 {
686 ILubyte *NewData;
687 ILuint i, j = 0;
688
689 NewData = (ILubyte*)ialloc(image->SizeOfData);
690 if (NewData == NULL)
691 return IL_FALSE;
692
693 //changed 20041230: images with offsety != 0 were not
694 //deinterlaced correctly before...
695 for (i = 0; i < image->OffY; i++, j++) {
696 memcpy(&NewData[i * image->Bps], &image->Data[j * image->Bps], image->Bps);
697 }
698
699 for (i = 0 + image->OffY; i < image->Height; i += 8, j++) {
700 memcpy(&NewData[i * image->Bps], &image->Data[j * image->Bps], image->Bps);
701 }
702
703 for (i = 4 + image->OffY; i < image->Height; i += 8, j++) {
704 memcpy(&NewData[i * image->Bps], &image->Data[j * image->Bps], image->Bps);
705 }
706
707 for (i = 2 + image->OffY; i < image->Height; i += 4, j++) {
708 memcpy(&NewData[i * image->Bps], &image->Data[j * image->Bps], image->Bps);
709 }
710
711 for (i = 1 + image->OffY; i < image->Height; i += 2, j++) {
712 memcpy(&NewData[i * image->Bps], &image->Data[j * image->Bps], image->Bps);
713 }
714
715 ifree(image->Data);
716 image->Data = NewData;
717
718 return IL_TRUE;
719 }
720
721
722 // Uses the transparent colour index to make an alpha channel.
ConvertTransparent(ILimage * Image,ILubyte TransColour)723 ILboolean ConvertTransparent(ILimage *Image, ILubyte TransColour)
724 {
725 ILubyte *Palette;
726 ILuint i, j;
727
728 if (!Image->Pal.Palette || !Image->Pal.PalSize) {
729 ilSetError(IL_INTERNAL_ERROR);
730 return IL_FALSE;
731 }
732
733 Palette = (ILubyte*)ialloc(Image->Pal.PalSize / 3 * 4);
734 if (Palette == NULL)
735 return IL_FALSE;
736
737 for (i = 0, j = 0; i < Image->Pal.PalSize; i += 3, j += 4) {
738 Palette[j ] = Image->Pal.Palette[i ];
739 Palette[j+1] = Image->Pal.Palette[i+1];
740 Palette[j+2] = Image->Pal.Palette[i+2];
741 if (j/4 == TransColour)
742 Palette[j+3] = 0x00;
743 else
744 Palette[j+3] = 0xFF;
745 }
746
747 ifree(Image->Pal.Palette);
748 Image->Pal.Palette = Palette;
749 Image->Pal.PalSize = Image->Pal.PalSize / 3 * 4;
750 Image->Pal.PalType = IL_PAL_RGBA32;
751
752 return IL_TRUE;
753 }
754
755 #endif //IL_NO_GIF
756