evas - gif loader rewrite (clean up code, document it a lot AND fix bugs)
authorCarsten Haitzler (Rasterman) <raster@rasterman.com>
Wed, 30 Oct 2013 09:16:09 +0000 (18:16 +0900)
committerCarsten Haitzler (Rasterman) <raster@rasterman.com>
Wed, 30 Oct 2013 09:18:08 +0000 (18:18 +0900)
after several days of beating head on desk, i gave up trying to find
the exact cause of some gifs not rendering right as animated gifs due
to the loader. it had something to do with dispose mode handling and
which frame it was applied to. i noticed the structure made it also
hard to fix, so this re-structures the entire thing with cleaner code,
less code AND more comments, with a limited memory cache (512k) for
previous frames per image (to avoid eating up huge memory blobs for
big/long animations - though at the expense of cpu), and with some
notes for future fixes - like fixing the "load 2 copies of the same
animated gif" issue... that's another day.

this does fix https://phab.enlightenment.org/T443 along with many
other things.

src/modules/evas/loaders/gif/evas_image_load_gif.c

index 6c2c634..3f2ef53 100644 (file)
 
 #include <gif_lib.h>
 
-#ifndef MIN
-# define MIN(a, b) (((a) < (b)) ? (a) : (b))
-#endif
-
-typedef struct _Gif_Frame Gif_Frame;
-
-typedef enum _Frame_Load_Type
-{
-   LOAD_FRAME_NONE = 0,
-   LOAD_FRAME_INFO = 1,
-   LOAD_FRAME_DATA = 2,
-   LOAD_FRAME_DATA_INFO = 3
-} Frame_Load_Type;
-
-struct _Gif_Frame
-{
-   struct {
-      /* Image descriptor */
-      int        x;
-      int        y;
-      int        w;
-      int        h;
-      int        interlace;
-   } image_des;
-
-   struct {
-      /* Graphic Control*/
-      int        disposal;
-      int        transparent;
-      int        delay;
-      int        input;
-   } frame_info;
-   int bg_val;
-};
+typedef struct _Frame_Info Frame_Info;
+typedef struct _Loader_Info Loader_Info;
 
-typedef struct _Evas_Loader_Internal Evas_Loader_Internal;
-struct _Evas_Loader_Internal
+struct _Loader_Info
 {
    Eina_File *f;
    Evas_Image_Load_Opts *opts;
    Evas_Image_Animated *animated;
 };
 
-static Eina_Bool evas_image_load_specific_frame(Eina_File *f, const Evas_Image_Load_Opts *opts, Evas_Image_Property *prop, Evas_Image_Animated *animated, int frame_index, int *error);
+struct _Frame_Info
+{
+   int x, y, w, h;
+   unsigned short delay; // delay time in 1/100ths of a sec
+   short transparent : 10; // -1 == not, anything else == index 
+   short dispose : 6; // 0, 1, 2, 3 (others invalid)
+   short interlace : 1; // interlaced or not
+};
 
-#define byte2_to_int(a,b)         (((b)<<8)|(a))
+#ifndef MIN
+# define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#define LOADERR(x) \
+do { \
+   *error = (x); \
+   goto on_error; \
+} while (0)
+#define PIX(_x, _y) rows[yin + _y][xin + _x]
+#define CMAP(_v) cmap->Colors[_v]
+#define PIXLK(_p) ARGB_JOIN(0xff, CMAP(_p).Red, CMAP(_p).Green, CMAP(_p).Blue)
 
-#define FRAME_MAX 1024
+// utility funcs...
 
-/* find specific frame in image entry */
-static Eina_Bool
-_find_frame(Evas_Image_Animated *animated, int frame_index, Image_Entry_Frame **frame)
+// brute force find frame index - gifs are normally saml so ok for now
+static Image_Entry_Frame *
+_find_frame(Evas_Image_Animated *animated, int index)
 {
    Eina_List *l;
-   Image_Entry_Frame *hit_frame = NULL;
+   Image_Entry_Frame *frame;
 
-   if (!animated->frames) return EINA_FALSE;
-
-   EINA_LIST_FOREACH(animated->frames, l, hit_frame)
+   if (!animated->frames) return NULL;
+   EINA_LIST_FOREACH(animated->frames, l, frame)
      {
-        if (hit_frame->index == frame_index)
-          {
-             *frame = hit_frame;
-             return EINA_TRUE;
-          }
+        if (frame->index == index) return frame;
      }
-   return EINA_FALSE;
+   return NULL;
 }
 
-static Eina_Bool
-_find_close_frame(Evas_Image_Animated *animated, int frame_index, Image_Entry_Frame **frame)
-{
-  Eina_Bool hit = EINA_FALSE;
-  int i;
-
-  i = frame_index -1;
-
-  if (!animated->frames) return EINA_FALSE;
-
-  for (; i > 0; i--)
-    {
-       hit = _find_frame(animated, i, frame);
-       if (hit)
-         return  EINA_TRUE;
-    }
-  return EINA_FALSE;
-}
-
-static Eina_Bool
-_evas_image_skip_frame(GifFileType *gif, int frame)
+// fill in am image with a specific rgba color value
+static void
+_fill_image(DATA32 *data, int rowpix, DATA32 val, int x, int y, int w, int h)
 {
-   int                 remain_frame = 0;
-   GifRecordType       rec;
-
-   if (!gif) return EINA_FALSE;
-   if (frame == 0) return EINA_TRUE; /* no need to skip */
-   if (frame < 0 || frame > FRAME_MAX) return EINA_FALSE;
-
-   remain_frame = frame;
-
-   do
+   int xx, yy;
+   DATA32 *p;
+   
+   for (yy = 0; yy < h; yy++)
      {
-        if (DGifGetRecordType(gif, &rec) == GIF_ERROR) return EINA_FALSE;
-
-        if (rec == EXTENSION_RECORD_TYPE)
+        p = data + ((y + yy) * rowpix) + x;
+        for (xx = 0; xx < w; xx++)
           {
-             int                 ext_code;
-             GifByteType        *ext;
-
-             ext = NULL;
-             DGifGetExtension(gif, &ext_code, &ext);
-             while (ext)
-               { /*skip extention */
-                  ext = NULL;
-                  DGifGetExtensionNext(gif, &ext);
-               }
+             *p = val;
+             p++;
           }
-
-        if (rec == IMAGE_DESC_RECORD_TYPE)
-          {
-             int                 img_code;
-             GifByteType        *img;
-
-             if (DGifGetImageDesc(gif) == GIF_ERROR) return EINA_FALSE;
-
-             remain_frame --;
-             /* we have to count frame, so use DGifGetCode and skip decoding */
-             if (DGifGetCode(gif, &img_code, &img) == GIF_ERROR) return EINA_FALSE;
-
-             while (img)
-               {
-                  img = NULL;
-                  DGifGetCodeNext(gif, &img);
-               }
-             if (remain_frame < 1) return EINA_TRUE;
-          }
-        if (rec == TERMINATE_RECORD_TYPE) return EINA_FALSE;  /* end of file */
-
-     } while ((rec != TERMINATE_RECORD_TYPE) && (remain_frame > 0));
-   return EINA_FALSE;
+     }
 }
 
