#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++)
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)
{
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);