Crossfire Client, Trunk
png.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, see the
9  * 'LICENSE' and 'COPYING' files.
10  *
11  * The authors can be reached via e-mail to crossfire-devel@real-time.com
12  */
13 
19 #include "client.h"
20 
21 #include <errno.h>
22 #include <gtk/gtk.h>
23 #include <png.h>
24 
25 /* Defines for PNG return values */
26 /* These should be in a header file, but currently our calling functions
27  * routines just check for nonzero return status and don't really care
28  * why the load failed.
29  */
30 #define PNGX_NOFILE 1
31 #define PNGX_OUTOFMEM 2
32 #define PNGX_DATA 3
33 
34 static guint8 *data_cp;
35 static int data_len, data_start;
36 
43 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
44 {
45  memcpy(data, data_cp + data_start, length);
46  data_start += length;
47 }
48 
49 guint8 *png_to_data(guint8 *data, int len, guint32 *width, guint32 *height) {
50  guint8 *pixels = NULL;
51  static png_bytepp rows = NULL;
52  static guint32 rows_byte = 0;
53 
54  png_structp png_ptr;
55  png_infop info_ptr;
56  int bit_depth, color_type, interlace_type;
57 
58  data_len = len;
59  data_cp = data;
60  data_start = 0;
61 
62  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
63  if (!png_ptr) {
64  return NULL;
65  }
66 
67  info_ptr = png_create_info_struct(png_ptr);
68  if (!info_ptr) {
69  png_destroy_read_struct(&png_ptr, NULL, NULL);
70  return NULL;
71  }
72 
73  if (setjmp(png_jmpbuf(png_ptr))) {
74  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
75  return NULL;
76  }
77 
78  png_set_read_fn(png_ptr, NULL, user_read_data);
79  png_read_info(png_ptr, info_ptr);
80 
81  /*
82  * This seems to bug on at least one system (other than mine)
83  * http://www.metalforge.net/cfmb/viewtopic.php?t=1085
84  *
85  * I think its actually a bug in libpng. This function dies with an
86  * error based on image width. However I've produced a work around
87  * using the indivial functions. Repeated below.
88  *
89  png_get_IHDR(png_ptr, info_ptr, (png_uint_32*)width, (png_unit_32*)height, &bit_depth,
90  &color_type, &interlace_type, NULL, &filter_type);
91  */
92  *width = png_get_image_width(png_ptr, info_ptr);
93  *height = png_get_image_height(png_ptr, info_ptr);
94  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
95  color_type = png_get_color_type(png_ptr, info_ptr);
96  interlace_type = png_get_interlace_type(png_ptr, info_ptr);
97 
98  if (color_type == PNG_COLOR_TYPE_PALETTE &&
99  bit_depth <= 8) {
100 
101  /* Convert indexed images to RGB */
102  png_set_expand (png_ptr);
103 
104  } else if (color_type == PNG_COLOR_TYPE_GRAY &&
105  bit_depth < 8) {
106 
107  /* Convert grayscale to RGB */
108  png_set_expand (png_ptr);
109 
110  } else if (png_get_valid (png_ptr,
111  info_ptr, PNG_INFO_tRNS)) {
112 
113  /* If we have transparency header, convert it to alpha
114  channel */
115  png_set_expand(png_ptr);
116 
117  } else if (bit_depth < 8) {
118 
119  /* If we have < 8 scale it up to 8 */
120  png_set_expand(png_ptr);
121 
122 
123  /* Conceivably, png_set_packing() is a better idea;
124  * God only knows how libpng works
125  */
126  }
127  /* If we are 16-bit, convert to 8-bit */
128  if (bit_depth == 16) {
129  png_set_strip_16(png_ptr);
130  }
131 
132  /* If gray scale, convert to RGB */
133  if (color_type == PNG_COLOR_TYPE_GRAY ||
134  color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
135  png_set_gray_to_rgb(png_ptr);
136  }
137 
138  /* If interlaced, handle that */
139  if (interlace_type != PNG_INTERLACE_NONE) {
140  png_set_interlace_handling(png_ptr);
141  }
142 
143  /* pad it to 4 bytes to make processing easier */
144  if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
145  png_set_filler(png_ptr, 255, PNG_FILLER_AFTER);
146  }
147 
148  /* Update the info the reflect our transformations */
149  png_read_update_info(png_ptr, info_ptr);
150  /* re-read due to transformations just made */
151  /*
152  * See above for error description
153  png_get_IHDR(png_ptr, info_ptr, (png_uint_32*)width, (png_uint_32*)height, &bit_depth,
154  &color_type, &interlace_type, NULL, &filter_type);
155  */
156  *width = png_get_image_width(png_ptr, info_ptr);
157  *height = png_get_image_height(png_ptr, info_ptr);
158 
159  pixels = (guint8*)g_malloc(*width **height * 4);
160 
161  if (!pixels) {
162  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
163  LOG(LOG_CRITICAL,"gtk-v2::png_to_data","Out of memory - exiting");
164  exit(1);
165  }
166 
167  /* the png library needs the rows, but we will just return the raw data */
168  if (rows_byte == 0) {
169  rows = (png_bytepp)g_malloc(sizeof(png_byte *) **height);
170  rows_byte = *height;
171  } else if (*height > rows_byte) {
172  rows = (png_bytepp)g_realloc(rows, sizeof(png_byte *) **height);
173 
174  if (rows == NULL) {
175  LOG(LOG_ERROR, "png_to_data",
176  "Could not allocate memory: %s", strerror(errno));
177  exit(EXIT_FAILURE);
178  }
179 
180  rows_byte = *height;
181  }
182  if (!rows) {
183  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
184  free(pixels);
185  return NULL;
186  }
187 
188  for (guint32 y = 0; y < *height; y++) {
189  rows[y] = pixels + y * *width * 4;
190  }
191 
192  png_read_image(png_ptr, rows);
193  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
194 
195  return pixels;
196 }
197 
198 /* RATIO is used to know what units scale is - in this case, a percentage, so
199  * it is set to 100
200  */
201 #define RATIO 100
202 
203 #define MAX_IMAGE_WIDTH 1024
204 #define MAX_IMAGE_HEIGHT 1024
205 #define BPP 4
206 
235 guint8 *rescale_rgba_data(guint8 *data, int *width, int *height, int scale)
236 {
237  static int xrow[BPP * MAX_IMAGE_WIDTH], yrow[BPP*MAX_IMAGE_HEIGHT];
238  static guint8 *nrows[MAX_IMAGE_HEIGHT];
239 
240  /* Figure out new height/width */
241  int new_width = *width * scale / RATIO, new_height = *height * scale / RATIO;
242 
243  int sourcerow=0, ytoleft, ytofill, xtoleft, xtofill, dest_column=0, source_column=0, needcol,
244  destrow=0;
245  int x,y;
246  guint8 *ndata;
247  guint8 r,g,b,a;
248 
249  if (*width > MAX_IMAGE_WIDTH || new_width > MAX_IMAGE_WIDTH
250  || *height > MAX_IMAGE_HEIGHT || new_height > MAX_IMAGE_HEIGHT) {
251  LOG(LOG_CRITICAL,"gtk-v2::rescale_rgba_data","Image too big");
252  exit(0);
253  }
254 
255  /* clear old values these may have */
256  memset(yrow, 0, sizeof(int) **height * BPP);
257 
258  ndata = (guint8*)g_malloc(new_width * new_height * BPP);
259 
260  for (y=0; y<new_height; y++) {
261  nrows[y] = (png_bytep) (ndata + y * new_width * BPP);
262  }
263 
264  ytoleft = scale;
265  ytofill = RATIO;
266 
267  for (y=0,sourcerow=0; y < new_height; y++) {
268  memset(xrow, 0, sizeof(int) **width * BPP);
269  while (ytoleft < ytofill) {
270  for (x=0; x< *width; ++x) {
271  /* Only want to copy the data if this is not a transperent pixel.
272  * If it is transparent, the color information is has is probably
273  * bogus, and blending that makes the results look worse.
274  */
275  if (data[(sourcerow **width + x)*BPP+3] > 0 ) {
276  yrow[x*BPP] += ytoleft * data[(sourcerow **width + x)*BPP]/RATIO;
277  yrow[x*BPP+1] += ytoleft * data[(sourcerow **width + x)*BPP+1]/RATIO;
278  yrow[x*BPP+2] += ytoleft * data[(sourcerow **width + x)*BPP+2]/RATIO;
279  }
280  /* Alpha is a bit special - we don't want to blend it -
281  * we want to take whatever is the more opaque value.
282  */
283  if (data[(sourcerow **width + x)*BPP+3] > yrow[x*BPP+3]) {
284  yrow[x*BPP+3] = data[(sourcerow **width + x)*BPP+3];
285  }
286  }
287  ytofill -= ytoleft;
288  ytoleft = scale;
289  if (sourcerow < *height) {
290  sourcerow++;
291  }
292  }
293 
294  for (x=0; x < *width; ++x) {
295  if (data[(sourcerow **width + x)*BPP+3] > 0 ) {
296  xrow[x*BPP] = yrow[x*BPP] + ytofill * data[(sourcerow **width + x)*BPP] / RATIO;
297  xrow[x*BPP+1] = yrow[x*BPP+1] + ytofill * data[(sourcerow **width + x)*BPP+1] / RATIO;
298  xrow[x*BPP+2] = yrow[x*BPP+2] + ytofill * data[(sourcerow **width + x)*BPP+2] / RATIO;
299  }
300  if (data[(sourcerow **width + x)*BPP+3] > xrow[x*BPP+3]) {
301  xrow[x*BPP+3] = data[(sourcerow **width + x)*BPP+3];
302  }
303  yrow[x*BPP]=0;
304  yrow[x*BPP+1]=0;
305  yrow[x*BPP+2]=0;
306  yrow[x*BPP+3]=0;
307  }
308 
309  ytoleft -= ytofill;
310  if (ytoleft <= 0) {
311  ytoleft = scale;
312  if (sourcerow < *height) {
313  sourcerow++;
314  }
315  }
316 
317  ytofill = RATIO;
318  xtofill = RATIO;
319  dest_column = 0;
320  source_column=0;
321  needcol=0;
322  r=0;
323  g=0;
324  b=0;
325  a=0;
326 
327  for (x=0; x< *width; x++) {
328  xtoleft = scale;
329 
330  while (xtoleft >= xtofill) {
331  if (needcol) {
332  dest_column++;
333  r=0;
334  g=0;
335  b=0;
336  a=0;
337  }
338 
339  if (xrow[source_column*BPP+3] > 0) {
340  r += xtofill * xrow[source_column*BPP] / RATIO;
341  g += xtofill * xrow[1+source_column*BPP] / RATIO;
342  b += xtofill * xrow[2+source_column*BPP] / RATIO;
343  }
344  if (xrow[3+source_column*BPP] > a) {
345  a = xrow[3+source_column*BPP];
346  }
347 
348  nrows[destrow][dest_column * BPP] = r;
349  nrows[destrow][1+dest_column * BPP] = g;
350  nrows[destrow][2+dest_column * BPP] = b;
351  nrows[destrow][3+dest_column * BPP] = a;
352  xtoleft -= xtofill;
353  xtofill = RATIO;
354  needcol=1;
355  }
356 
357  if (xtoleft > 0 ) {
358  if (needcol) {
359  dest_column++;
360  r=0;
361  g=0;
362  b=0;
363  a=0;
364  needcol=0;
365  }
366 
367  if (xrow[3+source_column*BPP] > 0) {
368  r += xtoleft * xrow[source_column*BPP] / RATIO;
369  g += xtoleft * xrow[1+source_column*BPP] / RATIO;
370  b += xtoleft * xrow[2+source_column*BPP] / RATIO;
371  }
372  if (xrow[3+source_column*BPP] > a) {
373  a = xrow[3+source_column*BPP];
374  }
375 
376  xtofill -= xtoleft;
377  }
378  source_column++;
379  }
380 
381  if (xtofill > 0 ) {
382  source_column--;
383  if (xrow[3+source_column*BPP] > 0) {
384  r += xtofill * xrow[source_column*BPP] / RATIO;
385  g += xtofill * xrow[1+source_column*BPP] / RATIO;
386  b += xtofill * xrow[2+source_column*BPP] / RATIO;
387  }
388  if (xrow[3+source_column*BPP] > a) {
389  a = xrow[3+source_column*BPP];
390  }
391  }
392 
393  /* Not positve, but without the bound checking for dest_column,
394  * we were overrunning the buffer. My guess is this only really
395  * showed up if when the images are being scaled - there is probably
396  * something like half a pixel left over.
397  */
398  if (!needcol && (dest_column < new_width)) {
399  nrows[destrow][dest_column * BPP] = r;
400  nrows[destrow][1+dest_column * BPP] = g;
401  nrows[destrow][2+dest_column * BPP] = b;
402  nrows[destrow][3+dest_column * BPP] = a;
403  }
404  destrow++;
405  }
406  *width = new_width;
407  *height = new_height;
408  return ndata;
409 }
410 
414 GdkPixbuf *rgba_to_gdkpixbuf(guint8 *data, int width, int height) {
415  /* Our data doesn't have correct stride values, so we can't just create it
416  * from raw data using gdk_pixbuf_new_from_data(). */
417 
418  GdkPixbuf *pix;
419  pix = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
420 
421  int rowstride = gdk_pixbuf_get_rowstride(pix);
422  unsigned char *pixels = gdk_pixbuf_get_pixels(pix);
423 
424  for (int y = 0; y < height; y++) {
425  for (int x = 0; x < width; x++) {
426  unsigned char *p = pixels + y * rowstride + x * 4;
427  p[0] = data[4*(x + y * width)];
428  p[1] = data[4*(x + y * width) + 1 ];
429  p[2] = data[4*(x + y * width) + 2 ];
430  p[3] = data[4*(x + y * width) + 3 ];
431  }
432  }
433 
434  return pix;
435 }
436 
440 cairo_surface_t *rgba_to_cairo_surface(guint8 *data, int width, int height) {
441  cairo_surface_t *surface;
442  surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
443  cairo_surface_flush(surface);
444 
445  unsigned char *pixels = cairo_image_surface_get_data(surface);
446  int stride = cairo_image_surface_get_stride(surface);
447 
448  for (int y = 0; y < height; y++) {
449  for (int x = 0; x < width; x++) {
450  guint32 *p = (guint32 *)(pixels + y * stride + x * 4);
451 
452  // Cairo format is native-endian ARGB, but our format is RGBA.
453  guint32 a = data[4 * (x + y * width) + 3];
454  guint32 r = data[4 * (x + y * width) + 0] * a / 255;
455  guint32 g = data[4 * (x + y * width) + 1] * a / 255;
456  guint32 b = data[4 * (x + y * width) + 2] * a / 255;
457 
458  *p = a << (3 * 8) | r << (2 * 8) | g << (1 * 8) | b << (0 * 8);
459  }
460  }
461 
462  cairo_surface_mark_dirty(surface);
463  return surface;
464 }
MAX_IMAGE_WIDTH
#define MAX_IMAGE_WIDTH
Definition: png.c:203
data_cp
static guint8 * data_cp
Definition: png.c:34
data_len
static int data_len
Definition: png.c:35
png_to_data
guint8 * png_to_data(guint8 *data, int len, guint32 *width, guint32 *height)
Definition: png.c:49
rescale_rgba_data
guint8 * rescale_rgba_data(guint8 *data, int *width, int *height, int scale)
Definition: png.c:235
BPP
#define BPP
Definition: png.c:205
height
static int height
Definition: mapdata.c:99
MAX_IMAGE_HEIGHT
#define MAX_IMAGE_HEIGHT
Definition: png.c:204
LOG_CRITICAL
@ LOG_CRITICAL
Fatal crash-worthy error.
Definition: client.h:437
LOG
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:111
width
static int width
Definition: mapdata.c:98
rgba_to_gdkpixbuf
GdkPixbuf * rgba_to_gdkpixbuf(guint8 *data, int width, int height)
Definition: png.c:414
data_start
static int data_start
Definition: png.c:35
LOG_ERROR
@ LOG_ERROR
Warning that something definitely didn't work.
Definition: client.h:436
rgba_to_cairo_surface
cairo_surface_t * rgba_to_cairo_surface(guint8 *data, int width, int height)
Definition: png.c:440
user_read_data
static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
Definition: png.c:43
client.h
RATIO
#define RATIO
Definition: png.c:201