-static Eina_Bool
-_evas_image_load_frame_graphic_info(Image_Entry_Frame *frame, GifByteType  *ext)
+// fix coords and work out an x and y inset in orig data if out of image bounds
+static void
+_clip_coords(int imw, int imh, int *xin, int *yin,
+             int x0, int y0, int w0, int h0,
+             int *x, int *y, int *w, int *h)
 {
-   Gif_Frame *gif_frame = NULL;
-   if ((!frame) || (!ext)) return EINA_FALSE;
-
-   gif_frame = (Gif_Frame *) frame->info;
-
-   /* transparent */
-   if ((ext[1] & 0x1) != 0)
-     gif_frame->frame_info.transparent = ext[4];
-   else
-     gif_frame->frame_info.transparent = -1;
-
-   gif_frame->frame_info.input = (ext[1] >>1) & 0x1;
-   gif_frame->frame_info.disposal = (ext[1] >>2) & 0x7;
-   gif_frame->frame_info.delay = byte2_to_int(ext[2], ext[3]);
-   return EINA_TRUE;
+   if (x0 < 0)
+     {
+        w0 += x0;
+        *xin = -x0;
+        x0 = 0;
+     }
+   if ((x0 + w0) > imw) w0 = imw - x0;
+   if (y0 < 0)
+     {
+        h0 += y0;
+        *yin = -y0;
+        y0 = 0;
+     }
+   if ((y0 + h0) > imh) h0 = imh - y0;
+   *x = x0;
+   *y = y0;
+   *w = w0;
+   *h = h0;
 }
 
-static Eina_Bool
-_evas_image_load_frame_image_des_info(GifFileType *gif, Image_Entry_Frame *frame)
+// file a rgba data pixle blob with a frame color (bg or trans) depending...
+static void
+_fill_frame(DATA32 *data, int rowpix, GifFileType *gif, Frame_Info *finfo,
+            int x, int y, int w, int h)
 {
-   Gif_Frame *gif_frame = NULL;
-   if ((!gif) || (!frame)) return EINA_FALSE;
-
-   gif_frame = (Gif_Frame *) frame->info;
-   gif_frame->image_des.x = gif->Image.Left;
-   gif_frame->image_des.y = gif->Image.Top;
-   gif_frame->image_des.w = gif->Image.Width;
-   gif_frame->image_des.h = gif->Image.Height;
-   gif_frame->image_des.interlace = gif->Image.Interlace;
-   return EINA_TRUE;
+   // solid color fill for pre frame region
+   if (finfo->transparent < 0)
+     {
+        ColorMapObject *cmap;
+        int bg;
+        
+        // work out color to use from cmap
+        if (gif->Image.ColorMap) cmap = gif->Image.ColorMap;
+        else cmap = gif->SColorMap;
+        bg = gif->SBackGroundColor;
+        // and do the fill
+        _fill_image
+          (data, rowpix,
+           ARGB_JOIN(0xff, CMAP(bg).Red, CMAP(bg).Green, CMAP(bg).Blue),
+           x, y, w, h);
+     }
+   // fill in region with 0 (transparent)
+   else
+     _fill_image(data, rowpix, 0, x, y, w, h);
 }
 
-#define PIX(_x, _y) rows[yin + _y][xin + _x]
-#define CMAP(_v) cmap->Colors[_v]
-
+// store common fields from gif file info into frame info
 static void
-_expand_gif_rows(GifRowType *rows, ColorMapObject *cmap, DATA32 *ptr,
-                 int rowpix, int xin, int yin, int w, int h,
-                 int x, int y, int alpha, Eina_Bool overwrite)
+_store_frame_info(GifFileType *gif, Frame_Info *finfo)
 {
-   int xx, yy, pix;
-   DATA32 *p;
-   
-   if (alpha >= 0)
-     {
-        if (overwrite)
-          {
-             for (yy = 0; yy < h; yy++)
-               {
-                  p = ptr + ((y + yy) * rowpix) + x;
-                  for (xx = 0; xx < w; xx++)
-                    {
-                       pix = PIX(xx, yy);
-                       if (pix != alpha)
-                         *p = ARGB_JOIN(0xff, CMAP(pix).Red,
-                                        CMAP(pix).Green, CMAP(pix).Blue);
-                       else *p = 0;
-                       p++;
-                    }
-               }
-          }
-        else
-          {
-             for (yy = 0; yy < h; yy++)
-               {
-                  p = ptr + ((y + yy) * rowpix) + x;
-                  for (xx = 0; xx < w; xx++)
-                    {
-                       pix = PIX(xx, yy);
-                       if (pix != alpha)
-                         *p = ARGB_JOIN(0xff, CMAP(pix).Red,
-                                        CMAP(pix).Green, CMAP(pix).Blue);
-                       p++;
-                    }
-               }
-          }
-     }
-   else
-     {
-        for (yy = 0; yy < h; yy++)
-          {
-             p = ptr + ((y + yy) * rowpix) + x;
-             for (xx = 0; xx < w; xx++)
-               {
-                  pix = PIX(xx, yy);
-                  *p = ARGB_JOIN(0xff, CMAP(pix).Red,
-                                 CMAP(pix).Green, CMAP(pix).Blue);
-                  p++;
-               }
-          }
-     }
+   finfo->x = gif->Image.Left;
+   finfo->y = gif->Image.Top;
+   finfo->w = gif->Image.Width;
+   finfo->h = gif->Image.Height;
+   finfo->interlace = gif->Image.Interlace;
 }
 
+// check if image fills "screen space" and if so, if it is transparent
+// at all then the image could be transparent - OR if image doesnt fill,
+// then it could be trasnparent (full coverage of screen). some gifs will
+// be recognized as solid here for faster rendering, but not all.
 static void
-_copy_pixels(DATA32 *ptr_src, DATA32 *ptr, int rowpix,
-             int x, int y, int w, int h)
+_check_transparency(Eina_Bool *full, Frame_Info *finfo, int w, int h)
 {
-   DATA32 *p1, *p2, *pe;
-   int yy;
-   
-   if ((w <= 0) || (h <= 0)) return;
-   for (yy = 0; yy < h; yy++)
+   if ((finfo->x == 0) && (finfo->y == 0) &&
+       (finfo->w == w) && (finfo->h == h))
      {
-        p1 = ptr_src + ((y + yy) * rowpix) + x;
-        p2 = ptr + ((y + yy) * rowpix) + x;
-        for (pe = p2 + w; p2 < pe;) *p2++ = *p1++;
+        if (finfo->transparent >= 0) *full = EINA_FALSE;
      }
+   else *full = EINA_FALSE;
 }
 
-static void
-_fill_pixels(DATA32 val, DATA32 *ptr, int rowpix,
-             int x, int y, int w, int h)
+// allocate frame and frame info and append to list and store fields
+static Frame_Info *
+_new_frame(Evas_Image_Animated *animated,
+           int transparent, int dispose, int delay,
+           int index)
 {
-   DATA32 *p2, *pe;
-   int yy;
-   
-   if ((w <= 0) || (h <= 0)) return;
-   for (yy = 0; yy < h; yy++)
+   Image_Entry_Frame *frame;
+   Frame_Info *finfo;
+
+   // allocate frame and frame info data (MUSt be separate)
+   frame = calloc(1, sizeof(Image_Entry_Frame));
+   if (!frame) return NULL;
+   finfo = calloc(1, sizeof(Frame_Info));
+   if (!finfo)
      {
-        p2 = ptr + ((y + yy) * rowpix) + x;
-        for (pe = p2 + w; p2 < pe;) *p2++ = val;
+        free(frame);
+        return NULL;
      }
+   // record transparent index to be used or -1 if none
+   // for this SPECIFIC frame
+   finfo->transparent = transparent;
+   // record dispose mode (3 bits)
+   finfo->dispose = dispose;
+   // record delay (2 bytes so max 65546 /100 sec)
+   finfo->delay = delay;
+   // record the index number we are at
+   frame->index = index;
+   // that frame is stored AT image/screen size
+   frame->info = finfo;
+   animated->frames = eina_list_append(animated->frames, frame);
+   return finfo;
 }
 
