diff --git a/src/modules/evas/loaders/gif/evas_image_load_gif.c b/src/modules/evas/loaders/gif/evas_image_load_gif.c index 6c2c634d6a..3f2ef53f11 100644 --- a/src/modules/evas/loaders/gif/evas_image_load_gif.c +++ b/src/modules/evas/loaders/gif/evas_image_load_gif.c @@ -8,315 +8,210 @@ #include -#ifndef MIN -# define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif +typedef struct _Frame_Info Frame_Info; +typedef struct _Loader_Info Loader_Info; -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 _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); - -#define byte2_to_int(a,b) (((b)<<8)|(a)) - -#define FRAME_MAX 1024 - -/* find specific frame in image entry */ -static Eina_Bool -_find_frame(Evas_Image_Animated *animated, int frame_index, Image_Entry_Frame **frame) +struct _Frame_Info { - Eina_List *l; - Image_Entry_Frame *hit_frame = NULL; - - if (!animated->frames) return EINA_FALSE; - - EINA_LIST_FOREACH(animated->frames, l, hit_frame) - { - if (hit_frame->index == frame_index) - { - *frame = hit_frame; - return EINA_TRUE; - } - } - return EINA_FALSE; -} - -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) -{ - 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 - { - if (DGifGetRecordType(gif, &rec) == GIF_ERROR) return EINA_FALSE; - - if (rec == EXTENSION_RECORD_TYPE) - { - int ext_code; - GifByteType *ext; - - ext = NULL; - DGifGetExtension(gif, &ext_code, &ext); - while (ext) - { /*skip extention */ - ext = NULL; - DGifGetExtensionNext(gif, &ext); - } - } - - 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) -{ - 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; -} - -static Eina_Bool -_evas_image_load_frame_image_des_info(GifFileType *gif, Image_Entry_Frame *frame) -{ - 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; -} + 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 +}; +#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) -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) +// utility funcs... + +// 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) { - int xx, yy, pix; + Eina_List *l; + Image_Entry_Frame *frame; + + if (!animated->frames) return NULL; + EINA_LIST_FOREACH(animated->frames, l, frame) + { + if (frame->index == index) return frame; + } + return NULL; +} + +// 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 xx, yy; DATA32 *p; - if (alpha >= 0) + for (yy = 0; yy < h; yy++) { - if (overwrite) + p = data + ((y + yy) * rowpix) + x; + for (xx = 0; xx < w; xx++) { - 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++; - } - } + *p = val; + p++; } } +} + +// 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) +{ + 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; +} + +// 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) +{ + // 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 - { - 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++; - } - } - } + _fill_image(data, rowpix, 0, x, y, w, h); } +// store common fields from gif file info into frame info static void -_copy_pixels(DATA32 *ptr_src, DATA32 *ptr, int rowpix, - int x, int y, int w, int h) +_store_frame_info(GifFileType *gif, Frame_Info *finfo) { - DATA32 *p1, *p2, *pe; - int yy; - - if ((w <= 0) || (h <= 0)) return; - for (yy = 0; yy < h; yy++) - { - p1 = ptr_src + ((y + yy) * rowpix) + x; - p2 = ptr + ((y + yy) * rowpix) + x; - for (pe = p2 + w; p2 < pe;) *p2++ = *p1++; - } + 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 -_fill_pixels(DATA32 val, 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 *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)) { - p2 = ptr + ((y + yy) * rowpix) + x; - for (pe = p2 + w; p2 < pe;) *p2++ = val; + if (finfo->transparent >= 0) *full = EINA_FALSE; } + else *full = EINA_FALSE; } +// 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) +{ + 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) + { + 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); + // work out what colormap to use + if (gif->Image.ColorMap) cmap = gif->Image.ColorMap; + else cmap = gif->SColorMap; - if (!cmap) + // if we need to deal with transparent pixels at all... + if (transparent >= 0) { - DGifCloseFile(gif); - if (frame->data) free(frame->data); - *error = EVAS_LOAD_ERROR_CORRUPT_FILE; - goto error; - } - - 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) - { - 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)) + // if we are told to FILL (overwrite with transparency kept) + if (fill) { - *error = EVAS_LOAD_ERROR_CORRUPT_FILE; - goto error; - } - /* load previous frame of cur_frame */ - for (i = start_frame; i < cur_frame; i++) - { - // 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); - } - - if (rows) free(rows); - frame->loaded = EINA_TRUE; - return EINA_TRUE; -error: - if (rows) free(rows); - return EINA_FALSE; -} - -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) -{ - 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; - - do - { - if (DGifGetRecordType(gif, &rec) == GIF_ERROR) return EINA_FALSE; - if (rec == IMAGE_DESC_RECORD_TYPE) + // walk pixels without worring about transparency at all + for (yy = 0; yy < h; yy++) { - img_res++; - break; - } - else if (rec == EXTENSION_RECORD_TYPE) - { - int ext_code; - GifByteType *ext; - - ext = NULL; - DGifGetExtension(gif, &ext_code, &ext); - while (ext) + p = data + ((y + yy) * rowpix) + x; + for (xx = 0; xx < w; xx++) { - 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); + pix = PIX(xx, yy); + *p = PIXLK(pix); + p++; } } - } 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; } - return EINA_TRUE; + ret = EINA_TRUE; + +on_error: + free(rows); + return ret; } - -/* 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) +// 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) { - /* only copy real frame part */ - memcpy(pixels, frame->data, prop->w * prop->h * sizeof (DATA32)); - prop->premul = EINA_TRUE; + 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; - *error = EVAS_LOAD_ERROR_NONE; - return EINA_TRUE; + // total up the amount of memory used by stored frames for this image + EINA_LIST_FOREACH(animated->frames, l, frame) + { + 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) + { + if ((frame != thisframe) && (frame != prevframe)) + { + free(frame->data); + frame->data = NULL; + // subtract memory used and if below target - stop flush + total -= (w * h * sizeof(DATA32)); + if (total < target) break; + } + } + // go to next - handle wrap to start + l = l->next; + if (!l) l = animated->frames; + } } + -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; + File_Info *fi = 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; - - 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); + } + while (rec != TERMINATE_RECORD_TYPE); - if (a >= 0) prop->alpha = 1; - - 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; -} + LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE); -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; + // 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; - 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; - - if (!animated->animated) return -1; - if ((start_frame + frame_num) > frame_count) return -1; - if (frame_num < 0) return -1; - - 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) - { - rec = TERMINATE_RECORD_TYPE; - } - if (rec == IMAGE_DESC_RECORD_TYPE) - { - int img_code; - GifByteType *img; - - if (DGifGetImageDesc(gif) == GIF_ERROR) - { - /* 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; - } - while (img) - { - img = NULL; - DGifGetExtensionNext(gif, &img); - } - } - else if (rec == EXTENSION_RECORD_TYPE) + LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT); + if (rec == EXTENSION_RECORD_TYPE) { int ext_code; GifByteType *ext; - + ext = NULL; DGifGetExtension(gif, &ext_code, &ext); while (ext) { - if (ext_code == 0xf9) /* Graphic Control Extension */ - { - 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 --; - } - } ext = NULL; DGifGetExtensionNext(gif, &ext); } - } - } while (rec != TERMINATE_RECORD_TYPE); + } + // get image description section + else if (rec == IMAGE_DESC_RECORD_TYPE) + { + 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; - on_error: + // 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)) + { + 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); + } + // if we hve a frame BUT the image is not animated... different + // path + else if ((thisframe) && (!thisframe->data) && + (!animated->animated)) + { + // 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 + { + // skip decoding and just walk image to next + if (DGifGetCode(gif, &img_code, &img) == GIF_ERROR) + LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT); + while (img) + { + img = NULL; + DGifGetCodeNext(gif, &img); + } + } + // 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);