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 * The MODELVIEW matrix *
23 ************************/
25 #include <string.h> /* memcpy() */
29 #include "math_floats.h" /* cosf(), sinf(), ... */
32 #include "gliv-image.h"
35 extern options_struct
*options
;
36 extern GlivImage
*current_image
;
39 * OpenGL uses a transposed matrix, we use a 'normal' one,
40 * we transpose it just before glLoadMatrix().
45 * 0 0 1 0 | 8 9 10 11 \ constant so
46 * 0 0 0 1 | 12 13 14 15 / unused.
49 #define MATRIX_C1 matrix[0]
50 #define MATRIX_S1 matrix[1]
51 #define MATRIX_X matrix[3]
52 #define MATRIX_S2 matrix[4]
53 #define MATRIX_C2 matrix[5]
54 #define MATRIX_Y matrix[7]
56 static gfloat matrix
[8] = {
57 /* We only use two rows. */
62 /* Used to know if the OpenGL matrix and this one are in sync. */
63 static gboolean matrix_changed
= TRUE
;
65 static void touch_matrix(void)
67 matrix_changed
= TRUE
;
70 G_GNUC_PURE gfloat
get_matrix_zoom(void)
73 * c1 == zoom*cos, s1 == zoom*sin
74 * cos^2 + sin^2 == 1 => c1^2 + s1^2 == zoom^2
76 return hypotf(MATRIX_C1
, MATRIX_S1
);
79 /* To be displayed in the status bar. */
80 G_GNUC_PURE gfloat
get_matrix_angle(void)
84 cosine
= MATRIX_C1
/ get_matrix_zoom();
85 angle
= acosf(cosine
);
88 /* Negative sine => negative angle. */
94 /* OpenGL coordinates to window coordinates. */
95 static void point_coord(gfloat x
, gfloat y
, gfloat
* res_x
, gfloat
* res_y
)
97 /* OpenGL coordinates through the modelview matrix. */
98 *res_x
= MATRIX_C1
* x
+ MATRIX_S1
* y
+ MATRIX_X
;
99 *res_y
= MATRIX_S2
* x
+ MATRIX_C2
* y
+ MATRIX_Y
;
101 /* And now through the projection matrix. */
102 *res_x
+= rt
->wid_size
->width
/ 2.0;
103 *res_y
+= rt
->wid_size
->height
/ 2.0;
106 static gfloat
min4(gfloat a
, gfloat b
, gfloat c
, gfloat d
)
117 static gfloat
max4(gfloat a
, gfloat b
, gfloat c
, gfloat d
)
128 /* Convenient function: a == b ? */
129 G_GNUC_PURE gboolean
float_equal(gfloat a
, gfloat b
)
131 return fabsf(a
- b
) < 1e-3;
134 void get_matrix_bounding_box(gfloat
* min_x
, gfloat
* max_x
,
135 gfloat
* min_y
, gfloat
* max_y
)
137 gfloat x0
, y0
, x1
, y1
;
138 gfloat x2
, y2
, x3
, y3
;
139 gfloat half_w
, half_h
;
141 half_w
= current_image
->width
/ 2.0;
142 half_h
= current_image
->height
/ 2.0;
144 point_coord(-half_w
, -half_h
, &x0
, &y0
);
145 point_coord(half_w
, -half_h
, &x1
, &y1
);
146 point_coord(-half_w
, half_h
, &x2
, &y2
);
147 point_coord(half_w
, half_h
, &x3
, &y3
);
149 *min_x
= min4(x0
, x1
, x2
, x3
);
150 *max_x
= max4(x0
, x1
, x2
, x3
);
151 *min_y
= min4(y0
, y1
, y2
, y3
);
152 *max_y
= max4(y0
, y1
, y2
, y3
);
155 /*** Input, output. ***/
157 void write_gl_matrix(void)
160 static gfloat transposed
[16] = {
168 if (matrix_changed
) {
169 transposed
[0] = MATRIX_C1
;
170 transposed
[5] = MATRIX_C2
;
171 transposed
[4] = MATRIX_S1
;
172 transposed
[1] = MATRIX_S2
;
173 transposed
[12] = MATRIX_X
;
174 transposed
[13] = MATRIX_Y
;
176 glLoadMatrixf(transposed
);
177 matrix_changed
= FALSE
;
181 /* In parameters dest and src, NULL is the current matrix. */
182 void matrix_cpy(gfloat
* dest
, gfloat
* src
)
187 } else if (src
== NULL
)
190 memcpy(dest
, src
, sizeof(matrix
));
193 /*** Informations gathering about the matrix. ***/
195 G_GNUC_PURE
static gboolean
angle_is_right(void)
199 modulo
= fmodf(get_matrix_angle(), PI
/ 2.0);
201 return float_equal(modulo
, 0.0);
204 gboolean
matrix_tile_visible(tile_dim
* tile
)
206 static guint select_buffer
[4];
208 glSelectBuffer(4, select_buffer
);
209 glRenderMode(GL_SELECT
);
214 glRectf(tile
->x0
, tile
->y0
, tile
->x1
, tile
->y1
);
216 return glRenderMode(GL_RENDER
) > 0;
219 G_GNUC_PURE gboolean
is_matrix_symmetry(void)
222 * c1 == c2 => rotation, c1 == -c2 => symmetry.
223 * s1 == -s2 => rotation, s1 == s2 => symmetry.
225 * Since c1^2 + s1^2 == 1, we use c1 only if far enough from 0.
228 return (MATRIX_C1
< -0.5 || MATRIX_C1
> 0.5) ?
229 !float_equal(MATRIX_C1
, MATRIX_C2
) :
230 !float_equal(MATRIX_S1
, -MATRIX_S2
);
233 G_GNUC_PURE gboolean
get_matrix_has_changed(void)
235 return matrix_changed
;
238 G_GNUC_PURE gboolean
is_filtering_needed(void)
240 return (float_equal(get_matrix_zoom(), 1.0) == FALSE
) ||
241 (angle_is_right() == FALSE
);
244 /*** Operations on the matrix. ***/
246 /* Returns FALSE if the image is already maximised. */
247 gboolean
matrix_set_max_zoom(gint width
, gint height
, gboolean do_it
)
249 gfloat min_x
, max_x
, min_y
, max_y
, zoom
;
251 if (current_image
== NULL
)
254 if (do_it
== FALSE
&& (float_equal(MATRIX_X
, 0.0) == FALSE
||
255 float_equal(MATRIX_Y
, 0.0) == FALSE
))
256 /* Image not centered. */
260 width
= rt
->wid_size
->width
;
263 height
= rt
->wid_size
->height
;
265 if ((options
->maximize
== FALSE
&&
266 (current_image
->width
< width
&& current_image
->height
< height
)) ||
267 (options
->scaledown
== FALSE
&&
268 (current_image
->width
> width
|| current_image
->height
> height
)))
271 get_matrix_bounding_box(&min_x
, &max_x
, &min_y
, &max_y
);
272 zoom
= MIN(width
/ (max_x
- min_x
), height
/ (max_y
- min_y
));
274 if (float_equal(zoom
, 1.0) == FALSE
||
275 float_equal(MATRIX_X
, 0.0) == FALSE
||
276 float_equal(MATRIX_Y
, 0.0) == FALSE
) {
279 matrix_zoom(zoom
, 0.0, 0.0);
280 MATRIX_X
= MATRIX_Y
= 0.0;
288 void matrix_fit(gboolean fit_w
, gboolean fit_h
)
290 gfloat min_x
, max_x
, min_y
, max_y
, zoom
= INFINITY
;
292 get_matrix_bounding_box(&min_x
, &max_x
, &min_y
, &max_y
);
296 zoom
= MIN(zoom
, (gfloat
) rt
->wid_size
->width
/ (max_x
- min_x
));
301 zoom
= MIN(zoom
, (gfloat
) rt
->wid_size
->height
/ (max_y
- min_y
));
304 matrix_zoom(zoom
, rt
->wid_size
->width
/ 2.0, rt
->wid_size
->height
/ 2.0);
308 static void reset_matrix(gfloat
* mat
)
320 void matrix_reset(void)
322 reset_matrix(matrix
);
328 * cos sin 0 0 | c1*cos+s2*sin s1*cos+c2*sin 0 x*cos+y*sin
329 * -sin cos 0 0 | -c1*sin+s2*cos -s1*sin+c2*cos 0 -x*sin+y*cos
333 void matrix_rotate(gfloat angle
)
336 gfloat c1
, s1
, c2
, s2
, x
, y
;
339 /* Do we maximize after rotating? */
340 zoom
= (options
->maximize
|| options
->scaledown
) &&
341 (matrix_set_max_zoom(-1, -1, FALSE
) == FALSE
);
343 cosine
= cosf(angle
);
346 /* Backup, as we'll modify them. */
354 MATRIX_C1
= c1
* cosine
+ s2
* sine
;
355 MATRIX_S1
= s1
* cosine
+ c2
* sine
;
356 MATRIX_S2
= -c1
* sine
+ s2
* cosine
;
357 MATRIX_C2
= -s1
* sine
+ c2
* cosine
;
359 MATRIX_X
= x
* cosine
+ y
* sine
;
360 MATRIX_Y
= -x
* sine
+ y
* cosine
;
363 matrix_set_max_zoom(-1, -1, TRUE
);
368 void matrix_move(gfloat x
, gfloat y
)
376 /* (x, y): zoom center. */
377 void matrix_zoom(gfloat ratio
, gfloat x
, gfloat y
)
379 gfloat offset_x
, offset_y
;
381 offset_x
= rt
->wid_size
->width
/ 2.0 - x
;
382 offset_y
= rt
->wid_size
->height
/ 2.0 - y
;
384 matrix_move(offset_x
, offset_y
);
393 matrix_move(-offset_x
, -offset_y
);
396 void flip_matrix(gfloat
* mat
, gboolean h_flip
)
398 /* Flip either the x or y row. */
399 gint id
= h_flip
? 4 : 0;
403 /* mat[id + 2] is 0.0. */
407 void matrix_flip_h(void)
410 flip_matrix(matrix
, TRUE
);
414 void matrix_flip_v(void)
417 flip_matrix(matrix
, FALSE
);
421 void matrix_set_initial_position(void)
423 gfloat min_x
, max_x
, min_y
, max_y
;
424 enum image_position pos
= options
->initial_pos
;
426 if (pos
== POSITION_KEEP
)
429 get_matrix_bounding_box(&min_x
, &max_x
, &min_y
, &max_y
);
431 if (pos
== POSITION_CENTER
) {
432 matrix_move((rt
->wid_size
->width
- min_x
- max_x
) / 2.0,
433 (rt
->wid_size
->height
- min_y
- max_y
) / 2.0);
437 if (min_x
< 0.0 || max_x
> (gfloat
) rt
->wid_size
->width
) {
438 if (pos
== POSITION_TOP_LEFT
|| pos
== POSITION_BOTTOM_LEFT
)
439 matrix_move(-min_x
, 0.0);
441 matrix_move((gfloat
) rt
->wid_size
->width
- max_x
, 0.0);
444 if (min_y
< 0.0 || max_y
> (gfloat
) rt
->wid_size
->height
) {
445 if (pos
== POSITION_TOP_LEFT
|| pos
== POSITION_TOP_RIGHT
)
446 matrix_move(0.0, -min_y
);
448 matrix_move(0.0, (gfloat
) rt
->wid_size
->height
- max_y
);
452 void configure_matrix(GlivImage
* im
)
454 GlivImage
*old
= current_image
;
457 if (im
->first_image
|| options
->keep_transfo
== FALSE
) {
459 if (options
->maximize
|| options
->scaledown
)
460 matrix_set_max_zoom(-1, -1, TRUE
);
463 matrix_set_initial_position();
468 gfloat
*new_matrix(void)
470 gfloat
*new_mat
= g_new(gfloat
, 8);
472 reset_matrix(new_mat
);
476 gfloat
*get_matrix_for_image(GlivImage
* im
)
478 gfloat current_matrix
[8];
481 matrix_cpy(current_matrix
, NULL
);
482 configure_matrix(im
);
484 new_mat
= new_matrix();
485 matrix_cpy(new_mat
, NULL
);
487 matrix_cpy(NULL
, current_matrix
);