wmaker: Adding copyright year 2017
[wmaker-crm.git] / wrlib / load.c
blobfb99876d569cd7841879e46f3862958deab10b0d
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 * Number of image to keep in the cache
51 static int RImageCacheSize = -1;
53 #define IMAGE_CACHE_DEFAULT_NBENTRIES 8
54 #define IMAGE_CACHE_MAXIMUM_NBENTRIES 256
57 * Max. size of image (in pixels) to store in the cache
59 static int RImageCacheMaxImage = -1; /* 0 = any size */
61 #define IMAGE_CACHE_DEFAULT_MAXPIXELS (64 * 64)
62 #define IMAGE_CACHE_MAXIMUM_MAXPIXELS (256 * 256)
65 static RCachedImage *RImageCache;
68 static WRImgFormat identFile(const char *path);
71 char **RSupportedFileFormats(void)
73 static char *tmp[IM_TYPES + 2];
74 int i = 0;
76 /* built-in */
77 tmp[i++] = "XPM";
78 /* built-in PNM here refers to anymap format: PPM, PGM, PBM */
79 tmp[i++] = "PNM";
81 * PPM is a just a sub-type of PNM, but it has to be in the list
82 * for compatibility with legacy programs that may expect it but
83 * not the new PNM type
85 tmp[i++] = "PPM";
86 #ifdef USE_TIFF
87 tmp[i++] = "TIFF";
88 #endif
89 #ifdef USE_PNG
90 tmp[i++] = "PNG";
91 #endif
92 #ifdef USE_JPEG
93 tmp[i++] = "JPEG";
94 #endif
95 #ifdef USE_GIF
96 tmp[i++] = "GIF";
97 #endif
98 #ifdef USE_WEBP
99 tmp[i++] = "WEBP";
100 #endif
101 tmp[i] = NULL;
103 return tmp;
106 static void init_cache(void)
108 char *tmp;
110 tmp = getenv("RIMAGE_CACHE");
111 if (!tmp || sscanf(tmp, "%i", &RImageCacheSize) != 1)
112 RImageCacheSize = IMAGE_CACHE_DEFAULT_NBENTRIES;
113 if (RImageCacheSize < 0)
114 RImageCacheSize = 0;
115 if (RImageCacheSize > IMAGE_CACHE_MAXIMUM_NBENTRIES)
116 RImageCacheSize = IMAGE_CACHE_MAXIMUM_NBENTRIES;
118 tmp = getenv("RIMAGE_CACHE_SIZE");
119 if (!tmp || sscanf(tmp, "%i", &RImageCacheMaxImage) != 1)
120 RImageCacheMaxImage = IMAGE_CACHE_DEFAULT_MAXPIXELS;
121 if (RImageCacheMaxImage < 0)
122 RImageCacheMaxImage = 0;
123 if (RImageCacheMaxImage > IMAGE_CACHE_MAXIMUM_MAXPIXELS)
124 RImageCacheMaxImage = IMAGE_CACHE_MAXIMUM_MAXPIXELS;
126 if (RImageCacheSize > 0) {
127 RImageCache = malloc(sizeof(RCachedImage) * RImageCacheSize);
128 if (RImageCache == NULL) {
129 printf("wrlib: out of memory for image cache\n");
130 return;
132 memset(RImageCache, 0, sizeof(RCachedImage) * RImageCacheSize);
136 void RReleaseCache(void)
138 int i;
140 if (RImageCacheSize > 0) {
141 for (i = 0; i < RImageCacheSize; i++) {
142 if (RImageCache[i].file) {
143 RReleaseImage(RImageCache[i].image);
144 free(RImageCache[i].file);
147 free(RImageCache);
148 RImageCache = NULL;
149 RImageCacheSize = -1;
153 RImage *RLoadImage(RContext *context, const char *file, int index)
155 RImage *image = NULL;
156 int i;
157 struct stat st;
159 assert(file != NULL);
161 if (RImageCacheSize < 0)
162 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 #ifdef USE_MAGICK
189 /* generic file format support using ImageMagick
190 * BMP, PCX, PICT, SVG, ...
192 image = RLoadMagick(file);
193 break;
194 #else
195 RErrorCode = RERR_BADFORMAT;
196 return NULL;
197 #endif
199 case IM_XPM:
200 image = RLoadXPM(context, file);
201 break;
203 #ifdef USE_TIFF
204 case IM_TIFF:
205 image = RLoadTIFF(file, index);
206 break;
207 #endif /* USE_TIFF */
209 #ifdef USE_PNG
210 case IM_PNG:
211 image = RLoadPNG(context, file);
212 break;
213 #endif /* USE_PNG */
215 #ifdef USE_JPEG
216 case IM_JPEG:
217 image = RLoadJPEG(file);
218 break;
219 #endif /* USE_JPEG */
221 #ifdef USE_GIF
222 case IM_GIF:
223 image = RLoadGIF(file, index);
224 break;
225 #endif /* USE_GIF */
227 #ifdef USE_WEBP
228 case IM_WEBP:
229 image = RLoadWEBP(file);
230 break;
231 #endif /* USE_WEBP */
233 case IM_PPM:
234 image = RLoadPPM(file);
235 break;
237 default:
238 RErrorCode = RERR_BADFORMAT;
239 return NULL;
242 /* store image in cache */
243 if (RImageCacheSize > 0 && image &&
244 (RImageCacheMaxImage == 0 || RImageCacheMaxImage >= image->width * image->height)) {
245 time_t oldest = time(NULL);
246 int oldest_idx = 0;
247 int done = 0;
249 for (i = 0; i < RImageCacheSize; i++) {
250 if (!RImageCache[i].file) {
251 RImageCache[i].file = malloc(strlen(file) + 1);
252 strcpy(RImageCache[i].file, file);
253 RImageCache[i].image = RCloneImage(image);
254 RImageCache[i].last_modif = st.st_mtime;
255 RImageCache[i].last_use = time(NULL);
256 done = 1;
257 break;
258 } else {
259 if (oldest > RImageCache[i].last_use) {
260 oldest = RImageCache[i].last_use;
261 oldest_idx = i;
266 /* if no slot available, dump least recently used one */
267 if (!done) {
268 free(RImageCache[oldest_idx].file);
269 RReleaseImage(RImageCache[oldest_idx].image);
270 RImageCache[oldest_idx].file = malloc(strlen(file) + 1);
271 strcpy(RImageCache[oldest_idx].file, file);
272 RImageCache[oldest_idx].image = RCloneImage(image);
273 RImageCache[oldest_idx].last_modif = st.st_mtime;
274 RImageCache[oldest_idx].last_use = time(NULL);
278 return image;
281 char *RGetImageFileFormat(const char *file)
283 switch (identFile(file)) {
284 case IM_XPM:
285 return "XPM";
287 #ifdef USE_TIFF
288 case IM_TIFF:
289 return "TIFF";
290 #endif /* USE_TIFF */
292 #ifdef USE_PNG
293 case IM_PNG:
294 return "PNG";
295 #endif /* USE_PNG */
297 #ifdef USE_JPEG
298 case IM_JPEG:
299 return "JPEG";
300 #endif /* USE_JPEG */
302 #ifdef USE_GIF
303 case IM_GIF:
304 return "GIF";
305 #endif /* USE_GIF */
307 #ifdef USE_WEBP
308 case IM_WEBP:
309 return "WEBP";
310 #endif /* USE_WEBP */
312 case IM_PPM:
313 return "PPM";
315 default:
316 return NULL;
320 static WRImgFormat identFile(const char *path)
322 FILE *file;
323 unsigned char buffer[32];
324 size_t nread;
326 assert(path != NULL);
328 for (;;) {
329 file = fopen(path, "rb");
330 if (file != NULL)
331 break;
332 if (errno != EINTR) {
333 RErrorCode = RERR_OPEN;
334 return IM_ERROR;
338 nread = fread(buffer, 1, sizeof(buffer), file);
339 if (nread < sizeof(buffer) || ferror(file)) {
340 fclose(file);
341 RErrorCode = RERR_READ;
342 return IM_ERROR;
344 fclose(file);
346 /* check for XPM */
347 if (strncmp((char *)buffer, "/* XPM */", 9) == 0)
348 return IM_XPM;
350 /* check for TIFF */
351 if ((buffer[0] == 'I' && buffer[1] == 'I' && buffer[2] == '*' && buffer[3] == 0)
352 || (buffer[0] == 'M' && buffer[1] == 'M' && buffer[2] == 0 && buffer[3] == '*'))
353 return IM_TIFF;
356 * check for PNG
358 * The signature is defined in the PNG specifiation:
359 * http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html
360 * it is valid for v1.0, v1.1, v1.2 and ISO version
362 if (buffer[0] == 137 && buffer[1] == 80 && buffer[2] == 78 && buffer[3] == 71 &&
363 buffer[4] == 13 && buffer[5] == 10 && buffer[6] == 26 && buffer[7] == 10)
364 return IM_PNG;
366 /* check for PBM or PGM or PPM */
367 if (buffer[0] == 'P' && (buffer[1] > '0' && buffer[1] < '7') && (buffer[2] == 0x0a || buffer[2] == 0x20 || buffer[2] == 0x09 || buffer[2] == 0x0d))
368 return IM_PPM;
370 /* check for JPEG */
371 if (buffer[0] == 0xff && buffer[1] == 0xd8)
372 return IM_JPEG;
374 /* check for GIF */
375 if (buffer[0] == 'G' && buffer[1] == 'I' && buffer[2] == 'F' && buffer[3] == '8' &&
376 (buffer[4] == '7' || buffer[4] == '9') && buffer[5] == 'a')
377 return IM_GIF;
379 /* check for WEBP */
380 if (buffer[0] == 'R' && buffer[1] == 'I' && buffer[2] == 'F' && buffer[3] == 'F' &&
381 buffer[8] == 'W' && buffer[9] == 'E' && buffer[10] == 'B' && buffer[11] == 'P' &&
382 buffer[12] == 'V' && buffer[13] == 'P' && buffer[14] == '8' &&
383 (buffer[15] == ' ' /* Simple File Format (Lossy) */
384 || buffer[15] == 'L' /* Simple File Format (Lossless) */
385 || buffer[15] == 'X')) /* Extended File Format */
386 return IM_WEBP;
388 return IM_UNKNOWN;