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 void RReleaseCache(void)
136 if (RImageCacheSize
> 0) {
137 for (i
= 0; i
< RImageCacheSize
; i
++) {
138 if (RImageCache
[i
].file
) {
139 RReleaseImage(RImageCache
[i
].image
);
140 free(RImageCache
[i
].file
);
145 RImageCacheSize
= -1;
149 RImage
*RLoadImage(RContext
* context
, const char *file
, int index
)
151 RImage
*image
= NULL
;
155 assert(file
!= NULL
);
157 if (RImageCacheSize
< 0) {
161 if (RImageCacheSize
> 0) {
163 for (i
= 0; i
< RImageCacheSize
; i
++) {
164 if (RImageCache
[i
].file
&& strcmp(file
, RImageCache
[i
].file
) == 0) {
166 if (stat(file
, &st
) == 0 && st
.st_mtime
== RImageCache
[i
].last_modif
) {
167 RImageCache
[i
].last_use
= time(NULL
);
169 return RCloneImage(RImageCache
[i
].image
);
172 free(RImageCache
[i
].file
);
173 RImageCache
[i
].file
= NULL
;
174 RReleaseImage(RImageCache
[i
].image
);
180 switch (identFile(file
)) {
186 /* generic file format support using ImageMagick
187 * BMP, PCX, PICT, SVG, ...
189 image
= RLoadMagick(file
);
192 RErrorCode
= RERR_BADFORMAT
;
197 image
= RLoadXPM(context
, file
);
202 image
= RLoadTIFF(file
, index
);
204 #endif /* USE_TIFF */
208 image
= RLoadPNG(context
, file
);
214 image
= RLoadJPEG(file
);
216 #endif /* USE_JPEG */
220 image
= RLoadGIF(file
, index
);
226 image
= RLoadWEBP(file
);
228 #endif /* USE_WEBP */
231 image
= RLoadPPM(file
);
235 RErrorCode
= RERR_BADFORMAT
;
239 /* store image in cache */
240 if (RImageCacheSize
> 0 && image
&&
241 (RImageCacheMaxImage
== 0 || RImageCacheMaxImage
>= image
->width
* image
->height
)) {
242 time_t oldest
= time(NULL
);
246 for (i
= 0; i
< RImageCacheSize
; i
++) {
247 if (!RImageCache
[i
].file
) {
248 RImageCache
[i
].file
= malloc(strlen(file
) + 1);
249 strcpy(RImageCache
[i
].file
, file
);
250 RImageCache
[i
].image
= RCloneImage(image
);
251 RImageCache
[i
].last_modif
= st
.st_mtime
;
252 RImageCache
[i
].last_use
= time(NULL
);
256 if (oldest
> RImageCache
[i
].last_use
) {
257 oldest
= RImageCache
[i
].last_use
;
263 /* if no slot available, dump least recently used one */
265 free(RImageCache
[oldest_idx
].file
);
266 RReleaseImage(RImageCache
[oldest_idx
].image
);
267 RImageCache
[oldest_idx
].file
= malloc(strlen(file
) + 1);
268 strcpy(RImageCache
[oldest_idx
].file
, file
);
269 RImageCache
[oldest_idx
].image
= RCloneImage(image
);
270 RImageCache
[oldest_idx
].last_modif
= st
.st_mtime
;
271 RImageCache
[oldest_idx
].last_use
= time(NULL
);
278 char *RGetImageFileFormat(const char *file
)
280 switch (identFile(file
)) {
287 #endif /* USE_TIFF */
297 #endif /* USE_JPEG */
307 #endif /* USE_WEBP */
317 static WRImgFormat
identFile(const char *path
)
320 unsigned char buffer
[32];
323 assert(path
!= NULL
);
325 RETRY( file
= fopen(path
, "rb") )
327 RErrorCode
= RERR_OPEN
;
331 RETRY( nread
= fread(buffer
, 1, sizeof(buffer
), file
) )
332 if (nread
< sizeof(buffer
) || ferror(file
)) {
333 RETRY( fclose(file
) )
334 RErrorCode
= RERR_READ
;
337 RETRY( fclose(file
) )
340 if (strncmp((char *)buffer
, "/* XPM */", 9) == 0)
344 if ((buffer
[0] == 'I' && buffer
[1] == 'I' && buffer
[2] == '*' && buffer
[3] == 0)
345 || (buffer
[0] == 'M' && buffer
[1] == 'M' && buffer
[2] == 0 && buffer
[3] == '*'))
351 * The signature is defined in the PNG specifiation:
352 * http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html
353 * it is valid for v1.0, v1.1, v1.2 and ISO version
355 if (buffer
[0] == 137 && buffer
[1] == 80 && buffer
[2] == 78 && buffer
[3] == 71 &&
356 buffer
[4] == 13 && buffer
[5] == 10 && buffer
[6] == 26 && buffer
[7] == 10)
359 /* check for PBM or PGM or PPM */
360 if (buffer
[0] == 'P' && (buffer
[1] > '0' && buffer
[1] < '7') && (buffer
[2] == 0x0a || buffer
[2] == 0x20 || buffer
[2] == 0x09 || buffer
[2] == 0x0d))
364 if (buffer
[0] == 0xff && buffer
[1] == 0xd8)
368 if (buffer
[0] == 'G' && buffer
[1] == 'I' && buffer
[2] == 'F' && buffer
[3] == '8' &&
369 (buffer
[4] == '7' || buffer
[4] == '9') && buffer
[5] == 'a')
373 if (buffer
[ 0] == 'R' && buffer
[ 1] == 'I' && buffer
[ 2] == 'F' && buffer
[ 3] == 'F' &&
374 buffer
[ 8] == 'W' && buffer
[ 9] == 'E' && buffer
[10] == 'B' && buffer
[11] == 'P' &&
375 buffer
[12] == 'V' && buffer
[13] == 'P' && buffer
[14] == '8' &&
376 (buffer
[15] == ' ' /* Simple File Format (Lossy) */
377 || buffer
[15] == 'L' /* Simple File Format (Lossless) */
378 || buffer
[15] == 'X')) /* Extended File Format */