Bug in il_gif.c

2012-07-13
2013-05-02
  • I have a GIF file that causes a heap corruption in DevIL. In GetImages:

    GifGetData(Image, Image->Data + ImageDesc.OffX + ImageDesc.OffY*Image->Width,

    The 2nd argument is important here. It is copied into a local variable called dataPtr.
    In this example, ImageDesc.OffY is positive, and the image contains the full number
    of lines. For this reason,

    DataPtr_ = *sp + PalOffset;

    cause the actual heap overflow when image->Height lines are decoded. The tests that are currently in place do not catch this error. At one place, DataPtr is recalculated using

    DataPtr = Data + j * Stride;

    This assignment does not honor  ImageDesc.OffX + ImageDesc.OffY, which may cause additional problems.

    Furthermore, I would advise to collect the global variables in this file in a struct, like this:

    struct GIFLoadState {
       ILint    curr_size, clear, ending, newcodes, top_slot, slot, navail_bytes, nbits_left;
       ILubyte    b1;
       ILubyte    byte_buff;
       ILubyte    *pbytes;
       ILubyte    *stack;
       ILubyte    *suffix;
       ILshort    *prefix;
       ILboolean success;
    };

    A simple and very dirty fix ist to ignore ImageDesc.OffX + ImageDesc.OffY in the call to GifGetData.
    This may work in some cases if only the first image in the GIF is of interest, but this bug should be fixed more cleanly.

    Please contact me if you need samples._

     
  • I propose the following fixed file for il_gif.h. The function GifGetData should be removed from il_gif.h

    //---------------------------------------------------
    //
    // ImageLib Sources
    // Copyright (C) 2000-2008 by Denton Woods
    // Last modified: 03/07/2009
    //
    // Filename: src-IL/src/il_gif.c
    //
    // Description: Reads from a Graphics Interchange Format (.gif) file.
    //
    //  The LZW decompression code is based on code released to the public domain
    //    by Javier Arevalo and can be found at
    //    http://www.programmersheaven.com/zone10/cat452
    //
    //---------------------------------------------------

    #include "il_internal.h"
    #ifndef IL_NO_GIF

    #include "il_gif.h"

    ILenum GifType;

    //! Checks if the file specified in FileName is a valid Gif file.
    ILboolean ilIsValidGif(ILconst_string FileName)
    {
    ILHANDLE GifFile;
    ILboolean bGif = IL_FALSE;

    if (!iCheckExtension(FileName, IL_TEXT("gif"))) {
    ilSetError(IL_INVALID_EXTENSION);
    return bGif;
    }

    GifFile = iopenr(FileName);
    if (GifFile == NULL) {
    ilSetError(IL_COULD_NOT_OPEN_FILE);
    return bGif;
    }

    bGif = ilIsValidGifF(GifFile);
    icloser(GifFile);

    return bGif;
    }

    //! Checks if the ILHANDLE contains a valid Gif file at the current position.
    ILboolean ilIsValidGifF(ILHANDLE File)
    {
    ILuint FirstPos;
    ILboolean bRet;

    iSetInputFile(File);
    FirstPos = itell();
    bRet = iIsValidGif();
    iseek(FirstPos, IL_SEEK_SET);

    return bRet;
    }

    //! Checks if Lump is a valid Gif lump.
    ILboolean ilIsValidGifL(const void *Lump, ILuint Size)
    {
    iSetInputLump(Lump, Size);
    return iIsValidGif();
    }

    // Internal function to get the header and check it.
    ILboolean iIsValidGif()
    {
    char Header;

    if (iread(Header, 1, 6) != 6)
    return IL_FALSE;
    iseek(-6, IL_SEEK_CUR);

    if (!_strnicmp(Header, "GIF87A", 6))
    return IL_TRUE;
    if (!_strnicmp(Header, "GIF89A", 6))
    return IL_TRUE;

    return IL_FALSE;
    }

    //! Reads a Gif file
    ILboolean ilLoadGif(ILconst_string FileName)
    {
    ILHANDLE GifFile;
    ILboolean bGif = IL_FALSE;

    GifFile = iopenr(FileName);
    if (GifFile == NULL) {
    ilSetError(IL_COULD_NOT_OPEN_FILE);
    return bGif;
    }

    bGif = ilLoadGifF(GifFile);
    icloser(GifFile);

    return bGif;
    }

    //! Reads an already-opened Gif file
    ILboolean ilLoadGifF(ILHANDLE File)
    {
    ILuint FirstPos;
    ILboolean bRet;

    iSetInputFile(File);
    FirstPos = itell();
    bRet = iLoadGifInternal();
    iseek(FirstPos, IL_SEEK_SET);

    return bRet;
    }

    //! Reads from a memory "lump" that contains a Gif
    ILboolean ilLoadGifL(const void *Lump, ILuint Size)
    {
    iSetInputLump(Lump, Size);
       return iLoadGifInternal();
    }

    // Internal function used to load the Gif.
    ILboolean iLoadGifInternal()
    {
    GIFHEAD Header;
    ILpal GlobalPal;

    if (iCurImage == NULL) {
    ilSetError(IL_ILLEGAL_OPERATION);
    return IL_FALSE;
    }

    GlobalPal.Palette = NULL;
    GlobalPal.PalSize = 0;

    //read header
    iread(&Header.Sig, 1, 6);
      Header.Width = GetLittleUShort();
        Header.Height = GetLittleUShort();
    Header.ColourInfo = igetc();
    Header.Background = igetc();
    Header.Aspect = igetc();
     
    if (!_strnicmp(Header.Sig, "GIF87A", 6)) {
    GifType = GIF87A;
    }
    else if (!_strnicmp(Header.Sig, "GIF89A", 6)) {
    GifType = GIF89A;
    }
    else {
    ilSetError(IL_INVALID_FILE_HEADER);
    return IL_FALSE;
    }

    if (!ilTexImage(Header.Width, Header.Height, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL))
    return IL_FALSE;
    iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;

    // Check for a global colour map.
    if (Header.ColourInfo & (1 << 7)) {
    if (!iGetPalette(Header.ColourInfo, &GlobalPal, IL_FALSE, NULL)) {
    return IL_FALSE;
    }
    }

    if (!GetImages(&GlobalPal, &Header))
    return IL_FALSE;

    if (GlobalPal.Palette && GlobalPal.PalSize)
    ifree(GlobalPal.Palette);
    GlobalPal.Palette = NULL;
    GlobalPal.PalSize = 0;

    return ilFixImage();
    }

    ILboolean iGetPalette(ILubyte Info, ILpal *Pal, ILboolean UsePrevPal, ILimage *PrevImage)
    {
    ILuint PalSize, PalOffset = 0;

    // If we have a local palette and have to use the previous frame as well,
    //  we have to copy the palette from the previous frame, in addition
    //  to the data, which we copy in GetImages.

    // The ld(palettes bpp - 1) is stored in the lower
    // 3 bits of Info (weird gif format … :) )
    PalSize = (1 << ((Info & 0x7) + 1)) * 3;
    if (UsePrevPal) {
    if (PrevImage == NULL) {  // Cannot use the previous palette if it does not exist.
    ilSetError(IL_ILLEGAL_FILE_VALUE);
    return IL_FALSE;
    }
    PalSize = PalSize + PrevImage->Pal.PalSize;
    PalOffset = PrevImage->Pal.PalSize;
    }
    if (PalSize > 256 * 3) {
    ilSetError(IL_ILLEGAL_FILE_VALUE);
    return IL_FALSE;
    }
    Pal->PalSize = PalSize;

    Pal->PalType = IL_PAL_RGB24;
    //Pal->Palette = (ILubyte*)ialloc(Pal->PalSize);
    Pal->Palette = (ILubyte*)ialloc(256 * 3);
    if (Pal->Palette == NULL)
    return IL_FALSE;
    if (UsePrevPal)
    memcpy(Pal->Palette, PrevImage->Pal.Palette, PrevImage->Pal.PalSize);  // Copy the old palette over.
    if (iread(Pal->Palette + PalOffset, 1, Pal->PalSize) != Pal->PalSize) {  // Read the new palette.
    ifree(Pal->Palette);
    Pal->Palette = NULL;
    return IL_FALSE;
    }

    return IL_TRUE;
    }

    // Forward declaration - could be removed by placing the entire function here,
    // but eases comparing the source codes for now
    ILboolean GifGetData(ILimage *Image, ILubyte *Data, ILuint ImageSize,
       ILuint OffX, ILuint OffY, ILuint Width, ILuint Height, ILuint Stride,
       ILuint PalOffset, GFXCONTROL *Gfx);

    ILboolean GetImages(ILpal *GlobalPal, GIFHEAD *GifHead)
    {
    IMAGEDESC ImageDesc, OldImageDesc;
    GFXCONTROL Gfx;
    ILboolean BaseImage = IL_TRUE;
    ILimage *Image = iCurImage, *TempImage = NULL, *PrevImage = NULL;
    ILuint NumImages = 0, i;
    ILint input;
    ILuint PalOffset;

    OldImageDesc.ImageInfo = 0; // to initialize the data with an harmless value
    Gfx.Used = IL_TRUE;

    while (!ieof()) {
    ILubyte DisposalMethod = 1;

    i = itell();
    if (!SkipExtensions(&Gfx))
    goto error_clean;
    i = itell();

    if (!Gfx.Used)
    DisposalMethod = (Gfx.Packed & 0x1C) >> 2;

    //read image descriptor
    ImageDesc.Separator = igetc();
    if (ImageDesc.Separator != 0x2C) //end of image
    break;
    ImageDesc.OffX = GetLittleUShort();
    ImageDesc.OffY = GetLittleUShort();
    ImageDesc.Width = GetLittleUShort();
    ImageDesc.Height = GetLittleUShort();
    ImageDesc.ImageInfo = igetc();

    if (ieof()) {
    ilGetError();  // Gets rid of the IL_FILE_READ_ERROR that inevitably results.
    break;
    }

    if (!BaseImage) {
    NumImages++;
    Image->Next = ilNewImage(iCurImage->Width, iCurImage->Height, 1, 1, 1);
    if (Image->Next == NULL)
    goto error_clean;
    //20040612: DisposalMethod controls how the new images data is to be combined
    //with the old image. 0 means that it doesn't matter how they are combined,
    //1 means keep the old image, 2 means set to background color, 3 is
    //load the image that was in place before the current (this is not implemented
    //here! (TODO?))
    if (DisposalMethod == 2 || DisposalMethod == 3)
    //Note that this is actually wrong, too: If the image has a local
    //color table, we should really search for the best fit of the
    //background color table and use that index (?). Furthermore,
    //we should only memset the part of the image that is not read
    //later (if we are sure that no parts of the read image are transparent).
    if (!Gfx.Used && Gfx.Packed & 0x1)
    memset(Image->Next->Data, Gfx.Transparent, Image->SizeOfData);
    else
    memset(Image->Next->Data, GifHead->Background, Image->SizeOfData);
    else if (DisposalMethod == 1 || DisposalMethod == 0)
    memcpy(Image->Next->Data, Image->Data, Image->SizeOfData);
    //Interlacing has to be removed after the image was copied (line above)
    if (OldImageDesc.ImageInfo & (1 << 6)) {  // Image is interlaced.
    if (!RemoveInterlace(Image))
    goto error_clean;
    }

    PrevImage = Image;
    Image = Image->Next;
    Image->Format = IL_COLOUR_INDEX;
    Image->Origin = IL_ORIGIN_UPPER_LEFT;
    } else {
    BaseImage = IL_FALSE;
    if (!Gfx.Used && Gfx.Packed & 0x1)
    memset(Image->Data, Gfx.Transparent, Image->SizeOfData);
    else
        memset(Image->Data, GifHead->Background, Image->SizeOfData);
    }

    Image->OffX = ImageDesc.OffX;
    Image->OffY = ImageDesc.OffY;
    PalOffset = 0;

    // Check to see if the image has its own palette.
    if (ImageDesc.ImageInfo & (1 << 7)) {
    ILboolean UsePrevPal = IL_FALSE;
    if (DisposalMethod == 1 && NumImages != 0) {  // Cannot be the first image for this.
    PalOffset = PrevImage->Pal.PalSize;
    UsePrevPal = IL_TRUE;
    }
    if (!iGetPalette(ImageDesc.ImageInfo, &Image->Pal, UsePrevPal, PrevImage)) {
    goto error_clean;
    }
    } else {
    if (!iCopyPalette(&Image->Pal, GlobalPal)) {
    goto error_clean;
    }
    }

          //if (!GifGetData(Image, Image->Data + ImageDesc.OffX + ImageDesc.OffY*Image->Width,
    if (!GifGetData(Image, Image->Data, Image->SizeOfData, ImageDesc.OffX, ImageDesc.OffY,
    ImageDesc.Width, ImageDesc.Height, Image->Width, PalOffset, &Gfx)) {
    memset(Image->Data, 0, Image->SizeOfData);  //@TODO: Remove this.  For debugging purposes right now.
    ilSetError(IL_ILLEGAL_FILE_VALUE);
    goto error_clean;
    }

    // See if there was a valid graphics control extension.
    if (!Gfx.Used) {
    Gfx.Used = IL_TRUE;
    Image->Duration = Gfx.Delay * 10;  // We want it in milliseconds.

    // See if a transparent colour is defined.
    if (Gfx.Packed & 1) {
    if (!ConvertTransparent(Image, Gfx.Transparent)) {
    goto error_clean;
    }
        }
    }
    i = itell();
    // Terminates each block.
    if((input = igetc()) == IL_EOF)
    goto error_clean;

    if (input != 0x00)
        iseek(-1, IL_SEEK_CUR);
    // break;

    OldImageDesc = ImageDesc;
    }

    //Deinterlace last image
    if (OldImageDesc.ImageInfo & (1 << 6)) {  // Image is interlaced.
    if (!RemoveInterlace(Image))
    goto error_clean;
    }

    if (BaseImage)  // Was not able to load any images in…
    return IL_FALSE;

    return IL_TRUE;

    error_clean:
    Image = iCurImage->Next;
        /* while (Image) {
    TempImage = Image;
    Image = Image->Next;
    ilCloseImage(TempImage);
    }*/
    return IL_FALSE;
    }

    ILboolean SkipExtensions(GFXCONTROL *Gfx)
    {
    ILint Code;
    ILint Label;
    ILint Size;

    // DW (06-03-2002):  Apparently there can be…
    //if (GifType == GIF87A)
    // return IL_TRUE;  // No extensions in the GIF87a format.

    do {
    if((Code = igetc()) == IL_EOF)
    return IL_FALSE;

    if (Code != 0x21) {
    iseek(-1, IL_SEEK_CUR);
    return IL_TRUE;
    }

    if((Label = igetc()) == IL_EOF)
    return IL_FALSE;

    switch (Label)
    {
    case 0xF9:
    Gfx->Size = igetc();
    Gfx->Packed = igetc();
    Gfx->Delay = GetLittleUShort();
    Gfx->Transparent = igetc();
    Gfx->Terminator = igetc();
    if (ieof())
    return IL_FALSE;
    Gfx->Used = IL_FALSE;
    break;
    /*case 0xFE:
    break;

    case 0x01:
    break;*/
    default:
    do {
    if((Size = igetc()) == IL_EOF)
    return IL_FALSE;
    iseek(Size, IL_SEEK_CUR);
    } while (!ieof() && Size != 0);
    }

    // @TODO:  Handle this better.
    if (ieof()) {
    ilSetError(IL_FILE_READ_ERROR);
    return IL_FALSE;
    }
    } while (1);

    return IL_TRUE;
    }

    #define MAX_CODES 4096
    struct GifLoadState {
       ILint curr_size, clear, ending, newcodes, top_slot, slot, navail_bytes, nbits_left;
       ILubyte b1;
       ILubyte byte_buff;
       ILubyte *pbytes;
       ILubyte *stack;
       ILubyte *suffix;
       ILshort *prefix;
    };

    ILboolean success;

    ILuint code_mask =
    {
       0L,
       0x0001L, 0x0003L,
       0x0007L, 0x000FL,
       0x001FL, 0x003FL,
       0x007FL, 0x00FFL,
       0x01FFL, 0x03FFL,
       0x07FFL, 0x0FFFL
    };

    ILint get_next_code(struct GifLoadState* state) {
    ILint i, t;
    ILuint ret;

    //20050102: Tests for IL_EOF were added because this function
    //crashed sometimes if igetc() returned IL_EOF
    //(for example "table-add-column-before-active.gif" included in the
    //mozilla source package)

    if (!state->nbits_left) {
    if (state->navail_bytes <= 0) {
    state->pbytes = state->byte_buff;
    state->navail_bytes = igetc();

    if(state->navail_bytes == IL_EOF) {
    success = IL_FALSE;
    return state->ending;
    }

    if (state->navail_bytes) {
    for (i = 0; i < state->navail_bytes; i++) {
    if((t = igetc()) == IL_EOF) {
    success = IL_FALSE;
    return state->ending;
    }
    state->byte_buff_ = t;
    }
    }
    }
    state->b1 = *state->pbytes++;
    state->nbits_left = 8;
    state->navail_bytes-;
    }

    ret = state->b1 >> (8 - state->nbits_left);
    while (state->curr_size >state-> nbits_left) {
    if (state->navail_bytes <= 0) {
    state->pbytes = state->byte_buff;
    state->navail_bytes = igetc();

    if(state->navail_bytes == IL_EOF) {
    success = IL_FALSE;
    return state->ending;
    }

    if (state->navail_bytes) {
    for (i = 0; i < state->navail_bytes; i++) {
    if((t = igetc()) == IL_EOF) {
    success = IL_FALSE;
    return state->ending;
    }
    state->byte_buff = t;
    }
    }
    }
    state->b1 = *state->pbytes++;
    ret |= state->b1 << state->nbits_left;
    state->nbits_left += 8;
    state->navail_bytes-;
    }
    state->nbits_left -= state->curr_size;

    return (ret & code_mask);
    }

    static void cleanUpGifLoadState(struct GifLoadState* state)
    {
    ifree(state->stack);
    ifree(state->suffix);
    ifree(state->prefix);
    }

    ILboolean GifGetData(ILimage *Image, ILubyte *Data, ILuint ImageSize,
       ILuint OffX, ILuint OffY, ILuint Width, ILuint Height, ILuint Stride,
       ILuint PalOffset, GFXCONTROL *Gfx)
    {
       struct GifLoadState state;
    ILubyte * sp;
    ILint code, fc, oc;
    ILubyte DisposalMethod = 0;
    ILint c, size;
    ILuint x = OffX, Read = 0, y = OffY;
       ILubyte * DataPtr = Data + y * Stride + x;

       state.navail_bytes = 0;
       state.nbits_left = 0;

    if (!Gfx->Used)
    DisposalMethod = (Gfx->Packed & 0x1C) >> 2;
    if((size = igetc()) == IL_EOF)
    return IL_FALSE;

    if (size < 2 || 9 < size) {
    return IL_FALSE;
    }

    state.stack  = (ILubyte*)ialloc(MAX_CODES + 1);
    state.suffix = (ILubyte*)ialloc(MAX_CODES + 1);
    state.prefix = (ILshort*)ialloc(sizeof(*state.prefix) * (MAX_CODES + 1));
    if (!state.stack || !state.suffix || !state.prefix)
    {
    cleanUpGifLoadState(&state);
    return IL_FALSE;
    }

    state.curr_size = size + 1;
    state.top_slot = 1 << state.curr_size;
    state.clear = 1 << size;
    state.ending = state.clear + 1;
    state.slot = state.newcodes = state.ending + 1;
    state.navail_bytes = state.nbits_left = 0;
    oc = fc = 0;
    sp = state.stack;

    while ((c = get_next_code(&state)) != state.ending && Read < Height) {
    if (c == state.clear)
    {
    state.curr_size = size + 1;
    state.slot = state.newcodes;
    state.top_slot = 1 << state.curr_size;
    while ((c = get_next_code(&state)) == state.clear);
    if (c == state.ending)
    break;
    if (c >= state.slot)
    c = 0;
    oc = fc = c;

    if (DisposalMethod == 1 && !Gfx->Used && Gfx->Transparent == c && (Gfx->Packed & 0x1) != 0)
    x++;
    else if (x < Width)
    DataPtr = c + PalOffset;

    if (x == Width)
    {
    DataPtr += Stride;
    x = 0;
    Read += 1;
                    ++y;
                    if (y >= Height) {
                       cleanUpGifLoadState(&state);
                       return IL_FALSE;
                    }
    }
    }
    else
    {
    code = c;
                //BG-2007-01-10: several fixes for incomplete GIFs
    if (code >= state.slot)
    {
    code = oc;
                    if (sp >= state.stack + MAX_CODES) {
                       cleanUpGifLoadState(&state);
                       return IL_FALSE;
                    }
                    *sp++ = fc;
    }

                if (code >= MAX_CODES)
                    return IL_FALSE;
                    while (code >= state.newcodes)
    {
                        if (sp >= state.stack + MAX_CODES)
    {
                            cleanUpGifLoadState(&state);
                            return IL_FALSE;
                        }
                        *sp++ = state.suffix_

    ;
                        code = state.prefix[code];
                    }
                
                    if (sp >= state.stack + MAX_CODES) {
                    cleanUpGifLoadState(&state);
                    return IL_FALSE;
                }
                *sp++ = (ILbyte)code;
                if (state.slot < state.top_slot)
                {
                    fc = code;
                    state.suffix[state.slot]   = fc;
                    state.prefix[state.slot++] = oc;
                    oc = c;
                }
                if (state.slot >= state.top_slot && state.curr_size < 12)
                {
                    state.top_slot <<= 1;
                    state.curr_size++;
                }
                while (sp > state.stack)
                {
                    sp--;
                    if (DisposalMethod == 1 && !Gfx->Used && Gfx->Transparent == *sp && (Gfx->Packed & 0x1) != 0)
                        x++;
                    else if (x < Width)
                        DataPtr[x++] = *sp + PalOffset;
                    if (x == Width)
                    {
                        x = 0;
                        Read += 1;
                        y = (y+1) % Height;
                        // Needs to start from Data, not Image->Data.
                        DataPtr = Data + y * Stride;
                    }
                }
            }
        }
        cleanUpGifLoadState(&state);
        return IL_TRUE;
    }
    /*From the GIF spec:
      The rows of an Interlaced images are arranged in the following order:
          Group 1 : Every 8th. row, starting with row 0.              (Pass 1)
          Group 2 : Every 8th. row, starting with row 4.              (Pass 2)
          Group 3 : Every 4th. row, starting with row 2.              (Pass 3)
          Group 4 : Every 2nd. row, starting with row 1.              (Pass 4)
    */
    ILboolean RemoveInterlace(ILimage *image)
    {
        ILubyte *NewData;
        ILuint  i, j = 0;
        NewData = (ILubyte*)ialloc(image->SizeOfData);
        if (NewData == NULL)
            return IL_FALSE;
        //changed 20041230: images with offsety != 0 were not
        //deinterlaced correctly before...
        for (i = 0; i < image->OffY; i++, j++) {
            memcpy(&NewData[i * image->Bps], &image->Data[j * image->Bps], image->Bps);
        }
        for (i = 0 + image->OffY; i < image->Height; i += 8, j++) {
            memcpy(&NewData[i * image->Bps], &image->Data[j * image->Bps], image->Bps);
        }
        for (i = 4 + image->OffY; i < image->Height; i += 8, j++) {
            memcpy(&NewData[i * image->Bps], &image->Data[j * image->Bps], image->Bps);
        }
        for (i = 2 + image->OffY; i < image->Height; i += 4, j++) {
            memcpy(&NewData[i * image->Bps], &image->Data[j * image->Bps], image->Bps);
        }
        for (i = 1 + image->OffY; i < image->Height; i += 2, j++) {
            memcpy(&NewData[i * image->Bps], &image->Data[j * image->Bps], image->Bps);
        }
        ifree(image->Data);
        image->Data = NewData;
        return IL_TRUE;
    }
    // Uses the transparent colour index to make an alpha channel.
    ILboolean ConvertTransparent(ILimage *Image, ILubyte TransColour)
    {
        ILubyte *Palette;
        ILuint  i, j;
        if (!Image->Pal.Palette || !Image->Pal.PalSize) {
            ilSetError(IL_INTERNAL_ERROR);
            return IL_FALSE;
        }
        Palette = (ILubyte*)ialloc(Image->Pal.PalSize / 3 * 4);
        if (Palette == NULL)
            return IL_FALSE;
        for (i = 0, j = 0; i < Image->Pal.PalSize; i += 3, j += 4) {
            Palette[j  ] = Image->Pal.Palette[i  ];
            Palette[j+1] = Image->Pal.Palette[i+1];
            Palette[j+2] = Image->Pal.Palette[i+2];
            if (j/4 == TransColour)
                Palette[j+3] = 0x00;
            else
                Palette[j+3] = 0xFF;
        }
        ifree(Image->Pal.Palette);
        Image->Pal.Palette = Palette;
        Image->Pal.PalSize = Image->Pal.PalSize / 3 * 4;
        Image->Pal.PalType = IL_PAL_RGBA32;
        return IL_TRUE;
    }
    #endif //IL_NO_GIF