Don't close the window when the user refused to quit.
[gliv.git] / src / rendering.c
blob9fc06fc109acbd5df1939f8e90962297124c65e7
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 <gtk/gtkgl.h>
26 #include <GL/gl.h>
28 #include "gliv.h"
29 #include "rendering.h"
30 #include "options.h"
31 #include "gliv_image.h"
32 #include "matrix.h"
33 #include "params.h"
34 #include "zoom_frame.h"
35 #include "scrollbars.h"
36 #include "gl_widget.h"
37 #include "textures.h"
38 #include "files_list.h"
39 #include "loading.h"
40 #include "windows.h"
41 #include "history.h"
43 extern rt_struct *rt;
44 extern options_struct *options;
45 extern gliv_image *current_image;
46 extern GtkWidget *gl_widget;
48 /* The gtk timer that calls redraw(). */
49 static guint idle_id = 0;
51 static void set_filter(gint filter)
53 gint id;
54 texture_map *map;
57 * Only the textures in the first map change their filter,
58 * since in the others maps the image is zoomed out.
60 map = current_image->maps;
62 for (id = 0; id < map->nb_tiles; id++) {
63 glBindTexture(GL_TEXTURE_2D, map->tex_ids[id]);
64 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
65 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
69 static gboolean is_filtering_enabled(void)
71 texture_map *map;
72 gint filter;
74 /* Only the first map changes its filter. */
75 map = current_image->maps;
77 glBindTexture(GL_TEXTURE_2D, map->tex_ids[0]);
78 glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &filter);
80 return filter == GL_LINEAR;
83 static void call_lists(gboolean smooth, gint level)
85 gint id;
86 texture_map *map;
88 if (rt->filtering_enabled != smooth) {
89 rt->filtering_enabled = smooth;
90 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 matrix_changed = get_matrix_has_changed();
109 if (rt->alpha_checks_changed) {
110 gushort texture[12];
111 gushort alpha1[3] = {
112 options->alpha1.red,
113 options->alpha1.green,
114 options->alpha1.blue
116 gushort alpha2[3] = {
117 options->alpha2.red,
118 options->alpha2.green,
119 options->alpha2.blue
122 if (tex_id == 0)
123 glGenTextures(1, &tex_id);
125 for (i = 0; i < 3; i++) {
126 texture[i] = texture[9 + i] = alpha1[i];
127 texture[3 + i] = texture[6 + i] = alpha2[i];
130 glBindTexture(GL_TEXTURE_2D, tex_id);
132 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
133 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
135 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
136 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
138 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB,
139 GL_UNSIGNED_SHORT, texture);
141 rt->alpha_checks_changed = FALSE;
142 } else
143 glBindTexture(GL_TEXTURE_2D, tex_id);
145 if (matrix_changed == FALSE)
146 /* glMatrixMode(GL_MODELVIEW); */
147 /* Save the matrix only if we will not replace it when redrawing. */
148 glPushMatrix();
150 glLoadIdentity();
152 half_w = rt->wid_size->width / 2.0;
153 half_h = rt->wid_size->height / 2.0;
155 glBegin(GL_QUADS);
157 glTexCoord2f(0.0, 0.0);
158 glVertex2f(-half_w, -half_h);
160 glTexCoord2f(half_w / 16.0, 0.0);
161 glVertex2f(half_w, -half_h);
163 glTexCoord2f(half_w / 16.0, half_h / 16.0);
164 glVertex2f(half_w, half_h);
166 glTexCoord2f(0.0, half_h / 16.0);
167 glVertex2f(-half_w, half_h);
169 glEnd();
171 if (matrix_changed == FALSE)
172 /* glMatrixMode(GL_MODELVIEW); */
173 glPopMatrix();
176 static gint choose_mipmap_level(void)
178 gfloat zoom, mipmap_ratio = MIPMAP_RATIO;
179 gint level = 1;
181 if (options->mipmap) {
182 zoom = get_matrix_zoom();
184 while (mipmap_ratio > zoom && level < current_image->nb_maps) {
185 mipmap_ratio *= MIPMAP_RATIO;
186 level++;
190 /* Mipmaps should only be scaled down. */
191 return level - 1;
194 /* Called by the timer when a redraw is needed. */
195 static gboolean redraw(void)
197 static GdkGLDrawable *gldrawable = NULL;
199 clear_zoom_frame();
201 if (gldrawable == NULL)
202 /* First time. */
203 gldrawable = gtk_widget_get_gl_drawable(gl_widget);
205 gdk_gl_drawable_wait_gdk(gldrawable);
207 glDisable(GL_DITHER);
209 if (current_image != NULL && current_image->has_alpha &&
210 options->alpha_checks)
212 draw_checker();
213 else
214 glClear(GL_COLOR_BUFFER_BIT);
216 glEnable(GL_DITHER);
218 write_gl_matrix();
220 if (current_image != NULL) {
221 gint mipmap_level;
222 gboolean filtering;
224 mipmap_level = choose_mipmap_level();
225 if (mipmap_level == 0)
226 filtering = is_filtering_needed();
227 else
228 filtering = TRUE;
230 call_lists(filtering, mipmap_level);
233 gdk_gl_drawable_swap_buffers(gldrawable);
234 update_scrollbars();
236 gdk_gl_drawable_wait_gl(gldrawable);
237 report_opengl_errors();
239 /* To remove the idle func. */
240 idle_id = 0;
241 return FALSE;
244 /* Called the first time an image is displayed. */
245 void render(void)
247 prioritize_textures(current_image, TRUE);
249 matrix_reset();
250 delete_selected_image();
251 fill_ident(current_image);
253 rt->filtering_enabled = is_filtering_enabled();
255 if (options->fullscreen == FALSE)
256 goto_window();
258 if (options->maximize || options->scaledown)
259 matrix_set_max_zoom(-1, -1, TRUE);
261 refresh(REFRESH_NOW | REFRESH_STATUS | APPEND_HISTORY);
263 /* A bit dumb, but needed on some cards. */
264 refresh(REFRESH_NOW);
266 g_free(current_image->ident);
267 current_image->ident = NULL;
269 update_window_title();
272 void zoom_in(gfloat ratio)
274 gint pointer_x, pointer_y;
275 gfloat x, y;
277 if (options->zoom_pointer) {
278 /* The pointer is the zoom center. */
279 gdk_window_get_pointer(gl_widget->window, &pointer_x, &pointer_y, NULL);
280 x = (gfloat) pointer_x;
281 y = (gfloat) pointer_y;
282 } else {
283 /* The zoom center is the midle of the window. */
284 x = rt->wid_size->width / 2.0;
285 y = rt->wid_size->height / 2.0;
288 matrix_zoom(ratio, x, y);
289 refresh(REFRESH_IMAGE | REFRESH_STATUS | APPEND_HISTORY);
292 static gboolean refresh_image(void)
294 idle_id = 0;
295 refresh(REFRESH_IMAGE);
297 return FALSE;
300 void refresh(gint what)
302 /* GTK does that automatically but with more overhead. */
303 if (what & REFRESH_IMAGE) {
304 if (idle_id == 0)
305 idle_id = gtk_idle_add_priority(GDK_PRIORITY_REDRAW,
306 (GtkFunction) redraw, NULL);
308 } else if (what & REFRESH_BURST) {
309 if (options->fps <= 0)
310 refresh(REFRESH_IMAGE);
312 else if (idle_id == 0)
313 idle_id = gtk_timeout_add(1000 / options->fps,
314 (GtkFunction) refresh_image, NULL);
316 } else if (what & REFRESH_NOW) {
317 if (idle_id != 0)
318 g_source_remove(idle_id);
320 redraw();
323 if (what & REFRESH_STATUS)
324 update_status_bar();
326 if (what & APPEND_HISTORY)
327 append_history();