evas - fix gif loader cpu overuse - used too much cpu to decode anims
stable release - cherry-pick me! the evas gif loader used way too much cpu to decode animated gifs because in the rewrite that made it correct, it did not store the current gif file handle and state, thus each frame it would have to decode all frames before that one before finally decoding the final one. that means to decode frame 200, it decoded frame 1, 2, 3, 4 etc. all the way up to 199 THEN decoded 200 on top, so decode cost became progressively more then further through the animation you were. this fixes that by storing state and file handle and allowing you to iterate through.
This commit is contained in:
parent
4a24b84185
commit
3047ec4d91
|
@ -10,12 +10,22 @@
|
|||
|
||||
typedef struct _Frame_Info Frame_Info;
|
||||
typedef struct _Loader_Info Loader_Info;
|
||||
typedef struct _File_Info File_Info;
|
||||
|
||||
struct _File_Info
|
||||
{
|
||||
unsigned char *map;
|
||||
int pos, len; // yes - gif uses ints for file sizes.
|
||||
};
|
||||
|
||||
struct _Loader_Info
|
||||
{
|
||||
Eina_File *f;
|
||||
Evas_Image_Load_Opts *opts;
|
||||
Evas_Image_Animated *animated;
|
||||
GifFileType *gif;
|
||||
int imgnum;
|
||||
File_Info fi;
|
||||
};
|
||||
|
||||
struct _Frame_Info
|
||||
|
@ -345,15 +355,6 @@ _flush_older_frames(Evas_Image_Animated *animated,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// file access info/funcs
|
||||
typedef struct _File_Info File_Info;
|
||||
struct _File_Info
|
||||
{
|
||||
unsigned char *map;
|
||||
int pos, len; // yes - gif uses ints for file sizes.
|
||||
};
|
||||
|
||||
static int
|
||||
_file_read(GifFileType *gft, GifByteType *buf, int len)
|
||||
{
|
||||
|
@ -542,7 +543,6 @@ evas_image_load_file_data_gif2(void *loader_data,
|
|||
Evas_Image_Animated *animated = loader->animated;
|
||||
Eina_File *f = loader->f;
|
||||
Eina_Bool ret = EINA_FALSE;
|
||||
File_Info fi = { NULL, 0, 0 };
|
||||
GifRecordType rec;
|
||||
GifFileType *gif = NULL;
|
||||
Image_Entry_Frame *frame;
|
||||
|
@ -573,21 +573,51 @@ evas_image_load_file_data_gif2(void *loader_data,
|
|||
else
|
||||
LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE);
|
||||
|
||||
// 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;
|
||||
|
||||
open_file:
|
||||
// actually ask libgif to open the file
|
||||
#if GIFLIB_MAJOR >= 5
|
||||
gif = DGifOpen(&fi, _file_read, NULL);
|
||||
#else
|
||||
gif = DGifOpen(&fi, _file_read);
|
||||
#endif
|
||||
if (!gif) LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
|
||||
gif = loader->gif;
|
||||
if (!gif)
|
||||
{
|
||||
// there was no file previously opened
|
||||
// map the file and store/track info
|
||||
loader->fi.map = eina_file_map_all(f, EINA_FILE_RANDOM);
|
||||
if (!loader->fi.map) LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE);
|
||||
loader->fi.len = eina_file_size_get(f);
|
||||
loader->fi.pos = 0;
|
||||
|
||||
#if GIFLIB_MAJOR >= 5
|
||||
gif = DGifOpen(&(loader->fi), _file_read, NULL);
|
||||
#else
|
||||
gif = DGifOpen(&(loader->fi), _file_read);
|
||||
#endif
|
||||
// if gif open failed... get out of here
|
||||
if (!gif)
|
||||
{
|
||||
if ((loader->fi.map) && (loader->f))
|
||||
eina_file_map_free(loader->f, loader->fi.map);
|
||||
loader->fi.map = NULL;
|
||||
LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
|
||||
}
|
||||
loader->gif = gif;
|
||||
loader->imgnum = 1;
|
||||
}
|
||||
|
||||
// if we want to go backwards, we likely need/want to re-decode from the
|
||||
// start as we have nothnig to build on
|
||||
if ((index > 0) && (index < loader->imgnum) && (loader->animated))
|
||||
{
|
||||
if (loader->gif) DGifCloseFile(loader->gif);
|
||||
if ((loader->fi.map) && (loader->f))
|
||||
eina_file_map_free(loader->f, loader->fi.map);
|
||||
loader->gif = NULL;
|
||||
loader->fi.map = NULL;
|
||||
loader->imgnum = 0;
|
||||
goto open_file;
|
||||
}
|
||||
|
||||
// our current position is the previous frame we decoded from the file
|
||||
imgnum = loader->imgnum;
|
||||
|
||||
imgnum = 1;
|
||||
// walk through gif records in file to figure out info
|
||||
do
|
||||
{
|
||||
|
@ -625,7 +655,7 @@ evas_image_load_file_data_gif2(void *loader_data,
|
|||
if ((thisframe) && (!thisframe->data) && (animated->animated))
|
||||
{
|
||||
Eina_Bool first = EINA_FALSE;
|
||||
|
||||
|
||||
// allocate it
|
||||
thisframe->data =
|
||||
malloc(prop->w * prop->h * sizeof(DATA32));
|
||||
|
@ -723,12 +753,25 @@ evas_image_load_file_data_gif2(void *loader_data,
|
|||
DGifGetCodeNext(gif, &img);
|
||||
}
|
||||
}
|
||||
// if we found the image we wanted - get out of here
|
||||
if (imgnum == index) break;
|
||||
imgnum++;
|
||||
// if we found the image we wanted - get out of here
|
||||
if (imgnum >= index) break;
|
||||
}
|
||||
}
|
||||
while (rec != TERMINATE_RECORD_TYPE);
|
||||
|
||||
// if we are at the end of the animation or not animated, close file
|
||||
loader->imgnum = imgnum;
|
||||
if ((!loader->animated) ||
|
||||
((loader->animated) && (rec == TERMINATE_RECORD_TYPE)))
|
||||
{
|
||||
if (loader->gif) DGifCloseFile(loader->gif);
|
||||
if ((loader->fi.map) && (loader->f))
|
||||
eina_file_map_free(loader->f, loader->fi.map);
|
||||
loader->gif = NULL;
|
||||
loader->fi.map = NULL;
|
||||
loader->imgnum = 0;
|
||||
}
|
||||
|
||||
on_ok:
|
||||
// no errors in header scan etc. so set err and return value
|
||||
|
@ -742,8 +785,6 @@ on_ok:
|
|||
prop->premul = EINA_TRUE;
|
||||
|
||||
on_error: // jump here on any errors to clean up
|
||||
if (gif) DGifCloseFile(gif);
|
||||
if (fi.map) eina_file_map_free(f, fi.map);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -808,6 +849,9 @@ static void
|
|||
evas_image_load_file_close_gif2(void *loader_data)
|
||||
{
|
||||
Loader_Info *loader = loader_data;
|
||||
if (loader->gif) DGifCloseFile(loader->gif);
|
||||
if ((loader->fi.map) && (loader->f))
|
||||
eina_file_map_free(loader->f, loader->fi.map);
|
||||
free(loader);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue