kolibrios-fun/contrib/sdk/sources/SDL-1.2.2/SDL_flic-1.2/SDL_flic.c

470 lines
18 KiB
C
Raw Normal View History

/*
SDL_flic - renders FLIC animations
Copyright (C) 2003 Andre de Leiradella
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
For information about SDL_flic contact leiradella@bigfoot.com
Version 1.0: first public release.
Version 1.1: fixed bug to set *error to FLI_OK when returning successfully from FLI_Open
added function FLI_Reset to reset the animation to the first frame
Version 1.2: added function FLI_Skip to skip the current frame without rendering
FLI_Animation->surface is now correctly locked and unlocked
the rwops stream is now part of the FLI_Animation structure and is closed inside FLI_Close
renamed FLI_Reset to FLI_Rewind
added function FLI_Version that returns the library version
*/
#include <SDL_flic.h>
#include <setjmp.h>
#include <stdlib.h>
#include <mem.h>
/* Library version. */
#define FLI_MAJOR 1
#define FLI_MINOR 2
/* Chunk types. */
#define FLI_COLOR256 4
#define FLI_SS2 7
#define FLI_COLOR 11
#define FLI_LC 12
#define FLI_BLACK 13
#define FLI_BRUN 15
#define FLI_COPY 16
#define FLI_PSTAMP 18
typedef struct {
Uint32 size, type, numchunks;
} FLI_Frame;
typedef struct {
Uint32 size, type, index;
} FLI_Chunk;
static INLINE void readbuffer(FLI_Animation *flic, void *buffer, int size) {
if (SDL_RWread(flic->rwops, buffer, 1, size) != size)
longjmp(flic->error, FLI_READERROR);
}
static INLINE Uint8 readu8(FLI_Animation *flic) {
Uint8 b;
readbuffer(flic, &b, 1);
return b;
}
static INLINE Uint16 readu16(FLI_Animation *flic) {
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
Uint16 hi, lo;
readbuffer(flic, &lo, 1);
readbuffer(flic, &hi, 1);
return hi << 8 | lo;
#else
Uint16 w;
readbuffer(flic, &w, 2);
return w;
#endif
}
static INLINE Uint32 readu32(FLI_Animation *flic) {
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
Uint32 hi;
hi = readu16(flic);
return hi << 16 | readu16(flic);
#else
Uint32 u;
readbuffer(flic, &u, 4);
return u;
#endif
}
static void readheader(FLI_Animation *flic) {
/* Skip size, we don't need it. */
SDL_RWseek(flic->rwops, 4, SEEK_CUR);
/* Read and check magic. */
flic->format = readu16(flic);
if (flic->format != FLI_FLI && flic->format != FLI_FLC)
longjmp(flic->error, FLI_CORRUPTEDFILE);
/* Read number of frames, maximum is 4000 for FLI and FLC files. */
flic->numframes = readu16(flic);
if (flic->numframes > 4000)
longjmp(flic->error, FLI_CORRUPTEDFILE);
/* Read width and height, must be 320x200 for FLI files. */
flic->width = readu16(flic);
flic->height = readu16(flic);
if (flic->format == FLI_FLI && (flic->width != 320 || flic->height != 200))
longjmp(flic->error, FLI_CORRUPTEDFILE);
/* Read color depth, must be 8 for FLI and FLC files. */
flic->depth = readu16(flic);
if (flic->depth != 8)
longjmp(flic->error, FLI_CORRUPTEDFILE);
/* Skip the flags, it doesn't look like it follows the specs. */
readu16(flic);
/* Read the delay between frames. */
flic->delay = (flic->format == FLI_FLI) ? readu16(flic) : readu32(flic);
/* Skip rest of the header. */
SDL_RWseek(flic->rwops, (flic->format == FLI_FLI) ? 110 : 108, SEEK_CUR);
}
static INLINE void readframe(FLI_Animation *flic, FLI_Frame *frame) {
/* Read the size of the frame, must be less than or equal to 64k in FLI files. */
frame->size = readu32(flic);
if (flic->format == FLI_FLI && frame->size > 65536)
longjmp(flic->error, FLI_CORRUPTEDFILE);
/* Read the type of the frame, must be 0xF1FA in FLI files or 0xF1FA or 0xF100 in FLC files. */
frame->type = readu16(flic);
if (frame->type != 0xF1FA && (flic->format == FLI_FLC && frame->type != 0xF100))
longjmp(flic->error, FLI_CORRUPTEDFILE);
/* Read the number of chunks in this frame. */
frame->numchunks = readu16(flic);
/* Skip rest of the data. */
SDL_RWseek(flic->rwops, 8, SEEK_CUR);
}
static INLINE void readchunk(FLI_Animation *flic, FLI_Chunk *chunk) {
/* Read the chunk size. */
chunk->size = readu32(flic);
/* Read the chunk type. */
chunk->type = readu16(flic);
}
static void handlecolor(FLI_Animation *flic, FLI_Chunk *chunk) {
int numpackets, index, count;
SDL_Color color;
(void)chunk;
/* Number of packets. */
numpackets = readu16(flic);
/* Color index that will be changed. */
index = 0;
while (numpackets-- > 0) {
/* Skip some colors. */
index += readu8(flic);
/* And change some others. */
count = readu8(flic);
if (count == 0)
count = 256;
while (count-- > 0) {
/* r, g and b are in the range [0..63]. */
color.r = ((Uint32)readu8(flic)) * 255 / 63;
color.g = ((Uint32)readu8(flic)) * 255 / 63;
color.b = ((Uint32)readu8(flic)) * 255 / 63;
SDL_SetColors(flic->surface, &color, index++, 1);
}
}
}
static void handlelc(FLI_Animation *flic, FLI_Chunk *chunk) {
int numlines, numpackets, size;
Uint8 *line, *p;
(void)chunk;
/* Skip lines at the top of the image. */
line = (Uint8 *)flic->surface->pixels + readu16(flic) * flic->surface->pitch;
/* numlines lines will change. */
numlines = readu16(flic);
while (numlines-- > 0) {
p = line;
line += flic->surface->pitch;
/* Each line has numpackets changes. */
numpackets = readu8(flic);
while (numpackets-- > 0) {
/* Skip pixels at the beginning of the line. */
p += readu8(flic);
/* size pixels will change. */
size = (Sint8)readu8(flic);
if (size >= 0) {
/* Pixels follow. */
readbuffer(flic, (void *)p, size);
} else {
size = -size;
/* One pixel to be repeated follow. */
memset((void *)p, readu8(flic), size);
}
p += size;
}
}
}
static void handleblack(FLI_Animation *flic, FLI_Chunk *chunk) {
(void)chunk;
/* Fill the surface with color 0. */
if (SDL_FillRect(flic->surface, NULL, 0) != 0)
longjmp(flic->error, FLI_SDLERROR);
}
static void handlebrun(FLI_Animation *flic, FLI_Chunk *chunk) {
int numlines, size;
Uint8 *p, *next;
(void)chunk;
/* Begin at the top of the image. */
p = (Uint8 *)flic->surface->pixels;
/* All lines will change. */
numlines = flic->height;
while (numlines-- > 0) {
/* The number of packages is ignored, packets run until the next line is reached. */
readu8(flic);
next = p + flic->surface->pitch;
while (p < next) {
/* size pixels will change. */
size = (Sint8)readu8(flic);
if (size < 0) {
size = -size;
/* Pixels follow. */
readbuffer(flic, (void *)p, size);
} else {
/* One pixel to be repeated follow. */
memset((void *)p, readu8(flic), size);
}
p += size;
}
}
}
static void handlecopy(FLI_Animation *flic, FLI_Chunk *chunk) {
(void)chunk;
/* Read the entire image from the stream. */
readbuffer(flic, (void *)flic->surface->pixels, flic->width * flic->height);
}
static void handlecolor256(FLI_Animation *flic, FLI_Chunk *chunk) {
int numpackets, index, count;
SDL_Color color;
(void)chunk;
if (flic->format == FLI_FLI)
longjmp(flic->error, FLI_CORRUPTEDFILE);
/* Number of packets. */
numpackets = readu16(flic);
/* Color index that will be changed. */
index = 0;
while (numpackets-- > 0) {
/* Skip some colors. */
index += readu8(flic);
/* And change some others. */
count = readu8(flic);
if (count == 0)
count = 256;
while (count-- > 0) {
/* r, g and b are in the range [0..255]. */
color.r = readu8(flic);
color.g = readu8(flic);
color.b = readu8(flic);
SDL_SetColors(flic->surface, &color, index++, 1);
}
}
}
static void handless2(FLI_Animation *flic, FLI_Chunk *chunk) {
int numlines, y, code, size;
Uint8 *p, c;
(void)chunk;
if (flic->format == FLI_FLI)
longjmp(flic->error, FLI_CORRUPTEDFILE);
/* numlines lines will change. */
numlines = readu16(flic);
y = 0;
while (numlines > 0) {
/* Read the code. */
code = readu16(flic);
switch ((code >> 14) & 0x03) {
case 0x00:
p = (Uint8 *)flic->surface->pixels + flic->surface->pitch * y;
while (code-- > 0) {
/* Skip some pixels. */
p += readu8(flic);
size = ((Sint8)readu8(flic)) * 2;
if (size >= 0) {
/* Pixels follow. */
readbuffer(flic, (void *)p, size);
} else {
size = -size;
readu8(flic);
/* One pixel to be repeated follow. */
memset((void *)p, readu8(flic), size);
}
p += size;
}
y++;
numlines--;
break;
case 0x01:
longjmp(flic->error, FLI_CORRUPTEDFILE);
case 0x02:
/* Last pixel of the line. */
p = (Uint8 *)flic->surface->pixels + flic->surface->pitch * (y + 1);
p[-1] = code & 0xFF;
break;
case 0x03:
/* Skip some lines. */
y += (code ^ 0xFFFF) + 1;
break;
}
}
}
int FLI_Version(void) {
return FLI_MAJOR << 16 | FLI_MINOR;
}
FLI_Animation *FLI_Open(SDL_RWops *rwops, int *error) {
FLI_Animation *flic;
FLI_Frame frame;
int err;
/* Alloc animation. */
flic = (FLI_Animation *)malloc(sizeof(FLI_Animation));
if (flic == NULL) {
if (error != NULL) *error = FLI_OUTOFMEMORY;
return NULL;
}
flic->rwops = rwops;
flic->surface = NULL;
/* Error handling. */
err = setjmp(flic->error);
if (err != 0) {
if (error != NULL) *error = err;
FLI_Close(flic);
return NULL;
}
/* Read the header. */
readheader(flic);
/* Create a buffer to hold the rendered frame. */
flic->surface = SDL_CreateRGBSurface(SDL_SWSURFACE, flic->width, flic->height, 8, 0, 0, 0, 0);
if (flic->surface == NULL)
longjmp(flic->error, FLI_SDLERROR);
/* Read the first frame. */
flic->offframe1 = SDL_RWtell(rwops);
readframe(flic, &frame);
/* If it's a prefix frame, skip it. */
if (frame.type == 0xF100) {
SDL_RWseek(rwops, frame.size - 16, SEEK_CUR);
flic->offframe1 = SDL_RWtell(rwops);
flic->numframes--;
}
flic->offnextframe = flic->offframe1;
flic->nextframe = 1;
if (error != NULL) *error = FLI_OK;
return flic;
}
void FLI_Close(FLI_Animation *flic) {
if (flic != NULL) {
if (flic->rwops != NULL)
SDL_RWclose(flic->rwops);
if (flic->surface != NULL)
SDL_FreeSurface(flic->surface);
free(flic);
}
}
int FLI_NextFrame(FLI_Animation *flic) {
FLI_Frame frame;
FLI_Chunk chunk;
int error, locked;
Uint32 i;
/* Flag to tell if the surface is locked. */
locked = 0;
/* Error handling. */
error = setjmp(flic->error);
if (error != 0) {
if (locked)
SDL_UnlockSurface(flic->surface);
return error;
}
/* Seek to the current frame. */
SDL_RWseek(flic->rwops, flic->offnextframe, SEEK_SET);
/* Read the current frame. */
readframe(flic, &frame);
/* Read and process each of the chunks of this frame. */
SDL_LockSurface(flic->surface);
locked = 1;
(void)locked;
for (i = frame.numchunks; i != 0; i--) {
readchunk(flic, &chunk);
switch (chunk.type) {
case FLI_COLOR:
handlecolor(flic, &chunk);
break;
case FLI_LC:
handlelc(flic, &chunk);
break;
case FLI_BLACK:
handleblack(flic, &chunk);
break;
case FLI_BRUN:
handlebrun(flic, &chunk);
break;
case FLI_COPY:
handlecopy(flic, &chunk);
break;
case FLI_COLOR256:
handlecolor256(flic, &chunk);
break;
case FLI_SS2:
handless2(flic, &chunk);
break;
case FLI_PSTAMP:
/* Ignore this chunk. */
break;
default:
longjmp(flic->error, FLI_CORRUPTEDFILE);
}
}
SDL_UnlockSurface(flic->surface);
/* Setup the number and position of next frame. If it wraps, go to the first one. */
if (++flic->nextframe > flic->numframes) {
flic->offnextframe = flic->offframe1;
flic->nextframe = 1;
} else
flic->offnextframe += frame.size;
return FLI_OK;
}
int FLI_Rewind(FLI_Animation *flic) {
flic->offnextframe = flic->offframe1;
flic->nextframe = 1;
return FLI_OK;
}
int FLI_Skip(FLI_Animation *flic) {
FLI_Frame frame;
int error;
/* Error handling. */
error = setjmp(flic->error);
if (error != 0)
return error;
/* Seek to the current frame. */
SDL_RWseek(flic->rwops, flic->offnextframe, SEEK_SET);
/* Read the current frame. */
readframe(flic, &frame);
/* Skip to the next frame without rendering. */
if (++flic->nextframe > flic->numframes) {
flic->offnextframe = flic->offframe1;
flic->nextframe = 1;
} else
flic->offnextframe += frame.size;
return FLI_OK;
}