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

# Content
1 /*
2 * static char *rcsid_png_c =
3 * "$Id$";
4 */
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 static long pngx_find_color(Display *display, Colormap *cmap, int red, int green, int blue)
77 {
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