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 G_GNUC_PURE gfloat
get_matrix_zoom(void)
68 * c1 == zoom*cos, s1 == zoom*sin
69 * cos^2 + sin^2 == 1 => c1^2 + s1^2 == zoom^2
71 return hypotf(MATRIX_C1
, MATRIX_S1
);
74 /* To be displayed in the status bar. */
75 G_GNUC_PURE gfloat
get_matrix_angle(void)
79 cosine
= MATRIX_C1
/ get_matrix_zoom();
80 angle
= acosf(cosine
);
83 /* Negative sine => negative angle. */
89 /* OpenGL coordinates to window coordinates. */
90 static void point_coord(gfloat x
, gfloat y
, gfloat
* res_x
, gfloat
* res_y
)
92 /* OpenGL coordinates through the modelview matrix. */
93 *res_x
= MATRIX_C1
* x
+ MATRIX_S1
* y
+ MATRIX_X
;
94 *res_y
= MATRIX_S2
* x
+ MATRIX_C2
* y
+ MATRIX_Y
;
96 /* And now through the projection matrix. */
97 *res_x
+= rt
->wid_size
->width
/ 2.0;
98 *res_y
+= rt
->wid_size
->height
/ 2.0;
101 static gfloat
min4(gfloat a
, gfloat b
, gfloat c
, gfloat d
)
112 static gfloat
max4(gfloat a
, gfloat b
, gfloat c
, gfloat d
)
123 /* Convenient function: a == b ? */
124 G_GNUC_PURE gboolean
float_equal(gfloat a
, gfloat b
)
126 return fabsf(a
- b
) < 1e-3;
129 void get_matrix_bounding_box(gfloat
* min_x
, gfloat
* max_x
,
130 gfloat
* min_y
, gfloat
* max_y
)
132 gfloat x0
, y0
, x1
, y1
;
133 gfloat x2
, y2
, x3
, y3
;
134 gfloat half_w
, half_h
;
136 half_w
= current_image
->width
/ 2.0;
137 half_h
= current_image
->height
/ 2.0;
139 point_coord(-half_w
, -half_h
, &x0
, &y0
);
140 point_coord(half_w
, -half_h
, &x1
, &y1
);
141 point_coord(-half_w
, half_h
, &x2
, &y2
);
142 point_coord(half_w
, half_h
, &x3
, &y3
);
144 *min_x
= min4(x0
, x1
, x2
, x3
);
145 *max_x
= max4(x0
, x1
, x2
, x3
);
146 *min_y
= min4(y0
, y1
, y2
, y3
);
147 *max_y
= max4(y0
, y1
, y2
, y3
);
150 /*** Input, output. ***/
152 void write_gl_matrix(void)
155 static gfloat transposed
[16] = {
163 if (matrix_changed
) {
164 transposed
[0] = MATRIX_C1
;
165 transposed
[5] = MATRIX_C2
;
166 transposed
[4] = MATRIX_S1
;
167 transposed
[1] = MATRIX_S2
;
168 transposed
[12] = MATRIX_X
;
169 transposed
[13] = MATRIX_Y
;
171 glLoadMatrixf(transposed
);
172 matrix_changed
= FALSE
;
176 /* In parameters dest and src, NULL is the current matrix. */
177 void matrix_cpy(gfloat
* dest
, gfloat
* src
)
181 matrix_changed
= TRUE
;
182 } else if (src
== NULL
)
185 memcpy(dest
, src
, sizeof(matrix
));
188 /*** Informations gathering about the matrix. ***/
190 G_GNUC_PURE
static gboolean
angle_is_right(void)
194 modulo
= fmodf(get_matrix_angle(), PI
/ 2.0);
196 return float_equal(modulo
, 0.0);
199 gboolean
matrix_tile_visible(tile_dim
* tile
)
201 static guint select_buffer
[4];
203 glSelectBuffer(4, select_buffer
);
204 glRenderMode(GL_SELECT
);
209 glRectf(tile
->x0
, tile
->y0
, tile
->x1
, tile
->y1
);
211 return glRenderMode(GL_RENDER
) > 0;
214 G_GNUC_PURE gboolean
is_matrix_symmetry(void)
217 * c1 == c2 => rotation, c1 == -c2 => symmetry.
218 * s1 == s2 => rotation, s1 == -s2 => symmetry.
220 * Since c1^2 + s1^2 == 1, we use c1 only if far enough from 0.
223 return (MATRIX_C1
< -0.5 || MATRIX_C1
> 0.5) ?
224 !float_equal(MATRIX_C1
, MATRIX_C2
) : !float_equal(MATRIX_S1
, MATRIX_S2
);
227 G_GNUC_PURE gboolean
get_matrix_has_changed(void)
229 return matrix_changed
;
232 G_GNUC_PURE gboolean
is_filtering_needed(void)
234 return (float_equal(get_matrix_zoom(), 1.0) == FALSE
) ||
235 (angle_is_right() == FALSE
);
238 /*** Operations on the matrix. ***/
240 /* Returns FALSE if the image is already maximised. */
241 gboolean
matrix_set_max_zoom(gint width
, gint height
, gboolean do_it
)
243 gfloat min_x
, max_x
, min_y
, max_y
, zoom
;
245 if (current_image
== NULL
)
248 if (do_it
== FALSE
&& (float_equal(MATRIX_X
, 0.0) == FALSE
||
249 float_equal(MATRIX_Y
, 0.0) == FALSE
))
250 /* Image not centered. */
254 width
= rt
->wid_size
->width
;
257 height
= rt
->wid_size
->height
;
259 if ((options
->maximize
== FALSE
&&
260 (current_image
->width
< width
&& current_image
->height
< height
)) ||
261 (options
->scaledown
== FALSE
&&
262 (current_image
->width
> width
|| current_image
->height
> height
)))
265 get_matrix_bounding_box(&min_x
, &max_x
, &min_y
, &max_y
);
266 zoom
= MIN(width
/ (max_x
- min_x
), height
/ (max_y
- min_y
));
268 if (float_equal(zoom
, 1.0) == FALSE
||
269 float_equal(MATRIX_X
, 0.0) == FALSE
||
270 float_equal(MATRIX_Y
, 0.0) == FALSE
) {
273 matrix_zoom(zoom
, 0.0, 0.0);
274 MATRIX_X
= MATRIX_Y
= 0.0;
282 void matrix_reset(void)
286 for (i
= 0; i
< 8; i
++)
287 matrix
[i
] = (gfloat
) (i
% 5 == 0);
289 matrix_changed
= TRUE
;
294 * cos sin 0 0 | c1*cos+s2*sin s1*cos+c2*sin 0 x*cos+y*sin
295 * -sin cos 0 0 | -c1*sin+s2*cos -s1*sin+c2*cos 0 -x*sin+y*cos
299 void matrix_rotate(gfloat angle
)
302 gfloat c1
, s1
, c2
, s2
, x
, y
;
305 /* Do we maximize after rotating? */
306 zoom
= (options
->maximize
|| options
->scaledown
) &&
307 (matrix_set_max_zoom(-1, -1, FALSE
) == FALSE
);
309 cosine
= cosf(angle
);
312 /* Backup, as we'll modify them. */
320 MATRIX_C1
= c1
* cosine
+ s2
* sine
;
321 MATRIX_S1
= s1
* cosine
+ c2
* sine
;
322 MATRIX_S2
= -c1
* sine
+ s2
* cosine
;
323 MATRIX_C2
= -s1
* sine
+ c2
* cosine
;
325 MATRIX_X
= x
* cosine
+ y
* sine
;
326 MATRIX_Y
= -x
* sine
+ y
* cosine
;
329 matrix_set_max_zoom(-1, -1, TRUE
);
331 matrix_changed
= TRUE
;
334 void matrix_move(gfloat x
, gfloat y
)
339 matrix_changed
= TRUE
;
342 /* (x, y): zoom center. */
343 void matrix_zoom(gfloat ratio
, gfloat x
, gfloat y
)
345 gfloat offset_x
, offset_y
;
347 offset_x
= rt
->wid_size
->width
/ 2.0 - x
;
348 offset_y
= rt
->wid_size
->height
/ 2.0 - y
;
350 matrix_move(offset_x
, offset_y
);
359 matrix_move(-offset_x
, -offset_y
);
362 static void flip(guchar id
)
364 /* Flip either the x or y row. */
367 matrix
[id
+ 1] *= -1.0;
368 /* matrix[id + 2] is 0.0. */
369 matrix
[id
+ 3] *= -1.0;
371 matrix_changed
= TRUE
;
374 void matrix_flip_h(void)
380 void matrix_flip_v(void)
386 void matrix_set_top_left(void)
388 gfloat min_x
, max_x
, min_y
, max_y
;
390 get_matrix_bounding_box(&min_x
, &max_x
, &min_y
, &max_y
);
392 if (min_x
< 0.0 || max_x
> (gfloat
) rt
->wid_size
->width
)
393 matrix_move(-min_x
, 0.0);
395 if (min_y
< 0.0 || max_y
> (gfloat
) rt
->wid_size
->height
)
396 matrix_move(0.0, -min_y
);
399 void configure_matrix(GlivImage
* im
)
401 GlivImage
*old
= current_image
;
405 if (options
->maximize
|| options
->scaledown
)
406 matrix_set_max_zoom(-1, -1, TRUE
);
408 else if (options
->no_center
)
409 matrix_set_top_left();
414 gfloat
*get_matrix_for_image(GlivImage
* im
)
416 gfloat current_matrix
[8];
419 matrix_cpy(current_matrix
, NULL
);
420 configure_matrix(im
);
422 new_matrix
= g_new(gfloat
, 8);
423 matrix_cpy(new_matrix
, NULL
);
425 matrix_cpy(NULL
, current_matrix
);