ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/crossedit/png.c
Revision: 1.3
Committed: Sun Aug 13 17:16:01 2006 UTC (17 years, 9 months ago) by elmex
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +0 -0 lines
State: FILE REMOVED
Log Message:
Made server compile with C++.
Removed cfanim plugin and crossedit.
C++ here we come.

File Contents

# User Rev Content
1 root 1.1 /*
2     * static char *rcsid_png_c =
3 pippijn 1.2 * "$Id$";
4 root 1.1 */
5     /*
6     Crossfire client, a client program for the crossfire program.
7    
8     Copyright (C) 2002 Mark Wedel & Crossfire Development Team
9    
10     This program is free software; you can redistribute it and/or modify
11     it under the terms of the GNU General Public License as published by
12     the Free Software Foundation; either version 2 of the License, or
13     (at your option) any later version.
14    
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18     GNU General Public License for more details.
19    
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23    
24     The authors can be reached via e-mail at crossfire-devel@real-time.com
25     */
26    
27     /* This is a light weight png -> xpixmap function. Most of the code is from
28     * the example png documentation.
29     * I wrote this because I could not find a simple function that did this -
30     * most all libraries out there tended to do a lot more than I needed for
31     * crossfire - in addition, imLib actually has bugs which prevents it from
32     * rendering some images properly.
33     *
34     * This function is far from complete, but does the job and removes the need
35     * for yet another library.
36     */
37    
38     #include <stdlib.h>
39     #include <sys/stat.h>
40     #include <unistd.h>
41     #include <png.h>
42     #include <X11/Xlib.h>
43     #include <X11/Xutil.h>
44    
45    
46     /* Defines for PNG return values */
47     /* These should be in a header file, but currently our calling functions
48     * routines just check for nonzero return status and don't really care
49     * why the load failed.
50     */
51     #define PNGX_NOFILE 1
52     #define PNGX_OUTOFMEM 2
53     #define PNGX_DATA 3
54    
55     static char *data_cp;
56     static int data_len, data_start;
57     static XImage *ximage;
58     static int rmask=0, bmask=0,gmask=0,need_color_alloc=0, rshift=16, bshift=0, gshift=8,
59     rev_rshift=0, rev_gshift=0, rev_bshift=0;
60     static int colors_alloced=0, private_cmap=0, colormap_size;
61     struct Pngx_Color_Values {
62     unsigned char red, green, blue;
63     long pixel_value;
64     } *color_values;
65    
66     #define COLOR_FACTOR 3
67     #define BRIGHTNESS_FACTOR 1
68    
69     /* This function is used to find the pixel and return it
70     * to the caller. We store what pixels we have already allocated
71     * and try to find a match against that. The reason for this is that
72     * XAllocColor is a very slow routine. Before my optimizations,
73     * png loading took about 140 seconds, of which 60 seconds of that
74     * was in XAllocColor calls.
75     */
76 pippijn 1.2 static long pngx_find_color(Display *display, Colormap *cmap, int red, int green, int blue)
77 root 1.1 {
78    
79     int i, closeness=0xffffff, close_entry=-1, tmpclose;
80     XColor scolor;
81    
82     for (i=0; i<colors_alloced; i++) {
83     if ((color_values[i].red == red) && (color_values[i].green == green) &&
84     (color_values[i].blue == blue)) return color_values[i].pixel_value;
85    
86     tmpclose = COLOR_FACTOR * (abs(red - color_values[i].red) +
87     abs(green - color_values[i].green) +
88     abs(blue - color_values[i].blue)) +
89     BRIGHTNESS_FACTOR * abs((red + green + blue) -
90     (color_values[i].red + color_values[i].green + color_values[i].blue));
91    
92     /* I already know that 8 bit is not enough to hold all the PNG colors,
93     * so lets do some early optimization
94     */
95     if (tmpclose < 3) return color_values[i].pixel_value;
96     if (tmpclose < closeness) {
97     closeness = tmpclose;
98     close_entry = i;
99     }
100     }
101    
102     /* If the colormap is full, no reason to do anything more */
103     if (colors_alloced == colormap_size)
104     return color_values[close_entry].pixel_value;
105    
106    
107     /* If we get here, we haven't cached the color */
108    
109     scolor.red = (red << 8) + red;
110     scolor.green = (green << 8) + green;
111     scolor.blue = (blue << 8) + blue;
112    
113    
114     again:
115     if (!XAllocColor(display, *cmap, &scolor)) {
116     if (!private_cmap) {
117     fprintf(stderr,"Going to private colormap after %d allocs\n", colors_alloced);
118     *cmap = XCopyColormapAndFree(display, *cmap);
119     private_cmap=1;
120     goto again;
121     }
122     else {
123     #if 0
124     fprintf(stderr,"Unable to allocate color %d %d %d, %d colors alloced, will use closeness value %d\n",
125     red, green, blue, colors_alloced, closeness);
126     #endif
127     colors_alloced = colormap_size; /* Colormap is exhausted */
128     return color_values[close_entry].pixel_value;
129     }
130     }
131     color_values[colors_alloced].red = red;
132     color_values[colors_alloced].green = green;
133     color_values[colors_alloced].blue = blue;
134     color_values[colors_alloced].pixel_value= scolor.pixel;
135     colors_alloced++;
136     return scolor.pixel;
137     }
138    
139    
140    
141     static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
142     memcpy(data, data_cp + data_start, length);
143     data_start += length;
144     }
145    
146     int init_pngx_loader(Display *display)
147     {
148     int pad,depth;
149     XVisualInfo xvinfo, *xvret;
150     Visual *visual;
151    
152     depth = DefaultDepth(display, DefaultScreen(display));
153     visual = DefaultVisual(display, DefaultScreen(display));
154     xvinfo.visualid = XVisualIDFromVisual(visual);
155     xvret = XGetVisualInfo(display, VisualIDMask, &xvinfo, &pad);
156     if (pad != 1) {
157     fprintf(stderr,"XGetVisual found %d matching visuals?\n", pad);
158     return 1;
159     }
160     rmask = xvret -> red_mask;
161     gmask = xvret -> green_mask;
162     bmask = xvret -> blue_mask;
163     /* We need to figure out how many bits to shift. Thats what this
164     * following block of code does. We can't presume to use just
165     * 16, 8, 0 bits for RGB respectively, as if you are on 16 bit,
166     * that is not correct. There may be a much easier way to do this -
167     * it is just bit manipulation. Note that we want to preserver
168     * the most significant bits, so these shift values can very
169     * well be negative, in which case we need to know that -
170     * the shift operators don't work with negative values.
171     * An example is 5 bits for blue - in that case, we really
172     * want to shfit right (>>) by 3 bits.
173     */
174     rshift=0;
175     if (rmask) {
176     while (!((1 << rshift) & rmask)) rshift++;
177     while (((1 << rshift) & rmask)) rshift++;
178     rshift -= 8;
179     if (rshift < 0 ) {
180     rev_rshift=1;
181     rshift = -rshift;
182     }
183     }
184     gshift=0;
185     if (gmask) {
186     while (!((1 << gshift) & gmask)) gshift++;
187     while (((1 << gshift) & gmask)) gshift++;
188     gshift -= 8;
189     if (gshift < 0 ) {
190     rev_gshift=1;
191     gshift = -gshift;
192     }
193     }
194     bshift=0;
195     if (bmask) {
196     while (!((1 << bshift) & bmask)) bshift++;
197     while (((1 << bshift) & bmask)) bshift++;
198     bshift -= 8;
199     if (bshift < 0 ) {
200     rev_bshift=1;
201     bshift = -bshift;
202     }
203     }
204    
205    
206     if (xvret->class==PseudoColor) {
207     need_color_alloc=1;
208     if (xvret->colormap_size>256) {
209     fprintf(stderr,"One a pseudocolor visual, but colormap has %d entries?\n", xvret->colormap_size);
210     }
211     color_values=malloc(sizeof(struct Pngx_Color_Values) * xvret->colormap_size);
212     colormap_size = xvret->colormap_size-1; /* comparing # of alloced colors against this */
213     }
214     XFree(xvret);
215    
216     if (depth>16) pad = 32;
217     else if (depth > 8) pad = 16;
218     else pad = 8;
219    
220     ximage = XCreateImage(display, visual,
221     depth,
222     ZPixmap, 0, 0,
223     256, 256, pad, 0);
224     if (!ximage) {
225     fprintf(stderr,"Failed to create Ximage\n");
226     return 1;
227     }
228     ximage->data = malloc(ximage->bytes_per_line * 256);
229     if (!ximage->data) {
230     fprintf(stderr,"Failed to create Ximage data\n");
231     return 1;
232     }
233     return 0;
234     }
235    
236    
237     int png_to_xpixmap(Display *display, Drawable draw, char *data, int len,
238     Pixmap *pix, Pixmap *mask, Colormap *cmap,
239     unsigned long *width, unsigned long *height)
240     {
241     static char *pixels=NULL;
242     static int pixels_byte=0, rows_byte=0;
243     static png_bytepp rows=NULL;
244    
245     png_structp png_ptr=NULL;
246     png_infop info_ptr=NULL, end_info=NULL;
247     int bit_depth, color_type, interlace_type, compression_type, filter_type,
248     red,green,blue, lastred=-1, lastgreen=-1, lastblue=-1,alpha,bpp, x,y,
249     has_alpha=0,cmask, lastcmask=-1, lastcolor=-1;
250     GC gc, gc_alpha;
251    
252    
253     data_len=len;
254     data_cp = data;
255     data_start=0;
256     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
257     NULL, NULL, NULL);
258     if (!png_ptr) {
259     return PNGX_OUTOFMEM;
260     }
261     info_ptr = png_create_info_struct (png_ptr);
262    
263     if (!info_ptr) {
264     png_destroy_read_struct (&png_ptr, NULL, NULL);
265     return PNGX_OUTOFMEM;
266     }
267     end_info = png_create_info_struct (png_ptr);
268     if (!end_info) {
269     png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
270     return PNGX_OUTOFMEM;
271     }
272     if (setjmp (png_ptr->jmpbuf)) {
273     png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
274     return PNGX_DATA;
275     }
276    
277     png_set_read_fn(png_ptr, NULL, user_read_data);
278     png_read_info (png_ptr, info_ptr);
279    
280     png_get_IHDR(png_ptr, info_ptr, width, height, &bit_depth,
281     &color_type, &interlace_type, &compression_type, &filter_type);
282    
283     if (color_type == PNG_COLOR_TYPE_PALETTE &&
284     bit_depth <= 8) {
285    
286     /* Convert indexed images to RGB */
287     png_set_expand (png_ptr);
288    
289     } else if (color_type == PNG_COLOR_TYPE_GRAY &&
290     bit_depth < 8) {
291    
292     /* Convert grayscale to RGB */
293     png_set_expand (png_ptr);
294    
295     } else if (png_get_valid (png_ptr,
296     info_ptr, PNG_INFO_tRNS)) {
297    
298     /* If we have transparency header, convert it to alpha
299     channel */
300     png_set_expand(png_ptr);
301    
302     } else if (bit_depth < 8) {
303    
304     /* If we have < 8 scale it up to 8 */
305     png_set_expand(png_ptr);
306    
307    
308     /* Conceivably, png_set_packing() is a better idea;
309     * God only knows how libpng works
310     */
311     }
312     /* If we are 16-bit, convert to 8-bit */
313     if (bit_depth == 16) {
314     png_set_strip_16(png_ptr);
315     }
316    
317     /* If gray scale, convert to RGB */
318     if (color_type == PNG_COLOR_TYPE_GRAY ||
319     color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
320     png_set_gray_to_rgb(png_ptr);
321     }
322    
323     /* If interlaced, handle that */
324     if (interlace_type != PNG_INTERLACE_NONE) {
325     png_set_interlace_handling(png_ptr);
326     }
327    
328     /* Update the info the reflect our transformations */
329     png_read_update_info(png_ptr, info_ptr);
330     /* re-read due to transformations just made */
331     png_get_IHDR(png_ptr, info_ptr, width, height, &bit_depth,
332     &color_type, &interlace_type, &compression_type, &filter_type);
333     if (color_type & PNG_COLOR_MASK_ALPHA)
334     bpp = 4;
335     else
336     bpp = 3;
337    
338     /* Allocate the memory we need once, and increase it if necessary.
339     * This is more efficient the allocating this block of memory every time.
340     */
341     if (pixels_byte==0) {
342     pixels = (char*)malloc(*width * *height * bpp);
343     pixels_byte =*width * *height * bpp;
344     } else if ((*width * *height * bpp) > pixels_byte) {
345     pixels=realloc(pixels, *width * *height * bpp);
346     pixels_byte =*width * *height * bpp;
347     }
348    
349     if (!pixels) {
350     pixels_byte=0;
351     png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
352     return PNGX_OUTOFMEM;
353     }
354     if (rows_byte == 0) {
355     rows =(png_bytepp) malloc(sizeof(char*) * *height);
356     rows_byte=*height;
357     } else if (*height > rows_byte) {
358     rows =(png_bytepp) realloc(rows, sizeof(char*) * *height);
359     rows_byte=*height;
360     }
361     if (!rows) {
362     pixels_byte=0;
363     png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
364     return PNGX_OUTOFMEM;
365     }
366    
367     for (y=0; y<*height; y++)
368     rows[y] = pixels + y * *width * bpp;
369    
370     png_read_image(png_ptr, rows);
371     #if 0
372     fprintf(stderr,"image is %d X %d, bpp=%d, color_type=%d\n",
373     *width, *height, bpp, color_type);
374     #endif
375    
376     *pix = XCreatePixmap(display, draw, *width, *height,
377     DefaultDepth(display, DefaultScreen(display)));
378    
379     gc=XCreateGC(display, *pix, 0, NULL);
380     XSetFunction(display, gc, GXcopy);
381     XSetPlaneMask(display, gc, AllPlanes);
382    
383     if (color_type & PNG_COLOR_MASK_ALPHA) {
384     /* The foreground/background colors are not really
385     * colors, but rather values to set in the mask.
386     * The values used below work properly on at least
387     * 8 bit and 16 bit display - using things like
388     * blackpixel & whitepixel does NO work on
389     * both types of display.
390     */
391     *mask=XCreatePixmap(display ,draw, *width, *height,1);
392     gc_alpha=XCreateGC(display, *mask, 0, NULL);
393     XSetFunction(display, gc_alpha, GXcopy);
394     XSetPlaneMask(display, gc_alpha, AllPlanes);
395     XSetForeground(display, gc_alpha, 1);
396     XFillRectangle(display, *mask, gc_alpha, 0, 0, *width, *height);
397     XSetForeground(display, gc_alpha, 0);
398     has_alpha=1;
399     }
400     else {
401     *mask = None;
402     gc_alpha = None; /* Prevent compile warnings */
403     }
404    
405     for (y=0; y<*height; y++) {
406     for (x=0; x<*width; x++) {
407     red=rows[y][x*bpp];
408     green=rows[y][x*bpp+1];
409     blue=rows[y][x*bpp+2];
410     if (has_alpha) {
411     alpha = rows[y][x*bpp+3];
412     /* Transparent bit */
413     if (alpha==0) {
414     XDrawPoint(display, *mask, gc_alpha, x, y);
415     }
416     }
417     if (need_color_alloc) {
418     /* We only use cmask to avoid calling pngx_find_color repeatedly.
419     * when the color has not changed from the last pixel.
420     */
421     if ((lastred != red) && (lastgreen != green) && (lastblue != blue)) {
422     lastcolor = pngx_find_color(display, cmap, red, green, blue);
423     lastcmask = cmask;
424     }
425     XPutPixel(ximage, x, y, lastcolor);
426     } else {
427     if ((lastred != red) && (lastgreen != green) && (lastblue != blue)) {
428     if (rev_rshift) red >>= rshift;
429     else red <<= rshift;
430     if (rev_gshift) green >>= gshift;
431     else green <<= gshift;
432     if (rev_bshift) blue >>= bshift;
433     else blue <<= bshift;
434    
435     cmask = (red & rmask) | (green & gmask) | (blue & bmask);
436     }
437     XPutPixel(ximage, x, y, cmask);
438     }
439     }
440     }
441    
442     XPutImage(display, *pix, gc, ximage, 0, 0, 0, 0, *width, *height);
443     if (has_alpha)
444     XFreeGC(display, gc_alpha);
445     XFreeGC(display, gc);
446     png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
447     return 0;
448     }
449     #if 0
450     int main(int argc, char *argv[])
451     {
452     Display *disp;
453     char data[256],buf[1024*1024];
454     int fd,i,z, alpha;
455     unsigned long width, height;
456     Window window;
457     XSetWindowAttributes wattrs;
458     Pixmap pix, mask;
459     GC gc;
460    
461     if (argc!=2) {
462     fprintf(stderr,"Usage: %s <filename>\n", argv[0]);
463     exit(1);
464     }
465    
466     if (!(disp=XOpenDisplay(NULL))) {
467     fprintf(stderr,"Unable to open display\n");
468     exit(1);
469     }
470    
471     wattrs.backing_store = WhenMapped;
472     wattrs.background_pixel = WhitePixel(disp, DefaultScreen(disp));
473    
474     window=XCreateWindow(disp, DefaultRootWindow(disp), 0, 0,
475     32, 32, 0, CopyFromParent, InputOutput, CopyFromParent,
476     CWBackingStore|CWBackPixel, &wattrs);
477    
478     i = open(argv[1], O_RDONLY);
479     z=read(i, buf, 1024*1024);
480     close(i);
481     fprintf(stderr,"Read %d bytes from %s\n", z, argv[1]);
482     png_to_xpixmap(disp, window, buf, z, &pix, &mask, DefaultColormap(disp,
483     DefaultScreen(disp)), &width, &height);
484     XResizeWindow(disp, window, width, height);
485     if (!window) {
486     fprintf(stderr,"Unable to create window\n");
487     exit(1);
488     }
489     XMapWindow(disp, window);
490     XFlush(disp);
491     gc=XCreateGC(disp, pix, 0, NULL);
492     if (mask)
493     XSetClipMask(disp, gc, mask);
494    
495     /* A simple way to display the image without needing to worry
496     * about exposures and redraws.
497     */
498     for (i=0; i<30; i++) {
499     XCopyArea(disp, pix, window, gc, 0, 0, width, height, 0 , 0);
500     XFlush(disp);
501     sleep(1);
502     }
503    
504     exit(0);
505     }
506    
507    
508     #endif