Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
5131 clevermous 1
/*
2
SDL_flic - renders FLIC animations
3
Copyright (C) 2003 Andre de Leiradella
4
 
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Lesser General Public
7
License as published by the Free Software Foundation; either
8
version 2.1 of the License, or (at your option) any later version.
9
 
10
This library is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
Lesser General Public License for more details.
14
 
15
You should have received a copy of the GNU Lesser General Public
16
License along with this library; if not, write to the Free Software
17
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 
19
For information about SDL_flic contact leiradella@bigfoot.com
20
 
21
Version 1.0: first public release.
22
Version 1.1: fixed bug to set *error to FLI_OK when returning successfully from FLI_Open
23
             added function FLI_Reset to reset the animation to the first frame
24
Version 1.2: added function FLI_Skip to skip the current frame without rendering
25
             FLI_Animation->surface is now correctly locked and unlocked
26
             the rwops stream is now part of the FLI_Animation structure and is closed inside FLI_Close
27
             renamed FLI_Reset to FLI_Rewind
28
             added function FLI_Version that returns the library version
29
*/
30
#include 
31
#include 
32
#include 
33
#include 
34
 
35
/* Library version. */
36
#define FLI_MAJOR 1
37
#define FLI_MINOR 2
38
 
39
/* Chunk types. */
40
#define FLI_COLOR256 4
41
#define FLI_SS2      7
42
#define FLI_COLOR    11
43
#define FLI_LC       12
44
#define FLI_BLACK    13
45
#define FLI_BRUN     15
46
#define FLI_COPY     16
47
#define FLI_PSTAMP   18
48
 
49
typedef struct {
50
        Uint32 size, type, numchunks;
51
} FLI_Frame;
52
 
53
typedef struct {
54
        Uint32 size, type, index;
55
} FLI_Chunk;
56
 
57
static INLINE void readbuffer(FLI_Animation *flic, void *buffer, int size) {
58
        if (SDL_RWread(flic->rwops, buffer, 1, size) != size)
59
                longjmp(flic->error, FLI_READERROR);
60
}
61
 
62
static INLINE Uint8 readu8(FLI_Animation *flic) {
63
        Uint8 b;
64
 
65
        readbuffer(flic, &b, 1);
66
        return b;
67
}
68
 
69
static INLINE Uint16 readu16(FLI_Animation *flic) {
70
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
71
        Uint16 hi, lo;
72
 
73
        readbuffer(flic, &lo, 1);
74
        readbuffer(flic, &hi, 1);
75
        return hi << 8 | lo;
76
#else
77
        Uint16 w;
78
 
79
        readbuffer(flic, &w, 2);
80
        return w;
81
#endif
82
}
83
 
84
static INLINE Uint32 readu32(FLI_Animation *flic) {
85
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
86
        Uint32 hi;
87
 
88
        hi = readu16(flic);
89
        return hi << 16 | readu16(flic);
90
#else
91
        Uint32 u;
92
 
93
        readbuffer(flic, &u, 4);
94
        return u;
95
#endif
96
}
97
 
98
static void readheader(FLI_Animation *flic) {
99
        /* Skip size, we don't need it. */
100
        SDL_RWseek(flic->rwops, 4, SEEK_CUR);
101
        /* Read and check magic. */
102
        flic->format = readu16(flic);
103
        if (flic->format != FLI_FLI && flic->format != FLI_FLC)
104
                longjmp(flic->error, FLI_CORRUPTEDFILE);
105
        /* Read number of frames, maximum is 4000 for FLI and FLC files. */
106
        flic->numframes = readu16(flic);
107
        if (flic->numframes > 4000)
108
                longjmp(flic->error, FLI_CORRUPTEDFILE);
109
        /* Read width and height, must be 320x200 for FLI files. */
110
        flic->width = readu16(flic);
111
        flic->height = readu16(flic);
112
        if (flic->format == FLI_FLI && (flic->width != 320 || flic->height != 200))
113
                longjmp(flic->error, FLI_CORRUPTEDFILE);
114
        /* Read color depth, must be 8 for FLI and FLC files. */
115
        flic->depth = readu16(flic);
116
        if (flic->depth != 8)
117
                longjmp(flic->error, FLI_CORRUPTEDFILE);
118
        /* Skip the flags, it doesn't look like it follows the specs. */
119
        readu16(flic);
120
        /* Read the delay between frames. */
121
        flic->delay = (flic->format == FLI_FLI) ? readu16(flic) : readu32(flic);
122
        /* Skip rest of the header. */
123
        SDL_RWseek(flic->rwops, (flic->format == FLI_FLI) ? 110 : 108, SEEK_CUR);
124
}
125
 