+// decode a gif image into rows then expand to 32bit into the destination
+// data pointer
 static Eina_Bool
-_evas_image_load_frame_image_data(Eina_File *f,
-                                  const Evas_Image_Load_Opts *opts EINA_UNUSED,
-                                  Evas_Image_Property *prop,
-                                  Evas_Image_Animated *animated,
-                                  GifFileType *gif, Image_Entry_Frame *frame, int *error)
+_decode_image(GifFileType *gif, DATA32 *data, int rowpix, int xin, int yin,
+              int transparent, int x, int y, int w, int h, Eina_Bool fill)
 {
-   ColorMapObject *cmap;
-   GifRowType *rows;
-   DATA32 *ptr, pad = 0xff000000;
-   Gif_Frame *gif_frame = NULL;
    int intoffset[] = { 0, 4, 2, 1 };
    int intjump[] = { 8, 8, 4, 2 };
-   int  x, y, w, h, i, bg, alpha, cache_w, cache_h, cur_h, cur_w, yy;
-   int  disposal = 0, xin = 0, yin = 0;
-
-   if ((!gif) || (!frame)) return EINA_FALSE;
-
-   gif_frame = (Gif_Frame *) frame->info;
-   w = gif->Image.Width;
-   h = gif->Image.Height;
-   x = gif->Image.Left;
-   y = gif->Image.Top;
-   cur_h = h;
-   cur_w = w;
-   cache_w = prop->w;
-   cache_h = prop->h;
+   int i, xx, yy, pix;
+   GifRowType *rows;
+   Eina_Bool ret = EINA_FALSE;
+   ColorMapObject *cmap;
+   DATA32 *p;
 
+   // build a blob of memory to have pointers to rows of pixels
+   // AND store the decoded gif pixels (1 byte per pixel) as welll
    rows = malloc((h * sizeof(GifRowType *)) + (w * h * sizeof(GifPixelType)));
-   if (!rows)
-     {
-        *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
-        return EINA_FALSE;
-     }
+   if (!rows) goto on_error;
+   
+   // fill in the pointers at the start
    for (yy = 0; yy < h; yy++)
      {
         rows[yy] = ((unsigned char *)rows) + (h * sizeof(GifRowType *)) +
           (yy * w * sizeof(GifPixelType));
      }
-
+   
+   // if give is interlaced, walk interlace pattern and decode into rows
    if (gif->Image.Interlace)
      {
         for (i = 0; i < 4; i++)
@@ -324,719 +219,612 @@ _evas_image_load_frame_image_data(Eina_File *f,
              for (yy = intoffset[i]; yy < h; yy += intjump[i])
                {
                   if (DGifGetLine(gif, rows[yy], w) != GIF_OK)
-                    {
-                       *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
-                       goto error;
-                    }
+                    goto on_error;
                }
           }
      }
+   // normal top to bottom - decode into rows
    else
      {
         for (yy = 0; yy < h; yy++)
           {
              if (DGifGetLine(gif, rows[yy], w) != GIF_OK)
-               {
-                  *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
-                  goto error;
-              }
+               goto on_error;
           }
      }
 
-   alpha = gif_frame->frame_info.transparent;
-   if ((prop->alpha) || (alpha >= 0)) pad = 0x00000000;
-   frame->data = malloc(cache_w * cache_h * sizeof(DATA32));
-   if (!frame->data)
-     {
-        *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
-        goto error;
-     }
-   bg = gif->SBackGroundColor;
-   cmap = (gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap);
-
-   if (!cmap)
-     {
-        DGifCloseFile(gif);
-        if (frame->data) free(frame->data);
-        *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
-        goto error;
-     }
+   // work out what colormap to use
+   if (gif->Image.ColorMap) cmap = gif->Image.ColorMap;
+   else cmap = gif->SColorMap;
 
-   if (cur_h > cache_h) cur_h = cache_h;
-   if (cur_w > cache_w) cur_w = cache_w;
-
-   // clip the image region to be within current image size and note the inset
-   if (x < 0)
-     {
-        w += x; xin = -x; x = 0;
-     }
-   if ((x + w) > cache_w) w = cache_w - x;
-   if (y < 0)
+   // if we need to deal with transparent pixels at all...
+   if (transparent >= 0)
      {
-        h += y; yin = -y; y = 0;
-     }
-   if ((y + h) > cache_h) h = cache_h - y;
-   
-   // data ptr to write to
-   ptr = frame->data;
-   if (frame->index > 1)
-     {
-        /* get previous frame only frame index is bigger than 1 */
-        DATA32            *ptr_src;
-        Image_Entry_Frame *new_frame = NULL;
-        int                cur_frame = frame->index;
-        int                start_frame = 1;
-
-        if (_find_close_frame(animated, cur_frame, &new_frame))
-          start_frame = new_frame->index + 1;
-
-        if ((start_frame < 1) || (start_frame > cur_frame))
-          {
-             *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
-             goto error;
-          }
-        /* load previous frame of cur_frame */
-        for (i = start_frame; i < cur_frame; i++)
+        // if we are told to FILL (overwrite with transparency kept)
+        if (fill)
           {
-             // FIXME : that one -v
-             if (!evas_image_load_specific_frame(f, opts, prop, animated, i, error))
+             for (yy = 0; yy < h; yy++)
                {
-                  *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
-                  goto error;
+                  p = data + ((y + yy) * rowpix) + x;
+                  for (xx = 0; xx < w; xx++)
+                    {
+                       pix = PIX(xx, yy);
+                       if (pix != transparent) *p = PIXLK(pix);
+                       else *p = 0;
+                       p++;
+                    }
                }
           }
-        if (!_find_frame(animated, cur_frame - 1, &new_frame))
-          {
-             *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
-             goto error;
-          }
+        // paste on top with transparent pixels untouched
         else
           {
-             Gif_Frame *gif_frame2 = NULL;
-             
-             ptr_src = new_frame->data;
-             if (new_frame->info)
+             for (yy = 0; yy < h; yy++)
                {
-                  gif_frame2 = (Gif_Frame *)(new_frame->info);
-                  disposal = gif_frame2->frame_info.disposal;
-                  gif_frame->bg_val = gif_frame2->bg_val;
-                  switch (disposal)
+                  p = data + ((y + yy) * rowpix) + x;
+                  for (xx = 0; xx < w; xx++)
                     {
-                     case 0: // no nothing
-                     case 2: // restore bg ... browsers don't respect bg, so neither shall we.
-                       // fill in the area OUTSIDE the image with 0/black
-                       _fill_pixels(pad, ptr, cache_w, 0, 0, cache_w, y);
-                       _fill_pixels(pad, ptr, cache_w, 0, y + h, cache_w, cache_h - (y + h));
-                       _fill_pixels(pad, ptr, cache_w, 0, y, x, h);
-                       _fill_pixels(pad, ptr, cache_w, x + w, y, cache_w - (x + w), h);
-                       _expand_gif_rows(rows, cmap, ptr, cache_w, xin, yin,
-                                        w, h, x, y, alpha, EINA_TRUE);
-                       break;
-                     case 1: // leave as-is
-                     case 3: // previous image
-                       _copy_pixels(ptr_src, ptr, cache_w, 0, 0,
-                                    cache_w, cache_h);
-                       _expand_gif_rows(rows, cmap, ptr, cache_w, xin, yin,
-                                        w, h, x, y, alpha, EINA_FALSE);
-                       break;
-                     default:
-                       break;
+                       pix = PIX(xx, yy);
+                       if (pix != transparent) *p = PIXLK(pix);
+                       p++;
                     }
                }
           }
      }
-   else /* first frame decoding */
+   else
      {
-        /* get the background value */
-        gif_frame->bg_val = RGB_JOIN(cmap->Colors[bg].Red, 
-                                     cmap->Colors[bg].Green,
-                                     cmap->Colors[bg].Blue);
-        // fill in the area OUTSIDE the image with 0/black
-        _fill_pixels(pad, ptr, cache_w, 0, 0, cache_w, y);
-        _fill_pixels(pad, ptr, cache_w, 0, y + h, cache_w, cache_h - (y + h));
-        _fill_pixels(pad, ptr, cache_w, 0, y, x, h);
-        _fill_pixels(pad, ptr, cache_w, x + w, y, cache_w - (x + w), h);
-        _expand_gif_rows(rows, cmap, ptr, cache_w, xin, yin,
-                         w, h, x, y, alpha, EINA_TRUE);
+        // walk pixels without worring about transparency at all
+        for (yy = 0; yy < h; yy++)
+          {
+             p = data + ((y + yy) * rowpix) + x;
+             for (xx = 0; xx < w; xx++)
+               {
+                  pix = PIX(xx, yy);
+                  *p = PIXLK(pix);
+                  p++;
+               }
+          }
      }
+   ret = EINA_TRUE;
 
-   if (rows) free(rows);
-   frame->loaded = EINA_TRUE;
-   return EINA_TRUE;
-error:
-   if (rows) free(rows);
-   return EINA_FALSE;
+on_error:
+   free(rows);
+   return ret;
 }
 
