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>
28 #include "rendering.h"
30 #include "gliv-image.h"
33 #include "zoom_frame.h"
34 #include "scrollbars.h"
35 #include "gl_widget.h"
37 #include "files_list.h"
41 #include "next_image.h"
44 #include "callbacks.h"
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
)
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)
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
)
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;
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
) {
113 gushort alpha1
[3] = {
115 options
->alpha1
.green
,
118 gushort alpha2
[3] = {
120 options
->alpha2
.green
,
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
;
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. */
154 half_w
= rt
->wid_size
->width
/ 2.0;
155 half_h
= rt
->wid_size
->height
/ 2.0;
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
);
173 if (matrix_changed
== FALSE
)
174 /* glMatrixMode(GL_MODELVIEW); */
180 static gint
choose_mipmap_level(void)
182 gfloat zoom
, mipmap_ratio
= MIPMAP_RATIO
;
185 if (options
->mipmap
) {
186 zoom
= get_matrix_zoom();
188 while (mipmap_ratio
> zoom
&& level
< current_image
->nb_maps
) {
189 mipmap_ratio
*= MIPMAP_RATIO
;
194 /* Mipmaps should only be scaled down. */
198 static void display_last_image_notice(void)
200 static GdkGC
*gc
= NULL
;
204 int x
= 10, y
= 10, dx
= 5, dy
= 5;
207 msg
= get_image_notice();
211 pc
= gtk_widget_get_pango_context(gl_widget
);
212 pl
= pango_layout_new(pc
);
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
);
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)
241 gboolean alpha_checks
= need_alpha_checks();
248 mipmap_level
= choose_mipmap_level();
249 if (mipmap_level
== 0)
250 filtering
= is_filtering_needed();
255 glPushAttrib(GL_ENABLE_BIT
);
259 call_lists(filtering
, mipmap_level
);
265 /* Called by the timer when a redraw is needed. */
266 static gboolean
redraw(void)
268 static GdkGLDrawable
*gldrawable
= NULL
;
270 if (gldrawable
== NULL
)
272 gldrawable
= gtk_widget_get_gl_drawable(gl_widget
);
274 gdk_gl_drawable_wait_gdk(gldrawable
);
277 if (need_alpha_checks() == FALSE
) {
278 glDisable(GL_DITHER
);
279 glClear(GL_COLOR_BUFFER_BIT
);
283 if (current_image
!= NULL
)
284 draw_current_image();
286 gdk_gl_drawable_swap_buffers(gldrawable
);
289 gdk_gl_drawable_wait_gl(gldrawable
);
291 display_last_image_notice();
293 /* To remove the idle func. */
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
);
308 delete_selected_image();
309 update_window_title();
310 refresh(REFRESH_STATUS
);
317 if (options
->fullscreen
== FALSE
)
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();
325 configure_matrix(im
);
330 /* Called the first time an image is displayed. */
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
;
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
;
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)
365 refresh(REFRESH_IMAGE
);
370 void refresh(gint what
)
372 /* GTK does that automatically but with more overhead. */
373 if (what
& REFRESH_IMAGE
) {
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
) {
388 g_source_remove(idle_id
);
393 if (what
& REFRESH_STATUS
)
396 if (what
& APPEND_HISTORY
)