Add OPEN_PLMENU option to parse command generated proplist style menus
[wmaker-crm.git] / wrlib / load.c
blobd3ae8ad18b2c51e0f861ddf7419a6a3e3a127ce3
1 /* load.c - load image from file
3 * Raster graphics library
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
23 #include <config.h>
25 #include <errno.h>
26 #include <X11/Xlib.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <sys/stat.h>
32 #include <string.h>
33 #include <time.h>
34 #include <assert.h>
36 #ifdef USE_PNG
37 #include <png.h>
38 #endif
40 #include "wraster.h"
42 #define RETRY( x ) do { \
43 x; \
44 } while (errno == EINTR);
46 typedef struct RCachedImage {
47 RImage *image;
48 char *file;
49 time_t last_modif; /* last time file was modified */
50 time_t last_use; /* last time image was used */
51 } RCachedImage;
54 * Size of image cache
56 static int RImageCacheSize = -1;
59 * Max. size of image to store in cache
61 static int RImageCacheMaxImage = -1; /* 0 = any size */
63 #define IMAGE_CACHE_SIZE 8
65 #define IMAGE_CACHE_MAX_IMAGE 64*64
67 static RCachedImage *RImageCache;
69 #define IM_ERROR -1
70 #define IM_UNKNOWN 0
71 #define IM_XPM 1
72 #define IM_TIFF 2
73 #define IM_PNG 3
74 #define IM_PPM 4
75 #define IM_JPEG 5
76 #define IM_GIF 6
77 /* How many image types do we have. */
78 /* Increase this when adding new image types! */
79 #define IM_TYPES 6
81 static int identFile(char *path);
83 extern RImage *RLoadPPM(char *file_name);
85 extern RImage *RLoadXPM(RContext * context, char *file);
87 #ifdef USE_TIFF
88 extern RImage *RLoadTIFF(char *file, int index);
89 #endif
90 #ifdef USE_PNG
91 extern RImage *RLoadPNG(RContext * context, char *file);
92 #endif
93 #ifdef USE_JPEG
94 extern RImage *RLoadJPEG(RContext * context, char *file_name);
95 #endif
96 #ifdef USE_GIF
97 extern RImage *RLoadGIF(char *file_name, int index);
98 #endif
100 char **RSupportedFileFormats(void)
102 static char *tmp[IM_TYPES + 1];
103 int i = 0;
105 /* built-in */
106 tmp[i++] = "XPM";
107 /* built-in */
108 tmp[i++] = "PPM";
109 #ifdef USE_TIFF
110 tmp[i++] = "TIFF";
111 #endif
112 #ifdef USE_PNG
113 tmp[i++] = "PNG";
114 #endif
115 #ifdef USE_JPEG
116 tmp[i++] = "JPEG";
117 #endif
118 #ifdef USE_GIF
119 tmp[i++] = "GIF";
120 #endif
121 tmp[i] = NULL;
123 return tmp;
126 static void init_cache()
128 char *tmp;
130 tmp = getenv("RIMAGE_CACHE");
131 if (!tmp || sscanf(tmp, "%i", &RImageCacheSize) != 1) {
132 RImageCacheSize = IMAGE_CACHE_SIZE;
134 if (RImageCacheSize < 0)
135 RImageCacheSize = 0;
137 tmp = getenv("RIMAGE_CACHE_SIZE");
138 if (!tmp || sscanf(tmp, "%i", &RImageCacheMaxImage) != 1) {
139 RImageCacheMaxImage = IMAGE_CACHE_MAX_IMAGE;
142 if (RImageCacheSize > 0) {
143 RImageCache = malloc(sizeof(RCachedImage) * RImageCacheSize);
144 if (RImageCache == NULL) {
145 printf("wrlib: out of memory for image cache\n");
146 return;
148 memset(RImageCache, 0, sizeof(RCachedImage) * RImageCacheSize);
152 RImage *RLoadImage(RContext * context, char *file, int index)
154 RImage *image = NULL;
155 int i;
156 struct stat st;
158 assert(file != NULL);
160 if (RImageCacheSize < 0) {
161 init_cache();
164 if (RImageCacheSize > 0) {
166 for (i = 0; i < RImageCacheSize; i++) {
167 if (RImageCache[i].file && strcmp(file, RImageCache[i].file) == 0) {
169 if (stat(file, &st) == 0 && st.st_mtime == RImageCache[i].last_modif) {
170 RImageCache[i].last_use = time(NULL);
172 return RCloneImage(RImageCache[i].image);
174 } else {
175 free(RImageCache[i].file);
176 RImageCache[i].file = NULL;
177 RReleaseImage(RImageCache[i].image);
183 switch (identFile(file)) {
184 case IM_ERROR:
185 return NULL;
187 case IM_UNKNOWN:
188 RErrorCode = RERR_BADFORMAT;
189 return NULL;
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(context, 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 case IM_PPM:
220 image = RLoadPPM(file);
221 break;
223 default:
224 RErrorCode = RERR_BADFORMAT;
225 return NULL;
228 /* store image in cache */
229 if (RImageCacheSize > 0 && image &&
230 (RImageCacheMaxImage == 0 || RImageCacheMaxImage >= image->width * image->height)) {
231 time_t oldest = time(NULL);
232 int oldest_idx = 0;
233 int done = 0;
235 for (i = 0; i < RImageCacheSize; i++) {
236 if (!RImageCache[i].file) {
237 RImageCache[i].file = malloc(strlen(file) + 1);
238 strcpy(RImageCache[i].file, file);
239 RImageCache[i].image = RCloneImage(image);
240 RImageCache[i].last_modif = st.st_mtime;
241 RImageCache[i].last_use = time(NULL);
242 done = 1;
243 break;
244 } else {
245 if (oldest > RImageCache[i].last_use) {
246 oldest = RImageCache[i].last_use;
247 oldest_idx = i;
252 /* if no slot available, dump least recently used one */
253 if (!done) {
254 free(RImageCache[oldest_idx].file);
255 RReleaseImage(RImageCache[oldest_idx].image);
256 RImageCache[oldest_idx].file = malloc(strlen(file) + 1);
257 strcpy(RImageCache[oldest_idx].file, file);
258 RImageCache[oldest_idx].image = RCloneImage(image);
259 RImageCache[oldest_idx].last_modif = st.st_mtime;
260 RImageCache[oldest_idx].last_use = time(NULL);
264 return image;
267 char *RGetImageFileFormat(char *file)
269 switch (identFile(file)) {
270 case IM_XPM:
271 return "XPM";
273 #ifdef USE_TIFF
274 case IM_TIFF:
275 return "TIFF";
276 #endif /* USE_TIFF */
278 #ifdef USE_PNG
279 case IM_PNG:
280 return "PNG";
281 #endif /* USE_PNG */
283 #ifdef USE_JPEG
284 case IM_JPEG:
285 return "JPEG";
286 #endif /* USE_JPEG */
288 #ifdef USE_GIF
289 case IM_GIF:
290 return "GIF";
291 #endif /* USE_GIF */
293 case IM_PPM:
294 return "PPM";
296 default:
297 return NULL;
301 static int identFile(char *path)
303 FILE *file;
304 unsigned char buffer[32];
305 size_t nread;
307 assert(path != NULL);
309 RETRY( file = fopen(path, "rb") )
310 if (file == NULL) {
311 RErrorCode = RERR_OPEN;
312 return IM_ERROR;
315 RETRY( nread = fread(buffer, 1, sizeof(buffer), file) )
316 if (nread < sizeof(buffer) || ferror(file)) {
317 RETRY( fclose(file) )
318 RErrorCode = RERR_READ;
319 return IM_ERROR;
321 RETRY( fclose(file) )
323 /* check for XPM */
324 if (strncmp((char *)buffer, "/* XPM */", 9) == 0)
325 return IM_XPM;
327 /* check for TIFF */
328 if ((buffer[0] == 'I' && buffer[1] == 'I' && buffer[2] == '*' && buffer[3] == 0)
329 || (buffer[0] == 'M' && buffer[1] == 'M' && buffer[2] == 0 && buffer[3] == '*'))
330 return IM_TIFF;
332 #ifdef USE_PNG
333 /* check for PNG */
334 if (!png_sig_cmp(buffer, 0, 8))
335 return IM_PNG;
336 #endif
338 /* check for raw PPM or PGM */
339 if (buffer[0] == 'P' && (buffer[1] == '5' || buffer[1] == '6'))
340 return IM_PPM;
342 /* check for JPEG */
343 if (buffer[0] == 0xff && buffer[1] == 0xd8)
344 return IM_JPEG;
346 /* check for GIF */
347 if (buffer[0] == 'G' && buffer[1] == 'I' && buffer[2] == 'F')
348 return IM_GIF;
350 return IM_UNKNOWN;