-static Eina_Bool
-_evas_image_load_frame(Eina_File *f, const Evas_Image_Load_Opts *opts,
-                       Evas_Image_Property *prop, Evas_Image_Animated *animated,
-                       GifFileType *gif, Image_Entry_Frame *frame, Frame_Load_Type type, int *error)
+// flush out older rgba frame images to save memory but skip current frame
+// and previous frame (prev needed for dispose mode 3)
+static void
+_flush_older_frames(Evas_Image_Animated *animated,
+                    int w, int h,
+                    Image_Entry_Frame *thisframe,
+                    Image_Entry_Frame *prevframe)
 {
-   GifRecordType       rec;
-   int                 gra_res = 0, img_res = 0;
-   Eina_Bool           res = EINA_FALSE;
-
-   if ((!gif) || (!frame)) return EINA_FALSE;
-   if (type > LOAD_FRAME_DATA_INFO) return EINA_FALSE;
+   Eina_List *l;
+   Image_Entry_Frame *frame;
+   // target is the amount of memory we want to be under for stored frames
+   int total = 0, target = 512 * 1024;
 
-   do
+   // total up the amount of memory used by stored frames for this image
+   EINA_LIST_FOREACH(animated->frames, l, frame)
      {
-        if (DGifGetRecordType(gif, &rec) == GIF_ERROR) return EINA_FALSE;
-        if (rec == IMAGE_DESC_RECORD_TYPE)
-          {
-             img_res++;
-             break;
-          }
-        else if (rec == EXTENSION_RECORD_TYPE)
+        if (frame->data) total++;
+     }
+   total *= (w * h * sizeof(DATA32));
+   // if we use less than target (512k) for frames - dont flush
+   if (total < target) return;
+   // clean oldest frames first and go until below target or until we loop
+   // around back to this frame (curent)
+   EINA_LIST_FOREACH(animated->frames, l, frame)
+     {
+        if (frame == thisframe) break;
+     }
+   if (!l) return;
+   // start on next frame after thisframe
+   l = l->next;
+   // handle wrap to start
+   if (!l) l = animated->frames;
+   // now walk until we hit thisframe again... then stop walk.
+   while (l)
+     {
+        frame = l->data;
+        if (frame == thisframe) break;
+        if (frame->data)
           {
-             int           ext_code;
-             GifByteType  *ext;
-
-             ext = NULL;
-             DGifGetExtension(gif, &ext_code, &ext);
-             while (ext)
+             if ((frame != thisframe) && (frame != prevframe))
                {
-                  if (ext_code == 0xf9) /* Graphic Control Extension */
-                    {
-                       gra_res++;
-                       /* fill frame info */
-                       if ((type == LOAD_FRAME_INFO) || (type == LOAD_FRAME_DATA_INFO))
-                         _evas_image_load_frame_graphic_info(frame,ext);
-                    }
-                  ext = NULL;
-                  DGifGetExtensionNext(gif, &ext);
+                  free(frame->data);
+                  frame->data = NULL;
+                  // subtract memory used and if below target - stop flush
+                  total -= (w * h * sizeof(DATA32));
+                  if (total < target) break;
                }
           }
-     } while ((rec != TERMINATE_RECORD_TYPE) && (img_res == 0));
-   if (img_res != 1) return EINA_FALSE;
-   if (DGifGetImageDesc(gif) == GIF_ERROR) return EINA_FALSE;
-   if ((type == LOAD_FRAME_INFO) || (type == LOAD_FRAME_DATA_INFO))
-     _evas_image_load_frame_image_des_info(gif, frame);
-
-   if ((type == LOAD_FRAME_DATA) || (type == LOAD_FRAME_DATA_INFO))
-     {
-        res = _evas_image_load_frame_image_data(f, opts, prop, animated,
-                                                gif, frame, error);
-        if (!res) return EINA_FALSE;
+        // go to next - handle wrap to start
+        l = l->next;
+        if (!l) l = animated->frames;
      }
-   return EINA_TRUE;
-}
-
-
-/* set frame data to cache entry's data */
-static Eina_Bool
-evas_image_load_file_data_gif_internal(Evas_Image_Property *prop,
-                                      Image_Entry_Frame *frame,
-                                      void *pixels,
-                                      int *error)
-{
-   /* only copy real frame part */
-   memcpy(pixels, frame->data, prop->w * prop->h * sizeof (DATA32));
-   prop->premul = EINA_TRUE;
-
-   *error = EVAS_LOAD_ERROR_NONE;
-   return EINA_TRUE;
 }
+                    
 
-typedef struct _Evas_GIF_Info Evas_GIF_Info;
-struct _Evas_GIF_Info
+// file access info/funcs
+typedef struct _File_Info File_Info;
+struct _File_Info
 {
    unsigned char *map;
-   int length;
-   int position;
+   int pos, len; // yes - gif uses ints for file sizes.
 };
 
 static int
