The EXIF orientation information is now honored.
[gliv.git] / src / loading.c
blobd6547b11f3eaf85b55944fe4f3a084919000abc1
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <guichaz@gmail.com>
21 /***********************************
22 * Loading and unloading of images *
23 ***********************************/
25 #include <stdio.h> /* FILE, fopen(), fread(), fclose(), perror() */
26 #include <signal.h> /* signal(), SIG_DFL, SIGSEGV */
27 #include <string.h> /* strlen(), strrchr() */
29 #include "gliv.h"
30 #include "loading.h"
31 #include "options.h"
32 #include "gliv-image.h"
33 #include "messages.h"
34 #include "str_utils.h"
35 #include "formats.h"
36 #include "params.h"
37 #include "files_list.h"
38 #include "textures.h"
39 #include "math_floats.h"
40 #include "thread.h"
41 #include "decompression.h"
42 #include "thumbnails.h"
43 #include "rendering.h"
44 #include "tree_browser.h"
46 extern rt_struct *rt;
47 extern options_struct *options;
48 extern GlivImage *current_image;
50 /* Used to know at any time if we are loading an image. */
51 static gchar *loading_filename = NULL;
54 * Used to be quiet when we try to load files
55 * not explicitly requested by the user.
57 static gint quiet = 0;
59 void start_quiet(void)
61 quiet++;
64 void stop_quiet(void)
66 quiet--;
69 const gchar *currently_loading(void)
71 return loading_filename;
74 static void segv_handler(gint sig)
76 const gchar *sig_str;
78 signal(sig, SIG_DFL);
79 sig_str = g_strsignal(sig);
81 if (loading_filename == NULL)
82 g_printerr(_("%s not while loading an image\n"), sig_str);
83 else
84 g_printerr(_("%s while loading %s\n"), sig_str, loading_filename);
86 raise(sig);
89 /* Called when the first image is loaded. */
90 void install_segv_handler(void)
92 signal(SIGSEGV, segv_handler);
95 void fill_ident(GlivImage * im)
97 gchar *alpha_str = NULL;
98 gint im_nr;
100 g_free(im->ident);
102 if (im->has_alpha)
103 alpha_str = g_strconcat(" (", _("alpha channel"), ")", NULL);
104 else
105 alpha_str = g_strdup("");
107 im_nr = get_image_number(im);
108 im->ident = g_strdup_printf("%s (%u/%u)%s",
109 im->node ?
110 filename_to_utf8(im->node->data) : "",
111 im_nr + 1, get_list_length(), alpha_str);
113 g_free(alpha_str);
116 const gchar *get_extension(const gchar * filename)
118 const gchar *ext;
119 const gchar *basename;
121 basename = strrchr(filename, '/');
122 if (basename == NULL)
123 basename = filename;
124 else
125 basename++;
127 ext = strrchr(basename, '.');
128 if (ext != NULL)
129 /* Skip the '.' */
130 ext++;
132 return ext;
135 #define LOADING_ERROR if (!quiet) g_printerr
137 static loader_t get_decompressor(const gchar * filename, gint ext_len)
139 const struct format *res;
141 /* We want the image extension between image_ext_start and image_ext_end. */
142 const gchar *image_ext_end = filename + strlen(filename) - ext_len - 1;
143 const gchar *image_ext_start = image_ext_end - 1;
145 gint image_ext_length = 0;
146 gchar *image_ext;
148 while (image_ext_start > filename && *image_ext_start != '.') {
149 image_ext_length++;
150 image_ext_start--;
153 image_ext = g_strndup(image_ext_start + 1, image_ext_length);
154 res = ext_to_loader(image_ext, image_ext_length);
156 g_free(image_ext);
158 if (res == NULL) {
159 LOADING_ERROR(_("%s: unknown decompressed extension\n"),
160 filename_to_utf8(filename));
161 return LOADER_NONE;
164 switch (res->loader) {
165 case LOADER_DECOMP:
166 LOADING_ERROR(_("%s: image cannot be compressed twice\n"),
167 filename_to_utf8(filename));
168 return LOADER_NONE;
170 case LOADER_PIXBUF:
171 return LOADER_DECOMP_PIXBUF;
173 case LOADER_DOT_GLIV:
174 return LOADER_DECOMP_DOT_GLIV;
176 default:
177 return LOADER_NONE;
181 loader_t get_loader(const gchar * filename)
183 const struct format *res;
184 const gchar *ext;
185 gint ext_len;
187 ext = get_extension(filename);
188 if (ext == NULL) {
189 LOADING_ERROR(_("%s: unknown extension (none)\n"),
190 filename_to_utf8(filename));
191 return LOADER_NONE;
194 ext_len = strlen(ext);
195 res = ext_to_loader(ext, ext_len);
197 if (res == NULL) {
198 LOADING_ERROR(_("%s: unknown extension\n"), filename_to_utf8(filename));
199 return LOADER_NONE;
202 if (res->loader == LOADER_DECOMP)
203 return get_decompressor(filename, ext_len);
205 return res->loader;
208 #if 0
210 * We used to blacken image pixels with alpha == 0.
211 * These pixels are invisible but may alter the bilinear filtering.
212 * We have stopped doing it since the time lost is too big for the benefit.
214 static void clean_alpha(GdkPixbuf * pixbuf)
216 guchar *pixel, *end;
218 pixel = gdk_pixbuf_get_pixels(pixbuf);
219 end = pixel + pixels_size(pixels_size);
221 for (; pixel < end; pixel += 4) {
222 if (pixel[3] == 0)
224 * alpha == 0
225 * This is not valid C since a cast cannot be a lvalue but...
227 *((guint32 *) pixel) = 0;
230 #endif
232 void set_loading(const gchar * filename)
234 /* Used if there is a segfault. */
235 g_free(loading_filename);
236 loading_filename = g_strdup(filename);
237 update_current_image_status(FALSE);
239 if (filename == NULL)
241 * Do it, now that we are no more in
242 * the currently_loading() case
244 load_later();
247 /* Wrapper: two args -> one arg. */
248 typedef struct {
249 const gchar *filename;
250 GError **error;
251 } fname_error;
253 static GdkPixbuf *load_gdk_pixbuf(fname_error * param)
255 GdkPixbuf *pixbuf = NULL;
256 loader_t loader;
258 loader = get_loader(param->filename);
259 if (loader == LOADER_DECOMP_PIXBUF)
260 pixbuf = load_compressed_pixbuf(param->filename, param->error);
262 if (*param->error)
263 return pixbuf;
265 if (pixbuf == NULL)
266 pixbuf = gdk_pixbuf_new_from_file(param->filename, param->error);
268 return pixbuf;
271 static GdkPixbuf *load_gdk_pixbuf_threaded(const gchar * filename,
272 GError ** error)
274 fname_error *param;
275 GdkPixbuf *pixbuf;
277 param = g_new(fname_error, 1);
278 param->filename = filename;
279 param->error = (error == NULL) ? g_new(GError *, 1) : error;
281 *param->error = NULL;
282 pixbuf = do_threaded((GThreadFunc) load_gdk_pixbuf, param);
284 if (pixbuf == NULL) {
285 if (error == NULL) {
287 * The caller won't handle the error, handle it here
289 if (*param->error != NULL)
290 DIALOG_MSG("%s", (*param->error)->message);
291 else
292 DIALOG_MSG(_("Cannot load %s"), filename);
294 } else
295 add_thumbnail(filename, pixbuf);
297 g_free(param);
298 return pixbuf;
301 gint pixels_size(GdkPixbuf * pixbuf)
303 /* The last rowstride may not be full. */
304 gint w = gdk_pixbuf_get_width(pixbuf);
305 gint h = gdk_pixbuf_get_height(pixbuf);
306 gint rs = gdk_pixbuf_get_rowstride(pixbuf);
307 gint nb_chan = gdk_pixbuf_get_n_channels(pixbuf);
308 gint bps = gdk_pixbuf_get_bits_per_sample(pixbuf);
310 return ((h - 1) * rs + w * ((nb_chan * bps + 7) / 8));
313 G_GNUC_PURE static gint nb_maps(gint dim)
315 gint ret = 1;
317 while (dim > rt->max_texture_size) {
318 dim *= MIPMAP_RATIO;
319 ret++;
322 return ret;
325 static void parse_exif_orientation(GdkPixbuf * pixbuf, GlivImage *im)
327 const gchar *orientation_str = gdk_pixbuf_get_option(pixbuf, "orientation");
329 if (orientation_str)
330 switch (orientation_str[0]) {
331 case '2':
332 im->initial_angle = PI;
333 im->initial_h_flip = TRUE;
334 break;
336 case '3':
337 im->initial_angle = PI;
338 break;
340 case '4':
341 im->initial_h_flip = TRUE;
342 break;
344 case '5':
345 im->initial_angle = PI / 2.0;
346 im->initial_h_flip = TRUE;
347 break;
349 case '6':
350 im->initial_angle = -PI / 2.0;
351 break;
353 case '7':
354 im->initial_angle = -PI / 2.0;
355 im->initial_h_flip = TRUE;
356 break;
358 case '8':
359 im->initial_angle = PI / 2.0;
360 break;
364 GlivImage *make_gliv_image(GdkPixbuf * pixbuf)
366 GlivImage *im;
368 im = gliv_image_new();
369 im->ident = NULL;
370 im->number = -1;
371 im->node = NULL;
372 im->first_image = (current_image == NULL);
374 im->width = gdk_pixbuf_get_width(pixbuf);
375 im->height = gdk_pixbuf_get_height(pixbuf);
376 im->has_alpha = gdk_pixbuf_get_has_alpha(pixbuf);
377 parse_exif_orientation(pixbuf, im);
379 if (options->mipmap) {
380 gint nb_w = nb_maps(im->width);
381 gint nb_h = nb_maps(im->height);
382 im->nb_maps = MIN(nb_w, nb_h);
383 } else
384 im->nb_maps = 1;
386 im->maps = g_new(texture_map, im->nb_maps);
387 im->maps[0].pixbuf = pixbuf;
389 #if 0
390 if (im->has_alpha)
391 clean_alpha(pixbuf);
392 #endif
394 create_maps(im);
395 return im;
398 GlivImage *load_file(const gchar * filename, GError ** error)
400 GdkPixbuf *loaded_image;
401 GlivImage *im = NULL;
403 set_loading(filename_to_utf8(filename));
405 loaded_image = load_gdk_pixbuf_threaded(filename, error);
406 if (loaded_image != NULL)
407 im = make_gliv_image(loaded_image);
409 set_loading(NULL);
411 return im;