Merged older cs.po file with newest pot file.
[gliv/czech_localization.git] / src / textures.c
blob4c7b162b84bf6dbc45b94fd2f7fea0a76fcce5eb
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 * Textures management *
23 ***********************/
25 #include "gliv.h"
26 #include "textures.h"
27 #include "math_floats.h" /* powf() */
28 #include "options.h"
29 #include "params.h"
30 #include "thread.h"
31 #include "dithering.h"
32 #include "opengl.h"
33 #include "tiling.h"
35 extern rt_struct *rt;
36 extern options_struct *options;
37 extern GtkWidget *gl_widget;
39 /* Draws a piece of a multi-textures image or a whole mono-texture one. */
40 static void draw_rectangle(gfloat tex_x0, gfloat tex_x1,
41 gfloat tex_y0, gfloat tex_y1,
42 gfloat vert_x0, gfloat vert_x1,
43 gfloat vert_y0, gfloat vert_y1)
46 * (tex_x0 ; tex_y0) : Origin of the interesting part of the texture.
47 * (tex_x1 ; tex_y1) : Extremity of the interesting part of the texture.
48 * (vert_x0 ; vert_y0) : Origin of the rectangle.
49 * (vert_x1 ; vert_y1) : Extremity of the rectangle.
52 glBegin(GL_QUADS);
54 glTexCoord2f(tex_x0, tex_y0);
55 glVertex2f(vert_x0, vert_y0);
57 glTexCoord2f(tex_x1, tex_y0);
58 glVertex2f(vert_x1, vert_y0);
60 glTexCoord2f(tex_x1, tex_y1);
61 glVertex2f(vert_x1, vert_y1);
63 glTexCoord2f(tex_x0, tex_y1);
64 glVertex2f(vert_x0, vert_y1);
66 glEnd();
70 * Called twice for each rectangle to get rectangle
71 * coordinates to put in the display list.
73 static void compute_coordinates(texture_map * map,
74 gfloat * vert0, gfloat * vert1,
75 gfloat * tex0, gfloat * tex1,
76 gint map_dim, gint pos, gint size)
78 gboolean last = pos + size >= map_dim;
80 *vert0 = -map_dim / 2.0 + pos;
82 if (pos == 0) {
83 *tex0 = 0.0;
85 if (last) {
86 /* Single tile. */
87 *vert1 = -*vert0;
88 *tex1 = (gfloat) map_dim / size;
89 } else {
91 * First tile.
92 * - 1.0: there is only one overlapping pixel.
94 *vert1 = *vert0 + size - 1.0;
95 *tex1 = (size - 0.5) / size;
97 } else {
98 *tex0 = 0.5 / size;
99 if (last) {
100 /* Last tile. */
101 *vert1 = map_dim / 2.0 - 1.0;
102 *tex1 = (gfloat) (map_dim - pos) / size;
103 } else {
104 /* Middle tiles. */
105 *vert1 = *vert0 + size - 1.0;
106 *tex1 = 1.0 - *tex0;
110 #if 0
111 printf("pos:%d map_dim:%d size:%d v0:%f v1:%f t0:%f t1:%f\n", pos, map_dim,
112 size, *vert0, *vert1, *tex0, *tex1);
113 #endif
116 static void rectangle(texture_map * map, tile_dim * tile, gint x, gint y,
117 gint w, gint h, gint level)
119 gfloat ty0, ty1, tx0, tx1;
120 gfloat x0, x1, y0, y1;
121 gfloat mipmap_coeff;
123 mipmap_coeff = powf(MIPMAP_RATIO, -level);
125 compute_coordinates(map, &x0, &x1, &tx0, &tx1, map->width, x, w);
126 compute_coordinates(map, &y0, &y1, &ty0, &ty1, map->height, y, h);
128 x0 *= mipmap_coeff;
129 x1 *= mipmap_coeff;
130 y0 *= mipmap_coeff;
131 y1 *= mipmap_coeff;
133 draw_rectangle(tx0, tx1, ty0, ty1, x0, x1, y0, y1);
135 /* Used when drawing, to know which tiles are hidden. */
136 tile->x0 = x0;
137 tile->y0 = y0;
138 tile->x1 = x1;
139 tile->y1 = y1;
142 /* Shortcut to OpenGL parameters common to all textures. */
143 static void texture_parameter(void)
145 /* We don't change the filter for small mip maps. */
146 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
147 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
149 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
150 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
153 typedef struct {
154 gint x, y;
155 gint w, h;
156 texture_map *map;
157 GdkPixbuf *tile_max_size;
158 gboolean has_alpha;
159 } texture_data;
161 /* Runs in a separate thread. */
162 static GdkPixbuf *make_tile(texture_data * data)
164 GdkPixbuf *tile;
165 gint real_w, real_h;
167 if (data->x == 0 && data->y == 0 &&
168 data->w == data->map->width && data->h == data->map->height) {
169 /* Special case, the image is OK for an OpenGL texture. */
170 g_object_ref(data->map->pixbuf);
171 return data->map->pixbuf;
174 if (data->w == rt->max_texture_size && data->h == rt->max_texture_size) {
175 tile = data->tile_max_size;
176 g_object_ref(tile);
177 } else
178 tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, data->has_alpha, 8,
179 data->w, data->h);
181 real_w = data->w;
182 if (data->x + data->w >= data->map->width)
183 real_w = data->map->width - data->x;
185 real_h = data->h;
186 if (data->y + data->h >= data->map->height)
187 real_h = data->map->height - data->y;
189 gdk_pixbuf_copy_area(data->map->pixbuf, data->x, data->y, real_w, real_h,
190 tile, 0, 0);
192 if (data->w != real_w)
193 /* Right border: copy the last column. */
194 gdk_pixbuf_copy_area(tile, real_w - 1, 0, 1, real_h, tile, real_w, 0);
196 if (data->h != real_h) {
197 /* Lower corner: copy the last line. */
198 gdk_pixbuf_copy_area(tile, 0, real_h - 1, real_w, 1, tile, 0, real_h);
200 if (data->w != real_w)
201 /* Lower-right corner: copy the last pixel. */
202 gdk_pixbuf_copy_area(tile, real_w - 1, real_h - 1, 1, 1,
203 tile, real_w, real_h);
206 return tile;
209 static void make_texture(GlivImage * im, gint level, gint x, gint y,
210 gint w, gint h)
212 gint texture0, texture1;
213 static GdkPixbuf *tile_max_size = NULL;
214 texture_data *data;
215 texture_map *map;
216 GdkPixbuf *tile;
218 if (tile_max_size == NULL &&
219 w == rt->max_texture_size && h == rt->max_texture_size)
220 tile_max_size =
221 gdk_pixbuf_new(GDK_COLORSPACE_RGB, im->has_alpha, 8,
222 rt->max_texture_size, rt->max_texture_size);
224 map = im->maps + level;
226 data = g_new(texture_data, 1);
227 data->map = map;
228 data->tile_max_size = tile_max_size;
229 if (data->tile_max_size)
230 g_object_ref(tile_max_size);
231 data->has_alpha = im->has_alpha;
232 data->x = x;
233 data->y = y;
234 data->w = w;
235 data->h = h;
237 texture_parameter();
239 /* We may change the current texture while waiting for the thread. */
240 glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture0);
241 tile = do_threaded((GThreadFunc) make_tile, data);
242 glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture1);
244 if (data->tile_max_size)
245 g_object_unref(data->tile_max_size);
247 if (texture0 != texture1)
248 glBindTexture(GL_TEXTURE_2D, texture0);
250 glTexImage2D(GL_TEXTURE_2D, 0, 3 + im->has_alpha, w, h, 0,
251 im->has_alpha ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE,
252 gdk_pixbuf_get_pixels(tile));
254 if (tile)
255 g_object_unref(tile);
257 if (x + w >= map->width && y + h >= map->height && tile_max_size != NULL) {
258 g_object_unref(tile_max_size);
259 tile_max_size = NULL;
262 g_free(data);
265 void prioritize_textures(GlivImage * im, gboolean is_prio)
267 GLclampf *priorities;
268 gint i;
270 if (im == NULL)
271 return;
274 * im->maps[0] has the biggest nb_tiles, so we don't change the
275 * priorities array for each map, we simply use the biggest for all.
277 priorities = g_new(GLclampf, im->maps[0].nb_tiles);
278 for (i = 0; i < im->maps[0].nb_tiles; i++)
279 priorities[i] = (gfloat) is_prio;
281 for (i = 0; i < im->nb_maps; i++)
282 glPrioritizeTextures(im->maps[i].nb_tiles, im->maps[i].tex_ids,
283 priorities);
285 g_free(priorities);
289 static void compute_gl_dimensions(texture_map * map)
291 map->x_tiles = make_tiles(map->width);
292 map->y_tiles = make_tiles(map->height);
294 map->nb_tiles = map->x_tiles->nb_tiles * map->y_tiles->nb_tiles;
296 map->tex_ids = g_new(guint, map->nb_tiles);
297 glGenTextures(map->nb_tiles, map->tex_ids);
299 map->list = glGenLists(map->nb_tiles);
302 static void create_a_map(GlivImage * im, gint level)
304 gint x, y, h, w;
305 gint id = 0;
306 texture_map *map;
307 struct tiles_iterator *x_iterator, *y_iterator;
309 map = im->maps + level;
310 compute_gl_dimensions(map);
312 map->tiles = g_new(tile_dim, map->nb_tiles);
314 y = 0;
315 y_iterator = tiles_iterator_new(map->y_tiles);
317 while ((h = tiles_iterator_next(y_iterator)) > 0) {
318 x_iterator = tiles_iterator_new(map->x_tiles);
319 x = 0;
320 while ((w = tiles_iterator_next(x_iterator)) > 0) {
322 glBindTexture(GL_TEXTURE_2D, map->tex_ids[id]);
323 #if 0
324 printf("%d: [x:%d y:%d w:%d h:%d]\n", id, x, y, w, h);
325 #endif
327 make_texture(im, level, x, y, w, h);
329 glNewList(map->list + id, GL_COMPILE);
331 /* Redundant but need to be in the display list. */
332 glBindTexture(GL_TEXTURE_2D, map->tex_ids[id]);
334 rectangle(map, map->tiles + id, x, y, w, h, level);
335 glEndList();
337 id++;
338 x += w - 1;
340 g_free(x_iterator);
341 y += h - 1;
343 g_free(y_iterator);
345 destroy_tiles(map->x_tiles);
346 destroy_tiles(map->y_tiles);
349 /*** Dithering ***/
351 typedef struct {
352 texture_map *map;
353 gboolean destroy_old;
354 } dithering;
356 /* Runs in a separate thread. */
357 static GdkPixbuf *_dither(dithering * todo)
359 GdkPixbuf *old, *dithered;
361 old = todo->map->pixbuf;
363 if (todo->destroy_old)
364 dithered = old;
365 else
366 dithered = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(old),
367 gdk_pixbuf_get_has_alpha(old),
368 gdk_pixbuf_get_bits_per_sample(old),
369 gdk_pixbuf_get_width(old),
370 gdk_pixbuf_get_height(old));
372 dither_pixbuf(dithered, old);
374 todo->map->pixbuf = dithered;
376 return old;
379 /* Returns the original pixbuf if not asked to be destroyed. */
380 static GdkPixbuf *dither(texture_map * map, gboolean destroy_old)
382 dithering todo;
384 todo.map = map;
385 todo.destroy_old = destroy_old;
387 return do_threaded((GThreadFunc) _dither, &todo);
390 /* Returns the original pixbuf. */
391 static GdkPixbuf *create_first_map(GlivImage * im)
393 texture_map *map;
394 GdkPixbuf *orig;
396 map = im->maps;
397 map->width = im->width;
398 map->height = im->height;
400 if (options->dither)
401 /* We keep the pixbuf only if mipmaps are used. */
402 orig = dither(map, (im->nb_maps == 1));
403 else
404 orig = map->pixbuf;
406 create_a_map(im, 0);
408 if (options->dither)
409 g_object_unref(map->pixbuf);
411 return orig;
414 /* Wrapper for do_threaded(). */
415 typedef struct {
416 GdkPixbuf *image;
417 gint width;
418 gint height;
419 GdkInterpType interp;
420 } rescale_arg;
422 static GdkPixbuf *rescale(rescale_arg * arg)
424 return gdk_pixbuf_scale_simple(arg->image, arg->width, arg->height,
425 arg->interp);
428 void create_maps(GlivImage * im)
430 gint level;
431 texture_map *map;
432 GdkPixbuf *previous;
433 gint width, height;
434 rescale_arg *arg;
436 previous = create_first_map(im);
438 if (im->nb_maps == 1) {
439 /* No mipmaps. */
441 if (options->dither == FALSE)
442 g_object_unref(previous);
444 prioritize_textures(im, FALSE);
445 return;
448 width = im->width;
449 height = im->height;
450 arg = g_new(rescale_arg, 1);
452 for (level = 1; level < im->nb_maps; level++) {
454 map = im->maps + level;
456 width = map->width = width * MIPMAP_RATIO;
457 height = map->height = height * MIPMAP_RATIO;
459 arg->image = previous;
460 arg->width = width;
461 arg->height = height;
462 arg->interp = GDK_INTERP_BILINEAR;
463 map->pixbuf = do_threaded((GThreadFunc) rescale, arg);
465 g_object_unref(previous);
467 if (options->dither)
469 * We must dither each map since rescaling does not preserve it.
470 * We keep the original image except for the last level.
472 previous = dither(map, (level == im->nb_maps - 1));
474 create_a_map(im, level);
476 if (options->dither)
477 g_object_unref(map->pixbuf);
478 else
479 previous = map->pixbuf;
482 if (options->dither == FALSE)
483 g_object_unref(previous);
485 prioritize_textures(im, FALSE);