Crossfire Client, Branch
R11627
|
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