-_evas_image_load_file_read(GifFileType* gft, GifByteType *buf,int length)
+_file_read(GifFileType *gft, GifByteType *buf, int len)
 {
-   Evas_GIF_Info *egi = gft->UserData;
-
-   if (egi->position == egi->length) return 0;
-   if (egi->position + length == egi->length) length = egi->length - egi->position;
-   memcpy(buf, egi->map + egi->position, length);
-   egi->position += length;
+   File_Info *fi = gft->UserData;
 
-   return length;
-}
-static void *
-evas_image_load_file_open_gif(Eina_File *f, Eina_Stringshare *key EINA_UNUSED,
-                               Evas_Image_Load_Opts *opts,
-                               Evas_Image_Animated *animated,
-                               int *error)
-{
-   Evas_Loader_Internal *loader;
-
-   loader = calloc(1, sizeof (Evas_Loader_Internal));
-   if (!loader)
-     {
-        *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
-        return NULL;
-     }
-
-   loader->f = f;
-   loader->opts = opts;
-   loader->animated = animated;
-
-   return loader;
-}
-
-static void
-evas_image_load_file_close_gif(void *loader_data)
-{
-   free(loader_data);
+   if (fi->pos >= fi->len) return 0; // if at or past end - no
+   if ((fi->pos + len) >= fi->len) len = fi->len - fi->pos; 
+   memcpy(buf, fi->map + fi->pos, len);
+   fi->pos += len;
+   return len;
 }
 
 static Eina_Bool
-evas_image_load_file_head_gif(void *loader_data,
-                             Evas_Image_Property *prop,
-                             int *error)
+evas_image_load_file_head_gif2(void *loader_data,
+                               Evas_Image_Property *prop,
+                               int *error)
 {
-   Evas_Loader_Internal *loader = loader_data;
-//   Evas_Image_Load_Opts *load_opts;
-   Evas_Image_Animated *animated;
-   Eina_File *f;
-
-   Evas_GIF_Info  egi;
-   GifRecordType  rec;
-   GifFileType   *gif = NULL;
-   int            loop_count = -1;
-   int            a;
-   Eina_Bool      r = EINA_FALSE;
-   //it is possible which gif file have error midle of frames,
-   //in that case we should play gif file until meet error frame.
-   int            image_count = 0;
-
-   f = loader->f;
-//   load_opts = loader->opts;
-   animated = loader->animated;
-
+   Loader_Info *loader = loader_data;
+   Evas_Image_Animated *animated = loader->animated;
+   Eina_File *f = loader->f;
+   Eina_Bool ret = EINA_FALSE;
+   File_Info fi;
+   GifRecordType rec;
+   GifFileType *gif = NULL;
+   // it is possible which gif file have error midle of frames,
+   // in that case we should play gif file until meet error frame.
+   int imgnum = 0;
+   int loop_count = -1;
+   Frame_Info *finfo = NULL;
+   Eina_Bool full = EINA_TRUE;
+
+   // init prop struct with some default null values
    prop->w = 0;
    prop->h = 0;
-   a = 0;
 
-   egi.map = eina_file_map_all(f, EINA_FILE_RANDOM);
-   if (!egi.map)
-     {
-        *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
-        goto on_error;
-     }
-   egi.length = eina_file_size_get(f);
-   egi.position = 0;
+   // map the file and store/track info
+   fi.map = eina_file_map_all(f, EINA_FILE_RANDOM);
+   if (!fi.map) LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE);
+   fi.len = eina_file_size_get(f);
+   fi.pos = 0;
 
+   // actually ask libgif to open the file
 #if GIFLIB_MAJOR >= 5
-   gif = DGifOpen(&egi, _evas_image_load_file_read, NULL);
+   gif = DGifOpen(&fi, _file_read, NULL);
 #else
-   gif = DGifOpen(&egi, _evas_image_load_file_read);
+   gif = DGifOpen(&fi, _file_read);
 #endif
-   if (!gif)
-     {
-        *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
-        goto on_error;
-     }
-
-   /* check logical screen size */
+   if (!gif) LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
+   // get the gif "screen size" (the actual image size)
    prop->w = gif->SWidth;
    prop->h = gif->SHeight;
-   /* support scale down feture in gif*/
-// disable scale down for gif until gif is handled right   
-//   if (load_opts->scale_down_by > 1)
-//     {
-//       prop->w /= load_opts->scale_down_by;
-//       prop->h /= load_opts->scale_down_by;
-//     }
-
+   // if size is invalid - abort here
    if ((prop->w < 1) || (prop->h < 1) ||
        (prop->w > IMG_MAX_SIZE) || (prop->h > IMG_MAX_SIZE) ||
        IMG_TOO_BIG(prop->w, prop->h))
      {
         if (IMG_TOO_BIG(prop->w, prop->h))
-          *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
-        else
-          *error = EVAS_LOAD_ERROR_GENERIC;
+          LOADERR(EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED);
+        LOADERR(EVAS_LOAD_ERROR_GENERIC);
         goto on_error;
      }
-
+   // walk through gif records in file to figure out info
    do
      {
         if (DGifGetRecordType(gif, &rec) == GIF_ERROR)
           {
-             if (image_count > 1) break; //we should show normal frames.
-             /* PrintGifError(); */
-             *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
-             goto on_error;
+             // if we have a gif that ends part way through a sequence
+             // (or animation) consider it valid and just break - no error
+             if (imgnum > 1) break;
+             LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
           }
-
-        /* image descript info */
+        // get image description section
         if (rec == IMAGE_DESC_RECORD_TYPE)
           {
              int img_code;
              GifByteType *img;
 
+             // get image desc
              if (DGifGetImageDesc(gif) == GIF_ERROR)
-               {
-                  /* PrintGifError(); */
-                  *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
-                  goto on_error;
-               }
-             /* we have to count frame, so use DGifGetCode and skip decoding */
+               LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
+             // skip decoding and just walk image to next
              if (DGifGetCode(gif, &img_code, &img) == GIF_ERROR)
-               {
-                  /* PrintGifError(); */
-                  *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
-                  goto on_error;
-               }
+               LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
+             // skip till next...
              while (img)
                {
                   img = NULL;
                   DGifGetCodeNext(gif, &img);
                }
-             image_count++;
+             // store geometry in the last frame info data
+             if (finfo)
+               {
+                  _store_frame_info(gif, finfo);
+                  _check_transparency(&full, finfo, prop->w, prop->h);
+               }
+             // or if we dont have a finfo entry - create one even for stills
+             else
+               {
+                  // allocate and save frame with field data
+                  finfo = _new_frame(animated, -1, 0, 0, imgnum + 1);
+                  if (!finfo)
+                    LOADERR(EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED);
+                  // store geometry info from gif image
+                  _store_frame_info(gif, finfo);
+                  // check for transparency/alpha
+                  _check_transparency(&full, finfo, prop->w, prop->h);
+               }
+             imgnum++;
           }
+        // we have an extension code block - for animated gifs for sure
         else if (rec == EXTENSION_RECORD_TYPE)
           {
-             int                 ext_code;
-             GifByteType        *ext;
+             int ext_code;
+             GifByteType *ext;
 
              ext = NULL;
+             // get the first extension entry
              DGifGetExtension(gif, &ext_code, &ext);
              while (ext)
                {
-                  if (ext_code == 0xf9) /* Graphic Control Extension */
+                  // graphic control extension - for animated gif data
+                  // and transparent index + flag
+                  if (ext_code == 0xf9)
                     {
-                       if ((ext[1] & 1) && (prop->alpha == 0))
-                        prop->alpha = (int)ext[4];
+                       // create frame and store it in image
+                       finfo = _new_frame
+                         (animated,
+                          (ext[1] & 1) ? ext[4] : -1, // transparency index
+                          (ext[1] >> 2) & 0x7, // dispose mode
+                          ((int)ext[3] << 8) | (int)ext[2], // delay
+                          imgnum + 1);
+                       if (!finfo)
+                         LOADERR(EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED);
                     }
+                  // netscape extension indicating loop count...
                   else if (ext_code == 0xff) /* application extension */
                     {
-                       if (!strncmp ((char*)(&ext[1]), "NETSCAPE2.0", 11) ||
-                           !strncmp ((char*)(&ext[1]), "ANIMEXTS1.0", 11))
+                       if (!strncmp((char *)(&ext[1]), "NETSCAPE2.0", 11) ||
+                           !strncmp((char *)(&ext[1]), "ANIMEXTS1.0", 11))
                          {
-                            ext=NULL;
+                            ext = NULL;
                             DGifGetExtensionNext(gif, &ext);
-
                             if (ext[1] == 0x01)
                               {
-                                 loop_count = ext[2] + (ext[3] << 8);
+                                 loop_count = ((int)ext[3] << 8) | (int)ext[2];
                                  if (loop_count > 0) loop_count++;
                               }
                          }
-                     }
-
+                    }
+                  // and continue onto the next extension entry
                   ext = NULL;
                   DGifGetExtensionNext(gif, &ext);
                }
           }
