IMG_gif.c
author Sam Lantinga
Wed, 09 Oct 2019 17:25:15 -0700
changeset 721 2ab0052024a3
parent 720 58e4a9ae8cd2
child 722 63ec5ac0b0b8
permissions -rw-r--r--
Use 100 ms as the default GIF animation delay if it's not specified, or very small.
This is compatible with IE, Mozilla, and Qt.
slouken@0
     1
/*
slouken@280
     2
  SDL_image:  An example image loading library for use with SDL
slouken@638
     3
  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@280
     5
  This software is provided 'as-is', without any express or implied
slouken@280
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@280
     7
  arising from the use of this software.
slouken@0
     8
slouken@280
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@280
    10
  including commercial applications, and to alter it and redistribute it
slouken@280
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@280
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@280
    14
     claim that you wrote the original software. If you use this software
slouken@280
    15
     in a product, an acknowledgment in the product documentation would be
slouken@280
    16
     appreciated but is not required.
slouken@280
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@280
    18
     misrepresented as being the original software.
slouken@280
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
slouken@0
    21
slouken@0
    22
/* This is a GIF image file loading framework */
slouken@0
    23
slouken@0
    24
#include "SDL_image.h"
slouken@0
    25
slouken@0
    26
#ifdef LOAD_GIF
slouken@0
    27
slouken@0
    28
/* Code from here to end of file has been adapted from XPaint:           */
slouken@0
    29
/* +-------------------------------------------------------------------+ */
slouken@368
    30
/* | Copyright 1990, 1991, 1993 David Koblas.                  | */
slouken@368
    31
/* | Copyright 1996 Torsten Martinsen.                     | */
slouken@0
    32
/* |   Permission to use, copy, modify, and distribute this software   | */
slouken@0
    33
/* |   and its documentation for any purpose and without fee is hereby | */
slouken@0
    34
/* |   granted, provided that the above copyright notice appear in all | */
slouken@0
    35
/* |   copies and that both that copyright notice and this permission  | */
slouken@0
    36
/* |   notice appear in supporting documentation.  This software is    | */
slouken@368
    37
/* |   provided "as is" without express or implied warranty.           | */
slouken@0
    38
/* +-------------------------------------------------------------------+ */
slouken@0
    39
slouken@0
    40
/* Adapted for use in SDL by Sam Lantinga -- 7/20/98 */
slouken@0
    41
/* Changes to work with SDL:
slouken@0
    42
slouken@0
    43
   Include SDL header file
slouken@0
    44
   Use SDL_Surface rather than xpaint Image structure
slouken@0
    45
   Define SDL versions of RWSetMsg(), ImageNewCmap() and ImageSetCmap()
slouken@0
    46
*/
slouken@0
    47
#include "SDL.h"
slouken@0
    48
slouken@368
    49
#define Image           SDL_Surface
slouken@368
    50
#define RWSetMsg        IMG_SetError
slouken@368
    51
#define ImageNewCmap(w, h, s)   SDL_CreateRGBSurface(SDL_SWSURFACE,w,h,8,0,0,0,0)
slouken@0
    52
#define ImageSetCmap(s, i, R, G, B) do { \
slouken@368
    53
                s->format->palette->colors[i].r = R; \
slouken@368
    54
                s->format->palette->colors[i].g = G; \
slouken@368
    55
                s->format->palette->colors[i].b = B; \
slouken@368
    56
            } while (0)
slouken@0
    57
/* * * * * */
slouken@0
    58
slouken@709
    59
#define GIF_DISPOSE_NA                  0   /* No disposal specified */
slouken@709
    60
#define GIF_DISPOSE_NONE                1   /* Do not dispose */
slouken@709
    61
#define GIF_DISPOSE_RESTORE_BACKGROUND  2   /* Restore to background */
slouken@709
    62
#define GIF_DISPOSE_RESTORE_PREVIOUS    3   /* Restore to previous */
slouken@0
    63
slouken@368
    64
#define MAXCOLORMAPSIZE     256
slouken@0
    65
slouken@368
    66
#define TRUE    1
slouken@368
    67
#define FALSE   0
slouken@0
    68
slouken@368
    69
#define CM_RED      0
slouken@368
    70
#define CM_GREEN    1
slouken@368
    71
#define CM_BLUE     2
slouken@0
    72
slouken@368
    73
#define MAX_LWZ_BITS        12
slouken@0
    74
slouken@368
    75
#define INTERLACE       0x40
slouken@368
    76
#define LOCALCOLORMAP   0x80
slouken@368
    77
#define BitSet(byte, bit)   (((byte) & (bit)) == (bit))
slouken@0
    78
slouken@368
    79
#define ReadOK(file,buffer,len) SDL_RWread(file, buffer, len, 1)
slouken@0
    80
slouken@368
    81
#define LM_to_uint(a,b)         (((b)<<8)|(a))
slouken@0
    82
slouken@649
    83
