Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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 <SDL_flic.h>
  31. #include <setjmp.h>
  32. #include <stdlib.h>
  33. #include <mem.h>
  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. }
  470.