-   } while (rec != TERMINATE_RECORD_TYPE);
-
-   if (a >= 0) prop->alpha = 1;
+     }
+   while (rec != TERMINATE_RECORD_TYPE);
 
-   if ((gif->ImageCount > 1) || (image_count > 1))
+   // if the gif main says we have more than one image or our image counting
+   // says so, then this image is animated - indicate this
+   if ((gif->ImageCount > 1) || (imgnum > 1))
      {
         animated->animated = 1;
         animated->loop_count = loop_count;
         animated->loop_hint = EVAS_IMAGE_ANIMATED_HINT_LOOP;
-        animated->frame_count = MIN(gif->ImageCount, image_count);
-        animated->frames = NULL;
+        animated->frame_count = MIN(gif->ImageCount, imgnum);
      }
+   if (!full) prop->alpha = 1;
+   animated->cur_frame = 1;
 
+   // no errors in header scan etc. so set err and return value
    *error = EVAS_LOAD_ERROR_NONE;
-   r = EINA_TRUE;
+   ret = EINA_TRUE;
 
- on_error:
+on_error: // jump here on any errors to clean up
    if (gif) DGifCloseFile(gif);
-   if (egi.map) eina_file_map_free(f, egi.map);
-   return r;
+   if (fi.map) eina_file_map_free(f, fi.map);
+   return ret;
 }
 
 static Eina_Bool
-evas_image_load_specific_frame(Eina_File *f,
-                               const Evas_Image_Load_Opts *opts,
+evas_image_load_file_data_gif2(void *loader_data,
                                Evas_Image_Property *prop,
-                               Evas_Image_Animated *animated, int frame_index,
+                               void *pixels,
                                int *error)
 {
-   GifFileType       *gif = NULL;
-   Image_Entry_Frame *frame = NULL;
-   Gif_Frame         *gif_frame = NULL;
-   Evas_GIF_Info      egi;
-   Eina_Bool          r = EINA_FALSE;
-
-   egi.map = eina_file_map_all(f, EINA_FILE_RANDOM);
-   if (!egi.map)
-     {
-        *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
-        goto on_error;
-     }
-   egi.length = eina_file_size_get(f);
-   egi.position = 0;
-
-#if GIFLIB_MAJOR >= 5
-   gif = DGifOpen(&egi, _evas_image_load_file_read, NULL);
-#else
-   gif = DGifOpen(&egi, _evas_image_load_file_read);
-#endif
-   if (!gif)
-     {
-        *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
-        goto on_error;
-     }
-   if (!_evas_image_skip_frame(gif, frame_index-1))
-     {
-        *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
-        goto on_error;
-     }
-
-   frame = malloc(sizeof (Image_Entry_Frame));
-   if (!frame)
-     {
-        *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
-        goto on_error;
-     }
-
-   gif_frame = calloc(1, sizeof (Gif_Frame));
-   if (!gif_frame)
-     {
-        *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
-        free(frame);
-        goto on_error;
-     }
-   frame->info = gif_frame;
-   frame->index = frame_index;
-   if (!_evas_image_load_frame(f, opts, prop, animated, gif, frame, LOAD_FRAME_DATA_INFO, error))
-     {
-        *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
-        free(gif_frame);
-        free(frame);
-        goto on_error;
-     }
-
-   animated->frames = eina_list_append(animated->frames, frame);
-   r = EINA_TRUE;
-
- on_error:
-   if (gif) DGifCloseFile(gif);
-   if (egi.map) eina_file_map_free(f, egi.map);
-   return r;
-}
-
-static Eina_Bool
-evas_image_load_file_data_gif(void *loader_data,
-                              Evas_Image_Property *prop,
-                              void *pixels, int *error)
-{
-   Evas_Loader_Internal *loader = loader_data;
-   Evas_Image_Load_Opts *opts;
-   Evas_Image_Animated *animated;
-   Eina_File *f;
-
-   Image_Entry_Frame *frame = NULL;
-   int cur_frame_index;
-   Eina_Bool hit;
-
-   opts = loader->opts;
-   animated = loader->animated;
-   f = loader->f;
-
-   if(!animated->animated)
-     cur_frame_index = 1;
-   else
-     cur_frame_index = animated->cur_frame;
-
+   Loader_Info *loader = loader_data;
+   Evas_Image_Animated *animated = loader->animated;
+   Eina_File *f = loader->f;
+   Eina_Bool ret = EINA_FALSE;
+   File_Info fi;
+   GifRecordType rec;
+   GifFileType *gif = NULL;
+   Image_Entry_Frame *frame;
+   int index = 0, imgnum = 0;
+   Frame_Info *finfo;
+
+   // XXX: this is so wrong - storing current frame IN the image
+   // so we have to load multiple times to animate. what if the
+   // same image is shared/loaded in 2 ore more places AND animated
+   // there?
+   
+   // use index stored in image (XXX: yuk!)
+   index = animated->cur_frame;
+   // if index is invalid for animated image - error out
    if ((animated->animated) &&
-       ((cur_frame_index < 0) || (cur_frame_index > FRAME_MAX) || (cur_frame_index > animated->frame_count)))
+       ((index <= 0) || (index > animated->frame_count)))
+     LOADERR(EVAS_LOAD_ERROR_GENERIC);
+   // find the given frame index
+   frame = _find_frame(animated, index);
+   if (frame)
      {
-        *error = EVAS_LOAD_ERROR_GENERIC;
-        return EINA_FALSE;
-     }
-
-   /* first time frame is set to be 0. so default is 1 */
-   if (cur_frame_index == 0) cur_frame_index++;
-
-   /* Check current frame exists in hash table */
-   hit = _find_frame(animated, cur_frame_index, &frame);
-
-   /* if current frame exist in has table, check load flag */
-   if (hit)
-     {
-        if (frame->loaded)
-         {
-             evas_image_load_file_data_gif_internal(prop, frame, pixels, error);
-         }
-        else
+        if ((frame->loaded) && (frame->data))
           {
-             Evas_GIF_Info egi;
-             GifFileType  *gif = NULL;
-             Eina_Bool     r = EINA_FALSE;
-
-             egi.map = eina_file_map_all(f, EINA_FILE_RANDOM);
-             if (!egi.map)
-               {
-                  *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
-                  goto on_error;
-               }
-             egi.length = eina_file_size_get(f);
-             egi.position = 0;
-
-#if GIFLIB_MAJOR >= 5
-            gif = DGifOpen(&egi, _evas_image_load_file_read, NULL);
-#else
-             gif = DGifOpen(&egi, _evas_image_load_file_read);
-#endif
-             if (!gif)
-               {
-                  *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
-                  goto on_error;
-               }
-             _evas_image_skip_frame(gif, cur_frame_index - 1);
-             if (!_evas_image_load_frame(f, opts, prop, animated, gif, frame, LOAD_FRAME_DATA, error))
-               {
-                  *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
-                  goto on_error;
-               }
-             if (!evas_image_load_file_data_gif_internal(prop, frame, pixels, error))
-               {
-                  *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
-                  goto on_error;
-               }
-             *error = EVAS_LOAD_ERROR_NONE;
-             r = EINA_TRUE;
-
-          on_error:
-             if (gif) DGifCloseFile(gif);
-             if (egi.map) eina_file_map_free(f, egi.map);
-             return r;
+             // frame is already there and decoded - jump to end
+             goto on_ok;
           }
      }
-   /* current frame does is not exist */
    else
-     {
-        if (!evas_image_load_specific_frame(f, opts, prop, animated, cur_frame_index, error))
-          return EINA_FALSE;
-        hit = EINA_FALSE;
-        frame = NULL;
-        hit = _find_frame(animated, cur_frame_index, &frame);
-        if (!hit) return EINA_FALSE;
-        if (!evas_image_load_file_data_gif_internal(prop, frame, pixels, error))
-          {
-             *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
-             return EINA_FALSE;
-          }
-        return EINA_TRUE;
-     }
-   return EINA_FALSE;
-}
-
-static double
-evas_image_load_frame_duration_gif(void *loader_data,
-                                  int start_frame, int frame_num)
-{
-   Evas_Loader_Internal *loader = loader_data;
-   Evas_Image_Animated *animated;
-   Eina_File *f;
-
-   Evas_GIF_Info  egi;
-   GifFileType   *gif = NULL;
-   GifRecordType  rec;
-   int            current_frame = 1;
-   int            remain_frames = frame_num;
-   double         duration = -1;
-   int            frame_count = 0;
-
-   animated = loader->animated;
-   f = loader->f;
-   frame_count = animated->frame_count;
+     LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE);
 
