~ubuntu-branches/ubuntu/maverick/crossfire-client/maverick

« back to all changes in this revision

Viewing changes to x11/png.c

  • Committer: Bazaar Package Importer
  • Author(s): Kari Pahula
  • Date: 2010-06-17 21:55:04 UTC
  • mfrom: (1.2.8 upstream) (3.1.6 sid)
  • Revision ID: james.westby@ubuntu.com-20100617215504-864remyqayon6dot
Tags: 1.50.0-2
Install cfsndserv*, not cfsndserv_alsa9 to make kfreebsd land happy.

Show diffs side-by-side

added added

removed removed

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