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"
41 typedef struct RCachedImage
{
44 time_t last_modif
; /* last time file was modified */
45 time_t last_use
; /* last time image was used */
51 static int RImageCacheSize
= -1;
54 * Max. size of image to store in cache
56 static int RImageCacheMaxImage
= -1; /* 0 = any size */
58 #define IMAGE_CACHE_SIZE 8
60 #define IMAGE_CACHE_MAX_IMAGE 64*64
62 static RCachedImage
*RImageCache
;
65 static WRImgFormat
identFile(const char *path
);
68 char **RSupportedFileFormats(void)
70 static char *tmp
[IM_TYPES
+ 2];
75 /* built-in PNM here refers to anymap format: PPM, PGM, PBM */
78 * PPM is a just a sub-type of PNM, but it has to be in the list
79 * for compatibility with legacy programs that may expect it but
80 * not the new PNM type
103 static void init_cache(void)
107 tmp
= getenv("RIMAGE_CACHE");
108 if (!tmp
|| sscanf(tmp
, "%i", &RImageCacheSize
) != 1)
109 RImageCacheSize
= IMAGE_CACHE_SIZE
;
111 if (RImageCacheSize
< 0)
114 tmp
= getenv("RIMAGE_CACHE_SIZE");
115 if (!tmp
|| sscanf(tmp
, "%i", &RImageCacheMaxImage
) != 1)
116 RImageCacheMaxImage
= IMAGE_CACHE_MAX_IMAGE
;
118 if (RImageCacheSize
> 0) {
119 RImageCache
= malloc(sizeof(RCachedImage
) * RImageCacheSize
);
120 if (RImageCache
== NULL
) {
121 printf("wrlib: out of memory for image cache\n");
124 memset(RImageCache
, 0, sizeof(RCachedImage
) * RImageCacheSize
);
128 void RReleaseCache(void)
132 if (RImageCacheSize
> 0) {
133 for (i
= 0; i
< RImageCacheSize
; i
++) {
134 if (RImageCache
[i
].file
) {
135 RReleaseImage(RImageCache
[i
].image
);
136 free(RImageCache
[i
].file
);
141 RImageCacheSize
= -1;
145 RImage
*RLoadImage(RContext
*context
, const char *file
, int index
)
147 RImage
*image
= NULL
;
151 assert(file
!= NULL
);
153 if (RImageCacheSize
< 0)
156 if (RImageCacheSize
> 0) {
158 for (i
= 0; i
< RImageCacheSize
; i
++) {
159 if (RImageCache
[i
].file
&& strcmp(file
, RImageCache
[i
].file
) == 0) {
161 if (stat(file
, &st
) == 0 && st
.st_mtime
== RImageCache
[i
].last_modif
) {
162 RImageCache
[i
].last_use
= time(NULL
);
164 return RCloneImage(RImageCache
[i
].image
);
167 free(RImageCache
[i
].file
);
168 RImageCache
[i
].file
= NULL
;
169 RReleaseImage(RImageCache
[i
].image
);
175 switch (identFile(file
)) {
181 /* generic file format support using ImageMagick
182 * BMP, PCX, PICT, SVG, ...
184 image
= RLoadMagick(file
);
187 RErrorCode
= RERR_BADFORMAT
;
192 image
= RLoadXPM(context
, file
);
197 image
= RLoadTIFF(file
, index
);
199 #endif /* USE_TIFF */
203 image
= RLoadPNG(context
, file
);
209 image
= RLoadJPEG(file
);
211 #endif /* USE_JPEG */
215 image
= RLoadGIF(file
, index
);
221 image
= RLoadWEBP(file
);
223 #endif /* USE_WEBP */
226 image
= RLoadPPM(file
);
230 RErrorCode
= RERR_BADFORMAT
;
234 /* store image in cache */
235 if (RImageCacheSize
> 0 && image
&&
236 (RImageCacheMaxImage
== 0 || RImageCacheMaxImage
>= image
->width
* image
->height
)) {
237 time_t oldest
= time(NULL
);
241 for (i
= 0; i
< RImageCacheSize
; i
++) {
242 if (!RImageCache
[i
].file
) {
243 RImageCache
[i
].file
= malloc(strlen(file
) + 1);
244 strcpy(RImageCache
[i
].file
, file
);
245 RImageCache
[i
].image
= RCloneImage(image
);
246 RImageCache
[i
].last_modif
= st
.st_mtime
;
247 RImageCache
[i
].last_use
= time(NULL
);
251 if (oldest
> RImageCache
[i
].last_use
) {
252 oldest
= RImageCache
[i
].last_use
;
258 /* if no slot available, dump least recently used one */
260 free(RImageCache
[oldest_idx
].file
);
261 RReleaseImage(RImageCache
[oldest_idx
].image
);
262 RImageCache
[oldest_idx
].file
= malloc(strlen(file
) + 1);
263 strcpy(RImageCache
[oldest_idx
].file
, file
);
264 RImageCache
[oldest_idx
].image
= RCloneImage(image
);
265 RImageCache
[oldest_idx
].last_modif
= st
.st_mtime
;
266 RImageCache
[oldest_idx
].last_use
= time(NULL
);
273 char *RGetImageFileFormat(const char *file
)
275 switch (identFile(file
)) {
282 #endif /* USE_TIFF */
292 #endif /* USE_JPEG */
302 #endif /* USE_WEBP */
312 static WRImgFormat
identFile(const char *path
)
315 unsigned char buffer
[32];
318 assert(path
!= NULL
);
321 file
= fopen(path
, "rb");
324 if (errno
!= EINTR
) {
325 RErrorCode
= RERR_OPEN
;
330 nread
= fread(buffer
, 1, sizeof(buffer
), file
);
331 if (nread
< sizeof(buffer
) || ferror(file
)) {
333 RErrorCode
= RERR_READ
;
339 if (strncmp((char *)buffer
, "/* XPM */", 9) == 0)
343 if ((buffer
[0] == 'I' && buffer
[1] == 'I' && buffer
[2] == '*' && buffer
[3] == 0)
344 || (buffer
[0] == 'M' && buffer
[1] == 'M' && buffer
[2] == 0 && buffer
[3] == '*'))
350 * The signature is defined in the PNG specifiation:
351 * http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html
352 * it is valid for v1.0, v1.1, v1.2 and ISO version
354 if (buffer
[0] == 137 && buffer
[1] == 80 && buffer
[2] == 78 && buffer
[3] == 71 &&
355 buffer
[4] == 13 && buffer
[5] == 10 && buffer
[6] == 26 && buffer
[7] == 10)
358 /* check for PBM or PGM or PPM */
359 if (buffer
[0] == 'P' && (buffer
[1] > '0' && buffer
[1] < '7') && (buffer
[2] == 0x0a || buffer
[2] == 0x20 || buffer
[2] == 0x09 || buffer
[2] == 0x0d))
363 if (buffer
[0] == 0xff && buffer
[1] == 0xd8)
367 if (buffer
[0] == 'G' && buffer
[1] == 'I' && buffer
[2] == 'F' && buffer
[3] == '8' &&
368 (buffer
[4] == '7' || buffer
[4] == '9') && buffer
[5] == 'a')
372 if (buffer
[0] == 'R' && buffer
[1] == 'I' && buffer
[2] == 'F' && buffer
[3] == 'F' &&
373 buffer
[8] == 'W' && buffer
[9] == 'E' && buffer
[10] == 'B' && buffer
[11] == 'P' &&
374 buffer
[12] == 'V' && buffer
[13] == 'P' && buffer
[14] == '8' &&
375 (buffer
[15] == ' ' /* Simple File Format (Lossy) */
376 || buffer
[15] == 'L' /* Simple File Format (Lossless) */
377 || buffer
[15] == 'X')) /* Extended File Format */