-   if (!animated->animated) return -1;
-   if ((start_frame + frame_num) > frame_count) return -1;
-   if (frame_num < 0) return -1;
+   // map the file and store/track info
+   fi.map = eina_file_map_all(f, EINA_FILE_RANDOM);
+   if (!fi.map) LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE);
+   fi.len = eina_file_size_get(f);
+   fi.pos = 0;
 
-   egi.map = eina_file_map_all(f, EINA_FILE_RANDOM);
-   if (!egi.map) goto on_error;
-   egi.length = eina_file_size_get(f);
-   egi.position = 0;        
+   // actually ask libgif to open the file
 #if GIFLIB_MAJOR >= 5
-   gif = DGifOpen(&egi, _evas_image_load_file_read, NULL);
+   gif = DGifOpen(&fi, _file_read, NULL);
 #else
-   gif = DGifOpen(&egi, _evas_image_load_file_read);
+   gif = DGifOpen(&fi, _file_read);
 #endif
-   if (!gif) goto on_error;
+   if (!gif) LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
 
-   duration = 0;
+   imgnum = 1;
+   // walk through gif records in file to figure out info
    do
      {
         if (DGifGetRecordType(gif, &rec) == GIF_ERROR)
+          LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
+        if (rec == EXTENSION_RECORD_TYPE)
           {
-             rec = TERMINATE_RECORD_TYPE;
+             int                 ext_code;
+             GifByteType        *ext;
+             
+             ext = NULL;
+             DGifGetExtension(gif, &ext_code, &ext);
+             while (ext)
+               {
+                  ext = NULL;
+                  DGifGetExtensionNext(gif, &ext);
+               }
           }
-        if (rec == IMAGE_DESC_RECORD_TYPE)
+        // get image description section
+        else if (rec == IMAGE_DESC_RECORD_TYPE)
           {
-             int                 img_code;
-             GifByteType        *img;
+             int xin = 0, yin = 0, x = 0, y = 0, w = 0, h = 0;
+             int img_code;
+             GifByteType *img;
+             Image_Entry_Frame *prevframe = NULL;
+             Image_Entry_Frame *thisframe = NULL;
 
+             // get image desc
              if (DGifGetImageDesc(gif) == GIF_ERROR)
+               LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
+             // get the previous frame entry AND the current one to fill in
+             prevframe = _find_frame(animated, imgnum - 1);
+             thisframe = _find_frame(animated, imgnum);
+             // if we have a frame AND we're animated AND we have no data...
+             if ((thisframe) && (!thisframe->data) && (animated->animated))
                {
-                  /* PrintGifError(); */
-                  rec = TERMINATE_RECORD_TYPE;
-               }
-             current_frame++;
-             /* we have to count frame, so use DGifGetCode and skip decoding */
-             if (DGifGetCode(gif, &img_code, &img) == GIF_ERROR)
-               {
-                  rec = TERMINATE_RECORD_TYPE;
+                  Eina_Bool first = EINA_FALSE;
+                  
+                  // allocate it
+                  thisframe->data =
+                    malloc(prop->w * prop->h * sizeof(DATA32));
+                  if (!thisframe->data)
+                    LOADERR(EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED);
+                  // if we have no prior frame OR prior frame data... empty
+                  if ((!prevframe) || (!prevframe->data))
+                    {
+                       first = EINA_TRUE;
+                       finfo = thisframe->info;
+                       memset(thisframe->data, 0,
+                              prop->w * prop->h * sizeof(DATA32));
+                    }
+                  // we have a prior frame to copy data from...
+                  else
+                    {
+                       finfo = prevframe->info;
+
+                       // fix coords of sub image in case it goes out...
+                       _clip_coords(prop->w, prop->h, &xin, &yin,
+                                    finfo->x, finfo->y, finfo->w, finfo->h,
+                                    &x, &y, &w, &h);
+                       // if dispose mode is not restore - then copy pre frame
+                       if (finfo->dispose != 3) // GIF_DISPOSE_RESTORE
+                         memcpy(thisframe->data, prevframe->data,
+                              prop->w * prop->h * sizeof(DATA32));
+                       // if dispose mode is "background" then fill with bg
+                       if (finfo->dispose == 2) // GIF_DISPOSE_BACKGND
+                         _fill_frame(thisframe->data, prop->w, gif,
+                                     finfo, x, y, w, h);
+                       else if (finfo->dispose == 3) // GIF_DISPOSE_RESTORE
+                         {
+                            Image_Entry_Frame *prevframe2;
+                            
+                            // we need to copy data from one frame back
+                            // from the prev frame into the current frame
+                            // (copy the whole image - at least the sample
+                            // GifWin.cpp from libgif indicates this is what
+                            // needs doing
+                            prevframe2 = _find_frame(animated, imgnum - 2);
+                            if (prevframe2)
+                              memcpy(thisframe->data, prevframe2->data,
+                                     prop->w * prop->h * sizeof(DATA32));
+                         }
+                    }
+                  // now draw this frame on top
+                  finfo = thisframe->info;
+                  _clip_coords(prop->w, prop->h, &xin, &yin,
+                               finfo->x, finfo->y, finfo->w, finfo->h,
+                               &x, &y, &w, &h);
+                  if (!_decode_image(gif, thisframe->data, prop->w,
+                                     xin, yin, finfo->transparent,
+                                     x, y, w, h, first))
+                    LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE);
+                  // mark as loaded and done
+                  thisframe->loaded = EINA_TRUE;
+                  // and flush old memory if needed (too much)
+                  _flush_older_frames(animated, prop->w, prop->h,
+                                      thisframe, prevframe);
                }
-             while (img)
+             // if we hve a frame BUT the image is not animated... different
+             // path
+             else if ((thisframe) && (!thisframe->data) && 
+                      (!animated->animated))
                {
-                  img = NULL;
-                  DGifGetExtensionNext(gif, &img);
+                  // if we don't have the data decoded yet - decode it
+                  if (!thisframe->loaded)
+                    {
+                       // use frame info but we WONT allocate frame pixels
+                       finfo = thisframe->info;
+                       _clip_coords(prop->w, prop->h, &xin, &yin,
+                                    finfo->x, finfo->y, finfo->w, finfo->h,
+                                    &x, &y, &w, &h);
+                       // clear out all pixels
+                       _fill_frame(pixels, prop->w, gif,
+                                   finfo, 0, 0, prop->w, prop->h);
+                       // and decode the gif with overwriting
+                       if (!_decode_image(gif, pixels, prop->w,
+                                          xin, yin, finfo->transparent,
+                                          x, y, w, h, EINA_TRUE))
+                         LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE);
+                       // mark as loaded and done
+                       thisframe->loaded = EINA_TRUE;
+                    }
+                  // flush mem we don't need (at expense of decode cpu)
                }
