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 */
49 * Number of image to keep in the cache
51 static int RImageCacheSize
= -1;
53 #define IMAGE_CACHE_DEFAULT_NBENTRIES 8
54 #define IMAGE_CACHE_MAXIMUM_NBENTRIES 256
57 * Max. size of image (in pixels) to store in the cache
59 static int RImageCacheMaxImage
= -1; /* 0 = any size */
61 #define IMAGE_CACHE_DEFAULT_MAXPIXELS (64 * 64)
62 #define IMAGE_CACHE_MAXIMUM_MAXPIXELS (256 * 256)
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_DEFAULT_NBENTRIES
;
113 if (RImageCacheSize
< 0)
115 if (RImageCacheSize
> IMAGE_CACHE_MAXIMUM_NBENTRIES
)
116 RImageCacheSize
= IMAGE_CACHE_MAXIMUM_NBENTRIES
;
118 tmp
= getenv("RIMAGE_CACHE_SIZE");
119 if (!tmp
|| sscanf(tmp
, "%i", &RImageCacheMaxImage
) != 1)
120 RImageCacheMaxImage
= IMAGE_CACHE_DEFAULT_MAXPIXELS
;
121 if (RImageCacheMaxImage
< 0)
122 RImageCacheMaxImage
= 0;
123 if (RImageCacheMaxImage
> IMAGE_CACHE_MAXIMUM_MAXPIXELS
)
124 RImageCacheMaxImage
= IMAGE_CACHE_MAXIMUM_MAXPIXELS
;
126 if (RImageCacheSize
> 0) {
127 RImageCache
= malloc(sizeof(RCachedImage
) * RImageCacheSize
);
128 if (RImageCache
== NULL
) {
129 printf("wrlib: out of memory for image cache\n");
132 memset(RImageCache
, 0, sizeof(RCachedImage
) * RImageCacheSize
);
136 void RReleaseCache(void)
140 if (RImageCacheSize
> 0) {
141 for (i
= 0; i
< RImageCacheSize
; i
++) {
142 if (RImageCache
[i
].file
) {
143 RReleaseImage(RImageCache
[i
].image
);
144 free(RImageCache
[i
].file
);
149 RImageCacheSize
= -1;
153 RImage
*RLoadImage(RContext
*context
, const char *file
, int index
)
155 RImage
*image
= NULL
;
159 assert(file
!= NULL
);
161 if (RImageCacheSize
< 0)
164 if (RImageCacheSize
> 0) {
166 for (i
= 0; i
< RImageCacheSize
; i
++) {
167 if (RImageCache
[i
].file
&& strcmp(file
, RImageCache
[i
].file
) == 0) {
169 if (stat(file
, &st
) == 0 && st
.st_mtime
== RImageCache
[i
].last_modif
) {
170 RImageCache
[i
].last_use
= time(NULL
);
172 return RCloneImage(RImageCache
[i
].image
);
175 free(RImageCache
[i
].file
);
176 RImageCache
[i
].file
= NULL
;
177 RReleaseImage(RImageCache
[i
].image
);
183 switch (identFile(file
)) {
189 /* generic file format support using ImageMagick
190 * BMP, PCX, PICT, SVG, ...
192 image
= RLoadMagick(file
);
195 RErrorCode
= RERR_BADFORMAT
;
200 image
= RLoadXPM(context
, file
);
205 image
= RLoadTIFF(file
, index
);
207 #endif /* USE_TIFF */
211 image
= RLoadPNG(context
, file
);
217 image
= RLoadJPEG(file
);
219 #endif /* USE_JPEG */
223 image
= RLoadGIF(file
, index
);
229 image
= RLoadWEBP(file
);
231 #endif /* USE_WEBP */
234 image
= RLoadPPM(file
);
238 RErrorCode
= RERR_BADFORMAT
;
242 /* store image in cache */
243 if (RImageCacheSize
> 0 && image
&&
244 (RImageCacheMaxImage
== 0 || RImageCacheMaxImage
>= image
->width
* image
->height
)) {
245 time_t oldest
= time(NULL
);
249 for (i
= 0; i
< RImageCacheSize
; i
++) {
250 if (!RImageCache
[i
].file
) {
251 RImageCache
[i
].file
= malloc(strlen(file
) + 1);
252 strcpy(RImageCache
[i
].file
, file
);
253 RImageCache
[i
].image
= RCloneImage(image
);
254 RImageCache
[i
].last_modif
= st
.st_mtime
;
255 RImageCache
[i
].last_use
= time(NULL
);
259 if (oldest
> RImageCache
[i
].last_use
) {
260 oldest
= RImageCache
[i
].last_use
;
266 /* if no slot available, dump least recently used one */
268 free(RImageCache
[oldest_idx
].file
);
269 RReleaseImage(RImageCache
[oldest_idx
].image
);
270 RImageCache
[oldest_idx
].file
= malloc(strlen(file
) + 1);
271 strcpy(RImageCache
[oldest_idx
].file
, file
);
272 RImageCache
[oldest_idx
].image
= RCloneImage(image
);
273 RImageCache
[oldest_idx
].last_modif
= st
.st_mtime
;
274 RImageCache
[oldest_idx
].last_use
= time(NULL
);
281 char *RGetImageFileFormat(const char *file
)
283 switch (identFile(file
)) {
290 #endif /* USE_TIFF */
300 #endif /* USE_JPEG */
310 #endif /* USE_WEBP */
320 static WRImgFormat
identFile(const char *path
)
323 unsigned char buffer
[32];
326 assert(path
!= NULL
);
329 file
= fopen(path
, "rb");
332 if (errno
!= EINTR
) {
333 RErrorCode
= RERR_OPEN
;
338 nread
= fread(buffer
, 1, sizeof(buffer
), file
);
339 if (nread
< sizeof(buffer
) || ferror(file
)) {
341 RErrorCode
= RERR_READ
;
347 if (strncmp((char *)buffer
, "/* XPM */", 9) == 0)
351 if ((buffer
[0] == 'I' && buffer
[1] == 'I' && buffer
[2] == '*' && buffer
[3] == 0)
352 || (buffer
[0] == 'M' && buffer
[1] == 'M' && buffer
[2] == 0 && buffer
[3] == '*'))
358 * The signature is defined in the PNG specifiation:
359 * http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html
360 * it is valid for v1.0, v1.1, v1.2 and ISO version
362 if (buffer
[0] == 137 && buffer
[1] == 80 && buffer
[2] == 78 && buffer
[3] == 71 &&
363 buffer
[4] == 13 && buffer
[5] == 10 && buffer
[6] == 26 && buffer
[7] == 10)
366 /* check for PBM or PGM or PPM */
367 if (buffer
[0] == 'P' && (buffer
[1] > '0' && buffer
[1] < '7') && (buffer
[2] == 0x0a || buffer
[2] == 0x20 || buffer
[2] == 0x09 || buffer
[2] == 0x0d))
371 if (buffer
[0] == 0xff && buffer
[1] == 0xd8)
375 if (buffer
[0] == 'G' && buffer
[1] == 'I' && buffer
[2] == 'F' && buffer
[3] == '8' &&
376 (buffer
[4] == '7' || buffer
[4] == '9') && buffer
[5] == 'a')
380 if (buffer
[0] == 'R' && buffer
[1] == 'I' && buffer
[2] == 'F' && buffer
[3] == 'F' &&
381 buffer
[8] == 'W' && buffer
[9] == 'E' && buffer
[10] == 'B' && buffer
[11] == 'P' &&
382 buffer
[12] == 'V' && buffer
[13] == 'P' && buffer
[14] == '8' &&
383 (buffer
[15] == ' ' /* Simple File Format (Lossy) */
384 || buffer
[15] == 'L' /* Simple File Format (Lossless) */
385 || buffer
[15] == 'X')) /* Extended File Format */