1
//------------------------------------
2
// VisualPng.C -- Shows a PNG image
3
//------------------------------------
5
// Copyright 2000, Willem van Schaik. For conditions of distribution and
6
// use, see the copyright/license/disclaimer notice in png.h
12
#define PROGNAME "VisualPng"
13
#define LONGNAME "Win32 Viewer for PNG-files"
14
#define VERSION "1.0 of 2000 June 07"
27
// application includes
35
// function prototypes
37
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
38
BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;
40
BOOL CenterAbout (HWND hwndChild, HWND hwndParent);
42
BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount,
45
BOOL SearchPngList (TCHAR *pFileList, int FileCount, int *pFileIndex,
46
PTSTR pstrPrevName, PTSTR pstrNextName);
48
BOOL LoadImageFile(HWND hwnd, PTSTR pstrPathName,
49
png_byte **ppbImage, int *pxImgSize, int *pyImgSize, int *piChannels,
50
png_color *pBkgColor);
52
BOOL DisplayImage (HWND hwnd, BYTE **ppDib,
53
BYTE **ppDiData, int cxWinSize, int cyWinSize,
54
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
58
BYTE *pDiData, int cxWinSize, int cyWinSize);
61
BYTE *pDiData, int cxWinSize, int cyWinSize,
62
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
65
// a few global variables
67
static char *szProgName = PROGNAME;
68
static char *szAppName = LONGNAME;
69
static char *szIconName = PROGNAME;
70
static char szCmdFileName [MAX_PATH];
74
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
75
PSTR szCmdLine, int iCmdShow)
81
int ixBorders, iyBorders;
83
wndclass.style = CS_HREDRAW | CS_VREDRAW;
84
wndclass.lpfnWndProc = WndProc;
85
wndclass.cbClsExtra = 0;
86
wndclass.cbWndExtra = 0;
87
wndclass.hInstance = hInstance;
88
wndclass.hIcon = LoadIcon (hInstance, szIconName) ;
89
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
90
wndclass.hbrBackground = NULL; // (HBRUSH) GetStockObject (GRAY_BRUSH);
91
wndclass.lpszMenuName = szProgName;
92
wndclass.lpszClassName = szProgName;
94
if (!RegisterClass (&wndclass))
96
MessageBox (NULL, TEXT ("Error: this program requires Windows NT!"),
97
szProgName, MB_ICONERROR);
101
// if filename given on commandline, store it
102
if ((szCmdLine != NULL) && (*szCmdLine != '\0'))
103
if (szCmdLine[0] == '"')
104
strncpy (szCmdFileName, szCmdLine + 1, strlen(szCmdLine) - 2);
106
strcpy (szCmdFileName, szCmdLine);
108
strcpy (szCmdFileName, "");
110
// calculate size of window-borders
111
ixBorders = 2 * (GetSystemMetrics (SM_CXBORDER) +
112
GetSystemMetrics (SM_CXDLGFRAME));
113
iyBorders = 2 * (GetSystemMetrics (SM_CYBORDER) +
114
GetSystemMetrics (SM_CYDLGFRAME)) +
115
GetSystemMetrics (SM_CYCAPTION) +
116
GetSystemMetrics (SM_CYMENUSIZE) +
117
1; /* WvS: don't ask me why? */
119
hwnd = CreateWindow (szProgName, szAppName,
121
CW_USEDEFAULT, CW_USEDEFAULT,
122
512 + 2 * MARGIN + ixBorders, 384 + 2 * MARGIN + iyBorders,
123
// CW_USEDEFAULT, CW_USEDEFAULT,
124
NULL, NULL, hInstance, NULL);
126
ShowWindow (hwnd, iCmdShow);
129
hAccel = LoadAccelerators (hInstance, szProgName);
131
while (GetMessage (&msg, NULL, 0, 0))
133
if (!TranslateAccelerator (hwnd, hAccel, &msg))
135
TranslateMessage (&msg);
136
DispatchMessage (&msg);
142
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,
145
static HINSTANCE hInstance ;
147
static PAINTSTRUCT ps;
150
static BITMAPFILEHEADER *pbmfh;
151
static BITMAPINFOHEADER *pbmih;
152
static BYTE *pbImage;
153
static int cxWinSize, cyWinSize;
154
static int cxImgSize, cyImgSize;
155
static int cImgChannels;
156
static png_color bkgColor = {127, 127, 127};
158
static BOOL bStretched = TRUE;
160
static BYTE *pDib = NULL;
161
static BYTE *pDiData = NULL;
163
static TCHAR szImgPathName [MAX_PATH];
164
static TCHAR szTitleName [MAX_PATH];
166
static TCHAR *pPngFileList = NULL;
167
static int iPngFileCount;
168
static int iPngFileIndex;
175
hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;
176
PngFileInitialize (hwnd);
178
strcpy (szImgPathName, "");
180
// in case we process file given on command-line
182
if (szCmdFileName[0] != '\0')
184
strcpy (szImgPathName, szCmdFileName);
186
// read the other png-files in the directory for later
187
// next/previous commands
189
BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount,
192
// load the image from file
194
if (!LoadImageFile (hwnd, szImgPathName,
195
&pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
198
// invalidate the client area for later update
200
InvalidateRect (hwnd, NULL, TRUE);
202
// display the PNG into the DIBitmap
204
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
205
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
211
cxWinSize = LOWORD (lParam);
212
cyWinSize = HIWORD (lParam);
214
// invalidate the client area for later update
216
InvalidateRect (hwnd, NULL, TRUE);
218
// display the PNG into the DIBitmap
220
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
221
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
225
case WM_INITMENUPOPUP:
226
hMenu = GetMenu (hwnd);
229
EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_ENABLED);
231
EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_GRAYED);
236
hMenu = GetMenu (hwnd);
238
switch (LOWORD (wParam))
242
// show the File Open dialog box
244
if (!PngFileOpenDlg (hwnd, szImgPathName, szTitleName))
247
// read the other png-files in the directory for later
248
// next/previous commands
250
BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount,
253
// load the image from file
255
if (!LoadImageFile (hwnd, szImgPathName,
256
&pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
259
// invalidate the client area for later update
261
InvalidateRect (hwnd, NULL, TRUE);
263
// display the PNG into the DIBitmap
265
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
266
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
272
// show the File Save dialog box
274
if (!PngFileSaveDlg (hwnd, szImgPathName, szTitleName))
277
// save the PNG to a disk file
279
SetCursor (LoadCursor (NULL, IDC_WAIT));
282
bOk = PngSaveImage (szImgPathName, pDiData, cxWinSize, cyWinSize,
286
SetCursor (LoadCursor (NULL, IDC_ARROW));
289
MessageBox (hwnd, TEXT ("Error in saving the PNG image"),
290
szProgName, MB_ICONEXCLAMATION | MB_OK);
295
// read next entry in the directory
297
if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex,
298
NULL, szImgPathName))
300
if (strcmp (szImgPathName, "") == 0)
303
// load the image from file
305
if (!LoadImageFile (hwnd, szImgPathName, &pbImage,
306
&cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
309
// invalidate the client area for later update
311
InvalidateRect (hwnd, NULL, TRUE);
313
// display the PNG into the DIBitmap
315
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
316
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
321
case IDM_FILE_PREVIOUS:
323
// read previous entry in the directory
325
if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex,
326
szImgPathName, NULL))
329
if (strcmp (szImgPathName, "") == 0)
332
// load the image from file
334
if (!LoadImageFile (hwnd, szImgPathName, &pbImage, &cxImgSize,
335
&cyImgSize, &cImgChannels, &bkgColor))
338
// invalidate the client area for later update
340
InvalidateRect (hwnd, NULL, TRUE);
342
// display the PNG into the DIBitmap
344
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
345
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
352
// more cleanup needed...
364
if (pPngFileList != NULL)
376
case IDM_OPTIONS_STRETCH:
377
bStretched = !bStretched;
379
CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_CHECKED);
381
CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_UNCHECKED);
383
// invalidate the client area for later update
385
InvalidateRect (hwnd, NULL, TRUE);
387
// display the PNG into the DIBitmap
389
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
390
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
395
DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;
403
hdc = BeginPaint (hwnd, &ps);
406
SetDIBitsToDevice (hdc, 0, 0, cxWinSize, cyWinSize, 0, 0,
407
0, cyWinSize, pDiData, (BITMAPINFO *) pDib, DIB_RGB_COLORS);
409
EndPaint (hwnd, &ps);
423
return DefWindowProc (hwnd, message, wParam, lParam);
426
BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message,
427
WPARAM wParam, LPARAM lParam)
432
ShowWindow (hDlg, SW_HIDE);
433
CenterAbout (hDlg, GetWindow (hDlg, GW_OWNER));
434
ShowWindow (hDlg, SW_SHOW);
438
switch (LOWORD (wParam))
442
EndDialog (hDlg, 0) ;
454
BOOL CenterAbout (HWND hwndChild, HWND hwndParent)
456
RECT rChild, rParent, rWorkArea;
457
int wChild, hChild, wParent, hParent;
461
// Get the Height and Width of the child window
462
GetWindowRect (hwndChild, &rChild);
463
wChild = rChild.right - rChild.left;
464
hChild = rChild.bottom - rChild.top;
466
// Get the Height and Width of the parent window
467
GetWindowRect (hwndParent, &rParent);
468
wParent = rParent.right - rParent.left;
469
hParent = rParent.bottom - rParent.top;
471
// Get the limits of the 'workarea'
472
bResult = SystemParametersInfo(
473
SPI_GETWORKAREA, // system parameter to query or set
478
rWorkArea.left = rWorkArea.top = 0;
479
rWorkArea.right = GetSystemMetrics(SM_CXSCREEN);
480
rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN);
483
// Calculate new X position, then adjust for workarea
484
xNew = rParent.left + ((wParent - wChild) /2);
485
if (xNew < rWorkArea.left) {
486
xNew = rWorkArea.left;
487
} else if ((xNew+wChild) > rWorkArea.right) {
488
xNew = rWorkArea.right - wChild;
491
// Calculate new Y position, then adjust for workarea
492
yNew = rParent.top + ((hParent - hChild) /2);
493
if (yNew < rWorkArea.top) {
494
yNew = rWorkArea.top;
495
} else if ((yNew+hChild) > rWorkArea.bottom) {
496
yNew = rWorkArea.bottom - hChild;
499
// Set it, and return
500
return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE |
508
BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount,
511
static TCHAR szImgPathName [MAX_PATH];
512
static TCHAR szImgFileName [MAX_PATH];
513
static TCHAR szImgFindName [MAX_PATH];
515
WIN32_FIND_DATA finddata;
518
static TCHAR szTmp [MAX_PATH];
523
// free previous file-list
525
if (*ppFileList != NULL)
531
// extract foldername, filename and search-name
533
strcpy (szImgPathName, pstrPathName);
534
strcpy (szImgFileName, strrchr (pstrPathName, '\\') + 1);
536
strcpy (szImgFindName, szImgPathName);
537
*(strrchr (szImgFindName, '\\') + 1) = '\0';
538
strcat (szImgFindName, "*.png");
540
// first cycle: count number of files in directory for memory allocation
544
hFind = FindFirstFile(szImgFindName, &finddata);
545
bOk = (hFind != (HANDLE) -1);
550
bOk = FindNextFile(hFind, &finddata);
554
// allocation memory for file-list
556
*ppFileList = (TCHAR *) malloc (*pFileCount * MAX_PATH);
558
// second cycle: read directory and store filenames in file-list
560
hFind = FindFirstFile(szImgFindName, &finddata);
561
bOk = (hFind != (HANDLE) -1);
567
strcpy (*ppFileList + ii, szImgPathName);
568
strcpy (strrchr(*ppFileList + ii, '\\') + 1, finddata.cFileName);
570
if (strcmp(pstrPathName, *ppFileList + ii) == 0)
576
bOk = FindNextFile(hFind, &finddata);
580
// finally we must sort the file-list
582
for (i = 0; i < *pFileCount - 1; i++)
585
for (j = i+1; j < *pFileCount; j++)
588
if (strcmp (*ppFileList + ii, *ppFileList + jj) > 0)
590
strcpy (szTmp, *ppFileList + jj);
591
strcpy (*ppFileList + jj, *ppFileList + ii);
592
strcpy (*ppFileList + ii, szTmp);
594
// check if this was the current image that we moved
596
if (*pFileIndex == i)
599
if (*pFileIndex == j)
613
TCHAR *pFileList, int FileCount, int *pFileIndex,
614
PTSTR pstrPrevName, PTSTR pstrNextName)
618
// get previous entry
620
if (pstrPrevName != NULL)
625
*pFileIndex = FileCount - 1;
627
strcpy (pstrPrevName, pFileList + (*pFileIndex * MAX_PATH));
632
if (pstrNextName != NULL)
634
if (*pFileIndex < FileCount - 1)
639
strcpy (pstrNextName, pFileList + (*pFileIndex * MAX_PATH));
654
BOOL LoadImageFile (HWND hwnd, PTSTR pstrPathName,
655
png_byte **ppbImage, int *pxImgSize, int *pyImgSize,
656
int *piChannels, png_color *pBkgColor)
658
static TCHAR szTmp [MAX_PATH];
660
// if there's an existing PNG, free the memory
668
// Load the entire PNG into memory
670
SetCursor (LoadCursor (NULL, IDC_WAIT));
673
PngLoadImage (pstrPathName, ppbImage, pxImgSize, pyImgSize, piChannels,
677
SetCursor (LoadCursor (NULL, IDC_ARROW));
679
if (*ppbImage != NULL)
681
sprintf (szTmp, "VisualPng - %s", strrchr(pstrPathName, '\\') + 1);
682
SetWindowText (hwnd, szTmp);
686
MessageBox (hwnd, TEXT ("Error in loading the PNG image"),
687
szProgName, MB_ICONEXCLAMATION | MB_OK);
698
BOOL DisplayImage (HWND hwnd, BYTE **ppDib,
699
BYTE **ppDiData, int cxWinSize, int cyWinSize,
700
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
704
BYTE *pDiData = *ppDiData;
705
// BITMAPFILEHEADER *pbmfh;
706
BITMAPINFOHEADER *pbmih;
708
png_color bkgBlack = {0, 0, 0};
709
png_color bkgGray = {127, 127, 127};
710
png_color bkgWhite = {255, 255, 255};
712
// allocate memory for the Device Independant bitmap
714
wDIRowBytes = (WORD) ((3 * cxWinSize + 3L) >> 2) << 2;
722
if (!(pDib = (BYTE *) malloc (sizeof(BITMAPINFOHEADER) +
723
wDIRowBytes * cyWinSize)))
725
MessageBox (hwnd, TEXT ("Error in displaying the PNG image"),
726
szProgName, MB_ICONEXCLAMATION | MB_OK);
727
*ppDib = pDib = NULL;
731
memset (pDib, 0, sizeof(BITMAPINFOHEADER));
733
// initialize the dib-structure
735
pbmih = (BITMAPINFOHEADER *) pDib;
736
pbmih->biSize = sizeof(BITMAPINFOHEADER);
737
pbmih->biWidth = cxWinSize;
738
pbmih->biHeight = -((long) cyWinSize);
740
pbmih->biBitCount = 24;
741
pbmih->biCompression = 0;
742
pDiData = pDib + sizeof(BITMAPINFOHEADER);
745
// first fill bitmap with gray and image border
747
InitBitmap (pDiData, cxWinSize, cyWinSize);
749
// then fill bitmap with image
754
pDiData, cxWinSize, cyWinSize,
755
pbImage, cxImgSize, cyImgSize, cImgChannels,
766
BOOL InitBitmap (BYTE *pDiData, int cxWinSize, int cyWinSize)
771
// initialize the background with gray
774
for (y = 0; y < cyWinSize; y++)
777
for (x = 0; x < cxWinSize; x++)
785
// rows start on 4 byte boundaries
786
while ((col % 4) != 0)
801
BYTE *pDiData, int cxWinSize, int cyWinSize,
802
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
805
BYTE *pStretchedImage;
809
const int cDIChannels = 3;
812
int cxNewSize, cyNewSize;
813
int cxImgPos, cyImgPos;
821
cxNewSize = cxWinSize - 2 * MARGIN;
822
cyNewSize = cyWinSize - 2 * MARGIN;
824
// stretch the image to it's window determined size
826
// the following two are the same, but the first has side-effects
827
// because of rounding
828
// if ((cyNewSize / cxNewSize) > (cyImgSize / cxImgSize))
829
if ((cyNewSize * cxImgSize) > (cyImgSize * cxNewSize))
831
cyNewSize = cxNewSize * cyImgSize / cxImgSize;
833
cyImgPos = (cyWinSize - cyNewSize) / 2;
837
cxNewSize = cyNewSize * cxImgSize / cyImgSize;
839
cxImgPos = (cxWinSize - cxNewSize) / 2;
842
pStretchedImage = malloc (cImgChannels * cxNewSize * cyNewSize);
843
pImg = pStretchedImage;
845
for (yNew = 0; yNew < cyNewSize; yNew++)
847
yOld = yNew * cyImgSize / cyNewSize;
848
for (xNew = 0; xNew < cxNewSize; xNew++)
850
xOld = xNew * cxImgSize / cxNewSize;
852
r = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 0);
853
g = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 1);
854
b = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 2);
858
if (cImgChannels == 4)
860
a = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld)
867
// calculate row-bytes
869
wImgRowBytes = cImgChannels * cxNewSize;
870
wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
872
// copy image to screen
874
for (yImg = 0, yWin = cyImgPos; yImg < cyNewSize; yImg++, yWin++)
876
if (yWin >= cyWinSize - cyImgPos)
878
src = pStretchedImage + yImg * wImgRowBytes;
879
dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels;
881
for (xImg = 0, xWin = cxImgPos; xImg < cxNewSize; xImg++, xWin++)
883
if (xWin >= cxWinSize - cxImgPos)
888
*dst++ = b; /* note the reverse order */
891
if (cImgChannels == 4)
900
if (pStretchedImage != NULL)
902
free (pStretchedImage);
903
pStretchedImage = NULL;
908
// process the image not-stretched
912
// calculate the central position
914
cxImgPos = (cxWinSize - cxImgSize) / 2;
915
cyImgPos = (cyWinSize - cyImgSize) / 2;
917
// check for image larger than window
919
if (cxImgPos < MARGIN)
921
if (cyImgPos < MARGIN)
924
// calculate both row-bytes
926
wImgRowBytes = cImgChannels * cxImgSize;
927
wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
929
// copy image to screen
931
for (yImg = 0, yWin = cyImgPos; yImg < cyImgSize; yImg++, yWin++)
933
if (yWin >= cyWinSize - MARGIN)
935
src = pbImage + yImg * wImgRowBytes;
936
dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels;
938
for (xImg = 0, xWin = cxImgPos; xImg < cxImgSize; xImg++, xWin++)
940
if (xWin >= cxWinSize - MARGIN)
945
*dst++ = b; /* note the reverse order */
948
if (cImgChannels == 4)