-          }
-        else if (rec == EXTENSION_RECORD_TYPE)
-          {
-             int                 ext_code;
-             GifByteType        *ext;
-
-             ext = NULL;
-             DGifGetExtension(gif, &ext_code, &ext);
-             while (ext)
+             else
                {
-                  if (ext_code == 0xf9) /* Graphic Control Extension */
+                  // skip decoding and just walk image to next
+                  if (DGifGetCode(gif, &img_code, &img) == GIF_ERROR)
+                    LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
+                  while (img)
                     {
-                       if ((current_frame  >= start_frame) && (current_frame <= frame_count))
-                         {
-                            int frame_duration = 0;
-                            if (remain_frames < 0) break;
-                            frame_duration = byte2_to_int (ext[2], ext[3]);
-                            if (frame_duration == 0)
-                              duration += 0.1;
-                            else
-                              duration += (double)frame_duration/100;
-                            remain_frames --;
-                         }
+                       img = NULL;
+                       DGifGetCodeNext(gif, &img);
                     }
-                  ext = NULL;
-                  DGifGetExtensionNext(gif, &ext);
                }
-         }
-     } while (rec != TERMINATE_RECORD_TYPE);
-
- on_error:
+             // if we found the image we wanted - get out of here
+             if (imgnum == index) break;
+             imgnum++;
+          }
+     }
+   while (rec != TERMINATE_RECORD_TYPE);
+   
+on_ok:   
+   // no errors in header scan etc. so set err and return value
+   *error = EVAS_LOAD_ERROR_NONE;
+   ret = EINA_TRUE;
+   
+   // if it was an animated image we need to copy the data to the
+   // pixels for the image from the frame holding the data
+   if (animated->animated)
+     memcpy(pixels, frame->data, prop->w * prop->h * sizeof (DATA32));
+   prop->premul = EINA_TRUE;
+   
+on_error: // jump here on any errors to clean up
    if (gif) DGifCloseFile(gif);
-   if (egi.map) eina_file_map_free(f, egi.map);
-   return duration;
+   if (fi.map) eina_file_map_free(f, fi.map);
+   return ret;
+}
+
+// get the time between 2 frames in the timeline
+static double
+evas_image_load_frame_duration_gif2(void *loader_data,
+                                    int start_frame,
+                                    int frame_num)
+{
+   Loader_Info *loader = loader_data;
+   Evas_Image_Animated *animated = loader->animated;
+   Image_Entry_Frame *frame;
+   int i, total = 0;
+
+   // if its not animated or requested frame data is invalid
+   if (!animated->animated) return -1.0;
+   if ((start_frame + frame_num) > animated->frame_count) return -1.0;
+   if (frame_num < 0) return -1.0;
+   
+   if (frame_num < 1) frame_num = 1;
+   // walk frames from start frame though and total up delays
+   for (i = start_frame; i < (start_frame + frame_num); i++)
+     {
+        Frame_Info *finfo;
+        
+        // find the frame
+        frame = _find_frame(animated, i);
+        // no frame? barf - bad file or i/o?
+        if (!frame) return -1.0;
+        // get delay and total it up
+        finfo = frame->info;
+        // if delay is sensible - use it else assume 10/100ths of a sec
+        if (finfo->delay > 0) total += finfo->delay;
+        else total += 10;
+     }
+   // return delay in seconds (since timing in gifs is in 1/100ths of a sec)
+   return (double)total / 100.0;
+}
+
+// called on opening of a file load
+static void *
+evas_image_load_file_open_gif2(Eina_File *f,
+                               Eina_Stringshare *key EINA_UNUSED, // XXX: we need to use key for frame #
+                               Evas_Image_Load_Opts *opts,
+                               Evas_Image_Animated *animated,
+                               int *error)
+{
+   Loader_Info *loader = calloc(1, sizeof (Loader_Info));
+   if (!loader)
+     {
+        *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
+        return NULL;
+     }
+   loader->f = f;
+   loader->opts = opts;
+   loader->animated = animated;
+   return loader;
+}
+
+// called on closing of an image file load (end of load)
+static void
+evas_image_load_file_close_gif2(void *loader_data)
+{
+   Loader_Info *loader = loader_data;
+   free(loader);
 }
 
+// general module delcaration stuff
 static Evas_Image_Load_Func evas_image_load_gif_func =
 {
-  evas_image_load_file_open_gif,
-  evas_image_load_file_close_gif,
-  evas_image_load_file_head_gif, 
-  evas_image_load_file_data_gif,
-  evas_image_load_frame_duration_gif,
+  evas_image_load_file_open_gif2,
+  evas_image_load_file_close_gif2,
+  evas_image_load_file_head_gif2
+  evas_image_load_file_data_gif2,
+  evas_image_load_frame_duration_gif2,
   EINA_TRUE,
   EINA_FALSE
 };
 
+// raw module api that the rest of the world sees
 static int
 module_open(Evas_Module *em)
 {
@@ -1052,13 +840,13 @@ module_close(Evas_Module *em EINA_UNUSED)
 
 static Evas_Module_Api evas_modapi =
 {
-  EVAS_MODULE_API_VERSION,
-  "gif",
-  "none",
-  {
-    module_open,
-    module_close
-  }
+   EVAS_MODULE_API_VERSION,
+   "gif",
+   "none",
+   {
+      module_open,
+      module_close
+   }
 };
 
 EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, gif);