126
static INLINE void readframe(FLI_Animation *flic, FLI_Frame *frame) {
127
        /* Read the size of the frame, must be less than or equal to 64k in FLI files. */
128
        frame->size = readu32(flic);
129
        if (flic->format == FLI_FLI && frame->size > 65536)
130
                longjmp(flic->error, FLI_CORRUPTEDFILE);
131
        /* Read the type of the frame, must be 0xF1FA in FLI files or 0xF1FA or 0xF100 in FLC files. */
132
        frame->type = readu16(flic);
133
        if (frame->type != 0xF1FA && (flic->format == FLI_FLC && frame->type != 0xF100))
134
                longjmp(flic->error, FLI_CORRUPTEDFILE);
135
        /* Read the number of chunks in this frame. */
136
        frame->numchunks = readu16(flic);
137
        /* Skip rest of the data. */
138
        SDL_RWseek(flic->rwops, 8, SEEK_CUR);
139
}
140
 
141
static INLINE void readchunk(FLI_Animation *flic, FLI_Chunk *chunk) {
142
        /* Read the chunk size. */
143
        chunk->size = readu32(flic);
144
        /* Read the chunk type. */
145
        chunk->type = readu16(flic);
146
}
147
 
148
static void handlecolor(FLI_Animation *flic, FLI_Chunk *chunk) {
149
        int       numpackets, index, count;
150
        SDL_Color color;
151
 
152
        (void)chunk;
153
        /* Number of packets. */
154
        numpackets = readu16(flic);
155
        /* Color index that will be changed. */
156
        index = 0;
157
        while (numpackets-- > 0) {
158
                /* Skip some colors. */
159
                index += readu8(flic);
160
                /* And change some others. */
161
                count = readu8(flic);
162
                if (count == 0)
163
                        count = 256;
164
                while (count-- > 0) {
165
                        /* r, g and b are in the range [0..63]. */
166
                        color.r = ((Uint32)readu8(flic)) * 255 / 63;
167
                        color.g = ((Uint32)readu8(flic)) * 255 / 63;
168
                        color.b = ((Uint32)readu8(flic)) * 255 / 63;
169
                        SDL_SetColors(flic->surface, &color, index++, 1);
170
                }
171
        }
172
}
173
 
174
static void handlelc(FLI_Animation *flic, FLI_Chunk *chunk) {
175
        int    numlines, numpackets, size;
176
        Uint8  *line, *p;
177
 
178
        (void)chunk;
179
        /* Skip lines at the top of the image. */
180
        line = (Uint8 *)flic->surface->pixels + readu16(flic) * flic->surface->pitch;
181
        /* numlines lines will change. */
182
        numlines = readu16(flic);
183
        while (numlines-- > 0) {
184
                p = line;
185
                line += flic->surface->pitch;
186
                /* Each line has numpackets changes. */
187
                numpackets = readu8(flic);
188
                while (numpackets-- > 0) {
189
                        /* Skip pixels at the beginning of the line. */
190
                        p += readu8(flic);
191
                        /* size pixels will change. */
192
                        size = (Sint8)readu8(flic);
193
                        if (size >= 0) {
194
                                /* Pixels follow. */
195
                                readbuffer(flic, (void *)p, size);
196
                        } else {
197
                                size = -size;
198
                                /* One pixel to be repeated follow. */
199
                                memset((void *)p, readu8(flic), size);
200
                        }
201
                        p += size;
202
                }
203
        }
204
}
205
 
206
static void handleblack(FLI_Animation *flic, FLI_Chunk *chunk) {
207
        (void)chunk;
208
        /* Fill the surface with color 0. */
209
        if (SDL_FillRect(flic->surface, NULL, 0) != 0)
210
                longjmp(flic->error, FLI_SDLERROR);
211
}
212
 
213
static void handlebrun(FLI_Animation *flic, FLI_Chunk *chunk) {
214
        int    numlines, size;
215
        Uint8  *p, *next;
216
 
217
        (void)chunk;
218
        /* Begin at the top of the image. */
219
        p = (Uint8 *)flic->surface->pixels;
220
        /* All lines will change. */
221
        numlines = flic->height;
222
        while (numlines-- > 0) {
223
                /* The number of packages is ignored, packets run until the next line is reached. */
224
                readu8(flic);
225
                next = p + flic->surface->pitch;
226
                while (p < next) {
227
                        /* size pixels will change. */
228
                        size = (Sint8)readu8(flic);
229
                        if (size < 0) {
230
                                size = -size;
231
                                /* Pixels follow. */
232
                                readbuffer(flic, (void *)p, size);
233
                        } else {
234
                                /* One pixel to be repeated follow. */
235
                                memset((void *)p, readu8(flic), size);
236
                        }
237
                        p += size;
238
                }
239
        }
240
}
241
 
