Update Serbian translation from master branch
[wmaker-crm.git] / wrlib / load.c
blob1f94f0e426f75d6aa2be4b73b4710fe683e4c6f0
1 /* load.c - load image from file
3 * Raster graphics library
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 2014-2021 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"
39 #include "wr_i18n.h"
42 typedef struct RCachedImage {
43 RImage *image;
44 char *file;
45 time_t last_modif; /* last time file was modified */
46 time_t last_use; /* last time image was used */
47 } RCachedImage;
50 * Number of image to keep in the cache
52 static int RImageCacheSize = -1;
54 #define IMAGE_CACHE_DEFAULT_NBENTRIES 8
55 #define IMAGE_CACHE_MAXIMUM_NBENTRIES 256
58 * Max. size of image (in pixels) to store in the cache
60 static int RImageCacheMaxImage = -1; /* 0 = any size */
62 #define IMAGE_CACHE_DEFAULT_MAXPIXELS (64 * 64)
63 #define IMAGE_CACHE_MAXIMUM_MAXPIXELS (256 * 256)
66 static RCachedImage *RImageCache;
69 static WRImgFormat identFile(const char *path);
72 char **RSupportedFileFormats(void)
74 static char *tmp[IM_TYPES + 2];
75 int i = 0;
77 /* built-in */
78 tmp[i++] = "XPM";
79 /* built-in PNM here refers to anymap format: PPM, PGM, PBM */
80 tmp[i++] = "PNM";
82 * PPM is a just a sub-type of PNM, but it has to be in the list
83 * for compatibility with legacy programs that may expect it but
84 * not the new PNM type
86 tmp[i++] = "PPM";
87 #ifdef USE_TIFF
88 tmp[i++] = "TIFF";
89 #endif
90 #ifdef USE_PNG
91 tmp[i++] = "PNG";
92 #endif
93 #ifdef USE_JPEG
94 tmp[i++] = "JPEG";
95 #endif
96 #ifdef USE_GIF
97 tmp[i++] = "GIF";
98 #endif
99 #ifdef USE_WEBP
100 tmp[i++] = "WEBP";
101 #endif
102 tmp[i] = NULL;
104 return tmp;
107 static void init_cache(void)
109 char *tmp;
111 tmp = getenv("RIMAGE_CACHE");
112 if (!tmp || sscanf(tmp, "%i", &RImageCacheSize) != 1)
113 RImageCacheSize = IMAGE_CACHE_DEFAULT_NBENTRIES;
114 if (RImageCacheSize < 0)
115 RImageCacheSize = 0;
116 if (RImageCacheSize > IMAGE_CACHE_MAXIMUM_NBENTRIES)
117 RImageCacheSize = IMAGE_CACHE_MAXIMUM_NBENTRIES;
119 tmp = getenv("RIMAGE_CACHE_SIZE");
120 if (!tmp || sscanf(tmp, "%i", &RImageCacheMaxImage) != 1)
121 RImageCacheMaxImage = IMAGE_CACHE_DEFAULT_MAXPIXELS;
122 if (RImageCacheMaxImage < 0)
123 RImageCacheMaxImage = 0;
124 if (RImageCacheMaxImage > IMAGE_CACHE_MAXIMUM_MAXPIXELS)
125 RImageCacheMaxImage = IMAGE_CACHE_MAXIMUM_MAXPIXELS;
127 if (RImageCacheSize > 0) {
128 RImageCache = malloc(sizeof(RCachedImage) * RImageCacheSize);
129 if (RImageCache == NULL) {
130 fprintf(stderr, _("wrlib: out of memory for image cache\n"));
131 return;
133 memset(RImageCache, 0, sizeof(RCachedImage) * RImageCacheSize);
137 void RReleaseCache(void)
139 int i;
141 if (RImageCacheSize > 0) {
142 for (i = 0; i < RImageCacheSize; i++) {
143 if (RImageCache[i].file) {
144 RReleaseImage(RImageCache[i].image);
145 free(RImageCache[i].file);
148 free(RImageCache);
149 RImageCache = NULL;
150 RImageCacheSize = -1;
154 RImage *RLoadImage(RContext *context, const char *file, int index)
156 RImage *image = NULL;
157 int i;
158 struct stat st;
160 assert(file != NULL);
162 if (RImageCacheSize < 0)
163 init_cache();
165 if (RImageCacheSize > 0) {
167 for (i = 0; i < RImageCacheSize; i++) {
168 if (RImageCache[i].file && strcmp(file, RImageCache[i].file) == 0) {
170 if (stat(file, &st) == 0 && st.st_mtime == RImageCache[i].last_modif) {
171 RImageCache[i].last_use = time(NULL);
173 return RCloneImage(RImageCache[i].image);
175 } else {
176 free(RImageCache[i].file);
177 RImageCache[i].file = NULL;
178 RReleaseImage(RImageCache[i].image);
184 switch (identFile(file)) {
185 case IM_ERROR:
186 return NULL;
188 case IM_UNKNOWN:
189 #ifdef USE_MAGICK
190 /* generic file format support using ImageMagick
191 * BMP, PCX, PICT, SVG, ...
193 image = RLoadMagick(file);
194 break;
195 #else
196 RErrorCode = RERR_BADFORMAT;
197 return NULL;
198 #endif
200 case IM_XPM:
201 image = RLoadXPM(context, file);
202 break;
204 #ifdef USE_TIFF
205 case IM_TIFF:
206 image = RLoadTIFF(file, index);
207 break;
208 #endif /* USE_TIFF */
210 #ifdef USE_PNG
211 case IM_PNG:
212 image = RLoadPNG(context, file);
213 break;
214 #endif /* USE_PNG */
216 #ifdef USE_JPEG
217 case IM_JPEG:
218 image = RLoadJPEG(file);
219 break;
220 #endif /* USE_JPEG */
222 #ifdef USE_GIF
223 case IM_GIF:
224 image = RLoadGIF(file, index);
225 break;
226 #endif /* USE_GIF */
228 #ifdef USE_WEBP
229 case IM_WEBP:
230 image = RLoadWEBP(file);
231 break;
232 #endif /* USE_WEBP */
234 case IM_PPM:
235 image = RLoadPPM(file);
236 break;
238 default:
239 RErrorCode = RERR_BADFORMAT;
240 return NULL;
243 /* store image in cache */
244 if (RImageCacheSize > 0 && image &&
245 (RImageCacheMaxImage == 0 || RImageCacheMaxImage >= image->width * image->height)) {
246 time_t oldest = time(NULL);
247 int oldest_idx = 0;
248 int done = 0;
250 if (stat(file, &st) != 0) {
251 /* If we can't get the info, at least use a valid time to reduce risk of problems */
252 st.st_mtime = oldest;
255 for (i = 0; i < RImageCacheSize; i++) {
256 if (!RImageCache[i].file) {
257 RImageCache[i].file = malloc(strlen(file) + 1);
258 strcpy(RImageCache[i].file, file);
259 RImageCache[i].image = RCloneImage(image);
260 RImageCache[i].last_modif = st.st_mtime;
261 RImageCache[i].last_use = time(NULL);
262 done = 1;
263 break;
264 } else {
265 if (oldest > RImageCache[i].last_use) {
266 oldest = RImageCache[i].last_use;
267 oldest_idx = i;
272 /* if no slot available, dump least recently used one */
273 if (!done) {
274 free(RImageCache[oldest_idx].file);
275 RReleaseImage(RImageCache[oldest_idx].image);
276 RImageCache[oldest_idx].file = malloc(strlen(file) + 1);
277 strcpy(RImageCache[oldest_idx].file, file);
278 RImageCache[oldest_idx].image = RCloneImage(image);
279 RImageCache[oldest_idx].last_modif = st.st_mtime;
280 RImageCache[oldest_idx].last_use = time(NULL);
284 return image;
287 char *RGetImageFileFormat(const char *file)
289 switch (identFile(file)) {
290 case IM_XPM:
291 return "XPM";
293 #ifdef USE_TIFF
294 case IM_TIFF:
295 return "TIFF";
296 #endif /* USE_TIFF */
298 #ifdef USE_PNG
299 case IM_PNG:
300 return "PNG";
301 #endif /* USE_PNG */
303 #ifdef USE_JPEG
304 case IM_JPEG:
305 return "JPEG";
306 #endif /* USE_JPEG */
308 #ifdef USE_GIF
309 case IM_GIF:
310 return "GIF";
311 #endif /* USE_GIF */
313 #ifdef USE_WEBP
314 case IM_WEBP:
315 return "WEBP";
316 #endif /* USE_WEBP */
318 case IM_PPM:
319 return "PPM";
321 default:
322 return NULL;
326 static WRImgFormat identFile(const char *path)
328 FILE *file;
329 unsigned char buffer[32];
330 size_t nread;
332 assert(path != NULL);
334 for (;;) {
335 file = fopen(path, "rb");
336 if (file != NULL)
337 break;
338 if (errno != EINTR) {
339 RErrorCode = RERR_OPEN;
340 return IM_ERROR;
344 nread = fread(buffer, 1, sizeof(buffer), file);
345 if (nread < sizeof(buffer) || ferror(file)) {
346 fclose(file);
347 RErrorCode = RERR_READ;
348 return IM_ERROR;
350 fclose(file);
352 /* check for XPM */
353 if (strncmp((char *)buffer, "/* XPM */", 9) == 0)
354 return IM_XPM;
356 /* check for TIFF */
357 if ((buffer[0] == 'I' && buffer[1] == 'I' && buffer[2] == '*' && buffer[3] == 0)
358 || (buffer[0] == 'M' && buffer[1] == 'M' && buffer[2] == 0 && buffer[3] == '*'))
359 return IM_TIFF;
362 * check for PNG
364 * The signature is defined in the PNG specifiation:
365 * http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html
366 * it is valid for v1.0, v1.1, v1.2 and ISO version
368 if (buffer[0] == 137 && buffer[1] == 80 && buffer[2] == 78 && buffer[3] == 71 &&
369 buffer[4] == 13 && buffer[5] == 10 && buffer[6] == 26 && buffer[7] == 10)
370 return IM_PNG;
372 /* check for PBM or PGM or PPM */
373 if (buffer[0] == 'P' && (buffer[1] > '0' && buffer[1] < '7') && (buffer[2] == 0x0a || buffer[2] == 0x20 || buffer[2] == 0x09 || buffer[2] == 0x0d))
374 return IM_PPM;
376 /* check for JPEG */
377 if (buffer[0] == 0xff && buffer[1] == 0xd8)
378 return IM_JPEG;
380 /* check for GIF */
381 if (buffer[0] == 'G' && buffer[1] == 'I' && buffer[2] == 'F' && buffer[3] == '8' &&
382 (buffer[4] == '7' || buffer[4] == '9') && buffer[5] == 'a')
383 return IM_GIF;
385 /* check for WEBP */
386 if (buffer[0] == 'R' && buffer[1] == 'I' && buffer[2] == 'F' && buffer[3] == 'F' &&
387 buffer[8] == 'W' && buffer[9] == 'E' && buffer[10] == 'B' && buffer[11] == 'P' &&
388 buffer[12] == 'V' && buffer[13] == 'P' && buffer[14] == '8' &&
389 (buffer[15] == ' ' /* Simple File Format (Lossy) */
390 || buffer[15] == 'L' /* Simple File Format (Lossless) */
391 || buffer[15] == 'X')) /* Extended File Format */
392 return IM_WEBP;
394 return IM_UNKNOWN;