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,
38 #include "imgformat.h"
40 #define RETRY( x ) do { \
42 } while (errno == EINTR);
44 typedef struct RCachedImage
{
47 time_t last_modif
; /* last time file was modified */
48 time_t last_use
; /* last time image was used */
54 static int RImageCacheSize
= -1;
57 * Max. size of image to store in cache
59 static int RImageCacheMaxImage
= -1; /* 0 = any size */
61 #define IMAGE_CACHE_SIZE 8
63 #define IMAGE_CACHE_MAX_IMAGE 64*64
65 static RCachedImage
*RImageCache
;
68 static WRImgFormat
identFile(const char *path
);
71 char **RSupportedFileFormats(void)
73 static char *tmp
[IM_TYPES
+ 2];
78 /* built-in PNM here refers to anymap format: PPM, PGM, PBM */
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
106 static void init_cache(void)
110 tmp
= getenv("RIMAGE_CACHE");
111 if (!tmp
|| sscanf(tmp
, "%i", &RImageCacheSize
) != 1) {
112 RImageCacheSize
= IMAGE_CACHE_SIZE
;
114 if (RImageCacheSize
< 0)
117 tmp
= getenv("RIMAGE_CACHE_SIZE");
118 if (!tmp
|| sscanf(tmp
, "%i", &RImageCacheMaxImage
) != 1) {
119 RImageCacheMaxImage
= IMAGE_CACHE_MAX_IMAGE
;
122 if (RImageCacheSize
> 0) {
123 RImageCache
= malloc(sizeof(RCachedImage
) * RImageCacheSize
);
124 if (RImageCache
== NULL
) {
125 printf("wrlib: out of memory for image cache\n");
128 memset(RImageCache
, 0, sizeof(RCachedImage
) * RImageCacheSize
);
132 RImage
*RLoadImage(RContext
* context
, const char *file
, int index
)
134 RImage
*image
= NULL
;
138 assert(file
!= NULL
);
140 if (RImageCacheSize
< 0) {
144 if (RImageCacheSize
> 0) {
146 for (i
= 0; i
< RImageCacheSize
; i
++) {
147 if (RImageCache
[i
].file
&& strcmp(file
, RImageCache
[i
].file
) == 0) {
149 if (stat(file
, &st
) == 0 && st
.st_mtime
== RImageCache
[i
].last_modif
) {
150 RImageCache
[i
].last_use
= time(NULL
);
152 return RCloneImage(RImageCache
[i
].image
);
155 free(RImageCache
[i
].file
);
156 RImageCache
[i
].file
= NULL
;
157 RReleaseImage(RImageCache
[i
].image
);
163 switch (identFile(file
)) {
169 /* generic file format support using ImageMagick
170 * BMP, PCX, PICT, SVG, ...
172 image
= RLoadMagick(file
);
175 RErrorCode
= RERR_BADFORMAT
;
180 image
= RLoadXPM(context
, file
);
185 image
= RLoadTIFF(file
, index
);
187 #endif /* USE_TIFF */
191 image
= RLoadPNG(context
, file
);
197 image
= RLoadJPEG(file
);
199 #endif /* USE_JPEG */
203 image
= RLoadGIF(file
, index
);
209 image
= RLoadWEBP(file
);
211 #endif /* USE_WEBP */
214 image
= RLoadPPM(file
);
218 RErrorCode
= RERR_BADFORMAT
;
222 /* store image in cache */
223 if (RImageCacheSize
> 0 && image
&&
224 (RImageCacheMaxImage
== 0 || RImageCacheMaxImage
>= image
->width
* image
->height
)) {
225 time_t oldest
= time(NULL
);
229 for (i
= 0; i
< RImageCacheSize
; i
++) {
230 if (!RImageCache
[i
].file
) {
231 RImageCache
[i
].file
= malloc(strlen(file
) + 1);
232 strcpy(RImageCache
[i
].file
, file
);
233 RImageCache
[i
].image
= RCloneImage(image
);
234 RImageCache
[i
].last_modif
= st
.st_mtime
;
235 RImageCache
[i
].last_use
= time(NULL
);
239 if (oldest
> RImageCache
[i
].last_use
) {
240 oldest
= RImageCache
[i
].last_use
;
246 /* if no slot available, dump least recently used one */
248 free(RImageCache
[oldest_idx
].file
);
249 RReleaseImage(RImageCache
[oldest_idx
].image
);
250 RImageCache
[oldest_idx
].file
= malloc(strlen(file
) + 1);
251 strcpy(RImageCache
[oldest_idx
].file
, file
);
252 RImageCache
[oldest_idx
].image
= RCloneImage(image
);
253 RImageCache
[oldest_idx
].last_modif
= st
.st_mtime
;
254 RImageCache
[oldest_idx
].last_use
= time(NULL
);
261 char *RGetImageFileFormat(const char *file
)
263 switch (identFile(file
)) {
270 #endif /* USE_TIFF */
280 #endif /* USE_JPEG */
290 #endif /* USE_WEBP */
300 static WRImgFormat
identFile(const char *path
)
303 unsigned char buffer
[32];
306 assert(path
!= NULL
);
308 RETRY( file
= fopen(path
, "rb") )
310 RErrorCode
= RERR_OPEN
;
314 RETRY( nread
= fread(buffer
, 1, sizeof(buffer
), file
) )
315 if (nread
< sizeof(buffer
) || ferror(file
)) {
316 RETRY( fclose(file
) )
317 RErrorCode
= RERR_READ
;
320 RETRY( fclose(file
) )
323 if (strncmp((char *)buffer
, "/* XPM */", 9) == 0)
327 if ((buffer
[0] == 'I' && buffer
[1] == 'I' && buffer
[2] == '*' && buffer
[3] == 0)
328 || (buffer
[0] == 'M' && buffer
[1] == 'M' && buffer
[2] == 0 && buffer
[3] == '*'))
334 * The signature is defined in the PNG specifiation:
335 * http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html
336 * it is valid for v1.0, v1.1, v1.2 and ISO version
338 if (buffer
[0] == 137 && buffer
[1] == 80 && buffer
[2] == 78 && buffer
[3] == 71 &&
339 buffer
[4] == 13 && buffer
[5] == 10 && buffer
[6] == 26 && buffer
[7] == 10)
342 /* check for PBM or PGM or PPM */
343 if (buffer
[0] == 'P' && (buffer
[1] > '0' && buffer
[1] < '7') && (buffer
[2] == 0x0a || buffer
[2] == 0x20 || buffer
[2] == 0x09 || buffer
[2] == 0x0d))
347 if (buffer
[0] == 0xff && buffer
[1] == 0xd8)
351 if (buffer
[0] == 'G' && buffer
[1] == 'I' && buffer
[2] == 'F' && buffer
[3] == '8' &&
352 (buffer
[4] == '7' || buffer
[4] == '9') && buffer
[5] == 'a')
356 if (buffer
[ 0] == 'R' && buffer
[ 1] == 'I' && buffer
[ 2] == 'F' && buffer
[ 3] == 'F' &&
357 buffer
[ 8] == 'W' && buffer
[ 9] == 'E' && buffer
[10] == 'B' && buffer
[11] == 'P' &&
358 buffer
[12] == 'V' && buffer
[13] == 'P' && buffer
[14] == '8' &&
359 (buffer
[15] == ' ' /* Simple File Format (Lossy) */
360 || buffer
[15] == 'L' /* Simple File Format (Lossless) */
361 || buffer
[15] == 'X')) /* Extended File Format */