Crossfire Client, Branch  R11627
png.c
Go to the documentation of this file.
00001 const char *rcsid_x11_png_c =
00002     "$Id: png.c 9195 2008-06-01 15:36:42Z anmaster $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2001 Mark Wedel & Crossfire Development Team
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 
00022     The author can be reached via e-mail to crossfire-devel@real-time.com
00023 */
00024 
00025 /* This is a light weight png -> xpixmap function.  Most of the code is from
00026  * the example png documentation.
00027  * I wrote this because I could not find a simple function that did this -
00028  * most all libraries out there tended to do a lot more than I needed for
00029  * crossfire - in addition, imLib actually has bugs which prevents it from
00030  * rendering some images properly.
00031  *
00032  * This function is far from complete, but does the job and removes the need
00033  * for yet another library.
00034  */
00035 
00036 #include <config.h>
00037 #include <stdlib.h>
00038 #include <sys/stat.h>
00039 #include <unistd.h>
00040 #include <png.h>
00041 #include <X11/Xlib.h>
00042 #include <X11/Xutil.h>
00043 #include "client-types.h"
00044 #include "client.h"
00045 #include "x11.h"
00046 
00047 /* Defines for PNG return values */
00048 /* These should be in a header file, but currently our calling functions
00049  * routines just check for nonzero return status and don't really care
00050  * why the load failed.
00051  */
00052 #define PNGX_NOFILE     1
00053 #define PNGX_OUTOFMEM   2
00054 #define PNGX_DATA       3
00055 
00056 static unsigned char *data_cp;
00057 static int data_len, data_start;
00058 
00059 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
00060     memcpy(data, data_cp + data_start, length);
00061     data_start += length;
00062 }
00063 
00064 
00065 uint8 *png_to_data(uint8 *data, int len, uint32 *width, uint32 *height)
00066 {
00067     uint8 *pixels=NULL;
00068     static png_bytepp   rows=NULL;
00069     static int rows_byte=0;
00070 
00071     png_structp png_ptr;
00072     png_infop   info_ptr;
00073     int bit_depth, color_type, interlace_type, compression_type, y;
00074 
00075     data_len=len;
00076     data_cp = data;
00077     data_start=0;
00078 
00079     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
00080                                      NULL, NULL, NULL);
00081 
00082     if (!png_ptr) {
00083         return NULL;
00084     }
00085     info_ptr = png_create_info_struct (png_ptr);
00086 
00087     if (!info_ptr) {
00088         png_destroy_read_struct (&png_ptr, NULL, NULL);
00089         return NULL;
00090     }
00091     if (setjmp (png_ptr->jmpbuf)) {
00092         png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
00093         return NULL;
00094     }
00095 
00096     png_set_read_fn(png_ptr, NULL, user_read_data);
00097     png_read_info (png_ptr, info_ptr);
00098 
00099     /* Breaking these out instead of using png_get_IHDR fixes bug
00100      * 1249877 - problems on 64 bit systems (amd64 at least)
00101      */
00102     *width = png_get_image_width(png_ptr, info_ptr);
00103     *height = png_get_image_height(png_ptr, info_ptr);
00104     bit_depth = png_get_bit_depth(png_ptr, info_ptr);
00105     color_type = png_get_color_type(png_ptr, info_ptr);
00106     interlace_type = png_get_interlace_type(png_ptr, info_ptr);
00107     compression_type = png_get_compression_type(png_ptr, info_ptr);
00108 
00109     if (color_type == PNG_COLOR_TYPE_PALETTE &&
00110             bit_depth <= 8) {
00111 
00112                 /* Convert indexed images to RGB */
00113                 png_set_expand (png_ptr);
00114 
00115     } else if (color_type == PNG_COLOR_TYPE_GRAY &&
00116                    bit_depth < 8) {
00117 
00118                 /* Convert grayscale to RGB */
00119                 png_set_expand (png_ptr);
00120 
00121     } else if (png_get_valid (png_ptr,
00122                                   info_ptr, PNG_INFO_tRNS)) {
00123 
00124                 /* If we have transparency header, convert it to alpha
00125                    channel */
00126                 png_set_expand(png_ptr);
00127 
00128     } else if (bit_depth < 8) {
00129 
00130                 /* If we have < 8 scale it up to 8 */
00131                 png_set_expand(png_ptr);
00132 
00133 
00134                 /* Conceivably, png_set_packing() is a better idea;
00135                  * God only knows how libpng works
00136                  */
00137     }
00138         /* If we are 16-bit, convert to 8-bit */
00139     if (bit_depth == 16) {
00140                 png_set_strip_16(png_ptr);
00141     }
00142 
00143         /* If gray scale, convert to RGB */
00144     if (color_type == PNG_COLOR_TYPE_GRAY ||
00145             color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
00146                 png_set_gray_to_rgb(png_ptr);
00147     }
00148 
00149         /* If interlaced, handle that */
00150     if (interlace_type != PNG_INTERLACE_NONE) {
00151                 png_set_interlace_handling(png_ptr);
00152     }
00153 
00154     /* pad it to 4 bytes to make processing easier */
00155     if (!(color_type & PNG_COLOR_MASK_ALPHA))
00156         png_set_filler(png_ptr, 255, PNG_FILLER_AFTER);
00157 
00158     /* Update the info the reflect our transformations */
00159     png_read_update_info(png_ptr, info_ptr);
00160 
00161     /* re-read due to transformations just made */
00162     /* Breaking these out instead of using png_get_IHDR fixes bug
00163      * 1249877 - problems on 64 bit systems (amd64 at least)
00164      */
00165     *width = png_get_image_width(png_ptr, info_ptr);
00166     *height = png_get_image_height(png_ptr, info_ptr);
00167     bit_depth = png_get_bit_depth(png_ptr, info_ptr);
00168     color_type = png_get_color_type(png_ptr, info_ptr);
00169     interlace_type = png_get_interlace_type(png_ptr, info_ptr);
00170     compression_type = png_get_compression_type(png_ptr, info_ptr);
00171 
00172     pixels = (uint8*)malloc(*width * *height * 4);
00173 
00174     if (!pixels) {
00175         png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
00176         fprintf(stderr,"Out of memory - exiting\n");
00177         exit(1);
00178     }
00179 
00180     /* the png library needs the rows, but we will just return the raw data */
00181     if (rows_byte == 0) {
00182         rows =(png_bytepp) malloc(sizeof(char*) * *height);
00183         rows_byte=*height;
00184     } else if (*height > rows_byte) {
00185         rows =(png_bytepp) realloc(rows, sizeof(char*) * *height);
00186         rows_byte=*height;
00187     }
00188     if (!rows) {
00189         png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
00190         return NULL;
00191     }
00192 
00193     for (y=0; y<*height; y++)
00194         rows[y] = pixels + y * *width * 4;
00195 
00196     png_read_image(png_ptr, rows);
00197     png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
00198 
00199     return pixels;
00200 }
00201 
00202 
00203 /* rescale_png_image takes png data and scales it accordingly.
00204  * This function is based on pnmscale, but has been modified to support alpha
00205  * channel - instead of blending the alpha channel, it takes the most opaque
00206  * value - blending it is not likely to give sane results IMO - for any image
00207  * that has transparent information, if we blended the alpha, the result would
00208  * be the edges of that region being partially transparent.
00209  * This function has also been re-written to use more static data - in the
00210  * case of the client, it will be called thousands of times, so it doesn't make
00211  * sense to free the data and then re-allocate it.
00212  *
00213  * For pixels that are fully transparent, the end result after scaling is they
00214  * will be tranparent black.  This is a needed effect for blending to work properly.
00215  *
00216  * This function returns a new pointer to the scaled image data.  This is
00217  * malloc'd data, so should be freed at some point to prevent leaks.
00218  * This function does not modify the data passed to it - the caller is responsible
00219  * for freeing it if it is no longer needed.
00220  *
00221  * function arguments:
00222  * data: PNG data - really, this is any 4 byte per pixel data, in RGBA format.
00223  * *width, *height: The source width and height.  These values are modified
00224  *   to contain the new image size.
00225  * scale: percentage size that new image should be.  100 is a same size
00226  *    image - values larger than 100 will result in zoom, values less than
00227  *    100 will result in a shrinkage.
00228  */
00229 
00230 /* RATIO is used to know what units scale is - in this case, a percentage, so
00231  * it is set to 100
00232  */
00233 #define RATIO   100
00234 
00235 #define MAX_IMAGE_WIDTH         1024
00236 #define MAX_IMAGE_HEIGHT        1024
00237 #define BPP 4
00238 
00239 uint8 *rescale_rgba_data(uint8 *data, int *width, int *height, int scale)
00240 {
00241     static int xrow[BPP * MAX_IMAGE_WIDTH], yrow[BPP*MAX_IMAGE_HEIGHT];
00242     static uint8 *nrows[MAX_IMAGE_HEIGHT];
00243 
00244     /* Figure out new height/width */
00245     int new_width = *width  * scale / RATIO, new_height = *height * scale / RATIO;
00246 
00247     int sourcerow=0, ytoleft, ytofill, xtoleft, xtofill, dest_column=0, source_column=0, needcol,
00248         destrow=0;
00249     int x,y;
00250     uint8 *ndata;
00251     uint8 r,g,b,a;
00252 
00253     if (*width > MAX_IMAGE_WIDTH || new_width > MAX_IMAGE_WIDTH
00254     || *height > MAX_IMAGE_HEIGHT || new_height > MAX_IMAGE_HEIGHT)
00255     {
00256         fprintf(stderr, "Image too big\n");
00257         exit(0);
00258     }
00259 
00260     /* clear old values these may have */
00261     memset(yrow, 0, sizeof(int) * *height * BPP);
00262 
00263     ndata = (uint8*)malloc(new_width * new_height * BPP);
00264 
00265     for (y=0; y<new_height; y++)
00266         nrows[y] = (png_bytep) (ndata + y * new_width * BPP);
00267 
00268     ytoleft = scale;
00269     ytofill = RATIO;
00270 
00271     for (y=0,sourcerow=0; y < new_height; y++) {
00272         memset(xrow, 0, sizeof(int) * *width * BPP);
00273         while (ytoleft < ytofill) {
00274             for (x=0; x< *width; ++x) {
00275                 /* Only want to copy the data if this is not a transperent pixel.
00276                  * If it is transparent, the color information is has is probably
00277                  * bogus, and blending that makes the results look worse.
00278                  */
00279                 if (data[(sourcerow * *width + x)*BPP+3] > 0 ) {
00280                     yrow[x*BPP] += ytoleft * data[(sourcerow * *width + x)*BPP]/RATIO;
00281                     yrow[x*BPP+1] += ytoleft * data[(sourcerow * *width + x)*BPP+1]/RATIO;
00282                     yrow[x*BPP+2] += ytoleft * data[(sourcerow * *width + x)*BPP+2]/RATIO;
00283                 }
00284                 /* Alpha is a bit special - we don't want to blend it -
00285                  * we want to take whatever is the more opaque value.
00286                  */
00287                 if (data[(sourcerow * *width + x)*BPP+3] > yrow[x*BPP+3])
00288                     yrow[x*BPP+3] = data[(sourcerow * *width + x)*BPP+3];
00289             }
00290             ytofill -= ytoleft;
00291             ytoleft = scale;
00292             if (sourcerow < *height)
00293                 sourcerow++;
00294         }
00295 
00296         for (x=0; x < *width; ++x) {
00297             if (data[(sourcerow * *width + x)*BPP+3] > 0 ) {
00298                 xrow[x*BPP] = yrow[x*BPP] + ytofill * data[(sourcerow * *width + x)*BPP] / RATIO;
00299                 xrow[x*BPP+1] = yrow[x*BPP+1] + ytofill * data[(sourcerow * *width + x)*BPP+1] / RATIO;
00300                 xrow[x*BPP+2] = yrow[x*BPP+2] + ytofill * data[(sourcerow * *width + x)*BPP+2] / RATIO;
00301             }
00302             if (data[(sourcerow * *width + x)*BPP+3] > xrow[x*BPP+3])
00303                 xrow[x*BPP+3] = data[(sourcerow * *width + x)*BPP+3];
00304             yrow[x*BPP]=0; yrow[x*BPP+1]=0; yrow[x*BPP+2]=0; yrow[x*BPP+3]=0;
00305         }
00306 
00307         ytoleft -= ytofill;
00308         if (ytoleft <= 0) {
00309             ytoleft = scale;
00310             if (sourcerow < *height)
00311                 sourcerow++;
00312         }
00313 
00314         ytofill = RATIO;
00315         xtofill = RATIO;
00316         dest_column = 0;
00317         source_column=0;
00318         needcol=0;
00319         r=0; g=0; b=0; a=0;
00320 
00321         for (x=0; x< *width; x++) {
00322             xtoleft = scale;
00323 
00324             while (xtoleft >= xtofill) {
00325                 if (needcol) {
00326                     dest_column++;
00327                     r=0; g=0; b=0; a=0;
00328                 }
00329 
00330                 if (xrow[source_column*BPP+3] > 0) {
00331                     r += xtofill * xrow[source_column*BPP] / RATIO;
00332                     g += xtofill * xrow[1+source_column*BPP] / RATIO;
00333                     b += xtofill * xrow[2+source_column*BPP] / RATIO;
00334                 }
00335                 if (xrow[3+source_column*BPP] > a)
00336                     a = xrow[3+source_column*BPP];
00337 
00338                 nrows[destrow][dest_column * BPP] = r;
00339                 nrows[destrow][1+dest_column * BPP] = g;
00340                 nrows[destrow][2+dest_column * BPP] = b;
00341                 nrows[destrow][3+dest_column * BPP] = a;
00342                 xtoleft -= xtofill;
00343                 xtofill = RATIO;
00344                 needcol=1;
00345             }
00346 
00347             if (xtoleft > 0 ){
00348                 if (needcol) {
00349                     dest_column++;
00350                     r=0; g=0; b=0; a=0;
00351                     needcol=0;
00352                 }
00353 
00354                 if (xrow[3+source_column*BPP] > 0) {
00355                     r += xtoleft * xrow[source_column*BPP] / RATIO;
00356                     g += xtoleft * xrow[1+source_column*BPP] / RATIO;
00357                     b += xtoleft * xrow[2+source_column*BPP] / RATIO;
00358                 }
00359                 if (xrow[3+source_column*BPP] > a)
00360                     a = xrow[3+source_column*BPP];
00361 
00362                 xtofill -= xtoleft;
00363             }
00364             source_column++;
00365         }
00366 
00367         if (xtofill > 0 ) {
00368             source_column--;
00369             if (xrow[3+source_column*BPP] > 0) {
00370                 r += xtofill * xrow[source_column*BPP] / RATIO;
00371                 g += xtofill * xrow[1+source_column*BPP] / RATIO;
00372                 b += xtofill * xrow[2+source_column*BPP] / RATIO;
00373             }
00374             if (xrow[3+source_column*BPP] > a)
00375                 a = xrow[3+source_column*BPP];
00376         }
00377 
00378         /* Not positve, but without the bound checking for dest_column,
00379          * we were overrunning the buffer.  My guess is this only really
00380          * showed up if when the images are being scaled - there is probably
00381          * something like half a pixel left over.
00382          */
00383         if (!needcol && (dest_column < new_width)) {
00384             nrows[destrow][dest_column * BPP] = r;
00385             nrows[destrow][1+dest_column * BPP] = g;
00386             nrows[destrow][2+dest_column * BPP] = b;
00387             nrows[destrow][3+dest_column * BPP] = a;
00388         }
00389         destrow++;
00390     }
00391     *width = new_width;
00392     *height = new_height;
00393     return ndata;
00394 }
00395 
00396 
00397 static XImage   *ximage;
00398 static int rmask=0, bmask=0,gmask=0,need_color_alloc=0, rshift=16, bshift=0, gshift=8,
00399     rev_rshift=0, rev_gshift=0, rev_bshift=0;
00400 static int colors_alloced=0, private_cmap=0, colormap_size;
00401 struct Pngx_Color_Values {
00402     unsigned char   red, green, blue;
00403     long    pixel_value;
00404 } *color_values;
00405 
00406 #define COLOR_FACTOR       3
00407 #define BRIGHTNESS_FACTOR  1
00408 
00409 /* This function is used to find the pixel and return it
00410  * to the caller.  We store what pixels we have already allocated
00411  * and try to find a match against that.  The reason for this is that
00412  * XAllocColor is a very slow routine. Before my optimizations,
00413  * png loading took about 140 seconds, of which 60 seconds of that
00414  * was in XAllocColor calls.
00415  */
00416 long pngx_find_color(Display *display, Colormap *cmap, int red, int green, int blue)
00417 {
00418 
00419     int i, closeness=0xffffff, close_entry=-1, tmpclose;
00420     XColor  scolor;
00421 
00422     for (i=0; i<colors_alloced; i++) {
00423         if ((color_values[i].red == red) && (color_values[i].green == green) &&
00424             (color_values[i].blue == blue)) return color_values[i].pixel_value;
00425 
00426         tmpclose = COLOR_FACTOR * (abs(red - color_values[i].red) +
00427                                    abs(green - color_values[i].green) +
00428                                    abs(blue - color_values[i].blue)) +
00429                     BRIGHTNESS_FACTOR * abs((red + green + blue) -
00430                                 (color_values[i].red + color_values[i].green + color_values[i].blue));
00431 
00432         /* I already know that 8 bit is not enough to hold all the PNG colors,
00433          * so lets do some early optimization
00434          */
00435         if (tmpclose < 3) return color_values[i].pixel_value;
00436         if (tmpclose < closeness) {
00437             closeness = tmpclose;
00438             close_entry = i;
00439         }
00440     }
00441 
00442     /* If the colormap is full, no reason to do anything more */
00443     if (colors_alloced == colormap_size)
00444         return color_values[close_entry].pixel_value;
00445 
00446 
00447     /* If we get here, we haven't cached the color */
00448 
00449     scolor.red = (red << 8) + red;
00450     scolor.green = (green << 8) + green;
00451     scolor.blue = (blue << 8) + blue;
00452 
00453 
00454 again:
00455     if (!XAllocColor(display, *cmap, &scolor)) {
00456         if (!private_cmap) {
00457             fprintf(stderr,"Going to private colormap after %d allocs\n", colors_alloced);
00458             *cmap = XCopyColormapAndFree(display, *cmap);
00459             private_cmap=1;
00460             goto again;
00461         }
00462         else {
00463 #if 0
00464             fprintf(stderr,"Unable to allocate color %d %d %d, %d colors alloced, will use closenss value %d\n",
00465                     red, green, blue, colors_alloced, closeness);
00466 #endif
00467             colors_alloced = colormap_size;     /* Colormap is exhausted */
00468             return color_values[close_entry].pixel_value;
00469         }
00470     }
00471     color_values[colors_alloced].red = red;
00472     color_values[colors_alloced].green = green;
00473     color_values[colors_alloced].blue = blue;
00474     color_values[colors_alloced].pixel_value= scolor.pixel;
00475     colors_alloced++;
00476     return scolor.pixel;
00477 }
00478 
00479 
00480 
00481 
00482 int init_pngx_loader(Display *display)
00483 {
00484     int pad,depth;
00485     XVisualInfo xvinfo, *xvret;
00486     Visual *visual;
00487 
00488     depth = DefaultDepth(display, DefaultScreen(display));
00489     visual = DefaultVisual(display, DefaultScreen(display));
00490     xvinfo.visualid = XVisualIDFromVisual(visual);
00491     xvret = XGetVisualInfo(display, VisualIDMask, &xvinfo, &pad);
00492     if (pad != 1) {
00493         fprintf(stderr,"XGetVisual found %d matching visuals?\n", pad);
00494         return 1;
00495     }
00496     rmask = xvret -> red_mask;
00497     gmask = xvret -> green_mask;
00498     bmask = xvret -> blue_mask;
00499     /* We need to figure out how many bits to shift.  Thats what this
00500      * following block of code does.  We can't presume to use just
00501      * 16, 8, 0 bits for RGB respectively, as if you are on 16 bit,
00502      * that is not correct.  There may be a much easier way to do this -
00503      * it is just bit manipulation.  Note that we want to preserver
00504      * the most significant bits, so these shift values can very
00505      * well be negative, in which case we need to know that -
00506      * the shift operators don't work with negative values.
00507      * An example is 5 bits for blue - in that case, we really
00508      * want to shfit right (>>) by 3 bits.
00509      */
00510     rshift=0;
00511     if (rmask) {
00512         while (!((1 << rshift) & rmask)) rshift++;
00513         while (((1 << rshift) & rmask)) rshift++;
00514         rshift -= 8;
00515         if (rshift < 0 ) {
00516             rev_rshift=1;
00517             rshift = -rshift;
00518         }
00519     }
00520     gshift=0;
00521     if (gmask) {
00522         while (!((1 << gshift) & gmask)) gshift++;
00523         while (((1 << gshift) & gmask)) gshift++;
00524         gshift -= 8;
00525         if (gshift < 0 ) {
00526             rev_gshift=1;
00527             gshift = -gshift;
00528         }
00529     }
00530     bshift=0;
00531     if (bmask) {
00532         while (!((1 << bshift) & bmask)) bshift++;
00533         while (((1 << bshift) & bmask)) bshift++;
00534         bshift -= 8;
00535         if (bshift < 0 ) {
00536             rev_bshift=1;
00537             bshift = -bshift;
00538         }
00539     }
00540 
00541 
00542     if (xvret->class==PseudoColor) {
00543         need_color_alloc=1;
00544         if (xvret->colormap_size>256) {
00545             fprintf(stderr,"One a pseudocolor visual, but colormap has %d entries?\n", xvret->colormap_size);
00546         }
00547         color_values=malloc(sizeof(struct Pngx_Color_Values) * xvret->colormap_size);
00548         colormap_size = xvret->colormap_size-1; /* comparing # of alloced colors against this */
00549     }
00550     XFree(xvret);
00551 
00552     if (depth>16) pad = 32;
00553     else if (depth > 8) pad = 16;
00554     else pad = 8;
00555 
00556     ximage = XCreateImage(display, visual,
00557                       depth,
00558                       ZPixmap, 0, 0,
00559                       MAX_IMAGE_SIZE, MAX_IMAGE_SIZE,  pad, 0);
00560     if (!ximage) {
00561         fprintf(stderr,"Failed to create Ximage\n");
00562         return 1;
00563     }
00564     ximage->data = malloc(ximage->bytes_per_line * MAX_IMAGE_SIZE);
00565     if (!ximage->data) {
00566         fprintf(stderr,"Failed to create Ximage data\n");
00567         return 1;
00568     }
00569     return 0;
00570 }
00571 
00572 
00573 int png_to_xpixmap(Display *display, Drawable draw, unsigned char *data, int len,
00574                    Pixmap *pix, Pixmap *mask, Colormap *cmap,
00575                    unsigned long *width, unsigned long *height)
00576 {
00577     static uint8 *pixels=NULL;
00578     static int pixels_byte=0, rows_byte=0;
00579     static png_bytepp   rows=NULL;
00580 
00581     png_structp png_ptr=NULL;
00582     png_infop   info_ptr=NULL;
00583     int bit_depth, color_type, interlace_type, compression_type,
00584         red,green,blue, lastred=-1, lastgreen=-1, lastblue=-1,alpha,bpp, x,y,
00585         has_alpha, cmask, lastcmask, lastcolor;
00586     GC  gc, gc_alpha;
00587 
00588 
00589     data_len=len;
00590     data_cp = data;
00591     data_start=0;
00592     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
00593                                      NULL, NULL, NULL);
00594     if (!png_ptr) {
00595         return PNGX_OUTOFMEM;
00596     }
00597     info_ptr = png_create_info_struct (png_ptr);
00598 
00599     if (!info_ptr) {
00600         png_destroy_read_struct (&png_ptr, NULL, NULL);
00601         return PNGX_OUTOFMEM;
00602     }
00603     if (setjmp (png_ptr->jmpbuf)) {
00604         png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
00605         return PNGX_DATA;
00606     }
00607     /* put these here to prevent compiler warnings about them getting
00608      * clobbered by setjmp
00609      */
00610     has_alpha=0;
00611     cmask=-1;
00612     lastcmask=-1;
00613     lastcolor=-1;
00614 
00615     png_set_read_fn(png_ptr, NULL, user_read_data);
00616     png_read_info (png_ptr, info_ptr);
00617 
00618     /* re-read due to transformations just made */
00619     /* Breaking these out instead of using png_get_IHDR fixes bug
00620      * 1249877 - problems on 64 bit systems (amd64 at least)
00621      */
00622     *width = png_get_image_width(png_ptr, info_ptr);
00623     *height = png_get_image_height(png_ptr, info_ptr);
00624     bit_depth = png_get_bit_depth(png_ptr, info_ptr);
00625     color_type = png_get_color_type(png_ptr, info_ptr);
00626     interlace_type = png_get_interlace_type(png_ptr, info_ptr);
00627     compression_type = png_get_compression_type(png_ptr, info_ptr);
00628 
00629     if (color_type == PNG_COLOR_TYPE_PALETTE &&
00630             bit_depth <= 8) {
00631 
00632                 /* Convert indexed images to RGB */
00633                 png_set_expand (png_ptr);
00634 
00635     } else if (color_type == PNG_COLOR_TYPE_GRAY &&
00636                    bit_depth < 8) {
00637 
00638                 /* Convert grayscale to RGB */
00639                 png_set_expand (png_ptr);
00640 
00641     } else if (png_get_valid (png_ptr,
00642                                   info_ptr, PNG_INFO_tRNS)) {
00643 
00644                 /* If we have transparency header, convert it to alpha
00645                    channel */
00646                 png_set_expand(png_ptr);
00647 
00648     } else if (bit_depth < 8) {
00649 
00650                 /* If we have < 8 scale it up to 8 */
00651                 png_set_expand(png_ptr);
00652 
00653 
00654                 /* Conceivably, png_set_packing() is a better idea;
00655                  * God only knows how libpng works
00656                  */
00657     }
00658         /* If we are 16-bit, convert to 8-bit */
00659     if (bit_depth == 16) {
00660                 png_set_strip_16(png_ptr);
00661     }
00662 
00663         /* If gray scale, convert to RGB */
00664     if (color_type == PNG_COLOR_TYPE_GRAY ||
00665             color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
00666                 png_set_gray_to_rgb(png_ptr);
00667     }
00668 
00669         /* If interlaced, handle that */
00670     if (interlace_type != PNG_INTERLACE_NONE) {
00671                 png_set_interlace_handling(png_ptr);
00672     }
00673 
00674     /* Update the info the reflect our transformations */
00675     png_read_update_info(png_ptr, info_ptr);
00676     /* re-read due to transformations just made */
00677     /* re-read due to transformations just made */
00678 
00679     /* Breaking these out instead of using png_get_IHDR fixes bug
00680      * 1249877 - problems on 64 bit systems (amd64 at least)
00681      */
00682     *width = png_get_image_width(png_ptr, info_ptr);
00683     *height = png_get_image_height(png_ptr, info_ptr);
00684     bit_depth = png_get_bit_depth(png_ptr, info_ptr);
00685     color_type = png_get_color_type(png_ptr, info_ptr);
00686     interlace_type = png_get_interlace_type(png_ptr, info_ptr);
00687     compression_type = png_get_compression_type(png_ptr, info_ptr);
00688 
00689     if (color_type & PNG_COLOR_MASK_ALPHA)
00690                 bpp = 4;
00691     else
00692                 bpp = 3;
00693 
00694     /* Allocate the memory we need once, and increase it if necessary.
00695      * This is more efficient the allocating this block of memory every time.
00696      */
00697     if (pixels_byte==0) {
00698         pixels_byte =*width * *height * bpp;
00699         pixels = (uint8*)malloc(pixels_byte);
00700     } else if ((*width * *height * bpp) > pixels_byte) {
00701         pixels_byte =*width * *height * bpp;
00702         pixels=realloc(pixels, pixels_byte);
00703     }
00704 
00705     if (!pixels) {
00706         pixels_byte=0;
00707         png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
00708         return PNGX_OUTOFMEM;
00709     }
00710     if (rows_byte == 0) {
00711         rows =(png_bytepp) malloc(sizeof(char*) * *height);
00712         rows_byte=*height;
00713     } else if (*height > rows_byte) {
00714         rows =(png_bytepp) realloc(rows, sizeof(char*) * *height);
00715         rows_byte=*height;
00716     }
00717     if (!rows) {
00718         pixels_byte=0;
00719         png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
00720         return PNGX_OUTOFMEM;
00721     }
00722 
00723     for (y=0; y<*height; y++)
00724         rows[y] = pixels + y * (*width) * bpp;
00725 
00726     png_read_image(png_ptr, rows);
00727 #if 0
00728     fprintf(stderr,"image is %d X %d, bpp=%d, color_type=%d\n",
00729             *width, *height, bpp, color_type);
00730 #endif
00731 
00732     *pix = XCreatePixmap(display, draw, *width, *height,
00733                         DefaultDepth(display,  DefaultScreen(display)));
00734 
00735     gc=XCreateGC(display, *pix, 0, NULL);
00736     XSetFunction(display, gc, GXcopy);
00737     XSetPlaneMask(display, gc, AllPlanes);
00738 
00739     if (color_type & PNG_COLOR_MASK_ALPHA) {
00740         /* The foreground/background colors are not really
00741          * colors, but rather values to set in the mask.
00742          * The values used below work properly on at least
00743          * 8 bit and 16 bit display - using things like
00744          * blackpixel & whitepixel does NO work on
00745          * both types of display.
00746          */
00747         *mask=XCreatePixmap(display ,draw, *width, *height,1);
00748         gc_alpha=XCreateGC(display, *mask, 0, NULL);
00749         XSetFunction(display, gc_alpha, GXcopy);
00750         XSetPlaneMask(display, gc_alpha, AllPlanes);
00751         XSetForeground(display, gc_alpha, 1);
00752         XFillRectangle(display, *mask, gc_alpha, 0, 0, *width, *height);
00753         XSetForeground(display, gc_alpha, 0);
00754         has_alpha=1;
00755     }
00756     else {
00757         *mask = None;
00758         gc_alpha = None;    /* Prevent compile warnings */
00759     }
00760 
00761     for (y=0; y<*height; y++) {
00762         for (x=0; x<*width; x++) {
00763             red=rows[y][x*bpp];
00764             green=rows[y][x*bpp+1];
00765             blue=rows[y][x*bpp+2];
00766             if (has_alpha) {
00767                 alpha = rows[y][x*bpp+3];
00768                 /* Transparent bit */
00769                 if (alpha==0) {
00770                     XDrawPoint(display, *mask, gc_alpha, x, y);
00771                 }
00772             }
00773             if (need_color_alloc) {
00774                 /* We only use cmask to avoid calling pngx_find_color repeatedly.
00775                  * when the color has not changed from the last pixel.
00776                  */
00777                 if ((lastred != red) && (lastgreen != green) && (lastblue != blue)) {
00778                     lastcolor = pngx_find_color(display, cmap, red, green, blue);
00779                     lastcmask = cmask;
00780                 }
00781                 XPutPixel(ximage, x, y, lastcolor);
00782             } else {
00783                 if ((lastred != red) && (lastgreen != green) && (lastblue != blue)) {
00784                     if (rev_rshift) red >>= rshift;
00785                     else red <<= rshift;
00786                     if (rev_gshift) green >>= gshift;
00787                     else green <<= gshift;
00788                     if (rev_bshift) blue >>= bshift;
00789                     else blue <<= bshift;
00790 
00791                     cmask = (red & rmask) | (green  & gmask) | (blue  & bmask);
00792                 }
00793                 XPutPixel(ximage, x, y, cmask);
00794             }
00795         }
00796     }
00797 
00798     XPutImage(display, *pix, gc, ximage, 0, 0, 0, 0, 32, 32);
00799     if (has_alpha)
00800         XFreeGC(display, gc_alpha);
00801     XFreeGC(display, gc);
00802     png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
00803     return 0;
00804 }
00805 
00806 
00807 /* like png_to_xpixmap above, but the data has already been decompressed
00808  * into rgba data.
00809  */
00810 
00811 int rgba_to_xpixmap(Display *display, Drawable draw, uint8 *pixels,
00812                    Pixmap *pix, Pixmap *mask, Colormap *cmap,
00813                    unsigned long width, unsigned long height)
00814 {
00815     int red,green,blue, lastred=-1, lastgreen=-1, lastblue=-1,alpha,x,y,
00816         cmask=-1, lastcmask, lastcolor=-1;
00817     GC  gc, gc_alpha;
00818 
00819     *pix = XCreatePixmap(display, draw, width, height,
00820                         DefaultDepth(display,  DefaultScreen(display)));
00821 
00822     gc=XCreateGC(display, *pix, 0, NULL);
00823     XSetFunction(display, gc, GXcopy);
00824     XSetPlaneMask(display, gc, AllPlanes);
00825 
00826     /* The foreground/background colors are not really
00827      * colors, but rather values to set in the mask.
00828      * The values used below work properly on at least
00829      * 8 bit and 16 bit display - using things like
00830      * blackpixel & whitepixel does NO work on
00831      * both types of display.
00832      */
00833 
00834     *mask=XCreatePixmap(display ,draw, width, height,1);
00835     gc_alpha=XCreateGC(display, *mask, 0, NULL);
00836     XSetFunction(display, gc_alpha, GXcopy);
00837     XSetPlaneMask(display, gc_alpha, AllPlanes);
00838     XSetForeground(display, gc_alpha, 1);
00839     XFillRectangle(display, *mask, gc_alpha, 0, 0, width, height);
00840     XSetForeground(display, gc_alpha, 0);
00841 
00842     for (y=0; y<height; y++) {
00843         for (x=0; x<width; x++) {
00844             red=    pixels[(y * width + x)*4];
00845             green=  pixels[(y * width + x)*4 + 1];
00846             blue=   pixels[(y * width + x)*4 + 2];
00847             alpha = pixels[(y * width + x)*4 + 3];
00848             if (alpha==0) {
00849                 XDrawPoint(display, *mask, gc_alpha, x, y);
00850             }
00851             if (need_color_alloc) {
00852                 /* We only use cmask to avoid calling pngx_find_color repeatedly.
00853                  * when the color has not changed from the last pixel.
00854                  */
00855                 if ((lastred != red) && (lastgreen != green) && (lastblue != blue)) {
00856                     lastcolor = pngx_find_color(display, cmap, red, green, blue);
00857                     lastcmask = cmask;
00858                 }
00859                 XPutPixel(ximage, x, y, lastcolor);
00860             } else {
00861                 if ((lastred != red) && (lastgreen != green) && (lastblue != blue)) {
00862                     if (rev_rshift) red >>= rshift;
00863                     else red <<= rshift;
00864                     if (rev_gshift) green >>= gshift;
00865                     else green <<= gshift;
00866                     if (rev_bshift) blue >>= bshift;
00867                     else blue <<= bshift;
00868 
00869                     cmask = (red & rmask) | (green  & gmask) | (blue  & bmask);
00870                 }
00871                 XPutPixel(ximage, x, y, cmask);
00872             }
00873         }
00874     }
00875 
00876     XPutImage(display, *pix, gc, ximage, 0, 0, 0, 0, width, height);
00877     XFreeGC(display, gc_alpha);
00878     XFreeGC(display, gc);
00879     return 0;
00880 }
00881 
00882 
00883 /* Takes the pixmap to put the data into, as well as the rgba
00884  * data (ie, already loaded with png_to_data).  Scales and
00885  * stores the relevant data into the pixmap structure.
00886  * returns 1 on failure.
00887  */
00888 int create_and_rescale_image_from_data(Cache_Entry *ce, int pixmap_num, uint8 *rgba_data, int width, int height)
00889 {
00890     struct PixmapInfo  *pi;
00891 
00892     if (pixmap_num <= 0 || pixmap_num >= MAXPIXMAPNUM)
00893         return 1;
00894 
00895     if (pixmaps[pixmap_num] != pixmaps[0]) {
00896         XFreePixmap(display, pixmaps[pixmap_num]->pixmap);
00897         if (pixmaps[pixmap_num]->mask)
00898             XFreePixmap(display, pixmaps[pixmap_num]->mask);
00899         free(pixmaps[pixmap_num]);
00900         pixmaps[pixmap_num] = pixmaps[0];
00901     }
00902 
00903     pi = malloc(sizeof(struct PixmapInfo));
00904     if (rgba_to_xpixmap(display, win_game, rgba_data, &pi->pixmap,
00905                    &pi->mask, &colormap, width, height) != 0) {
00906         free(pi);
00907         return 1;
00908     }
00909 
00910     if (!pi->pixmap || !pi->mask) {
00911         if (pi->pixmap)
00912             XFreePixmap(display, pi->pixmap);
00913         if (pi->mask)
00914             XFreePixmap(display, pi->mask);
00915         free(pi);
00916         return 1;
00917     }
00918 
00919     pi->width = width / image_size;
00920     pi->height = height / image_size;
00921 
00922     if (ce) {
00923         ce->image_data = pi;
00924     }
00925     pixmaps[pixmap_num] = pi;
00926     return 0;
00927 }
00928 
00929 void get_map_image_size(int face, uint8 *w, uint8 *h)
00930 {
00931     /* This function is not implemented yet, so just return default values */
00932     if (face < 0 || face >= MAXPIXMAPNUM) {
00933         *w = 1;
00934         *h = 1;
00935     }
00936     else {
00937         *w = pixmaps[face]->width;
00938         *h = pixmaps[face]->height;
00939     }
00940 }
00941 
00942 
00943 #if PNG_MAIN
00944 
00945 int main(int argc, char *argv[])
00946 {
00947     Display *disp;
00948     char    data[256],buf[1024*1024];
00949     int     fd,i,z, alpha;
00950     unsigned long  width, height;
00951     Window  window;
00952     XSetWindowAttributes    wattrs;
00953     Pixmap  pix, mask;
00954     GC      gc;
00955 
00956     if (argc!=2) {
00957         fprintf(stderr,"Usage: %s <filename>\n", argv[0]);
00958         exit(1);
00959     }
00960 
00961     if (!(disp=XOpenDisplay(NULL))) {
00962         fprintf(stderr,"Unable to open display\n");
00963         exit(1);
00964     }
00965 
00966     wattrs.backing_store = WhenMapped;
00967     wattrs.background_pixel = WhitePixel(disp, DefaultScreen(disp));
00968 
00969     window=XCreateWindow(disp, DefaultRootWindow(disp), 0, 0,
00970          32, 32, 0, CopyFromParent, InputOutput, CopyFromParent,
00971         CWBackingStore|CWBackPixel, &wattrs);
00972 
00973     i = open(argv[1], O_RDONLY);
00974     z=read(i, buf, 1024*1024);
00975     close(i);
00976     fprintf(stderr,"Read %d bytes from %s\n", z, argv[1]);
00977     png_to_xpixmap(disp, window, buf, z, &pix, &mask, DefaultColormap(disp,
00978                                 DefaultScreen(disp)), &width, &height);
00979     XResizeWindow(disp, window, width, height);
00980     if (!window) {
00981         fprintf(stderr,"Unable to create window\n");
00982         exit(1);
00983     }
00984     if (mask) XShapeCombineMask(disp,window,ShapeBounding,0,0,mask,ShapeSet);
00985     XMapWindow(disp, window);
00986     XFlush(disp);
00987     gc=XCreateGC(disp, pix, 0, NULL);
00988     if (mask)
00989         XSetClipMask(disp, gc, mask);
00990 
00991     /* A simple way to display the image without needing to worry
00992      * about exposures and redraws.
00993      */
00994     for (i=0; i<30; i++) {
00995         XCopyArea(disp, pix, window, gc, 0, 0, width, height, 0 , 0);
00996         XFlush(disp);
00997         sleep(1);
00998     }
00999 
01000     exit(0);
01001 }
01002 #endif