1 #include "common.h"
2 
3 #include <ctype.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <string.h>
7 #include <time.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #ifdef BUILD_X11
11 #include <X11/Xlib.h>
12 #endif
13 
14 #include "Imlib2.h"
15 #include "file.h"
16 #include "image.h"
17 #include "loaders.h"
18 
19 /* Imlib loader context */
20 struct _imlibldctx {
21    ImlibProgressFunction progress;
22    char                granularity;
23    int                 pct, area, row;
24    int                 pass, n_pass;
25 };
26 
27 static ImlibImage  *images = NULL;
28 
29 #ifdef BUILD_X11
30 static ImlibImagePixmap *pixmaps = NULL;
31 #endif
32 static int          cache_size = 4096 * 1024;
33 
34 __EXPORT__ DATA32  *
__imlib_AllocateData(ImlibImage * im)35 __imlib_AllocateData(ImlibImage * im)
36 {
37    int                 w = im->w;
38    int                 h = im->h;
39 
40    if (w <= 0 || h <= 0)
41       return NULL;
42 
43    if (im->data_memory_func)
44       im->data = im->data_memory_func(NULL, w * h * sizeof(DATA32));
45    else
46       im->data = malloc(w * h * sizeof(DATA32));
47 
48    return im->data;
49 }
50 
51 __EXPORT__ void
__imlib_FreeData(ImlibImage * im)52 __imlib_FreeData(ImlibImage * im)
53 {
54    if (im->data)
55      {
56         if (im->data_memory_func)
57            im->data_memory_func(im->data, im->w * im->h * sizeof(DATA32));
58         else
59            free(im->data);
60 
61         im->data = NULL;
62      }
63    im->w = 0;
64    im->h = 0;
65 }
66 
67 __EXPORT__ void
__imlib_ReplaceData(ImlibImage * im,unsigned int * new_data)68 __imlib_ReplaceData(ImlibImage * im, unsigned int *new_data)
69 {
70    if (im->data)
71      {
72         if (im->data_memory_func)
73            im->data_memory_func(im->data, im->w * im->h * sizeof(DATA32));
74         else
75            free(im->data);
76      }
77    im->data = new_data;
78    im->data_memory_func = NULL;
79 }
80 
81 /* create an image data struct and fill it in */
82 static ImlibImage  *
__imlib_ProduceImage(void)83 __imlib_ProduceImage(void)
84 {
85    ImlibImage         *im;
86 
87    im = calloc(1, sizeof(ImlibImage));
88    im->flags = F_FORMAT_IRRELEVANT | F_BORDER_IRRELEVANT | F_ALPHA_IRRELEVANT;
89 
90    return im;
91 }
92 
93 /* free an image struct */
94 static void
__imlib_ConsumeImage(ImlibImage * im)95 __imlib_ConsumeImage(ImlibImage * im)
96 {
97 #ifdef BUILD_X11
98    ImlibImagePixmap   *ip;
99 #endif
100 
101    __imlib_FreeAllTags(im);
102 
103    if (im->real_file && im->real_file != im->file)
104       free(im->real_file);
105    if (im->file)
106       free(im->file);
107    if (im->key)
108       free(im->key);
109    if ((IMAGE_FREE_DATA(im)) && (im->data))
110       __imlib_FreeData(im);
111    if (im->format)
112       free(im->format);
113    free(im);
114 #ifdef BUILD_X11
115    ip = pixmaps;
116    while (ip)
117      {
118         if (ip->image == im)
119           {
120              ip->image = NULL;
121              ip->dirty = 1;
122           }
123         ip = ip->next;
124      }
125 #endif
126 }
127 
128 static ImlibImage  *
__imlib_FindCachedImage(const char * file)129 __imlib_FindCachedImage(const char *file)
130 {
131    ImlibImage         *im, *previous_im;
132 
133    im = images;
134    previous_im = NULL;
135    /* go through the images list */
136    while (im)
137      {
138         /* if the filenames match and it's valid */
139         if ((!strcmp(file, im->file)) && (IMAGE_IS_VALID(im)))
140           {
141              /* move the image to the head of the pixmap list */
142              if (previous_im)
143                {
144                   previous_im->next = im->next;
145                   im->next = images;
146                   images = im;
147                }
148              /* return it */
149              return im;
150           }
151         previous_im = im;
152         im = im->next;
153      }
154    return NULL;
155 }
156 
157 /* add an image to the cache of images (at the start) */
158 static void
__imlib_AddImageToCache(ImlibImage * im)159 __imlib_AddImageToCache(ImlibImage * im)
160 {
161    im->next = images;
162    images = im;
163 }
164 
165 /* remove (unlink) an image from the cache of images */
166 static void
__imlib_RemoveImageFromCache(ImlibImage * im)167 __imlib_RemoveImageFromCache(ImlibImage * im)
168 {
169    ImlibImage         *current_im, *previous_im;
170 
171    current_im = images;
172    previous_im = NULL;
173    while (current_im)
174      {
175         if (im == current_im)
176           {
177              if (previous_im)
178                 previous_im->next = im->next;
179              else
180                 images = im->next;
181              return;
182           }
183         previous_im = current_im;
184         current_im = current_im->next;
185      }
186 }
187 
188 /* work out how much we have floaitng aroudn in our speculative cache */
189 /* (images and pixmaps that have 0 reference counts) */
190 int
__imlib_CurrentCacheSize(void)191 __imlib_CurrentCacheSize(void)
192 {
193    ImlibImage         *im;
194 
195 #ifdef BUILD_X11
196    ImlibImagePixmap   *ip;
197 #endif
198    int                 current_cache = 0;
199 
200    /* go through the image cache */
201    im = images;
202    while (im)
203      {
204         /* mayaswell clean out stuff thats invalid that we dont need anymore */
205         if (im->references == 0)
206           {
207              if (!(IMAGE_IS_VALID(im)))
208                {
209                   ImlibImage         *tmp_im;
210 
211                   tmp_im = im;
212                   im = im->next;
213                   __imlib_RemoveImageFromCache(tmp_im);
214                   __imlib_ConsumeImage(tmp_im);
215                   continue;
216                }
217              /* it's valid but has 0 ref's - append to cache size count */
218              else
219                 current_cache += im->w * im->h * sizeof(DATA32);
220           }
221         im = im->next;
222      }
223 
224 #ifdef BUILD_X11
225    /* go through the pixmaps */
226    ip = pixmaps;
227    while (ip)
228      {
229         /* if the pixmap has 0 references */
230         if (ip->references == 0)
231           {
232              /* if the image is invalid */
233              if ((ip->dirty) || ((ip->image) && (!(IMAGE_IS_VALID(ip->image)))))
234                {
235                   ImlibImagePixmap   *tmp_ip;
236 
237                   tmp_ip = ip;
238                   ip = ip->next;
239                   __imlib_RemoveImagePixmapFromCache(tmp_ip);
240                   __imlib_ConsumeImagePixmap(tmp_ip);
241                   continue;
242                }
243              else
244                {
245                   /* add the pixmap data size to the cache size */
246                   if (ip->pixmap)
247                     {
248                        if (ip->depth < 8)
249                           current_cache += ip->w * ip->h * (ip->depth / 8);
250                        else if (ip->depth == 8)
251                           current_cache += ip->w * ip->h;
252                        else if (ip->depth <= 16)
253                           current_cache += ip->w * ip->h * 2;
254                        else if (ip->depth <= 32)
255                           current_cache += ip->w * ip->h * 4;
256                     }
257                   /* if theres a mask add it too */
258                   if (ip->mask)
259                      current_cache += ip->w * ip->h / 8;
260                }
261           }
262         ip = ip->next;
263      }
264 #endif
265    return current_cache;
266 }
267 
268 /* clean out images from the cache if the cache is overgrown */
269 static void
__imlib_CleanupImageCache(void)270 __imlib_CleanupImageCache(void)
271 {
272    ImlibImage         *im, *im_last;
273    int                 current_cache;
274 
275    current_cache = __imlib_CurrentCacheSize();
276    im_last = NULL;
277    im = images;
278    /* remove 0 ref count invalid (dirty) images */
279    while (im)
280      {
281         im_last = im;
282         im = im->next;
283         if ((im_last->references <= 0) && (!(IMAGE_IS_VALID(im_last))))
284           {
285              __imlib_RemoveImageFromCache(im_last);
286              __imlib_ConsumeImage(im_last);
287           }
288      }
289    /* while the cache size of 0 ref coutn data is bigger than the set value */
290    /* clean out the oldest members of the imaeg cache */
291    while (current_cache > cache_size)
292      {
293         im_last = NULL;
294         im = images;
295         while (im)
296           {
297              if (im->references <= 0)
298                 im_last = im;
299              im = im->next;
300           }
301         if (!im_last)
302            break;
303 
304         __imlib_RemoveImageFromCache(im_last);
305         __imlib_ConsumeImage(im_last);
306 
307         current_cache = __imlib_CurrentCacheSize();
308      }
309 }
310 
311 /* set the cache size */
312 void
__imlib_SetCacheSize(int size)313 __imlib_SetCacheSize(int size)
314 {
315    cache_size = size;
316    __imlib_CleanupImageCache();
317 #ifdef BUILD_X11
318    __imlib_CleanupImagePixmapCache();
319 #endif
320 }
321 
322 /* return the cache size */
323 int
__imlib_GetCacheSize(void)324 __imlib_GetCacheSize(void)
325 {
326    return cache_size;
327 }
328 
329 #ifdef BUILD_X11
330 /* create a pixmap cache data struct */
331 ImlibImagePixmap   *
__imlib_ProduceImagePixmap(void)332 __imlib_ProduceImagePixmap(void)
333 {
334    ImlibImagePixmap   *ip;
335 
336    ip = calloc(1, sizeof(ImlibImagePixmap));
337 
338    return ip;
339 }
340 
341 /* free a pixmap cache data struct and the pixmaps in it */
342 void
__imlib_ConsumeImagePixmap(ImlibImagePixmap * ip)343 __imlib_ConsumeImagePixmap(ImlibImagePixmap * ip)
344 {
345 #ifdef DEBUG_CACHE
346    fprintf(stderr,
347            "[Imlib2]  Deleting pixmap.  Reference count is %d, pixmap 0x%08x, mask 0x%08x\n",
348            ip->references, ip->pixmap, ip->mask);
349 #endif
350    if (ip->pixmap)
351       XFreePixmap(ip->display, ip->pixmap);
352    if (ip->mask)
353       XFreePixmap(ip->display, ip->mask);
354    if (ip->file)
355       free(ip->file);
356    free(ip);
357 }
358 
359 ImlibImagePixmap   *
__imlib_FindCachedImagePixmap(ImlibImage * im,int w,int h,Display * d,Visual * v,int depth,int sx,int sy,int sw,int sh,Colormap cm,char aa,char hiq,char dmask,DATABIG modification_count)360 __imlib_FindCachedImagePixmap(ImlibImage * im, int w, int h, Display * d,
361                               Visual * v, int depth, int sx, int sy, int sw,
362                               int sh, Colormap cm, char aa, char hiq,
363                               char dmask, DATABIG modification_count)
364 {
365    ImlibImagePixmap   *ip, *previous_ip;
366 
367    ip = pixmaps;
368    previous_ip = NULL;
369    /* go through the pixmap list */
370    while (ip)
371      {
372         /* if all the pixmap attributes match */
373         if ((ip->w == w) && (ip->h == h) && (ip->depth == depth) && (!ip->dirty)
374             && (ip->visual == v) && (ip->display == d)
375             && (ip->source_x == sx) && (ip->source_x == sy)
376             && (ip->source_w == sw) && (ip->source_h == sh)
377             && (ip->colormap == cm) && (ip->antialias == aa)
378             && (ip->modification_count == modification_count)
379             && (ip->dither_mask == dmask)
380             && (ip->border.left == im->border.left)
381             && (ip->border.right == im->border.right)
382             && (ip->border.top == im->border.top)
383             && (ip->border.bottom == im->border.bottom) &&
384             (((im->file) && (ip->file) && !strcmp(im->file, ip->file)) ||
385              ((!im->file) && (!ip->file) && (im == ip->image))))
386           {
387              /* move the pixmap to the head of the pixmap list */
388              if (previous_ip)
389                {
390                   previous_ip->next = ip->next;
391                   ip->next = pixmaps;
392                   pixmaps = ip;
393                }
394              /* return it */
395              return ip;
396           }
397         previous_ip = ip;
398         ip = ip->next;
399      }
400    return NULL;
401 }
402 
403 ImlibImagePixmap   *
__imlib_FindCachedImagePixmapByID(Display * d,Pixmap p)404 __imlib_FindCachedImagePixmapByID(Display * d, Pixmap p)
405 {
406    ImlibImagePixmap   *ip;
407 
408    ip = pixmaps;
409    /* go through the pixmap list */
410    while (ip)
411      {
412         /* if all the pixmap attributes match */
413         if ((ip->pixmap == p) && (ip->display == d))
414            return ip;
415         ip = ip->next;
416      }
417    return NULL;
418 }
419 
420 /* add a pixmap cahce struct to the pixmap cache (at the start of course */
421 void
__imlib_AddImagePixmapToCache(ImlibImagePixmap * ip)422 __imlib_AddImagePixmapToCache(ImlibImagePixmap * ip)
423 {
424    ip->next = pixmaps;
425    pixmaps = ip;
426 }
427 
428 /* remove a pixmap cache struct from the pixmap cache */
429 void
__imlib_RemoveImagePixmapFromCache(ImlibImagePixmap * ip)430 __imlib_RemoveImagePixmapFromCache(ImlibImagePixmap * ip)
431 {
432    ImlibImagePixmap   *current_ip, *previous_ip;
433 
434    current_ip = pixmaps;
435    previous_ip = NULL;
436    while (current_ip)
437      {
438         if (ip == current_ip)
439           {
440              if (previous_ip)
441                 previous_ip->next = ip->next;
442              else
443                 pixmaps = ip->next;
444              return;
445           }
446         previous_ip = current_ip;
447         current_ip = current_ip->next;
448      }
449 }
450 
451 /* clean out 0 reference count & dirty pixmaps from the cache */
452 void
__imlib_CleanupImagePixmapCache(void)453 __imlib_CleanupImagePixmapCache(void)
454 {
455    ImlibImagePixmap   *ip, *ip_last;
456    int                 current_cache;
457 
458    current_cache = __imlib_CurrentCacheSize();
459    ip_last = NULL;
460    ip = pixmaps;
461    while (ip)
462      {
463         ip_last = ip;
464         ip = ip->next;
465         if ((ip_last->references <= 0) && (ip_last->dirty))
466           {
467              __imlib_RemoveImagePixmapFromCache(ip_last);
468              __imlib_ConsumeImagePixmap(ip_last);
469           }
470      }
471    while (current_cache > cache_size)
472      {
473         ip_last = NULL;
474         ip = pixmaps;
475         while (ip)
476           {
477              if (ip->references <= 0)
478                 ip_last = ip;
479              ip = ip->next;
480           }
481         if (!ip_last)
482            break;
483 
484         __imlib_RemoveImagePixmapFromCache(ip_last);
485         __imlib_ConsumeImagePixmap(ip_last);
486 
487         current_cache = __imlib_CurrentCacheSize();
488      }
489 }
490 #endif
491 
492 static int
__imlib_ErrorFromErrno(int err,int save)493 __imlib_ErrorFromErrno(int err, int save)
494 {
495    switch (err)
496      {
497      default:
498         return IMLIB_LOAD_ERROR_UNKNOWN;
499         /* standrad fopen() type errors translated */
500      case 0:
501         return IMLIB_LOAD_ERROR_NONE;
502      case EEXIST:
503         return IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST;
504      case EISDIR:
505         return IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY;
506      case EACCES:
507      case EROFS:
508         return (save) ? IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE :
509            IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ;
510      case ENAMETOOLONG:
511         return IMLIB_LOAD_ERROR_PATH_TOO_LONG;
512      case ENOENT:
513         return IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT;
514      case ENOTDIR:
515         return IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY;
516      case EFAULT:
517         return IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE;
518      case ELOOP:
519         return IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS;
520      case ENOMEM:
521         return IMLIB_LOAD_ERROR_OUT_OF_MEMORY;
522      case EMFILE:
523         return IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS;
524      case ENOSPC:
525         return IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE;
526      }
527 }
528 
529 static int
__imlib_FileCheck(const char * file,FILE * fp,struct stat * st,ImlibLoadError * er)530 __imlib_FileCheck(const char *file, FILE * fp, struct stat *st,
531                   ImlibLoadError * er)
532 {
533    int                 err;
534 
535    err = 0;
536 
537    if (fp ? fstat(fileno(fp), st) : __imlib_FileStat(file, st))
538       err = IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST;
539    else if (__imlib_StatIsDir(st))
540       err = IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY;
541    else if (st->st_size == 0)
542       err = IMLIB_LOAD_ERROR_UNKNOWN;
543 
544    if (er)
545       *er = err;
546 
547    return err;
548 }
549 
550 /* create a new image struct from data passed that is wize w x h then return */
551 /* a pointer to that image sturct */
552 ImlibImage         *
__imlib_CreateImage(int w,int h,DATA32 * data)553 __imlib_CreateImage(int w, int h, DATA32 * data)
554 {
555    ImlibImage         *im;
556 
557    im = __imlib_ProduceImage();
558    im->w = w;
559    im->h = h;
560    im->data = data;
561    im->references = 1;
562    SET_FLAG(im->flags, F_UNCACHEABLE);
563    return im;
564 }
565 
566 static int
__imlib_LoadImageWrapper(const ImlibLoader * l,ImlibImage * im,int load_data)567 __imlib_LoadImageWrapper(const ImlibLoader * l, ImlibImage * im, int load_data)
568 {
569    int                 rc;
570 
571    if (l->load2)
572      {
573         FILE               *fp = NULL;
574 
575         if (!im->fp)
576           {
577              fp = im->fp = fopen(im->real_file, "rb");
578              if (!im->fp)
579                 return 0;
580           }
581         rc = l->load2(im, load_data);
582 
583         if (fp)
584            fclose(fp);
585      }
586    else if (l->load)
587      {
588         if (im->lc)
589            rc = l->load(im, im->lc->progress, im->lc->granularity, 1);
590         else
591            rc = l->load(im, NULL, 0, load_data);
592      }
593    else
594      {
595         return 0;
596      }
597 
598    if (rc == 0)
599      {
600         /* Failed - clean up */
601         if (im->w != 0 || im->h != 0)
602           {
603              im->w = im->h = 0;
604           }
605         if (im->data)
606           {
607              __imlib_FreeData(im);
608           }
609         if (im->format)
610           {
611              free(im->format);
612              im->format = NULL;
613           }
614      }
615    else
616      {
617         if (!im->format && l->formats && l->formats[0])
618            im->format = strdup(l->formats[0]);
619      }
620 
621    return rc;
622 }
623 
624 static void
__imlib_LoadCtxInit(ImlibImage * im,ImlibLdCtx * lc,ImlibProgressFunction prog,int gran)625 __imlib_LoadCtxInit(ImlibImage * im, ImlibLdCtx * lc,
626                     ImlibProgressFunction prog, int gran)
627 {
628    im->lc = lc;
629    lc->progress = prog;
630    lc->granularity = gran;
631    lc->pct = lc->row = 0;
632    lc->area = 0;
633    lc->pass = 0;
634    lc->n_pass = 1;
635 }
636 
637 __EXPORT__ int
__imlib_LoadEmbedded(ImlibLoader * l,ImlibImage * im,const char * file,int load_data)638 __imlib_LoadEmbedded(ImlibLoader * l, ImlibImage * im, const char *file,
639                      int load_data)
640 {
641    int                 rc;
642    char               *file_save;
643    FILE               *fp_save;
644 
645    if (!l || !im)
646       return 0;
647 
648    /* remember the original filename */
649    file_save = im->real_file;
650    im->real_file = strdup(file);
651    fp_save = im->fp;
652    im->fp = NULL;
653 
654    rc = __imlib_LoadImageWrapper(l, im, load_data);
655 
656    im->fp = fp_save;
657    free(im->real_file);
658    im->real_file = file_save;
659 
660    return rc;
661 }
662 
663 ImlibImage         *
__imlib_LoadImage(const char * file,FILE * fp,ImlibProgressFunction progress,char progress_granularity,char immediate_load,char dont_cache,ImlibLoadError * er)664 __imlib_LoadImage(const char *file, FILE * fp, ImlibProgressFunction progress,
665                   char progress_granularity, char immediate_load,
666                   char dont_cache, ImlibLoadError * er)
667 {
668    ImlibImage         *im;
669    ImlibLoader        *best_loader;
670    int                 loader_ret;
671    ImlibLdCtx          ilc;
672    struct stat         st;
673 
674    if (!file || file[0] == '\0')
675       return NULL;
676 
677    /* see if we already have the image cached */
678    im = __imlib_FindCachedImage(file);
679 
680    /* if we found a cached image and we should always check that it is */
681    /* accurate to the disk conents if they changed since we last loaded */
682    /* and that it is still a valid image */
683    if ((im) && (IMAGE_IS_VALID(im)))
684      {
685         if (IMAGE_ALWAYS_CHECK_DISK(im))
686           {
687              time_t              current_modified_time;
688 
689              current_modified_time = fp ?
690                 __imlib_FileModDateFd(fileno(fp)) :
691                 __imlib_FileModDate(im->real_file);
692              /* if the file on disk is newer than the cached one */
693              if (current_modified_time != im->moddate)
694                {
695                   /* invalidate image */
696                   SET_FLAG(im->flags, F_INVALID);
697                }
698              else
699                {
700                   /* image is ok to re-use - program is just being stupid loading */
701                   /* the same data twice */
702                   im->references++;
703                   return im;
704                }
705           }
706         else
707           {
708              im->references++;
709              return im;
710           }
711      }
712 
713    if (__imlib_FileCheck(file, fp, &st, er))
714       return NULL;
715 
716    /* either image in cache is invalid or we dont even have it in cache */
717    /* so produce a new one and load an image into that */
718    im = __imlib_ProduceImage();
719    im->file = strdup(file);
720 
721    if (__imlib_StatIsFile(&st))
722      {
723         im->real_file = im->file;
724         im->key = NULL;
725      }
726    else
727      {
728         im->real_file = __imlib_FileRealFile(file);
729         im->key = __imlib_FileKey(file);
730      }
731 
732    if (fp)
733       im->fp = fp;
734    else
735       im->fp = fopen(im->real_file, "rb");
736 
737    if (!im->fp)
738      {
739         if (er)
740            *er = __imlib_ErrorFromErrno(errno, 0);
741         __imlib_ConsumeImage(im);
742         return NULL;
743      }
744 
745    im->moddate = __imlib_StatModDate(&st);
746 
747    im->data_memory_func = imlib_context_get_image_data_memory_function();
748 
749    if (progress)
750      {
751         __imlib_LoadCtxInit(im, &ilc, progress, progress_granularity);
752         immediate_load = 1;
753      }
754 
755    __imlib_LoadAllLoaders();
756 
757    loader_ret = 0;
758 
759    /* take a guess by extension on the best loader to use */
760    best_loader = __imlib_FindBestLoaderForFile(im->real_file, 0);
761    errno = 0;
762    if (best_loader)
763       loader_ret = __imlib_LoadImageWrapper(best_loader, im, immediate_load);
764 
765    if (loader_ret > 0)
766      {
767         im->loader = best_loader;
768      }
769    else
770      {
771         ImlibLoader       **loaders = __imlib_GetLoaderList();
772         ImlibLoader        *l, *previous_l;
773 
774         errno = 0;
775         /* run through all loaders and try load until one succeeds */
776         for (l = *loaders, previous_l = NULL; l; previous_l = l, l = l->next)
777           {
778              /* if its not the best loader that already failed - try load */
779              if (l == best_loader)
780                 continue;
781              rewind(im->fp);
782              loader_ret = __imlib_LoadImageWrapper(l, im, immediate_load);
783              if (loader_ret > 0)
784                 break;
785           }
786 
787         /* if we have a loader then its the loader that succeeded */
788         /* move the successful loader to the head of the list */
789         /* as long as it's not already at the head of the list */
790         if (l)
791           {
792              im->loader = l;
793              if (previous_l)
794                {
795                   previous_l->next = l->next;
796                   l->next = *loaders;
797                   *loaders = l;
798                }
799           }
800      }
801 
802    im->lc = NULL;
803 
804    if (!fp)
805       fclose(im->fp);
806    im->fp = NULL;
807 
808    /* all loaders have been tried and they all failed. free the skeleton */
809    /* image struct we had and return NULL */
810    if (loader_ret <= 0)
811      {
812         /* if the caller wants an error return */
813         if (er)
814            *er = __imlib_ErrorFromErrno(errno, 0);
815         __imlib_ConsumeImage(im);
816         return NULL;
817      }
818 
819    /* the load succeeded - make sure the image is referenced then add */
820    /* it to our cache if dont_cache isn't set */
821    im->references = 1;
822    if (loader_ret == 2)
823       dont_cache = 1;
824    if (!dont_cache)
825       __imlib_AddImageToCache(im);
826    else
827       SET_FLAG(im->flags, F_UNCACHEABLE);
828 
829    return im;
830 }
831 
832 int
__imlib_LoadImageData(ImlibImage * im)833 __imlib_LoadImageData(ImlibImage * im)
834 {
835    if (!im->data && im->loader)
836       if (__imlib_LoadImageWrapper(im->loader, im, 1) == 0)
837          return 1;              /* Load failed */
838    return im->data == NULL;
839 }
840 
841 __EXPORT__ void
__imlib_LoadProgressSetPass(ImlibImage * im,int pass,int n_pass)842 __imlib_LoadProgressSetPass(ImlibImage * im, int pass, int n_pass)
843 {
844    im->lc->pass = pass;
845    im->lc->n_pass = n_pass;
846 
847    im->lc->row = 0;
848 }
849 
850 __EXPORT__ int
__imlib_LoadProgress(ImlibImage * im,int x,int y,int w,int h)851 __imlib_LoadProgress(ImlibImage * im, int x, int y, int w, int h)
852 {
853    ImlibLdCtx         *lc = im->lc;
854    int                 rc;
855 
856    lc->area += w * h;
857    lc->pct = (100. * lc->area + .1) / (im->w * im->h);
858 
859    rc = !lc->progress(im, lc->pct, x, y, w, h);
860 
861    return rc;
862 }
863 
864 __EXPORT__ int
__imlib_LoadProgressRows(ImlibImage * im,int row,int nrows)865 __imlib_LoadProgressRows(ImlibImage * im, int row, int nrows)
866 {
867    ImlibLdCtx         *lc = im->lc;
868    int                 rc = 0;
869    int                 pct, nrtot;
870 
871    if (nrows > 0)
872      {
873         /* Row index counting up */
874         nrtot = row + nrows;
875         row = lc->row;
876         nrows = nrtot - lc->row;
877      }
878    else
879      {
880         /* Row index counting down */
881         nrtot = im->h - row;
882         row = row;
883         nrows = nrtot - lc->row;
884      }
885 
886    pct = (100 * nrtot * (lc->pass + 1)) / (im->h * lc->n_pass);
887    if (pct == 100 || pct >= lc->pct + lc->granularity)
888      {
889         rc = !lc->progress(im, pct, 0, row, im->w, nrows);
890         lc->row = nrtot;
891         lc->pct += lc->granularity;
892      }
893 
894    return rc;
895 }
896 
897 #ifdef BUILD_X11
898 /* find an imagepixmap cache entry by the display and pixmap id */
899 ImlibImagePixmap   *
__imlib_FindImlibImagePixmapByID(Display * d,Pixmap p)900 __imlib_FindImlibImagePixmapByID(Display * d, Pixmap p)
901 {
902    ImlibImagePixmap   *ip;
903 
904    ip = pixmaps;
905    /* go through the pixmap list */
906    while (ip)
907      {
908         /* if all the pixmap ID & Display match */
909         if ((ip->pixmap == p) && (ip->display == d))
910           {
911 #ifdef DEBUG_CACHE
912              fprintf(stderr,
913                      "[Imlib2]  Match found.  Reference count is %d, pixmap 0x%08x, mask 0x%08x\n",
914                      ip->references, ip->pixmap, ip->mask);
915 #endif
916              return ip;
917           }
918         ip = ip->next;
919      }
920    return NULL;
921 }
922 #endif
923 
924 /* free and image - if its uncachable and refcoutn is 0 - free it in reality */
925 void
__imlib_FreeImage(ImlibImage * im)926 __imlib_FreeImage(ImlibImage * im)
927 {
928    /* if the refcount is positive */
929    if (im->references >= 0)
930      {
931         /* reduce a reference from the count */
932         im->references--;
933         /* if its uncachchable ... */
934         if (IMAGE_IS_UNCACHEABLE(im))
935           {
936              /* and we're down to no references for the image then free it */
937              if (im->references == 0)
938                 __imlib_ConsumeImage(im);
939           }
940         /* otherwise clean up our cache if the image becoem 0 ref count */
941         else if (im->references == 0)
942            __imlib_CleanupImageCache();
943      }
944 }
945 
946 #ifdef BUILD_X11
947 /* free a cached pixmap */
948 void
__imlib_FreePixmap(Display * d,Pixmap p)949 __imlib_FreePixmap(Display * d, Pixmap p)
950 {
951    ImlibImagePixmap   *ip;
952 
953    /* find the pixmap in the cache by display and id */
954    ip = __imlib_FindImlibImagePixmapByID(d, p);
955    if (ip)
956      {
957         /* if tis positive reference count */
958         if (ip->references > 0)
959           {
960              /* dereference it by one */
961              ip->references--;
962 #ifdef DEBUG_CACHE
963              fprintf(stderr,
964                      "[Imlib2]  Reference count is now %d for pixmap 0x%08x\n",
965                      ip->references, ip->pixmap);
966 #endif
967              /* if it becaume 0 reference count - clean the cache up */
968              if (ip->references == 0)
969                 __imlib_CleanupImagePixmapCache();
970           }
971      }
972    else
973      {
974 #ifdef DEBUG_CACHE
975         fprintf(stderr, "[Imlib2]  Pixmap 0x%08x not found.  Freeing.\n", p);
976 #endif
977         XFreePixmap(d, p);
978      }
979 }
980 
981 /* mark all pixmaps generated from this image as dirty so the cache code */
982 /* wont pick up on them again since they are now invalid since the original */
983 /* data they were generated from has changed */
984 void
__imlib_DirtyPixmapsForImage(ImlibImage * im)985 __imlib_DirtyPixmapsForImage(ImlibImage * im)
986 {
987    ImlibImagePixmap   *ip;
988 
989    ip = pixmaps;
990    /* go through the pixmap list */
991    while (ip)
992      {
993         /* if image matches */
994         if (ip->image == im)
995            ip->dirty = 1;
996         ip = ip->next;
997      }
998    __imlib_CleanupImagePixmapCache();
999 }
1000 #endif
1001 
1002 /* dirty and image by settings its invalid flag */
1003 void
__imlib_DirtyImage(ImlibImage * im)1004 __imlib_DirtyImage(ImlibImage * im)
1005 {
1006    SET_FLAG(im->flags, F_INVALID);
1007 #ifdef BUILD_X11
1008    /* and dirty all pixmaps generated from it */
1009    __imlib_DirtyPixmapsForImage(im);
1010 #endif
1011 }
1012 
1013 void
__imlib_SaveImage(ImlibImage * im,const char * file,ImlibProgressFunction progress,char progress_granularity,ImlibLoadError * er)1014 __imlib_SaveImage(ImlibImage * im, const char *file,
1015                   ImlibProgressFunction progress, char progress_granularity,
1016                   ImlibLoadError * er)
1017 {
1018    ImlibLoader        *l;
1019    char                e, *pfile;
1020    ImlibLdCtx          ilc;
1021 
1022    if (!file)
1023      {
1024         if (er)
1025            *er = IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST;
1026         return;
1027      }
1028 
1029    __imlib_LoadAllLoaders();
1030 
1031    /* find the laoder for the format - if its null use the extension */
1032    l = __imlib_FindBestLoaderForFileFormat(file, im->format, 1);
1033    /* no loader - abort */
1034    if (!l)
1035      {
1036         if (er)
1037            *er = IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT;
1038         return;
1039      }
1040 
1041    if (progress)
1042       __imlib_LoadCtxInit(im, &ilc, progress, progress_granularity);
1043 
1044    /* set the filename to the user supplied one */
1045    pfile = im->real_file;
1046    im->real_file = strdup(file);
1047 
1048    /* call the saver */
1049    e = l->save(im, progress, progress_granularity);
1050 
1051    /* set the filename back to the laoder image filename */
1052    free(im->real_file);
1053    im->real_file = pfile;
1054 
1055    im->lc = NULL;
1056 
1057    if (er)
1058       *er = __imlib_ErrorFromErrno(e > 0 ? 0 : errno, 1);
1059 }
1060