Mercurial > fife-parpg
comparison ext/libpng-1.2.29/contrib/gregbook/rpng2-x.c @ 0:4a0efb7baf70
* Datasets becomes the new trunk and retires after that :-)
author | mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Sun, 29 Jun 2008 18:44:17 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4a0efb7baf70 |
---|---|
1 /*--------------------------------------------------------------------------- | |
2 | |
3 rpng2 - progressive-model PNG display program rpng2-x.c | |
4 | |
5 This program decodes and displays PNG files progressively, as if it were | |
6 a web browser (though the front end is only set up to read from files). | |
7 It supports gamma correction, user-specified background colors, and user- | |
8 specified background patterns (for transparent images). This version is | |
9 for the X Window System (tested by the author under Unix and by Martin | |
10 Zinser under OpenVMS; may work under OS/2 with a little tweaking). | |
11 | |
12 Thanks to Adam Costello and Pieter S. van der Meulen for the "diamond" | |
13 and "radial waves" patterns, respectively. | |
14 | |
15 to do (someday, maybe): | |
16 - fix expose/redraw code: don't draw entire row if only part exposed | |
17 - 8-bit (colormapped) X support | |
18 - finish resizable checkerboard-gradient (sizes 4-128?) | |
19 - use %.1023s to simplify truncation of title-bar string? | |
20 | |
21 --------------------------------------------------------------------------- | |
22 | |
23 Changelog: | |
24 - 1.01: initial public release | |
25 - 1.02: modified to allow abbreviated options; fixed char/uchar mismatch | |
26 - 1.10: added support for non-default visuals; fixed X pixel-conversion | |
27 - 1.11: added -usleep option for demos; fixed command-line parsing bug | |
28 - 1.12: added -pause option for demos and testing | |
29 - 1.20: added runtime MMX-enabling/disabling and new -mmx* options | |
30 - 1.21: fixed some small X memory leaks (thanks to François Petitjean) | |
31 - 1.22: fixed XFreeGC() crash bug (thanks to Patrick Welche) | |
32 - 1.23: added -bgpat 0 mode (std white/gray checkerboard, 8x8 squares) | |
33 - 1.30: added -loop option for -bgpat (ifdef FEATURE_LOOP); fixed bpp = | |
34 24; added support for X resources (thanks to Gerhard Niklasch) | |
35 - 1.31: added code to skip unused chunks (thanks to Glenn Randers-Pehrson) | |
36 - 1.32: added AMD64/EM64T support (__x86_64__); added basic expose/redraw | |
37 handling | |
38 - 2.00: dual-licensed (added GNU GPL) | |
39 - 2.01: fixed 64-bit typo in readpng2.c; fixed -pause usage description | |
40 - 2.02: fixed improper display of usage screen on PNG error(s); fixed | |
41 unexpected-EOF and file-read-error cases; fixed Trace() cut-and- | |
42 paste bugs | |
43 | |
44 --------------------------------------------------------------------------- | |
45 | |
46 Copyright (c) 1998-2008 Greg Roelofs. All rights reserved. | |
47 | |
48 This software is provided "as is," without warranty of any kind, | |
49 express or implied. In no event shall the author or contributors | |
50 be held liable for any damages arising in any way from the use of | |
51 this software. | |
52 | |
53 The contents of this file are DUAL-LICENSED. You may modify and/or | |
54 redistribute this software according to the terms of one of the | |
55 following two licenses (at your option): | |
56 | |
57 | |
58 LICENSE 1 ("BSD-like with advertising clause"): | |
59 | |
60 Permission is granted to anyone to use this software for any purpose, | |
61 including commercial applications, and to alter it and redistribute | |
62 it freely, subject to the following restrictions: | |
63 | |
64 1. Redistributions of source code must retain the above copyright | |
65 notice, disclaimer, and this list of conditions. | |
66 2. Redistributions in binary form must reproduce the above copyright | |
67 notice, disclaimer, and this list of conditions in the documenta- | |
68 tion and/or other materials provided with the distribution. | |
69 3. All advertising materials mentioning features or use of this | |
70 software must display the following acknowledgment: | |
71 | |
72 This product includes software developed by Greg Roelofs | |
73 and contributors for the book, "PNG: The Definitive Guide," | |
74 published by O'Reilly and Associates. | |
75 | |
76 | |
77 LICENSE 2 (GNU GPL v2 or later): | |
78 | |
79 This program is free software; you can redistribute it and/or modify | |
80 it under the terms of the GNU General Public License as published by | |
81 the Free Software Foundation; either version 2 of the License, or | |
82 (at your option) any later version. | |
83 | |
84 This program is distributed in the hope that it will be useful, | |
85 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
86 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
87 GNU General Public License for more details. | |
88 | |
89 You should have received a copy of the GNU General Public License | |
90 along with this program; if not, write to the Free Software Foundation, | |
91 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
92 | |
93 ---------------------------------------------------------------------------*/ | |
94 | |
95 #define PROGNAME "rpng2-x" | |
96 #define LONGNAME "Progressive PNG Viewer for X" | |
97 #define VERSION "2.02 of 16 March 2008" | |
98 #define RESNAME "rpng2" /* our X resource application name */ | |
99 #define RESCLASS "Rpng" /* our X resource class name */ | |
100 | |
101 #include <stdio.h> | |
102 #include <stdlib.h> | |
103 #include <ctype.h> | |
104 #include <string.h> | |
105 #include <setjmp.h> /* for jmpbuf declaration in readpng2.h */ | |
106 #include <time.h> | |
107 #include <math.h> /* only for PvdM background code */ | |
108 #include <X11/Xlib.h> | |
109 #include <X11/Xutil.h> | |
110 #include <X11/Xos.h> | |
111 #include <X11/keysym.h> /* defines XK_* macros */ | |
112 | |
113 #ifdef VMS | |
114 # include <unistd.h> | |
115 #endif | |
116 | |
117 /* all for PvdM background code: */ | |
118 #ifndef PI | |
119 # define PI 3.141592653589793238 | |
120 #endif | |
121 #define PI_2 (PI*0.5) | |
122 #define INV_PI_360 (360.0 / PI) | |
123 #define MAX(a,b) (a>b?a:b) | |
124 #define MIN(a,b) (a<b?a:b) | |
125 #define CLIP(a,min,max) MAX(min,MIN((a),max)) | |
126 #define ABS(a) ((a)<0?-(a):(a)) | |
127 #define CLIP8P(c) MAX(0,(MIN((c),255))) /* 8-bit pos. integer (uch) */ | |
128 #define ROUNDF(f) ((int)(f + 0.5)) | |
129 | |
130 #define QUIT(e,k) ((e.type == ButtonPress && e.xbutton.button == Button1) || \ | |
131 (e.type == KeyPress && /* v--- or 1 for shifted keys */ \ | |
132 ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape))) | |
133 | |
134 #define NO_24BIT_MASKS /* undef case not fully written--only for redisplay() */ | |
135 | |
136 #define rgb1_max bg_freq | |
137 #define rgb1_min bg_gray | |
138 #define rgb2_max bg_bsat | |
139 #define rgb2_min bg_brot | |
140 | |
141 /* #define DEBUG */ /* this enables the Trace() macros */ | |
142 | |
143 #include "readpng2.h" /* typedefs, common macros, readpng2 prototypes */ | |
144 | |
145 | |
146 /* could just include png.h, but this macro is the only thing we need | |
147 * (name and typedefs changed to local versions); note that side effects | |
148 * only happen with alpha (which could easily be avoided with | |
149 * "ush acopy = (alpha);") */ | |
150 | |
151 #define alpha_composite(composite, fg, alpha, bg) { \ | |
152 ush temp = ((ush)(fg)*(ush)(alpha) + \ | |
153 (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128); \ | |
154 (composite) = (uch)((temp + (temp >> 8)) >> 8); \ | |
155 } | |
156 | |
157 | |
158 #define INBUFSIZE 4096 /* with pseudo-timing on (1 sec delay/block), this | |
159 * block size corresponds roughly to a download | |
160 * speed 10% faster than theoretical 33.6K maximum | |
161 * (assuming 8 data bits, 1 stop bit and no other | |
162 * overhead) */ | |
163 | |
164 /* local prototypes */ | |
165 static void rpng2_x_init (void); | |
166 static int rpng2_x_create_window (void); | |
167 static int rpng2_x_load_bg_image (void); | |
168 static void rpng2_x_display_row (ulg row); | |
169 static void rpng2_x_finish_display (void); | |
170 static void rpng2_x_redisplay_image (ulg startcol, ulg startrow, | |
171 ulg width, ulg height); | |
172 #ifdef FEATURE_LOOP | |
173 static void rpng2_x_reload_bg_image (void); | |
174 static int is_number (char *p); | |
175 #endif | |
176 static void rpng2_x_cleanup (void); | |
177 static int rpng2_x_msb (ulg u32val); | |
178 | |
179 | |
180 static char titlebar[1024], *window_name = titlebar; | |
181 static char *appname = LONGNAME; | |
182 static char *icon_name = PROGNAME; | |
183 static char *res_name = RESNAME; | |
184 static char *res_class = RESCLASS; | |
185 static char *filename; | |
186 static FILE *infile; | |
187 | |
188 static mainprog_info rpng2_info; | |
189 | |
190 static uch inbuf[INBUFSIZE]; | |
191 static int incount; | |
192 | |
193 static int pat = 6; /* must be less than num_bgpat */ | |
194 static int bg_image = 0; | |
195 static int bgscale, bgscale_default = 16; | |
196 static ulg bg_rowbytes; | |
197 static uch *bg_data; | |
198 | |
199 int pause_after_pass = FALSE; | |
200 int demo_timing = FALSE; | |
201 ulg usleep_duration = 0L; | |
202 | |
203 static struct rgb_color { | |
204 uch r, g, b; | |
205 } rgb[] = { | |
206 { 0, 0, 0}, /* 0: black */ | |
207 {255, 255, 255}, /* 1: white */ | |
208 {173, 132, 57}, /* 2: tan */ | |
209 { 64, 132, 0}, /* 3: medium green */ | |
210 {189, 117, 1}, /* 4: gold */ | |
211 {253, 249, 1}, /* 5: yellow */ | |
212 { 0, 0, 255}, /* 6: blue */ | |
213 { 0, 0, 120}, /* 7: medium blue */ | |
214 {255, 0, 255}, /* 8: magenta */ | |
215 { 64, 0, 64}, /* 9: dark magenta */ | |
216 {255, 0, 0}, /* 10: red */ | |
217 { 64, 0, 0}, /* 11: dark red */ | |
218 {255, 127, 0}, /* 12: orange */ | |
219 {192, 96, 0}, /* 13: darker orange */ | |
220 { 24, 60, 0}, /* 14: dark green-yellow */ | |
221 { 85, 125, 200}, /* 15: ice blue */ | |
222 {192, 192, 192} /* 16: Netscape/Mosaic gray */ | |
223 }; | |
224 /* not used for now, but should be for error-checking: | |
225 static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color); | |
226 */ | |
227 | |
228 /* | |
229 This whole struct is a fairly cheesy way to keep the number of | |
230 command-line options to a minimum. The radial-waves background | |
231 type is a particularly poor fit to the integer elements of the | |
232 struct...but a few macros and a little fixed-point math will do | |
233 wonders for ya. | |
234 | |
235 type bits: | |
236 F E D C B A 9 8 7 6 5 4 3 2 1 0 | |
237 | | | | | | |
238 | | +-+-+-- 0 = sharp-edged checkerboard | |
239 | | 1 = soft diamonds | |
240 | | 2 = radial waves | |
241 | | 3-7 = undefined | |
242 | +-- gradient #2 inverted? | |
243 +-- alternating columns inverted? | |
244 */ | |
245 static struct background_pattern { | |
246 ush type; | |
247 int rgb1_max, rgb1_min; /* or bg_freq, bg_gray */ | |
248 int rgb2_max, rgb2_min; /* or bg_bsat, bg_brot (both scaled by 10)*/ | |
249 } bg[] = { | |
250 {0, 1,1, 16,16}, /* checkered: white vs. light gray (basic) */ | |
251 {0+8, 2,0, 1,15}, /* checkered: tan/black vs. white/ice blue */ | |
252 {0+24, 2,0, 1,0}, /* checkered: tan/black vs. white/black */ | |
253 {0+8, 4,5, 0,2}, /* checkered: gold/yellow vs. black/tan */ | |
254 {0+8, 4,5, 0,6}, /* checkered: gold/yellow vs. black/blue */ | |
255 {0, 7,0, 8,9}, /* checkered: deep blue/black vs. magenta */ | |
256 {0+8, 13,0, 5,14}, /* checkered: orange/black vs. yellow */ | |
257 {0+8, 12,0, 10,11}, /* checkered: orange/black vs. red */ | |
258 {1, 7,0, 8,0}, /* diamonds: deep blue/black vs. magenta */ | |
259 {1, 12,0, 11,0}, /* diamonds: orange vs. dark red */ | |
260 {1, 10,0, 7,0}, /* diamonds: red vs. medium blue */ | |
261 {1, 4,0, 5,0}, /* diamonds: gold vs. yellow */ | |
262 {1, 3,0, 0,0}, /* diamonds: medium green vs. black */ | |
263 {2, 16, 100, 20, 0}, /* radial: ~hard radial color-beams */ | |
264 {2, 18, 100, 10, 2}, /* radial: soft, curved radial color-beams */ | |
265 {2, 16, 256, 100, 250}, /* radial: very tight spiral */ | |
266 {2, 10000, 256, 11, 0} /* radial: dipole-moire' (almost fractal) */ | |
267 }; | |
268 static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern); | |
269 | |
270 | |
271 /* X-specific variables */ | |
272 static char *displayname; | |
273 static XImage *ximage; | |
274 static Display *display; | |
275 static int depth; | |
276 static Visual *visual; | |
277 static XVisualInfo *visual_list; | |
278 static int RShift, GShift, BShift; | |
279 static ulg RMask, GMask, BMask; | |
280 static Window window; | |
281 static GC gc; | |
282 static Colormap colormap; | |
283 | |
284 static int have_nondefault_visual = FALSE; | |
285 static int have_colormap = FALSE; | |
286 static int have_window = FALSE; | |
287 static int have_gc = FALSE; | |
288 | |
289 | |
290 | |
291 | |
292 int main(int argc, char **argv) | |
293 { | |
294 #ifdef sgi | |
295 char tmpline[80]; | |
296 #endif | |
297 char *p, *bgstr = NULL; | |
298 int rc, alen, flen; | |
299 int error = 0; | |
300 int timing = FALSE; | |
301 int have_bg = FALSE; | |
302 #ifdef FEATURE_LOOP | |
303 int loop = FALSE; | |
304 long loop_interval = -1; /* seconds (100,000 max) */ | |
305 #endif | |
306 double LUT_exponent; /* just the lookup table */ | |
307 double CRT_exponent = 2.2; /* just the monitor */ | |
308 double default_display_exponent; /* whole display system */ | |
309 XEvent e; | |
310 KeySym k; | |
311 | |
312 | |
313 /* First initialize a few things, just to be sure--memset takes care of | |
314 * default background color (black), booleans (FALSE), pointers (NULL), | |
315 * etc. */ | |
316 | |
317 displayname = (char *)NULL; | |
318 filename = (char *)NULL; | |
319 memset(&rpng2_info, 0, sizeof(mainprog_info)); | |
320 | |
321 | |
322 /* Set the default value for our display-system exponent, i.e., the | |
323 * product of the CRT exponent and the exponent corresponding to | |
324 * the frame-buffer's lookup table (LUT), if any. This is not an | |
325 * exhaustive list of LUT values (e.g., OpenStep has a lot of weird | |
326 * ones), but it should cover 99% of the current possibilities. */ | |
327 | |
328 #if defined(NeXT) | |
329 /* third-party utilities can modify the default LUT exponent */ | |
330 LUT_exponent = 1.0 / 2.2; | |
331 /* | |
332 if (some_next_function_that_returns_gamma(&next_gamma)) | |
333 LUT_exponent = 1.0 / next_gamma; | |
334 */ | |
335 #elif defined(sgi) | |
336 LUT_exponent = 1.0 / 1.7; | |
337 /* there doesn't seem to be any documented function to | |
338 * get the "gamma" value, so we do it the hard way */ | |
339 infile = fopen("/etc/config/system.glGammaVal", "r"); | |
340 if (infile) { | |
341 double sgi_gamma; | |
342 | |
343 fgets(tmpline, 80, infile); | |
344 fclose(infile); | |
345 sgi_gamma = atof(tmpline); | |
346 if (sgi_gamma > 0.0) | |
347 LUT_exponent = 1.0 / sgi_gamma; | |
348 } | |
349 #elif defined(Macintosh) | |
350 LUT_exponent = 1.8 / 2.61; | |
351 /* | |
352 if (some_mac_function_that_returns_gamma(&mac_gamma)) | |
353 LUT_exponent = mac_gamma / 2.61; | |
354 */ | |
355 #else | |
356 LUT_exponent = 1.0; /* assume no LUT: most PCs */ | |
357 #endif | |
358 | |
359 /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */ | |
360 default_display_exponent = LUT_exponent * CRT_exponent; | |
361 | |
362 | |
363 /* If the user has set the SCREEN_GAMMA environment variable as suggested | |
364 * (somewhat imprecisely) in the libpng documentation, use that; otherwise | |
365 * use the default value we just calculated. Either way, the user may | |
366 * override this via a command-line option. */ | |
367 | |
368 if ((p = getenv("SCREEN_GAMMA")) != NULL) | |
369 rpng2_info.display_exponent = atof(p); | |
370 else | |
371 rpng2_info.display_exponent = default_display_exponent; | |
372 | |
373 | |
374 /* Now parse the command line for options and the PNG filename. */ | |
375 | |
376 while (*++argv && !error) { | |
377 if (!strncmp(*argv, "-display", 2)) { | |
378 if (!*++argv) | |
379 ++error; | |
380 else | |
381 displayname = *argv; | |
382 } else if (!strncmp(*argv, "-gamma", 2)) { | |
383 if (!*++argv) | |
384 ++error; | |
385 else { | |
386 rpng2_info.display_exponent = atof(*argv); | |
387 if (rpng2_info.display_exponent <= 0.0) | |
388 ++error; | |
389 } | |
390 } else if (!strncmp(*argv, "-bgcolor", 4)) { | |
391 if (!*++argv) | |
392 ++error; | |
393 else { | |
394 bgstr = *argv; | |
395 if (strlen(bgstr) != 7 || bgstr[0] != '#') | |
396 ++error; | |
397 else { | |
398 have_bg = TRUE; | |
399 bg_image = FALSE; | |
400 } | |
401 } | |
402 } else if (!strncmp(*argv, "-bgpat", 4)) { | |
403 if (!*++argv) | |
404 ++error; | |
405 else { | |
406 pat = atoi(*argv); | |
407 if (pat >= 0 && pat < num_bgpat) { | |
408 bg_image = TRUE; | |
409 have_bg = FALSE; | |
410 } else | |
411 ++error; | |
412 } | |
413 } else if (!strncmp(*argv, "-usleep", 2)) { | |
414 if (!*++argv) | |
415 ++error; | |
416 else { | |
417 usleep_duration = (ulg)atol(*argv); | |
418 demo_timing = TRUE; | |
419 } | |
420 } else if (!strncmp(*argv, "-pause", 2)) { | |
421 pause_after_pass = TRUE; | |
422 } else if (!strncmp(*argv, "-timing", 2)) { | |
423 timing = TRUE; | |
424 #ifdef FEATURE_LOOP | |
425 } else if (!strncmp(*argv, "-loop", 2)) { | |
426 loop = TRUE; | |
427 if (!argv[1] || !is_number(argv[1])) | |
428 loop_interval = 2; | |
429 else { | |
430 ++argv; | |
431 loop_interval = atol(*argv); | |
432 if (loop_interval < 0) | |
433 loop_interval = 2; | |
434 else if (loop_interval > 100000) /* bit more than one day */ | |
435 loop_interval = 100000; | |
436 } | |
437 #endif | |
438 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) | |
439 } else if (!strncmp(*argv, "-nommxfilters", 7)) { | |
440 rpng2_info.nommxfilters = TRUE; | |
441 } else if (!strncmp(*argv, "-nommxcombine", 7)) { | |
442 rpng2_info.nommxcombine = TRUE; | |
443 } else if (!strncmp(*argv, "-nommxinterlace", 7)) { | |
444 rpng2_info.nommxinterlace = TRUE; | |
445 } else if (!strcmp(*argv, "-nommx")) { | |
446 rpng2_info.nommxfilters = TRUE; | |
447 rpng2_info.nommxcombine = TRUE; | |
448 rpng2_info.nommxinterlace = TRUE; | |
449 #endif | |
450 } else { | |
451 if (**argv != '-') { | |
452 filename = *argv; | |
453 if (argv[1]) /* shouldn't be any more args after filename */ | |
454 ++error; | |
455 } else | |
456 ++error; /* not expecting any other options */ | |
457 } | |
458 } | |
459 | |
460 if (!filename) | |
461 ++error; | |
462 | |
463 | |
464 /* print usage screen if any errors up to this point */ | |
465 | |
466 if (error) { | |
467 fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname); | |
468 readpng2_version_info(); | |
469 fprintf(stderr, "\n" | |
470 "Usage: %s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n" | |
471 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) | |
472 " %*s [[-nommxfilters] [-nommxcombine] [-nommxinterlace] | -nommx]\n" | |
473 #endif | |
474 #ifdef FEATURE_LOOP | |
475 " %*s [-usleep dur | -timing] [-pause] [-loop [sec]] file.png\n\n" | |
476 #else | |
477 " %*s [-usleep dur | -timing] [-pause] file.png\n\n" | |
478 #endif | |
479 " xdpy\tname of the target X display (e.g., ``hostname:0'')\n" | |
480 " exp \ttransfer-function exponent (``gamma'') of the display\n" | |
481 "\t\t system in floating-point format (e.g., ``%.1f''); equal\n" | |
482 "\t\t to the product of the lookup-table exponent (varies)\n" | |
483 "\t\t and the CRT exponent (usually 2.2); must be positive\n" | |
484 " bg \tdesired background color in 7-character hex RGB format\n" | |
485 "\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n" | |
486 "\t\t used with transparent images; overrides -bgpat\n" | |
487 " pat \tdesired background pattern number (0-%d); used with\n" | |
488 "\t\t transparent images; overrides -bgcolor\n" | |
489 #ifdef FEATURE_LOOP | |
490 " -loop\tloops through background images after initial display\n" | |
491 "\t\t is complete (depends on -bgpat)\n" | |
492 " sec \tseconds to display each background image (default = 2)\n" | |
493 #endif | |
494 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) | |
495 " -nommx*\tdisable optimized MMX routines for decoding row filters,\n" | |
496 "\t\t combining rows, and expanding interlacing, respectively\n" | |
497 #endif | |
498 " dur \tduration in microseconds to wait after displaying each\n" | |
499 "\t\t row (for demo purposes)\n" | |
500 " -timing\tenables delay for every block read, to simulate modem\n" | |
501 "\t\t download of image (~36 Kbps)\n" | |
502 " -pause\tpauses after displaying each pass until mouse clicked\n" | |
503 "\nPress Q, Esc or mouse button 1 (within image window, after image\n" | |
504 "is displayed) to quit.\n" | |
505 "\n", PROGNAME, | |
506 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) | |
507 (int)strlen(PROGNAME), " ", | |
508 #endif | |
509 (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat-1); | |
510 exit(1); | |
511 } | |
512 | |
513 | |
514 if (!(infile = fopen(filename, "rb"))) { | |
515 fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename); | |
516 ++error; | |
517 } else { | |
518 incount = fread(inbuf, 1, INBUFSIZE, infile); | |
519 if (incount < 8 || !readpng2_check_sig(inbuf, 8)) { | |
520 fprintf(stderr, PROGNAME | |
521 ": [%s] is not a PNG file: incorrect signature\n", | |
522 filename); | |
523 ++error; | |
524 } else if ((rc = readpng2_init(&rpng2_info)) != 0) { | |
525 switch (rc) { | |
526 case 2: | |
527 fprintf(stderr, PROGNAME | |
528 ": [%s] has bad IHDR (libpng longjmp)\n", filename); | |
529 break; | |
530 case 4: | |
531 fprintf(stderr, PROGNAME ": insufficient memory\n"); | |
532 break; | |
533 default: | |
534 fprintf(stderr, PROGNAME | |
535 ": unknown readpng2_init() error\n"); | |
536 break; | |
537 } | |
538 ++error; | |
539 } else { | |
540 Trace((stderr, "about to call XOpenDisplay()\n")) | |
541 display = XOpenDisplay(displayname); | |
542 if (!display) { | |
543 readpng2_cleanup(&rpng2_info); | |
544 fprintf(stderr, PROGNAME ": can't open X display [%s]\n", | |
545 displayname? displayname : "default"); | |
546 ++error; | |
547 } | |
548 } | |
549 if (error) | |
550 fclose(infile); | |
551 } | |
552 | |
553 | |
554 if (error) { | |
555 fprintf(stderr, PROGNAME ": aborting.\n"); | |
556 exit(2); | |
557 } | |
558 | |
559 | |
560 /* set the title-bar string, but make sure buffer doesn't overflow */ | |
561 | |
562 alen = strlen(appname); | |
563 flen = strlen(filename); | |
564 if (alen + flen + 3 > 1023) | |
565 sprintf(titlebar, "%s: ...%s", appname, filename+(alen+flen+6-1023)); | |
566 else | |
567 sprintf(titlebar, "%s: %s", appname, filename); | |
568 | |
569 | |
570 /* set some final rpng2_info variables before entering main data loop */ | |
571 | |
572 if (have_bg) { | |
573 unsigned r, g, b; /* this approach quiets compiler warnings */ | |
574 | |
575 sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b); | |
576 rpng2_info.bg_red = (uch)r; | |
577 rpng2_info.bg_green = (uch)g; | |
578 rpng2_info.bg_blue = (uch)b; | |
579 } else | |
580 rpng2_info.need_bgcolor = TRUE; | |
581 | |
582 rpng2_info.state = kPreInit; | |
583 rpng2_info.mainprog_init = rpng2_x_init; | |
584 rpng2_info.mainprog_display_row = rpng2_x_display_row; | |
585 rpng2_info.mainprog_finish_display = rpng2_x_finish_display; | |
586 | |
587 | |
588 /* OK, this is the fun part: call readpng2_decode_data() at the start of | |
589 * the loop to deal with our first buffer of data (read in above to verify | |
590 * that the file is a PNG image), then loop through the file and continue | |
591 * calling the same routine to handle each chunk of data. It in turn | |
592 * passes the data to libpng, which will invoke one or more of our call- | |
593 * backs as decoded data become available. We optionally call sleep() for | |
594 * one second per iteration to simulate downloading the image via an analog | |
595 * modem. */ | |
596 | |
597 for (;;) { | |
598 Trace((stderr, "about to call readpng2_decode_data()\n")) | |
599 if (readpng2_decode_data(&rpng2_info, inbuf, incount)) | |
600 ++error; | |
601 Trace((stderr, "done with readpng2_decode_data()\n")) | |
602 | |
603 if (error || incount != INBUFSIZE || rpng2_info.state == kDone) { | |
604 if (rpng2_info.state == kDone) { | |
605 Trace((stderr, "done decoding PNG image\n")) | |
606 } else if (ferror(infile)) { | |
607 fprintf(stderr, PROGNAME | |
608 ": error while reading PNG image file\n"); | |
609 exit(3); | |
610 } else if (feof(infile)) { | |
611 fprintf(stderr, PROGNAME ": end of file reached " | |
612 "(unexpectedly) while reading PNG image file\n"); | |
613 exit(3); | |
614 } else /* if (error) */ { | |
615 // will print error message below | |
616 } | |
617 break; | |
618 } | |
619 | |
620 if (timing) | |
621 sleep(1); | |
622 | |
623 incount = fread(inbuf, 1, INBUFSIZE, infile); | |
624 } | |
625 | |
626 | |
627 /* clean up PNG stuff and report any decoding errors */ | |
628 | |
629 fclose(infile); | |
630 Trace((stderr, "about to call readpng2_cleanup()\n")) | |
631 readpng2_cleanup(&rpng2_info); | |
632 | |
633 if (error) { | |
634 fprintf(stderr, PROGNAME ": libpng error while decoding PNG image\n"); | |
635 exit(3); | |
636 } | |
637 | |
638 | |
639 #ifdef FEATURE_LOOP | |
640 | |
641 if (loop && bg_image) { | |
642 Trace((stderr, "entering -loop loop (FEATURE_LOOP)\n")) | |
643 for (;;) { | |
644 int i, use_sleep; | |
645 struct timeval now, then; | |
646 | |
647 /* get current time and add loop_interval to get target time */ | |
648 if (gettimeofday(&then, NULL) == 0) { | |
649 then.tv_sec += loop_interval; | |
650 use_sleep = FALSE; | |
651 } else | |
652 use_sleep = TRUE; | |
653 | |
654 /* do quick check for a quit event but don't wait for it */ | |
655 /* GRR BUG: should also check for Expose events and redraw... */ | |
656 if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask, &e)) | |
657 if (QUIT(e,k)) | |
658 break; | |
659 | |
660 /* generate next background image */ | |
661 if (++pat >= num_bgpat) | |
662 pat = 0; | |
663 rpng2_x_reload_bg_image(); | |
664 | |
665 /* wait for timeout, using whatever means are available */ | |
666 if (use_sleep || gettimeofday(&now, NULL) != 0) { | |
667 for (i = loop_interval; i > 0; --i) { | |
668 sleep(1); | |
669 /* GRR BUG: also need to check for Expose (and redraw!) */ | |
670 if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask, | |
671 &e) && QUIT(e,k)) | |
672 break; | |
673 } | |
674 } else { | |
675 /* Y2038 BUG! */ | |
676 if (now.tv_sec < then.tv_sec || | |
677 (now.tv_sec == then.tv_sec && now.tv_usec < then.tv_usec)) | |
678 { | |
679 int quit = FALSE; | |
680 long seconds_to_go = then.tv_sec - now.tv_sec; | |
681 long usleep_usec; | |
682 | |
683 /* basically chew up most of remaining loop-interval with | |
684 * calls to sleep(1) interleaved with checks for quit | |
685 * events, but also recalc time-to-go periodically; when | |
686 * done, clean up any remaining time with usleep() call | |
687 * (could also use SIGALRM, but signals are a pain...) */ | |
688 while (seconds_to_go-- > 1) { | |
689 int seconds_done = 0; | |
690 | |
691 for (i = seconds_to_go; i > 0 && !quit; --i) { | |
692 sleep(1); | |
693 /* GRR BUG: need to check for Expose and redraw */ | |
694 if (XCheckMaskEvent(display, KeyPressMask | | |
695 ButtonPressMask, &e) && QUIT(e,k)) | |
696 quit = TRUE; | |
697 if (++seconds_done > 1000) | |
698 break; /* time to redo seconds_to_go meas. */ | |
699 } | |
700 if (quit) | |
701 break; | |
702 | |
703 /* OK, more than 1000 seconds since last check: | |
704 * correct the time-to-go measurement for drift */ | |
705 if (gettimeofday(&now, NULL) == 0) { | |
706 if (now.tv_sec >= then.tv_sec) | |
707 break; | |
708 seconds_to_go = then.tv_sec - now.tv_sec; | |
709 } else | |
710 ++seconds_to_go; /* restore what we subtracted */ | |
711 } | |
712 if (quit) | |
713 break; /* breaks outer do-loop, skips redisplay */ | |
714 | |
715 /* since difference between "now" and "then" is already | |
716 * eaten up to within a couple of seconds, don't need to | |
717 * worry about overflow--but might have overshot (neg.) */ | |
718 if (gettimeofday(&now, NULL) == 0) { | |
719 usleep_usec = 1000000L*(then.tv_sec - now.tv_sec) + | |
720 then.tv_usec - now.tv_usec; | |
721 if (usleep_usec > 0) | |
722 usleep((ulg)usleep_usec); | |
723 } | |
724 } | |
725 } | |
726 | |
727 /* composite image against new background and display (note that | |
728 * we do not take into account the time spent doing this...) */ | |
729 rpng2_x_redisplay_image (0, 0, rpng2_info.width, rpng2_info.height); | |
730 } | |
731 | |
732 } else /* FALL THROUGH and do the normal thing */ | |
733 | |
734 #endif /* FEATURE_LOOP */ | |
735 | |
736 /* wait for the user to tell us when to quit */ | |
737 | |
738 if (rpng2_info.state >= kWindowInit) { | |
739 Trace((stderr, "entering final wait-for-quit-event loop\n")) | |
740 do { | |
741 XNextEvent(display, &e); | |
742 if (e.type == Expose) { | |
743 XExposeEvent *ex = (XExposeEvent *)&e; | |
744 rpng2_x_redisplay_image (ex->x, ex->y, ex->width, ex->height); | |
745 } | |
746 } while (!QUIT(e,k)); | |
747 } else { | |
748 fprintf(stderr, PROGNAME ": init callback never called: probable " | |
749 "libpng error while decoding PNG metadata\n"); | |
750 exit(4); | |
751 } | |
752 | |
753 | |
754 /* we're done: clean up all image and X resources and go away */ | |
755 | |
756 Trace((stderr, "about to call rpng2_x_cleanup()\n")) | |
757 rpng2_x_cleanup(); | |
758 | |
759 return 0; | |
760 } | |
761 | |
762 | |
763 | |
764 | |
765 | |
766 /* this function is called by readpng2_info_callback() in readpng2.c, which | |
767 * in turn is called by libpng after all of the pre-IDAT chunks have been | |
768 * read and processed--i.e., we now have enough info to finish initializing */ | |
769 | |
770 static void rpng2_x_init(void) | |
771 { | |
772 ulg i; | |
773 ulg rowbytes = rpng2_info.rowbytes; | |
774 | |
775 Trace((stderr, "beginning rpng2_x_init()\n")) | |
776 Trace((stderr, " rowbytes = %d\n", rpng2_info.rowbytes)) | |
777 Trace((stderr, " width = %ld\n", rpng2_info.width)) | |
778 Trace((stderr, " height = %ld\n", rpng2_info.height)) | |
779 | |
780 rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height); | |
781 if (!rpng2_info.image_data) { | |
782 readpng2_cleanup(&rpng2_info); | |
783 return; | |
784 } | |
785 | |
786 rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *)); | |
787 if (!rpng2_info.row_pointers) { | |
788 free(rpng2_info.image_data); | |
789 rpng2_info.image_data = NULL; | |
790 readpng2_cleanup(&rpng2_info); | |
791 return; | |
792 } | |
793 | |
794 for (i = 0; i < rpng2_info.height; ++i) | |
795 rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes; | |
796 | |
797 | |
798 /* do the basic X initialization stuff, make the window, and fill it with | |
799 * the user-specified, file-specified or default background color or | |
800 * pattern */ | |
801 | |
802 if (rpng2_x_create_window()) { | |
803 | |
804 /* GRR TEMPORARY HACK: this is fundamentally no different from cases | |
805 * above; libpng should longjmp() back to us when png_ptr goes away. | |
806 * If we/it segfault instead, seems like a libpng bug... */ | |
807 | |
808 /* we're here via libpng callback, so if window fails, clean and bail */ | |
809 readpng2_cleanup(&rpng2_info); | |
810 rpng2_x_cleanup(); | |
811 exit(2); | |
812 } | |
813 | |
814 rpng2_info.state = kWindowInit; | |
815 } | |
816 | |
817 | |
818 | |
819 | |
820 | |
821 static int rpng2_x_create_window(void) | |
822 { | |
823 ulg bg_red = rpng2_info.bg_red; | |
824 ulg bg_green = rpng2_info.bg_green; | |
825 ulg bg_blue = rpng2_info.bg_blue; | |
826 ulg bg_pixel = 0L; | |
827 ulg attrmask; | |
828 int need_colormap = FALSE; | |
829 int screen, pad; | |
830 uch *xdata; | |
831 Window root; | |
832 XEvent e; | |
833 XGCValues gcvalues; | |
834 XSetWindowAttributes attr; | |
835 XTextProperty windowName, *pWindowName = &windowName; | |
836 XTextProperty iconName, *pIconName = &iconName; | |
837 XVisualInfo visual_info; | |
838 XSizeHints *size_hints; | |
839 XWMHints *wm_hints; | |
840 XClassHint *class_hints; | |
841 | |
842 | |
843 Trace((stderr, "beginning rpng2_x_create_window()\n")) | |
844 | |
845 screen = DefaultScreen(display); | |
846 depth = DisplayPlanes(display, screen); | |
847 root = RootWindow(display, screen); | |
848 | |
849 #ifdef DEBUG | |
850 XSynchronize(display, True); | |
851 #endif | |
852 | |
853 if (depth != 16 && depth != 24 && depth != 32) { | |
854 int visuals_matched = 0; | |
855 | |
856 Trace((stderr, "default depth is %d: checking other visuals\n", | |
857 depth)) | |
858 | |
859 /* 24-bit first */ | |
860 visual_info.screen = screen; | |
861 visual_info.depth = 24; | |
862 visual_list = XGetVisualInfo(display, | |
863 VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched); | |
864 if (visuals_matched == 0) { | |
865 /* GRR: add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */ | |
866 fprintf(stderr, "default screen depth %d not supported, and no" | |
867 " 24-bit visuals found\n", depth); | |
868 return 2; | |
869 } | |
870 Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n", | |
871 visuals_matched)) | |
872 visual = visual_list[0].visual; | |
873 depth = visual_list[0].depth; | |
874 /* | |
875 colormap_size = visual_list[0].colormap_size; | |
876 visual_class = visual->class; | |
877 visualID = XVisualIDFromVisual(visual); | |
878 */ | |
879 have_nondefault_visual = TRUE; | |
880 need_colormap = TRUE; | |
881 } else { | |
882 XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info); | |
883 visual = visual_info.visual; | |
884 } | |
885 | |
886 RMask = visual->red_mask; | |
887 GMask = visual->green_mask; | |
888 BMask = visual->blue_mask; | |
889 | |
890 /* GRR: add/check 8-bit support */ | |
891 if (depth == 8 || need_colormap) { | |
892 colormap = XCreateColormap(display, root, visual, AllocNone); | |
893 if (!colormap) { | |
894 fprintf(stderr, "XCreateColormap() failed\n"); | |
895 return 2; | |
896 } | |
897 have_colormap = TRUE; | |
898 if (depth == 8) | |
899 bg_image = FALSE; /* gradient just wastes palette entries */ | |
900 } | |
901 if (depth == 15 || depth == 16) { | |
902 RShift = 15 - rpng2_x_msb(RMask); /* these are right-shifts */ | |
903 GShift = 15 - rpng2_x_msb(GMask); | |
904 BShift = 15 - rpng2_x_msb(BMask); | |
905 } else if (depth > 16) { | |
906 RShift = rpng2_x_msb(RMask) - 7; /* these are left-shifts */ | |
907 GShift = rpng2_x_msb(GMask) - 7; | |
908 BShift = rpng2_x_msb(BMask) - 7; | |
909 } | |
910 if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) { | |
911 fprintf(stderr, "rpng2 internal logic error: negative X shift(s)!\n"); | |
912 return 2; | |
913 } | |
914 | |
915 /*--------------------------------------------------------------------------- | |
916 Finally, create the window. | |
917 ---------------------------------------------------------------------------*/ | |
918 | |
919 attr.backing_store = Always; | |
920 attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask; | |
921 attrmask = CWBackingStore | CWEventMask; | |
922 if (have_nondefault_visual) { | |
923 attr.colormap = colormap; | |
924 attr.background_pixel = 0; | |
925 attr.border_pixel = 1; | |
926 attrmask |= CWColormap | CWBackPixel | CWBorderPixel; | |
927 } | |
928 | |
929 window = XCreateWindow(display, root, 0, 0, rpng2_info.width, | |
930 rpng2_info.height, 0, depth, InputOutput, visual, attrmask, &attr); | |
931 | |
932 if (window == None) { | |
933 fprintf(stderr, "XCreateWindow() failed\n"); | |
934 return 2; | |
935 } else | |
936 have_window = TRUE; | |
937 | |
938 if (depth == 8) | |
939 XSetWindowColormap(display, window, colormap); | |
940 | |
941 if (!XStringListToTextProperty(&window_name, 1, pWindowName)) | |
942 pWindowName = NULL; | |
943 if (!XStringListToTextProperty(&icon_name, 1, pIconName)) | |
944 pIconName = NULL; | |
945 | |
946 /* OK if either hints allocation fails; XSetWMProperties() allows NULLs */ | |
947 | |
948 if ((size_hints = XAllocSizeHints()) != NULL) { | |
949 /* window will not be resizable */ | |
950 size_hints->flags = PMinSize | PMaxSize; | |
951 size_hints->min_width = size_hints->max_width = (int)rpng2_info.width; | |
952 size_hints->min_height = size_hints->max_height = | |
953 (int)rpng2_info.height; | |
954 } | |
955 | |
956 if ((wm_hints = XAllocWMHints()) != NULL) { | |
957 wm_hints->initial_state = NormalState; | |
958 wm_hints->input = True; | |
959 /* wm_hints->icon_pixmap = icon_pixmap; */ | |
960 wm_hints->flags = StateHint | InputHint /* | IconPixmapHint */ ; | |
961 } | |
962 | |
963 if ((class_hints = XAllocClassHint()) != NULL) { | |
964 class_hints->res_name = res_name; | |
965 class_hints->res_class = res_class; | |
966 } | |
967 | |
968 XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0, | |
969 size_hints, wm_hints, class_hints); | |
970 | |
971 /* various properties and hints no longer needed; free memory */ | |
972 if (pWindowName) | |
973 XFree(pWindowName->value); | |
974 if (pIconName) | |
975 XFree(pIconName->value); | |
976 if (size_hints) | |
977 XFree(size_hints); | |
978 if (wm_hints) | |
979 XFree(wm_hints); | |
980 if (class_hints) | |
981 XFree(class_hints); | |
982 | |
983 XMapWindow(display, window); | |
984 | |
985 gc = XCreateGC(display, window, 0, &gcvalues); | |
986 have_gc = TRUE; | |
987 | |
988 /*--------------------------------------------------------------------------- | |
989 Allocate memory for the X- and display-specific version of the image. | |
990 ---------------------------------------------------------------------------*/ | |
991 | |
992 if (depth == 24 || depth == 32) { | |
993 xdata = (uch *)malloc(4*rpng2_info.width*rpng2_info.height); | |
994 pad = 32; | |
995 } else if (depth == 16) { | |
996 xdata = (uch *)malloc(2*rpng2_info.width*rpng2_info.height); | |
997 pad = 16; | |
998 } else /* depth == 8 */ { | |
999 xdata = (uch *)malloc(rpng2_info.width*rpng2_info.height); | |
1000 pad = 8; | |
1001 } | |
1002 | |
1003 if (!xdata) { | |
1004 fprintf(stderr, PROGNAME ": unable to allocate image memory\n"); | |
1005 return 4; | |
1006 } | |
1007 | |
1008 ximage = XCreateImage(display, visual, depth, ZPixmap, 0, | |
1009 (char *)xdata, rpng2_info.width, rpng2_info.height, pad, 0); | |
1010 | |
1011 if (!ximage) { | |
1012 fprintf(stderr, PROGNAME ": XCreateImage() failed\n"); | |
1013 free(xdata); | |
1014 return 3; | |
1015 } | |
1016 | |
1017 /* to avoid testing the byte order every pixel (or doubling the size of | |
1018 * the drawing routine with a giant if-test), we arbitrarily set the byte | |
1019 * order to MSBFirst and let Xlib worry about inverting things on little- | |
1020 * endian machines (e.g., Linux/x86, old VAXen, etc.)--this is not the | |
1021 * most efficient approach (the giant if-test would be better), but in | |
1022 * the interest of clarity, we'll take the easy way out... */ | |
1023 | |
1024 ximage->byte_order = MSBFirst; | |
1025 | |
1026 /*--------------------------------------------------------------------------- | |
1027 Fill window with the specified background color (default is black) or | |
1028 faked "background image" (but latter is disabled if 8-bit; gradients | |
1029 just waste palette entries). | |
1030 ---------------------------------------------------------------------------*/ | |
1031 | |
1032 if (bg_image) | |
1033 rpng2_x_load_bg_image(); /* resets bg_image if fails */ | |
1034 | |
1035 if (!bg_image) { | |
1036 if (depth == 24 || depth == 32) { | |
1037 bg_pixel = (bg_red << RShift) | | |
1038 (bg_green << GShift) | | |
1039 (bg_blue << BShift); | |
1040 } else if (depth == 16) { | |
1041 bg_pixel = (((bg_red << 8) >> RShift) & RMask) | | |
1042 (((bg_green << 8) >> GShift) & GMask) | | |
1043 (((bg_blue << 8) >> BShift) & BMask); | |
1044 } else /* depth == 8 */ { | |
1045 | |
1046 /* GRR: add 8-bit support */ | |
1047 | |
1048 } | |
1049 XSetForeground(display, gc, bg_pixel); | |
1050 XFillRectangle(display, window, gc, 0, 0, rpng2_info.width, | |
1051 rpng2_info.height); | |
1052 } | |
1053 | |
1054 /*--------------------------------------------------------------------------- | |
1055 Wait for first Expose event to do any drawing, then flush and return. | |
1056 ---------------------------------------------------------------------------*/ | |
1057 | |
1058 do | |
1059 XNextEvent(display, &e); | |
1060 while (e.type != Expose || e.xexpose.count); | |
1061 | |
1062 XFlush(display); | |
1063 | |
1064 return 0; | |
1065 | |
1066 } /* end function rpng2_x_create_window() */ | |
1067 | |
1068 | |
1069 | |
1070 | |
1071 | |
1072 static int rpng2_x_load_bg_image(void) | |
1073 { | |
1074 uch *src; | |
1075 char *dest; | |
1076 uch r1, r2, g1, g2, b1, b2; | |
1077 uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv; | |
1078 int k, hmax, max; | |
1079 int xidx, yidx, yidx_max; | |
1080 int even_odd_vert, even_odd_horiz, even_odd; | |
1081 int invert_gradient2 = (bg[pat].type & 0x08); | |
1082 int invert_column; | |
1083 int ximage_rowbytes = ximage->bytes_per_line; | |
1084 ulg i, row; | |
1085 ulg pixel; | |
1086 | |
1087 /*--------------------------------------------------------------------------- | |
1088 Allocate buffer for fake background image to be used with transparent | |
1089 images; if this fails, revert to plain background color. | |
1090 ---------------------------------------------------------------------------*/ | |
1091 | |
1092 bg_rowbytes = 3 * rpng2_info.width; | |
1093 bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height); | |
1094 if (!bg_data) { | |
1095 fprintf(stderr, PROGNAME | |
1096 ": unable to allocate memory for background image\n"); | |
1097 bg_image = 0; | |
1098 return 1; | |
1099 } | |
1100 | |
1101 bgscale = (pat == 0)? 8 : bgscale_default; | |
1102 yidx_max = bgscale - 1; | |
1103 | |
1104 /*--------------------------------------------------------------------------- | |
1105 Vertical gradients (ramps) in NxN squares, alternating direction and | |
1106 colors (N == bgscale). | |
1107 ---------------------------------------------------------------------------*/ | |
1108 | |
1109 if ((bg[pat].type & 0x07) == 0) { | |
1110 uch r1_min = rgb[bg[pat].rgb1_min].r; | |
1111 uch g1_min = rgb[bg[pat].rgb1_min].g; | |
1112 uch b1_min = rgb[bg[pat].rgb1_min].b; | |
1113 uch r2_min = rgb[bg[pat].rgb2_min].r; | |
1114 uch g2_min = rgb[bg[pat].rgb2_min].g; | |
1115 uch b2_min = rgb[bg[pat].rgb2_min].b; | |
1116 int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min; | |
1117 int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min; | |
1118 int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min; | |
1119 int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min; | |
1120 int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min; | |
1121 int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min; | |
1122 | |
1123 for (row = 0; row < rpng2_info.height; ++row) { | |
1124 yidx = (int)(row % bgscale); | |
1125 even_odd_vert = (int)((row / bgscale) & 1); | |
1126 | |
1127 r1 = r1_min + (r1_diff * yidx) / yidx_max; | |
1128 g1 = g1_min + (g1_diff * yidx) / yidx_max; | |
1129 b1 = b1_min + (b1_diff * yidx) / yidx_max; | |
1130 r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max; | |
1131 g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max; | |
1132 b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max; | |
1133 | |
1134 r2 = r2_min + (r2_diff * yidx) / yidx_max; | |
1135 g2 = g2_min + (g2_diff * yidx) / yidx_max; | |
1136 b2 = b2_min + (b2_diff * yidx) / yidx_max; | |
1137 r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max; | |
1138 g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max; | |
1139 b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max; | |
1140 | |
1141 dest = (char *)bg_data + row*bg_rowbytes; | |
1142 for (i = 0; i < rpng2_info.width; ++i) { | |
1143 even_odd_horiz = (int)((i / bgscale) & 1); | |
1144 even_odd = even_odd_vert ^ even_odd_horiz; | |
1145 invert_column = | |
1146 (even_odd_horiz && (bg[pat].type & 0x10)); | |
1147 if (even_odd == 0) { /* gradient #1 */ | |
1148 if (invert_column) { | |
1149 *dest++ = r1_inv; | |
1150 *dest++ = g1_inv; | |
1151 *dest++ = b1_inv; | |
1152 } else { | |
1153 *dest++ = r1; | |
1154 *dest++ = g1; | |
1155 *dest++ = b1; | |
1156 } | |
1157 } else { /* gradient #2 */ | |
1158 if ((invert_column && invert_gradient2) || | |
1159 (!invert_column && !invert_gradient2)) | |
1160 { | |
1161 *dest++ = r2; /* not inverted or */ | |
1162 *dest++ = g2; /* doubly inverted */ | |
1163 *dest++ = b2; | |
1164 } else { | |
1165 *dest++ = r2_inv; | |
1166 *dest++ = g2_inv; /* singly inverted */ | |
1167 *dest++ = b2_inv; | |
1168 } | |
1169 } | |
1170 } | |
1171 } | |
1172 | |
1173 /*--------------------------------------------------------------------------- | |
1174 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam | |
1175 M. Costello. | |
1176 ---------------------------------------------------------------------------*/ | |
1177 | |
1178 } else if ((bg[pat].type & 0x07) == 1) { | |
1179 | |
1180 hmax = (bgscale-1)/2; /* half the max weight of a color */ | |
1181 max = 2*hmax; /* the max weight of a color */ | |
1182 | |
1183 r1 = rgb[bg[pat].rgb1_max].r; | |
1184 g1 = rgb[bg[pat].rgb1_max].g; | |
1185 b1 = rgb[bg[pat].rgb1_max].b; | |
1186 r2 = rgb[bg[pat].rgb2_max].r; | |
1187 g2 = rgb[bg[pat].rgb2_max].g; | |
1188 b2 = rgb[bg[pat].rgb2_max].b; | |
1189 | |
1190 for (row = 0; row < rpng2_info.height; ++row) { | |
1191 yidx = (int)(row % bgscale); | |
1192 if (yidx > hmax) | |
1193 yidx = bgscale-1 - yidx; | |
1194 dest = (char *)bg_data + row*bg_rowbytes; | |
1195 for (i = 0; i < rpng2_info.width; ++i) { | |
1196 xidx = (int)(i % bgscale); | |
1197 if (xidx > hmax) | |
1198 xidx = bgscale-1 - xidx; | |
1199 k = xidx + yidx; | |
1200 *dest++ = (k*r1 + (max-k)*r2) / max; | |
1201 *dest++ = (k*g1 + (max-k)*g2) / max; | |
1202 *dest++ = (k*b1 + (max-k)*b2) / max; | |
1203 } | |
1204 } | |
1205 | |
1206 /*--------------------------------------------------------------------------- | |
1207 Radial "starburst" with azimuthal sinusoids; [eventually number of sinu- | |
1208 soids will equal bgscale?]. This one is slow but very cool. Code con- | |
1209 tributed by Pieter S. van der Meulen (originally in Smalltalk). | |
1210 ---------------------------------------------------------------------------*/ | |
1211 | |
1212 } else if ((bg[pat].type & 0x07) == 2) { | |
1213 uch ch; | |
1214 int ii, x, y, hw, hh, grayspot; | |
1215 double freq, rotate, saturate, gray, intensity; | |
1216 double angle=0.0, aoffset=0.0, maxDist, dist; | |
1217 double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t; | |
1218 | |
1219 fprintf(stderr, "%s: computing radial background...", | |
1220 PROGNAME); | |
1221 fflush(stderr); | |
1222 | |
1223 hh = (int)(rpng2_info.height / 2); | |
1224 hw = (int)(rpng2_info.width / 2); | |
1225 | |
1226 /* variables for radial waves: | |
1227 * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED] | |
1228 * freq: number of color beams originating from the center | |
1229 * grayspot: size of the graying center area (anti-alias) | |
1230 * rotate: rotation of the beams as a function of radius | |
1231 * saturate: saturation of beams' shape azimuthally | |
1232 */ | |
1233 angle = CLIP(angle, 0.0, 360.0); | |
1234 grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw)); | |
1235 freq = MAX((double)bg[pat].bg_freq, 0.0); | |
1236 saturate = (double)bg[pat].bg_bsat * 0.1; | |
1237 rotate = (double)bg[pat].bg_brot * 0.1; | |
1238 gray = 0.0; | |
1239 intensity = 0.0; | |
1240 maxDist = (double)((hw*hw) + (hh*hh)); | |
1241 | |
1242 for (row = 0; row < rpng2_info.height; ++row) { | |
1243 y = (int)(row - hh); | |
1244 dest = (char *)bg_data + row*bg_rowbytes; | |
1245 for (i = 0; i < rpng2_info.width; ++i) { | |
1246 x = (int)(i - hw); | |
1247 angle = (x == 0)? PI_2 : atan((double)y / (double)x); | |
1248 gray = (double)MAX(ABS(y), ABS(x)) / grayspot; | |
1249 gray = MIN(1.0, gray); | |
1250 dist = (double)((x*x) + (y*y)) / maxDist; | |
1251 intensity = cos((angle+(rotate*dist*PI)) * freq) * | |
1252 gray * saturate; | |
1253 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5; | |
1254 hue = (angle + PI) * INV_PI_360 + aoffset; | |
1255 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh)); | |
1256 s = MIN(MAX(s,0.0), 1.0); | |
1257 v = MIN(MAX(intensity,0.0), 1.0); | |
1258 | |
1259 if (s == 0.0) { | |
1260 ch = (uch)(v * 255.0); | |
1261 *dest++ = ch; | |
1262 *dest++ = ch; | |
1263 *dest++ = ch; | |
1264 } else { | |
1265 if ((hue < 0.0) || (hue >= 360.0)) | |
1266 hue -= (((int)(hue / 360.0)) * 360.0); | |
1267 hue /= 60.0; | |
1268 ii = (int)hue; | |
1269 f = hue - (double)ii; | |
1270 p = (1.0 - s) * v; | |
1271 q = (1.0 - (s * f)) * v; | |
1272 t = (1.0 - (s * (1.0 - f))) * v; | |
1273 if (ii == 0) { red = v; green = t; blue = p; } | |
1274 else if (ii == 1) { red = q; green = v; blue = p; } | |
1275 else if (ii == 2) { red = p; green = v; blue = t; } | |
1276 else if (ii == 3) { red = p; green = q; blue = v; } | |
1277 else if (ii == 4) { red = t; green = p; blue = v; } | |
1278 else if (ii == 5) { red = v; green = p; blue = q; } | |
1279 *dest++ = (uch)(red * 255.0); | |
1280 *dest++ = (uch)(green * 255.0); | |
1281 *dest++ = (uch)(blue * 255.0); | |
1282 } | |
1283 } | |
1284 } | |
1285 fprintf(stderr, "done.\n"); | |
1286 fflush(stderr); | |
1287 } | |
1288 | |
1289 /*--------------------------------------------------------------------------- | |
1290 Blast background image to display buffer before beginning PNG decode. | |
1291 ---------------------------------------------------------------------------*/ | |
1292 | |
1293 if (depth == 24 || depth == 32) { | |
1294 ulg red, green, blue; | |
1295 int bpp = ximage->bits_per_pixel; | |
1296 | |
1297 for (row = 0; row < rpng2_info.height; ++row) { | |
1298 src = bg_data + row*bg_rowbytes; | |
1299 dest = ximage->data + row*ximage_rowbytes; | |
1300 if (bpp == 32) { /* slightly optimized version */ | |
1301 for (i = rpng2_info.width; i > 0; --i) { | |
1302 red = *src++; | |
1303 green = *src++; | |
1304 blue = *src++; | |
1305 pixel = (red << RShift) | | |
1306 (green << GShift) | | |
1307 (blue << BShift); | |
1308 /* recall that we set ximage->byte_order = MSBFirst above */ | |
1309 *dest++ = (char)((pixel >> 24) & 0xff); | |
1310 *dest++ = (char)((pixel >> 16) & 0xff); | |
1311 *dest++ = (char)((pixel >> 8) & 0xff); | |
1312 *dest++ = (char)( pixel & 0xff); | |
1313 } | |
1314 } else { | |
1315 for (i = rpng2_info.width; i > 0; --i) { | |
1316 red = *src++; | |
1317 green = *src++; | |
1318 blue = *src++; | |
1319 pixel = (red << RShift) | | |
1320 (green << GShift) | | |
1321 (blue << BShift); | |
1322 /* recall that we set ximage->byte_order = MSBFirst above */ | |
1323 /* GRR BUG? this assumes bpp == 24 & bits are packed low */ | |
1324 /* (probably need to use RShift, RMask, etc.) */ | |
1325 *dest++ = (char)((pixel >> 16) & 0xff); | |
1326 *dest++ = (char)((pixel >> 8) & 0xff); | |
1327 *dest++ = (char)( pixel & 0xff); | |
1328 } | |
1329 } | |
1330 } | |
1331 | |
1332 } else if (depth == 16) { | |
1333 ush red, green, blue; | |
1334 | |
1335 for (row = 0; row < rpng2_info.height; ++row) { | |
1336 src = bg_data + row*bg_rowbytes; | |
1337 dest = ximage->data + row*ximage_rowbytes; | |
1338 for (i = rpng2_info.width; i > 0; --i) { | |
1339 red = ((ush)(*src) << 8); ++src; | |
1340 green = ((ush)(*src) << 8); ++src; | |
1341 blue = ((ush)(*src) << 8); ++src; | |
1342 pixel = ((red >> RShift) & RMask) | | |
1343 ((green >> GShift) & GMask) | | |
1344 ((blue >> BShift) & BMask); | |
1345 /* recall that we set ximage->byte_order = MSBFirst above */ | |
1346 *dest++ = (char)((pixel >> 8) & 0xff); | |
1347 *dest++ = (char)( pixel & 0xff); | |
1348 } | |
1349 } | |
1350 | |
1351 } else /* depth == 8 */ { | |
1352 | |
1353 /* GRR: add 8-bit support */ | |
1354 | |
1355 } | |
1356 | |
1357 XPutImage(display, window, gc, ximage, 0, 0, 0, 0, rpng2_info.width, | |
1358 rpng2_info.height); | |
1359 | |
1360 return 0; | |
1361 | |
1362 } /* end function rpng2_x_load_bg_image() */ | |
1363 | |
1364 | |
1365 | |
1366 | |
1367 | |
1368 static void rpng2_x_display_row(ulg row) | |
1369 { | |
1370 uch bg_red = rpng2_info.bg_red; | |
1371 uch bg_green = rpng2_info.bg_green; | |
1372 uch bg_blue = rpng2_info.bg_blue; | |
1373 uch *src, *src2=NULL; | |
1374 char *dest; | |
1375 uch r, g, b, a; | |
1376 int ximage_rowbytes = ximage->bytes_per_line; | |
1377 ulg i, pixel; | |
1378 static int rows=0, prevpass=(-1); | |
1379 static ulg firstrow; | |
1380 | |
1381 /*--------------------------------------------------------------------------- | |
1382 rows and firstrow simply track how many rows (and which ones) have not | |
1383 yet been displayed; alternatively, we could call XPutImage() for every | |
1384 row and not bother with the records-keeping. | |
1385 ---------------------------------------------------------------------------*/ | |
1386 | |
1387 Trace((stderr, "beginning rpng2_x_display_row()\n")) | |
1388 | |
1389 if (rpng2_info.pass != prevpass) { | |
1390 if (pause_after_pass && rpng2_info.pass > 0) { | |
1391 XEvent e; | |
1392 KeySym k; | |
1393 | |
1394 fprintf(stderr, | |
1395 "%s: end of pass %d of 7; click in image window to continue\n", | |
1396 PROGNAME, prevpass + 1); | |
1397 do | |
1398 XNextEvent(display, &e); | |
1399 while (!QUIT(e,k)); | |
1400 } | |
1401 fprintf(stderr, "%s: pass %d of 7\r", PROGNAME, rpng2_info.pass + 1); | |
1402 fflush(stderr); | |
1403 prevpass = rpng2_info.pass; | |
1404 } | |
1405 | |
1406 if (rows == 0) | |
1407 firstrow = row; /* first row that is not yet displayed */ | |
1408 | |
1409 ++rows; /* count of rows received but not yet displayed */ | |
1410 | |
1411 /*--------------------------------------------------------------------------- | |
1412 Aside from the use of the rpng2_info struct, the lack of an outer loop | |
1413 (over rows) and moving the XPutImage() call outside the "if (depth)" | |
1414 tests, this routine is identical to rpng_x_display_image() in the non- | |
1415 progressive version of the program. | |
1416 ---------------------------------------------------------------------------*/ | |
1417 | |
1418 if (depth == 24 || depth == 32) { | |
1419 ulg red, green, blue; | |
1420 int bpp = ximage->bits_per_pixel; | |
1421 | |
1422 src = rpng2_info.image_data + row*rpng2_info.rowbytes; | |
1423 if (bg_image) | |
1424 src2 = bg_data + row*bg_rowbytes; | |
1425 dest = ximage->data + row*ximage_rowbytes; | |
1426 if (rpng2_info.channels == 3) { | |
1427 for (i = rpng2_info.width; i > 0; --i) { | |
1428 red = *src++; | |
1429 green = *src++; | |
1430 blue = *src++; | |
1431 pixel = (red << RShift) | | |
1432 (green << GShift) | | |
1433 (blue << BShift); | |
1434 /* recall that we set ximage->byte_order = MSBFirst above */ | |
1435 if (bpp == 32) { | |
1436 *dest++ = (char)((pixel >> 24) & 0xff); | |
1437 *dest++ = (char)((pixel >> 16) & 0xff); | |
1438 *dest++ = (char)((pixel >> 8) & 0xff); | |
1439 *dest++ = (char)( pixel & 0xff); | |
1440 } else { | |
1441 /* GRR BUG? this assumes bpp == 24 & bits are packed low */ | |
1442 /* (probably need to use RShift, RMask, etc.) */ | |
1443 *dest++ = (char)((pixel >> 16) & 0xff); | |
1444 *dest++ = (char)((pixel >> 8) & 0xff); | |
1445 *dest++ = (char)( pixel & 0xff); | |
1446 } | |
1447 } | |
1448 } else /* if (rpng2_info.channels == 4) */ { | |
1449 for (i = rpng2_info.width; i > 0; --i) { | |
1450 r = *src++; | |
1451 g = *src++; | |
1452 b = *src++; | |
1453 a = *src++; | |
1454 if (bg_image) { | |
1455 bg_red = *src2++; | |
1456 bg_green = *src2++; | |
1457 bg_blue = *src2++; | |
1458 } | |
1459 if (a == 255) { | |
1460 red = r; | |
1461 green = g; | |
1462 blue = b; | |
1463 } else if (a == 0) { | |
1464 red = bg_red; | |
1465 green = bg_green; | |
1466 blue = bg_blue; | |
1467 } else { | |
1468 /* this macro (from png.h) composites the foreground | |
1469 * and background values and puts the result into the | |
1470 * first argument */ | |
1471 alpha_composite(red, r, a, bg_red); | |
1472 alpha_composite(green, g, a, bg_green); | |
1473 alpha_composite(blue, b, a, bg_blue); | |
1474 } | |
1475 pixel = (red << RShift) | | |
1476 (green << GShift) | | |
1477 (blue << BShift); | |
1478 /* recall that we set ximage->byte_order = MSBFirst above */ | |
1479 if (bpp == 32) { | |
1480 *dest++ = (char)((pixel >> 24) & 0xff); | |
1481 *dest++ = (char)((pixel >> 16) & 0xff); | |
1482 *dest++ = (char)((pixel >> 8) & 0xff); | |
1483 *dest++ = (char)( pixel & 0xff); | |
1484 } else { | |
1485 /* GRR BUG? this assumes bpp == 24 & bits are packed low */ | |
1486 /* (probably need to use RShift, RMask, etc.) */ | |
1487 *dest++ = (char)((pixel >> 16) & 0xff); | |
1488 *dest++ = (char)((pixel >> 8) & 0xff); | |
1489 *dest++ = (char)( pixel & 0xff); | |
1490 } | |
1491 } | |
1492 } | |
1493 | |
1494 } else if (depth == 16) { | |
1495 ush red, green, blue; | |
1496 | |
1497 src = rpng2_info.row_pointers[row]; | |
1498 if (bg_image) | |
1499 src2 = bg_data + row*bg_rowbytes; | |
1500 dest = ximage->data + row*ximage_rowbytes; | |
1501 if (rpng2_info.channels == 3) { | |
1502 for (i = rpng2_info.width; i > 0; --i) { | |
1503 red = ((ush)(*src) << 8); | |
1504 ++src; | |
1505 green = ((ush)(*src) << 8); | |
1506 ++src; | |
1507 blue = ((ush)(*src) << 8); | |
1508 ++src; | |
1509 pixel = ((red >> RShift) & RMask) | | |
1510 ((green >> GShift) & GMask) | | |
1511 ((blue >> BShift) & BMask); | |
1512 /* recall that we set ximage->byte_order = MSBFirst above */ | |
1513 *dest++ = (char)((pixel >> 8) & 0xff); | |
1514 *dest++ = (char)( pixel & 0xff); | |
1515 } | |
1516 } else /* if (rpng2_info.channels == 4) */ { | |
1517 for (i = rpng2_info.width; i > 0; --i) { | |
1518 r = *src++; | |
1519 g = *src++; | |
1520 b = *src++; | |
1521 a = *src++; | |
1522 if (bg_image) { | |
1523 bg_red = *src2++; | |
1524 bg_green = *src2++; | |
1525 bg_blue = *src2++; | |
1526 } | |
1527 if (a == 255) { | |
1528 red = ((ush)r << 8); | |
1529 green = ((ush)g << 8); | |
1530 blue = ((ush)b << 8); | |
1531 } else if (a == 0) { | |
1532 red = ((ush)bg_red << 8); | |
1533 green = ((ush)bg_green << 8); | |
1534 blue = ((ush)bg_blue << 8); | |
1535 } else { | |
1536 /* this macro (from png.h) composites the foreground | |
1537 * and background values and puts the result back into | |
1538 * the first argument (== fg byte here: safe) */ | |
1539 alpha_composite(r, r, a, bg_red); | |
1540 alpha_composite(g, g, a, bg_green); | |
1541 alpha_composite(b, b, a, bg_blue); | |
1542 red = ((ush)r << 8); | |
1543 green = ((ush)g << 8); | |
1544 blue = ((ush)b << 8); | |
1545 } | |
1546 pixel = ((red >> RShift) & RMask) | | |
1547 ((green >> GShift) & GMask) | | |
1548 ((blue >> BShift) & BMask); | |
1549 /* recall that we set ximage->byte_order = MSBFirst above */ | |
1550 *dest++ = (char)((pixel >> 8) & 0xff); | |
1551 *dest++ = (char)( pixel & 0xff); | |
1552 } | |
1553 } | |
1554 | |
1555 } else /* depth == 8 */ { | |
1556 | |
1557 /* GRR: add 8-bit support */ | |
1558 | |
1559 } | |
1560 | |
1561 | |
1562 /*--------------------------------------------------------------------------- | |
1563 Display after every 16 rows or when on one of last two rows. (Region | |
1564 may include previously displayed lines due to interlacing--i.e., not | |
1565 contiguous. Also, second-to-last row is final one in interlaced images | |
1566 with odd number of rows.) For demos, flush (and delay) after every 16th | |
1567 row so "sparse" passes don't go twice as fast. | |
1568 ---------------------------------------------------------------------------*/ | |
1569 | |
1570 if (demo_timing && (row - firstrow >= 16 || row >= rpng2_info.height-2)) { | |
1571 XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0, | |
1572 (int)firstrow, rpng2_info.width, row - firstrow + 1); | |
1573 XFlush(display); | |
1574 rows = 0; | |
1575 usleep(usleep_duration); | |
1576 } else | |
1577 if (!demo_timing && ((rows & 0xf) == 0 || row >= rpng2_info.height-2)) { | |
1578 XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0, | |
1579 (int)firstrow, rpng2_info.width, row - firstrow + 1); | |
1580 XFlush(display); | |
1581 rows = 0; | |
1582 } | |
1583 | |
1584 } | |
1585 | |
1586 | |
1587 | |
1588 | |
1589 | |
1590 static void rpng2_x_finish_display(void) | |
1591 { | |
1592 Trace((stderr, "beginning rpng2_x_finish_display()\n")) | |
1593 | |
1594 /* last row has already been displayed by rpng2_x_display_row(), so we | |
1595 * have nothing to do here except set a flag and let the user know that | |
1596 * the image is done */ | |
1597 | |
1598 rpng2_info.state = kDone; | |
1599 printf( | |
1600 "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n"); | |
1601 fflush(stdout); | |
1602 } | |
1603 | |
1604 | |
1605 | |
1606 | |
1607 | |
1608 static void rpng2_x_redisplay_image(ulg startcol, ulg startrow, | |
1609 ulg width, ulg height) | |
1610 { | |
1611 uch bg_red = rpng2_info.bg_red; | |
1612 uch bg_green = rpng2_info.bg_green; | |
1613 uch bg_blue = rpng2_info.bg_blue; | |
1614 uch *src, *src2=NULL; | |
1615 char *dest; | |
1616 uch r, g, b, a; | |
1617 ulg i, row, lastrow = 0; | |
1618 ulg pixel; | |
1619 int ximage_rowbytes = ximage->bytes_per_line; | |
1620 | |
1621 | |
1622 Trace((stderr, "beginning display loop (image_channels == %d)\n", | |
1623 rpng2_info.channels)) | |
1624 Trace((stderr, " (width = %ld, rowbytes = %d, ximage_rowbytes = %d)\n", | |
1625 rpng2_info.width, rpng2_info.rowbytes, ximage_rowbytes)) | |
1626 Trace((stderr, " (bpp = %d)\n", ximage->bits_per_pixel)) | |
1627 Trace((stderr, " (byte_order = %s)\n", ximage->byte_order == MSBFirst? | |
1628 "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown"))) | |
1629 | |
1630 /*--------------------------------------------------------------------------- | |
1631 Aside from the use of the rpng2_info struct and of src2 (for background | |
1632 image), this routine is identical to rpng_x_display_image() in the non- | |
1633 progressive version of the program--for the simple reason that redisplay | |
1634 of the image against a new background happens after the image is fully | |
1635 decoded and therefore is, by definition, non-progressive. | |
1636 ---------------------------------------------------------------------------*/ | |
1637 | |
1638 if (depth == 24 || depth == 32) { | |
1639 ulg red, green, blue; | |
1640 int bpp = ximage->bits_per_pixel; | |
1641 | |
1642 for (lastrow = row = startrow; row < startrow+height; ++row) { | |
1643 src = rpng2_info.image_data + row*rpng2_info.rowbytes; | |
1644 if (bg_image) | |
1645 src2 = bg_data + row*bg_rowbytes; | |
1646 dest = ximage->data + row*ximage_rowbytes; | |
1647 if (rpng2_info.channels == 3) { | |
1648 for (i = rpng2_info.width; i > 0; --i) { | |
1649 red = *src++; | |
1650 green = *src++; | |
1651 blue = *src++; | |
1652 #ifdef NO_24BIT_MASKS | |
1653 pixel = (red << RShift) | | |
1654 (green << GShift) | | |
1655 (blue << BShift); | |
1656 /* recall that we set ximage->byte_order = MSBFirst above */ | |
1657 if (bpp == 32) { | |
1658 *dest++ = (char)((pixel >> 24) & 0xff); | |
1659 *dest++ = (char)((pixel >> 16) & 0xff); | |
1660 *dest++ = (char)((pixel >> 8) & 0xff); | |
1661 *dest++ = (char)( pixel & 0xff); | |
1662 } else { | |
1663 /* this assumes bpp == 24 & bits are packed low */ | |
1664 /* (probably need to use RShift, RMask, etc.) */ | |
1665 *dest++ = (char)((pixel >> 16) & 0xff); | |
1666 *dest++ = (char)((pixel >> 8) & 0xff); | |
1667 *dest++ = (char)( pixel & 0xff); | |
1668 } | |
1669 #else | |
1670 red = (RShift < 0)? red << (-RShift) : red >> RShift; | |
1671 green = (GShift < 0)? green << (-GShift) : green >> GShift; | |
1672 blue = (BShift < 0)? blue << (-BShift) : blue >> BShift; | |
1673 pixel = (red & RMask) | (green & GMask) | (blue & BMask); | |
1674 /* recall that we set ximage->byte_order = MSBFirst above */ | |
1675 if (bpp == 32) { | |
1676 *dest++ = (char)((pixel >> 24) & 0xff); | |
1677 *dest++ = (char)((pixel >> 16) & 0xff); | |
1678 *dest++ = (char)((pixel >> 8) & 0xff); | |
1679 *dest++ = (char)( pixel & 0xff); | |
1680 } else { | |
1681 /* GRR BUG */ | |
1682 /* this assumes bpp == 24 & bits are packed low */ | |
1683 /* (probably need to use RShift/RMask/etc. here, too) */ | |
1684 *dest++ = (char)((pixel >> 16) & 0xff); | |
1685 *dest++ = (char)((pixel >> 8) & 0xff); | |
1686 *dest++ = (char)( pixel & 0xff); | |
1687 } | |
1688 #endif | |
1689 } | |
1690 | |
1691 } else /* if (rpng2_info.channels == 4) */ { | |
1692 for (i = rpng2_info.width; i > 0; --i) { | |
1693 r = *src++; | |
1694 g = *src++; | |
1695 b = *src++; | |
1696 a = *src++; | |
1697 if (bg_image) { | |
1698 bg_red = *src2++; | |
1699 bg_green = *src2++; | |
1700 bg_blue = *src2++; | |
1701 } | |
1702 if (a == 255) { | |
1703 red = r; | |
1704 green = g; | |
1705 blue = b; | |
1706 } else if (a == 0) { | |
1707 red = bg_red; | |
1708 green = bg_green; | |
1709 blue = bg_blue; | |
1710 } else { | |
1711 /* this macro (from png.h) composites the foreground | |
1712 * and background values and puts the result into the | |
1713 * first argument */ | |
1714 alpha_composite(red, r, a, bg_red); | |
1715 alpha_composite(green, g, a, bg_green); | |
1716 alpha_composite(blue, b, a, bg_blue); | |
1717 } | |
1718 #ifdef NO_24BIT_MASKS | |
1719 pixel = (red << RShift) | | |
1720 (green << GShift) | | |
1721 (blue << BShift); | |
1722 /* recall that we set ximage->byte_order = MSBFirst above */ | |
1723 if (bpp == 32) { | |
1724 *dest++ = (char)((pixel >> 24) & 0xff); | |
1725 *dest++ = (char)((pixel >> 16) & 0xff); | |
1726 *dest++ = (char)((pixel >> 8) & 0xff); | |
1727 *dest++ = (char)( pixel & 0xff); | |
1728 } else { | |
1729 /* this assumes bpp == 24 & bits are packed low */ | |
1730 /* (probably need to use RShift, RMask, etc.) */ | |
1731 *dest++ = (char)((pixel >> 16) & 0xff); | |
1732 *dest++ = (char)((pixel >> 8) & 0xff); | |
1733 *dest++ = (char)( pixel & 0xff); | |
1734 } | |
1735 #else | |
1736 red = (RShift < 0)? red << (-RShift) : red >> RShift; | |
1737 green = (GShift < 0)? green << (-GShift) : green >> GShift; | |
1738 blue = (BShift < 0)? blue << (-BShift) : blue >> BShift; | |
1739 pixel = (red & RMask) | (green & GMask) | (blue & BMask); | |
1740 /* recall that we set ximage->byte_order = MSBFirst above */ | |
1741 if (bpp == 32) { | |
1742 *dest++ = (char)((pixel >> 24) & 0xff); | |
1743 *dest++ = (char)((pixel >> 16) & 0xff); | |
1744 *dest++ = (char)((pixel >> 8) & 0xff); | |
1745 *dest++ = (char)( pixel & 0xff); | |
1746 } else { | |
1747 /* GRR BUG */ | |
1748 /* this assumes bpp == 24 & bits are packed low */ | |
1749 /* (probably need to use RShift/RMask/etc. here, too) */ | |
1750 *dest++ = (char)((pixel >> 16) & 0xff); | |
1751 *dest++ = (char)((pixel >> 8) & 0xff); | |
1752 *dest++ = (char)( pixel & 0xff); | |
1753 } | |
1754 #endif | |
1755 } | |
1756 } | |
1757 /* display after every 16 lines */ | |
1758 if (((row+1) & 0xf) == 0) { | |
1759 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0, | |
1760 (int)lastrow, rpng2_info.width, 16); | |
1761 XFlush(display); | |
1762 lastrow = row + 1; | |
1763 } | |
1764 } | |
1765 | |
1766 } else if (depth == 16) { | |
1767 ush red, green, blue; | |
1768 | |
1769 for (lastrow = row = startrow; row < startrow+height; ++row) { | |
1770 src = rpng2_info.row_pointers[row]; | |
1771 if (bg_image) | |
1772 src2 = bg_data + row*bg_rowbytes; | |
1773 dest = ximage->data + row*ximage_rowbytes; | |
1774 if (rpng2_info.channels == 3) { | |
1775 for (i = rpng2_info.width; i > 0; --i) { | |
1776 red = ((ush)(*src) << 8); | |
1777 ++src; | |
1778 green = ((ush)(*src) << 8); | |
1779 ++src; | |
1780 blue = ((ush)(*src) << 8); | |
1781 ++src; | |
1782 pixel = ((red >> RShift) & RMask) | | |
1783 ((green >> GShift) & GMask) | | |
1784 ((blue >> BShift) & BMask); | |
1785 /* recall that we set ximage->byte_order = MSBFirst above */ | |
1786 *dest++ = (char)((pixel >> 8) & 0xff); | |
1787 *dest++ = (char)( pixel & 0xff); | |
1788 } | |
1789 } else /* if (rpng2_info.channels == 4) */ { | |
1790 for (i = rpng2_info.width; i > 0; --i) { | |
1791 r = *src++; | |
1792 g = *src++; | |
1793 b = *src++; | |
1794 a = *src++; | |
1795 if (bg_image) { | |
1796 bg_red = *src2++; | |
1797 bg_green = *src2++; | |
1798 bg_blue = *src2++; | |
1799 } | |
1800 if (a == 255) { | |
1801 red = ((ush)r << 8); | |
1802 green = ((ush)g << 8); | |
1803 blue = ((ush)b << 8); | |
1804 } else if (a == 0) { | |
1805 red = ((ush)bg_red << 8); | |
1806 green = ((ush)bg_green << 8); | |
1807 blue = ((ush)bg_blue << 8); | |
1808 } else { | |
1809 /* this macro (from png.h) composites the foreground | |
1810 * and background values and puts the result back into | |
1811 * the first argument (== fg byte here: safe) */ | |
1812 alpha_composite(r, r, a, bg_red); | |
1813 alpha_composite(g, g, a, bg_green); | |
1814 alpha_composite(b, b, a, bg_blue); | |
1815 red = ((ush)r << 8); | |
1816 green = ((ush)g << 8); | |
1817 blue = ((ush)b << 8); | |
1818 } | |
1819 pixel = ((red >> RShift) & RMask) | | |
1820 ((green >> GShift) & GMask) | | |
1821 ((blue >> BShift) & BMask); | |
1822 /* recall that we set ximage->byte_order = MSBFirst above */ | |
1823 *dest++ = (char)((pixel >> 8) & 0xff); | |
1824 *dest++ = (char)( pixel & 0xff); | |
1825 } | |
1826 } | |
1827 /* display after every 16 lines */ | |
1828 if (((row+1) & 0xf) == 0) { | |
1829 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0, | |
1830 (int)lastrow, rpng2_info.width, 16); | |
1831 XFlush(display); | |
1832 lastrow = row + 1; | |
1833 } | |
1834 } | |
1835 | |
1836 } else /* depth == 8 */ { | |
1837 | |
1838 /* GRR: add 8-bit support */ | |
1839 | |
1840 } | |
1841 | |
1842 Trace((stderr, "calling final XPutImage()\n")) | |
1843 if (lastrow < startrow+height) { | |
1844 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0, | |
1845 (int)lastrow, rpng2_info.width, rpng2_info.height-lastrow); | |
1846 XFlush(display); | |
1847 } | |
1848 | |
1849 } /* end function rpng2_x_redisplay_image() */ | |
1850 | |
1851 | |
1852 | |
1853 | |
1854 | |
1855 #ifdef FEATURE_LOOP | |
1856 | |
1857 static void rpng2_x_reload_bg_image(void) | |
1858 { | |
1859 char *dest; | |
1860 uch r1, r2, g1, g2, b1, b2; | |
1861 uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv; | |
1862 int k, hmax, max; | |
1863 int xidx, yidx, yidx_max; | |
1864 int even_odd_vert, even_odd_horiz, even_odd; | |
1865 int invert_gradient2 = (bg[pat].type & 0x08); | |
1866 int invert_column; | |
1867 ulg i, row; | |
1868 | |
1869 | |
1870 bgscale = (pat == 0)? 8 : bgscale_default; | |
1871 yidx_max = bgscale - 1; | |
1872 | |
1873 /*--------------------------------------------------------------------------- | |
1874 Vertical gradients (ramps) in NxN squares, alternating direction and | |
1875 colors (N == bgscale). | |
1876 ---------------------------------------------------------------------------*/ | |
1877 | |
1878 if ((bg[pat].type & 0x07) == 0) { | |
1879 uch r1_min = rgb[bg[pat].rgb1_min].r; | |
1880 uch g1_min = rgb[bg[pat].rgb1_min].g; | |
1881 uch b1_min = rgb[bg[pat].rgb1_min].b; | |
1882 uch r2_min = rgb[bg[pat].rgb2_min].r; | |
1883 uch g2_min = rgb[bg[pat].rgb2_min].g; | |
1884 uch b2_min = rgb[bg[pat].rgb2_min].b; | |
1885 int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min; | |
1886 int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min; | |
1887 int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min; | |
1888 int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min; | |
1889 int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min; | |
1890 int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min; | |
1891 | |
1892 for (row = 0; row < rpng2_info.height; ++row) { | |
1893 yidx = (int)(row % bgscale); | |
1894 even_odd_vert = (int)((row / bgscale) & 1); | |
1895 | |
1896 r1 = r1_min + (r1_diff * yidx) / yidx_max; | |
1897 g1 = g1_min + (g1_diff * yidx) / yidx_max; | |
1898 b1 = b1_min + (b1_diff * yidx) / yidx_max; | |
1899 r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max; | |
1900 g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max; | |
1901 b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max; | |
1902 | |
1903 r2 = r2_min + (r2_diff * yidx) / yidx_max; | |
1904 g2 = g2_min + (g2_diff * yidx) / yidx_max; | |
1905 b2 = b2_min + (b2_diff * yidx) / yidx_max; | |
1906 r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max; | |
1907 g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max; | |
1908 b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max; | |
1909 | |
1910 dest = (char *)bg_data + row*bg_rowbytes; | |
1911 for (i = 0; i < rpng2_info.width; ++i) { | |
1912 even_odd_horiz = (int)((i / bgscale) & 1); | |
1913 even_odd = even_odd_vert ^ even_odd_horiz; | |
1914 invert_column = | |
1915 (even_odd_horiz && (bg[pat].type & 0x10)); | |
1916 if (even_odd == 0) { /* gradient #1 */ | |
1917 if (invert_column) { | |
1918 *dest++ = r1_inv; | |
1919 *dest++ = g1_inv; | |
1920 *dest++ = b1_inv; | |
1921 } else { | |
1922 *dest++ = r1; | |
1923 *dest++ = g1; | |
1924 *dest++ = b1; | |
1925 } | |
1926 } else { /* gradient #2 */ | |
1927 if ((invert_column && invert_gradient2) || | |
1928 (!invert_column && !invert_gradient2)) | |
1929 { | |
1930 *dest++ = r2; /* not inverted or */ | |
1931 *dest++ = g2; /* doubly inverted */ | |
1932 *dest++ = b2; | |
1933 } else { | |
1934 *dest++ = r2_inv; | |
1935 *dest++ = g2_inv; /* singly inverted */ | |
1936 *dest++ = b2_inv; | |
1937 } | |
1938 } | |
1939 } | |
1940 } | |
1941 | |
1942 /*--------------------------------------------------------------------------- | |
1943 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam | |
1944 M. Costello. | |
1945 ---------------------------------------------------------------------------*/ | |
1946 | |
1947 } else if ((bg[pat].type & 0x07) == 1) { | |
1948 | |
1949 hmax = (bgscale-1)/2; /* half the max weight of a color */ | |
1950 max = 2*hmax; /* the max weight of a color */ | |
1951 | |
1952 r1 = rgb[bg[pat].rgb1_max].r; | |
1953 g1 = rgb[bg[pat].rgb1_max].g; | |
1954 b1 = rgb[bg[pat].rgb1_max].b; | |
1955 r2 = rgb[bg[pat].rgb2_max].r; | |
1956 g2 = rgb[bg[pat].rgb2_max].g; | |
1957 b2 = rgb[bg[pat].rgb2_max].b; | |
1958 | |
1959 for (row = 0; row < rpng2_info.height; ++row) { | |
1960 yidx = (int)(row % bgscale); | |
1961 if (yidx > hmax) | |
1962 yidx = bgscale-1 - yidx; | |
1963 dest = (char *)bg_data + row*bg_rowbytes; | |
1964 for (i = 0; i < rpng2_info.width; ++i) { | |
1965 xidx = (int)(i % bgscale); | |
1966 if (xidx > hmax) | |
1967 xidx = bgscale-1 - xidx; | |
1968 k = xidx + yidx; | |
1969 *dest++ = (k*r1 + (max-k)*r2) / max; | |
1970 *dest++ = (k*g1 + (max-k)*g2) / max; | |
1971 *dest++ = (k*b1 + (max-k)*b2) / max; | |
1972 } | |
1973 } | |
1974 | |
1975 /*--------------------------------------------------------------------------- | |
1976 Radial "starburst" with azimuthal sinusoids; [eventually number of sinu- | |
1977 soids will equal bgscale?]. This one is slow but very cool. Code con- | |
1978 tributed by Pieter S. van der Meulen (originally in Smalltalk). | |
1979 ---------------------------------------------------------------------------*/ | |
1980 | |
1981 } else if ((bg[pat].type & 0x07) == 2) { | |
1982 uch ch; | |
1983 int ii, x, y, hw, hh, grayspot; | |
1984 double freq, rotate, saturate, gray, intensity; | |
1985 double angle=0.0, aoffset=0.0, maxDist, dist; | |
1986 double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t; | |
1987 | |
1988 hh = (int)(rpng2_info.height / 2); | |
1989 hw = (int)(rpng2_info.width / 2); | |
1990 | |
1991 /* variables for radial waves: | |
1992 * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED] | |
1993 * freq: number of color beams originating from the center | |
1994 * grayspot: size of the graying center area (anti-alias) | |
1995 * rotate: rotation of the beams as a function of radius | |
1996 * saturate: saturation of beams' shape azimuthally | |
1997 */ | |
1998 angle = CLIP(angle, 0.0, 360.0); | |
1999 grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw)); | |
2000 freq = MAX((double)bg[pat].bg_freq, 0.0); | |
2001 saturate = (double)bg[pat].bg_bsat * 0.1; | |
2002 rotate = (double)bg[pat].bg_brot * 0.1; | |
2003 gray = 0.0; | |
2004 intensity = 0.0; | |
2005 maxDist = (double)((hw*hw) + (hh*hh)); | |
2006 | |
2007 for (row = 0; row < rpng2_info.height; ++row) { | |
2008 y = (int)(row - hh); | |
2009 dest = (char *)bg_data + row*bg_rowbytes; | |
2010 for (i = 0; i < rpng2_info.width; ++i) { | |
2011 x = (int)(i - hw); | |
2012 angle = (x == 0)? PI_2 : atan((double)y / (double)x); | |
2013 gray = (double)MAX(ABS(y), ABS(x)) / grayspot; | |
2014 gray = MIN(1.0, gray); | |
2015 dist = (double)((x*x) + (y*y)) / maxDist; | |
2016 intensity = cos((angle+(rotate*dist*PI)) * freq) * | |
2017 gray * saturate; | |
2018 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5; | |
2019 hue = (angle + PI) * INV_PI_360 + aoffset; | |
2020 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh)); | |
2021 s = MIN(MAX(s,0.0), 1.0); | |
2022 v = MIN(MAX(intensity,0.0), 1.0); | |
2023 | |
2024 if (s == 0.0) { | |
2025 ch = (uch)(v * 255.0); | |
2026 *dest++ = ch; | |
2027 *dest++ = ch; | |
2028 *dest++ = ch; | |
2029 } else { | |
2030 if ((hue < 0.0) || (hue >= 360.0)) | |
2031 hue -= (((int)(hue / 360.0)) * 360.0); | |
2032 hue /= 60.0; | |
2033 ii = (int)hue; | |
2034 f = hue - (double)ii; | |
2035 p = (1.0 - s) * v; | |
2036 q = (1.0 - (s * f)) * v; | |
2037 t = (1.0 - (s * (1.0 - f))) * v; | |
2038 if (ii == 0) { red = v; green = t; blue = p; } | |
2039 else if (ii == 1) { red = q; green = v; blue = p; } | |
2040 else if (ii == 2) { red = p; green = v; blue = t; } | |
2041 else if (ii == 3) { red = p; green = q; blue = v; } | |
2042 else if (ii == 4) { red = t; green = p; blue = v; } | |
2043 else if (ii == 5) { red = v; green = p; blue = q; } | |
2044 *dest++ = (uch)(red * 255.0); | |
2045 *dest++ = (uch)(green * 255.0); | |
2046 *dest++ = (uch)(blue * 255.0); | |
2047 } | |
2048 } | |
2049 } | |
2050 } | |
2051 | |
2052 } /* end function rpng2_x_reload_bg_image() */ | |
2053 | |
2054 | |
2055 | |
2056 | |
2057 | |
2058 static int is_number(char *p) | |
2059 { | |
2060 while (*p) { | |
2061 if (!isdigit(*p)) | |
2062 return FALSE; | |
2063 ++p; | |
2064 } | |
2065 return TRUE; | |
2066 } | |
2067 | |
2068 #endif /* FEATURE_LOOP */ | |
2069 | |
2070 | |
2071 | |
2072 | |
2073 | |
2074 static void rpng2_x_cleanup(void) | |
2075 { | |
2076 if (bg_image && bg_data) { | |
2077 free(bg_data); | |
2078 bg_data = NULL; | |
2079 } | |
2080 | |
2081 if (rpng2_info.image_data) { | |
2082 free(rpng2_info.image_data); | |
2083 rpng2_info.image_data = NULL; | |
2084 } | |
2085 | |
2086 if (rpng2_info.row_pointers) { | |
2087 free(rpng2_info.row_pointers); | |
2088 rpng2_info.row_pointers = NULL; | |
2089 } | |
2090 | |
2091 if (ximage) { | |
2092 if (ximage->data) { | |
2093 free(ximage->data); /* we allocated it, so we free it */ | |
2094 ximage->data = (char *)NULL; /* instead of XDestroyImage() */ | |
2095 } | |
2096 XDestroyImage(ximage); | |
2097 ximage = NULL; | |
2098 } | |
2099 | |
2100 if (have_gc) | |
2101 XFreeGC(display, gc); | |
2102 | |
2103 if (have_window) | |
2104 XDestroyWindow(display, window); | |
2105 | |
2106 if (have_colormap) | |
2107 XFreeColormap(display, colormap); | |
2108 | |
2109 if (have_nondefault_visual) | |
2110 XFree(visual_list); | |
2111 } | |
2112 | |
2113 | |
2114 | |
2115 | |
2116 | |
2117 static int rpng2_x_msb(ulg u32val) | |
2118 { | |
2119 int i; | |
2120 | |
2121 for (i = 31; i >= 0; --i) { | |
2122 if (u32val & 0x80000000L) | |
2123 break; | |
2124 u32val <<= 1; | |
2125 } | |
2126 return i; | |
2127 } |