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() */
32 #include "gliv-image.h"
34 #include "str_utils.h"
37 #include "files_list.h"
39 #include "math_floats.h"
41 #include "decompression.h"
42 #include "thumbnails.h"
43 #include "rendering.h"
44 #include "tree_browser.h"
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)
69 const gchar
*currently_loading(void)
71 return loading_filename
;
74 static void segv_handler(gint sig
)
79 sig_str
= g_strsignal(sig
);
81 if (loading_filename
== NULL
)
82 g_printerr(_("%s not while loading an image\n"), sig_str
);
84 g_printerr(_("%s while loading %s\n"), sig_str
, loading_filename
);
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
;
103 alpha_str
= g_strconcat(" (", _("alpha channel"), ")", NULL
);
105 alpha_str
= g_strdup("");
107 im_nr
= get_image_number(im
);
108 im
->ident
= g_strdup_printf("%s (%u/%u)%s",
110 filename_to_utf8(im
->node
->data
) : "",
111 im_nr
+ 1, get_list_length(), alpha_str
);
116 const gchar
*get_extension(const gchar
* filename
)
119 const gchar
*basename
;
121 basename
= strrchr(filename
, '/');
122 if (basename
== NULL
)
127 ext
= strrchr(basename
, '.');
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;
148 while (image_ext_start
> filename
&& *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
);
159 LOADING_ERROR(_("%s: unknown decompressed extension\n"),
160 filename_to_utf8(filename
));
164 switch (res
->loader
) {
166 LOADING_ERROR(_("%s: image cannot be compressed twice\n"),
167 filename_to_utf8(filename
));
171 return LOADER_DECOMP_PIXBUF
;
173 case LOADER_DOT_GLIV
:
174 return LOADER_DECOMP_DOT_GLIV
;
181 loader_t
get_loader(const gchar
* filename
)
183 const struct format
*res
;
187 ext
= get_extension(filename
);
189 LOADING_ERROR(_("%s: unknown extension (none)\n"),
190 filename_to_utf8(filename
));
194 ext_len
= strlen(ext
);
195 res
= ext_to_loader(ext
, ext_len
);
198 LOADING_ERROR(_("%s: unknown extension\n"), filename_to_utf8(filename
));
202 if (res
->loader
== LOADER_DECOMP
)
203 return get_decompressor(filename
, ext_len
);
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
)
218 pixel
= gdk_pixbuf_get_pixels(pixbuf
);
219 end
= pixel
+ pixels_size(pixels_size
);
221 for (; pixel
< end
; pixel
+= 4) {
225 * This is not valid C since a cast cannot be a lvalue but...
227 *((guint32
*) pixel
) = 0;
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
247 /* Wrapper: two args -> one arg. */
249 const gchar
*filename
;
253 static GdkPixbuf
*load_gdk_pixbuf(fname_error
* param
)
255 GdkPixbuf
*pixbuf
= NULL
;
258 loader
= get_loader(param
->filename
);
259 if (loader
== LOADER_DECOMP_PIXBUF
)
260 pixbuf
= load_compressed_pixbuf(param
->filename
, param
->error
);
266 pixbuf
= gdk_pixbuf_new_from_file(param
->filename
, param
->error
);
271 static GdkPixbuf
*load_gdk_pixbuf_threaded(const gchar
* filename
,
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
) {
287 * The caller won't handle the error, handle it here
289 if (*param
->error
!= NULL
)
290 DIALOG_MSG("%s", (*param
->error
)->message
);
292 DIALOG_MSG(_("Cannot load %s"), filename
);
295 add_thumbnail(filename
, 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
)
317 while (dim
> rt
->max_texture_size
) {
325 static void parse_exif_orientation(GdkPixbuf
* pixbuf
, GlivImage
*im
)
327 const gchar
*orientation_str
= gdk_pixbuf_get_option(pixbuf
, "orientation");
330 switch (orientation_str
[0]) {
332 im
->initial_angle
= PI
;
333 im
->initial_h_flip
= TRUE
;
337 im
->initial_angle
= PI
;
341 im
->initial_h_flip
= TRUE
;
345 im
->initial_angle
= PI
/ 2.0;
346 im
->initial_h_flip
= TRUE
;
350 im
->initial_angle
= -PI
/ 2.0;
354 im
->initial_angle
= -PI
/ 2.0;
355 im
->initial_h_flip
= TRUE
;
359 im
->initial_angle
= PI
/ 2.0;
364 GlivImage
*make_gliv_image(GdkPixbuf
* pixbuf
)
368 im
= gliv_image_new();
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
);
386 im
->maps
= g_new(texture_map
, im
->nb_maps
);
387 im
->maps
[0].pixbuf
= pixbuf
;
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
);