Merged older cs.po file with newest pot file.
[gliv/czech_localization.git] / src / rendering.c
blob7306c7794997bd0dc3d72efa7f2abaad7766cc2b
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 * Rendering functions *
23 ***********************/
25 #include <stdlib.h> /* abs() */
27 #include "gliv.h"
28 #include "rendering.h"
29 #include "options.h"
30 #include "gliv-image.h"
31 #include "matrix.h"
32 #include "params.h"
33 #include "zoom_frame.h"
34 #include "scrollbars.h"
35 #include "textures.h"
36 #include "files_list.h"
37 #include "loading.h"
38 #include "windows.h"
39 #include "history.h"
40 #include "next_image.h"
41 #include "opengl.h"
42 #include "transition.h"
43 #include "images_menus.h"
45 extern rt_struct *rt;
46 extern options_struct *options;
47 extern GlivImage *current_image;
48 extern GtkWidget *gl_widget;
50 static GTimeVal last_redraw;
51 static gint advance;
53 static void set_filter(gint filter)
55 gint id;
56 texture_map *map;
59 * Only the textures in the first map change their filter,
60 * since in the others maps the image is zoomed out.
62 map = current_image->maps;
64 for (id = 0; id < map->nb_tiles; id++) {
65 glBindTexture(GL_TEXTURE_2D, map->tex_ids[id]);
66 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
67 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
71 static gboolean is_filtering_enabled(void)
73 texture_map *map;
74 gint filter;
76 /* Only the first map changes its filter. */
77 map = current_image->maps;
79 glBindTexture(GL_TEXTURE_2D, map->tex_ids[0]);
80 glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &filter);
82 return filter == GL_LINEAR;
85 static void call_lists(gboolean smooth, gint level)
87 gint id;
88 texture_map *map;
90 if (level == 0 && is_filtering_enabled() != smooth)
91 set_filter(smooth ? GL_LINEAR : GL_NEAREST);
93 map = current_image->maps + level;
95 for (id = 0; id < map->nb_tiles; id++)
96 if (matrix_tile_visible(map->tiles + id))
97 glCallList(map->list + id);
100 static void draw_checker(void)
102 static guint tex_id = 0;
103 gint i;
104 gfloat half_w, half_h;
105 gboolean matrix_changed;
107 glDisable(GL_DITHER);
108 glClear(GL_COLOR_BUFFER_BIT);
109 matrix_changed = get_matrix_has_changed();
111 if (rt->alpha_checks_changed) {
112 gushort texture[12];
113 gushort alpha1[3] = {
114 options->alpha1.red,
115 options->alpha1.green,
116 options->alpha1.blue
118 gushort alpha2[3] = {
119 options->alpha2.red,
120 options->alpha2.green,
121 options->alpha2.blue
124 if (tex_id == 0)
125 glGenTextures(1, &tex_id);
127 for (i = 0; i < 3; i++) {
128 texture[i] = texture[9 + i] = alpha1[i];
129 texture[3 + i] = texture[6 + i] = alpha2[i];
132 glBindTexture(GL_TEXTURE_2D, tex_id);
134 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
135 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
137 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
138 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
140 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB,
141 GL_UNSIGNED_SHORT, texture);
143 rt->alpha_checks_changed = FALSE;
144 } else
145 glBindTexture(GL_TEXTURE_2D, tex_id);
147 if (matrix_changed == FALSE)
148 /* glMatrixMode(GL_MODELVIEW); */
149 /* Save the matrix only if we will not replace it when redrawing. */
150 glPushMatrix();
152 glLoadIdentity();
154 half_w = rt->wid_size->width / 2.0;
155 half_h = rt->wid_size->height / 2.0;
157 glBegin(GL_QUADS);
159 glTexCoord2f(0.0, 0.0);
160 glVertex2f(-half_w, -half_h);
162 glTexCoord2f(half_w / 16.0, 0.0);
163 glVertex2f(half_w, -half_h);
165 glTexCoord2f(half_w / 16.0, half_h / 16.0);
166 glVertex2f(half_w, half_h);
168 glTexCoord2f(0.0, half_h / 16.0);
169 glVertex2f(-half_w, half_h);
171 glEnd();
173 if (matrix_changed == FALSE)
174 /* glMatrixMode(GL_MODELVIEW); */
175 glPopMatrix();
177 glEnable(GL_DITHER);
180 static gint choose_mipmap_level(void)
182 gfloat zoom, mipmap_ratio = MIPMAP_RATIO;
183 gint level = 1;
185 if (options->mipmap) {
186 zoom = get_matrix_zoom();
188 while (mipmap_ratio > zoom && level < current_image->nb_maps) {
189 mipmap_ratio *= MIPMAP_RATIO;
190 level++;
194 /* Mipmaps should only be scaled down. */
195 return level - 1;
198 static void display_last_image_notice(void)
200 static GdkGC *gc = NULL;
201 const gchar *msg;
202 PangoContext *pc;
203 PangoLayout *pl;
204 gint x = 10, y = 10, dx = 5, dy = 5;
205 gint w, h;
207 msg = get_image_notice();
208 if (msg == NULL)
209 return;
211 pc = gtk_widget_get_pango_context(gl_widget);
212 pl = pango_layout_new(pc);
214 if (gc == NULL)
215 /* First time. */
216 gc = gdk_gc_new(gl_widget->window);
218 pango_layout_set_text(pl, msg, -1);
220 gdk_gc_set_foreground(gc, &(gl_widget->style->white));
221 pango_layout_get_pixel_size(pl, &w, &h);
222 gdk_draw_rectangle(gl_widget->window, gc, TRUE,
223 x - dx, y - dy, w + 2 * dx, h + 2 * dy);
225 gdk_gc_set_foreground(gc, &(gl_widget->style->black));
226 gdk_draw_layout(gl_widget->window, gc, x, y, pl);
228 g_object_unref(pl);
231 static gboolean need_alpha_checks(void)
233 return current_image != NULL && current_image->has_alpha &&
234 options->alpha_checks;
237 void draw_current_image(void)
239 gint mipmap_level;
240 gboolean filtering;
241 gboolean alpha_checks = need_alpha_checks();
243 if (alpha_checks)
244 draw_checker();
245 else {
247 * I don't know why but this seems to be required to see images instead
248 * black rectangles with Mesa's software rendering.
250 glBegin(GL_QUADS);
251 glVertex2f(0, 0);
252 glEnd();
255 write_gl_matrix();
257 mipmap_level = choose_mipmap_level();
258 if (options->filtering == FALSE)
259 filtering = FALSE;
260 else if (mipmap_level == 0)
261 filtering = is_filtering_needed();
262 else
263 filtering = TRUE;
265 if (alpha_checks) {
266 glPushAttrib(GL_ENABLE_BIT);
267 glEnable(GL_BLEND);
270 call_lists(filtering, mipmap_level);
272 if (alpha_checks)
273 glPopAttrib();
276 /* Called by the timer when a redraw is needed. */
277 static void redraw(void)
279 static GdkGLDrawable *gldrawable = NULL;
280 gint frame_advance;
282 if (is_in_transition())
283 return;
285 if (gldrawable == NULL)
286 /* First time. */
287 gldrawable = gtk_widget_get_gl_drawable(gl_widget);
289 gdk_gl_drawable_wait_gdk(gldrawable);
290 clear_zoom_frame();
292 if (need_alpha_checks() == FALSE) {
293 glDisable(GL_DITHER);
294 glClear(GL_COLOR_BUFFER_BIT);
295 glEnable(GL_DITHER);
298 if (current_image != NULL)
299 draw_current_image();
301 gdk_gl_drawable_swap_buffers(gldrawable);
302 gdk_gl_drawable_wait_gl(gldrawable);
304 display_last_image_notice();
305 refresh(REFRESH_SCROLL);
307 frame_advance = get_fps_limiter_delay(&last_redraw);
308 if (frame_advance && abs(frame_advance) < G_USEC_PER_SEC / options->fps)
309 advance += frame_advance;
310 else
311 advance = 0;
312 g_get_current_time(&last_redraw);
315 /* Called the first time an image is displayed. */
316 void render(void)
318 gboolean list_changed;
320 configure_matrix(current_image);
321 prioritize_textures(current_image, TRUE);
323 refresh(REFRESH_NOW | APPEND_HISTORY | REFRESH_STATUS);
325 /* A bit dumb, but needed on some cards. */
326 refresh(REFRESH_NOW);
328 /* Post rendering */
329 list_changed = remove_obsolete_nodes();
330 if (list_changed)
331 do_later(G_PRIORITY_LOW, cond_rebuild_menus);
332 update_current_image_status(list_changed);
334 refresh(REFRESH_TITLE);
337 void zoom_in(gfloat ratio)
339 gint pointer_x, pointer_y;
340 gfloat x, y;
342 if (options->zoom_pointer) {
343 /* The pointer is the zoom center. */
344 gdk_window_get_pointer(gl_widget->window, &pointer_x, &pointer_y, NULL);
345 x = (gfloat) pointer_x;
346 y = (gfloat) pointer_y;
347 } else {
348 /* The zoom center is the midle of the window. */
349 x = rt->wid_size->width / 2.0;
350 y = rt->wid_size->height / 2.0;
353 matrix_zoom(ratio, x, y);
354 refresh(REFRESH_IMAGE | REFRESH_STATUS | APPEND_HISTORY);
357 static gboolean has_do_later(GSourceDummyMarshal idle_func);
358 static void add_do_later(GSourceDummyMarshal idle_func);
359 static void remove_do_later(GSourceDummyMarshal idle_func);
361 static gboolean refresh_image(void)
363 remove_do_later(redraw);
364 refresh(REFRESH_IMAGE);
366 return FALSE;
369 gint diff_timeval_us(GTimeVal * after, GTimeVal * before)
371 GTimeVal diff;
373 diff_timeval(&diff, after, before);
374 return diff.tv_sec * G_USEC_PER_SEC + diff.tv_usec;
377 gint get_fps_limiter_delay(GTimeVal * previous_frame)
379 GTimeVal now;
380 gint diff;
382 if (options->fps <= 0)
383 return 0;
385 g_get_current_time(&now);
386 diff =
387 G_USEC_PER_SEC / options->fps - diff_timeval_us(&now, previous_frame);
389 return diff;
392 void refresh(gint what)
394 /* GTK does that automatically but with more overhead. */
395 if (what & REFRESH_IMAGE)
396 do_later(GDK_PRIORITY_REDRAW, redraw);
398 else if (what & REFRESH_BURST) {
399 if (!has_do_later(redraw)) {
400 gint diff = get_fps_limiter_delay(&last_redraw) + advance;
402 if (diff > 0) {
403 add_do_later(redraw);
404 g_timeout_add(diff / 1000, (GtkFunction) refresh_image, NULL);
405 } else
406 refresh(REFRESH_IMAGE);
408 } else if (what & REFRESH_NOW) {
409 remove_do_later(redraw);
410 redraw();
413 if (what & REFRESH_STATUS)
414 do_later(G_PRIORITY_HIGH, update_status_bar);
416 if (what & APPEND_HISTORY)
417 append_history();
419 if (what & REFRESH_TITLE)
420 do_later(G_PRIORITY_DEFAULT, update_window_title);
422 if (what & REFRESH_SCROLL)
423 do_later(G_PRIORITY_LOW, update_scrollbars);
426 void update_current_image_status(gboolean find_number)
428 if (current_image != NULL) {
429 if (find_number)
430 current_image->number = -1;
432 fill_ident(current_image);
435 refresh(REFRESH_STATUS);
438 static GHashTable *idle_functions = NULL;
440 static void init_do_later(void)
442 if (idle_functions == NULL)
443 /* First time */
444 idle_functions = g_hash_table_new(g_direct_hash, NULL);
447 static gboolean has_do_later(GSourceDummyMarshal idle_func)
449 init_do_later();
450 return g_hash_table_lookup(idle_functions, idle_func) != NULL;
453 static void add_do_later(GSourceDummyMarshal idle_func)
455 init_do_later();
456 g_hash_table_insert(idle_functions, idle_func, idle_func);
459 static void remove_do_later(GSourceDummyMarshal idle_func)
461 init_do_later();
462 g_hash_table_remove(idle_functions, idle_func);
466 static gboolean wrapper(gpointer data)
468 GSourceDummyMarshal func = data;
469 remove_do_later(func);
470 func();
471 return FALSE;
474 void do_later(gint priority, GSourceDummyMarshal func)
476 if (has_do_later(func))
477 /* Already scheduled */
478 return;
480 add_do_later(func);
481 g_idle_add_full(priority, wrapper, func, NULL);