summaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
authorCarsten Haitzler (Rasterman) <raster@rasterman.com>2013-10-30 18:16:09 +0900
committerCarsten Haitzler (Rasterman) <raster@rasterman.com>2013-10-30 18:18:08 +0900
commit309e287b7c5ca4fb505d7756748d01da5c0fc203 (patch)
tree5eccf403b31e924d772a1d69f5cf83adad6545cc /src/modules
parent6c28aff7a0ac5c917d03f9f275ce3aa0dd749d1c (diff)
evas - gif loader rewrite (clean up code, document it a lot AND fix bugs)
after several days of beating head on desk, i gave up trying to find the exact cause of some gifs not rendering right as animated gifs due to the loader. it had something to do with dispose mode handling and which frame it was applied to. i noticed the structure made it also hard to fix, so this re-structures the entire thing with cleaner code, less code AND more comments, with a limited memory cache (512k) for previous frames per image (to avoid eating up huge memory blobs for big/long animations - though at the expense of cpu), and with some notes for future fixes - like fixing the "load 2 copies of the same animated gif" issue... that's another day. this does fix https://phab.enlightenment.org/T443 along with many other things.
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/evas/loaders/gif/evas_image_load_gif.c1416
1 files changed, 602 insertions, 814 deletions
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 @@
8 8
9#include <gif_lib.h> 9#include <gif_lib.h>
10 10
11#ifndef MIN 11typedef struct _Frame_Info Frame_Info;
12# define MIN(a, b) (((a) < (b)) ? (a) : (b)) 12typedef struct _Loader_Info Loader_Info;
13#endif
14
15typedef struct _Gif_Frame Gif_Frame;
16
17typedef enum _Frame_Load_Type
18{
19 LOAD_FRAME_NONE = 0,
20 LOAD_FRAME_INFO = 1,
21 LOAD_FRAME_DATA = 2,
22 LOAD_FRAME_DATA_INFO = 3
23} Frame_Load_Type;
24
25struct _Gif_Frame
26{
27 struct {
28 /* Image descriptor */
29 int x;
30 int y;
31 int w;
32 int h;
33 int interlace;
34 } image_des;
35
36 struct {
37 /* Graphic Control*/
38 int disposal;
39 int transparent;
40 int delay;
41 int input;
42 } frame_info;
43 int bg_val;
44};
45 13
46typedef struct _Evas_Loader_Internal Evas_Loader_Internal; 14struct _Loader_Info
47struct _Evas_Loader_Internal
48{ 15{
49 Eina_File *f; 16 Eina_File *f;
50 Evas_Image_Load_Opts *opts; 17 Evas_Image_Load_Opts *opts;
51 Evas_Image_Animated *animated; 18 Evas_Image_Animated *animated;
52}; 19};
53 20
54static 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); 21struct _Frame_Info
22{
23 int x, y, w, h;
24 unsigned short delay; // delay time in 1/100ths of a sec
25 short transparent : 10; // -1 == not, anything else == index
26 short dispose : 6; // 0, 1, 2, 3 (others invalid)
27 short interlace : 1; // interlaced or not
28};
55 29
56#define byte2_to_int(a,b) (((b)<<8)|(a)) 30#ifndef MIN
31# define MIN(a, b) (((a) < (b)) ? (a) : (b))
32#endif
33#define LOADERR(x) \
34do { \
35 *error = (x); \
36 goto on_error; \
37} while (0)
38#define PIX(_x, _y) rows[yin + _y][xin + _x]
39#define CMAP(_v) cmap->Colors[_v]
40#define PIXLK(_p) ARGB_JOIN(0xff, CMAP(_p).Red, CMAP(_p).Green, CMAP(_p).Blue)
57 41
58#define FRAME_MAX 1024 42// utility funcs...
59 43
60/* find specific frame in image entry */ 44// brute force find frame index - gifs are normally saml so ok for now
61static Eina_Bool 45static Image_Entry_Frame *
62_find_frame(Evas_Image_Animated *animated, int frame_index, Image_Entry_Frame **frame) 46_find_frame(Evas_Image_Animated *animated, int index)
63{ 47{
64 Eina_List *l; 48 Eina_List *l;
65 Image_Entry_Frame *hit_frame = NULL; 49 Image_Entry_Frame *frame;
66 50
67 if (!animated->frames) return EINA_FALSE; 51 if (!animated->frames) return NULL;
68 52 EINA_LIST_FOREACH(animated->frames, l, frame)
69 EINA_LIST_FOREACH(animated->frames, l, hit_frame)
70 { 53 {
71 if (hit_frame->index == frame_index) 54 if (frame->index == index) return frame;
72 {
73 *frame = hit_frame;
74 return EINA_TRUE;
75 }
76 } 55 }
77 return EINA_FALSE; 56 return NULL;
78} 57}
79 58
80static Eina_Bool 59// fill in am image with a specific rgba color value
81_find_close_frame(Evas_Image_Animated *animated, int frame_index, Image_Entry_Frame **frame) 60static void
82{ 61_fill_image(DATA32 *data, int rowpix, DATA32 val, int x, int y, int w, int h)
83 Eina_Bool hit = EINA_FALSE;
84 int i;
85
86 i = frame_index -1;
87
88 if (!animated->frames) return EINA_FALSE;
89
90 for (; i > 0; i--)
91 {
92 hit = _find_frame(animated, i, frame);
93 if (hit)
94 return EINA_TRUE;
95 }
96 return EINA_FALSE;
97}
98
99static Eina_Bool
100_evas_image_skip_frame(GifFileType *gif, int frame)
101{ 62{
102 int remain_frame = 0; 63 int xx, yy;
103 GifRecordType rec; 64 DATA32 *p;
104 65
105 if (!gif) return EINA_FALSE; 66 for (yy = 0; yy < h; yy++)
106 if (frame == 0) return EINA_TRUE; /* no need to skip */
107 if (frame < 0 || frame > FRAME_MAX) return EINA_FALSE;
108
109 remain_frame = frame;
110
111 do
112 { 67 {
113 if (DGifGetRecordType(gif, &rec) == GIF_ERROR) return EINA_FALSE; 68 p = data + ((y + yy) * rowpix) + x;
114 69 for (xx = 0; xx < w; xx++)
115 if (rec == EXTENSION_RECORD_TYPE)
116 { 70 {
117 int ext_code; 71 *p = val;
118 GifByteType *ext; 72 p++;
119
120 ext = NULL;
121 DGifGetExtension(gif, &ext_code, &ext);
122 while (ext)
123 { /*skip extention */
124 ext = NULL;
125 DGifGetExtensionNext(gif, &ext);
126 }
127 } 73 }
128 74 }
129 if (rec == IMAGE_DESC_RECORD_TYPE)
130 {
131 int img_code;
132 GifByteType *img;
133
134 if (DGifGetImageDesc(gif) == GIF_ERROR) return EINA_FALSE;
135
136 remain_frame --;
137 /* we have to count frame, so use DGifGetCode and skip decoding */
138 if (DGifGetCode(gif, &img_code, &img) == GIF_ERROR) return EINA_FALSE;
139
140 while (img)
141 {
142 img = NULL;
143 DGifGetCodeNext(gif, &img);
144 }
145 if (remain_frame < 1) return EINA_TRUE;
146 }
147 if (rec == TERMINATE_RECORD_TYPE) return EINA_FALSE; /* end of file */
148
149 } while ((rec != TERMINATE_RECORD_TYPE) && (remain_frame > 0));
150 return EINA_FALSE;
151} 75}
152 76
153static Eina_Bool 77// fix coords and work out an x and y inset in orig data if out of image bounds
154_evas_image_load_frame_graphic_info(Image_Entry_Frame *frame, GifByteType *ext) 78static void
79_clip_coords(int imw, int imh, int *xin, int *yin,
80 int x0, int y0, int w0, int h0,
81 int *x, int *y, int *w, int *h)
155{ 82{
156 Gif_Frame *gif_frame = NULL; 83 if (x0 < 0)
157 if ((!frame) || (!ext)) return EINA_FALSE; 84 {
158 85 w0 += x0;
159 gif_frame = (Gif_Frame *) frame->info; 86 *xin = -x0;
160 87 x0 = 0;
161 /* transparent */ 88 }
162 if ((ext[1] & 0x1) != 0) 89 if ((x0 + w0) > imw) w0 = imw - x0;
163 gif_frame->frame_info.transparent = ext[4]; 90 if (y0 < 0)
164 else 91 {
165 gif_frame->frame_info.transparent = -1; 92 h0 += y0;
166 93 *yin = -y0;
167 gif_frame->frame_info.input = (ext[1] >>1) & 0x1; 94 y0 = 0;
168 gif_frame->frame_info.disposal = (ext[1] >>2) & 0x7; 95 }
169 gif_frame->frame_info.delay = byte2_to_int(ext[2], ext[3]); 96 if ((y0 + h0) > imh) h0 = imh - y0;
170 return EINA_TRUE; 97 *x = x0;
98 *y = y0;
99 *w = w0;
100 *h = h0;
171} 101}
172 102
173static Eina_Bool 103// file a rgba data pixle blob with a frame color (bg or trans) depending...
174_evas_image_load_frame_image_des_info(GifFileType *gif, Image_Entry_Frame *frame) 104static void
105_fill_frame(DATA32 *data, int rowpix, GifFileType *gif, Frame_Info *finfo,
106 int x, int y, int w, int h)
175{ 107{
176 Gif_Frame *gif_frame = NULL; 108 // solid color fill for pre frame region
177 if ((!gif) || (!frame)) return EINA_FALSE; 109 if (finfo->transparent < 0)
178 110 {
179 gif_frame = (Gif_Frame *) frame->info; 111 ColorMapObject *cmap;
180 gif_frame->image_des.x = gif->Image.Left; 112 int bg;
181 gif_frame->image_des.y = gif->Image.Top; 113
182 gif_frame->image_des.w = gif->Image.Width; 114 // work out color to use from cmap
183 gif_frame->image_des.h = gif->Image.Height; 115 if (gif->Image.ColorMap) cmap = gif->Image.ColorMap;
184 gif_frame->image_des.interlace = gif->Image.Interlace; 116 else cmap = gif->SColorMap;
185 return EINA_TRUE; 117 bg = gif->SBackGroundColor;
118 // and do the fill
119 _fill_image
120 (data, rowpix,
121 ARGB_JOIN(0xff, CMAP(bg).Red, CMAP(bg).Green, CMAP(bg).Blue),
122 x, y, w, h);
123 }
124 // fill in region with 0 (transparent)
125 else
126 _fill_image(data, rowpix, 0, x, y, w, h);
186} 127}
187 128
188#define PIX(_x, _y) rows[yin + _y][xin + _x] 129// store common fields from gif file info into frame info
189#define CMAP(_v) cmap->Colors[_v]
190
191static void 130static void
192_expand_gif_rows(GifRowType *rows, ColorMapObject *cmap, DATA32 *ptr, 131_store_frame_info(GifFileType *gif, Frame_Info *finfo)
193 int rowpix, int xin, int yin, int w, int h,
194 int x, int y, int alpha, Eina_Bool overwrite)
195{ 132{
196 int xx, yy, pix; 133 finfo->x = gif->Image.Left;
197 DATA32 *p; 134 finfo->y = gif->Image.Top;
198 135 finfo->w = gif->Image.Width;
199 if (alpha >= 0) 136 finfo->h = gif->Image.Height;
200 { 137 finfo->interlace = gif->Image.Interlace;
201 if (overwrite)
202 {
203 for (yy = 0; yy < h; yy++)
204 {
205 p = ptr + ((y + yy) * rowpix) + x;
206 for (xx = 0; xx < w; xx++)
207 {
208 pix = PIX(xx, yy);
209 if (pix != alpha)
210 *p = ARGB_JOIN(0xff, CMAP(pix).Red,
211 CMAP(pix).Green, CMAP(pix).Blue);
212 else *p = 0;
213 p++;
214 }
215 }
216 }
217 else
218 {
219 for (yy = 0; yy < h; yy++)
220 {
221 p = ptr + ((y + yy) * rowpix) + x;
222 for (xx = 0; xx < w; xx++)
223 {
224 pix = PIX(xx, yy);
225 if (pix != alpha)
226 *p = ARGB_JOIN(0xff, CMAP(pix).Red,
227 CMAP(pix).Green, CMAP(pix).Blue);
228 p++;
229 }
230 }
231 }
232 }
233 else
234 {
235 for (yy = 0; yy < h; yy++)
236 {
237 p = ptr + ((y + yy) * rowpix) + x;
238 for (xx = 0; xx < w; xx++)
239 {
240 pix = PIX(xx, yy);
241 *p = ARGB_JOIN(0xff, CMAP(pix).Red,
242 CMAP(pix).Green, CMAP(pix).Blue);
243 p++;
244 }
245 }
246 }
247} 138}
248 139
140// check if image fills "screen space" and if so, if it is transparent
141// at all then the image could be transparent - OR if image doesnt fill,
142// then it could be trasnparent (full coverage of screen). some gifs will
143// be recognized as solid here for faster rendering, but not all.
249static void 144static void
250_copy_pixels(DATA32 *ptr_src, DATA32 *ptr, int rowpix, 145_check_transparency(Eina_Bool *full, Frame_Info *finfo, int w, int h)
251 int x, int y, int w, int h)
252{ 146{
253 DATA32 *p1, *p2, *pe; 147 if ((finfo->x == 0) && (finfo->y == 0) &&
254 int yy; 148 (finfo->w == w) && (finfo->h == h))
255
256 if ((w <= 0) || (h <= 0)) return;
257 for (yy = 0; yy < h; yy++)
258 { 149 {
259 p1 = ptr_src + ((y + yy) * rowpix) + x; 150 if (finfo->transparent >= 0) *full = EINA_FALSE;
260 p2 = ptr + ((y + yy) * rowpix) + x;
261 for (pe = p2 + w; p2 < pe;) *p2++ = *p1++;
262 } 151 }
152 else *full = EINA_FALSE;
263} 153}
264 154
265static void 155// allocate frame and frame info and append to list and store fields
266_fill_pixels(DATA32 val, DATA32 *ptr, int rowpix, 156static Frame_Info *
267 int x, int y, int w, int h) 157_new_frame(Evas_Image_Animated *animated,
158 int transparent, int dispose, int delay,
159 int index)
268{ 160{
269 DATA32 *p2, *pe; 161 Image_Entry_Frame *frame;
270 int yy; 162 Frame_Info *finfo;
271 163
272 if ((w <= 0) || (h <= 0)) return; 164 // allocate frame and frame info data (MUSt be separate)
273 for (yy = 0; yy < h; yy++) 165 frame = calloc(1, sizeof(Image_Entry_Frame));
166 if (!frame) return NULL;
167 finfo = calloc(1, sizeof(Frame_Info));
168 if (!finfo)
274 { 169 {
275 p2 = ptr + ((y + yy) * rowpix) + x; 170 free(frame);
276 for (pe = p2 + w; p2 < pe;) *p2++ = val; 171 return NULL;
277 } 172 }
173 // record transparent index to be used or -1 if none
174 // for this SPECIFIC frame
175 finfo->transparent = transparent;
176 // record dispose mode (3 bits)
177 finfo->dispose = dispose;
178 // record delay (2 bytes so max 65546 /100 sec)
179 finfo->delay = delay;
180 // record the index number we are at
181 frame->index = index;
182 // that frame is stored AT image/screen size
183 frame->info = finfo;
184 animated->frames = eina_list_append(animated->frames, frame);
185 return finfo;
278} 186}
279 187
188// decode a gif image into rows then expand to 32bit into the destination
189// data pointer
280static Eina_Bool 190static Eina_Bool
281_evas_image_load_frame_image_data(Eina_File *f, 191_decode_image(GifFileType *gif, DATA32 *data, int rowpix, int xin, int yin,
282 const Evas_Image_Load_Opts *opts EINA_UNUSED, 192 int transparent, int x, int y, int w, int h, Eina_Bool fill)
283 Evas_Image_Property *prop,
284 Evas_Image_Animated *animated,
285 GifFileType *gif, Image_Entry_Frame *frame, int *error)
286{ 193{
287 ColorMapObject *cmap;
288 GifRowType *rows;
289 DATA32 *ptr, pad = 0xff000000;
290 Gif_Frame *gif_frame = NULL;
291 int intoffset[] = { 0, 4, 2, 1 }; 194 int intoffset[] = { 0, 4, 2, 1 };
292 int intjump[] = { 8, 8, 4, 2 }; 195 int intjump[] = { 8, 8, 4, 2 };
293 int x, y, w, h, i, bg, alpha, cache_w, cache_h, cur_h, cur_w, yy; 196 int i, xx, yy, pix;
294 int disposal = 0, xin = 0, yin = 0; 197 GifRowType *rows;
295 198 Eina_Bool ret = EINA_FALSE;
296 if ((!gif) || (!frame)) return EINA_FALSE; 199 ColorMapObject *cmap;
297 200 DATA32 *p;
298 gif_frame = (Gif_Frame *) frame->info;
299 w = gif->Image.Width;
300 h = gif->Image.Height;
301 x = gif->Image.Left;
302 y = gif->Image.Top;
303 cur_h = h;
304 cur_w = w;
305 cache_w = prop->w;
306 cache_h = prop->h;
307 201
202 // build a blob of memory to have pointers to rows of pixels
203 // AND store the decoded gif pixels (1 byte per pixel) as welll
308 rows = malloc((h * sizeof(GifRowType *)) + (w * h * sizeof(GifPixelType))); 204 rows = malloc((h * sizeof(GifRowType *)) + (w * h * sizeof(GifPixelType)));
309 if (!rows) 205 if (!rows) goto on_error;
310 { 206
311 *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; 207 // fill in the pointers at the start
312 return EINA_FALSE;
313 }
314 for (yy = 0; yy < h; yy++) 208 for (yy = 0; yy < h; yy++)
315 { 209 {
316 rows[yy] = ((unsigned char *)rows) + (h * sizeof(GifRowType *)) + 210 rows[yy] = ((unsigned char *)rows) + (h * sizeof(GifRowType *)) +
317 (yy * w * sizeof(GifPixelType)); 211 (yy * w * sizeof(GifPixelType));
318 } 212 }
319 213
214 // if give is interlaced, walk interlace pattern and decode into rows
320 if (gif->Image.Interlace) 215 if (gif->Image.Interlace)
321 { 216 {
322 for (i = 0; i < 4; i++) 217 for (i = 0; i < 4; i++)
@@ -324,719 +219,612 @@ _evas_image_load_frame_image_data(Eina_File *f,
324 for (yy = intoffset[i]; yy < h; yy += intjump[i]) 219 for (yy = intoffset[i]; yy < h; yy += intjump[i])
325 { 220 {
326 if (DGifGetLine(gif, rows[yy], w) != GIF_OK) 221 if (DGifGetLine(gif, rows[yy], w) != GIF_OK)
327 { 222 goto on_error;
328 *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
329 goto error;
330 }
331 } 223 }
332 } 224 }
333 } 225 }
226 // normal top to bottom - decode into rows
334 else 227 else
335 { 228 {
336 for (yy = 0; yy < h; yy++) 229 for (yy = 0; yy < h; yy++)
337 { 230 {
338 if (DGifGetLine(gif, rows[yy], w) != GIF_OK) 231 if (DGifGetLine(gif, rows[yy], w) != GIF_OK)
339 { 232 goto on_error;
340 *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
341 goto error;
342 }
343 } 233 }
344 } 234 }
345 235
346 alpha = gif_frame->frame_info.transparent; 236 // work out what colormap to use
347 if ((prop->alpha) || (alpha >= 0)) pad = 0x00000000; 237 if (gif->Image.ColorMap) cmap = gif->Image.ColorMap;
348 frame->data = malloc(cache_w * cache_h * sizeof(DATA32)); 238 else cmap = gif->SColorMap;
349 if (!frame->data)
350 {
351 *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
352 goto error;
353 }
354 bg = gif->SBackGroundColor;
355 cmap = (gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap);
356
357 if (!cmap)
358 {
359 DGifCloseFile(gif);
360 if (frame->data) free(frame->data);
361 *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
362 goto error;
363 }
364 239
365 if (cur_h > cache_h) cur_h = cache_h; 240 // if we need to deal with transparent pixels at all...
366 if (cur_w > cache_w) cur_w = cache_w; 241 if (transparent >= 0)
367
368 // clip the image region to be within current image size and note the inset
369 if (x < 0)
370 {
371 w += x; xin = -x; x = 0;
372 }
373 if ((x + w) > cache_w) w = cache_w - x;
374 if (y < 0)
375 { 242 {
376 h += y; yin = -y; y = 0; 243 // if we are told to FILL (overwrite with transparency kept)
377 } 244 if (fill)
378 if ((y + h) > cache_h) h = cache_h - y;
379
380 // data ptr to write to
381 ptr = frame->data;
382 if (frame->index > 1)
383 {
384 /* get previous frame only frame index is bigger than 1 */
385 DATA32 *ptr_src;
386 Image_Entry_Frame *new_frame = NULL;
387 int cur_frame = frame->index;
388 int start_frame = 1;
389
390 if (_find_close_frame(animated, cur_frame, &new_frame))
391 start_frame = new_frame->index + 1;
392
393 if ((start_frame < 1) || (start_frame > cur_frame))
394 {
395 *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
396 goto error;
397 }
398 /* load previous frame of cur_frame */
399 for (i = start_frame; i < cur_frame; i++)
400 { 245 {
401 // FIXME : that one -v 246 for (yy = 0; yy < h; yy++)
402 if (!evas_image_load_specific_frame(f, opts, prop, animated, i, error))
403 { 247 {
404 *error = EVAS_LOAD_ERROR_CORRUPT_FILE; 248 p = data + ((y + yy) * rowpix) + x;
405 goto error; 249 for (xx = 0; xx < w; xx++)
250 {
251 pix = PIX(xx, yy);
252 if (pix != transparent) *p = PIXLK(pix);
253 else *p = 0;
254 p++;
255 }
406 } 256 }
407 } 257 }
408 if (!_find_frame(animated, cur_frame - 1, &new_frame)) 258 // paste on top with transparent pixels untouched
409 {
410 *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
411 goto error;
412 }
413 else 259 else
414 { 260 {
415 Gif_Frame *gif_frame2 = NULL; 261 for (yy = 0; yy < h; yy++)
416
417 ptr_src = new_frame->data;
418 if (new_frame->info)
419 { 262 {
420 gif_frame2 = (Gif_Frame *)(new_frame->info); 263 p = data + ((y + yy) * rowpix) + x;
421 disposal = gif_frame2->frame_info.disposal; 264 for (xx = 0; xx < w; xx++)
422 gif_frame->bg_val = gif_frame2->bg_val;
423 switch (disposal)
424 { 265 {
425 case 0: // no nothing 266 pix = PIX(xx, yy);
426 case 2: // restore bg ... browsers don't respect bg, so neither shall we. 267 if (pix != transparent) *p = PIXLK(pix);
427 // fill in the area OUTSIDE the image with 0/black 268 p++;
428 _fill_pixels(pad, ptr, cache_w, 0, 0, cache_w, y);
429 _fill_pixels(pad, ptr, cache_w, 0, y + h, cache_w, cache_h - (y + h));
430 _fill_pixels(pad, ptr, cache_w, 0, y, x, h);
431 _fill_pixels(pad, ptr, cache_w, x + w, y, cache_w - (x + w), h);
432 _expand_gif_rows(rows, cmap, ptr, cache_w, xin, yin,
433 w, h, x, y, alpha, EINA_TRUE);
434 break;
435 case 1: // leave as-is
436 case 3: // previous image
437 _copy_pixels(ptr_src, ptr, cache_w, 0, 0,
438 cache_w, cache_h);
439 _expand_gif_rows(rows, cmap, ptr, cache_w, xin, yin,
440 w, h, x, y, alpha, EINA_FALSE);
441 break;
442 default:
443 break;
444 } 269 }
445 } 270 }
446 } 271 }
447 } 272 }
448 else /* first frame decoding */ 273 else
449 { 274 {
450 /* get the background value */ 275 // walk pixels without worring about transparency at all
451 gif_frame->bg_val = RGB_JOIN(cmap->Colors[bg].Red, 276 for (yy = 0; yy < h; yy++)
452 cmap->Colors[bg].Green, 277 {
453 cmap->Colors[bg].Blue); 278 p = data + ((y + yy) * rowpix) + x;
454 // fill in the area OUTSIDE the image with 0/black 279 for (xx = 0; xx < w; xx++)
455 _fill_pixels(pad, ptr, cache_w, 0, 0, cache_w, y); 280 {
456 _fill_pixels(pad, ptr, cache_w, 0, y + h, cache_w, cache_h - (y + h)); 281 pix = PIX(xx, yy);
457 _fill_pixels(pad, ptr, cache_w, 0, y, x, h); 282 *p = PIXLK(pix);
458 _fill_pixels(pad, ptr, cache_w, x + w, y, cache_w - (x + w), h); 283 p++;
459 _expand_gif_rows(rows, cmap, ptr, cache_w, xin, yin, 284 }
460 w, h, x, y, alpha, EINA_TRUE); 285 }
461 } 286 }
287 ret = EINA_TRUE;
462 288
463 if (rows) free(rows); 289on_error:
464 frame->loaded = EINA_TRUE; 290 free(rows);
465 return EINA_TRUE; 291 return ret;
466error:
467 if (rows) free(rows);
468 return EINA_FALSE;
469} 292}
470 293
471static Eina_Bool 294// flush out older rgba frame images to save memory but skip current frame
472_evas_image_load_frame(Eina_File *f, const Evas_Image_Load_Opts *opts, 295// and previous frame (prev needed for dispose mode 3)
473 Evas_Image_Property *prop, Evas_Image_Animated *animated, 296static void
474 GifFileType *gif, Image_Entry_Frame *frame, Frame_Load_Type type, int *error) 297_flush_older_frames(Evas_Image_Animated *animated,
298 int w, int h,
299 Image_Entry_Frame *thisframe,
300 Image_Entry_Frame *prevframe)
475{ 301{
476 GifRecordType rec; 302 Eina_List *l;
477 int gra_res = 0, img_res = 0; 303 Image_Entry_Frame *frame;
478 Eina_Bool res = EINA_FALSE; 304 // target is the amount of memory we want to be under for stored frames
479 305 int total = 0, target = 512 * 1024;
480 if ((!gif) || (!frame)) return EINA_FALSE;
481 if (type > LOAD_FRAME_DATA_INFO) return EINA_FALSE;
482 306
483 do 307 // total up the amount of memory used by stored frames for this image
308 EINA_LIST_FOREACH(animated->frames, l, frame)
484 { 309 {
485 if (DGifGetRecordType(gif, &rec) == GIF_ERROR) return EINA_FALSE; 310 if (frame->data) total++;
486 if (rec == IMAGE_DESC_RECORD_TYPE) 311 }
487 { 312 total *= (w * h * sizeof(DATA32));
488 img_res++; 313 // if we use less than target (512k) for frames - dont flush
489 break; 314 if (total < target) return;
490 } 315 // clean oldest frames first and go until below target or until we loop
491 else if (rec == EXTENSION_RECORD_TYPE) 316 // around back to this frame (curent)
317 EINA_LIST_FOREACH(animated->frames, l, frame)
318 {
319 if (frame == thisframe) break;
320 }
321 if (!l) return;
322 // start on next frame after thisframe
323 l = l->next;
324 // handle wrap to start
325 if (!l) l = animated->frames;
326 // now walk until we hit thisframe again... then stop walk.
327 while (l)
328 {
329 frame = l->data;
330 if (frame == thisframe) break;
331 if (frame->data)
492 { 332 {
493 int ext_code; 333 if ((frame != thisframe) && (frame != prevframe))
494 GifByteType *ext;
495
496 ext = NULL;
497 DGifGetExtension(gif, &ext_code, &ext);
498 while (ext)
499 { 334 {
500 if (ext_code == 0xf9) /* Graphic Control Extension */ 335 free(frame->data);
501 { 336 frame->data = NULL;
502 gra_res++; 337 // subtract memory used and if below target - stop flush
503 /* fill frame info */ 338 total -= (w * h * sizeof(DATA32));
504 if ((type == LOAD_FRAME_INFO) || (type == LOAD_FRAME_DATA_INFO)) 339 if (total < target) break;
505 _evas_image_load_frame_graphic_info(frame,ext);
506 }
507 ext = NULL;
508 DGifGetExtensionNext(gif, &ext);
509 } 340 }
510 } 341 }
511 } while ((rec != TERMINATE_RECORD_TYPE) && (img_res == 0)); 342 // go to next - handle wrap to start
512 if (img_res != 1) return EINA_FALSE; 343 l = l->next;
513 if (DGifGetImageDesc(gif) == GIF_ERROR) return EINA_FALSE; 344 if (!l) l = animated->frames;
514 if ((type == LOAD_FRAME_INFO) || (type == LOAD_FRAME_DATA_INFO))
515 _evas_image_load_frame_image_des_info(gif, frame);
516
517 if ((type == LOAD_FRAME_DATA) || (type == LOAD_FRAME_DATA_INFO))
518 {
519 res = _evas_image_load_frame_image_data(f, opts, prop, animated,
520 gif, frame, error);
521 if (!res) return EINA_FALSE;
522 } 345 }
523 return EINA_TRUE;
524}
525
526
527/* set frame data to cache entry's data */
528static Eina_Bool
529evas_image_load_file_data_gif_internal(Evas_Image_Property *prop,
530 Image_Entry_Frame *frame,
531 void *pixels,
532 int *error)
533{
534 /* only copy real frame part */
535 memcpy(pixels, frame->data, prop->w * prop->h * sizeof (DATA32));
536 prop->premul = EINA_TRUE;
537
538 *error = EVAS_LOAD_ERROR_NONE;
539 return EINA_TRUE;
540} 346}
347
541 348
542typedef struct _Evas_GIF_Info Evas_GIF_Info; 349// file access info/funcs
543struct _Evas_GIF_Info 350typedef struct _File_Info File_Info;
351struct _File_Info
544{ 352{
545 unsigned char *map; 353 unsigned char *map;
546 int length; 354 int pos, len; // yes - gif uses ints for file sizes.
547 int position;
548}; 355};
549 356
550static int 357static int
551_evas_image_load_file_read(GifFileType* gft, GifByteType *buf,int length) 358_file_read(GifFileType *gft, GifByteType *buf, int len)
552{ 359{
553 Evas_GIF_Info *egi = gft->UserData; 360 File_Info *fi = gft->UserData;
554
555 if (egi->position == egi->length) return 0;
556 if (egi->position + length == egi->length) length = egi->length - egi->position;
557 memcpy(buf, egi->map + egi->position, length);
558 egi->position += length;
559 361
560 return length; 362 if (fi->pos >= fi->len) return 0; // if at or past end - no
561} 363 if ((fi->pos + len) >= fi->len) len = fi->len - fi->pos;
562static void * 364 memcpy(buf, fi->map + fi->pos, len);
563evas_image_load_file_open_gif(Eina_File *f, Eina_Stringshare *key EINA_UNUSED, 365 fi->pos += len;
564 Evas_Image_Load_Opts *opts, 366 return len;
565 Evas_Image_Animated *animated,
566 int *error)
567{
568 Evas_Loader_Internal *loader;
569
570 loader = calloc(1, sizeof (Evas_Loader_Internal));
571 if (!loader)
572 {
573 *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
574 return NULL;
575 }
576
577 loader->f = f;
578 loader->opts = opts;
579 loader->animated = animated;
580
581 return loader;
582}
583
584static void
585evas_image_load_file_close_gif(void *loader_data)
586{
587 free(loader_data);
588} 367}
589 368
590static Eina_Bool 369static Eina_Bool
591evas_image_load_file_head_gif(void *loader_data, 370evas_image_load_file_head_gif2(void *loader_data,
592 Evas_Image_Property *prop, 371 Evas_Image_Property *prop,
593 int *error) 372 int *error)
594{ 373{
595 Evas_Loader_Internal *loader = loader_data; 374 Loader_Info *loader = loader_data;
596// Evas_Image_Load_Opts *load_opts; 375 Evas_Image_Animated *animated = loader->animated;
597 Evas_Image_Animated *animated; 376 Eina_File *f = loader->f;
598 Eina_File *f; 377 Eina_Bool ret = EINA_FALSE;
599 378 File_Info fi;
600 Evas_GIF_Info egi; 379 GifRecordType rec;
601 GifRecordType rec; 380 GifFileType *gif = NULL;
602 GifFileType *gif = NULL; 381 // it is possible which gif file have error midle of frames,
603 int loop_count = -1; 382 // in that case we should play gif file until meet error frame.
604 int a; 383 int imgnum = 0;
605 Eina_Bool r = EINA_FALSE; 384 int loop_count = -1;
606 //it is possible which gif file have error midle of frames, 385 Frame_Info *finfo = NULL;
607 //in that case we should play gif file until meet error frame. 386 Eina_Bool full = EINA_TRUE;
608 int image_count = 0; 387
609 388 // init prop struct with some default null values
610 f = loader->f;
611// load_opts = loader->opts;
612 animated = loader->animated;
613
614 prop->w = 0; 389 prop->w = 0;
615 prop->h = 0; 390 prop->h = 0;
616 a = 0;
617 391
618 egi.map = eina_file_map_all(f, EINA_FILE_RANDOM); 392 // map the file and store/track info
619 if (!egi.map) 393 fi.map = eina_file_map_all(f, EINA_FILE_RANDOM);
620 { 394 if (!fi.map) LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE);
621 *error = EVAS_LOAD_ERROR_CORRUPT_FILE; 395 fi.len = eina_file_size_get(f);
622 goto on_error; 396 fi.pos = 0;
623 }
624 egi.length = eina_file_size_get(f);
625 egi.position = 0;
626 397
398 // actually ask libgif to open the file
627#if GIFLIB_MAJOR >= 5 399#if GIFLIB_MAJOR >= 5
628 gif = DGifOpen(&egi, _evas_image_load_file_read, NULL); 400 gif = DGifOpen(&fi, _file_read, NULL);
629#else 401#else
630 gif = DGifOpen(&egi, _evas_image_load_file_read); 402 gif = DGifOpen(&fi, _file_read);
631#endif 403#endif
632 if (!gif) 404 if (!gif) LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
633 { 405 // get the gif "screen size" (the actual image size)
634 *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
635 goto on_error;
636 }
637
638 /* check logical screen size */
639 prop->w = gif->SWidth; 406 prop->w = gif->SWidth;
640 prop->h = gif->SHeight; 407 prop->h = gif->SHeight;
641 /* support scale down feture in gif*/ 408 // if size is invalid - abort here
642// disable scale down for gif until gif is handled right
643// if (load_opts->scale_down_by > 1)
644// {
645// prop->w /= load_opts->scale_down_by;
646// prop->h /= load_opts->scale_down_by;
647// }
648
649 if ((prop->w < 1) || (prop->h < 1) || 409 if ((prop->w < 1) || (prop->h < 1) ||
650 (prop->w > IMG_MAX_SIZE) || (prop->h > IMG_MAX_SIZE) || 410 (prop->w > IMG_MAX_SIZE) || (prop->h > IMG_MAX_SIZE) ||
651 IMG_TOO_BIG(prop->w, prop->h)) 411 IMG_TOO_BIG(prop->w, prop->h))
652 { 412 {
653 if (IMG_TOO_BIG(prop->w, prop->h)) 413 if (IMG_TOO_BIG(prop->w, prop->h))
654 *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; 414 LOADERR(EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED);
655 else 415 LOADERR(EVAS_LOAD_ERROR_GENERIC);
656 *error = EVAS_LOAD_ERROR_GENERIC;
657 goto on_error; 416 goto on_error;
658 } 417 }
659 418 // walk through gif records in file to figure out info
660 do 419 do
661 { 420 {
662 if (DGifGetRecordType(gif, &rec) == GIF_ERROR) 421 if (DGifGetRecordType(gif, &rec) == GIF_ERROR)
663 { 422 {
664 if (image_count > 1) break; //we should show normal frames. 423 // if we have a gif that ends part way through a sequence
665 /* PrintGifError(); */ 424 // (or animation) consider it valid and just break - no error
666 *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; 425 if (imgnum > 1) break;
667 goto on_error; 426 LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
668 } 427 }
669 428 // get image description section
670 /* image descript info */
671 if (rec == IMAGE_DESC_RECORD_TYPE) 429 if (rec == IMAGE_DESC_RECORD_TYPE)
672 { 430 {
673 int img_code; 431 int img_code;
674 GifByteType *img; 432 GifByteType *img;
675 433
434 // get image desc
676 if (DGifGetImageDesc(gif) == GIF_ERROR) 435 if (DGifGetImageDesc(gif) == GIF_ERROR)
677 { 436 LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
678 /* PrintGifError(); */ 437 // skip decoding and just walk image to next
679 *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
680 goto on_error;
681 }
682 /* we have to count frame, so use DGifGetCode and skip decoding */
683 if (DGifGetCode(gif, &img_code, &img) == GIF_ERROR) 438 if (DGifGetCode(gif, &img_code, &img) == GIF_ERROR)
684 { 439 LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
685 /* PrintGifError(); */ 440 // skip till next...
686 *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
687 goto on_error;
688 }
689 while (img) 441 while (img)
690 { 442 {
691 img = NULL; 443 img = NULL;
692 DGifGetCodeNext(gif, &img); 444 DGifGetCodeNext(gif, &img);
693 } 445 }
694 image_count++; 446 // store geometry in the last frame info data
447 if (finfo)
448 {
449 _store_frame_info(gif, finfo);
450 _check_transparency(&full, finfo, prop->w, prop->h);
451 }
452 // or if we dont have a finfo entry - create one even for stills
453 else
454 {
455 // allocate and save frame with field data
456 finfo = _new_frame(animated, -1, 0, 0, imgnum + 1);
457 if (!finfo)
458 LOADERR(EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED);
459 // store geometry info from gif image
460 _store_frame_info(gif, finfo);
461 // check for transparency/alpha
462 _check_transparency(&full, finfo, prop->w, prop->h);
463 }
464 imgnum++;
695 } 465 }
466 // we have an extension code block - for animated gifs for sure
696 else if (rec == EXTENSION_RECORD_TYPE) 467 else if (rec == EXTENSION_RECORD_TYPE)
697 { 468 {
698 int ext_code; 469 int ext_code;
699 GifByteType *ext; 470 GifByteType *ext;
700 471
701 ext = NULL; 472 ext = NULL;
473 // get the first extension entry
702 DGifGetExtension(gif, &ext_code, &ext); 474 DGifGetExtension(gif, &ext_code, &ext);
703 while (ext) 475 while (ext)
704 { 476 {
705 if (ext_code == 0xf9) /* Graphic Control Extension */ 477 // graphic control extension - for animated gif data
478 // and transparent index + flag
479 if (ext_code == 0xf9)
706 { 480 {
707 if ((ext[1] & 1) && (prop->alpha == 0)) 481 // create frame and store it in image
708 prop->alpha = (int)ext[4]; 482 finfo = _new_frame
483 (animated,
484 (ext[1] & 1) ? ext[4] : -1, // transparency index
485 (ext[1] >> 2) & 0x7, // dispose mode
486 ((int)ext[3] << 8) | (int)ext[2], // delay
487 imgnum + 1);
488 if (!finfo)
489 LOADERR(EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED);
709 } 490 }
491 // netscape extension indicating loop count...
710 else if (ext_code == 0xff) /* application extension */ 492 else if (ext_code == 0xff) /* application extension */
711 { 493 {
712 if (!strncmp ((char*)(&ext[1]), "NETSCAPE2.0", 11) || 494 if (!strncmp((char *)(&ext[1]), "NETSCAPE2.0", 11) ||
713 !strncmp ((char*)(&ext[1]), "ANIMEXTS1.0", 11)) 495 !strncmp((char *)(&ext[1]), "ANIMEXTS1.0", 11))
714 { 496 {
715 ext=NULL; 497 ext = NULL;
716 DGifGetExtensionNext(gif, &ext); 498 DGifGetExtensionNext(gif, &ext);
717
718 if (ext[1] == 0x01) 499 if (ext[1] == 0x01)
719 { 500 {
720 loop_count = ext[2] + (ext[3] << 8); 501 loop_count = ((int)ext[3] << 8) | (int)ext[2];
721 if (loop_count > 0) loop_count++; 502 if (loop_count > 0) loop_count++;
722 } 503 }
723 } 504 }
724 } 505 }
725 506 // and continue onto the next extension entry
726 ext = NULL; 507 ext = NULL;
727 DGifGetExtensionNext(gif, &ext); 508 DGifGetExtensionNext(gif, &ext);
728 } 509 }
729 } 510 }
730 } while (rec != TERMINATE_RECORD_TYPE); 511 }
731 512 while (rec != TERMINATE_RECORD_TYPE);
732 if (a >= 0) prop->alpha = 1;
733 513
734 if ((gif->ImageCount > 1) || (image_count > 1)) 514 // if the gif main says we have more than one image or our image counting
515 // says so, then this image is animated - indicate this
516 if ((gif->ImageCount > 1) || (imgnum > 1))
735 { 517 {
736 animated->animated = 1; 518 animated->animated = 1;
737 animated->loop_count = loop_count; 519 animated->loop_count = loop_count;
738 animated->loop_hint = EVAS_IMAGE_ANIMATED_HINT_LOOP; 520 animated->loop_hint = EVAS_IMAGE_ANIMATED_HINT_LOOP;
739 animated->frame_count = MIN(gif->ImageCount, image_count); 521 animated->frame_count = MIN(gif->ImageCount, imgnum);
740 animated->frames = NULL;
741 } 522 }
523 if (!full) prop->alpha = 1;
524 animated->cur_frame = 1;
742 525
526 // no errors in header scan etc. so set err and return value
743 *error = EVAS_LOAD_ERROR_NONE; 527 *error = EVAS_LOAD_ERROR_NONE;
744 r = EINA_TRUE; 528 ret = EINA_TRUE;
745 529
746 on_error: 530on_error: // jump here on any errors to clean up
747 if (gif) DGifCloseFile(gif); 531 if (gif) DGifCloseFile(gif);
748 if (egi.map) eina_file_map_free(f, egi.map); 532 if (fi.map) eina_file_map_free(f, fi.map);
749 return r; 533 return ret;
750} 534}
751 535
752static Eina_Bool 536static Eina_Bool
753evas_image_load_specific_frame(Eina_File *f, 537evas_image_load_file_data_gif2(void *loader_data,
754 const Evas_Image_Load_Opts *opts,
755 Evas_Image_Property *prop, 538 Evas_Image_Property *prop,
756 Evas_Image_Animated *animated, int frame_index, 539 void *pixels,
757 int *error) 540 int *error)
758{ 541{
759 GifFileType *gif = NULL; 542 Loader_Info *loader = loader_data;
760 Image_Entry_Frame *frame = NULL; 543 Evas_Image_Animated *animated = loader->animated;
761 Gif_Frame *gif_frame = NULL; 544 Eina_File *f = loader->f;
762 Evas_GIF_Info egi; 545 Eina_Bool ret = EINA_FALSE;
763 Eina_Bool r = EINA_FALSE; 546 File_Info fi;
764 547 GifRecordType rec;
765 egi.map = eina_file_map_all(f, EINA_FILE_RANDOM); 548 GifFileType *gif = NULL;
766 if (!egi.map) 549 Image_Entry_Frame *frame;
767 { 550 int index = 0, imgnum = 0;
768 *error = EVAS_LOAD_ERROR_CORRUPT_FILE; 551 Frame_Info *finfo;
769 goto on_error; 552
770 } 553 // XXX: this is so wrong - storing current frame IN the image
771 egi.length = eina_file_size_get(f); 554 // so we have to load multiple times to animate. what if the
772 egi.position = 0; 555 // same image is shared/loaded in 2 ore more places AND animated
773 556 // there?
774#if GIFLIB_MAJOR >= 5 557
775 gif = DGifOpen(&egi, _evas_image_load_file_read, NULL); 558 // use index stored in image (XXX: yuk!)
776#else 559 index = animated->cur_frame;
777 gif = DGifOpen(&egi, _evas_image_load_file_read); 560 // if index is invalid for animated image - error out
778#endif
779 if (!gif)
780 {
781 *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
782 goto on_error;
783 }
784 if (!_evas_image_skip_frame(gif, frame_index-1))
785 {
786 *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
787 goto on_error;
788 }
789
790 frame = malloc(sizeof (Image_Entry_Frame));
791 if (!frame)
792 {
793 *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
794 goto on_error;
795 }
796
797 gif_frame = calloc(1, sizeof (Gif_Frame));
798 if (!gif_frame)
799 {
800 *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
801 free(frame);
802 goto on_error;
803 }
804 frame->info = gif_frame;
805 frame->index = frame_index;
806 if (!_evas_image_load_frame(f, opts, prop, animated, gif, frame, LOAD_FRAME_DATA_INFO, error))
807 {
808 *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
809 free(gif_frame);
810 free(frame);
811 goto on_error;
812 }
813
814 animated->frames = eina_list_append(animated->frames, frame);
815 r = EINA_TRUE;
816
817 on_error:
818 if (gif) DGifCloseFile(gif);
819 if (egi.map) eina_file_map_free(f, egi.map);
820 return r;
821}
822
823static Eina_Bool
824evas_image_load_file_data_gif(void *loader_data,
825 Evas_Image_Property *prop,
826 void *pixels, int *error)
827{
828 Evas_Loader_Internal *loader = loader_data;
829 Evas_Image_Load_Opts *opts;
830 Evas_Image_Animated *animated;
831 Eina_File *f;
832
833 Image_Entry_Frame *frame = NULL;
834 int cur_frame_index;
835 Eina_Bool hit;
836
837 opts = loader->opts;
838 animated = loader->animated;
839 f = loader->f;
840
841 if(!animated->animated)
842 cur_frame_index = 1;
843 else
844 cur_frame_index = animated->cur_frame;
845
846 if ((animated->animated) && 561 if ((animated->animated) &&
847 ((cur_frame_index < 0) || (cur_frame_index > FRAME_MAX) || (cur_frame_index > animated->frame_count))) 562 ((index <= 0) || (index > animated->frame_count)))
563 LOADERR(EVAS_LOAD_ERROR_GENERIC);
564 // find the given frame index
565 frame = _find_frame(animated, index);
566 if (frame)
848 { 567 {
849 *error = EVAS_LOAD_ERROR_GENERIC; 568 if ((frame->loaded) && (frame->data))
850 return EINA_FALSE;
851 }
852
853 /* first time frame is set to be 0. so default is 1 */
854 if (cur_frame_index == 0) cur_frame_index++;
855
856 /* Check current frame exists in hash table */
857 hit = _find_frame(animated, cur_frame_index, &frame);
858
859 /* if current frame exist in has table, check load flag */
860 if (hit)
861 {
862 if (frame->loaded)
863 {
864 evas_image_load_file_data_gif_internal(prop, frame, pixels, error);
865 }
866 else
867 { 569 {
868 Evas_GIF_Info egi; 570 // frame is already there and decoded - jump to end
869 GifFileType *gif = NULL; 571 goto on_ok;
870 Eina_Bool r = EINA_FALSE;
871
872 egi.map = eina_file_map_all(f, EINA_FILE_RANDOM);
873 if (!egi.map)
874 {
875 *error = EVAS_LOAD_ERROR_CORRUPT_FILE;
876 goto on_error;
877 }
878 egi.length = eina_file_size_get(f);
879 egi.position = 0;
880
881#if GIFLIB_MAJOR >= 5
882 gif = DGifOpen(&egi, _evas_image_load_file_read, NULL);
883#else
884 gif = DGifOpen(&egi, _evas_image_load_file_read);
885#endif
886 if (!gif)
887 {
888 *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
889 goto on_error;
890 }
891 _evas_image_skip_frame(gif, cur_frame_index - 1);
892 if (!_evas_image_load_frame(f, opts, prop, animated, gif, frame, LOAD_FRAME_DATA, error))
893 {
894 *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
895 goto on_error;
896 }
897 if (!evas_image_load_file_data_gif_internal(prop, frame, pixels, error))
898 {
899 *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
900 goto on_error;
901 }
902 *error = EVAS_LOAD_ERROR_NONE;
903 r = EINA_TRUE;
904
905 on_error:
906 if (gif) DGifCloseFile(gif);
907 if (egi.map) eina_file_map_free(f, egi.map);
908 return r;
909 } 572 }
910 } 573 }
911 /* current frame does is not exist */
912 else 574 else
913 { 575 LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE);
914 if (!evas_image_load_specific_frame(f, opts, prop, animated, cur_frame_index, error))
915 return EINA_FALSE;
916 hit = EINA_FALSE;
917 frame = NULL;
918 hit = _find_frame(animated, cur_frame_index, &frame);
919 if (!hit) return EINA_FALSE;
920 if (!evas_image_load_file_data_gif_internal(prop, frame, pixels, error))
921 {
922 *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
923 return EINA_FALSE;
924 }
925 return EINA_TRUE;
926 }
927 return EINA_FALSE;
928}
929
930static double
931evas_image_load_frame_duration_gif(void *loader_data,
932 int start_frame, int frame_num)
933{
934 Evas_Loader_Internal *loader = loader_data;
935 Evas_Image_Animated *animated;
936 Eina_File *f;
937
938 Evas_GIF_Info egi;
939 GifFileType *gif = NULL;
940 GifRecordType rec;
941 int current_frame = 1;
942 int remain_frames = frame_num;
943 double duration = -1;
944 int frame_count = 0;
945
946 animated = loader->animated;
947 f = loader->f;
948 frame_count = animated->frame_count;
949 576
950 if (!animated->animated) return -1; 577 // map the file and store/track info
951 if ((start_frame + frame_num) > frame_count) return -1; 578 fi.map = eina_file_map_all(f, EINA_FILE_RANDOM);
952 if (frame_num < 0) return -1; 579 if (!fi.map) LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE);
580 fi.len = eina_file_size_get(f);
581 fi.pos = 0;
953 582
954 egi.map = eina_file_map_all(f, EINA_FILE_RANDOM); 583 // actually ask libgif to open the file
955 if (!egi.map) goto on_error;
956 egi.length = eina_file_size_get(f);
957 egi.position = 0;
958#if GIFLIB_MAJOR >= 5 584#if GIFLIB_MAJOR >= 5
959 gif = DGifOpen(&egi, _evas_image_load_file_read, NULL); 585 gif = DGifOpen(&fi, _file_read, NULL);
960#else 586#else
961 gif = DGifOpen(&egi, _evas_image_load_file_read); 587 gif = DGifOpen(&fi, _file_read);
962#endif 588#endif
963 if (!gif) goto on_error; 589 if (!gif) LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
964 590
965 duration = 0; 591 imgnum = 1;
592 // walk through gif records in file to figure out info
966 do 593 do
967 { 594 {
968 if (DGifGetRecordType(gif, &rec) == GIF_ERROR) 595 if (DGifGetRecordType(gif, &rec) == GIF_ERROR)
596 LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
597 if (rec == EXTENSION_RECORD_TYPE)
969 { 598 {
970 rec = TERMINATE_RECORD_TYPE; 599 int ext_code;
600 GifByteType *ext;
601
602 ext = NULL;
603 DGifGetExtension(gif, &ext_code, &ext);
604 while (ext)
605 {
606 ext = NULL;
607 DGifGetExtensionNext(gif, &ext);
608 }
971 } 609 }
972 if (rec == IMAGE_DESC_RECORD_TYPE) 610 // get image description section
611 else if (rec == IMAGE_DESC_RECORD_TYPE)
973 { 612 {
974 int img_code; 613 int xin = 0, yin = 0, x = 0, y = 0, w = 0, h = 0;
975 GifByteType *img; 614 int img_code;
615 GifByteType *img;
616 Image_Entry_Frame *prevframe = NULL;
617 Image_Entry_Frame *thisframe = NULL;
976 618
619 // get image desc
977 if (DGifGetImageDesc(gif) == GIF_ERROR) 620 if (DGifGetImageDesc(gif) == GIF_ERROR)
621 LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
622 // get the previous frame entry AND the current one to fill in
623 prevframe = _find_frame(animated, imgnum - 1);
624 thisframe = _find_frame(animated, imgnum);
625 // if we have a frame AND we're animated AND we have no data...
626 if ((thisframe) && (!thisframe->data) && (animated->animated))
978 { 627 {
979 /* PrintGifError(); */ 628 Eina_Bool first = EINA_FALSE;
980 rec = TERMINATE_RECORD_TYPE; 629
981 } 630 // allocate it
982 current_frame++; 631 thisframe->data =
983 /* we have to count frame, so use DGifGetCode and skip decoding */ 632 malloc(prop->w * prop->h * sizeof(DATA32));
984 if (DGifGetCode(gif, &img_code, &img) == GIF_ERROR) 633 if (!thisframe->data)
985 { 634 LOADERR(EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED);
986 rec = TERMINATE_RECORD_TYPE; 635 // if we have no prior frame OR prior frame data... empty
636 if ((!prevframe) || (!prevframe->data))
637 {
638 first = EINA_TRUE;
639 finfo = thisframe->info;
640 memset(thisframe->data, 0,
641 prop->w * prop->h * sizeof(DATA32));
642 }
643 // we have a prior frame to copy data from...
644 else
645 {
646 finfo = prevframe->info;
647
648 // fix coords of sub image in case it goes out...
649 _clip_coords(prop->w, prop->h, &xin, &yin,
650 finfo->x, finfo->y, finfo->w, finfo->h,
651 &x, &y, &w, &h);
652 // if dispose mode is not restore - then copy pre frame
653 if (finfo->dispose != 3) // GIF_DISPOSE_RESTORE
654 memcpy(thisframe->data, prevframe->data,
655 prop->w * prop->h * sizeof(DATA32));
656 // if dispose mode is "background" then fill with bg
657 if (finfo->dispose == 2) // GIF_DISPOSE_BACKGND
658 _fill_frame(thisframe->data, prop->w, gif,
659 finfo, x, y, w, h);
660 else if (finfo->dispose == 3) // GIF_DISPOSE_RESTORE
661 {
662 Image_Entry_Frame *prevframe2;
663
664 // we need to copy data from one frame back
665 // from the prev frame into the current frame
666 // (copy the whole image - at least the sample
667 // GifWin.cpp from libgif indicates this is what
668 // needs doing
669 prevframe2 = _find_frame(animated, imgnum - 2);
670 if (prevframe2)
671 memcpy(thisframe->data, prevframe2->data,
672 prop->w * prop->h * sizeof(DATA32));
673 }
674 }
675 // now draw this frame on top
676 finfo = thisframe->info;
677 _clip_coords(prop->w, prop->h, &xin, &yin,
678 finfo->x, finfo->y, finfo->w, finfo->h,
679 &x, &y, &w, &h);
680 if (!_decode_image(gif, thisframe->data, prop->w,
681 xin, yin, finfo->transparent,
682 x, y, w, h, first))
683 LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE);
684 // mark as loaded and done
685 thisframe->loaded = EINA_TRUE;
686 // and flush old memory if needed (too much)
687 _flush_older_frames(animated, prop->w, prop->h,
688 thisframe, prevframe);
987 } 689 }
988 while (img) 690 // if we hve a frame BUT the image is not animated... different
691 // path
692 else if ((thisframe) && (!thisframe->data) &&
693 (!animated->animated))
989 { 694 {
990 img = NULL; 695 // if we don't have the data decoded yet - decode it
991 DGifGetExtensionNext(gif, &img); 696 if (!thisframe->loaded)
697 {
698 // use frame info but we WONT allocate frame pixels
699 finfo = thisframe->info;
700 _clip_coords(prop->w, prop->h, &xin, &yin,
701 finfo->x, finfo->y, finfo->w, finfo->h,
702 &x, &y, &w, &h);
703 // clear out all pixels
704 _fill_frame(pixels, prop->w, gif,
705 finfo, 0, 0, prop->w, prop->h);
706 // and decode the gif with overwriting
707 if (!_decode_image(gif, pixels, prop->w,
708 xin, yin, finfo->transparent,
709 x, y, w, h, EINA_TRUE))
710 LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE);
711 // mark as loaded and done
712 thisframe->loaded = EINA_TRUE;
713 }
714 // flush mem we don't need (at expense of decode cpu)
992 } 715 }
993 } 716 else
994 else if (rec == EXTENSION_RECORD_TYPE)
995 {
996 int ext_code;
997 GifByteType *ext;
998
999 ext = NULL;
1000 DGifGetExtension(gif, &ext_code, &ext);
1001 while (ext)
1002 { 717 {
1003 if (ext_code == 0xf9) /* Graphic Control Extension */ 718 // skip decoding and just walk image to next
719 if (DGifGetCode(gif, &img_code, &img) == GIF_ERROR)
720 LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT);
721 while (img)
1004 { 722 {
1005 if ((current_frame >= start_frame) && (current_frame <= frame_count)) 723 img = NULL;
1006 { 724 DGifGetCodeNext(gif, &img);
1007 int frame_duration = 0;
1008 if (remain_frames < 0) break;
1009 frame_duration = byte2_to_int (ext[2], ext[3]);
1010 if (frame_duration == 0)
1011 duration += 0.1;
1012 else
1013 duration += (double)frame_duration/100;
1014 remain_frames --;
1015 }
1016 } 725 }
1017 ext = NULL;
1018 DGifGetExtensionNext(gif, &ext);
1019 } 726 }
1020 } 727 // if we found the image we wanted - get out of here
1021 } while (rec != TERMINATE_RECORD_TYPE); 728 if (imgnum == index) break;
1022 729 imgnum++;
1023 on_error: 730 }
731 }
732 while (rec != TERMINATE_RECORD_TYPE);
733
734on_ok:
735 // no errors in header scan etc. so set err and return value
736 *error = EVAS_LOAD_ERROR_NONE;
737 ret = EINA_TRUE;
738
739 // if it was an animated image we need to copy the data to the
740 // pixels for the image from the frame holding the data
741 if (animated->animated)
742 memcpy(pixels, frame->data, prop->w * prop->h * sizeof (DATA32));
743 prop->premul = EINA_TRUE;
744
745on_error: // jump here on any errors to clean up
1024 if (gif) DGifCloseFile(gif); 746 if (gif) DGifCloseFile(gif);
1025 if (egi.map) eina_file_map_free(f, egi.map); 747 if (fi.map) eina_file_map_free(f, fi.map);
1026 return duration; 748 return ret;
749}
750
751// get the time between 2 frames in the timeline
752static double
753evas_image_load_frame_duration_gif2(void *loader_data,
754 int start_frame,
755 int frame_num)
756{
757 Loader_Info *loader = loader_data;
758 Evas_Image_Animated *animated = loader->animated;
759 Image_Entry_Frame *frame;
760 int i, total = 0;
761
762 // if its not animated or requested frame data is invalid
763 if (!animated->animated) return -1.0;
764 if ((start_frame + frame_num) > animated->frame_count) return -1.0;
765 if (frame_num < 0) return -1.0;
766
767 if (frame_num < 1) frame_num = 1;
768 // walk frames from start frame though and total up delays
769 for (i = start_frame; i < (start_frame + frame_num); i++)
770 {
771 Frame_Info *finfo;
772
773 // find the frame
774 frame = _find_frame(animated, i);
775 // no frame? barf - bad file or i/o?
776 if (!frame) return -1.0;
777 // get delay and total it up
778 finfo = frame->info;
779 // if delay is sensible - use it else assume 10/100ths of a sec
780 if (finfo->delay > 0) total += finfo->delay;
781 else total += 10;
782 }
783 // return delay in seconds (since timing in gifs is in 1/100ths of a sec)
784 return (double)total / 100.0;
785}
786
787// called on opening of a file load
788static void *
789evas_image_load_file_open_gif2(Eina_File *f,
790 Eina_Stringshare *key EINA_UNUSED, // XXX: we need to use key for frame #
791 Evas_Image_Load_Opts *opts,
792 Evas_Image_Animated *animated,
793 int *error)
794{
795 Loader_Info *loader = calloc(1, sizeof (Loader_Info));
796 if (!loader)
797 {
798 *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
799 return NULL;
800 }
801 loader->f = f;
802 loader->opts = opts;
803 loader->animated = animated;
804 return loader;
805}
806
807// called on closing of an image file load (end of load)
808static void
809evas_image_load_file_close_gif2(void *loader_data)
810{
811 Loader_Info *loader = loader_data;
812 free(loader);
1027} 813}
1028 814
815// general module delcaration stuff
1029static Evas_Image_Load_Func evas_image_load_gif_func = 816static Evas_Image_Load_Func evas_image_load_gif_func =
1030{ 817{
1031 evas_image_load_file_open_gif, 818 evas_image_load_file_open_gif2,
1032 evas_image_load_file_close_gif, 819 evas_image_load_file_close_gif2,
1033 evas_image_load_file_head_gif, 820 evas_image_load_file_head_gif2,
1034 evas_image_load_file_data_gif, 821 evas_image_load_file_data_gif2,
1035 evas_image_load_frame_duration_gif, 822 evas_image_load_frame_duration_gif2,
1036 EINA_TRUE, 823 EINA_TRUE,
1037 EINA_FALSE 824 EINA_FALSE
1038}; 825};
1039 826
827// raw module api that the rest of the world sees
1040static int 828static int
1041module_open(Evas_Module *em) 829module_open(Evas_Module *em)
1042{ 830{
@@ -1052,13 +840,13 @@ module_close(Evas_Module *em EINA_UNUSED)
1052 840
1053static Evas_Module_Api evas_modapi = 841static Evas_Module_Api evas_modapi =
1054{ 842{
1055 EVAS_MODULE_API_VERSION, 843 EVAS_MODULE_API_VERSION,
1056 "gif", 844 "gif",
1057 "none", 845 "none",
1058 { 846 {
1059 module_open, 847 module_open,
1060 module_close 848 module_close
1061 } 849 }
1062}; 850};
1063 851
1064EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, gif); 852EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, gif);