typedef struct {
slouken@649
    84
    struct {
slouken@649
    85
        unsigned int Width;
slouken@649
    86
        unsigned int Height;
slouken@649
    87
        unsigned char ColorMap[3][MAXCOLORMAPSIZE];
slouken@649
    88
        unsigned int BitPixel;
slouken@649
    89
        unsigned int ColorResolution;
slouken@649
    90
        unsigned int Background;
slouken@649
    91
        unsigned int AspectRatio;
slouken@649
    92
        int GrayScale;
slouken@649
    93
    } GifScreen;
slouken@0
    94
slouken@649
    95
    struct {
slouken@649
    96
        int transparent;
slouken@649
    97
        int delayTime;
slouken@649
    98
        int inputFlag;
slouken@649
    99
        int disposal;
slouken@649
   100
    } Gif89;
slouken@649
   101
slouken@649
   102
    unsigned char buf[280];
slouken@649
   103
    int curbit, lastbit, done, last_byte;
slouken@649
   104
slouken@649
   105
    int fresh;
slouken@649
   106
    int code_size, set_code_size;
slouken@649
   107
    int max_code, max_code_size;
slouken@649
   108
    int firstcode, oldcode;
slouken@649
   109
    int clear_code, end_code;
slouken@649
   110
    int table[2][(1 << MAX_LWZ_BITS)];
slouken@649
   111
    int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp;
slouken@649
   112
slouken@649
   113
    int ZeroDataBlock;
slouken@649
   114
} State_t;
slouken@0
   115
slouken@709
   116
typedef struct
slouken@709
   117
{
slouken@709
   118
    Image *image;
slouken@709
   119
    int x, y;
slouken@709
   120
    int disposal;
slouken@709
   121
    int delay;
slouken@709
   122
} Frame_t;
slouken@709
   123
slouken@709
   124
typedef struct
slouken@709
   125
{
slouken@709
   126
    int count;
slouken@709
   127
    Frame_t *frames;
slouken@709
   128
} Anim_t;
slouken@709
   129
slouken@0
   130
static int ReadColorMap(SDL_RWops * src, int number,
slouken@368
   131
            unsigned char buffer[3][MAXCOLORMAPSIZE], int *flag);
slouken@649
   132
static int DoExtension(SDL_RWops * src, int label, State_t * state);
slouken@649
   133
static int GetDataBlock(SDL_RWops * src, unsigned char *buf, State_t * state);
slouken@649
   134
static int GetCode(SDL_RWops * src, int code_size, int flag, State_t * state);
slouken@649
   135
static int LWZReadByte(SDL_RWops * src, int flag, int input_code_size, State_t * state);
slouken@0
   136
static Image *ReadImage(SDL_RWops * src, int len, int height, int,
slouken@368
   137
            unsigned char cmap[3][MAXCOLORMAPSIZE],
slouken@649
   138
            int gray, int interlace, int ignore, State_t * state);
slouken@0
   139
slouken@709
   140
static SDL_bool NormalizeFrames(Frame_t *frames, int count)
slouken@709
   141
{
slouken@709
   142
    SDL_Surface *image;
slouken@709
   143
    int i;
slouken@709
   144
    int lastDispose = GIF_DISPOSE_RESTORE_BACKGROUND;
slouken@709
   145
    int iRestore = 0;
slouken@709
   146
    Uint32 fill;
slouken@709
   147
    SDL_Rect rect;
slouken@709
   148
slouken@709
   149
slouken@709
   150
    if (SDL_HasColorKey(frames[0].image)) {
slouken@709
   151
        image = SDL_ConvertSurfaceFormat(frames[0].image, SDL_PIXELFORMAT_ARGB8888, 0);
slouken@709
   152
    } else {
slouken@709
   153
        image = SDL_ConvertSurfaceFormat(frames[0].image, SDL_PIXELFORMAT_RGB888, 0);
slouken@709
   154
    }
slouken@709
   155
    if (!image) {
slouken@709
   156
        return SDL_FALSE;
slouken@709
   157
    }
slouken@709
   158
slouken@709
   159
    fill = SDL_MapRGBA(image->format, 0, 0, 0, SDL_ALPHA_TRANSPARENT);
slouken@709
   160
slouken@709
   161
    rect.x = 0;
slouken@709
   162
    rect.y = 0;
slouken@709
   163
    rect.w = image->w;
slouken@709
   164
    rect.h = image->h;
slouken@709
   165
slouken@709
   166
    for (i = 0; i < count; ++i) {
slouken@709
   167
        switch (lastDispose) {
slouken@709
   168
        case GIF_DISPOSE_RESTORE_BACKGROUND:
slouken@709
   169
            SDL_FillRect(image, &rect, fill);
slouken@709
   170
            break;
slouken@709
   171
        case GIF_DISPOSE_RESTORE_PREVIOUS:
slouken@709
   172
            SDL_BlitSurface(frames[iRestore].image, &rect, image, &rect);
slouken@709
   173
            break;
slouken@709
   174
        default:
slouken@709
   175
            break;
slouken@709
   176
        }
slouken@709
   177
slouken@709
   178
        if (frames[i].disposal != GIF_DISPOSE_RESTORE_PREVIOUS) {
slouken@709
   179
            iRestore = i;
slouken@709
   180
        }
slouken@709
   181
slouken@709
   182
        rect.x = (Sint16)frames[i].x;
slouken@709
   183
        rect.y = (Sint16)frames[i].y;
slouken@709
   184
        rect.w = frames[i].image->w;
slouken@709
   185
        rect.h = frames[i].image->h;
slouken@709
   186
        SDL_BlitSurface(frames[i].image, NULL, image, &rect);
slouken@709
   187
slouken@709
   188
        SDL_FreeSurface(frames[i].image);
slouken@709
   189
        frames[i].image = SDL_DuplicateSurface(image);
slouken@711
   190
        if (!frames[i].image) {
slouken@711
   191
            return SDL_FALSE;
slouken@711
   192
        }
slouken@709
   193
slouken@709
   194
        lastDispose = frames[i].disposal;
slouken@709
   195
    }
slouken@709
   196
slouken@709
   197
    SDL_FreeSurface( image );
slouken@709
   198
slouken@709
   199
    return SDL_TRUE;
slouken@709
   200
}
slouken@709
   201
