gliv-1.4.5
[gliv.git] / src / textures.c
blobf7df515d8c8030124e75b041c42285860aaf3e81
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 <booh@altern.org>
21 /***********************
22 * Texture management. *
23 ***********************/
25 #include "gliv.h"
27 #include <math.h> /* ceil() */
28 #include <GL/gl.h>
30 extern rt_struct *rt;
31 extern options_struct *options;
33 /* Lowest power of two bigger than the argument. */
34 G_GNUC_CONST static guint p2(guint p)
36 guint ret = 1;
38 while (ret < p)
39 ret <<= 1;
41 return ret;
44 /* Draws a piece of a multi-textures image or a whole mono-texture one. */
45 static void draw_rectangle(gdouble tex_x0, gdouble tex_x1,
46 gdouble tex_y0, gdouble tex_y1,
47 gdouble vert_x0, gdouble vert_x1,
48 gdouble vert_y0, gdouble vert_y1)
51 * (tex_x0 ; tex_y0) : Origin of the interesting part of the texture.
52 * (tex_x1 ; tex_y1) : Extremity of the interesting part of the texture.
53 * (vert_x0 ; vert_y0) : Origin of the rectangle.
54 * (vert_x1 ; vert_y1) : Extremity of the rectangle.
57 glBegin(GL_QUADS);
59 glTexCoord2d(tex_x0, tex_y0);
60 glVertex2d(vert_x0, vert_y0);
62 glTexCoord2d(tex_x1, tex_y0);
63 glVertex2d(vert_x1, vert_y0);
65 glTexCoord2d(tex_x1, tex_y1);
66 glVertex2d(vert_x1, vert_y1);
68 glTexCoord2d(tex_x0, tex_y1);
69 glVertex2d(vert_x0, vert_y1);
71 glEnd();
74 /*
75 * The tiler was originally based on code from
76 * evas by Carsten Haitzler (The Rasterman).
79 /*
80 * Called twice for each rectangle to get rectangle
81 * coordinates to put in the display list.
83 static void compute_coordinates(gliv_image * im,
84 gdouble * vert0, gdouble * vert1,
85 gdouble * tex0, gdouble * tex1,
86 gboolean is_x, guint id)
88 guint tiles, left, edge;
89 gint dim;
90 gdouble ratio;
92 if (is_x == TRUE) {
93 tiles = im->init->x_tiles;
94 dim = im->width;
95 left = im->init->x_left;
96 edge = im->init->x_edge;
97 } else {
98 tiles = im->init->y_tiles;
99 dim = im->height;
100 left = im->init->y_left;
101 edge = im->init->y_edge;
104 if (id != tiles - 1) {
105 *tex1 = (rt->max_texture_size - 2.0) / (rt->max_texture_size - 1.0);
106 if (id == 0) {
107 /* First tile. */
108 *vert0 = -dim / 2.0 - 1.0;
109 *vert1 = *vert0 + rt->max_texture_size - 1.0;
110 *tex0 = 0.0;
111 } else {
112 /* Middle tiles. */
113 *vert0 = -dim / 2.0 + id * (rt->max_texture_size - 2.0);
114 *vert1 = *vert0 + rt->max_texture_size - 2.0;
115 *tex0 = 1.0 / (rt->max_texture_size + 1.0);
116 *tex1 = (rt->max_texture_size - 2.0) / (rt->max_texture_size - 1.0);
118 } else {
119 *vert1 = dim / 2.0;
120 if (id == 0) {
121 /* Single tile. */
122 *vert0 = -(*vert1);
123 *tex0 = 0.0;
124 *tex1 = (gdouble) left / edge;
125 } else {
126 /* Last tile. */
127 *vert0 = -dim / 2.0 + id * (rt->max_texture_size - 2.0);
128 *vert1 = dim / 2.0 - 1.0;
129 *tex0 = 1.0 / (edge + 1.0);
130 *tex1 = (left - 1.0) / (edge - 1.0);
134 * For some textures smaller than 128x128 there are problems.
135 * The code below shouldn't be needed but it doesn't hurt and
136 * it is a workaround for these problems.
138 ratio = 128.0 / p2(dim - id * (rt->max_texture_size - 2));
139 if (ratio > 1.0) {
140 *tex0 /= ratio;
141 *tex1 /= ratio;
146 static void rectangle(gliv_image * im, tile_dim * tile, guint i, guint j)
148 gdouble ty0, ty1, tx0, tx1;
149 gdouble x0, x1, y0, y1;
151 compute_coordinates(im, &x0, &x1, &tx0, &tx1, TRUE, i);
152 compute_coordinates(im, &y0, &y1, &ty0, &ty1, FALSE, j);
154 draw_rectangle(tx0, tx1, ty0, ty1, x0, x1, y0, y1);
156 /* Used when drawing, to know which tiles are hidden. */
157 tile->x0 = x0;
158 tile->y0 = y0;
159 tile->x1 = x1;
160 tile->y1 = y1;
163 /* Shortcut to OpenGL parameters common to all textures. */
164 static void texture_parameter(void)
166 /* These filters will be changed but it's important to initialize them. */
167 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
168 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
170 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
171 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
174 static void make_texture(gliv_image * im, guint i, guint j)
176 static GdkPixbuf *tile_max_size = NULL;
177 gboolean last_tile;
178 GdkPixbuf *tile;
179 gint x, y, w, h, gl_w, gl_h;
181 if (tile_max_size == NULL && im->nb_tiles != 1)
182 /* First tile. */
183 tile_max_size = gdk_pixbuf_new(GDK_COLORSPACE_RGB, im->has_alpha, 8,
184 rt->max_texture_size,
185 rt->max_texture_size);
187 x = i * (rt->max_texture_size - 2);
188 y = j * (rt->max_texture_size - 2);
190 if (i + 1 == im->init->x_tiles) {
191 /* Last horizontal tile. */
192 w = im->width - x;
193 gl_w = (gint) p2((guint) w);
194 last_tile = TRUE;
195 } else {
196 w = gl_w = rt->max_texture_size;
197 last_tile = FALSE;
200 if (j + 1 == im->init->y_tiles) {
201 /* Last vertical tile. */
202 h = im->height - y;
203 gl_h = (gint) p2((guint) h);
204 last_tile = TRUE;
205 } else {
206 h = gl_h = rt->max_texture_size;
207 last_tile = FALSE;
210 texture_parameter();
213 * For some textures smaller than 128x128 there are problems.
214 * The code below shouldn't be needed but it doesn't hurt and
215 * it is a workaround for these problems.
217 gl_w = CLAMP(gl_w, 128, rt->max_texture_size);
218 gl_h = CLAMP(gl_h, 128, rt->max_texture_size);
220 if (gl_w == rt->max_texture_size && gl_h == rt->max_texture_size &&
221 tile_max_size != NULL)
223 tile = tile_max_size;
224 else
225 tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, im->has_alpha, 8, gl_w, gl_h);
227 gdk_pixbuf_copy_area(im->init->im, x, y, w, h, tile, 0, 0);
229 if (gl_w != w)
230 /* Right border : copy the last column. */
231 gdk_pixbuf_copy_area(tile, w - 1, 0, 1, h, tile, w, 0);
233 if (gl_h != h) {
234 /* Lower corner : copy the last line. */
235 gdk_pixbuf_copy_area(tile, 0, h - 1, w, 1, tile, 0, h);
237 if (gl_w != w)
238 /* Lower-right corner : copy the last pixel. */
239 gdk_pixbuf_copy_area(tile, w - 1, h - 1, 1, 1, tile, w, h);
242 glTexImage2D(GL_TEXTURE_2D, 0, 3 + im->has_alpha, gl_w, gl_h, 0,
243 (im->has_alpha == TRUE) ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE,
244 gdk_pixbuf_get_pixels(tile));
246 if (tile != tile_max_size)
247 gdk_pixbuf_unref(tile);
249 if (last_tile == TRUE && tile_max_size != NULL) {
250 gdk_pixbuf_unref(tile_max_size);
251 tile_max_size = NULL;
255 static void tiles_parameters(gliv_image * im, gboolean is_x)
257 guint *tiles, *left, *edge;
258 gint dim;
260 if (is_x == TRUE) {
261 tiles = &im->init->x_tiles;
262 left = &im->init->x_left;
263 edge = &im->init->x_edge;
264 dim = im->width;
265 } else {
266 tiles = &im->init->y_tiles;
267 left = &im->init->y_left;
268 edge = &im->init->y_edge;
269 dim = im->height;
272 if (dim <= 2)
273 *tiles = 1;
274 else
275 *tiles = (guint) ceil((dim - 2.0) / (rt->max_texture_size - 2.0));
277 *left = dim - (*tiles - 1) * (rt->max_texture_size - 2);
279 *edge = p2(*left);
282 static void compute_gl_dimensions(gliv_image * im)
284 tiles_parameters(im, TRUE);
285 tiles_parameters(im, FALSE);
287 im->nb_tiles = im->init->x_tiles * im->init->y_tiles;
289 im->tex_ids = g_new(guint, im->nb_tiles);
290 glGenTextures(im->nb_tiles, im->tex_ids);
292 im->list = glGenLists(im->nb_tiles);
295 void create_display_list(gliv_image * im)
297 guint i, j;
298 guint id = 0;
300 compute_gl_dimensions(im);
302 im->tiles = g_new(tile_dim, im->nb_tiles);
304 for (j = 0; j < im->init->y_tiles; j++)
305 for (i = 0; i < im->init->x_tiles; i++) {
306 glBindTexture(GL_TEXTURE_2D, im->tex_ids[id]);
307 make_texture(im, i, j);
309 glNewList(im->list + id, GL_COMPILE);
311 /* Redundant but need to be in the display list. */
312 glBindTexture(GL_TEXTURE_2D, im->tex_ids[id]);
314 rectangle(im, im->tiles + id, i, j);
315 glEndList();
317 id++;