1
/*---------------------------------------------------------------------------
3
rpng2 - progressive-model PNG display program rpng2-win.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 32-bit Windows; it may compile under 16-bit Windows with a little
10
tweaking (or maybe not). Thanks to Adam Costello and Pieter S. van der
11
Meulen for the "diamond" and "radial waves" patterns, respectively.
13
to do (someday, maybe):
14
- handle quoted command-line args (especially filenames with spaces)
15
- finish resizable checkerboard-gradient (sizes 4-128?)
16
- use %.1023s to simplify truncation of title-bar string?
17
- have minimum window width: oh well
19
---------------------------------------------------------------------------
22
- 1.01: initial public release
23
- 1.02: fixed cut-and-paste error in usage screen (oops...)
24
- 1.03: modified to allow abbreviated options
25
- 1.04: removed bogus extra argument from usage fprintf() [Glenn R-P?];
26
fixed command-line parsing bug
27
- 1.10: enabled "message window"/console (thanks to David Geldreich)
28
- 1.20: added runtime MMX-enabling/disabling and new -mmx* options
29
- 1.21: made minor tweak to usage screen to fit within 25-line console
30
- 1.22: added AMD64/EM64T support (__x86_64__)
31
- 2.00: dual-licensed (added GNU GPL)
32
- 2.01: fixed 64-bit typo in readpng2.c
33
- 2.02: fixed improper display of usage screen on PNG error(s); fixed
34
unexpected-EOF and file-read-error cases
35
- 2.03: removed runtime MMX-enabling/disabling and obsolete -mmx* options
37
---------------------------------------------------------------------------
39
Copyright (c) 1998-2008 Greg Roelofs. All rights reserved.
41
This software is provided "as is," without warranty of any kind,
42
express or implied. In no event shall the author or contributors
43
be held liable for any damages arising in any way from the use of
46
The contents of this file are DUAL-LICENSED. You may modify and/or
47
redistribute this software according to the terms of one of the
48
following two licenses (at your option):
51
LICENSE 1 ("BSD-like with advertising clause"):
53
Permission is granted to anyone to use this software for any purpose,
54
including commercial applications, and to alter it and redistribute
55
it freely, subject to the following restrictions:
57
1. Redistributions of source code must retain the above copyright
58
notice, disclaimer, and this list of conditions.
59
2. Redistributions in binary form must reproduce the above copyright
60
notice, disclaimer, and this list of conditions in the documenta-
61
tion and/or other materials provided with the distribution.
62
3. All advertising materials mentioning features or use of this
63
software must display the following acknowledgment:
65
This product includes software developed by Greg Roelofs
66
and contributors for the book, "PNG: The Definitive Guide,"
67
published by O'Reilly and Associates.
70
LICENSE 2 (GNU GPL v2 or later):
72
This program is free software; you can redistribute it and/or modify
73
it under the terms of the GNU General Public License as published by
74
the Free Software Foundation; either version 2 of the License, or
75
(at your option) any later version.
77
This program is distributed in the hope that it will be useful,
78
but WITHOUT ANY WARRANTY; without even the implied warranty of
79
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
80
GNU General Public License for more details.
82
You should have received a copy of the GNU General Public License
83
along with this program; if not, write to the Free Software Foundation,
84
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
86
---------------------------------------------------------------------------*/
88
#define PROGNAME "rpng2-win"
89
#define LONGNAME "Progressive PNG Viewer for Windows"
90
#define VERSION "2.02 of 16 March 2008"
95
#include <setjmp.h> /* for jmpbuf declaration in readpng2.h */
97
#include <math.h> /* only for PvdM background code */
100
/* getch replacement. Turns out, we don't really need this,
101
* but leave it here if we ever enable any of the uses of
102
* _getch in the main code
106
#include <sys/ioctl.h>
107
int repl_getch( void )
110
int fd = fileno(stdin);
111
struct termio old_tty, new_tty;
113
ioctl(fd, TCGETA, &old_tty);
115
new_tty.c_lflag &= ~(ICANON | ECHO | ISIG);
116
ioctl(fd, TCSETA, &new_tty);
117
fread(&ch, 1, sizeof(ch), stdin);
118
ioctl(fd, TCSETA, &old_tty);
122
#define _getch repl_getch
124
#include <conio.h> /* only for _getch() */
127
/* all for PvdM background code: */
129
# define PI 3.141592653589793238
131
#define PI_2 (PI*0.5)
132
#define INV_PI_360 (360.0 / PI)
133
#define MAX(a,b) (a>b?a:b)
134
#define MIN(a,b) (a<b?a:b)
135
#define CLIP(a,min,max) MAX(min,MIN((a),max))
136
#define ABS(a) ((a)<0?-(a):(a))
137
#define CLIP8P(c) MAX(0,(MIN((c),255))) /* 8-bit pos. integer (uch) */
138
#define ROUNDF(f) ((int)(f + 0.5))
140
#define rgb1_max bg_freq
141
#define rgb1_min bg_gray
142
#define rgb2_max bg_bsat
143
#define rgb2_min bg_brot
145
/* #define DEBUG */ /* this enables the Trace() macros */
147
#include "readpng2.h" /* typedefs, common macros, readpng2 prototypes */
150
/* could just include png.h, but this macro is the only thing we need
151
* (name and typedefs changed to local versions); note that side effects
152
* only happen with alpha (which could easily be avoided with
153
* "ush acopy = (alpha);") */
155
#define alpha_composite(composite, fg, alpha, bg) { \
156
ush temp = ((ush)(fg)*(ush)(alpha) + \
157
(ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128); \
158
(composite) = (uch)((temp + (temp >> 8)) >> 8); \
162
#define INBUFSIZE 4096 /* with pseudo-timing on (1 sec delay/block), this
163
* block size corresponds roughly to a download
164
* speed 10% faster than theoretical 33.6K maximum
165
* (assuming 8 data bits, 1 stop bit and no other
168
/* local prototypes */
169
static void rpng2_win_init(void);
170
static int rpng2_win_create_window(void);
171
static int rpng2_win_load_bg_image(void);
172
static void rpng2_win_display_row(ulg row);
173
static void rpng2_win_finish_display(void);
174
static void rpng2_win_cleanup(void);
175
LRESULT CALLBACK rpng2_win_wndproc(HWND, UINT, WPARAM, LPARAM);
178
static char titlebar[1024];
179
static char *progname = PROGNAME;
180
static char *appname = LONGNAME;
181
static char *filename;
184
static mainprog_info rpng2_info;
186
static uch inbuf[INBUFSIZE];
189
static int pat = 6; /* must be less than num_bgpat */
190
static int bg_image = 0;
191
static int bgscale = 16;
192
static ulg bg_rowbytes;
195
static struct rgb_color {
198
{ 0, 0, 0}, /* 0: black */
199
{255, 255, 255}, /* 1: white */
200
{173, 132, 57}, /* 2: tan */
201
{ 64, 132, 0}, /* 3: medium green */
202
{189, 117, 1}, /* 4: gold */
203
{253, 249, 1}, /* 5: yellow */
204
{ 0, 0, 255}, /* 6: blue */
205
{ 0, 0, 120}, /* 7: medium blue */
206
{255, 0, 255}, /* 8: magenta */
207
{ 64, 0, 64}, /* 9: dark magenta */
208
{255, 0, 0}, /* 10: red */
209
{ 64, 0, 0}, /* 11: dark red */
210
{255, 127, 0}, /* 12: orange */
211
{192, 96, 0}, /* 13: darker orange */
212
{ 24, 60, 0}, /* 14: dark green-yellow */
213
{ 85, 125, 200} /* 15: ice blue */
215
/* not used for now, but should be for error-checking:
216
static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
220
This whole struct is a fairly cheesy way to keep the number of
221
command-line options to a minimum. The radial-waves background
222
type is a particularly poor fit to the integer elements of the
223
struct...but a few macros and a little fixed-point math will do
227
F E D C B A 9 8 7 6 5 4 3 2 1 0
229
| | +-+-+-- 0 = sharp-edged checkerboard
230
| | 1 = soft diamonds
233
| +-- gradient #2 inverted?
234
+-- alternating columns inverted?
236
static struct background_pattern {
238
int rgb1_max, rgb1_min; /* or bg_freq, bg_gray */
239
int rgb2_max, rgb2_min; /* or bg_bsat, bg_brot (both scaled by 10)*/
241
{0+8, 2,0, 1,15}, /* checkered: tan/black vs. white/ice blue */
242
{0+24, 2,0, 1,0}, /* checkered: tan/black vs. white/black */
243
{0+8, 4,5, 0,2}, /* checkered: gold/yellow vs. black/tan */
244
{0+8, 4,5, 0,6}, /* checkered: gold/yellow vs. black/blue */
245
{0, 7,0, 8,9}, /* checkered: deep blue/black vs. magenta */
246
{0+8, 13,0, 5,14}, /* checkered: orange/black vs. yellow */
247
{0+8, 12,0, 10,11}, /* checkered: orange/black vs. red */
248
{1, 7,0, 8,0}, /* diamonds: deep blue/black vs. magenta */
249
{1, 12,0, 11,0}, /* diamonds: orange vs. dark red */
250
{1, 10,0, 7,0}, /* diamonds: red vs. medium blue */
251
{1, 4,0, 5,0}, /* diamonds: gold vs. yellow */
252
{1, 3,0, 0,0}, /* diamonds: medium green vs. black */
253
{2, 16, 100, 20, 0}, /* radial: ~hard radial color-beams */
254
{2, 18, 100, 10, 2}, /* radial: soft, curved radial color-beams */
255
{2, 16, 256, 100, 250}, /* radial: very tight spiral */
256
{2, 10000, 256, 11, 0} /* radial: dipole-moire' (almost fractal) */
258
static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
261
/* Windows-specific global variables (could go in struct, but messy...) */
262
static ulg wimage_rowbytes;
264
static uch *wimage_data;
265
static BITMAPINFOHEADER *bmih;
267
static HWND global_hwnd;
268
static HINSTANCE global_hInst;
269
static int global_showmode;
274
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode)
276
char *args[1024]; /* arbitrary limit, but should suffice */
278
char *p, *q, *bgstr = NULL;
284
double LUT_exponent; /* just the lookup table */
285
double CRT_exponent = 2.2; /* just the monitor */
286
double default_display_exponent; /* whole display system */
290
/* First initialize a few things, just to be sure--memset takes care of
291
* default background color (black), booleans (FALSE), pointers (NULL),
294
global_hInst = hInst;
295
global_showmode = showmode;
296
filename = (char *)NULL;
297
memset(&rpng2_info, 0, sizeof(mainprog_info));
300
/* Next reenable console output, which normally goes to the bit bucket
301
* for windowed apps. Closing the console window will terminate the
302
* app. Thanks to David.Geldreich@realviz.com for supplying the magical
306
freopen("CONOUT$", "a", stderr);
307
freopen("CONOUT$", "a", stdout);
310
/* Set the default value for our display-system exponent, i.e., the
311
* product of the CRT exponent and the exponent corresponding to
312
* the frame-buffer's lookup table (LUT), if any. This is not an
313
* exhaustive list of LUT values (e.g., OpenStep has a lot of weird
314
* ones), but it should cover 99% of the current possibilities. And
315
* yes, these ifdefs are completely wasted in a Windows program... */
318
/* third-party utilities can modify the default LUT exponent */
319
LUT_exponent = 1.0 / 2.2;
321
if (some_next_function_that_returns_gamma(&next_gamma))
322
LUT_exponent = 1.0 / next_gamma;
325
LUT_exponent = 1.0 / 1.7;
326
/* there doesn't seem to be any documented function to
327
* get the "gamma" value, so we do it the hard way */
328
infile = fopen("/etc/config/system.glGammaVal", "r");
332
fgets(tmpline, 80, infile);
334
sgi_gamma = atof(tmpline);
336
LUT_exponent = 1.0 / sgi_gamma;
338
#elif defined(Macintosh)
339
LUT_exponent = 1.8 / 2.61;
341
if (some_mac_function_that_returns_gamma(&mac_gamma))
342
LUT_exponent = mac_gamma / 2.61;
345
LUT_exponent = 1.0; /* assume no LUT: most PCs */
348
/* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
349
default_display_exponent = LUT_exponent * CRT_exponent;
352
/* If the user has set the SCREEN_GAMMA environment variable as suggested
353
* (somewhat imprecisely) in the libpng documentation, use that; otherwise
354
* use the default value we just calculated. Either way, the user may
355
* override this via a command-line option. */
357
if ((p = getenv("SCREEN_GAMMA")) != NULL)
358
rpng2_info.display_exponent = atof(p);
360
rpng2_info.display_exponent = default_display_exponent;
363
/* Windows really hates command lines, so we have to set up our own argv.
364
* Note that we do NOT bother with quoted arguments here, so don't use
365
* filenames with spaces in 'em! */
367
argv[argc++] = PROGNAME;
373
/* now p points at the first non-space after some spaces */
375
break; /* nothing after the spaces: done */
376
argv[argc++] = q = p;
377
while (*q && *q != ' ')
379
/* now q points at a space or the end of the string */
381
break; /* last argv already terminated; quit */
382
*q = '\0'; /* change space to terminator */
385
argv[argc] = NULL; /* terminate the argv array itself */
388
/* Now parse the command line for options and the PNG filename. */
390
while (*++argv && !error) {
391
if (!strncmp(*argv, "-gamma", 2)) {
395
rpng2_info.display_exponent = atof(*argv);
396
if (rpng2_info.display_exponent <= 0.0)
399
} else if (!strncmp(*argv, "-bgcolor", 4)) {
404
if (strlen(bgstr) != 7 || bgstr[0] != '#')
411
} else if (!strncmp(*argv, "-bgpat", 4)) {
415
pat = atoi(*argv) - 1;
416
if (pat < 0 || pat >= num_bgpat)
423
} else if (!strncmp(*argv, "-timing", 2)) {
428
if (argv[1]) /* shouldn't be any more args after filename */
431
++error; /* not expecting any other options */
439
/* print usage screen if any errors up to this point */
446
fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname);
447
readpng2_version_info();
449
"Usage: %s [-gamma exp] [-bgcolor bg | -bgpat pat] [-timing]\n"
451
" exp \ttransfer-function exponent (``gamma'') of the display\n"
452
"\t\t system in floating-point format (e.g., ``%.1f''); equal\n"
453
"\t\t to the product of the lookup-table exponent (varies)\n"
454
"\t\t and the CRT exponent (usually 2.2); must be positive\n"
455
" bg \tdesired background color in 7-character hex RGB format\n"
456
"\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n"
457
"\t\t used with transparent images; overrides -bgpat option\n"
458
" pat \tdesired background pattern number (1-%d); used with\n"
459
"\t\t transparent images; overrides -bgcolor option\n"
460
" -timing\tenables delay for every block read, to simulate modem\n"
461
"\t\t download of image (~36 Kbps)\n"
462
"\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
464
"Press Q or Esc to quit this usage screen. ",
469
#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) && \
470
!(defined(__CYGWIN__) || defined(__MINGW32__))
471
(int)strlen(PROGNAME), " ",
473
(int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat);
478
while (ch != 'q' && ch != 'Q' && ch != 0x1B);
484
if (!(infile = fopen(filename, "rb"))) {
485
fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename);
488
incount = fread(inbuf, 1, INBUFSIZE, infile);
489
if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
490
fprintf(stderr, PROGNAME
491
": [%s] is not a PNG file: incorrect signature\n",
494
} else if ((rc = readpng2_init(&rpng2_info)) != 0) {
497
fprintf(stderr, PROGNAME
498
": [%s] has bad IHDR (libpng longjmp)\n", filename);
501
fprintf(stderr, PROGNAME ": insufficient memory\n");
504
fprintf(stderr, PROGNAME
505
": unknown readpng2_init() error\n");
520
fprintf(stderr, PROGNAME ": aborting.\n");
524
while (ch != 'q' && ch != 'Q' && ch != 0x1B);
528
fprintf(stderr, "\n%s %s: %s\n", PROGNAME, VERSION, appname);
531
"\n [console window: closing this window will terminate %s]\n\n",
538
/* set the title-bar string, but make sure buffer doesn't overflow */
540
alen = strlen(appname);
541
flen = strlen(filename);
542
if (alen + flen + 3 > 1023)
543
sprintf(titlebar, "%s: ...%s", appname, filename+(alen+flen+6-1023));
545
sprintf(titlebar, "%s: %s", appname, filename);
548
/* set some final rpng2_info variables before entering main data loop */
551
unsigned r, g, b; /* this approach quiets compiler warnings */
553
sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
554
rpng2_info.bg_red = (uch)r;
555
rpng2_info.bg_green = (uch)g;
556
rpng2_info.bg_blue = (uch)b;
558
rpng2_info.need_bgcolor = TRUE;
560
rpng2_info.state = kPreInit;
561
rpng2_info.mainprog_init = rpng2_win_init;
562
rpng2_info.mainprog_display_row = rpng2_win_display_row;
563
rpng2_info.mainprog_finish_display = rpng2_win_finish_display;
566
/* OK, this is the fun part: call readpng2_decode_data() at the start of
567
* the loop to deal with our first buffer of data (read in above to verify
568
* that the file is a PNG image), then loop through the file and continue
569
* calling the same routine to handle each chunk of data. It in turn
570
* passes the data to libpng, which will invoke one or more of our call-
571
* backs as decoded data become available. We optionally call Sleep() for
572
* one second per iteration to simulate downloading the image via an analog
576
Trace((stderr, "about to call readpng2_decode_data()\n"))
577
if (readpng2_decode_data(&rpng2_info, inbuf, incount))
579
Trace((stderr, "done with readpng2_decode_data()\n"))
581
if (error || incount != INBUFSIZE || rpng2_info.state == kDone) {
582
if (rpng2_info.state == kDone) {
583
Trace((stderr, "done decoding PNG image\n"))
584
} else if (ferror(infile)) {
585
fprintf(stderr, PROGNAME
586
": error while reading PNG image file\n");
588
} else if (feof(infile)) {
589
fprintf(stderr, PROGNAME ": end of file reached "
590
"(unexpectedly) while reading PNG image file\n");
592
} else /* if (error) */ {
593
// will print error message below
601
incount = fread(inbuf, 1, INBUFSIZE, infile);
605
/* clean up PNG stuff and report any decoding errors */
608
Trace((stderr, "about to call readpng2_cleanup()\n"))
609
readpng2_cleanup(&rpng2_info);
612
fprintf(stderr, PROGNAME ": libpng error while decoding PNG image\n");
617
/* wait for the user to tell us when to quit */
619
while (GetMessage(&msg, NULL, 0, 0)) {
620
TranslateMessage(&msg);
621
DispatchMessage(&msg);
625
/* we're done: clean up all image and Windows resources and go away */
627
Trace((stderr, "about to call rpng2_win_cleanup()\n"))
637
/* this function is called by readpng2_info_callback() in readpng2.c, which
638
* in turn is called by libpng after all of the pre-IDAT chunks have been
639
* read and processed--i.e., we now have enough info to finish initializing */
641
static void rpng2_win_init()
644
ulg rowbytes = rpng2_info.rowbytes;
646
Trace((stderr, "beginning rpng2_win_init()\n"))
647
Trace((stderr, " rowbytes = %d\n", rpng2_info.rowbytes))
648
Trace((stderr, " width = %ld\n", rpng2_info.width))
649
Trace((stderr, " height = %ld\n", rpng2_info.height))
651
rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
652
if (!rpng2_info.image_data) {
653
readpng2_cleanup(&rpng2_info);
657
rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
658
if (!rpng2_info.row_pointers) {
659
free(rpng2_info.image_data);
660
rpng2_info.image_data = NULL;
661
readpng2_cleanup(&rpng2_info);
665
for (i = 0; i < rpng2_info.height; ++i)
666
rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
668
/*---------------------------------------------------------------------------
669
Do the basic Windows initialization stuff, make the window, and fill it
670
with the user-specified, file-specified or default background color.
671
---------------------------------------------------------------------------*/
673
if (rpng2_win_create_window()) {
674
readpng2_cleanup(&rpng2_info);
678
rpng2_info.state = kWindowInit;
685
static int rpng2_win_create_window()
687
uch bg_red = rpng2_info.bg_red;
688
uch bg_green = rpng2_info.bg_green;
689
uch bg_blue = rpng2_info.bg_blue;
691
int extra_width, extra_height;
697
/*---------------------------------------------------------------------------
698
Allocate memory for the display-specific version of the image (round up
699
to multiple of 4 for Windows DIB).
700
---------------------------------------------------------------------------*/
702
wimage_rowbytes = ((3*rpng2_info.width + 3L) >> 2) << 2;
704
if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) +
705
wimage_rowbytes*rpng2_info.height)))
710
/*---------------------------------------------------------------------------
711
Initialize the DIB. Negative height means to use top-down BMP ordering
712
(must be uncompressed, but that's what we want). Bit count of 1, 4 or 8
713
implies a colormap of RGBX quads, but 24-bit BMPs just use B,G,R values
714
directly => wimage_data begins immediately after BMP header.
715
---------------------------------------------------------------------------*/
717
memset(dib, 0, sizeof(BITMAPINFOHEADER));
718
bmih = (BITMAPINFOHEADER *)dib;
719
bmih->biSize = sizeof(BITMAPINFOHEADER);
720
bmih->biWidth = rpng2_info.width;
721
bmih->biHeight = -((long)rpng2_info.height);
723
bmih->biBitCount = 24;
724
bmih->biCompression = 0;
725
wimage_data = dib + sizeof(BITMAPINFOHEADER);
727
/*---------------------------------------------------------------------------
728
Fill window with the specified background color (default is black), but
729
defer loading faked "background image" until window is displayed (may be
730
slow to compute). Data are in BGR order.
731
---------------------------------------------------------------------------*/
733
if (bg_image) { /* just fill with black for now */
734
memset(wimage_data, 0, wimage_rowbytes*rpng2_info.height);
736
for (j = 0; j < rpng2_info.height; ++j) {
737
dest = wimage_data + j*wimage_rowbytes;
738
for (i = rpng2_info.width; i > 0; --i) {
746
/*---------------------------------------------------------------------------
747
Set the window parameters.
748
---------------------------------------------------------------------------*/
750
memset(&wndclass, 0, sizeof(wndclass));
752
wndclass.cbSize = sizeof(wndclass);
753
wndclass.style = CS_HREDRAW | CS_VREDRAW;
754
wndclass.lpfnWndProc = rpng2_win_wndproc;
755
wndclass.hInstance = global_hInst;
756
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
757
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
758
wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
759
wndclass.lpszMenuName = NULL;
760
wndclass.lpszClassName = progname;
761
wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
763
RegisterClassEx(&wndclass);
765
/*---------------------------------------------------------------------------
766
Finally, create the window.
767
---------------------------------------------------------------------------*/
769
extra_width = 2*(GetSystemMetrics(SM_CXBORDER) +
770
GetSystemMetrics(SM_CXDLGFRAME));
771
extra_height = 2*(GetSystemMetrics(SM_CYBORDER) +
772
GetSystemMetrics(SM_CYDLGFRAME)) +
773
GetSystemMetrics(SM_CYCAPTION);
775
global_hwnd = CreateWindow(progname, titlebar, WS_OVERLAPPEDWINDOW,
776
CW_USEDEFAULT, CW_USEDEFAULT, rpng2_info.width+extra_width,
777
rpng2_info.height+extra_height, NULL, NULL, global_hInst, NULL);
779
ShowWindow(global_hwnd, global_showmode);
780
UpdateWindow(global_hwnd);
782
/*---------------------------------------------------------------------------
783
Now compute the background image and display it. If it fails (memory
784
allocation), revert to a plain background color.
785
---------------------------------------------------------------------------*/
788
static const char *msg = "Computing background image...";
789
int x, y, len = strlen(msg);
790
HDC hdc = GetDC(global_hwnd);
793
GetTextMetrics(hdc, &tm);
794
x = (rpng2_info.width - len*tm.tmAveCharWidth)/2;
795
y = (rpng2_info.height - tm.tmHeight)/2;
796
SetBkMode(hdc, TRANSPARENT);
797
SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
798
/* this can still begin out of bounds even if x is positive (???): */
799
TextOut(hdc, ((x < 0)? 0 : x), ((y < 0)? 0 : y), msg, len);
800
ReleaseDC(global_hwnd, hdc);
802
rpng2_win_load_bg_image(); /* resets bg_image if fails */
806
for (j = 0; j < rpng2_info.height; ++j) {
807
dest = wimage_data + j*wimage_rowbytes;
808
for (i = rpng2_info.width; i > 0; --i) {
818
rect.right = (LONG)rpng2_info.width; /* possibly off by one? */
819
rect.bottom = (LONG)rpng2_info.height; /* possibly off by one? */
820
InvalidateRect(global_hwnd, &rect, FALSE);
821
UpdateWindow(global_hwnd); /* similar to XFlush() */
825
} /* end function rpng2_win_create_window() */
831
static int rpng2_win_load_bg_image()
834
uch r1, r2, g1, g2, b1, b2;
835
uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
837
int xidx, yidx, yidx_max = (bgscale-1);
838
int even_odd_vert, even_odd_horiz, even_odd;
839
int invert_gradient2 = (bg[pat].type & 0x08);
843
/*---------------------------------------------------------------------------
844
Allocate buffer for fake background image to be used with transparent
845
images; if this fails, revert to plain background color.
846
---------------------------------------------------------------------------*/
848
bg_rowbytes = 3 * rpng2_info.width;
849
bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
851
fprintf(stderr, PROGNAME
852
": unable to allocate memory for background image\n");
857
/*---------------------------------------------------------------------------
858
Vertical gradients (ramps) in NxN squares, alternating direction and
859
colors (N == bgscale).
860
---------------------------------------------------------------------------*/
862
if ((bg[pat].type & 0x07) == 0) {
863
uch r1_min = rgb[bg[pat].rgb1_min].r;
864
uch g1_min = rgb[bg[pat].rgb1_min].g;
865
uch b1_min = rgb[bg[pat].rgb1_min].b;
866
uch r2_min = rgb[bg[pat].rgb2_min].r;
867
uch g2_min = rgb[bg[pat].rgb2_min].g;
868
uch b2_min = rgb[bg[pat].rgb2_min].b;
869
int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
870
int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
871
int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
872
int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
873
int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
874
int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
876
for (row = 0; row < rpng2_info.height; ++row) {
877
yidx = row % bgscale;
878
even_odd_vert = (row / bgscale) & 1;
880
r1 = r1_min + (r1_diff * yidx) / yidx_max;
881
g1 = g1_min + (g1_diff * yidx) / yidx_max;
882
b1 = b1_min + (b1_diff * yidx) / yidx_max;
883
r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
884
g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
885
b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
887
r2 = r2_min + (r2_diff * yidx) / yidx_max;
888
g2 = g2_min + (g2_diff * yidx) / yidx_max;
889
b2 = b2_min + (b2_diff * yidx) / yidx_max;
890
r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
891
g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
892
b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
894
dest = bg_data + row*bg_rowbytes;
895
for (i = 0; i < rpng2_info.width; ++i) {
896
even_odd_horiz = (i / bgscale) & 1;
897
even_odd = even_odd_vert ^ even_odd_horiz;
899
(even_odd_horiz && (bg[pat].type & 0x10));
900
if (even_odd == 0) { /* gradient #1 */
910
} else { /* gradient #2 */
911
if ((invert_column && invert_gradient2) ||
912
(!invert_column && !invert_gradient2))
914
*dest++ = r2; /* not inverted or */
915
*dest++ = g2; /* doubly inverted */
919
*dest++ = g2_inv; /* singly inverted */
926
/*---------------------------------------------------------------------------
927
Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
929
---------------------------------------------------------------------------*/
931
} else if ((bg[pat].type & 0x07) == 1) {
933
hmax = (bgscale-1)/2; /* half the max weight of a color */
934
max = 2*hmax; /* the max weight of a color */
936
r1 = rgb[bg[pat].rgb1_max].r;
937
g1 = rgb[bg[pat].rgb1_max].g;
938
b1 = rgb[bg[pat].rgb1_max].b;
939
r2 = rgb[bg[pat].rgb2_max].r;
940
g2 = rgb[bg[pat].rgb2_max].g;
941
b2 = rgb[bg[pat].rgb2_max].b;
943
for (row = 0; row < rpng2_info.height; ++row) {
944
yidx = row % bgscale;
946
yidx = bgscale-1 - yidx;
947
dest = bg_data + row*bg_rowbytes;
948
for (i = 0; i < rpng2_info.width; ++i) {
951
xidx = bgscale-1 - xidx;
953
*dest++ = (k*r1 + (max-k)*r2) / max;
954
*dest++ = (k*g1 + (max-k)*g2) / max;
955
*dest++ = (k*b1 + (max-k)*b2) / max;
959
/*---------------------------------------------------------------------------
960
Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
961
soids will equal bgscale?]. This one is slow but very cool. Code con-
962
tributed by Pieter S. van der Meulen (originally in Smalltalk).
963
---------------------------------------------------------------------------*/
965
} else if ((bg[pat].type & 0x07) == 2) {
967
int ii, x, y, hw, hh, grayspot;
968
double freq, rotate, saturate, gray, intensity;
969
double angle=0.0, aoffset=0.0, maxDist, dist;
970
double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
972
fprintf(stderr, "%s: computing radial background...",
976
hh = rpng2_info.height / 2;
977
hw = rpng2_info.width / 2;
979
/* variables for radial waves:
980
* aoffset: number of degrees to rotate hue [CURRENTLY NOT USED]
981
* freq: number of color beams originating from the center
982
* grayspot: size of the graying center area (anti-alias)
983
* rotate: rotation of the beams as a function of radius
984
* saturate: saturation of beams' shape azimuthally
986
angle = CLIP(angle, 0.0, 360.0);
987
grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
988
freq = MAX((double)bg[pat].bg_freq, 0.0);
989
saturate = (double)bg[pat].bg_bsat * 0.1;
990
rotate = (double)bg[pat].bg_brot * 0.1;
993
maxDist = (double)((hw*hw) + (hh*hh));
995
for (row = 0; row < rpng2_info.height; ++row) {
997
dest = bg_data + row*bg_rowbytes;
998
for (i = 0; i < rpng2_info.width; ++i) {
1000
angle = (x == 0)? PI_2 : atan((double)y / (double)x);
1001
gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
1002
gray = MIN(1.0, gray);
1003
dist = (double)((x*x) + (y*y)) / maxDist;
1004
intensity = cos((angle+(rotate*dist*PI)) * freq) *
1006
intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
1007
hue = (angle + PI) * INV_PI_360 + aoffset;
1008
s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
1009
s = MIN(MAX(s,0.0), 1.0);
1010
v = MIN(MAX(intensity,0.0), 1.0);
1013
ch = (uch)(v * 255.0);
1018
if ((hue < 0.0) || (hue >= 360.0))
1019
hue -= (((int)(hue / 360.0)) * 360.0);
1022
f = hue - (double)ii;
1024
q = (1.0 - (s * f)) * v;
1025
t = (1.0 - (s * (1.0 - f))) * v;
1026
if (ii == 0) { red = v; green = t; blue = p; }
1027
else if (ii == 1) { red = q; green = v; blue = p; }
1028
else if (ii == 2) { red = p; green = v; blue = t; }
1029
else if (ii == 3) { red = p; green = q; blue = v; }
1030
else if (ii == 4) { red = t; green = p; blue = v; }
1031
else if (ii == 5) { red = v; green = p; blue = q; }
1032
*dest++ = (uch)(red * 255.0);
1033
*dest++ = (uch)(green * 255.0);
1034
*dest++ = (uch)(blue * 255.0);
1038
fprintf(stderr, "done.\n");
1042
/*---------------------------------------------------------------------------
1043
Blast background image to display buffer before beginning PNG decode;
1044
calling function will handle invalidation and UpdateWindow() call.
1045
---------------------------------------------------------------------------*/
1047
for (row = 0; row < rpng2_info.height; ++row) {
1048
src = bg_data + row*bg_rowbytes;
1049
dest = wimage_data + row*wimage_rowbytes;
1050
for (i = rpng2_info.width; i > 0; --i) {
1055
*dest++ = g1; /* note reverse order */
1062
} /* end function rpng2_win_load_bg_image() */
1068
static void rpng2_win_display_row(ulg row)
1070
uch bg_red = rpng2_info.bg_red;
1071
uch bg_green = rpng2_info.bg_green;
1072
uch bg_blue = rpng2_info.bg_blue;
1073
uch *src, *src2=NULL, *dest;
1077
static ulg firstrow;
1079
/*---------------------------------------------------------------------------
1080
rows and firstrow simply track how many rows (and which ones) have not
1081
yet been displayed; alternatively, we could call InvalidateRect() for
1082
every row and not bother with the records-keeping.
1083
---------------------------------------------------------------------------*/
1085
Trace((stderr, "beginning rpng2_win_display_row()\n"))
1088
firstrow = row; /* first row not yet displayed */
1090
++rows; /* count of rows received but not yet displayed */
1092
/*---------------------------------------------------------------------------
1093
Aside from the use of the rpng2_info struct and the lack of an outer
1094
loop (over rows), this routine is identical to rpng_win_display_image()
1095
in the non-progressive version of the program.
1096
---------------------------------------------------------------------------*/
1098
src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1100
src2 = bg_data + row*bg_rowbytes;
1101
dest = wimage_data + row*wimage_rowbytes;
1103
if (rpng2_info.channels == 3) {
1104
for (i = rpng2_info.width; i > 0; --i) {
1109
*dest++ = g; /* note reverse order */
1112
} else /* if (rpng2_info.channels == 4) */ {
1113
for (i = rpng2_info.width; i > 0; --i) {
1127
} else if (a == 0) {
1132
/* this macro (copied from png.h) composites the
1133
* foreground and background values and puts the
1134
* result into the first argument; there are no
1135
* side effects with the first argument */
1136
alpha_composite(*dest++, b, a, bg_blue);
1137
alpha_composite(*dest++, g, a, bg_green);
1138
alpha_composite(*dest++, r, a, bg_red);
1143
/*---------------------------------------------------------------------------
1144
Display after every 16 rows or when on last row. (Region may include
1145
previously displayed lines due to interlacing--i.e., not contiguous.)
1146
---------------------------------------------------------------------------*/
1148
if ((rows & 0xf) == 0 || row == rpng2_info.height-1) {
1152
rect.top = (LONG)firstrow;
1153
rect.right = (LONG)rpng2_info.width; /* possibly off by one? */
1154
rect.bottom = (LONG)row + 1L; /* possibly off by one? */
1155
InvalidateRect(global_hwnd, &rect, FALSE);
1156
UpdateWindow(global_hwnd); /* similar to XFlush() */
1160
} /* end function rpng2_win_display_row() */
1166
static void rpng2_win_finish_display()
1168
Trace((stderr, "beginning rpng2_win_finish_display()\n"))
1170
/* last row has already been displayed by rpng2_win_display_row(), so
1171
* we have nothing to do here except set a flag and let the user know
1172
* that the image is done */
1174
rpng2_info.state = kDone;
1177
"Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n"
1179
"Done. Press mouse button 1 (within image window) to quit.\n"
1189
static void rpng2_win_cleanup()
1191
if (bg_image && bg_data) {
1196
if (rpng2_info.image_data) {
1197
free(rpng2_info.image_data);
1198
rpng2_info.image_data = NULL;
1201
if (rpng2_info.row_pointers) {
1202
free(rpng2_info.row_pointers);
1203
rpng2_info.row_pointers = NULL;
1216
LRESULT CALLBACK rpng2_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP)
1224
/* one-time processing here, if any */
1228
hdc = BeginPaint(hwnd, &ps);
1229
rc = StretchDIBits(hdc, 0, 0, rpng2_info.width, rpng2_info.height,
1230
0, 0, rpng2_info.width, rpng2_info.height,
1231
wimage_data, (BITMAPINFO *)bmih,
1233
EndPaint(hwnd, &ps);
1236
/* wait for the user to tell us when to quit */
1238
switch (wP) { /* only need one, so ignore repeat count */
1241
case 0x1B: /* Esc key */
1246
case WM_LBUTTONDOWN: /* another way of quitting */
1252
return DefWindowProc(hwnd, iMsg, wP, lP);