slouken@709
   202
Anim_t *
slouken@709
   203
IMG_LoadGIF_RW_Internal(SDL_RWops *src, SDL_bool load_anim)
slouken@0
   204
{
aschiffler@343
   205
    Sint64 start;
slouken@0
   206
    unsigned char buf[16];
slouken@0
   207
    unsigned char c;
slouken@0
   208
    unsigned char localColorMap[3][MAXCOLORMAPSIZE];
slouken@0
   209
    int grayScale;
slouken@0
   210
    int useGlobalColormap;
slouken@0
   211
    int bitPixel;
slouken@0
   212
    char version[4];
slouken@0
   213
    Image *image = NULL;
sezeroz@720
   214
    Anim_t *anim;
sezeroz@720
   215
    Frame_t *frames, *frame;
slouken@649
   216
    State_t state;
sezeroz@720
   217
slouken@649
   218
    state.ZeroDataBlock = FALSE;
slouken@649
   219
    state.fresh = FALSE;
Dmitry@660
   220
    state.last_byte = 0;
slouken@0
   221
sezeroz@703
   222
    if (src == NULL) {
sezeroz@703
   223
        return NULL;
slouken@0
   224
    }
slouken@118
   225
    start = SDL_RWtell(src);
slouken@118
   226
slouken@709
   227
    anim = (Anim_t *)SDL_calloc(1, sizeof(*anim));
slouken@709
   228
    if (!anim) {
slouken@709
   229
        SDL_OutOfMemory();
slouken@709
   230
        return NULL;
slouken@709
   231
    }
slouken@709
   232
slouken@0
   233
    if (!ReadOK(src, buf, 6)) {
sezeroz@703
   234
        RWSetMsg("error reading magic number");
slouken@0
   235
        goto done;
slouken@0
   236
    }
aschiffler@343
   237
    if (SDL_strncmp((char *) buf, "GIF", 3) != 0) {
sezeroz@703
   238
        RWSetMsg("not a GIF file");
slouken@0
   239
        goto done;
slouken@0
   240
    }
aschiffler@343
   241
    SDL_memcpy(version, (char *) buf + 3, 3);
slouken@0
   242
    version[3] = '\0';
slouken@0
   243
aschiffler@343
   244
    if ((SDL_strcmp(version, "87a") != 0) && (SDL_strcmp(version, "89a") != 0)) {
sezeroz@703
   245
        RWSetMsg("bad version number, not '87a' or '89a'");
slouken@0
   246
        goto done;
slouken@0
   247
    }
slouken@649
   248
    state.Gif89.transparent = -1;
slouken@649
   249
    state.Gif89.delayTime = -1;
slouken@649
   250
    state.Gif89.inputFlag = -1;
slouken@709
   251
    state.Gif89.disposal = GIF_DISPOSE_NA;
slouken@0
   252
slouken@0
   253
    if (!ReadOK(src, buf, 7)) {
sezeroz@703
   254
        RWSetMsg("failed to read screen descriptor");
slouken@0
   255
        goto done;
slouken@0
   256
    }
slouken@649
   257
    state.GifScreen.Width = LM_to_uint(buf[0], buf[1]);
slouken@649
   258
    state.GifScreen.Height = LM_to_uint(buf[2], buf[3]);
slouken@649
   259
    state.GifScreen.BitPixel = 2 << (buf[4] & 0x07);
slouken@649
   260
    state.GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
slouken@649
   261
    state.GifScreen.Background = buf[5];
slouken@649
   262
    state.GifScreen.AspectRatio = buf[6];
slouken@0
   263
slouken@368
   264
    if (BitSet(buf[4], LOCALCOLORMAP)) {    /* Global Colormap */
sezeroz@703
   265
        if (ReadColorMap(src, state.GifScreen.BitPixel,
sezeroz@703
   266
                         state.GifScreen.ColorMap, &state.GifScreen.GrayScale)) {
sezeroz@703
   267
            RWSetMsg("error reading global colormap");
slouken@0
   268
            goto done;
slouken@368
   269
        }
slouken@368
   270
    }
slouken@709
   271
    for ( ; ; ) {
slouken@368
   272
        if (!ReadOK(src, &c, 1)) {
sezeroz@703
   273
            RWSetMsg("EOF / read error on image data");
sezeroz@703
   274
            goto done;
sezeroz@703
   275
        }
sezeroz@703
   276
        if (c == ';') {     /* GIF terminator */
slouken@709
   277
            goto done;
slouken@368
   278
        }
sezeroz@703
   279
        if (c == '!') {     /* Extension */
sezeroz@703
   280
            if (!ReadOK(src, &c, 1)) {
sezeroz@703
   281
                RWSetMsg("EOF / read error on extention function code");
sezeroz@703
   282
                goto done;
sezeroz@703
   283
            }
sezeroz@703
   284
            DoExtension(src, c, &state);
sezeroz@703
   285
            continue;
sezeroz@703
   286
        }
sezeroz@703
   287
        if (c != ',') {     /* Not a valid start character */
sezeroz@703
   288
            continue;
sezeroz@703
   289
        }
slouken@0
   290
sezeroz@703
   291
        if (!ReadOK(src, buf, 9)) {
sezeroz@703
   292
            RWSetMsg("couldn't read left/top/width/height");
sezeroz@703
   293
            goto done;
slouken@368
   294
        }
sezeroz@703
   295
        useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
sezeroz@703
   296
sezeroz@703
   297
        bitPixel = 1 << ((buf[8] & 0x07) + 1);
sezeroz@703
   298
sezeroz@703
   299
        if (!useGlobalColormap) {
sezeroz@703
   300
            if (ReadColorMap(src, bitPixel, localColorMap, &grayScale)) {
sezeroz@703
   301
                RWSetMsg("error reading local colormap");
sezeroz@703
   302
                goto done;
sezeroz@703
   303
            }
sezeroz@703
   304
            image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
sezeroz@703
   305
                      LM_to_uint(buf[6], buf[7]),
sezeroz@703
   306
                      bitPixel, localColorMap, grayScale,
sezeroz@703
   307
                      BitSet(buf[8], INTERLACE),
slouken@709
   308
                      0, &state);
sezeroz@703
   309
        } else {
sezeroz@703
   310
            image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
sezeroz@703
   311
                      LM_to_uint(buf[6], buf[7]),
sezeroz@703
   312
                      state.GifScreen.BitPixel, state.GifScreen.ColorMap,
sezeroz@703
   313
                      state.GifScreen.GrayScale, BitSet(buf[8], INTERLACE),
slouken@709
   314
                      0, &state);
sezeroz@703
   315
        }