242
static void handlecopy(FLI_Animation *flic, FLI_Chunk *chunk) {
243
        (void)chunk;
244
        /* Read the entire image from the stream. */
245
        readbuffer(flic, (void *)flic->surface->pixels, flic->width * flic->height);
246
}
247
 
248
static void handlecolor256(FLI_Animation *flic, FLI_Chunk *chunk) {
249
        int       numpackets, index, count;
250
        SDL_Color color;
251
 
252
        (void)chunk;
253
        if (flic->format == FLI_FLI)
254
                longjmp(flic->error, FLI_CORRUPTEDFILE);
255
        /* Number of packets. */
256
        numpackets = readu16(flic);
257
        /* Color index that will be changed. */
258
        index = 0;
259
        while (numpackets-- > 0) {
260
                /* Skip some colors. */
261
                index += readu8(flic);
262
                /* And change some others. */
263
                count = readu8(flic);
264
                if (count == 0)
265
                        count = 256;
266
                while (count-- > 0) {
267
                        /* r, g and b are in the range [0..255]. */
268
                        color.r = readu8(flic);
269
                        color.g = readu8(flic);
270
                        color.b = readu8(flic);
271
                        SDL_SetColors(flic->surface, &color, index++, 1);
272
                }
273
        }
274
}
275
 
276
static void handless2(FLI_Animation *flic, FLI_Chunk *chunk) {
277
        int   numlines, y, code, size;
278
        Uint8 *p, c;
279
 
280
        (void)chunk;
281
        if (flic->format == FLI_FLI)
282
                longjmp(flic->error, FLI_CORRUPTEDFILE);
283
        /* numlines lines will change. */
284
        numlines = readu16(flic);
285
        y = 0;
286
        while (numlines > 0) {
287
                /* Read the code. */
288
                code = readu16(flic);
289
                switch ((code >> 14) & 0x03) {
290
                        case 0x00:
291
                                p = (Uint8 *)flic->surface->pixels + flic->surface->pitch * y;
292
                                while (code-- > 0) {
293
                                        /* Skip some pixels. */
294
                                        p += readu8(flic);
295
                                        size = ((Sint8)readu8(flic)) * 2;
296
                                        if (size >= 0) {
297
                                                /* Pixels follow. */
298
                                                readbuffer(flic, (void *)p, size);
299
                                        } else {
300
                                                size = -size;
301
                                                readu8(flic);
302
                                                /* One pixel to be repeated follow. */
303
                                                memset((void *)p, readu8(flic), size);
304
                                        }
305
                                        p += size;
306
                                }
307
                                y++;
308
                                numlines--;
309
                                break;
310
                        case 0x01:
311
                                longjmp(flic->error, FLI_CORRUPTEDFILE);
312
                        case 0x02:
313
                                /* Last pixel of the line. */
314
                                p = (Uint8 *)flic->surface->pixels + flic->surface->pitch * (y + 1);
315
                                p[-1] = code & 0xFF;
316
                                break;
317
                        case 0x03:
318
                                /* Skip some lines. */
319
                                y += (code ^ 0xFFFF) + 1;
320
                                break;
321
                }
322
        }
323
}
324
 
325
int FLI_Version(void) {
326
        return FLI_MAJOR << 16 | FLI_MINOR;
327
}
328
 
