1
/*---------------------------------------------------------------------------
3
rpng2 - progressive-model PNG display program rpng2-x.c
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).
12
Thanks to Adam Costello and Pieter S. van der Meulen for the "diamond"
13
and "radial waves" patterns, respectively.
17
- finish resizable checkerboard-gradient (sizes 4-128?)
18
- use %.1023s to simplify truncation of title-bar string?
20
---------------------------------------------------------------------------
23
- 1.01: initial public release
24
- 1.02: modified to allow abbreviated options; fixed char/uchar mismatch
25
- 1.10: added support for non-default visuals; fixed X pixel-conversion
26
- 1.11: added -usleep option for demos; fixed command-line parsing bug
27
- 1.12: added -pause option for demos and testing
29
---------------------------------------------------------------------------
31
Copyright (c) 1998-2000 Greg Roelofs. All rights reserved.
33
This software is provided "as is," without warranty of any kind,
34
express or implied. In no event shall the author or contributors
35
be held liable for any damages arising in any way from the use of
38
Permission is granted to anyone to use this software for any purpose,
39
including commercial applications, and to alter it and redistribute
40
it freely, subject to the following restrictions:
42
1. Redistributions of source code must retain the above copyright
43
notice, disclaimer, and this list of conditions.
44
2. Redistributions in binary form must reproduce the above copyright
45
notice, disclaimer, and this list of conditions in the documenta-
46
tion and/or other materials provided with the distribution.
47
3. All advertising materials mentioning features or use of this
48
software must display the following acknowledgment:
50
This product includes software developed by Greg Roelofs
51
and contributors for the book, "PNG: The Definitive Guide,"
52
published by O'Reilly and Associates.
54
---------------------------------------------------------------------------*/
56
#define PROGNAME "rpng2-x"
57
#define LONGNAME "Progressive PNG Viewer for X"
58
#define VERSION "1.12 of 19 March 2000"
63
#include <setjmp.h> /* for jmpbuf declaration in readpng2.h */
65
#include <math.h> /* only for PvdM background code */
67
#include <X11/Xutil.h>
69
#include <X11/keysym.h> /* defines XK_* macros */
75
/* all for PvdM background code: */
77
# define PI 3.141592653589793238
80
#define INV_PI_360 (360.0 / PI)
81
#define MAX(a,b) (a>b?a:b)
82
#define MIN(a,b) (a<b?a:b)
83
#define CLIP(a,min,max) MAX(min,MIN((a),max))
84
#define ABS(a) ((a)<0?-(a):(a))
85
#define CLIP8P(c) MAX(0,(MIN((c),255))) /* 8-bit pos. integer (uch) */
86
#define ROUNDF(f) ((int)(f + 0.5))
88
#define rgb1_max bg_freq
89
#define rgb1_min bg_gray
90
#define rgb2_max bg_bsat
91
#define rgb2_min bg_brot
93
/* #define DEBUG */ /* this enables the Trace() macros */
95
#include "readpng2.h" /* typedefs, common macros, readpng2 prototypes */
98
/* could just include png.h, but this macro is the only thing we need
99
* (name and typedefs changed to local versions); note that side effects
100
* only happen with alpha (which could easily be avoided with
101
* "ush acopy = (alpha);") */
103
#define alpha_composite(composite, fg, alpha, bg) { \
104
ush temp = ((ush)(fg)*(ush)(alpha) + \
105
(ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128); \
106
(composite) = (uch)((temp + (temp >> 8)) >> 8); \
110
#define INBUFSIZE 4096 /* with pseudo-timing on (1 sec delay/block), this
111
* block size corresponds roughly to a download
112
* speed 10% faster than theoretical 33.6K maximum
113
* (assuming 8 data bits, 1 stop bit and no other
116
/* local prototypes */
117
static void rpng2_x_init(void);
118
static int rpng2_x_create_window(void);
119
static int rpng2_x_load_bg_image(void);
120
static void rpng2_x_display_row(ulg row);
121
static void rpng2_x_finish_display(void);
122
static void rpng2_x_cleanup(void);
123
static int rpng2_x_msb(ulg u32val);
126
static char titlebar[1024], *window_name = titlebar;
127
static char *appname = LONGNAME;
128
static char *icon_name = PROGNAME;
129
static char *filename;
132
static mainprog_info rpng2_info;
134
static uch inbuf[INBUFSIZE];
137
static int pat = 6; /* must be less than num_bgpat */
138
static int bg_image = 0;
139
static int bgscale = 16;
140
static ulg bg_rowbytes;
143
int pause_after_pass = FALSE;
144
int demo_timing = FALSE;
145
ulg usleep_duration = 0L;
147
static struct rgb_color {
150
{ 0, 0, 0}, /* 0: black */
151
{255, 255, 255}, /* 1: white */
152
{173, 132, 57}, /* 2: tan */
153
{ 64, 132, 0}, /* 3: medium green */
154
{189, 117, 1}, /* 4: gold */
155
{253, 249, 1}, /* 5: yellow */
156
{ 0, 0, 255}, /* 6: blue */
157
{ 0, 0, 120}, /* 7: medium blue */
158
{255, 0, 255}, /* 8: magenta */
159
{ 64, 0, 64}, /* 9: dark magenta */
160
{255, 0, 0}, /* 10: red */
161
{ 64, 0, 0}, /* 11: dark red */
162
{255, 127, 0}, /* 12: orange */
163
{192, 96, 0}, /* 13: darker orange */
164
{ 24, 60, 0}, /* 14: dark green-yellow */
165
{ 85, 125, 200} /* 15: ice blue */
167
/* not used for now, but should be for error-checking:
168
static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
172
This whole struct is a fairly cheesy way to keep the number of
173
command-line options to a minimum. The radial-waves background
174
type is a particularly poor fit to the integer elements of the
175
struct...but a few macros and a little fixed-point math will do
179
F E D C B A 9 8 7 6 5 4 3 2 1 0
181
| | +-+-+-- 0 = sharp-edged checkerboard
182
| | 1 = soft diamonds
185
| +-- gradient #2 inverted?
186
+-- alternating columns inverted?
188
static struct background_pattern {
190
int rgb1_max, rgb1_min; /* or bg_freq, bg_gray */
191
int rgb2_max, rgb2_min; /* or bg_bsat, bg_brot (both scaled by 10)*/
193
{0+8, 2,0, 1,15}, /* checkered: tan/black vs. white/ice blue */
194
{0+24, 2,0, 1,0}, /* checkered: tan/black vs. white/black */
195
{0+8, 4,5, 0,2}, /* checkered: gold/yellow vs. black/tan */
196
{0+8, 4,5, 0,6}, /* checkered: gold/yellow vs. black/blue */
197
{0, 7,0, 8,9}, /* checkered: deep blue/black vs. magenta */
198
{0+8, 13,0, 5,14}, /* checkered: orange/black vs. yellow */
199
{0+8, 12,0, 10,11}, /* checkered: orange/black vs. red */
200
{1, 7,0, 8,0}, /* diamonds: deep blue/black vs. magenta */
201
{1, 12,0, 11,0}, /* diamonds: orange vs. dark red */
202
{1, 10,0, 7,0}, /* diamonds: red vs. medium blue */
203
{1, 4,0, 5,0}, /* diamonds: gold vs. yellow */
204
{1, 3,0, 0,0}, /* diamonds: medium green vs. black */
205
{2, 16, 100, 20, 0}, /* radial: ~hard radial color-beams */
206
{2, 18, 100, 10, 2}, /* radial: soft, curved radial color-beams */
207
{2, 16, 256, 100, 250}, /* radial: very tight spiral */
208
{2, 10000, 256, 11, 0} /* radial: dipole-moire' (almost fractal) */
210
static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
213
/* X-specific variables */
214
static char *displayname;
215
static XImage *ximage;
216
static Display *display;
218
static Visual *visual;
219
static XVisualInfo *visual_list;
220
static int RShift, GShift, BShift;
221
static ulg RMask, GMask, BMask;
222
static Window window;
224
static Colormap colormap;
226
static int have_nondefault_visual = FALSE;
227
static int have_colormap = FALSE;
228
static int have_window = FALSE;
233
int main(int argc, char **argv)
238
char *p, *bgstr = NULL;
243
double LUT_exponent; /* just the lookup table */
244
double CRT_exponent = 2.2; /* just the monitor */
245
double default_display_exponent; /* whole display system */
250
/* First initialize a few things, just to be sure--memset takes care of
251
* default background color (black), booleans (FALSE), pointers (NULL),
254
displayname = (char *)NULL;
255
filename = (char *)NULL;
256
memset(&rpng2_info, 0, sizeof(mainprog_info));
259
/* Set the default value for our display-system exponent, i.e., the
260
* product of the CRT exponent and the exponent corresponding to
261
* the frame-buffer's lookup table (LUT), if any. This is not an
262
* exhaustive list of LUT values (e.g., OpenStep has a lot of weird
263
* ones), but it should cover 99% of the current possibilities. */
266
/* third-party utilities can modify the default LUT exponent */
267
LUT_exponent = 1.0 / 2.2;
269
if (some_next_function_that_returns_gamma(&next_gamma))
270
LUT_exponent = 1.0 / next_gamma;
273
LUT_exponent = 1.0 / 1.7;
274
/* there doesn't seem to be any documented function to
275
* get the "gamma" value, so we do it the hard way */
276
infile = fopen("/etc/config/system.glGammaVal", "r");
280
fgets(tmpline, 80, infile);
282
sgi_gamma = atof(tmpline);
284
LUT_exponent = 1.0 / sgi_gamma;
286
#elif defined(Macintosh)
287
LUT_exponent = 1.8 / 2.61;
289
if (some_mac_function_that_returns_gamma(&mac_gamma))
290
LUT_exponent = mac_gamma / 2.61;
293
LUT_exponent = 1.0; /* assume no LUT: most PCs */
296
/* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
297
default_display_exponent = LUT_exponent * CRT_exponent;
300
/* If the user has set the SCREEN_GAMMA environment variable as suggested
301
* (somewhat imprecisely) in the libpng documentation, use that; otherwise
302
* use the default value we just calculated. Either way, the user may
303
* override this via a command-line option. */
305
if ((p = getenv("SCREEN_GAMMA")) != NULL)
306
rpng2_info.display_exponent = atof(p);
308
rpng2_info.display_exponent = default_display_exponent;
311
/* Now parse the command line for options and the PNG filename. */
313
while (*++argv && !error) {
314
if (!strncmp(*argv, "-display", 2)) {
319
} else if (!strncmp(*argv, "-gamma", 2)) {
323
rpng2_info.display_exponent = atof(*argv);
324
if (rpng2_info.display_exponent <= 0.0)
327
} else if (!strncmp(*argv, "-bgcolor", 4)) {
332
if (strlen(bgstr) != 7 || bgstr[0] != '#')
339
} else if (!strncmp(*argv, "-bgpat", 4)) {
343
pat = atoi(*argv) - 1;
344
if (pat < 0 || pat >= num_bgpat)
351
} else if (!strncmp(*argv, "-usleep", 2)) {
355
usleep_duration = (ulg)atol(*argv);
358
} else if (!strncmp(*argv, "-pause", 2)) {
359
pause_after_pass = TRUE;
360
} else if (!strncmp(*argv, "-timing", 2)) {
365
if (argv[1]) /* shouldn't be any more args after filename */
368
++error; /* not expecting any other options */
374
} else if (!(infile = fopen(filename, "rb"))) {
375
fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename);
378
incount = fread(inbuf, 1, INBUFSIZE, infile);
379
if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
380
fprintf(stderr, PROGNAME
381
": [%s] is not a PNG file: incorrect signature\n",
384
} else if ((rc = readpng2_init(&rpng2_info)) != 0) {
387
fprintf(stderr, PROGNAME
388
": [%s] has bad IHDR (libpng longjmp)\n",
392
fprintf(stderr, PROGNAME ": insufficient memory\n");
395
fprintf(stderr, PROGNAME
396
": unknown readpng2_init() error\n");
401
display = XOpenDisplay(displayname);
403
readpng2_cleanup(&rpng2_info);
404
fprintf(stderr, PROGNAME ": can't open X display [%s]\n",
405
displayname? displayname : "default");
417
fprintf(stderr, "\n%s %s: %s\n", PROGNAME, VERSION, appname);
418
readpng2_version_info();
420
"Usage: %s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n"
421
" %*s [-usleep dur | -timing] [-pause] file.png\n\n"
422
" xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
423
" exp \ttransfer-function exponent (``gamma'') of the display\n"
424
"\t\t system in floating-point format (e.g., ``%.1f''); equal\n"
425
"\t\t to the product of the lookup-table exponent (varies)\n"
426
"\t\t and the CRT exponent (usually 2.2); must be positive\n"
427
" bg \tdesired background color in 7-character hex RGB format\n"
428
"\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n"
429
"\t\t used with transparent images; overrides -bgpat\n"
430
" pat \tdesired background pattern number (1-%d); used with\n"
431
"\t\t transparent images; overrides -bgcolor\n"
432
" dur \tduration in microseconds to wait after displaying each\n"
433
"\t\t row (for demo purposes)\n"
434
" -timing\tenables delay for every block read, to simulate modem\n"
435
"\t\t download of image (~36 Kbps)\n"
436
" -pause\tpauses after displaying each pass until key pressed\n"
437
"\nPress Q, Esc or mouse button 1 (within image window, after image\n"
438
"is displayed) to quit.\n"
439
"\n", PROGNAME, strlen(PROGNAME), " ", default_display_exponent,
445
/* set the title-bar string, but make sure buffer doesn't overflow */
447
alen = strlen(appname);
448
flen = strlen(filename);
449
if (alen + flen + 3 > 1023)
450
sprintf(titlebar, "%s: ...%s", appname, filename+(alen+flen+6-1023));
452
sprintf(titlebar, "%s: %s", appname, filename);
455
/* set some final rpng2_info variables before entering main data loop */
458
unsigned r, g, b; /* this approach quiets compiler warnings */
460
sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
461
rpng2_info.bg_red = (uch)r;
462
rpng2_info.bg_green = (uch)g;
463
rpng2_info.bg_blue = (uch)b;
465
rpng2_info.need_bgcolor = TRUE;
467
rpng2_info.done = FALSE;
468
rpng2_info.mainprog_init = rpng2_x_init;
469
rpng2_info.mainprog_display_row = rpng2_x_display_row;
470
rpng2_info.mainprog_finish_display = rpng2_x_finish_display;
473
/* OK, this is the fun part: call readpng2_decode_data() at the start of
474
* the loop to deal with our first buffer of data (read in above to verify
475
* that the file is a PNG image), then loop through the file and continue
476
* calling the same routine to handle each chunk of data. It in turn
477
* passes the data to libpng, which will invoke one or more of our call-
478
* backs as decoded data become available. We optionally call sleep() for
479
* one second per iteration to simulate downloading the image via an analog
483
Trace((stderr, "about to call readpng2_decode_data()\n"))
484
if (readpng2_decode_data(&rpng2_info, inbuf, incount))
486
Trace((stderr, "done with readpng2_decode_data()\n"))
487
if (error || feof(infile) || rpng2_info.done)
491
incount = fread(inbuf, 1, INBUFSIZE, infile);
495
/* clean up PNG stuff and report any decoding errors */
498
Trace((stderr, "about to call readpng2_cleanup()\n"))
499
readpng2_cleanup(&rpng2_info);
502
fprintf(stderr, PROGNAME ": libpng error while decoding PNG image\n");
507
/* wait for the user to tell us when to quit */
510
XNextEvent(display, &e);
511
while (!(e.type == ButtonPress && e.xbutton.button == Button1) &&
512
!(e.type == KeyPress && /* v--- or 1 for shifted keys */
513
((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape) ));
516
/* we're done: clean up all image and X resources and go away */
518
Trace((stderr, "about to call rpng2_x_cleanup()\n"))
528
/* this function is called by readpng2_info_callback() in readpng2.c, which
529
* in turn is called by libpng after all of the pre-IDAT chunks have been
530
* read and processed--i.e., we now have enough info to finish initializing */
532
static void rpng2_x_init(void)
535
ulg rowbytes = rpng2_info.rowbytes;
537
Trace((stderr, "beginning rpng2_x_init()\n"))
538
Trace((stderr, " rowbytes = %ld\n", rpng2_info.rowbytes))
539
Trace((stderr, " width = %ld\n", rpng2_info.width))
540
Trace((stderr, " height = %ld\n", rpng2_info.height))
542
rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
543
if (!rpng2_info.image_data) {
544
readpng2_cleanup(&rpng2_info);
548
rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
549
if (!rpng2_info.row_pointers) {
550
free(rpng2_info.image_data);
551
rpng2_info.image_data = NULL;
552
readpng2_cleanup(&rpng2_info);
556
for (i = 0; i < rpng2_info.height; ++i)
557
rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
560
/* do the basic X initialization stuff, make the window, and fill it with
561
* the user-specified, file-specified or default background color or
564
if (rpng2_x_create_window()) {
565
readpng2_cleanup(&rpng2_info);
574
static int rpng2_x_create_window(void)
576
ulg bg_red = rpng2_info.bg_red;
577
ulg bg_green = rpng2_info.bg_green;
578
ulg bg_blue = rpng2_info.bg_blue;
581
int need_colormap = FALSE;
587
XSetWindowAttributes attr;
588
XSizeHints *size_hints;
589
XTextProperty windowName, *pWindowName = &windowName;
590
XTextProperty iconName, *pIconName = &iconName;
591
XVisualInfo visual_info;
595
Trace((stderr, "beginning rpng2_x_create_window()\n"))
597
screen = DefaultScreen(display);
598
depth = DisplayPlanes(display, screen);
599
root = RootWindow(display, screen);
602
XSynchronize(display, True);
605
if (depth != 16 && depth != 24 && depth != 32) {
606
int visuals_matched = 0;
608
Trace((stderr, "default depth is %d: checking other visuals\n",
612
visual_info.screen = screen;
613
visual_info.depth = 24;
614
visual_list = XGetVisualInfo(display,
615
VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched);
616
if (visuals_matched == 0) {
617
/* GRR: add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */
618
fprintf(stderr, "default screen depth %d not supported, and no"
619
" 24-bit visuals found\n", depth);
622
Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n",
624
visual = visual_list[0].visual;
625
depth = visual_list[0].depth;
627
colormap_size = visual_list[0].colormap_size;
628
visual_class = visual->class;
629
visualID = XVisualIDFromVisual(visual);
631
have_nondefault_visual = TRUE;
632
need_colormap = TRUE;
634
XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info);
635
visual = visual_info.visual;
638
RMask = visual->red_mask;
639
GMask = visual->green_mask;
640
BMask = visual->blue_mask;
642
/* GRR: add/check 8-bit support */
643
if (depth == 8 || need_colormap) {
644
colormap = XCreateColormap(display, root, visual, AllocNone);
646
fprintf(stderr, "XCreateColormap() failed\n");
649
have_colormap = TRUE;
651
bg_image = FALSE; /* gradient just wastes palette entries */
653
if (depth == 15 || depth == 16) {
654
RShift = 15 - rpng2_x_msb(RMask); /* these are right-shifts */
655
GShift = 15 - rpng2_x_msb(GMask);
656
BShift = 15 - rpng2_x_msb(BMask);
657
} else if (depth > 16) {
658
RShift = rpng2_x_msb(RMask) - 7; /* these are left-shifts */
659
GShift = rpng2_x_msb(GMask) - 7;
660
BShift = rpng2_x_msb(BMask) - 7;
662
if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) {
663
fprintf(stderr, "rpng2 internal logic error: negative X shift(s)!\n");
667
/*---------------------------------------------------------------------------
668
Finally, create the window.
669
---------------------------------------------------------------------------*/
671
attr.backing_store = Always;
672
attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
673
attrmask = CWBackingStore | CWEventMask;
674
if (have_nondefault_visual) {
675
attr.colormap = colormap;
676
attr.background_pixel = 0;
677
attr.border_pixel = 1;
678
attrmask |= CWColormap | CWBackPixel | CWBorderPixel;
681
window = XCreateWindow(display, root, 0, 0, rpng2_info.width,
682
rpng2_info.height, 0, depth, InputOutput, visual, attrmask, &attr);
684
if (window == None) {
685
fprintf(stderr, "XCreateWindow() failed\n");
691
XSetWindowColormap(display, window, colormap);
693
if (!XStringListToTextProperty(&window_name, 1, pWindowName))
695
if (!XStringListToTextProperty(&icon_name, 1, pIconName))
698
/* OK if either hints allocation fails; XSetWMProperties() allows NULLs */
700
if ((size_hints = XAllocSizeHints()) != NULL) {
701
/* window will not be resizable */
702
size_hints->flags = PMinSize | PMaxSize;
703
size_hints->min_width = size_hints->max_width = (int)rpng2_info.width;
704
size_hints->min_height = size_hints->max_height =
705
(int)rpng2_info.height;
708
if ((wm_hints = XAllocWMHints()) != NULL) {
709
wm_hints->initial_state = NormalState;
710
wm_hints->input = True;
711
/* wm_hints->icon_pixmap = icon_pixmap; */
712
wm_hints->flags = StateHint | InputHint /* | IconPixmapHint */ ;
715
XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
716
size_hints, wm_hints, NULL);
718
XMapWindow(display, window);
720
gc = XCreateGC(display, window, 0, &gcvalues);
722
/*---------------------------------------------------------------------------
723
Allocate memory for the X- and display-specific version of the image.
724
---------------------------------------------------------------------------*/
726
if (depth == 24 || depth == 32) {
727
xdata = (uch *)malloc(4*rpng2_info.width*rpng2_info.height);
729
} else if (depth == 16) {
730
xdata = (uch *)malloc(2*rpng2_info.width*rpng2_info.height);
732
} else /* depth == 8 */ {
733
xdata = (uch *)malloc(rpng2_info.width*rpng2_info.height);
738
fprintf(stderr, PROGNAME ": unable to allocate image memory\n");
742
ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
743
(char *)xdata, rpng2_info.width, rpng2_info.height, pad, 0);
746
fprintf(stderr, PROGNAME ": XCreateImage() failed\n");
751
/* to avoid testing the byte order every pixel (or doubling the size of
752
* the drawing routine with a giant if-test), we arbitrarily set the byte
753
* order to MSBFirst and let Xlib worry about inverting things on little-
754
* endian machines (e.g., Linux/x86, old VAXen, etc.)--this is not the
755
* most efficient approach (the giant if-test would be better), but in
756
* the interest of clarity, we'll take the easy way out... */
758
ximage->byte_order = MSBFirst;
760
/*---------------------------------------------------------------------------
761
Fill window with the specified background color (default is black) or
762
faked "background image" (but latter is disabled if 8-bit; gradients
763
just waste palette entries).
764
---------------------------------------------------------------------------*/
767
rpng2_x_load_bg_image(); /* resets bg_image if fails */
770
if (depth == 24 || depth == 32) {
771
bg_pixel = (bg_red << RShift) |
772
(bg_green << GShift) |
774
} else if (depth == 16) {
775
bg_pixel = (((bg_red << 8) >> RShift) & RMask) |
776
(((bg_green << 8) >> GShift) & GMask) |
777
(((bg_blue << 8) >> BShift) & BMask);
778
} else /* depth == 8 */ {
780
/* GRR: add 8-bit support */
783
XSetForeground(display, gc, bg_pixel);
784
XFillRectangle(display, window, gc, 0, 0, rpng2_info.width,
788
/*---------------------------------------------------------------------------
789
Wait for first Expose event to do any drawing, then flush and return.
790
---------------------------------------------------------------------------*/
793
XNextEvent(display, &e);
794
while (e.type != Expose || e.xexpose.count);
800
} /* end function rpng2_x_create_window() */
806
static int rpng2_x_load_bg_image(void)
810
uch r1, r2, g1, g2, b1, b2;
811
uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
813
int xidx, yidx, yidx_max = (bgscale-1);
814
int even_odd_vert, even_odd_horiz, even_odd;
815
int invert_gradient2 = (bg[pat].type & 0x08);
817
int ximage_rowbytes = ximage->bytes_per_line;
821
/*---------------------------------------------------------------------------
822
Allocate buffer for fake background image to be used with transparent
823
images; if this fails, revert to plain background color.
824
---------------------------------------------------------------------------*/
826
bg_rowbytes = 3 * rpng2_info.width;
827
bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
829
fprintf(stderr, PROGNAME
830
": unable to allocate memory for background image\n");
835
/*---------------------------------------------------------------------------
836
Vertical gradients (ramps) in NxN squares, alternating direction and
837
colors (N == bgscale).
838
---------------------------------------------------------------------------*/
840
if ((bg[pat].type & 0x07) == 0) {
841
uch r1_min = rgb[bg[pat].rgb1_min].r;
842
uch g1_min = rgb[bg[pat].rgb1_min].g;
843
uch b1_min = rgb[bg[pat].rgb1_min].b;
844
uch r2_min = rgb[bg[pat].rgb2_min].r;
845
uch g2_min = rgb[bg[pat].rgb2_min].g;
846
uch b2_min = rgb[bg[pat].rgb2_min].b;
847
int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
848
int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
849
int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
850
int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
851
int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
852
int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
854
for (row = 0; row < rpng2_info.height; ++row) {
855
yidx = (int)(row % bgscale);
856
even_odd_vert = (int)((row / bgscale) & 1);
858
r1 = r1_min + (r1_diff * yidx) / yidx_max;
859
g1 = g1_min + (g1_diff * yidx) / yidx_max;
860
b1 = b1_min + (b1_diff * yidx) / yidx_max;
861
r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
862
g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
863
b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
865
r2 = r2_min + (r2_diff * yidx) / yidx_max;
866
g2 = g2_min + (g2_diff * yidx) / yidx_max;
867
b2 = b2_min + (b2_diff * yidx) / yidx_max;
868
r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
869
g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
870
b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
872
dest = (char *)bg_data + row*bg_rowbytes;
873
for (i = 0; i < rpng2_info.width; ++i) {
874
even_odd_horiz = (int)((i / bgscale) & 1);
875
even_odd = even_odd_vert ^ even_odd_horiz;
877
(even_odd_horiz && (bg[pat].type & 0x10));
878
if (even_odd == 0) { /* gradient #1 */
888
} else { /* gradient #2 */
889
if ((invert_column && invert_gradient2) ||
890
(!invert_column && !invert_gradient2))
892
*dest++ = r2; /* not inverted or */
893
*dest++ = g2; /* doubly inverted */
897
*dest++ = g2_inv; /* singly inverted */
904
/*---------------------------------------------------------------------------
905
Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
907
---------------------------------------------------------------------------*/
909
} else if ((bg[pat].type & 0x07) == 1) {
911
hmax = (bgscale-1)/2; /* half the max weight of a color */
912
max = 2*hmax; /* the max weight of a color */
914
r1 = rgb[bg[pat].rgb1_max].r;
915
g1 = rgb[bg[pat].rgb1_max].g;
916
b1 = rgb[bg[pat].rgb1_max].b;
917
r2 = rgb[bg[pat].rgb2_max].r;
918
g2 = rgb[bg[pat].rgb2_max].g;
919
b2 = rgb[bg[pat].rgb2_max].b;
921
for (row = 0; row < rpng2_info.height; ++row) {
922
yidx = (int)(row % bgscale);
924
yidx = bgscale-1 - yidx;
925
dest = (char *)bg_data + row*bg_rowbytes;
926
for (i = 0; i < rpng2_info.width; ++i) {
927
xidx = (int)(i % bgscale);
929
xidx = bgscale-1 - xidx;
931
*dest++ = (k*r1 + (max-k)*r2) / max;
932
*dest++ = (k*g1 + (max-k)*g2) / max;
933
*dest++ = (k*b1 + (max-k)*b2) / max;
937
/*---------------------------------------------------------------------------
938
Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
939
soids will equal bgscale?]. This one is slow but very cool. Code con-
940
tributed by Pieter S. van der Meulen (originally in Smalltalk).
941
---------------------------------------------------------------------------*/
943
} else if ((bg[pat].type & 0x07) == 2) {
945
int ii, x, y, hw, hh, grayspot;
946
double freq, rotate, saturate, gray, intensity;
947
double angle=0.0, aoffset=0.0, maxDist, dist;
948
double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
950
fprintf(stderr, "%s: computing radial background...",
954
hh = (int)(rpng2_info.height / 2);
955
hw = (int)(rpng2_info.width / 2);
957
/* variables for radial waves:
958
* aoffset: number of degrees to rotate hue [CURRENTLY NOT USED]
959
* freq: number of color beams originating from the center
960
* grayspot: size of the graying center area (anti-alias)
961
* rotate: rotation of the beams as a function of radius
962
* saturate: saturation of beams' shape azimuthally
964
angle = CLIP(angle, 0.0, 360.0);
965
grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
966
freq = MAX((double)bg[pat].bg_freq, 0.0);
967
saturate = (double)bg[pat].bg_bsat * 0.1;
968
rotate = (double)bg[pat].bg_brot * 0.1;
971
maxDist = (double)((hw*hw) + (hh*hh));
973
for (row = 0; row < rpng2_info.height; ++row) {
975
dest = (char *)bg_data + row*bg_rowbytes;
976
for (i = 0; i < rpng2_info.width; ++i) {
978
angle = (x == 0)? PI_2 : atan((double)y / (double)x);
979
gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
980
gray = MIN(1.0, gray);
981
dist = (double)((x*x) + (y*y)) / maxDist;
982
intensity = cos((angle+(rotate*dist*PI)) * freq) *
984
intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
985
hue = (angle + PI) * INV_PI_360 + aoffset;
986
s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
987
s = MIN(MAX(s,0.0), 1.0);
988
v = MIN(MAX(intensity,0.0), 1.0);
991
ch = (uch)(v * 255.0);
996
if ((hue < 0.0) || (hue >= 360.0))
997
hue -= (((int)(hue / 360.0)) * 360.0);
1000
f = hue - (double)ii;
1002
q = (1.0 - (s * f)) * v;
1003
t = (1.0 - (s * (1.0 - f))) * v;
1004
if (ii == 0) { red = v; green = t; blue = p; }
1005
else if (ii == 1) { red = q; green = v; blue = p; }
1006
else if (ii == 2) { red = p; green = v; blue = t; }
1007
else if (ii == 3) { red = p; green = q; blue = v; }
1008
else if (ii == 4) { red = t; green = p; blue = v; }
1009
else if (ii == 5) { red = v; green = p; blue = q; }
1010
*dest++ = (uch)(red * 255.0);
1011
*dest++ = (uch)(green * 255.0);
1012
*dest++ = (uch)(blue * 255.0);
1016
fprintf(stderr, "done.\n");
1020
/*---------------------------------------------------------------------------
1021
Blast background image to display buffer before beginning PNG decode.
1022
---------------------------------------------------------------------------*/
1024
if (depth == 24 || depth == 32) {
1025
ulg red, green, blue;
1027
for (row = 0; row < rpng2_info.height; ++row) {
1028
src = bg_data + row*bg_rowbytes;
1029
dest = ximage->data + row*ximage_rowbytes;
1030
for (i = rpng2_info.width; i > 0; --i) {
1034
pixel = (red << RShift) |
1037
/* recall that we set ximage->byte_order = MSBFirst above */
1038
/* GRR BUG: this assumes bpp == 32, but may be 24: */
1039
*dest++ = (char)((pixel >> 24) & 0xff);
1040
*dest++ = (char)((pixel >> 16) & 0xff);
1041
*dest++ = (char)((pixel >> 8) & 0xff);
1042
*dest++ = (char)( pixel & 0xff);
1046
} else if (depth == 16) {
1047
ush red, green, blue;
1049
for (row = 0; row < rpng2_info.height; ++row) {
1050
src = bg_data + row*bg_rowbytes;
1051
dest = ximage->data + row*ximage_rowbytes;
1052
for (i = rpng2_info.width; i > 0; --i) {
1053
red = ((ush)(*src) << 8); ++src;
1054
green = ((ush)(*src) << 8); ++src;
1055
blue = ((ush)(*src) << 8); ++src;
1056
pixel = ((red >> RShift) & RMask) |
1057
((green >> GShift) & GMask) |
1058
((blue >> BShift) & BMask);
1059
/* recall that we set ximage->byte_order = MSBFirst above */
1060
*dest++ = (char)((pixel >> 8) & 0xff);
1061
*dest++ = (char)( pixel & 0xff);
1065
} else /* depth == 8 */ {
1067
/* GRR: add 8-bit support */
1071
XPutImage(display, window, gc, ximage, 0, 0, 0, 0, rpng2_info.width,
1076
} /* end function rpng2_x_load_bg_image() */
1082
static void rpng2_x_display_row(ulg row)
1084
uch bg_red = rpng2_info.bg_red;
1085
uch bg_green = rpng2_info.bg_green;
1086
uch bg_blue = rpng2_info.bg_blue;
1087
uch *src, *src2=NULL;
1090
int ximage_rowbytes = ximage->bytes_per_line;
1092
static int rows=0, prevpass=(-1);
1093
static ulg firstrow;
1095
/*---------------------------------------------------------------------------
1096
rows and firstrow simply track how many rows (and which ones) have not
1097
yet been displayed; alternatively, we could call XPutImage() for every
1098
row and not bother with the records-keeping.
1099
---------------------------------------------------------------------------*/
1101
Trace((stderr, "beginning rpng2_x_display_row()\n"))
1103
if (rpng2_info.pass != prevpass) {
1104
if (pause_after_pass && rpng2_info.pass > 0) {
1109
"%s: end of pass %d of 7; click in image window to continue\n",
1110
PROGNAME, prevpass + 1);
1112
XNextEvent(display, &e);
1113
while (!(e.type == ButtonPress && e.xbutton.button == Button1)
1114
&& !(e.type == KeyPress &&
1115
((k = XLookupKeysym(&e.xkey, 0)) == XK_q
1116
|| k == XK_Escape) )) ;
1118
fprintf(stderr, "%s: pass %d of 7\r", PROGNAME, rpng2_info.pass + 1);
1120
prevpass = rpng2_info.pass;
1124
firstrow = row; /* first row that is not yet displayed */
1126
++rows; /* count of rows received but not yet displayed */
1128
/*---------------------------------------------------------------------------
1129
Aside from the use of the rpng2_info struct, the lack of an outer loop
1130
(over rows) and moving the XPutImage() call outside the "if (depth)"
1131
tests, this routine is identical to rpng_x_display_image() in the non-
1132
progressive version of the program.
1133
---------------------------------------------------------------------------*/
1135
if (depth == 24 || depth == 32) {
1136
ulg red, green, blue;
1138
src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1140
src2 = bg_data + row*bg_rowbytes;
1141
dest = ximage->data + row*ximage_rowbytes;
1142
if (rpng2_info.channels == 3) {
1143
for (i = rpng2_info.width; i > 0; --i) {
1147
pixel = (red << RShift) |
1150
/* recall that we set ximage->byte_order = MSBFirst above */
1151
/* GRR BUG: this assumes bpp == 32, but may be 24: */
1152
*dest++ = (char)((pixel >> 24) & 0xff);
1153
*dest++ = (char)((pixel >> 16) & 0xff);
1154
*dest++ = (char)((pixel >> 8) & 0xff);
1155
*dest++ = (char)( pixel & 0xff);
1157
} else /* if (rpng2_info.channels == 4) */ {
1158
for (i = rpng2_info.width; i > 0; --i) {
1172
} else if (a == 0) {
1177
/* this macro (from png.h) composites the foreground
1178
* and background values and puts the result into the
1180
alpha_composite(red, r, a, bg_red);
1181
alpha_composite(green, g, a, bg_green);
1182
alpha_composite(blue, b, a, bg_blue);
1184
pixel = (red << RShift) |
1187
/* recall that we set ximage->byte_order = MSBFirst above */
1188
/* GRR BUG: this assumes bpp == 32, but may be 24: */
1189
*dest++ = (char)((pixel >> 24) & 0xff);
1190
*dest++ = (char)((pixel >> 16) & 0xff);
1191
*dest++ = (char)((pixel >> 8) & 0xff);
1192
*dest++ = (char)( pixel & 0xff);
1196
} else if (depth == 16) {
1197
ush red, green, blue;
1199
src = rpng2_info.row_pointers[row];
1201
src2 = bg_data + row*bg_rowbytes;
1202
dest = ximage->data + row*ximage_rowbytes;
1203
if (rpng2_info.channels == 3) {
1204
for (i = rpng2_info.width; i > 0; --i) {
1205
red = ((ush)(*src) << 8);
1207
green = ((ush)(*src) << 8);
1209
blue = ((ush)(*src) << 8);
1211
pixel = ((red >> RShift) & RMask) |
1212
((green >> GShift) & GMask) |
1213
((blue >> BShift) & BMask);
1214
/* recall that we set ximage->byte_order = MSBFirst above */
1215
*dest++ = (char)((pixel >> 8) & 0xff);
1216
*dest++ = (char)( pixel & 0xff);
1218
} else /* if (rpng2_info.channels == 4) */ {
1219
for (i = rpng2_info.width; i > 0; --i) {
1230
red = ((ush)r << 8);
1231
green = ((ush)g << 8);
1232
blue = ((ush)b << 8);
1233
} else if (a == 0) {
1234
red = ((ush)bg_red << 8);
1235
green = ((ush)bg_green << 8);
1236
blue = ((ush)bg_blue << 8);
1238
/* this macro (from png.h) composites the foreground
1239
* and background values and puts the result back into
1240
* the first argument (== fg byte here: safe) */
1241
alpha_composite(r, r, a, bg_red);
1242
alpha_composite(g, g, a, bg_green);
1243
alpha_composite(b, b, a, bg_blue);
1244
red = ((ush)r << 8);
1245
green = ((ush)g << 8);
1246
blue = ((ush)b << 8);
1248
pixel = ((red >> RShift) & RMask) |
1249
((green >> GShift) & GMask) |
1250
((blue >> BShift) & BMask);
1251
/* recall that we set ximage->byte_order = MSBFirst above */
1252
*dest++ = (char)((pixel >> 8) & 0xff);
1253
*dest++ = (char)( pixel & 0xff);
1257
} else /* depth == 8 */ {
1259
/* GRR: add 8-bit support */
1264
/*---------------------------------------------------------------------------
1265
Display after every 16 rows or when on one of last two rows. (Region
1266
may include previously displayed lines due to interlacing--i.e., not
1267
contiguous. Also, second-to-last row is final one in interlaced images
1268
with odd number of rows.) For demos, flush (and delay) after every 16th
1269
row so "sparse" passes don't go twice as fast.
1270
---------------------------------------------------------------------------*/
1272
if (demo_timing && (row - firstrow >= 16 || row >= rpng2_info.height-2)) {
1273
XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
1274
(int)firstrow, rpng2_info.width, row - firstrow + 1);
1277
usleep(usleep_duration);
1279
if (!demo_timing && ((rows & 0xf) == 0 || row >= rpng2_info.height-2)) {
1280
XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0,
1281
(int)firstrow, rpng2_info.width, row - firstrow + 1);
1292
static void rpng2_x_finish_display(void)
1294
Trace((stderr, "beginning rpng2_x_finish_display()\n"))
1296
/* last row has already been displayed by rpng2_x_display_row(), so we
1297
* have nothing to do here except set a flag and let the user know that
1298
* the image is done */
1300
rpng2_info.done = TRUE;
1302
"Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n");
1310
static void rpng2_x_cleanup(void)
1312
if (bg_image && bg_data) {
1317
if (rpng2_info.image_data) {
1318
free(rpng2_info.image_data);
1319
rpng2_info.image_data = NULL;
1322
if (rpng2_info.row_pointers) {
1323
free(rpng2_info.row_pointers);
1324
rpng2_info.row_pointers = NULL;
1329
free(ximage->data); /* we allocated it, so we free it */
1330
ximage->data = (char *)NULL; /* instead of XDestroyImage() */
1332
XDestroyImage(ximage);
1336
XFreeGC(display, gc);
1339
XDestroyWindow(display, window);
1342
XFreeColormap(display, colormap);
1344
if (have_nondefault_visual)
1352
static int rpng2_x_msb(ulg u32val)
1356
for (i = 31; i >= 0; --i) {
1357
if (u32val & 0x80000000L)