slouken@709
   316
slouken@709
   317
        if (image) {
slouken@709
   318
            if (state.Gif89.transparent >= 0) {
slouken@709
   319
                SDL_SetColorKey(image, SDL_TRUE, state.Gif89.transparent);
slouken@709
   320
            }
slouken@0
   321
slouken@709
   322
            frames = (Frame_t *)SDL_realloc(anim->frames, (anim->count + 1) * sizeof(*anim->frames));
slouken@709
   323
            if (!frames) {
slouken@709
   324
                SDL_OutOfMemory();
slouken@709
   325
                goto done;
slouken@709
   326
            }
slouken@709
   327
            ++anim->count;
slouken@709
   328
            anim->frames = frames;
slouken@709
   329
            frame = &anim->frames[anim->count - 1];
slouken@709
   330
slouken@709
   331
            frame->image = image;
slouken@709
   332
            frame->x = LM_to_uint(buf[0], buf[1]);
slouken@709
   333
            frame->y = LM_to_uint(buf[2], buf[3]);
slouken@709
   334
            frame->disposal = state.Gif89.disposal;
slouken@721
   335
            frame->delay = (state.Gif89.delayTime < 2 ? 10 : state.Gif89.delayTime) * 10;
slouken@709
   336
slouken@709
   337
            if (!load_anim) {
slouken@709
   338
                /* We only need one frame, we're done */
slouken@709
   339
                goto done;
slouken@709
   340
            }
slouken@709
   341
        }
slouken@0
   342
    }
slouken@0
   343
slouken@0
   344
done:
slouken@709
   345
    if (anim->count > 1) {
slouken@709
   346
        /* Normalize the frames */
slouken@709
   347
        if (!NormalizeFrames(anim->frames, anim->count)) {
slouken@709
   348
            int i;
slouken@709
   349
            for (i = 0; i < anim->count; ++i) {
slouken@709
   350
                SDL_FreeSurface(anim->frames[i].image);
slouken@709
   351
            }
slouken@709
   352
            anim->count = 0;
slouken@709
   353
        }
slouken@118
   354
    }
slouken@709
   355
    if (anim->count == 0) {
slouken@709
   356
        SDL_free(anim->frames);
slouken@709
   357
        SDL_free(anim);
slouken@709
   358
        return NULL;
slouken@709
   359
    }
slouken@709
   360
    return anim;
slouken@0
   361
}
slouken@0
   362
slouken@0
   363
static int
slouken@0
   364
ReadColorMap(SDL_RWops *src, int number,
slouken@0
   365
             unsigned char buffer[3][MAXCOLORMAPSIZE], int *gray)
slouken@0
   366
{
slouken@0
   367
    int i;
slouken@0
   368
    unsigned char rgb[3];
slouken@0
   369
    int flag;
slouken@0
   370
slouken@0
   371
    flag = TRUE;
slouken@0
   372
slouken@0
   373
    for (i = 0; i < number; ++i) {
sezeroz@703
   374
        if (!ReadOK(src, rgb, sizeof(rgb))) {
sezeroz@703
   375
            RWSetMsg("bad colormap");
sezeroz@703
   376
            return 1;
sezeroz@703
   377
        }
sezeroz@703
   378
        buffer[CM_RED][i] = rgb[0];
sezeroz@703
   379
        buffer[CM_GREEN][i] = rgb[1];
sezeroz@703
   380
        buffer[CM_BLUE][i] = rgb[2];
sezeroz@703
   381
        flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
slouken@0
   382
    }
slouken@0
   383
slouken@0
   384
#if 0
slouken@0
   385
    if (flag)
sezeroz@703
   386
        *gray = (number == 2) ? PBM_TYPE : PGM_TYPE;
slouken@0
   387
    else
sezeroz@703
   388
        *gray = PPM_TYPE;
slouken@0
   389
#else
slouken@0
   390
    *gray = 0;
slouken@0
   391
#endif
slouken@0
   392
slouken@0
   393
    return FALSE;
slouken@0
   394
}
slouken@0
   395
