Crossfire Client, Branches  R11627
png.c
Go to the documentation of this file.
1 const char * const rcsid_gtk_png_c =
2  "$Id: png.c 9190 2008-06-01 08:53:05Z anmaster $";
3 /*
4  Crossfire client, a client program for the crossfire program.
5 
6  Copyright (C) 2001 Mark Wedel & Crossfire Development Team
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22  The author can be reached via e-mail to crossfire-devel@real-time.com
23 */
24 
25 
26 #include <config.h>
27 #include <stdlib.h>
28 #include <sys/stat.h>
29 #ifndef WIN32
30 #include <unistd.h>
31 #endif
32 #include <png.h>
33 #include <client-types.h>
34 #include <client.h>
35 
36 /* Pick up the gtk headers we need */
37 #include <gtk/gtk.h>
38 #ifndef WIN32
39 #include <gdk/gdkx.h>
40 #else
41 #include <gdk/gdkwin32.h>
42 #endif
43 #include <gdk/gdkkeysyms.h>
44 
45 
46 /* Defines for PNG return values */
47 /* These should be in a header file, but currently our calling functions
48  * routines just check for nonzero return status and don't really care
49  * why the load failed.
50  */
51 #define PNGX_NOFILE 1
52 #define PNGX_OUTOFMEM 2
53 #define PNGX_DATA 3
54 
55 static uint8 *data_cp;
56 static int data_len, data_start;
57 
58 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
59  memcpy(data, data_cp + data_start, length);
60  data_start += length;
61 }
62 
64 {
65  uint8 *pixels=NULL;
66  static png_bytepp rows=NULL;
67  static int rows_byte=0;
68 
69  png_structp png_ptr;
70  png_infop info_ptr;
71  int bit_depth, color_type, interlace_type, compression_type, y;
72 
73  data_len=len;
74  data_cp = data;
75  data_start=0;
76 
77  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
78  NULL, NULL, NULL);
79 
80  if (!png_ptr) {
81  return NULL;
82  }
83  info_ptr = png_create_info_struct (png_ptr);
84 
85  if (!info_ptr) {
86  png_destroy_read_struct (&png_ptr, NULL, NULL);
87  return NULL;
88  }
89  if (setjmp (png_ptr->jmpbuf)) {
90  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
91  return NULL;
92  }
93 
94  png_set_read_fn(png_ptr, NULL, user_read_data);
95  png_read_info (png_ptr, info_ptr);
96  /*
97  * This seems to bug on at least one system (other than mine)
98  * http://www.metalforge.net/cfmb/viewtopic.php?t=1085
99  *
100  * I think its actually a bug in libpng. This function dies with an
101  * error based on image width. However I've produced a work around
102  * using the indivial functions. Repeated below.
103  *
104  png_get_IHDR(png_ptr, info_ptr, (png_uint_32*)width, (png_uint_32*)height, &bit_depth,
105  &color_type, &interlace_type, &compression_type, &filter_type);
106  */
107  *width = png_get_image_width(png_ptr, info_ptr);
108  * height = png_get_image_height(png_ptr, info_ptr);
109  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
110  color_type = png_get_color_type(png_ptr, info_ptr);
111  interlace_type = png_get_interlace_type(png_ptr, info_ptr);
112  compression_type = png_get_compression_type(png_ptr, info_ptr);
113  if (color_type == PNG_COLOR_TYPE_PALETTE &&
114  bit_depth <= 8) {
115 
116  /* Convert indexed images to RGB */
117  png_set_expand (png_ptr);
118 
119  } else if (color_type == PNG_COLOR_TYPE_GRAY &&
120  bit_depth < 8) {
121 
122  /* Convert grayscale to RGB */
123  png_set_expand (png_ptr);
124 
125  } else if (png_get_valid (png_ptr,
126  info_ptr, PNG_INFO_tRNS)) {
127 
128  /* If we have transparency header, convert it to alpha
129  channel */
130  png_set_expand(png_ptr);
131 
132  } else if (bit_depth < 8) {
133 
134  /* If we have < 8 scale it up to 8 */
135  png_set_expand(png_ptr);
136 
137 
138  /* Conceivably, png_set_packing() is a better idea;
139  * God only knows how libpng works
140  */
141  }
142  /* If we are 16-bit, convert to 8-bit */
143  if (bit_depth == 16) {
144  png_set_strip_16(png_ptr);
145  }
146 
147  /* If gray scale, convert to RGB */
148  if (color_type == PNG_COLOR_TYPE_GRAY ||
149  color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
150  png_set_gray_to_rgb(png_ptr);
151  }
152 
153  /* If interlaced, handle that */
154  if (interlace_type != PNG_INTERLACE_NONE) {
155  png_set_interlace_handling(png_ptr);
156  }
157 
158  /* pad it to 4 bytes to make processing easier */
159  if (!(color_type & PNG_COLOR_MASK_ALPHA))
160  png_set_filler(png_ptr, 255, PNG_FILLER_AFTER);
161 
162  /* Update the info the reflect our transformations */
163  png_read_update_info(png_ptr, info_ptr);
164  /* re-read due to transformations just made */
165  /*
166  * See above for error description
167  png_get_IHDR(png_ptr, info_ptr, (png_uint_32*)width, (png_uint_32*)height, &bit_depth,
168  &color_type, &interlace_type, &compression_type, &filter_type);
169  */
170  *width = png_get_image_width(png_ptr, info_ptr);
171  *height = png_get_image_height(png_ptr, info_ptr);
172  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
173  color_type = png_get_color_type(png_ptr, info_ptr);
174  interlace_type = png_get_interlace_type(png_ptr, info_ptr);
175  compression_type = png_get_compression_type(png_ptr, info_ptr);
176 
177  pixels = (uint8*)malloc(*width * *height * 4);
178 
179  if (!pixels) {
180  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
181  LOG(LOG_CRITICAL,"gtk::png_to_data","Out of memory - exiting");
182  exit(1);
183  }
184 
185  /* the png library needs the rows, but we will just return the raw data */
186  if (rows_byte == 0) {
187  rows =(png_bytepp) malloc(sizeof(char*) * *height);
188  rows_byte=*height;
189  } else if (*height > rows_byte) {
190  rows =(png_bytepp) realloc(rows, sizeof(char*) * *height);
191  rows_byte=*height;
192  }
193  if (!rows) {
194  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
195  return NULL;
196  }
197 
198  for (y=0; y<*height; y++)
199  rows[y] = pixels + y * *width * 4;
200 
201  png_read_image(png_ptr, rows);
202  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
203 
204  return pixels;
205 }
206 
207 
208 /* rescale_png_image takes png data and scales it accordingly.
209  * This function is based on pnmscale, but has been modified to support alpha
210  * channel - instead of blending the alpha channel, it takes the most opaque
211  * value - blending it is not likely to give sane results IMO - for any image
212  * that has transparent information, if we blended the alpha, the result would
213  * be the edges of that region being partially transparent.
214  * This function has also been re-written to use more static data - in the
215  * case of the client, it will be called thousands of times, so it doesn't make
216  * sense to free the data and then re-allocate it.
217  *
218  * For pixels that are fully transparent, the end result after scaling is they
219  * will be tranparent black. This is a needed effect for blending to work properly.
220  *
221  * This function returns a new pointer to the scaled image data. This is
222  * malloc'd data, so should be freed at some point to prevent leaks.
223  * This function does not modify the data passed to it - the caller is responsible
224  * for freeing it if it is no longer needed.
225  *
226  * function arguments:
227  * data: PNG data - really, this is any 4 byte per pixel data, in RGBA format.
228  * *width, *height: The source width and height. These values are modified
229  * to contain the new image size.
230  * scale: percentage size that new image should be. 100 is a same size
231  * image - values larger than 100 will result in zoom, values less than
232  * 100 will result in a shrinkage.
233  */
234 
235 /* RATIO is used to know what units scale is - in this case, a percentage, so
236  * it is set to 100
237  */
238 #define RATIO 100
239 
240 #define MAX_IMAGE_WIDTH 1024
241 #define MAX_IMAGE_HEIGHT 1024
242 #define BPP 4
243 
244 uint8 *rescale_rgba_data(uint8 *data, int *width, int *height, int scale)
245 {
246  static int xrow[BPP * MAX_IMAGE_WIDTH], yrow[BPP*MAX_IMAGE_HEIGHT];
247  static uint8 *nrows[MAX_IMAGE_HEIGHT];
248 
249  /* Figure out new height/width */
250  int new_width = *width * scale / RATIO, new_height = *height * scale / RATIO;
251 
252  int sourcerow=0, ytoleft, ytofill, xtoleft, xtofill, dest_column=0, source_column=0, needcol,
253  destrow=0;
254  int x,y;
255  uint8 *ndata;
256  uint8 r,g,b,a;
257 
258  if (*width > MAX_IMAGE_WIDTH || new_width > MAX_IMAGE_WIDTH
259  || *height > MAX_IMAGE_HEIGHT || new_height > MAX_IMAGE_HEIGHT)
260  {
261  LOG(LOG_CRITICAL,"gtk::rescale_rgba_data","Image too big");
262  exit(0);
263  }
264 
265  /* clear old values these may have */
266  memset(yrow, 0, sizeof(int) * *height * BPP);
267 
268  ndata = (uint8*)malloc(new_width * new_height * BPP);
269 
270  for (y=0; y<new_height; y++)
271  nrows[y] = (png_bytep) (ndata + y * new_width * BPP);
272 
273  ytoleft = scale;
274  ytofill = RATIO;
275 
276  for (y=0,sourcerow=0; y < new_height; y++) {
277  memset(xrow, 0, sizeof(int) * *width * BPP);
278  while (ytoleft < ytofill) {
279  for (x=0; x< *width; ++x) {
280  /* Only want to copy the data if this is not a transperent pixel.
281  * If it is transparent, the color information is has is probably
282  * bogus, and blending that makes the results look worse.
283  */
284  if (data[(sourcerow * *width + x)*BPP+3] > 0 ) {
285  yrow[x*BPP] += ytoleft * data[(sourcerow * *width + x)*BPP]/RATIO;
286  yrow[x*BPP+1] += ytoleft * data[(sourcerow * *width + x)*BPP+1]/RATIO;
287  yrow[x*BPP+2] += ytoleft * data[(sourcerow * *width + x)*BPP+2]/RATIO;
288  }
289  /* Alpha is a bit special - we don't want to blend it -
290  * we want to take whatever is the more opaque value.
291  */
292  if (data[(sourcerow * *width + x)*BPP+3] > yrow[x*BPP+3])
293  yrow[x*BPP+3] = data[(sourcerow * *width + x)*BPP+3];
294  }
295  ytofill -= ytoleft;
296  ytoleft = scale;
297  if (sourcerow < *height)
298  sourcerow++;
299  }
300 
301  for (x=0; x < *width; ++x) {
302  if (data[(sourcerow * *width + x)*BPP+3] > 0 ) {
303  xrow[x*BPP] = yrow[x*BPP] + ytofill * data[(sourcerow * *width + x)*BPP] / RATIO;
304  xrow[x*BPP+1] = yrow[x*BPP+1] + ytofill * data[(sourcerow * *width + x)*BPP+1] / RATIO;
305  xrow[x*BPP+2] = yrow[x*BPP+2] + ytofill * data[(sourcerow * *width + x)*BPP+2] / RATIO;
306  }
307  if (data[(sourcerow * *width + x)*BPP+3] > xrow[x*BPP+3])
308  xrow[x*BPP+3] = data[(sourcerow * *width + x)*BPP+3];
309  yrow[x*BPP]=0; yrow[x*BPP+1]=0; yrow[x*BPP+2]=0; yrow[x*BPP+3]=0;
310  }
311 
312  ytoleft -= ytofill;
313  if (ytoleft <= 0) {
314  ytoleft = scale;
315  if (sourcerow < *height)
316  sourcerow++;
317  }
318 
319  ytofill = RATIO;
320  xtofill = RATIO;
321  dest_column = 0;
322  source_column=0;
323  needcol=0;
324  r=0; g=0; b=0; a=0;
325 
326  for (x=0; x< *width; x++) {
327  xtoleft = scale;
328 
329  while (xtoleft >= xtofill) {
330  if (needcol) {
331  dest_column++;
332  r=0; g=0; b=0; a=0;
333  }
334 
335  if (xrow[source_column*BPP+3] > 0) {
336  r += xtofill * xrow[source_column*BPP] / RATIO;
337  g += xtofill * xrow[1+source_column*BPP] / RATIO;
338  b += xtofill * xrow[2+source_column*BPP] / RATIO;
339  }
340  if (xrow[3+source_column*BPP] > a)
341  a = xrow[3+source_column*BPP];
342 
343  nrows[destrow][dest_column * BPP] = r;
344  nrows[destrow][1+dest_column * BPP] = g;
345  nrows[destrow][2+dest_column * BPP] = b;
346  nrows[destrow][3+dest_column * BPP] = a;
347  xtoleft -= xtofill;
348  xtofill = RATIO;
349  needcol=1;
350  }
351 
352  if (xtoleft > 0 ){
353  if (needcol) {
354  dest_column++;
355  r=0; g=0; b=0; a=0;
356  needcol=0;
357  }
358 
359  if (xrow[3+source_column*BPP] > 0) {
360  r += xtoleft * xrow[source_column*BPP] / RATIO;
361  g += xtoleft * xrow[1+source_column*BPP] / RATIO;
362  b += xtoleft * xrow[2+source_column*BPP] / RATIO;
363  }
364  if (xrow[3+source_column*BPP] > a)
365  a = xrow[3+source_column*BPP];
366 
367  xtofill -= xtoleft;
368  }
369  source_column++;
370  }
371 
372  if (xtofill > 0 ) {
373  source_column--;
374  if (xrow[3+source_column*BPP] > 0) {
375  r += xtofill * xrow[source_column*BPP] / RATIO;
376  g += xtofill * xrow[1+source_column*BPP] / RATIO;
377  b += xtofill * xrow[2+source_column*BPP] / RATIO;
378  }
379  if (xrow[3+source_column*BPP] > a)
380  a = xrow[3+source_column*BPP];
381  }
382 
383  /* Not positve, but without the bound checking for dest_column,
384  * we were overrunning the buffer. My guess is this only really
385  * showed up if when the images are being scaled - there is probably
386  * something like half a pixel left over.
387  */
388  if (!needcol && (dest_column < new_width)) {
389  nrows[destrow][dest_column * BPP] = r;
390  nrows[destrow][1+dest_column * BPP] = g;
391  nrows[destrow][2+dest_column * BPP] = b;
392  nrows[destrow][3+dest_column * BPP] = a;
393  }
394  destrow++;
395  }
396  *width = new_width;
397  *height = new_height;
398  return ndata;
399 }
400 
401 
402 /* This takes data that has already been converted into RGBA format (via
403  * png_to_data above perhaps) and creates a GdkPixmap and GdkBitmap out
404  * of it.
405  * Return non zero on error (currently, no checks for error conditions is done
406  */
407 int rgba_to_gdkpixmap(GdkWindow *window, uint8 *data,int width, int height,
408  GdkPixmap **pix, GdkBitmap **mask, GdkColormap *colormap)
409 {
410  GdkGC *gc, *gc_alpha;
411  int has_alpha=0, alpha;
412  GdkColor scolor;
413  int x,y;
414 
415  *pix = gdk_pixmap_new(window, width, height, -1);
416 
417  gc=gdk_gc_new(*pix);
418  gdk_gc_set_function(gc, GDK_COPY);
419 
420  *mask=gdk_pixmap_new(window, width, height,1);
421  gc_alpha=gdk_gc_new(*mask);
422 
423  scolor.pixel=1;
424  gdk_gc_set_foreground(gc_alpha, &scolor);
425  gdk_draw_rectangle(*mask, gc_alpha, 1, 0, 0, width, height);
426 
427  scolor.pixel=0;
428  gdk_gc_set_foreground(gc_alpha, &scolor);
429 
430  /* we need to draw the alpha channel. The image may not in fact
431  * have alpha, but no way to know at this point other than to try
432  * and draw it.
433  */
434  for (y=0; y<height; y++) {
435  for (x=0; x<width; x++) {
436  alpha = data[(y * width + x) * 4 +3];
437  /* Transparent bit */
438  if (alpha==0) {
439  gdk_draw_point(*mask, gc_alpha, x, y);
440  has_alpha=1;
441  }
442  }
443  }
444 
445  gdk_draw_rgb_32_image(*pix, gc, 0, 0, width, height, GDK_RGB_DITHER_NONE, data, width*4);
446  if (!has_alpha) {
447  gdk_pixmap_unref(*mask);
448  *mask = NULL;
449  }
450 
451  gdk_gc_destroy(gc_alpha);
452  gdk_gc_destroy(gc);
453  return 0;
454 }
int rgba_to_gdkpixmap(GdkWindow *window, uint8 *data, int width, int height, GdkPixmap **pix, GdkBitmap **mask, GdkColormap *colormap)
Definition: png.c:407
static int height
Definition: mapdata.c:104
static int data_len
Definition: png.c:56
uint8 * png_to_data(uint8 *data, int len, uint32 *width, uint32 *height)
Definition: png.c:63
static int width
Definition: mapdata.c:104
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:178
Pixmap mask
Definition: xutil.c:67
static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
Definition: png.c:58
const char *const rcsid_gtk_png_c
Definition: png.c:1
static int data_start
Definition: png.c:56
#define BPP
Definition: png.c:242
uint8 * rescale_rgba_data(uint8 *data, int *width, int *height, int scale)
Definition: png.c:244
static uint8 * data_cp
Definition: png.c:55
unsigned int uint32
Definition: client-types.h:77
#define RATIO
Definition: png.c:238
unsigned char uint8
Definition: client-types.h:81
Colormap colormap
Definition: x11.c:190
#define MAX_IMAGE_HEIGHT
Definition: png.c:241
#define MAX_IMAGE_WIDTH
Definition: png.c:240