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@yahoo.fr>
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() */
33 #include "gliv_image.h"
35 #include "gl_widget.h"
36 #include "str_utils.h"
39 #include "files_list.h"
43 #include "decompression.h"
46 extern options_struct
*options
;
48 /* Used to know at any time if we are loading an image. */
49 static const gchar
*loading_filename
= NULL
;
51 gboolean
is_loading(void)
53 return loading_filename
!= NULL
;
56 void destroy_image(gliv_image
* im
)
62 for (level
= 0; level
< im
->nb_maps
; level
++) {
63 map
= im
->maps
+ level
;
65 glDeleteTextures(map
->nb_tiles
, map
->tex_ids
);
68 glDeleteLists(map
->list
, map
->nb_tiles
);
76 report_opengl_errors();
80 static void segv_handler(gint sig
)
85 sig_str
= g_strsignal(sig
);
87 if (loading_filename
== NULL
)
88 ERROR_MSG(_("%s not while loading an image\n"), sig_str
);
90 ERROR_MSG(_("%s while loading %s\n"),
91 sig_str
, filename_to_utf8(loading_filename
));
96 /* Called when the first image is loaded. */
97 void install_segv_handler(void)
99 signal(SIGSEGV
, segv_handler
);
102 void fill_ident(gliv_image
* im
)
108 alpha_str
= im
->has_alpha
? _("alpha channel") : _("no alpha channel");
110 im
->ident
= g_strdup_printf("%s (%u/%u) (%s)",
111 filename_to_utf8(im
->node
->data
),
112 im
->number
+ 1, get_list_length(), alpha_str
);
115 const gchar
*get_extension(const gchar
* filename
)
118 const gchar
*basename
;
120 basename
= strrchr(filename
, '/');
121 if (basename
== NULL
)
126 ext
= strrchr(basename
, '.');
134 static loader_t
get_decompressor(const gchar
* filename
, gint ext_len
)
136 const struct format
*res
;
138 /* We want the image extension between image_ext_start and image_ext_end. */
139 const gchar
*image_ext_end
= filename
+ strlen(filename
) - ext_len
- 1;
140 const gchar
*image_ext_start
= image_ext_end
- 1;
142 gint image_ext_length
= 0;
145 while (image_ext_start
> filename
&& *image_ext_start
!= '.') {
150 image_ext
= g_strndup(image_ext_start
+ 1, image_ext_length
);
151 res
= ext_to_loader(image_ext
, image_ext_length
);
156 ERROR_MSG(_("%s: unknown decompressed extension\n"),
157 filename_to_utf8(filename
));
161 switch (res
->loader
) {
163 ERROR_MSG(_("%s: image cannot be compressed twice\n"),
164 filename_to_utf8(filename
));
168 return LOADER_DECOMP_PIXBUF
;
170 case LOADER_DOT_GLIV
:
171 return LOADER_DECOMP_DOT_GLIV
;
178 loader_t
get_loader(const gchar
* filename
)
180 const struct format
*res
;
184 ext
= get_extension(filename
);
186 ERROR_MSG(_("%s: unknown extension (none)\n"),
187 filename_to_utf8(filename
));
191 ext_len
= strlen(ext
);
192 res
= ext_to_loader(ext
, ext_len
);
195 ERROR_MSG(_("%s: unknown extension\n"), filename_to_utf8(filename
));
199 if (res
->loader
== LOADER_DECOMP
)
200 return get_decompressor(filename
, ext_len
);
207 * We used to blacken image pixels with alpha == 0.
208 * These pixels are invisible but may alter the bilinear filtering.
209 * We have stopped doing it since the time lost is too big for the benefit.
211 static void clean_alpha(GdkPixbuf
* pixbuf
)
215 pixel
= gdk_pixbuf_get_pixels(pixbuf
);
216 end
= pixel
+ gdk_pixbuf_get_rowstride(pixbuf
) *
217 gdk_pixbuf_get_height(pixbuf
);
219 for (; pixel
< end
; pixel
+= 4) {
223 * This is not valid C since a cast cannot be a lvalue but...
225 *((guint32
*) pixel
) = 0;
230 static void set_loading(const gchar
* filename
)
232 /* Used if there is a segfault. */
233 loading_filename
= filename
;
235 set_loading_entry(filename
);
238 /* Wrapper: two args -> one arg. */
240 const gchar
*filename
;
244 static GdkPixbuf
*load_gdk_pixbuf(fname_error
* param
)
246 GdkPixbuf
*pixbuf
= NULL
;
249 loader
= get_loader(param
->filename
);
250 if (loader
== LOADER_DECOMP_PIXBUF
)
251 pixbuf
= load_compressed_pixbuf(param
->filename
, param
->error
);
257 pixbuf
= gdk_pixbuf_new_from_file(param
->filename
, param
->error
);
262 GdkPixbuf
*load_gdk_pixbuf_threaded(const gchar
* filename
)
267 param
= g_new(fname_error
, 1);
268 param
->filename
= filename
;
269 param
->error
= g_new(GError
*, 1);
270 *param
->error
= NULL
;
272 pixbuf
= do_threaded((GThreadFunc
) load_gdk_pixbuf
, param
);
274 if (pixbuf
== NULL
) {
275 if (*param
->error
!= NULL
)
276 DIALOG_MSG("%s", (*param
->error
)->message
);
278 DIALOG_MSG(_("Cannot load %s"), filename
);
284 G_GNUC_PURE
static gint
nb_maps(gint dim
)
288 while (dim
> rt
->max_texture_size
) {
296 gliv_image
*load_file(const gchar
* filename
)
299 GdkPixbuf
*loaded_image
;
301 set_loading(filename
);
303 loaded_image
= load_gdk_pixbuf_threaded(filename
);
304 if (loaded_image
== NULL
) {
305 /* Loading failed. */
310 im
= g_new(gliv_image
, 1);
313 im
->width
= gdk_pixbuf_get_width(loaded_image
);
314 im
->height
= gdk_pixbuf_get_height(loaded_image
);
315 im
->has_alpha
= gdk_pixbuf_get_has_alpha(loaded_image
);
317 if (options
->mipmap
) {
318 gint nb_w
= nb_maps(im
->width
);
319 gint nb_h
= nb_maps(im
->height
);
320 im
->nb_maps
= MIN(nb_w
, nb_h
);
324 im
->maps
= g_new(texture_map
, im
->nb_maps
);
325 im
->maps
[0].map_init
= g_new(loading_data
, 1);
326 im
->maps
[0].map_init
->pixbuf
= loaded_image
;
330 clean_alpha(loaded_image
);
335 /* If there is a segfault it won't be because of the loading. */