Change to the linux kernel coding style
[wmaker-crm.git] / wrlib / load.c
1 /* load.c - load image from file
2  *
3  * Raster graphics library
4  *
5  * Copyright (c) 1997-2003 Alfredo K. Kojima
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Library General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Library General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Library General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 #include <config.h>
23
24 #include <X11/Xlib.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #include <string.h>
31 #include <time.h>
32 #include <assert.h>
33
34 #ifdef USE_PNG
35 #include <png.h>
36 #endif
37
38 #include "wraster.h"
39
40 /* Silly hack for Windows systems with cygwin */
41 #ifndef O_BINARY
42 # define O_BINARY 0
43 #endif
44
45 typedef struct RCachedImage {
46         RImage *image;
47         char *file;
48         time_t last_modif;      /* last time file was modified */
49         time_t last_use;        /* last time image was used */
50 } RCachedImage;
51
52 /*
53  * Size of image cache
54  */
55 static int RImageCacheSize = -1;
56
57 /*
58  * Max. size of image to store in cache
59  */
60 static int RImageCacheMaxImage = -1;    /* 0 = any size */
61
62 #define IMAGE_CACHE_SIZE        8
63
64 #define IMAGE_CACHE_MAX_IMAGE   64*64
65
66 static RCachedImage *RImageCache;
67
68 #define IM_ERROR        -1
69 #define IM_UNKNOWN      0
70 #define IM_XPM          1
71 #define IM_TIFF         2
72 #define IM_PNG          3
73 #define IM_PPM          4
74 #define IM_JPEG         5
75 #define IM_GIF          6
76 /* How many image types do we have. */
77 /* Increase this when adding new image types! */
78 #define IM_TYPES        6
79
80 static int identFile(char *path);
81
82 extern RImage *RLoadPPM(RContext * context, char *file_name, int index);
83
84 extern RImage *RLoadXPM(RContext * context, char *file, int index);
85
86 #ifdef USE_TIFF
87 extern RImage *RLoadTIFF(RContext * context, char *file, int index);
88 #endif
89 #ifdef USE_PNG
90 extern RImage *RLoadPNG(RContext * context, char *file, int index);
91 #endif
92 #ifdef USE_JPEG
93 extern RImage *RLoadJPEG(RContext * context, char *file_name, int index);
94 #endif
95 #ifdef USE_GIF
96 extern RImage *RLoadGIF(RContext * context, char *file_name, int index);
97 #endif
98
99 char **RSupportedFileFormats(void)
100 {
101         static char *tmp[IM_TYPES + 1];
102         int i = 0;
103
104         /* built-in */
105         tmp[i++] = "XPM";
106         /* built-in */
107         tmp[i++] = "PPM";
108 #ifdef USE_TIFF
109         tmp[i++] = "TIFF";
110 #endif
111 #ifdef USE_PNG
112         tmp[i++] = "PNG";
113 #endif
114 #ifdef USE_JPEG
115         tmp[i++] = "JPEG";
116 #endif
117 #ifdef USE_GIF
118         tmp[i++] = "GIF";
119 #endif
120         tmp[i] = NULL;
121
122         return tmp;
123 }
124
125 static void init_cache()
126 {
127         char *tmp;
128
129         tmp = getenv("RIMAGE_CACHE");
130         if (!tmp || sscanf(tmp, "%i", &RImageCacheSize) != 1) {
131                 RImageCacheSize = IMAGE_CACHE_SIZE;
132         }
133         if (RImageCacheSize < 0)
134                 RImageCacheSize = 0;
135
136         tmp = getenv("RIMAGE_CACHE_SIZE");
137         if (!tmp || sscanf(tmp, "%i", &RImageCacheMaxImage) != 1) {
138                 RImageCacheMaxImage = IMAGE_CACHE_MAX_IMAGE;
139         }
140
141         if (RImageCacheSize > 0) {
142                 RImageCache = malloc(sizeof(RCachedImage) * RImageCacheSize);
143                 if (RImageCache == NULL) {
144                         printf("wrlib: out of memory for image cache\n");
145                         return;
146                 }
147                 memset(RImageCache, 0, sizeof(RCachedImage) * RImageCacheSize);
148         }
149 }
150
151 RImage *RLoadImage(RContext * context, char *file, int index)
152 {
153         RImage *image = NULL;
154         int i;
155         struct stat st;
156
157         assert(file != NULL);
158
159         if (RImageCacheSize < 0) {
160                 init_cache();
161         }
162
163         if (RImageCacheSize > 0) {
164
165                 for (i = 0; i < RImageCacheSize; i++) {
166                         if (RImageCache[i].file && strcmp(file, RImageCache[i].file) == 0) {
167
168                                 if (stat(file, &st) == 0 && st.st_mtime == RImageCache[i].last_modif) {
169                                         RImageCache[i].last_use = time(NULL);
170
171                                         return RCloneImage(RImageCache[i].image);
172
173                                 } else {
174                                         free(RImageCache[i].file);
175                                         RImageCache[i].file = NULL;
176                                         RReleaseImage(RImageCache[i].image);
177                                 }
178                         }
179                 }
180         }
181
182         switch (identFile(file)) {
183         case IM_ERROR:
184                 return NULL;
185
186         case IM_UNKNOWN:
187                 RErrorCode = RERR_BADFORMAT;
188                 return NULL;
189
190         case IM_XPM:
191                 image = RLoadXPM(context, file, index);
192                 break;
193
194 #ifdef USE_TIFF
195         case IM_TIFF:
196                 image = RLoadTIFF(context, file, index);
197                 break;
198 #endif                          /* USE_TIFF */
199
200 #ifdef USE_PNG
201         case IM_PNG:
202                 image = RLoadPNG(context, file, index);
203                 break;
204 #endif                          /* USE_PNG */
205
206 #ifdef USE_JPEG
207         case IM_JPEG:
208                 image = RLoadJPEG(context, file, index);
209                 break;
210 #endif                          /* USE_JPEG */
211
212 #ifdef USE_GIF
213         case IM_GIF:
214                 image = RLoadGIF(context, file, index);
215                 break;
216 #endif                          /* USE_GIF */
217
218         case IM_PPM:
219                 image = RLoadPPM(context, file, index);
220                 break;
221
222         default:
223                 RErrorCode = RERR_BADFORMAT;
224                 return NULL;
225         }
226
227         /* store image in cache */
228         if (RImageCacheSize > 0 && image &&
229             (RImageCacheMaxImage == 0 || RImageCacheMaxImage >= image->width * image->height)) {
230                 time_t oldest = time(NULL);
231                 int oldest_idx = 0;
232                 int done = 0;
233
234                 for (i = 0; i < RImageCacheSize; i++) {
235                         if (!RImageCache[i].file) {
236                                 RImageCache[i].file = malloc(strlen(file) + 1);
237                                 strcpy(RImageCache[i].file, file);
238                                 RImageCache[i].image = RCloneImage(image);
239                                 RImageCache[i].last_modif = st.st_mtime;
240                                 RImageCache[i].last_use = time(NULL);
241                                 done = 1;
242                                 break;
243                         } else {
244                                 if (oldest > RImageCache[i].last_use) {
245                                         oldest = RImageCache[i].last_use;
246                                         oldest_idx = i;
247                                 }
248                         }
249                 }
250
251                 /* if no slot available, dump least recently used one */
252                 if (!done) {
253                         free(RImageCache[oldest_idx].file);
254                         RReleaseImage(RImageCache[oldest_idx].image);
255                         RImageCache[oldest_idx].file = malloc(strlen(file) + 1);
256                         strcpy(RImageCache[oldest_idx].file, file);
257                         RImageCache[oldest_idx].image = RCloneImage(image);
258                         RImageCache[oldest_idx].last_modif = st.st_mtime;
259                         RImageCache[oldest_idx].last_use = time(NULL);
260                 }
261         }
262
263         return image;
264 }
265
266 char *RGetImageFileFormat(char *file)
267 {
268         switch (identFile(file)) {
269         case IM_XPM:
270                 return "XPM";
271
272 #ifdef USE_TIFF
273         case IM_TIFF:
274                 return "TIFF";
275 #endif                          /* USE_TIFF */
276
277 #ifdef USE_PNG
278         case IM_PNG:
279                 return "PNG";
280 #endif                          /* USE_PNG */
281
282 #ifdef USE_JPEG
283         case IM_JPEG:
284                 return "JPEG";
285 #endif                          /* USE_JPEG */
286
287 #ifdef USE_GIF
288         case IM_GIF:
289                 return "GIF";
290 #endif                          /* USE_GIF */
291
292         case IM_PPM:
293                 return "PPM";
294
295         default:
296                 return NULL;
297         }
298 }
299
300 static int identFile(char *path)
301 {
302         int fd;
303         unsigned char buffer[32];
304
305         assert(path != NULL);
306
307         fd = open(path, O_RDONLY | O_BINARY);
308         if (fd < 0) {
309                 RErrorCode = RERR_OPEN;
310                 return IM_ERROR;
311         }
312         if (read(fd, buffer, 32) < 1) {
313                 close(fd);
314                 RErrorCode = RERR_READ;
315                 return IM_ERROR;
316         }
317         close(fd);
318
319         /* check for XPM */
320         if (strncmp((char *)buffer, "/* XPM */", 9) == 0)
321                 return IM_XPM;
322
323         /* check for TIFF */
324         if ((buffer[0] == 'I' && buffer[1] == 'I' && buffer[2] == '*' && buffer[3] == 0)
325             || (buffer[0] == 'M' && buffer[1] == 'M' && buffer[2] == 0 && buffer[3] == '*'))
326                 return IM_TIFF;
327
328 #ifdef USE_PNG
329         /* check for PNG */
330         if (png_check_sig(buffer, 8))
331                 return IM_PNG;
332 #endif
333
334         /* check for raw PPM or PGM */
335         if (buffer[0] == 'P' && (buffer[1] == '5' || buffer[1] == '6'))
336                 return IM_PPM;
337
338         /* check for JPEG */
339         if (buffer[0] == 0xff && buffer[1] == 0xd8)
340                 return IM_JPEG;
341
342         /* check for GIF */
343         if (buffer[0] == 'G' && buffer[1] == 'I' && buffer[2] == 'F')
344                 return IM_GIF;
345
346         return IM_UNKNOWN;
347 }