slouken@0
   396
static int
slouken@649
   397
DoExtension(SDL_RWops *src, int label, State_t * state)
slouken@0
   398
{
slouken@649
   399
    unsigned char buf[256];
slouken@0
   400
    char *str;
slouken@0
   401
slouken@0
   402
    switch (label) {
slouken@368
   403
    case 0x01:          /* Plain Text Extension */
sezeroz@703
   404
        str = "Plain Text Extension";
sezeroz@703
   405
        break;
slouken@368
   406
    case 0xff:          /* Application Extension */
sezeroz@703
   407
        str = "Application Extension";
sezeroz@703
   408
        break;
slouken@368
   409
    case 0xfe:          /* Comment Extension */
sezeroz@703
   410
        str = "Comment Extension";
sezeroz@703
   411
        while (GetDataBlock(src, (unsigned char *) buf, state) > 0)
sezeroz@703
   412
            ;
sezeroz@703
   413
        return FALSE;
slouken@368
   414
    case 0xf9:          /* Graphic Control Extension */
sezeroz@703
   415
        str = "Graphic Control Extension";
sezeroz@703
   416
        (void) GetDataBlock(src, (unsigned char *) buf, state);
sezeroz@703
   417
        state->Gif89.disposal = (buf[0] >> 2) & 0x7;
sezeroz@703
   418
        state->Gif89.inputFlag = (buf[0] >> 1) & 0x1;
sezeroz@703
   419
        state->Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
sezeroz@703
   420
        if ((buf[0] & 0x1) != 0)
sezeroz@703
   421
            state->Gif89.transparent = buf[3];
sezeroz@703
   422
sezeroz@703
   423
        while (GetDataBlock(src, (unsigned char *) buf, state) > 0)
sezeroz@703
   424
            ;
sezeroz@703
   425
        return FALSE;
sezeroz@703
   426
    default:
sezeroz@703
   427
        str = (char *)buf;
sezeroz@703
   428
        SDL_snprintf(str, 256, "UNKNOWN (0x%02x)", label);
sezeroz@703
   429
        break;
sezeroz@703
   430
    }
slouken@0
   431
slouken@649
   432
    while (GetDataBlock(src, (unsigned char *) buf, state) > 0)
slouken@368
   433
        ;
slouken@0
   434
slouken@0
   435
    return FALSE;
slouken@0
   436
}
slouken@0
   437
slouken@0
   438
static int
slouken@649
   439
GetDataBlock(SDL_RWops *src, unsigned char *buf, State_t * state)
slouken@0
   440
{
slouken@0
   441
    unsigned char count;
slouken@0
   442
slouken@0
   443
    if (!ReadOK(src, &count, 1)) {
sezeroz@703
   444
        /* pm_message("error in getting DataBlock size" ); */
sezeroz@703
   445
        return -1;
slouken@0
   446
    }
slouken@649
   447
    state->ZeroDataBlock = count == 0;
slouken@0
   448
slouken@0
   449
    if ((count != 0) && (!ReadOK(src, buf, count))) {
sezeroz@703
   450
        /* pm_message("error in reading DataBlock" ); */
sezeroz@703
   451
        return -1;
slouken@0
   452
    }
slouken@0
   453
    return count;
slouken@0
   454
}
slouken@0
   455
slouken@0
   456
static int
slouken@649
   457
GetCode(SDL_RWops *src, int code_size, int flag, State_t * state)
slouken@0
   458
{
slouken@0
   459
    int i, j, ret;
slouken@0
   460
    unsigned char count;
slouken@0
   461
slouken@0
   462
    if (flag) {
sezeroz@703
   463
        state->curbit = 0;
sezeroz@703
   464
        state->lastbit = 0;
sezeroz@703
   465
        state->done = FALSE;
sezeroz@703
   466
        return 0;
slouken@0
   467
    }
slouken@649
   468
    if ((state->curbit + code_size) >= state->lastbit) {
sezeroz@703
   469
        if (state->done) {
sezeroz@703
   470
            if (state->curbit >= state->lastbit)
sezeroz@703
   471
                RWSetMsg("ran off the end of my bits");
sezeroz@703
   472
            return -1;
sezeroz@703
   473
        }
sezeroz@703
   474
        state->buf[0] = state->buf[state->last_byte - 2];
sezeroz@703
   475
        state->buf[1] = state->buf[state->last_byte - 1];
slouken@0
   476
sezeroz@708
   477
        if ((ret = GetDataBlock(src, &state->buf[2], state)) > 0)
sezeroz@708
   478
            count = (unsigned char) ret;
sezeroz@708
   479
        else {
sezeroz@708
   480
            count = 0;
sezeroz@703
   481
            state->done = TRUE;
sezeroz@708
   482
        }
slouken@0
   483
sezeroz@703
   484
        state->last_byte = 2 + count;
sezeroz@703
   485
        state->curbit = (state->curbit - state->lastbit) + 16;
sezeroz@703
   486
        state->lastbit = (2 + count) * 8;
slouken@0
   487
    }
slouken@0
   488
    ret = 0;
slouken@649
   489
    for (i = state->curbit, j = 0; j < code_size; ++i, ++j)
sezeroz@703
   490
        ret |= ((state->buf[i / 8] & (1 << (i % 8))) != 0) << j;
slouken@0
   491
slouken@649
   492
    state->curbit += code_size;
slouken@0
   493
slouken@0
   494
    return ret;
slouken@0
   495
}
slouken@0
   496
