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