process_events() update, the number of processed events is bounded.
[gliv.git] / src / loading.c
blobab55bb217e19a1215be5993f8a09f63bb4bd23f2
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@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() */
28 #include <GL/gl.h>
30 #include "gliv.h"
31 #include "loading.h"
32 #include "options.h"
33 #include "gliv_image.h"
34 #include "messages.h"
35 #include "gl_widget.h"
36 #include "str_utils.h"
37 #include "formats.h"
38 #include "params.h"
39 #include "files_list.h"
40 #include "textures.h"
41 #include "windows.h"
42 #include "thread.h"
43 #include "decompression.h"
45 extern rt_struct *rt;
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)
58 gint level;
59 texture_map *map;
61 if (im != NULL) {
62 for (level = 0; level < im->nb_maps; level++) {
63 map = im->maps + level;
65 glDeleteTextures(map->nb_tiles, map->tex_ids);
66 g_free(map->tex_ids);
68 glDeleteLists(map->list, map->nb_tiles);
69 g_free(map->tiles);
72 g_free(im->maps);
73 g_free(im->ident);
74 g_free(im);
76 report_opengl_errors();
80 static void segv_handler(gint sig)
82 const gchar *sig_str;
84 signal(sig, SIG_DFL);
85 sig_str = g_strsignal(sig);
87 if (loading_filename == NULL)
88 ERROR_MSG(_("%s not while loading an image\n"), sig_str);
89 else
90 ERROR_MSG(_("%s while loading %s\n"),
91 sig_str, filename_to_utf8(loading_filename));
93 raise(sig);
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)
104 gchar *alpha_str;
106 g_free(im->ident);
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)
117 const gchar *ext;
118 const gchar *basename;
120 basename = strrchr(filename, '/');
121 if (basename == NULL)
122 basename = filename;
123 else
124 basename++;
126 ext = strrchr(basename, '.');
127 if (ext != NULL)
128 /* Skip the '.' */
129 ext++;
131 return ext;
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;
143 gchar *image_ext;
145 while (image_ext_start > filename && *image_ext_start != '.') {
146 image_ext_length++;
147 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);
153 g_free(image_ext);
155 if (res == NULL) {
156 ERROR_MSG(_("%s: unknown decompressed extension\n"),
157 filename_to_utf8(filename));
158 return LOADER_NONE;
161 switch (res->loader) {
162 case LOADER_DECOMP:
163 ERROR_MSG(_("%s: image cannot be compressed twice\n"),
164 filename_to_utf8(filename));
165 return LOADER_NONE;
167 case LOADER_PIXBUF:
168 return LOADER_DECOMP_PIXBUF;
170 case LOADER_DOT_GLIV:
171 return LOADER_DECOMP_DOT_GLIV;
173 default:
174 return LOADER_NONE;
178 loader_t get_loader(const gchar * filename)
180 const struct format *res;
181 const gchar *ext;
182 gint ext_len;
184 ext = get_extension(filename);
185 if (ext == NULL) {
186 ERROR_MSG(_("%s: unknown extension (none)\n"),
187 filename_to_utf8(filename));
188 return LOADER_NONE;
191 ext_len = strlen(ext);
192 res = ext_to_loader(ext, ext_len);
194 if (res == NULL) {
195 ERROR_MSG(_("%s: unknown extension\n"), filename_to_utf8(filename));
196 return LOADER_NONE;
199 if (res->loader == LOADER_DECOMP)
200 return get_decompressor(filename, ext_len);
202 return res->loader;
205 #if 0
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)
213 guchar *pixel, *end;
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) {
220 if (pixel[3] == 0)
222 * alpha == 0
223 * This is not valid C since a cast cannot be a lvalue but...
225 *((guint32 *) pixel) = 0;
228 #endif
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. */
239 typedef struct {
240 const gchar *filename;
241 GError **error;
242 } fname_error;
244 static GdkPixbuf *load_gdk_pixbuf(fname_error * param)
246 GdkPixbuf *pixbuf = NULL;
247 loader_t loader;
249 loader = get_loader(param->filename);
250 if (loader == LOADER_DECOMP_PIXBUF)
251 pixbuf = load_compressed_pixbuf(param->filename, param->error);
253 if (*param->error)
254 return pixbuf;
256 if (pixbuf == NULL)
257 pixbuf = gdk_pixbuf_new_from_file(param->filename, param->error);
259 return pixbuf;
262 GdkPixbuf *load_gdk_pixbuf_threaded(const gchar * filename)
264 fname_error *param;
265 GdkPixbuf *pixbuf;
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);
277 else
278 DIALOG_MSG(_("Cannot load %s"), filename);
281 return pixbuf;
284 G_GNUC_PURE static gint nb_maps(gint dim)
286 gint ret = 1;
288 while (dim > rt->max_texture_size) {
289 dim *= MIPMAP_RATIO;
290 ret++;
293 return ret;
296 gliv_image *load_file(const gchar * filename)
298 gliv_image *im;
299 GdkPixbuf *loaded_image;
301 set_loading(filename);
303 loaded_image = load_gdk_pixbuf_threaded(filename);
304 if (loaded_image == NULL) {
305 /* Loading failed. */
306 set_loading(NULL);
307 return NULL;
310 im = g_new(gliv_image, 1);
311 im->ident = NULL;
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);
321 } else
322 im->nb_maps = 1;
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;
328 #if 0
329 if (im->has_alpha)
330 clean_alpha(loaded_image);
331 #endif
333 create_maps(im);
335 /* If there is a segfault it won't be because of the loading. */
336 set_loading(NULL);
338 return im;