OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
texture.c
Go to the documentation of this file.
1 /*
2  * mape - C4 Landscape.txt editor
3  *
4  * Copyright (c) 2005-2009, Armin Burgmeier
5  *
6  * Distributed under the terms of the ISC license; see accompanying file
7  * "COPYING" for details.
8  *
9  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
10  * See accompanying file "TRADEMARK" for details.
11  *
12  * To redistribute this file separately, substitute the full license texts
13  * for the above references.
14  */
15 
16 #include <string.h>
17 
20 #include "mape/texture.h"
21 
22 /* Declare private API */
25 
28 
32  GHashTable* texture_table;
35 };
36 
37 enum {
39 
40  /* read only */
44 };
45 
46 #define MAPE_TEXTURE_MAP_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), MAPE_TYPE_TEXTURE_MAP, MapeTextureMapPrivate))
47 
48 static GQuark mape_texture_map_error_quark;
49 
50 G_DEFINE_TYPE(MapeTextureMap, mape_texture_map, G_TYPE_OBJECT)
51 
52 /*
53  * Private helper functions
54  */
55 static guint32
56 mape_texture_map_get_average_color(GdkPixbuf* pixbuf)
57 {
58  int width;
59  int height;
60  int bpp;
61  const guchar* pixels;
62  int x, y;
63  int rowstride;
64  unsigned int accum[4];
65  double size;
66 
67  width = gdk_pixbuf_get_width(pixbuf);
68  height = gdk_pixbuf_get_height(pixbuf);
69  bpp = gdk_pixbuf_get_n_channels(pixbuf);
70  pixels = gdk_pixbuf_get_pixels(pixbuf);
71  rowstride = gdk_pixbuf_get_rowstride(pixbuf);
72  accum[0] = accum[1] = accum[2] = accum[3] = 0;
73 
74  for(y = 0; y < height; ++y)
75  {
76  for(x = 0; x < width; ++x)
77  {
78  accum[0] += (int)pixels[bpp*x + y*rowstride + 0];
79  accum[1] += (int)pixels[bpp*x + y*rowstride + 1];
80  accum[2] += (int)pixels[bpp*x + y*rowstride + 2];
81  if(bpp >= 4)
82  accum[3] += (int)pixels[bpp*x + y*rowstride + 3];
83  }
84  }
85 
86  size = width * height;
87  accum[0] = (unsigned int)(accum[0] / size + 0.5);
88  accum[1] = (unsigned int)(accum[1] / size + 0.5);
89  accum[2] = (unsigned int)(accum[2] / size + 0.5);
90  accum[3] = (unsigned int)(accum[3] / size + 0.5);
91  return accum[0] | (accum[1] << 8) | (accum[2] << 16) | (accum[3] << 24);
92 }
93 
94 /*
95  * GObject overrides.
96  */
97 
98 static void
99 mape_texture_map_init(MapeTextureMap* texture_map)
100 {
101  MapeTextureMapPrivate* priv;
102  priv = MAPE_TEXTURE_MAP_PRIVATE(texture_map);
103 
105  priv->texture_table = NULL;
106  priv->overload_materials = FALSE;
107  priv->overload_textures = FALSE;
108 }
109 
110 static void
111 mape_texture_map_finalize(GObject* object)
112 {
113  MapeTextureMap* texture_map;
114  MapeTextureMapPrivate* priv;
115 
116  texture_map = MAPE_TEXTURE_MAP(object);
117  priv = MAPE_TEXTURE_MAP_PRIVATE(texture_map);
118 
119  if(priv->texture_table != NULL)
120  g_hash_table_destroy(priv->texture_table);
122 
123  G_OBJECT_CLASS(mape_texture_map_parent_class)->finalize(object);
124 }
125 
126 static void
127 mape_texture_map_set_property(GObject* object,
128  guint prop_id,
129  const GValue* value,
130  GParamSpec* pspec)
131 {
132  switch(prop_id)
133  {
134  /* we have only readonly properties */
135  default:
136  G_OBJECT_WARN_INVALID_PROPERTY_ID(value, prop_id, pspec);
137  break;
138  }
139 }
140 
141 static void
142 mape_texture_map_get_property(GObject* object,
143  guint prop_id,
144  GValue* value,
145  GParamSpec* pspec)
146 {
147  MapeTextureMap* texture_map;
148  MapeTextureMapPrivate* priv;
149 
150  texture_map = MAPE_TEXTURE_MAP(object);
151  priv = MAPE_TEXTURE_MAP_PRIVATE(texture_map);
152 
153  switch(prop_id)
154  {
155  case PROP_N_TEXTURES:
156  if(priv->texture_table != NULL)
157  g_value_set_uint(value, g_hash_table_size(priv->texture_table));
158  else
159  g_value_set_uint(value, 0u);
160  break;
162  g_value_set_boolean(value, priv->overload_materials);
163  break;
165  g_value_set_boolean(value, priv->overload_textures);
166  break;
167  default:
168  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
169  break;
170  }
171 }
172 
173 /*
174  * Gype registration.
175  */
176 
177 static void
178 mape_texture_map_class_init(MapeTextureMapClass *class)
179 {
180  GObjectClass* object_class;
181 
182  object_class = G_OBJECT_CLASS(class);
183  mape_texture_map_parent_class =
184  G_OBJECT_CLASS(g_type_class_peek_parent(class));
185  g_type_class_add_private(class, sizeof(MapeTextureMapPrivate));
186 
187  object_class->finalize = mape_texture_map_finalize;
188  object_class->set_property = mape_texture_map_set_property;
189  object_class->get_property = mape_texture_map_get_property;
190 
191  mape_texture_map_error_quark =
192  g_quark_from_static_string("MAPE_TEXTURE_MAP_ERROR");
193 
194  g_object_class_install_property(
195  object_class,
197  g_param_spec_uint(
198  "n-textures",
199  "Texture count",
200  "The number of loaded textures",
201  0,
202  G_MAXUINT,
203  0,
204  G_PARAM_READABLE
205  )
206  );
207 
208  g_object_class_install_property(
209  object_class,
211  g_param_spec_boolean(
212  "overload-materials",
213  "Overload Materials",
214  "Whether to overload globally loaded materials",
215  FALSE,
216  G_PARAM_READABLE
217  )
218  );
219 
220  g_object_class_install_property(
221  object_class,
223  g_param_spec_boolean(
224  "overload-textures",
225  "Overload Textures",
226  "Whether to overload globally loaded textures",
227  FALSE,
228  G_PARAM_READABLE
229  )
230  );
231 }
232 
233 /*
234  * Public API.
235  */
236 
248 {
249  return MAPE_TEXTURE_MAP(g_object_new(MAPE_TYPE_TEXTURE_MAP, NULL));
250 }
251 
264 gboolean
266  MapeGroup* group,
267  GError** error)
268 {
269  MapeTextureMapPrivate* priv;
270 
271  g_return_val_if_fail(MAPE_IS_TEXTURE_MAP(texture_map), FALSE);
272  g_return_val_if_fail(MAPE_IS_GROUP(group), FALSE);
273  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
274 
275  priv = MAPE_TEXTURE_MAP_PRIVATE(texture_map);
276 
278  priv->handle,
279  _mape_group_get_handle(group),
280  "TexMap.txt",
281  &priv->overload_materials,
282  &priv->overload_textures
283  );
284 
285  g_object_notify(G_OBJECT(texture_map), "overload-materials");
286  g_object_notify(G_OBJECT(texture_map), "overload-textures");
287 
288  return TRUE;
289 }
290 
303 gboolean
305  MapeGroup* group,
306  GError** error)
307 {
308  /* We don't use C4TextureMap::LoadTextures here because that would try
309  * to load them as C4Surface. Instead, we have our own routine which loads
310  * them into GdkPixbufs. */
311  static const char* SUFFIXES[] = {
312  ".png", ".jpg", ".jpeg", ".bmp",
313  ".PNG", ".JPG", ".JPEG", ".BMP",
314  NULL
315  };
316 
317  MapeTextureMapPrivate* priv;
318 
319  gchar* name;
320  const char* const* ext;
321  gsize len;
322  gchar* casefold_name;
323 
324  guchar* data;
325  gsize datalen;
326  GdkPixbufLoader* loader;
327  GdkPixbuf* pixbuf;
328 
329  g_return_val_if_fail(MAPE_IS_TEXTURE_MAP(texture_map), FALSE);
330  g_return_val_if_fail(MAPE_IS_GROUP(group), FALSE);
331  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
332 
333  priv = MAPE_TEXTURE_MAP_PRIVATE(texture_map);
334 
335  if(priv->texture_table == NULL)
336  {
337  priv->texture_table = g_hash_table_new_full(
338  g_str_hash,
339  g_str_equal,
340  g_free,
341  g_object_unref
342  );
343  }
344 
345  mape_group_rewind(group);
346  while( (name = mape_group_get_next_entry(group)) != NULL)
347  {
348  for(ext = SUFFIXES; *ext != NULL; ++ext)
349  if(g_str_has_suffix(name, *ext))
350  break;
351 
352  if(*ext != NULL)
353  {
354  /* Make texname out of filename */
355  len = strlen(name)-strlen(*ext);
356  /* Use this for hashtable lookup */
357  casefold_name = g_utf8_casefold(name, len);
358 
359  /* If we have this texture loaded already then don't load it again --
360  * this is how overloading works: We first load the primary group and
361  * then the overloaded one. */
362  if(g_hash_table_lookup(priv->texture_table, casefold_name) == NULL)
363  {
364  data = mape_group_load_entry(group, name, &datalen, error);
365  if(data == NULL)
366  {
367  g_free(name);
368  return FALSE;
369  }
370 
371  loader = gdk_pixbuf_loader_new();
372  gdk_pixbuf_loader_set_size(loader, 24, 24);
373  if(!gdk_pixbuf_loader_write(loader, data, datalen, error))
374  {
375  g_free(name);
376  g_free(data);
377  gdk_pixbuf_loader_close(loader, NULL);
378  g_object_unref(loader);
379  return FALSE;
380  }
381 
382  g_free(data);
383  if(!gdk_pixbuf_loader_close(loader, error))
384  {
385  g_free(name);
386  g_object_unref(loader);
387  return FALSE;
388  }
389 
390  pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
391  g_object_ref(pixbuf);
392  g_object_unref(loader);
393 
394  /* Add texture to texmap (without actual Surface, only texture color),
395  * just so that the map generator knows the presence
396  * of the texture. */
397  name[len] = '\0';
399  priv->handle,
400  name,
401  mape_texture_map_get_average_color(pixbuf)
402  );
403  g_hash_table_insert(priv->texture_table, casefold_name, pixbuf);
404  }
405  else
406  {
407  g_free(casefold_name);
408  }
409  }
410 
411  g_free(name);
412  }
413 
414  return TRUE;
415 }
416 
426 gboolean
428 {
429  g_return_val_if_fail(MAPE_IS_TEXTURE_MAP(texture_map), FALSE);
430  return MAPE_TEXTURE_MAP_PRIVATE(texture_map)->overload_materials;
431 }
432 
442 gboolean
444 {
445  g_return_val_if_fail(MAPE_IS_TEXTURE_MAP(texture_map), FALSE);
446  return MAPE_TEXTURE_MAP_PRIVATE(texture_map)->overload_materials;
447 }
448 
458 guint
460 {
461  MapeTextureMapPrivate* priv;
462  g_return_val_if_fail(MAPE_IS_TEXTURE_MAP(texture_map), FALSE);
463  priv = MAPE_TEXTURE_MAP_PRIVATE(texture_map);
464  return g_hash_table_size(priv->texture_table);
465 }
466 
477 const gchar*
479  guint index)
480 {
481  MapeTextureMapPrivate* priv;
482 
483  g_return_val_if_fail(MAPE_IS_TEXTURE_MAP(texture_map), NULL);
484  g_return_val_if_fail(
485  index < mape_texture_map_get_texture_count(texture_map), NULL
486  );
487 
488  priv = MAPE_TEXTURE_MAP_PRIVATE(texture_map);
489  return c4_texture_map_handle_get_texture(priv->handle, index);
490 }
491 
503 GdkPixbuf*
505  const gchar* name)
506 {
507  MapeTextureMapPrivate* priv;
508  gchar* casefold_name;
509  GdkPixbuf* pixbuf;
510 
511  g_return_val_if_fail(MAPE_IS_TEXTURE_MAP(texture_map), NULL);
512  g_return_val_if_fail(name != NULL, NULL);
513 
514  priv = MAPE_TEXTURE_MAP_PRIVATE(texture_map);
515  casefold_name = g_utf8_casefold(name, -1);
516  pixbuf = GDK_PIXBUF(g_hash_table_lookup(priv->texture_table, casefold_name));
517  g_free(casefold_name);
518 
519  return pixbuf;
520 }
521 
535 const gchar*
537  guint index)
538 {
539  MapeTextureMapPrivate* priv;
540  g_return_val_if_fail(MAPE_IS_TEXTURE_MAP(texture_map), NULL);
541  priv = MAPE_TEXTURE_MAP_PRIVATE(texture_map);
543 }
544 
558 const gchar*
560  guint index)
561 {
562  MapeTextureMapPrivate* priv;
563  g_return_val_if_fail(MAPE_IS_TEXTURE_MAP(texture_map), NULL);
564  priv = MAPE_TEXTURE_MAP_PRIVATE(texture_map);
566 }
567 
579 guint32
581  const gchar* name)
582 {
583  MapeTextureMapPrivate* priv;
584  g_return_val_if_fail(MAPE_IS_TEXTURE_MAP(texture_map), 0);
585  priv = MAPE_TEXTURE_MAP_PRIVATE(texture_map);
587 }
588 
589 /* This function is for internal use only */
592 {
593  g_return_val_if_fail(MAPE_IS_TEXTURE_MAP(map), NULL);
594  return MAPE_TEXTURE_MAP_PRIVATE(map)->handle;
595 }
596 
597 /* vim:set et sw=2 ts=2: */
Definition: texture.c:38
gboolean c4_texture_map_handle_add_texture(C4TextureMapHandle *texture_map, const char *texture, guint32 avg_color)
gchar * mape_group_get_next_entry(MapeGroup *group)
Definition: group.c:496
const char * c4_texture_map_handle_get_texture(C4TextureMapHandle *texture_map, guint index)
const char * c4_texture_handle_get_entry_texture_name(C4TextureMapHandle *texture_map, guint index)
#define MAPE_TYPE_TEXTURE_MAP
Definition: texture.h:26
gboolean overload_materials
Definition: texture.c:33
gboolean mape_texture_map_load_map(MapeTextureMap *texture_map, MapeGroup *group, GError **error)
Definition: texture.c:265
typedefG_BEGIN_DECLS struct _C4GroupHandle C4GroupHandle
Definition: group-handle.h:23
guint c4_texture_map_handle_load_map(C4TextureMapHandle *texture_map, C4GroupHandle *group, const char *entry_name, gboolean *overload_materials, gboolean *overload_textures)
guint32 mape_texture_map_get_average_texture_color(MapeTextureMap *texture_map, const gchar *name)
Definition: texture.c:580
const char * c4_texture_handle_get_entry_material_name(C4TextureMapHandle *texture_map, guint index)
void c4_texture_map_handle_free(C4TextureMapHandle *texture_map)
gboolean overload_textures
Definition: texture.c:34
C4TextureMapHandle * _mape_texture_map_get_handle(MapeTextureMap *map)
Definition: texture.c:591
const gchar * mape_texture_map_get_material_name_from_mapping(MapeTextureMap *texture_map, guint index)
Definition: texture.c:536
MapeTextureMap * mape_texture_map_new(void)
Definition: texture.c:247
#define MAPE_TEXTURE_MAP_PRIVATE(obj)
Definition: texture.c:46
void mape_group_rewind(MapeGroup *group)
Definition: group.c:462
C4GroupHandle * _mape_group_get_handle(MapeGroup *group)
Definition: group.c:682
gboolean mape_texture_map_load_textures(MapeTextureMap *texture_map, MapeGroup *group, GError **error)
Definition: texture.c:304
GdkPixbuf * mape_texture_map_lookup_texture(MapeTextureMap *texture_map, const gchar *name)
Definition: texture.c:504
gboolean mape_texture_map_get_overload_materials(MapeTextureMap *texture_map)
Definition: texture.c:427
#define MAPE_TEXTURE_MAP(obj)
Definition: texture.h:27
guint mape_texture_map_get_texture_count(MapeTextureMap *texture_map)
Definition: texture.c:459
const gchar * mape_texture_map_get_texture_name_from_mapping(MapeTextureMap *texture_map, guint index)
Definition: texture.c:559
const gchar * mape_texture_map_get_texture_name(MapeTextureMap *texture_map, guint index)
Definition: texture.c:478
gboolean mape_texture_map_get_overload_textures(MapeTextureMap *texture_map)
Definition: texture.c:443
C4TextureMapHandle * c4_texture_map_handle_new(void)
guchar * mape_group_load_entry(MapeGroup *group, const gchar *entry, gsize *size, GError **error)
Definition: group.c:557
#define MAPE_IS_TEXTURE_MAP(obj)
Definition: texture.h:29
C4TextureMapHandle * handle
Definition: texture.c:31
guint32 c4_texture_handle_get_average_texture_color(C4TextureMapHandle *texture_map, const char *name)
GHashTable * texture_table
Definition: texture.c:32
#define MAPE_IS_GROUP(obj)
Definition: group.h:26
typedefG_BEGIN_DECLS struct _C4TextureMapHandle C4TextureMapHandle