slouken@0
   497
static int
slouken@649
   498
LWZReadByte(SDL_RWops *src, int flag, int input_code_size, State_t * state)
slouken@0
   499
{
slouken@0
   500
    int code, incode;
slouken@0
   501
    register int i;
slouken@0
   502
slouken@181
   503
    /* Fixed buffer overflow found by Michael Skladnikiewicz */
slouken@181
   504
    if (input_code_size > MAX_LWZ_BITS)
slouken@181
   505
        return -1;
slouken@181
   506
slouken@0
   507
    if (flag) {
sezeroz@703
   508
        state->set_code_size = input_code_size;
sezeroz@703
   509
        state->code_size = state->set_code_size + 1;
sezeroz@703
   510
        state->clear_code = 1 << state->set_code_size;
sezeroz@703
   511
        state->end_code = state->clear_code + 1;
sezeroz@703
   512
        state->max_code_size = 2 * state->clear_code;
sezeroz@703
   513
        state->max_code = state->clear_code + 2;
slouken@0
   514
sezeroz@703
   515
        GetCode(src, 0, TRUE, state);
slouken@0
   516
sezeroz@703
   517
        state->fresh = TRUE;
slouken@0
   518
sezeroz@703
   519
        for (i = 0; i < state->clear_code; ++i) {
sezeroz@703
   520
            state->table[0][i] = 0;
sezeroz@703
   521
            state->table[1][i] = i;
sezeroz@703
   522
        }
sezeroz@703
   523
        state->table[1][0] = 0;
sezeroz@703
   524
        for (; i < (1 << MAX_LWZ_BITS); ++i)
sezeroz@703
   525
            state->table[0][i] = 0;
slouken@0
   526
sezeroz@703
   527
        state->sp = state->stack;
slouken@0
   528
sezeroz@703
   529
        return 0;
slouken@649
   530
    } else if (state->fresh) {
sezeroz@703
   531
        state->fresh = FALSE;
sezeroz@703
   532
        do {
sezeroz@703
   533
            state->firstcode = state->oldcode = GetCode(src, state->code_size, FALSE, state);
sezeroz@703
   534
        } while (state->firstcode == state->clear_code);
sezeroz@703
   535
        return state->firstcode;
slouken@0
   536
    }
slouken@649
   537
    if (state->sp > state->stack)
sezeroz@703
   538
        return *--state->sp;
slouken@0
   539
slouken@649
   540
    while ((code = GetCode(src, state->code_size, FALSE, state)) >= 0) {
sezeroz@703
   541
        if (code == state->clear_code) {
sezeroz@703
   542
            for (i = 0; i < state->clear_code; ++i) {
sezeroz@703
   543
                state->table[0][i] = 0;
sezeroz@703
   544
                state->table[1][i] = i;
sezeroz@703
   545
            }
sezeroz@703
   546
            for (; i < (1 << MAX_LWZ_BITS); ++i)
sezeroz@703
   547
                state->table[0][i] = state->table[1][i] = 0;
sezeroz@703
   548
            state->code_size = state->set_code_size + 1;
sezeroz@703
   549
            state->max_code_size = 2 * state->clear_code;
sezeroz@703
   550
            state->max_code = state->clear_code + 2;
sezeroz@703
   551
            state->sp = state->stack;
sezeroz@703
   552
            state->firstcode = state->oldcode = GetCode(src, state->code_size, FALSE, state);
sezeroz@703
   553
            return state->firstcode;
sezeroz@703
   554
        } else if (code == state->end_code) {
sezeroz@703
   555
            int count;
sezeroz@703
   556
            unsigned char buf[260];
sezeroz@703
   557
sezeroz@703
   558
            if (state->ZeroDataBlock)
sezeroz@703
   559
                return -2;
sezeroz@703
   560
sezeroz@703
   561
            while ((count = GetDataBlock(src, buf, state)) > 0)
sezeroz@703
   562
                ;
slouken@0
   563
sezeroz@703
   564
            if (count != 0) {
sezeroz@703
   565
            /*
sezeroz@703
   566
             * pm_message("missing EOD in data stream (common occurence)");
sezeroz@703
   567
             */
sezeroz@703
   568
            }
sezeroz@703
   569
            return -2;
sezeroz@703
   570
        }
sezeroz@703
   571
        incode = code;
slouken@0
   572
sezeroz@703
   573
        if (code >= state->max_code) {
sezeroz@703
   574
            *state->sp++ = state->firstcode;
sezeroz@703
   575
            code = state->oldcode;
slouken@368
   576
        }
sezeroz@703
   577
        while (code >= state->clear_code) {
sezeroz@703
   578
            /* Guard against buffer overruns */
sezeroz@703
   579
            if (code < 0 || code >= (1 << MAX_LWZ_BITS)) {
sezeroz@703
   580
                RWSetMsg("invalid LWZ data");
sezeroz@703
   581
                return -3;
sezeroz@703
   582
            }
sezeroz@703
   583
            *state->sp++ = state->table[1][code];
sezeroz@703
   584
            if (code == state->table[0][code]) {
sezeroz@703
   585
                RWSetMsg("circular table entry BIG ERROR");
sezeroz@703
   586
                return -3;
sezeroz@703
   587
            }
sezeroz@703
   588
            code = state->table[0][code];
sezeroz@703
   589
        }
slouken@0
   590
slouken@368
   591
        /* Guard against buffer overruns */
slouken@368
   592
        if (code < 0 || code >= (1 << MAX_LWZ_BITS)) {
slouken@368
   593
            RWSetMsg("invalid LWZ data");
sezeroz@703
   594
            return -4;
slouken@368
   595
        }
sezeroz@703
   596
        *state->sp++ = state->firstcode = state->table[1][code];
slouken@0
   597
sezeroz@703
   598
        if ((code = state->max_code) < (1 << MAX_LWZ_BITS)) {
sezeroz@703
   599
            state->table[0][code] = state->oldcode;
sezeroz@703
   600
            state->table[1][code] = state->firstcode;
sezeroz@703
   601
            ++state->max_code;
sezeroz@703
   602
            if ((state->max_code >= state->max_code_size) &&
sezeroz@703
   603
                (state->max_code_size < (1 << MAX_LWZ_BITS))) {
sezeroz@703
   604
                state->max_code_size *= 2;
sezeroz@703
   605
                ++state->code_size;
sezeroz@703
   606
            }
sezeroz@703
   607
        }
sezeroz@703
   608
        state->oldcode = incode;
slouken@0
   609
sezeroz@703
   610
        if (state->sp > state->stack)
sezeroz@703
   611
            return *--state->sp;
slouken@0
   612
    }
slouken@0
   613
    return code;
slouken@0
   614
}
slouken@0
   615
