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"
42 typedef struct RCachedImage
{
45 time_t last_modif
; /* last time file was modified */
46 time_t last_use
; /* last time image was used */
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];
79 /* built-in PNM here refers to anymap format: PPM, PGM, PBM */
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
107 static void init_cache(void)
111 tmp
= getenv("RIMAGE_CACHE");
112 if (!tmp
|| sscanf(tmp
, "%i", &RImageCacheSize
) != 1)
113 RImageCacheSize
= IMAGE_CACHE_DEFAULT_NBENTRIES
;
114 if (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"));
133 memset(RImageCache
, 0, sizeof(RCachedImage
) * RImageCacheSize
);
137 void RReleaseCache(void)
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
);
150 RImageCacheSize
= -1;
154 RImage
*RLoadImage(RContext
*context
, const char *file
, int index
)
156 RImage
*image
= NULL
;
160 assert(file
!= NULL
);
162 if (RImageCacheSize
< 0)
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
);
176 free(RImageCache
[i
].file
);
177 RImageCache
[i
].file
= NULL
;
178 RReleaseImage(RImageCache
[i
].image
);
184 switch (identFile(file
)) {
190 /* generic file format support using ImageMagick
191 * BMP, PCX, PICT, SVG, ...
193 image
= RLoadMagick(file
);
196 RErrorCode
= RERR_BADFORMAT
;
201 image
= RLoadXPM(context
, file
);
206 image
= RLoadTIFF(file
, index
);
208 #endif /* USE_TIFF */
212 image
= RLoadPNG(context
, file
);
218 image
= RLoadJPEG(file
);
220 #endif /* USE_JPEG */
224 image
= RLoadGIF(file
, index
);
230 image
= RLoadWEBP(file
);
232 #endif /* USE_WEBP */
235 image
= RLoadPPM(file
);
239 RErrorCode
= RERR_BADFORMAT
;
243 /* store image in cache */
244 if (RImageCacheSize
> 0 && image
&&
245 (RImageCacheMaxImage
== 0 || RImageCacheMaxImage
>= image
->width
* image
->height
)) {
246 time_t oldest
= time(NULL
);
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
);
265 if (oldest
> RImageCache
[i
].last_use
) {
266 oldest
= RImageCache
[i
].last_use
;
272 /* if no slot available, dump least recently used one */
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
);
287 char *RGetImageFileFormat(const char *file
)
289 switch (identFile(file
)) {
296 #endif /* USE_TIFF */
306 #endif /* USE_JPEG */
316 #endif /* USE_WEBP */
326 static WRImgFormat
identFile(const char *path
)
329 unsigned char buffer
[32];
332 assert(path
!= NULL
);
335 file
= fopen(path
, "rb");
338 if (errno
!= EINTR
) {
339 RErrorCode
= RERR_OPEN
;
344 nread
= fread(buffer
, 1, sizeof(buffer
), file
);
345 if (nread
< sizeof(buffer
) || ferror(file
)) {
347 RErrorCode
= RERR_READ
;
353 if (strncmp((char *)buffer
, "/* XPM */", 9) == 0)
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] == '*'))
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)
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))
377 if (buffer
[0] == 0xff && buffer
[1] == 0xd8)
381 if (buffer
[0] == 'G' && buffer
[1] == 'I' && buffer
[2] == 'F' && buffer
[3] == '8' &&
382 (buffer
[4] == '7' || buffer
[4] == '9') && buffer
[5] == 'a')
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 */