Crossfire Client, Branches  R11627
png.c
Go to the documentation of this file.
1 const char *rcsid_x11_png_c =
2  "$Id: png.c 9195 2008-06-01 15:36:42Z 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 /* This is a light weight png -> xpixmap function. Most of the code is from
26  * the example png documentation.
27  * I wrote this because I could not find a simple function that did this -
28  * most all libraries out there tended to do a lot more than I needed for
29  * crossfire - in addition, imLib actually has bugs which prevents it from
30  * rendering some images properly.
31  *
32  * This function is far from complete, but does the job and removes the need
33  * for yet another library.
34  */
35 
36 #include <config.h>
37 #include <stdlib.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
40 #include <png.h>
41 #include <X11/Xlib.h>
42 #include <X11/Xutil.h>
43 #include "client-types.h"
44 #include "client.h"
45 #include "x11.h"
46 
47 /* Defines for PNG return values */
48 /* These should be in a header file, but currently our calling functions
49  * routines just check for nonzero return status and don't really care
50  * why the load failed.
51  */
52 #define PNGX_NOFILE 1
53 #define PNGX_OUTOFMEM 2
54 #define PNGX_DATA 3
55 
56 static unsigned char *data_cp;
57 static int data_len, data_start;
58 
59 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
60  memcpy(data, data_cp + data_start, length);
61  data_start += length;
62 }
63 
64 
66 {
67  uint8 *pixels=NULL;
68  static png_bytepp rows=NULL;
69  static int rows_byte=0;
70 
71  png_structp png_ptr;
72  png_infop info_ptr;
73  int bit_depth, color_type, interlace_type, compression_type, y;
74 
75  data_len=len;
76  data_cp = data;
77  data_start=0;
78 
79  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
80  NULL, NULL, NULL);
81 
82  if (!png_ptr) {
83  return NULL;
84  }
85  info_ptr = png_create_info_struct (png_ptr);
86 
87  if (!info_ptr) {
88  png_destroy_read_struct (&png_ptr, NULL, NULL);
89  return NULL;
90  }
91  if (setjmp (png_ptr->jmpbuf)) {
92  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
93  return NULL;
94  }
95 
96  png_set_read_fn(png_ptr, NULL, user_read_data);
97  png_read_info (png_ptr, info_ptr);
98 
99  /* Breaking these out instead of using png_get_IHDR fixes bug
100  * 1249877 - problems on 64 bit systems (amd64 at least)
101  */
102  *width = png_get_image_width(png_ptr, info_ptr);
103  *height = png_get_image_height(png_ptr, info_ptr);
104  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
105  color_type = png_get_color_type(png_ptr, info_ptr);
106  interlace_type = png_get_interlace_type(png_ptr, info_ptr);
107  compression_type = png_get_compression_type(png_ptr, info_ptr);
108 
109  if (color_type == PNG_COLOR_TYPE_PALETTE &&
110  bit_depth <= 8) {
111 
112  /* Convert indexed images to RGB */
113  png_set_expand (png_ptr);
114 
115  } else if (color_type == PNG_COLOR_TYPE_GRAY &&
116  bit_depth < 8) {
117 
118  /* Convert grayscale to RGB */
119  png_set_expand (png_ptr);
120 
121  } else if (png_get_valid (png_ptr,
122  info_ptr, PNG_INFO_tRNS)) {
123 
124  /* If we have transparency header, convert it to alpha
125  channel */
126  png_set_expand(png_ptr);
127 
128  } else if (bit_depth < 8) {
129 
130  /* If we have < 8 scale it up to 8 */
131  png_set_expand(png_ptr);
132 
133 
134  /* Conceivably, png_set_packing() is a better idea;
135  * God only knows how libpng works
136  */
137  }
138  /* If we are 16-bit, convert to 8-bit */
139  if (bit_depth == 16) {
140  png_set_strip_16(png_ptr);
141  }
142 
143  /* If gray scale, convert to RGB */
144  if (color_type == PNG_COLOR_TYPE_GRAY ||
145  color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
146  png_set_gray_to_rgb(png_ptr);
147  }
148 
149  /* If interlaced, handle that */
150  if (interlace_type != PNG_INTERLACE_NONE) {
151  png_set_interlace_handling(png_ptr);
152  }
153 
154  /* pad it to 4 bytes to make processing easier */
155  if (!(color_type & PNG_COLOR_MASK_ALPHA))
156  png_set_filler(png_ptr, 255, PNG_FILLER_AFTER);
157 
158  /* Update the info the reflect our transformations */
159  png_read_update_info(png_ptr, info_ptr);
160 
161  /* re-read due to transformations just made */
162  /* Breaking these out instead of using png_get_IHDR fixes bug
163  * 1249877 - problems on 64 bit systems (amd64 at least)
164  */
165  *width = png_get_image_width(png_ptr, info_ptr);
166  *height = png_get_image_height(png_ptr, info_ptr);
167  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
168  color_type = png_get_color_type(png_ptr, info_ptr);
169  interlace_type = png_get_interlace_type(png_ptr, info_ptr);
170  compression_type = png_get_compression_type(png_ptr, info_ptr);
171 
172  pixels = (uint8*)malloc(*width * *height * 4);
173 
174  if (!pixels) {
175  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
176  fprintf(stderr,"Out of memory - exiting\n");
177  exit(1);
178  }
179 
180  /* the png library needs the rows, but we will just return the raw data */
181  if (rows_byte == 0) {
182  rows =(png_bytepp) malloc(sizeof(char*) * *height);
183  rows_byte=*height;
184  } else if (*height > rows_byte) {
185  rows =(png_bytepp) realloc(rows, sizeof(char*) * *height);
186  rows_byte=*height;
187  }
188  if (!rows) {
189  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
190  return NULL;
191  }
192 
193  for (y=0; y<*height; y++)
194  rows[y] = pixels + y * *width * 4;
195 
196  png_read_image(png_ptr, rows);
197  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
198 
199  return pixels;
200 }
201 
202 
203 /* rescale_png_image takes png data and scales it accordingly.
204  * This function is based on pnmscale, but has been modified to support alpha
205  * channel - instead of blending the alpha channel, it takes the most opaque
206  * value - blending it is not likely to give sane results IMO - for any image
207  * that has transparent information, if we blended the alpha, the result would
208  * be the edges of that region being partially transparent.
209  * This function has also been re-written to use more static data - in the
210  * case of the client, it will be called thousands of times, so it doesn't make
211  * sense to free the data and then re-allocate it.
212  *
213  * For pixels that are fully transparent, the end result after scaling is they
214  * will be tranparent black. This is a needed effect for blending to work properly.
215  *
216  * This function returns a new pointer to the scaled image data. This is
217  * malloc'd data, so should be freed at some point to prevent leaks.
218  * This function does not modify the data passed to it - the caller is responsible
219  * for freeing it if it is no longer needed.
220  *
221  * function arguments:
222  * data: PNG data - really, this is any 4 byte per pixel data, in RGBA format.
223  * *width, *height: The source width and height. These values are modified
224  * to contain the new image size.
225  * scale: percentage size that new image should be. 100 is a same size
226  * image - values larger than 100 will result in zoom, values less than
227  * 100 will result in a shrinkage.
228  */
229 
230 /* RATIO is used to know what units scale is - in this case, a percentage, so
231  * it is set to 100
232  */
233 #define RATIO 100
234 
235 #define MAX_IMAGE_WIDTH 1024
236 #define MAX_IMAGE_HEIGHT 1024
237 #define BPP 4
238 
239 uint8 *rescale_rgba_data(uint8 *data, int *width, int *height, int scale)
240 {
241  static int xrow[BPP * MAX_IMAGE_WIDTH], yrow[BPP*MAX_IMAGE_HEIGHT];
242  static uint8 *nrows[MAX_IMAGE_HEIGHT];
243 
244  /* Figure out new height/width */
245  int new_width = *width * scale / RATIO, new_height = *height * scale / RATIO;
246 
247  int sourcerow=0, ytoleft, ytofill, xtoleft, xtofill, dest_column=0, source_column=0, needcol,
248  destrow=0;
249  int x,y;
250  uint8 *ndata;
251  uint8 r,g,b,a;
252 
253  if (*width > MAX_IMAGE_WIDTH || new_width > MAX_IMAGE_WIDTH
254  || *height > MAX_IMAGE_HEIGHT || new_height > MAX_IMAGE_HEIGHT)
255  {
256  fprintf(stderr, "Image too big\n");
257  exit(0);
258  }
259 
260  /* clear old values these may have */
261  memset(yrow, 0, sizeof(int) * *height * BPP);
262 
263  ndata = (uint8*)malloc(new_width * new_height * BPP);
264 
265  for (y=0; y<new_height; y++)
266  nrows[y] = (png_bytep) (ndata + y * new_width * BPP);
267 
268  ytoleft = scale;
269  ytofill = RATIO;
270 
271  for (y=0,sourcerow=0; y < new_height; y++) {
272  memset(xrow, 0, sizeof(int) * *width * BPP);
273  while (ytoleft < ytofill) {
274  for (x=0; x< *width; ++x) {
275  /* Only want to copy the data if this is not a transperent pixel.
276  * If it is transparent, the color information is has is probably
277  * bogus, and blending that makes the results look worse.
278  */
279  if (data[(sourcerow * *width + x)*BPP+3] > 0 ) {
280  yrow[x*BPP] += ytoleft * data[(sourcerow * *width + x)*BPP]/RATIO;
281  yrow[x*BPP+1] += ytoleft * data[(sourcerow * *width + x)*BPP+1]/RATIO;
282  yrow[x*BPP+2] += ytoleft * data[(sourcerow * *width + x)*BPP+2]/RATIO;
283  }
284  /* Alpha is a bit special - we don't want to blend it -
285  * we want to take whatever is the more opaque value.
286  */
287  if (data[(sourcerow * *width + x)*BPP+3] > yrow[x*BPP+3])
288  yrow[x*BPP+3] = data[(sourcerow * *width + x)*BPP+3];
289  }
290  ytofill -= ytoleft;
291  ytoleft = scale;
292  if (sourcerow < *height)
293  sourcerow++;
294  }
295 
296  for (x=0; x < *width; ++x) {
297  if (data[(sourcerow * *width + x)*BPP+3] > 0 ) {
298  xrow[x*BPP] = yrow[x*BPP] + ytofill * data[(sourcerow * *width + x)*BPP] / RATIO;
299  xrow[x*BPP+1] = yrow[x*BPP+1] + ytofill * data[(sourcerow * *width + x)*BPP+1] / RATIO;
300  xrow[x*BPP+2] = yrow[x*BPP+2] + ytofill * data[(sourcerow * *width + x)*BPP+2] / RATIO;
301  }
302  if (data[(sourcerow * *width + x)*BPP+3] > xrow[x*BPP+3])
303  xrow[x*BPP+3] = data[(sourcerow * *width + x)*BPP+3];
304  yrow[x*BPP]=0; yrow[x*BPP+1]=0; yrow[x*BPP+2]=0; yrow[x*BPP+3]=0;
305  }
306 
307  ytoleft -= ytofill;
308  if (ytoleft <= 0) {
309  ytoleft = scale;
310  if (sourcerow < *height)
311  sourcerow++;
312  }
313 
314  ytofill = RATIO;
315  xtofill = RATIO;
316  dest_column = 0;
317  source_column=0;
318  needcol=0;
319  r=0; g=0; b=0; a=0;
320 
321  for (x=0; x< *width; x++) {
322  xtoleft = scale;
323 
324  while (xtoleft >= xtofill) {
325  if (needcol) {
326  dest_column++;
327  r=0; g=0; b=0; a=0;
328  }
329 
330  if (xrow[source_column*BPP+3] > 0) {
331  r += xtofill * xrow[source_column*BPP] / RATIO;
332  g += xtofill * xrow[1+source_column*BPP] / RATIO;
333  b += xtofill * xrow[2+source_column*BPP] / RATIO;
334  }
335  if (xrow[3+source_column*BPP] > a)
336  a = xrow[3+source_column*BPP];
337 
338  nrows[destrow][dest_column * BPP] = r;
339  nrows[destrow][1+dest_column * BPP] = g;
340  nrows[destrow][2+dest_column * BPP] = b;
341  nrows[destrow][3+dest_column * BPP] = a;
342  xtoleft -= xtofill;
343  xtofill = RATIO;
344  needcol=1;
345  }
346 
347  if (xtoleft > 0 ){
348  if (needcol) {
349  dest_column++;
350  r=0; g=0; b=0; a=0;
351  needcol=0;
352  }
353 
354  if (xrow[3+source_column*BPP] > 0) {
355  r += xtoleft * xrow[source_column*BPP] / RATIO;
356  g += xtoleft * xrow[1+source_column*BPP] / RATIO;
357  b += xtoleft * xrow[2+source_column*BPP] / RATIO;
358  }
359  if (xrow[3+source_column*BPP] > a)
360  a = xrow[3+source_column*BPP];
361 
362  xtofill -= xtoleft;
363  }
364  source_column++;
365  }
366 
367  if (xtofill > 0 ) {
368  source_column--;
369  if (xrow[3+source_column*BPP] > 0) {
370  r += xtofill * xrow[source_column*BPP] / RATIO;
371  g += xtofill * xrow[1+source_column*BPP] / RATIO;
372  b += xtofill * xrow[2+source_column*BPP] / RATIO;
373  }
374  if (xrow[3+source_column*BPP] > a)
375  a = xrow[3+source_column*BPP];
376  }
377 
378  /* Not positve, but without the bound checking for dest_column,
379  * we were overrunning the buffer. My guess is this only really
380  * showed up if when the images are being scaled - there is probably
381  * something like half a pixel left over.
382  */
383  if (!needcol && (dest_column < new_width)) {
384  nrows[destrow][dest_column * BPP] = r;
385  nrows[destrow][1+dest_column * BPP] = g;
386  nrows[destrow][2+dest_column * BPP] = b;
387  nrows[destrow][3+dest_column * BPP] = a;
388  }
389  destrow++;
390  }
391  *width = new_width;
392  *height = new_height;
393  return ndata;
394 }
395 
396 
397 static XImage *ximage;
398 static int rmask=0, bmask=0,gmask=0,need_color_alloc=0, rshift=16, bshift=0, gshift=8,
402  unsigned char red, green, blue;
404 } *color_values;
405 
406 #define COLOR_FACTOR 3
407 #define BRIGHTNESS_FACTOR 1
408 
409 /* This function is used to find the pixel and return it
410  * to the caller. We store what pixels we have already allocated
411  * and try to find a match against that. The reason for this is that
412  * XAllocColor is a very slow routine. Before my optimizations,
413  * png loading took about 140 seconds, of which 60 seconds of that
414  * was in XAllocColor calls.
415  */
416 long pngx_find_color(Display *display, Colormap *cmap, int red, int green, int blue)
417 {
418 
419  int i, closeness=0xffffff, close_entry=-1, tmpclose;
420  XColor scolor;
421 
422  for (i=0; i<colors_alloced; i++) {
423  if ((color_values[i].red == red) && (color_values[i].green == green) &&
424  (color_values[i].blue == blue)) return color_values[i].pixel_value;
425 
426  tmpclose = COLOR_FACTOR * (abs(red - color_values[i].red) +
427  abs(green - color_values[i].green) +
428  abs(blue - color_values[i].blue)) +
429  BRIGHTNESS_FACTOR * abs((red + green + blue) -
431 
432  /* I already know that 8 bit is not enough to hold all the PNG colors,
433  * so lets do some early optimization
434  */
435  if (tmpclose < 3) return color_values[i].pixel_value;
436  if (tmpclose < closeness) {
437  closeness = tmpclose;
438  close_entry = i;
439  }
440  }
441 
442  /* If the colormap is full, no reason to do anything more */
443  if (colors_alloced == colormap_size)
444  return color_values[close_entry].pixel_value;
445 
446 
447  /* If we get here, we haven't cached the color */
448 
449  scolor.red = (red << 8) + red;
450  scolor.green = (green << 8) + green;
451  scolor.blue = (blue << 8) + blue;
452 
453 
454 again:
455  if (!XAllocColor(display, *cmap, &scolor)) {
456  if (!private_cmap) {
457  fprintf(stderr,"Going to private colormap after %d allocs\n", colors_alloced);
458  *cmap = XCopyColormapAndFree(display, *cmap);
459  private_cmap=1;
460  goto again;
461  }
462  else {
463 #if 0
464  fprintf(stderr,"Unable to allocate color %d %d %d, %d colors alloced, will use closenss value %d\n",
465  red, green, blue, colors_alloced, closeness);
466 #endif
467  colors_alloced = colormap_size; /* Colormap is exhausted */
468  return color_values[close_entry].pixel_value;
469  }
470  }
474  color_values[colors_alloced].pixel_value= scolor.pixel;
475  colors_alloced++;
476  return scolor.pixel;
477 }
478 
479 
480 
481 
483 {
484  int pad,depth;
485  XVisualInfo xvinfo, *xvret;
486  Visual *visual;
487 
488  depth = DefaultDepth(display, DefaultScreen(display));
489  visual = DefaultVisual(display, DefaultScreen(display));
490  xvinfo.visualid = XVisualIDFromVisual(visual);
491  xvret = XGetVisualInfo(display, VisualIDMask, &xvinfo, &pad);
492  if (pad != 1) {
493  fprintf(stderr,"XGetVisual found %d matching visuals?\n", pad);
494  return 1;
495  }
496  rmask = xvret -> red_mask;
497  gmask = xvret -> green_mask;
498  bmask = xvret -> blue_mask;
499  /* We need to figure out how many bits to shift. Thats what this
500  * following block of code does. We can't presume to use just
501  * 16, 8, 0 bits for RGB respectively, as if you are on 16 bit,
502  * that is not correct. There may be a much easier way to do this -
503  * it is just bit manipulation. Note that we want to preserver
504  * the most significant bits, so these shift values can very
505  * well be negative, in which case we need to know that -
506  * the shift operators don't work with negative values.
507  * An example is 5 bits for blue - in that case, we really
508  * want to shfit right (>>) by 3 bits.
509  */
510  rshift=0;
511  if (rmask) {
512  while (!((1 << rshift) & rmask)) rshift++;
513  while (((1 << rshift) & rmask)) rshift++;
514  rshift -= 8;
515  if (rshift < 0 ) {
516  rev_rshift=1;
517  rshift = -rshift;
518  }
519  }
520  gshift=0;
521  if (gmask) {
522  while (!((1 << gshift) & gmask)) gshift++;
523  while (((1 << gshift) & gmask)) gshift++;
524  gshift -= 8;
525  if (gshift < 0 ) {
526  rev_gshift=1;
527  gshift = -gshift;
528  }
529  }
530  bshift=0;
531  if (bmask) {
532  while (!((1 << bshift) & bmask)) bshift++;
533  while (((1 << bshift) & bmask)) bshift++;
534  bshift -= 8;
535  if (bshift < 0 ) {
536  rev_bshift=1;
537  bshift = -bshift;
538  }
539  }
540 
541 
542  if (xvret->class==PseudoColor) {
544  if (xvret->colormap_size>256) {
545  fprintf(stderr,"One a pseudocolor visual, but colormap has %d entries?\n", xvret->colormap_size);
546  }
547  color_values=malloc(sizeof(struct Pngx_Color_Values) * xvret->colormap_size);
548  colormap_size = xvret->colormap_size-1; /* comparing # of alloced colors against this */
549  }
550  XFree(xvret);
551 
552  if (depth>16) pad = 32;
553  else if (depth > 8) pad = 16;
554  else pad = 8;
555 
556  ximage = XCreateImage(display, visual,
557  depth,
558  ZPixmap, 0, 0,
559  MAX_IMAGE_SIZE, MAX_IMAGE_SIZE, pad, 0);
560  if (!ximage) {
561  fprintf(stderr,"Failed to create Ximage\n");
562  return 1;
563  }
564  ximage->data = malloc(ximage->bytes_per_line * MAX_IMAGE_SIZE);
565  if (!ximage->data) {
566  fprintf(stderr,"Failed to create Ximage data\n");
567  return 1;
568  }
569  return 0;
570 }
571 
572 
573 int png_to_xpixmap(Display *display, Drawable draw, unsigned char *data, int len,
574  Pixmap *pix, Pixmap *mask, Colormap *cmap,
575  unsigned long *width, unsigned long *height)
576 {
577  static uint8 *pixels=NULL;
578  static int pixels_byte=0, rows_byte=0;
579  static png_bytepp rows=NULL;
580 
581  png_structp png_ptr=NULL;
582  png_infop info_ptr=NULL;
583  int bit_depth, color_type, interlace_type, compression_type,
584  red,green,blue, lastred=-1, lastgreen=-1, lastblue=-1,alpha,bpp, x,y,
585  has_alpha, cmask, lastcmask, lastcolor;
586  GC gc, gc_alpha;
587 
588 
589  data_len=len;
590  data_cp = data;
591  data_start=0;
592  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
593  NULL, NULL, NULL);
594  if (!png_ptr) {
595  return PNGX_OUTOFMEM;
596  }
597  info_ptr = png_create_info_struct (png_ptr);
598 
599  if (!info_ptr) {
600  png_destroy_read_struct (&png_ptr, NULL, NULL);
601  return PNGX_OUTOFMEM;
602  }
603  if (setjmp (png_ptr->jmpbuf)) {
604  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
605  return PNGX_DATA;
606  }
607  /* put these here to prevent compiler warnings about them getting
608  * clobbered by setjmp
609  */
610  has_alpha=0;
611  cmask=-1;
612  lastcmask=-1;
613  lastcolor=-1;
614 
615  png_set_read_fn(png_ptr, NULL, user_read_data);
616  png_read_info (png_ptr, info_ptr);
617 
618  /* re-read due to transformations just made */
619  /* Breaking these out instead of using png_get_IHDR fixes bug
620  * 1249877 - problems on 64 bit systems (amd64 at least)
621  */
622  *width = png_get_image_width(png_ptr, info_ptr);
623  *height = png_get_image_height(png_ptr, info_ptr);
624  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
625  color_type = png_get_color_type(png_ptr, info_ptr);
626  interlace_type = png_get_interlace_type(png_ptr, info_ptr);
627  compression_type = png_get_compression_type(png_ptr, info_ptr);
628 
629  if (color_type == PNG_COLOR_TYPE_PALETTE &&
630  bit_depth <= 8) {
631 
632  /* Convert indexed images to RGB */
633  png_set_expand (png_ptr);
634 
635  } else if (color_type == PNG_COLOR_TYPE_GRAY &&
636  bit_depth < 8) {
637 
638  /* Convert grayscale to RGB */
639  png_set_expand (png_ptr);
640 
641  } else if (png_get_valid (png_ptr,
642  info_ptr, PNG_INFO_tRNS)) {
643 
644  /* If we have transparency header, convert it to alpha
645  channel */
646  png_set_expand(png_ptr);
647 
648  } else if (bit_depth < 8) {
649 
650  /* If we have < 8 scale it up to 8 */
651  png_set_expand(png_ptr);
652 
653 
654  /* Conceivably, png_set_packing() is a better idea;
655  * God only knows how libpng works
656  */
657  }
658  /* If we are 16-bit, convert to 8-bit */
659  if (bit_depth == 16) {
660  png_set_strip_16(png_ptr);
661  }
662 
663  /* If gray scale, convert to RGB */
664  if (color_type == PNG_COLOR_TYPE_GRAY ||
665  color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
666  png_set_gray_to_rgb(png_ptr);
667  }
668 
669  /* If interlaced, handle that */
670  if (interlace_type != PNG_INTERLACE_NONE) {
671  png_set_interlace_handling(png_ptr);
672  }
673 
674  /* Update the info the reflect our transformations */
675  png_read_update_info(png_ptr, info_ptr);
676  /* re-read due to transformations just made */
677  /* re-read due to transformations just made */
678 
679  /* Breaking these out instead of using png_get_IHDR fixes bug
680  * 1249877 - problems on 64 bit systems (amd64 at least)
681  */
682  *width = png_get_image_width(png_ptr, info_ptr);
683  *height = png_get_image_height(png_ptr, info_ptr);
684  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
685  color_type = png_get_color_type(png_ptr, info_ptr);
686  interlace_type = png_get_interlace_type(png_ptr, info_ptr);
687  compression_type = png_get_compression_type(png_ptr, info_ptr);
688 
689  if (color_type & PNG_COLOR_MASK_ALPHA)
690  bpp = 4;
691  else
692  bpp = 3;
693 
694  /* Allocate the memory we need once, and increase it if necessary.
695  * This is more efficient the allocating this block of memory every time.
696  */
697  if (pixels_byte==0) {
698  pixels_byte =*width * *height * bpp;
699  pixels = (uint8*)malloc(pixels_byte);
700  } else if ((*width * *height * bpp) > pixels_byte) {
701  pixels_byte =*width * *height * bpp;
702  pixels=realloc(pixels, pixels_byte);
703  }
704 
705  if (!pixels) {
706  pixels_byte=0;
707  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
708  return PNGX_OUTOFMEM;
709  }
710  if (rows_byte == 0) {
711  rows =(png_bytepp) malloc(sizeof(char*) * *height);
712  rows_byte=*height;
713  } else if (*height > rows_byte) {
714  rows =(png_bytepp) realloc(rows, sizeof(char*) * *height);
715  rows_byte=*height;
716  }
717  if (!rows) {
718  pixels_byte=0;
719  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
720  return PNGX_OUTOFMEM;
721  }
722 
723  for (y=0; y<*height; y++)
724  rows[y] = pixels + y * (*width) * bpp;
725 
726  png_read_image(png_ptr, rows);
727 #if 0
728  fprintf(stderr,"image is %d X %d, bpp=%d, color_type=%d\n",
729  *width, *height, bpp, color_type);
730 #endif
731 
732  *pix = XCreatePixmap(display, draw, *width, *height,
733  DefaultDepth(display, DefaultScreen(display)));
734 
735  gc=XCreateGC(display, *pix, 0, NULL);
736  XSetFunction(display, gc, GXcopy);
737  XSetPlaneMask(display, gc, AllPlanes);
738 
739  if (color_type & PNG_COLOR_MASK_ALPHA) {
740  /* The foreground/background colors are not really
741  * colors, but rather values to set in the mask.
742  * The values used below work properly on at least
743  * 8 bit and 16 bit display - using things like
744  * blackpixel & whitepixel does NO work on
745  * both types of display.
746  */
747  *mask=XCreatePixmap(display ,draw, *width, *height,1);
748  gc_alpha=XCreateGC(display, *mask, 0, NULL);
749  XSetFunction(display, gc_alpha, GXcopy);
750  XSetPlaneMask(display, gc_alpha, AllPlanes);
751  XSetForeground(display, gc_alpha, 1);
752  XFillRectangle(display, *mask, gc_alpha, 0, 0, *width, *height);
753  XSetForeground(display, gc_alpha, 0);
754  has_alpha=1;
755  }
756  else {
757  *mask = None;
758  gc_alpha = None; /* Prevent compile warnings */
759  }
760 
761  for (y=0; y<*height; y++) {
762  for (x=0; x<*width; x++) {
763  red=rows[y][x*bpp];
764  green=rows[y][x*bpp+1];
765  blue=rows[y][x*bpp+2];
766  if (has_alpha) {
767  alpha = rows[y][x*bpp+3];
768  /* Transparent bit */
769  if (alpha==0) {
770  XDrawPoint(display, *mask, gc_alpha, x, y);
771  }
772  }
773  if (need_color_alloc) {
774  /* We only use cmask to avoid calling pngx_find_color repeatedly.
775  * when the color has not changed from the last pixel.
776  */
777  if ((lastred != red) && (lastgreen != green) && (lastblue != blue)) {
778  lastcolor = pngx_find_color(display, cmap, red, green, blue);
779  lastcmask = cmask;
780  }
781  XPutPixel(ximage, x, y, lastcolor);
782  } else {
783  if ((lastred != red) && (lastgreen != green) && (lastblue != blue)) {
784  if (rev_rshift) red >>= rshift;
785  else red <<= rshift;
786  if (rev_gshift) green >>= gshift;
787  else green <<= gshift;
788  if (rev_bshift) blue >>= bshift;
789  else blue <<= bshift;
790 
791  cmask = (red & rmask) | (green & gmask) | (blue & bmask);
792  }
793  XPutPixel(ximage, x, y, cmask);
794  }
795  }
796  }
797 
798  XPutImage(display, *pix, gc, ximage, 0, 0, 0, 0, 32, 32);
799  if (has_alpha)
800  XFreeGC(display, gc_alpha);
801  XFreeGC(display, gc);
802  png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
803  return 0;
804 }
805 
806 
807 /* like png_to_xpixmap above, but the data has already been decompressed
808  * into rgba data.
809  */
810 
811 int rgba_to_xpixmap(Display *display, Drawable draw, uint8 *pixels,
812  Pixmap *pix, Pixmap *mask, Colormap *cmap,
813  unsigned long width, unsigned long height)
814 {
815  int red,green,blue, lastred=-1, lastgreen=-1, lastblue=-1,alpha,x,y,
816  cmask=-1, lastcmask, lastcolor=-1;
817  GC gc, gc_alpha;
818 
819  *pix = XCreatePixmap(display, draw, width, height,
820  DefaultDepth(display, DefaultScreen(display)));
821 
822  gc=XCreateGC(display, *pix, 0, NULL);
823  XSetFunction(display, gc, GXcopy);
824  XSetPlaneMask(display, gc, AllPlanes);
825 
826  /* The foreground/background colors are not really
827  * colors, but rather values to set in the mask.
828  * The values used below work properly on at least
829  * 8 bit and 16 bit display - using things like
830  * blackpixel & whitepixel does NO work on
831  * both types of display.
832  */
833 
834  *mask=XCreatePixmap(display ,draw, width, height,1);
835  gc_alpha=XCreateGC(display, *mask, 0, NULL);
836  XSetFunction(display, gc_alpha, GXcopy);
837  XSetPlaneMask(display, gc_alpha, AllPlanes);
838  XSetForeground(display, gc_alpha, 1);
839  XFillRectangle(display, *mask, gc_alpha, 0, 0, width, height);
840  XSetForeground(display, gc_alpha, 0);
841 
842  for (y=0; y<height; y++) {
843  for (x=0; x<width; x++) {
844  red= pixels[(y * width + x)*4];
845  green= pixels[(y * width + x)*4 + 1];
846  blue= pixels[(y * width + x)*4 + 2];
847  alpha = pixels[(y * width + x)*4 + 3];
848  if (alpha==0) {
849  XDrawPoint(display, *mask, gc_alpha, x, y);
850  }
851  if (need_color_alloc) {
852  /* We only use cmask to avoid calling pngx_find_color repeatedly.
853  * when the color has not changed from the last pixel.
854  */
855  if ((lastred != red) && (lastgreen != green) && (lastblue != blue)) {
856  lastcolor = pngx_find_color(display, cmap, red, green, blue);
857  lastcmask = cmask;
858  }
859  XPutPixel(ximage, x, y, lastcolor);
860  } else {
861  if ((lastred != red) && (lastgreen != green) && (lastblue != blue)) {
862  if (rev_rshift) red >>= rshift;
863  else red <<= rshift;
864  if (rev_gshift) green >>= gshift;
865  else green <<= gshift;
866  if (rev_bshift) blue >>= bshift;
867  else blue <<= bshift;
868 
869  cmask = (red & rmask) | (green & gmask) | (blue & bmask);
870  }
871  XPutPixel(ximage, x, y, cmask);
872  }
873  }
874  }
875 
876  XPutImage(display, *pix, gc, ximage, 0, 0, 0, 0, width, height);
877  XFreeGC(display, gc_alpha);
878  XFreeGC(display, gc);
879  return 0;
880 }
881 
882 
883 /* Takes the pixmap to put the data into, as well as the rgba
884  * data (ie, already loaded with png_to_data). Scales and
885  * stores the relevant data into the pixmap structure.
886  * returns 1 on failure.
887  */
888 int create_and_rescale_image_from_data(Cache_Entry *ce, int pixmap_num, uint8 *rgba_data, int width, int height)
889 {
890  struct PixmapInfo *pi;
891 
892  if (pixmap_num <= 0 || pixmap_num >= MAXPIXMAPNUM)
893  return 1;
894 
895  if (pixmaps[pixmap_num] != pixmaps[0]) {
896  XFreePixmap(display, pixmaps[pixmap_num]->pixmap);
897  if (pixmaps[pixmap_num]->mask)
898  XFreePixmap(display, pixmaps[pixmap_num]->mask);
899  free(pixmaps[pixmap_num]);
900  pixmaps[pixmap_num] = pixmaps[0];
901  }
902 
903  pi = malloc(sizeof(struct PixmapInfo));
904  if (rgba_to_xpixmap(display, win_game, rgba_data, &pi->pixmap,
905  &pi->mask, &colormap, width, height) != 0) {
906  free(pi);
907  return 1;
908  }
909 
910  if (!pi->pixmap || !pi->mask) {
911  if (pi->pixmap)
912  XFreePixmap(display, pi->pixmap);
913  if (pi->mask)
914  XFreePixmap(display, pi->mask);
915  free(pi);
916  return 1;
917  }
918 
919  pi->width = width / image_size;
920  pi->height = height / image_size;
921 
922  if (ce) {
923  ce->image_data = pi;
924  }
925  pixmaps[pixmap_num] = pi;
926  return 0;
927 }
928 
929 void get_map_image_size(int face, uint8 *w, uint8 *h)
930 {
931  /* This function is not implemented yet, so just return default values */
932  if (face < 0 || face >= MAXPIXMAPNUM) {
933  *w = 1;
934  *h = 1;
935  }
936  else {
937  *w = pixmaps[face]->width;
938  *h = pixmaps[face]->height;
939  }
940 }
941 
942 
943 #if PNG_MAIN
944 
945 int main(int argc, char *argv[])
946 {
947  Display *disp;
948  char data[256],buf[1024*1024];
949  int fd,i,z, alpha;
950  unsigned long width, height;
951  Window window;
952  XSetWindowAttributes wattrs;
953  Pixmap pix, mask;
954  GC gc;
955 
956  if (argc!=2) {
957  fprintf(stderr,"Usage: %s <filename>\n", argv[0]);
958  exit(1);
959  }
960 
961  if (!(disp=XOpenDisplay(NULL))) {
962  fprintf(stderr,"Unable to open display\n");
963  exit(1);
964  }
965 
966  wattrs.backing_store = WhenMapped;
967  wattrs.background_pixel = WhitePixel(disp, DefaultScreen(disp));
968 
969  window=XCreateWindow(disp, DefaultRootWindow(disp), 0, 0,
970  32, 32, 0, CopyFromParent, InputOutput, CopyFromParent,
971  CWBackingStore|CWBackPixel, &wattrs);
972 
973  i = open(argv[1], O_RDONLY);
974  z=read(i, buf, 1024*1024);
975  close(i);
976  fprintf(stderr,"Read %d bytes from %s\n", z, argv[1]);
977  png_to_xpixmap(disp, window, buf, z, &pix, &mask, DefaultColormap(disp,
978  DefaultScreen(disp)), &width, &height);
979  XResizeWindow(disp, window, width, height);
980  if (!window) {
981  fprintf(stderr,"Unable to create window\n");
982  exit(1);
983  }
984  if (mask) XShapeCombineMask(disp,window,ShapeBounding,0,0,mask,ShapeSet);
985  XMapWindow(disp, window);
986  XFlush(disp);
987  gc=XCreateGC(disp, pix, 0, NULL);
988  if (mask)
989  XSetClipMask(disp, gc, mask);
990 
991  /* A simple way to display the image without needing to worry
992  * about exposures and redraws.
993  */
994  for (i=0; i<30; i++) {
995  XCopyArea(disp, pix, window, gc, 0, 0, width, height, 0 , 0);
996  XFlush(disp);
997  sleep(1);
998  }
999 
1000  exit(0);
1001 }
1002 #endif
Pixmap pixmap
Definition: x11.h:37
unsigned char blue
Definition: png.c:402
static int height
Definition: mapdata.c:104
long pixel_value
Definition: png.c:403
static int gshift
Definition: png.c:398
long pngx_find_color(Display *display, Colormap *cmap, int red, int green, int blue)
Definition: png.c:416
int init_pngx_loader(Display *display)
Definition: png.c:482
int main()
Definition: first.c:1
unsigned char green
Definition: png.c:402
uint8 * png_to_data(uint8 *data, int len, uint32 *width, uint32 *height)
Definition: png.c:63
static int bmask
Definition: png.c:398
static int bshift
Definition: png.c:398
static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
Definition: png.c:59
#define MAXPIXMAPNUM
Definition: client.h:470
static XImage * ximage
Definition: png.c:397
static unsigned char * data_cp
Definition: png.c:56
void * image_data
Definition: client.h:484
PixmapInfo * pixmaps[MAXPIXMAPNUM]
Definition: gx11.c:118
struct Pngx_Color_Values * color_values
uint8 height
Definition: x11.h:38
static int width
Definition: mapdata.c:104
#define BPP
Definition: png.c:237
static int colormap_size
Definition: png.c:400
Pixmap mask
Definition: xutil.c:67
#define MAX_IMAGE_SIZE
Definition: client.h:316
static int rev_rshift
Definition: png.c:399
int rgba_to_xpixmap(Display *display, Drawable draw, uint8 *pixels, Pixmap *pix, Pixmap *mask, Colormap *cmap, unsigned long width, unsigned long height)
Definition: png.c:811
#define COLOR_FACTOR
Definition: png.c:406
static int rshift
Definition: png.c:398
Pixmap mask
Definition: x11.h:37
unsigned char red
Definition: png.c:402
int png_to_xpixmap(Display *display, Drawable draw, unsigned char *data, int len, Pixmap *pix, Pixmap *mask, Colormap *cmap, unsigned long *width, unsigned long *height)
Definition: png.c:573
Display * display
Definition: x11.c:184
static int private_cmap
Definition: png.c:400
static int gmask
Definition: png.c:398
int create_and_rescale_image_from_data(Cache_Entry *ce, int pixmap_num, uint8 *rgba_data, int width, int height)
Definition: png.c:888
static int rev_gshift
Definition: png.c:399
uint8 * rescale_rgba_data(uint8 *data, int *width, int *height, int scale)
Definition: png.c:244
#define BRIGHTNESS_FACTOR
Definition: png.c:407
unsigned int uint32
Definition: client-types.h:77
#define PNGX_DATA
Definition: png.c:54
static int rev_bshift
Definition: png.c:399
#define MAX_IMAGE_HEIGHT
Definition: png.c:236
Definition: client.h:480
static int need_color_alloc
Definition: png.c:398
#define PNGX_OUTOFMEM
Definition: png.c:53
Pixmap pixmap
Definition: xutil.c:67
static int rmask
Definition: png.c:398
static int data_start
Definition: png.c:57
uint8 width
Definition: x11.h:38
unsigned char uint8
Definition: client-types.h:81
#define RATIO
Definition: png.c:233
Colormap colormap
Definition: x11.c:190
void get_map_image_size(int face, uint8 *w, uint8 *h)
Definition: png.c:929
static int data_len
Definition: png.c:57
#define MAX_IMAGE_WIDTH
Definition: png.c:235
static int colors_alloced
Definition: png.c:400
int image_size
Definition: gx11.c:116
Window win_game
Definition: x11.c:189
const char * rcsid_x11_png_c
Definition: png.c:1