329
FLI_Animation *FLI_Open(SDL_RWops *rwops, int *error) {
330
        FLI_Animation *flic;
331
        FLI_Frame     frame;
332
        int           err;
333
 
334
        /* Alloc animation. */
335
        flic = (FLI_Animation *)malloc(sizeof(FLI_Animation));
336
        if (flic == NULL) {
337
                if (error != NULL) *error = FLI_OUTOFMEMORY;
338
                return NULL;
339
        }
340
        flic->rwops = rwops;
341
        flic->surface = NULL;
342
        /* Error handling. */
343
        err = setjmp(flic->error);
344
        if (err != 0) {
345
                if (error != NULL) *error = err;
346
                FLI_Close(flic);
347
                return NULL;
348
        }
349
        /* Read the header. */
350
        readheader(flic);
351
        /* Create a buffer to hold the rendered frame. */
352
        flic->surface = SDL_CreateRGBSurface(SDL_SWSURFACE, flic->width, flic->height, 8, 0, 0, 0, 0);
353
        if (flic->surface == NULL)
354
                longjmp(flic->error, FLI_SDLERROR);
355
        /* Read the first frame. */
356
        flic->offframe1 = SDL_RWtell(rwops);
357
        readframe(flic, &frame);
358
        /* If it's a prefix frame, skip it. */
359
        if (frame.type == 0xF100) {
360
                SDL_RWseek(rwops, frame.size - 16, SEEK_CUR);
361
                flic->offframe1 = SDL_RWtell(rwops);
362
                flic->numframes--;
363
        }
364
        flic->offnextframe = flic->offframe1;
365
        flic->nextframe = 1;
366
        if (error != NULL) *error = FLI_OK;
367
        return flic;
368
}
369
 
370
void FLI_Close(FLI_Animation *flic) {
371
        if (flic != NULL) {
372
                if (flic->rwops != NULL)
373
                        SDL_RWclose(flic->rwops);
374
                if (flic->surface != NULL)
375
                        SDL_FreeSurface(flic->surface);
376
                free(flic);
377
        }
378
}
379
 
380
int FLI_NextFrame(FLI_Animation *flic) {
381
        FLI_Frame frame;
382
        FLI_Chunk chunk;
383
        int       error, locked;
384
        Uint32    i;
385
 
386
        /* Flag to tell if the surface is locked. */
387
        locked = 0;
388
        /* Error handling. */
389
        error = setjmp(flic->error);
390
        if (error != 0) {
391
                if (locked)
392
                        SDL_UnlockSurface(flic->surface);
393
                return error;
394
        }
395
        /* Seek to the current frame. */
396
        SDL_RWseek(flic->rwops, flic->offnextframe, SEEK_SET);
397
        /* Read the current frame. */
398
        readframe(flic, &frame);
399
        /* Read and process each of the chunks of this frame. */
400
        SDL_LockSurface(flic->surface);
401
        locked = 1;
402
        (void)locked;
403
        for (i = frame.numchunks; i != 0; i--) {
404
                readchunk(flic, &chunk);
405
                switch (chunk.type) {
406
                        case FLI_COLOR:
407
                                handlecolor(flic, &chunk);
408
                                break;
409
                        case FLI_LC:
410
                                handlelc(flic, &chunk);
411
                                break;
412
                        case FLI_BLACK:
413
                                handleblack(flic, &chunk);
414
                                break;
415
                        case FLI_BRUN:
416
                                handlebrun(flic, &chunk);
417
                                break;
418
                        case FLI_COPY:
419
                                handlecopy(flic, &chunk);
420
                                break;
421
                        case FLI_COLOR256:
422
                                handlecolor256(flic, &chunk);
423
                                break;
424
                        case FLI_SS2:
425
                                handless2(flic, &chunk);
426
                                break;
427
                        case FLI_PSTAMP:
428
                                /* Ignore this chunk. */
429
                                break;
430
                        default:
431
                                longjmp(flic->error, FLI_CORRUPTEDFILE);
432
                }
433
        }
434
        SDL_UnlockSurface(flic->surface);
435
        /* Setup the number and position of next frame. If it wraps, go to the first one. */
436
        if (++flic->nextframe > flic->numframes) {
437
                flic->offnextframe = flic->offframe1;
438
                flic->nextframe = 1;
439
        } else
440
                flic->offnextframe += frame.size;
441
        return FLI_OK;
442
}
443
 
444
int FLI_Rewind(FLI_Animation *flic) {
445
        flic->offnextframe = flic->offframe1;
446
        flic->nextframe = 1;
447
        return FLI_OK;
448
}
449
 
450
int FLI_Skip(FLI_Animation *flic) {
451
        FLI_Frame frame;
452
        int       error;
453
 
454
        /* Error handling. */
455
        error = setjmp(flic->error);
456
        if (error != 0)
457
                return error;
458
        /* Seek to the current frame. */
459
        SDL_RWseek(flic->rwops, flic->offnextframe, SEEK_SET);
460
        /* Read the current frame. */
461
        readframe(flic, &frame);
462
        /* Skip to the next frame without rendering. */
463
        if (++flic->nextframe > flic->numframes) {
464
                flic->offnextframe = flic->offframe1;
465
                flic->nextframe = 1;
466
        } else
467
                flic->offnextframe += frame.size;
468
        return FLI_OK;
469
}