slouken@0
   616
static Image *
slouken@0
   617
ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
sezeroz@703
   618
          unsigned char cmap[3][MAXCOLORMAPSIZE],
sezeroz@720
   619
          int gray, int interlace, int ignore, State_t * state)
slouken@0
   620
{
slouken@0
   621
    Image *image;
slouken@0
   622
    unsigned char c;
slouken@0
   623
    int i, v;
slouken@0
   624
    int xpos = 0, ypos = 0, pass = 0;
slouken@0
   625
sezeroz@720
   626
    (void) gray; /* unused */
sezeroz@720
   627
slouken@0
   628
    /*
slouken@368
   629
    **  Initialize the compression routines
slouken@0
   630
     */
slouken@0
   631
    if (!ReadOK(src, &c, 1)) {
sezeroz@703
   632
        RWSetMsg("EOF / read error on image data");
sezeroz@703
   633
        return NULL;
slouken@0
   634
    }
slouken@649
   635
    if (LWZReadByte(src, TRUE, c, state) < 0) {
sezeroz@703
   636
        RWSetMsg("error reading image");
sezeroz@703
   637
        return NULL;
slouken@0
   638
    }
slouken@0
   639
    /*
slouken@368
   640
    **  If this is an "uninteresting picture" ignore it.
slouken@0
   641
     */
slouken@0
   642
    if (ignore) {
sezeroz@703
   643
        while (LWZReadByte(src, FALSE, c, state) >= 0)
sezeroz@703
   644
            ;
sezeroz@703
   645
        return NULL;
slouken@0
   646
    }
slouken@0
   647
    image = ImageNewCmap(len, height, cmapSize);
slouken@0
   648
slouken@0
   649
    for (i = 0; i < cmapSize; i++)
sezeroz@703
   650
        ImageSetCmap(image, i, cmap[CM_RED][i],
sezeroz@703
   651
                     cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
slouken@0
   652
slouken@649
   653
    while ((v = LWZReadByte(src, FALSE, c, state)) >= 0) {
slouken@719
   654
        ((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = (Uint8)v;
sezeroz@703
   655
        ++xpos;
sezeroz@703
   656
        if (xpos == len) {
sezeroz@703
   657
            xpos = 0;
sezeroz@703
   658
            if (interlace) {
sezeroz@703
   659
                switch (pass) {
sezeroz@703
   660
                case 0:
sezeroz@703
   661
                case 1:
sezeroz@703
   662
                    ypos += 8;
sezeroz@703
   663
                    break;
sezeroz@703
   664
                case 2:
sezeroz@703
   665
                    ypos += 4;
sezeroz@703
   666
                    break;
sezeroz@703
   667
                case 3:
sezeroz@703
   668
                    ypos += 2;
sezeroz@703
   669
                    break;
sezeroz@703
   670
                }
slouken@0
   671
sezeroz@703
   672
                if (ypos >= height) {
sezeroz@703
   673
                    ++pass;
sezeroz@703
   674
                    switch (pass) {
sezeroz@703
   675
                    case 1:
sezeroz@703
   676
                        ypos = 4;
sezeroz@703
   677
                        break;
sezeroz@703
   678
                    case 2:
sezeroz@703
   679
                        ypos = 2;
sezeroz@703
   680
                        break;
sezeroz@703
   681
                    case 3:
sezeroz@703
   682
                        ypos = 1;
sezeroz@703
   683
                        break;
sezeroz@703
   684
                    default:
sezeroz@703
   685
                        goto fini;
sezeroz@703
   686
                    }
sezeroz@703
   687
                }
sezeroz@703
   688
            } else {
sezeroz@703
   689
                ++ypos;
slouken@368
   690
            }
slouken@368
   691
        }
sezeroz@703
   692
        if (ypos >= height)
sezeroz@703
   693
            break;
slouken@0
   694
    }
slouken@0
   695
slouken@0
   696
  fini:
slouken@0
   697
slouken@0
   698
    return image;
slouken@0
   699
}
slouken@0
   700
slouken@709
   701
/* Load a GIF type animation from an SDL datasource */
slouken@709
   702
IMG_Animation *IMG_LoadGIFAnimation_RW(SDL_RWops *src)
slouken@709
   703
{
slouken@709
   704
    Anim_t *internal = IMG_LoadGIF_RW_Internal(src, SDL_TRUE);
slouken@709
   705
    if (internal) {
slouken@709
   706
        IMG_Animation *anim = (IMG_Animation *)SDL_malloc(sizeof(*anim));
slouken@709
   707
        if (anim) {
slouken@709
   708
            anim->w = internal->frames[0].image->w;
slouken@709
   709
            anim->h = internal->frames[0].image->h;
slouken@709
   710
            anim->count = internal->count;
slouken@709
   711
slouken@709
   712
            anim->frames = (SDL_Surface **)SDL_calloc(anim->count, sizeof(*anim->frames));
slouken@709
   713
            anim->delays = (int *)SDL_calloc(anim->count, sizeof(*anim->delays));
slouken@709
   714
slouken@709
   715
            if (anim->frames && anim->delays) {
slouken@709
   716
                int i;
slouken@709
   717
slouken@709
   718
                for (i = 0; i < anim->count; ++i) {
slouken@709
   719
                    anim->frames[i] = internal->frames[i].image;
slouken@709
   720
                    anim->delays[i] = internal->frames[i].delay;
slouken@709
   721
                }
slouken@709
   722
            } else {
slouken@709
   723
                IMG_FreeAnimation(anim);
slouken@709
   724
                anim = NULL;
slouken@709
   725
            }
slouken@709
   726
        }
slouken@709
   727
        if (!anim) {
slouken@709
   728
            SDL_OutOfMemory();
slouken@709
   729
        }
slouken@709
   730
        SDL_free(internal->frames);
slouken@709
   731
        SDL_free(internal);
slouken@709
   732
        return anim;
slouken@709
   733
    }
slouken@709
   734
    return NULL;
slouken@709
   735
}
slouken@709
   736
slouken@709
   737
#else
slouken@709
   738
slouken@709
   739
/* Load a GIF type animation from an SDL datasource */
slouken@709
   740
IMG_Animation *IMG_LoadGIFAnimation_RW(SDL_RWops *src)
slouken@709
   741
{
slouken@709
   742
    return NULL;
slouken@709
   743
}
slouken@709
   744
slouken@709
   745
#endif /* LOAD_GIF */
slouken@709
   746
slouken@709
   747
#if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)
slouken@709
   748
slouken@709
   749
#ifdef LOAD_GIF
slouken@709
   750
slouken@709
   751
/* See if an image is contained in a data source */
slouken@709
   752
int IMG_isGIF(SDL_RWops *src)
slouken@709
   753
{
slouken@709
   754
    Sint64 start;
slouken@709
   755
    int is_GIF;
slouken@709
   756
    char magic[6];
slouken@709
   757
slouken@709
   758
    if ( !src )
slouken@709
   759
        return 0;
slouken@709
   760
    start = SDL_RWtell(src);
slouken@709
   761
    is_GIF = 0;
slouken@709
   762
    if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
slouken@709
   763
        if ( (SDL_strncmp(magic, "GIF", 3) == 0) &&
slouken@709
   764
             ((SDL_memcmp(magic + 3, "87a", 3) == 0) ||
slouken@709
   765
              (SDL_memcmp(magic + 3, "89a", 3) == 0)) ) {
slouken@709
   766
            is_GIF = 1;
slouken@709
   767
        }
slouken@709
   768
    }
slouken@709
   769
    SDL_RWseek(src, start, RW_SEEK_SET);
slouken@709
   770
    return(is_GIF);
slouken@709
   771
}
slouken@709
   772
slouken@709
   773
/* Load a GIF type image from an SDL datasource */
slouken@709
   774
SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
slouken@709
   775
{
slouken@709
   776
    SDL_Surface *image = NULL;
slouken@709
   777
    Anim_t *internal = IMG_LoadGIF_RW_Internal(src, SDL_FALSE);
slouken@709
   778
    if (internal) {
slouken@709
   779
        image = internal->frames[0].image;
slouken@709
   780
        SDL_free(internal->frames);
slouken@709
   781
        SDL_free(internal);
slouken@709
   782
    }
slouken@709
   783
    return image;
slouken@709
   784
}
slouken@709
   785
slouken@0
   786
#else
slouken@719
   787
#if _MSC_VER >= 1300
slouken@719
   788
#pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */
slouken@719
   789
#endif
slouken@0
   790
slouken@0
   791
/* See if an image is contained in a data source */
slouken@0
   792
int IMG_isGIF(SDL_RWops *src)
slouken@0
   793
{
slouken@368
   794
    return(0);
slouken@0
   795
}
slouken@0
   796
slouken@0
   797
/* Load a GIF type image from an SDL datasource */
slouken@0
   798
SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
slouken@0
   799
{
slouken@368
   800
    return(NULL);
slouken@0
   801
}
slouken@0
   802
slouken@0
   803
#endif /* LOAD_GIF */
slouken@237
   804
slouken@237
   805
#endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */