wrlib: removed macro RETRY that does not does what is expected (Coverity #50160)
[wmaker-crm.git] / wrlib / load.c
blob9bebc9a361bd2b55a9779133d6aa2aab1b62ed91
1 /* load.c - load image from file
3 * Raster graphics library
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 2014 Window Maker Team
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
21 * MA 02110-1301, USA.
24 #include <config.h>
26 #include <errno.h>
27 #include <X11/Xlib.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <sys/stat.h>
33 #include <string.h>
34 #include <time.h>
35 #include <assert.h>
37 #include "wraster.h"
38 #include "imgformat.h"
41 typedef struct RCachedImage {
42 RImage *image;
43 char *file;
44 time_t last_modif; /* last time file was modified */
45 time_t last_use; /* last time image was used */
46 } RCachedImage;
49 * Size of image cache
51 static int RImageCacheSize = -1;
54 * Max. size of image to store in cache
56 static int RImageCacheMaxImage = -1; /* 0 = any size */
58 #define IMAGE_CACHE_SIZE 8
60 #define IMAGE_CACHE_MAX_IMAGE 64*64
62 static RCachedImage *RImageCache;
65 static WRImgFormat identFile(const char *path);
68 char **RSupportedFileFormats(void)
70 static char *tmp[IM_TYPES + 2];
71 int i = 0;
73 /* built-in */
74 tmp[i++] = "XPM";
75 /* built-in PNM here refers to anymap format: PPM, PGM, PBM */
76 tmp[i++] = "PNM";
78 * PPM is a just a sub-type of PNM, but it has to be in the list
79 * for compatibility with legacy programs that may expect it but
80 * not the new PNM type
82 tmp[i++] = "PPM";
83 #ifdef USE_TIFF
84 tmp[i++] = "TIFF";
85 #endif
86 #ifdef USE_PNG
87 tmp[i++] = "PNG";
88 #endif
89 #ifdef USE_JPEG
90 tmp[i++] = "JPEG";
91 #endif
92 #ifdef USE_GIF
93 tmp[i++] = "GIF";
94 #endif
95 #ifdef USE_WEBP
96 tmp[i++] = "WEBP";
97 #endif
98 tmp[i] = NULL;
100 return tmp;
103 static void init_cache(void)
105 char *tmp;
107 tmp = getenv("RIMAGE_CACHE");
108 if (!tmp || sscanf(tmp, "%i", &RImageCacheSize) != 1)
109 RImageCacheSize = IMAGE_CACHE_SIZE;
111 if (RImageCacheSize < 0)
112 RImageCacheSize = 0;
114 tmp = getenv("RIMAGE_CACHE_SIZE");
115 if (!tmp || sscanf(tmp, "%i", &RImageCacheMaxImage) != 1)
116 RImageCacheMaxImage = IMAGE_CACHE_MAX_IMAGE;
118 if (RImageCacheSize > 0) {
119 RImageCache = malloc(sizeof(RCachedImage) * RImageCacheSize);
120 if (RImageCache == NULL) {
121 printf("wrlib: out of memory for image cache\n");
122 return;
124 memset(RImageCache, 0, sizeof(RCachedImage) * RImageCacheSize);
128 void RReleaseCache(void)
130 int i;
132 if (RImageCacheSize > 0) {
133 for (i = 0; i < RImageCacheSize; i++) {
134 if (RImageCache[i].file) {
135 RReleaseImage(RImageCache[i].image);
136 free(RImageCache[i].file);
139 free(RImageCache);
140 RImageCache = NULL;
141 RImageCacheSize = -1;
145 RImage *RLoadImage(RContext *context, const char *file, int index)
147 RImage *image = NULL;
148 int i;
149 struct stat st;
151 assert(file != NULL);
153 if (RImageCacheSize < 0)
154 init_cache();
156 if (RImageCacheSize > 0) {
158 for (i = 0; i < RImageCacheSize; i++) {
159 if (RImageCache[i].file && strcmp(file, RImageCache[i].file) == 0) {
161 if (stat(file, &st) == 0 && st.st_mtime == RImageCache[i].last_modif) {
162 RImageCache[i].last_use = time(NULL);
164 return RCloneImage(RImageCache[i].image);
166 } else {
167 free(RImageCache[i].file);
168 RImageCache[i].file = NULL;
169 RReleaseImage(RImageCache[i].image);
175 switch (identFile(file)) {
176 case IM_ERROR:
177 return NULL;
179 case IM_UNKNOWN:
180 #ifdef USE_MAGICK
181 /* generic file format support using ImageMagick
182 * BMP, PCX, PICT, SVG, ...
184 image = RLoadMagick(file);
185 break;
186 #else
187 RErrorCode = RERR_BADFORMAT;
188 return NULL;
189 #endif
191 case IM_XPM:
192 image = RLoadXPM(context, file);
193 break;
195 #ifdef USE_TIFF
196 case IM_TIFF:
197 image = RLoadTIFF(file, index);
198 break;
199 #endif /* USE_TIFF */
201 #ifdef USE_PNG
202 case IM_PNG:
203 image = RLoadPNG(context, file);
204 break;
205 #endif /* USE_PNG */
207 #ifdef USE_JPEG
208 case IM_JPEG:
209 image = RLoadJPEG(file);
210 break;
211 #endif /* USE_JPEG */
213 #ifdef USE_GIF
214 case IM_GIF:
215 image = RLoadGIF(file, index);
216 break;
217 #endif /* USE_GIF */
219 #ifdef USE_WEBP
220 case IM_WEBP:
221 image = RLoadWEBP(file);
222 break;
223 #endif /* USE_WEBP */
225 case IM_PPM:
226 image = RLoadPPM(file);
227 break;
229 default:
230 RErrorCode = RERR_BADFORMAT;
231 return NULL;
234 /* store image in cache */
235 if (RImageCacheSize > 0 && image &&
236 (RImageCacheMaxImage == 0 || RImageCacheMaxImage >= image->width * image->height)) {
237 time_t oldest = time(NULL);
238 int oldest_idx = 0;
239 int done = 0;
241 for (i = 0; i < RImageCacheSize; i++) {
242 if (!RImageCache[i].file) {
243 RImageCache[i].file = malloc(strlen(file) + 1);
244 strcpy(RImageCache[i].file, file);
245 RImageCache[i].image = RCloneImage(image);
246 RImageCache[i].last_modif = st.st_mtime;
247 RImageCache[i].last_use = time(NULL);
248 done = 1;
249 break;
250 } else {
251 if (oldest > RImageCache[i].last_use) {
252 oldest = RImageCache[i].last_use;
253 oldest_idx = i;
258 /* if no slot available, dump least recently used one */
259 if (!done) {
260 free(RImageCache[oldest_idx].file);
261 RReleaseImage(RImageCache[oldest_idx].image);
262 RImageCache[oldest_idx].file = malloc(strlen(file) + 1);
263 strcpy(RImageCache[oldest_idx].file, file);
264 RImageCache[oldest_idx].image = RCloneImage(image);
265 RImageCache[oldest_idx].last_modif = st.st_mtime;
266 RImageCache[oldest_idx].last_use = time(NULL);
270 return image;
273 char *RGetImageFileFormat(const char *file)
275 switch (identFile(file)) {
276 case IM_XPM:
277 return "XPM";
279 #ifdef USE_TIFF
280 case IM_TIFF:
281 return "TIFF";
282 #endif /* USE_TIFF */
284 #ifdef USE_PNG
285 case IM_PNG:
286 return "PNG";
287 #endif /* USE_PNG */
289 #ifdef USE_JPEG
290 case IM_JPEG:
291 return "JPEG";
292 #endif /* USE_JPEG */
294 #ifdef USE_GIF
295 case IM_GIF:
296 return "GIF";
297 #endif /* USE_GIF */
299 #ifdef USE_WEBP
300 case IM_WEBP:
301 return "WEBP";
302 #endif /* USE_WEBP */
304 case IM_PPM:
305 return "PPM";
307 default:
308 return NULL;
312 static WRImgFormat identFile(const char *path)
314 FILE *file;
315 unsigned char buffer[32];
316 size_t nread;
318 assert(path != NULL);
320 for (;;) {
321 file = fopen(path, "rb");
322 if (file != NULL)
323 break;
324 if (errno != EINTR) {
325 RErrorCode = RERR_OPEN;
326 return IM_ERROR;
330 nread = fread(buffer, 1, sizeof(buffer), file);
331 if (nread < sizeof(buffer) || ferror(file)) {
332 fclose(file);
333 RErrorCode = RERR_READ;
334 return IM_ERROR;
336 fclose(file);
338 /* check for XPM */
339 if (strncmp((char *)buffer, "/* XPM */", 9) == 0)
340 return IM_XPM;
342 /* check for TIFF */
343 if ((buffer[0] == 'I' && buffer[1] == 'I' && buffer[2] == '*' && buffer[3] == 0)
344 || (buffer[0] == 'M' && buffer[1] == 'M' && buffer[2] == 0 && buffer[3] == '*'))
345 return IM_TIFF;
348 * check for PNG
350 * The signature is defined in the PNG specifiation:
351 * http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html
352 * it is valid for v1.0, v1.1, v1.2 and ISO version
354 if (buffer[0] == 137 && buffer[1] == 80 && buffer[2] == 78 && buffer[3] == 71 &&
355 buffer[4] == 13 && buffer[5] == 10 && buffer[6] == 26 && buffer[7] == 10)
356 return IM_PNG;
358 /* check for PBM or PGM or PPM */
359 if (buffer[0] == 'P' && (buffer[1] > '0' && buffer[1] < '7') && (buffer[2] == 0x0a || buffer[2] == 0x20 || buffer[2] == 0x09 || buffer[2] == 0x0d))
360 return IM_PPM;
362 /* check for JPEG */
363 if (buffer[0] == 0xff && buffer[1] == 0xd8)
364 return IM_JPEG;
366 /* check for GIF */
367 if (buffer[0] == 'G' && buffer[1] == 'I' && buffer[2] == 'F' && buffer[3] == '8' &&
368 (buffer[4] == '7' || buffer[4] == '9') && buffer[5] == 'a')
369 return IM_GIF;
371 /* check for WEBP */
372 if (buffer[0] == 'R' && buffer[1] == 'I' && buffer[2] == 'F' && buffer[3] == 'F' &&
373 buffer[8] == 'W' && buffer[9] == 'E' && buffer[10] == 'B' && buffer[11] == 'P' &&
374 buffer[12] == 'V' && buffer[13] == 'P' && buffer[14] == '8' &&
375 (buffer[15] == ' ' /* Simple File Format (Lossy) */
376 || buffer[15] == 'L' /* Simple File Format (Lossless) */
377 || buffer[15] == 'X')) /* Extended File Format */
378 return IM_WEBP;
380 return IM_UNKNOWN;