Add the logo to Subversion.
[gliv.git] / src / rendering.c
blobaa9ad63679f530f8e6c559984b32ca8c7c60f7ee
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>
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 "gl_widget.h"
36 #include "textures.h"
37 #include "files_list.h"
38 #include "loading.h"
39 #include "windows.h"
40 #include "history.h"
41 #include "next_image.h"
42 #include "messages.h"
43 #include "opengl.h"
44 #include "callbacks.h"
46 extern rt_struct *rt;
47 extern options_struct *options;
48 extern GlivImage *current_image;
49 extern GtkWidget *gl_widget;
51 /* The gtk timer that calls redraw(). */
52 static guint idle_id = 0;
54 static void set_filter(gint filter)
56 gint id;
57 texture_map *map;
60 * Only the textures in the first map change their filter,
61 * since in the others maps the image is zoomed out.
63 map = current_image->maps;
65 for (id = 0; id < map->nb_tiles; id++) {
66 glBindTexture(GL_TEXTURE_2D, map->tex_ids[id]);
67 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
68 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
72 static gboolean is_filtering_enabled(void)
74 texture_map *map;
75 gint filter;
77 /* Only the first map changes its filter. */
78 map = current_image->maps;
80 glBindTexture(GL_TEXTURE_2D, map->tex_ids[0]);
81 glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &filter);
83 return filter == GL_LINEAR;
86 static void call_lists(gboolean smooth, gint level)
88 gint id;
89 texture_map *map;
91 if (level == 0 && is_filtering_enabled() != smooth)
92 set_filter(smooth ? GL_LINEAR : GL_NEAREST);
94 map = current_image->maps + level;
96 for (id = 0; id < map->nb_tiles; id++)
97 if (matrix_tile_visible(map->tiles + id))
98 glCallList(map->list + id);
101 static void draw_checker(void)
103 static guint tex_id = 0;
104 gint i;
105 gfloat half_w, half_h;
106 gboolean matrix_changed;
108 glDisable(GL_DITHER);
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 int x = 10, y = 10, dx = 5, dy = 5;
205 int 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();
246 write_gl_matrix();
248 mipmap_level = choose_mipmap_level();
249 if (mipmap_level == 0)
250 filtering = is_filtering_needed();
251 else
252 filtering = TRUE;
254 if (alpha_checks) {
255 glPushAttrib(GL_ENABLE_BIT);
256 glEnable(GL_BLEND);
259 call_lists(filtering, mipmap_level);
261 if (alpha_checks)
262 glPopAttrib();
265 /* Called by the timer when a redraw is needed. */
266 static gboolean redraw(void)
268 static GdkGLDrawable *gldrawable = NULL;
270 if (gldrawable == NULL)
271 /* First time. */
272 gldrawable = gtk_widget_get_gl_drawable(gl_widget);
274 gdk_gl_drawable_wait_gdk(gldrawable);
275 clear_zoom_frame();
277 if (need_alpha_checks() == FALSE) {
278 glDisable(GL_DITHER);
279 glClear(GL_COLOR_BUFFER_BIT);
280 glEnable(GL_DITHER);
283 if (current_image != NULL)
284 draw_current_image();
286 gdk_gl_drawable_swap_buffers(gldrawable);
287 update_scrollbars();
289 gdk_gl_drawable_wait_gl(gldrawable);
291 display_last_image_notice();
293 /* To remove the idle func. */
294 idle_id = 0;
295 return FALSE;
298 /* We are going to change the image. */
299 gfloat *prepare_rendering(GlivImage * im)
301 GlivImage *old = current_image;
302 gfloat *old_matrix = g_new(gfloat, 8);
304 matrix_cpy(old_matrix, NULL);
305 current_image = im;
307 fill_ident(im);
308 delete_selected_image();
309 update_window_title();
310 refresh(REFRESH_STATUS);
312 current_image = old;
314 g_free(im->ident);
315 im->ident = NULL;
317 if (options->fullscreen == FALSE)
318 goto_window(im);
320 /* Be sure to update the display now. A transition may take place ... */
321 gtk_widget_queue_draw(GTK_WIDGET(get_current_window()));
322 gdk_window_process_all_updates();
323 process_events();
325 configure_matrix(im);
327 return old_matrix;
330 /* Called the first time an image is displayed. */
331 void render(void)
333 configure_matrix(current_image);
334 prioritize_textures(current_image, TRUE);
336 refresh(REFRESH_NOW | APPEND_HISTORY);
338 /* A bit dumb, but needed on some cards. */
339 refresh(REFRESH_NOW);
342 void zoom_in(gfloat ratio)
344 gint pointer_x, pointer_y;
345 gfloat x, y;
347 if (options->zoom_pointer) {
348 /* The pointer is the zoom center. */
349 gdk_window_get_pointer(gl_widget->window, &pointer_x, &pointer_y, NULL);
350 x = (gfloat) pointer_x;
351 y = (gfloat) pointer_y;
352 } else {
353 /* The zoom center is the midle of the window. */
354 x = rt->wid_size->width / 2.0;
355 y = rt->wid_size->height / 2.0;
358 matrix_zoom(ratio, x, y);
359 refresh(REFRESH_IMAGE | REFRESH_STATUS | APPEND_HISTORY);
362 static gboolean refresh_image(void)
364 idle_id = 0;
365 refresh(REFRESH_IMAGE);
367 return FALSE;
370 void refresh(gint what)
372 /* GTK does that automatically but with more overhead. */
373 if (what & REFRESH_IMAGE) {
374 if (idle_id == 0)
375 idle_id = gtk_idle_add_priority(GDK_PRIORITY_REDRAW,
376 (GtkFunction) redraw, NULL);
378 } else if (what & REFRESH_BURST) {
379 if (options->fps <= 0)
380 refresh(REFRESH_IMAGE);
382 else if (idle_id == 0)
383 idle_id = gtk_timeout_add(1000 / options->fps,
384 (GtkFunction) refresh_image, NULL);
386 } else if (what & REFRESH_NOW) {
387 if (idle_id != 0)
388 g_source_remove(idle_id);
390 redraw();
393 if (what & REFRESH_STATUS)
394 update_status_bar();
396 if (what & APPEND_HISTORY)
397 append_history();