1
//------------------------------------
2
// VisualPng.C -- Shows a PNG image
3
//------------------------------------
5
// Copyright 2000, Willem van Schaik.
7
// This code is released under the libpng license.
8
// For conditions of distribution and use, see the disclaimer
9
// and license in png.h
15
#define PROGNAME "VisualPng"
16
#define LONGNAME "Win32 Viewer for PNG-files"
17
#define VERSION "1.0 of 2000 June 07"
30
// application includes
38
// function prototypes
40
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
41
BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;
43
BOOL CenterAbout (HWND hwndChild, HWND hwndParent);
45
BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount,
48
BOOL SearchPngList (TCHAR *pFileList, int FileCount, int *pFileIndex,
49
PTSTR pstrPrevName, PTSTR pstrNextName);
51
BOOL LoadImageFile(HWND hwnd, PTSTR pstrPathName,
52
png_byte **ppbImage, int *pxImgSize, int *pyImgSize, int *piChannels,
53
png_color *pBkgColor);
55
BOOL DisplayImage (HWND hwnd, BYTE **ppDib,
56
BYTE **ppDiData, int cxWinSize, int cyWinSize,
57
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
61
BYTE *pDiData, int cxWinSize, int cyWinSize);
64
BYTE *pDiData, int cxWinSize, int cyWinSize,
65
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
68
// a few global variables
70
static char *szProgName = PROGNAME;
71
static char *szAppName = LONGNAME;
72
static char *szIconName = PROGNAME;
73
static char szCmdFileName [MAX_PATH];
77
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
78
PSTR szCmdLine, int iCmdShow)
84
int ixBorders, iyBorders;
86
wndclass.style = CS_HREDRAW | CS_VREDRAW;
87
wndclass.lpfnWndProc = WndProc;
88
wndclass.cbClsExtra = 0;
89
wndclass.cbWndExtra = 0;
90
wndclass.hInstance = hInstance;
91
wndclass.hIcon = LoadIcon (hInstance, szIconName) ;
92
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
93
wndclass.hbrBackground = NULL; // (HBRUSH) GetStockObject (GRAY_BRUSH);
94
wndclass.lpszMenuName = szProgName;
95
wndclass.lpszClassName = szProgName;
97
if (!RegisterClass (&wndclass))
99
MessageBox (NULL, TEXT ("Error: this program requires Windows NT!"),
100
szProgName, MB_ICONERROR);
104
// if filename given on commandline, store it
105
if ((szCmdLine != NULL) && (*szCmdLine != '\0'))
106
if (szCmdLine[0] == '"')
107
strncpy (szCmdFileName, szCmdLine + 1, strlen(szCmdLine) - 2);
109
strcpy (szCmdFileName, szCmdLine);
111
strcpy (szCmdFileName, "");
113
// calculate size of window-borders
114
ixBorders = 2 * (GetSystemMetrics (SM_CXBORDER) +
115
GetSystemMetrics (SM_CXDLGFRAME));
116
iyBorders = 2 * (GetSystemMetrics (SM_CYBORDER) +
117
GetSystemMetrics (SM_CYDLGFRAME)) +
118
GetSystemMetrics (SM_CYCAPTION) +
119
GetSystemMetrics (SM_CYMENUSIZE) +
120
1; /* WvS: don't ask me why? */
122
hwnd = CreateWindow (szProgName, szAppName,
124
CW_USEDEFAULT, CW_USEDEFAULT,
125
512 + 2 * MARGIN + ixBorders, 384 + 2 * MARGIN + iyBorders,
126
// CW_USEDEFAULT, CW_USEDEFAULT,
127
NULL, NULL, hInstance, NULL);
129
ShowWindow (hwnd, iCmdShow);
132
hAccel = LoadAccelerators (hInstance, szProgName);
134
while (GetMessage (&msg, NULL, 0, 0))
136
if (!TranslateAccelerator (hwnd, hAccel, &msg))
138
TranslateMessage (&msg);
139
DispatchMessage (&msg);
145
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,
148
static HINSTANCE hInstance ;
150
static PAINTSTRUCT ps;
153
static BITMAPFILEHEADER *pbmfh;
154
static BITMAPINFOHEADER *pbmih;
155
static BYTE *pbImage;
156
static int cxWinSize, cyWinSize;
157
static int cxImgSize, cyImgSize;
158
static int cImgChannels;
159
static png_color bkgColor = {127, 127, 127};
161
static BOOL bStretched = TRUE;
163
static BYTE *pDib = NULL;
164
static BYTE *pDiData = NULL;
166
static TCHAR szImgPathName [MAX_PATH];
167
static TCHAR szTitleName [MAX_PATH];
169
static TCHAR *pPngFileList = NULL;
170
static int iPngFileCount;
171
static int iPngFileIndex;
178
hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;
179
PngFileInitialize (hwnd);
181
strcpy (szImgPathName, "");
183
// in case we process file given on command-line
185
if (szCmdFileName[0] != '\0')
187
strcpy (szImgPathName, szCmdFileName);
189
// read the other png-files in the directory for later
190
// next/previous commands
192
BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount,
195
// load the image from file
197
if (!LoadImageFile (hwnd, szImgPathName,
198
&pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
201
// invalidate the client area for later update
203
InvalidateRect (hwnd, NULL, TRUE);
205
// display the PNG into the DIBitmap
207
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
208
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
214
cxWinSize = LOWORD (lParam);
215
cyWinSize = HIWORD (lParam);
217
// invalidate the client area for later update
219
InvalidateRect (hwnd, NULL, TRUE);
221
// display the PNG into the DIBitmap
223
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
224
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
228
case WM_INITMENUPOPUP:
229
hMenu = GetMenu (hwnd);
232
EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_ENABLED);
234
EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_GRAYED);
239
hMenu = GetMenu (hwnd);
241
switch (LOWORD (wParam))
245
// show the File Open dialog box
247
if (!PngFileOpenDlg (hwnd, szImgPathName, szTitleName))
250
// read the other png-files in the directory for later
251
// next/previous commands
253
BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount,
256
// load the image from file
258
if (!LoadImageFile (hwnd, szImgPathName,
259
&pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
262
// invalidate the client area for later update
264
InvalidateRect (hwnd, NULL, TRUE);
266
// display the PNG into the DIBitmap
268
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
269
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
275
// show the File Save dialog box
277
if (!PngFileSaveDlg (hwnd, szImgPathName, szTitleName))
280
// save the PNG to a disk file
282
SetCursor (LoadCursor (NULL, IDC_WAIT));
285
bOk = PngSaveImage (szImgPathName, pDiData, cxWinSize, cyWinSize,
289
SetCursor (LoadCursor (NULL, IDC_ARROW));
292
MessageBox (hwnd, TEXT ("Error in saving the PNG image"),
293
szProgName, MB_ICONEXCLAMATION | MB_OK);
298
// read next entry in the directory
300
if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex,
301
NULL, szImgPathName))
303
if (strcmp (szImgPathName, "") == 0)
306
// load the image from file
308
if (!LoadImageFile (hwnd, szImgPathName, &pbImage,
309
&cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
312
// invalidate the client area for later update
314
InvalidateRect (hwnd, NULL, TRUE);
316
// display the PNG into the DIBitmap
318
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
319
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
324
case IDM_FILE_PREVIOUS:
326
// read previous entry in the directory
328
if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex,
329
szImgPathName, NULL))
332
if (strcmp (szImgPathName, "") == 0)
335
// load the image from file
337
if (!LoadImageFile (hwnd, szImgPathName, &pbImage, &cxImgSize,
338
&cyImgSize, &cImgChannels, &bkgColor))
341
// invalidate the client area for later update
343
InvalidateRect (hwnd, NULL, TRUE);
345
// display the PNG into the DIBitmap
347
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
348
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
355
// more cleanup needed...
367
if (pPngFileList != NULL)
379
case IDM_OPTIONS_STRETCH:
380
bStretched = !bStretched;
382
CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_CHECKED);
384
CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_UNCHECKED);
386
// invalidate the client area for later update
388
InvalidateRect (hwnd, NULL, TRUE);
390
// display the PNG into the DIBitmap
392
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
393
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
398
DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;
406
hdc = BeginPaint (hwnd, &ps);
409
SetDIBitsToDevice (hdc, 0, 0, cxWinSize, cyWinSize, 0, 0,
410
0, cyWinSize, pDiData, (BITMAPINFO *) pDib, DIB_RGB_COLORS);
412
EndPaint (hwnd, &ps);
426
return DefWindowProc (hwnd, message, wParam, lParam);
429
BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message,
430
WPARAM wParam, LPARAM lParam)
435
ShowWindow (hDlg, SW_HIDE);
436
CenterAbout (hDlg, GetWindow (hDlg, GW_OWNER));
437
ShowWindow (hDlg, SW_SHOW);
441
switch (LOWORD (wParam))
445
EndDialog (hDlg, 0) ;
457
BOOL CenterAbout (HWND hwndChild, HWND hwndParent)
459
RECT rChild, rParent, rWorkArea;
460
int wChild, hChild, wParent, hParent;
464
// Get the Height and Width of the child window
465
GetWindowRect (hwndChild, &rChild);
466
wChild = rChild.right - rChild.left;
467
hChild = rChild.bottom - rChild.top;
469
// Get the Height and Width of the parent window
470
GetWindowRect (hwndParent, &rParent);
471
wParent = rParent.right - rParent.left;
472
hParent = rParent.bottom - rParent.top;
474
// Get the limits of the 'workarea'
475
bResult = SystemParametersInfo(
476
SPI_GETWORKAREA, // system parameter to query or set
481
rWorkArea.left = rWorkArea.top = 0;
482
rWorkArea.right = GetSystemMetrics(SM_CXSCREEN);
483
rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN);
486
// Calculate new X position, then adjust for workarea
487
xNew = rParent.left + ((wParent - wChild) /2);
488
if (xNew < rWorkArea.left) {
489
xNew = rWorkArea.left;
490
} else if ((xNew+wChild) > rWorkArea.right) {
491
xNew = rWorkArea.right - wChild;
494
// Calculate new Y position, then adjust for workarea
495
yNew = rParent.top + ((hParent - hChild) /2);
496
if (yNew < rWorkArea.top) {
497
yNew = rWorkArea.top;
498
} else if ((yNew+hChild) > rWorkArea.bottom) {
499
yNew = rWorkArea.bottom - hChild;
502
// Set it, and return
503
return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE |
511
BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount,
514
static TCHAR szImgPathName [MAX_PATH];
515
static TCHAR szImgFileName [MAX_PATH];
516
static TCHAR szImgFindName [MAX_PATH];
518
WIN32_FIND_DATA finddata;
521
static TCHAR szTmp [MAX_PATH];
526
// free previous file-list
528
if (*ppFileList != NULL)
534
// extract foldername, filename and search-name
536
strcpy (szImgPathName, pstrPathName);
537
strcpy (szImgFileName, strrchr (pstrPathName, '\\') + 1);
539
strcpy (szImgFindName, szImgPathName);
540
*(strrchr (szImgFindName, '\\') + 1) = '\0';
541
strcat (szImgFindName, "*.png");
543
// first cycle: count number of files in directory for memory allocation
547
hFind = FindFirstFile(szImgFindName, &finddata);
548
bOk = (hFind != (HANDLE) -1);
553
bOk = FindNextFile(hFind, &finddata);
557
// allocation memory for file-list
559
*ppFileList = (TCHAR *) malloc (*pFileCount * MAX_PATH);
561
// second cycle: read directory and store filenames in file-list
563
hFind = FindFirstFile(szImgFindName, &finddata);
564
bOk = (hFind != (HANDLE) -1);
570
strcpy (*ppFileList + ii, szImgPathName);
571
strcpy (strrchr(*ppFileList + ii, '\\') + 1, finddata.cFileName);
573
if (strcmp(pstrPathName, *ppFileList + ii) == 0)
579
bOk = FindNextFile(hFind, &finddata);
583
// finally we must sort the file-list
585
for (i = 0; i < *pFileCount - 1; i++)
588
for (j = i+1; j < *pFileCount; j++)
591
if (strcmp (*ppFileList + ii, *ppFileList + jj) > 0)
593
strcpy (szTmp, *ppFileList + jj);
594
strcpy (*ppFileList + jj, *ppFileList + ii);
595
strcpy (*ppFileList + ii, szTmp);
597
// check if this was the current image that we moved
599
if (*pFileIndex == i)
602
if (*pFileIndex == j)
616
TCHAR *pFileList, int FileCount, int *pFileIndex,
617
PTSTR pstrPrevName, PTSTR pstrNextName)
621
// get previous entry
623
if (pstrPrevName != NULL)
628
*pFileIndex = FileCount - 1;
630
strcpy (pstrPrevName, pFileList + (*pFileIndex * MAX_PATH));
635
if (pstrNextName != NULL)
637
if (*pFileIndex < FileCount - 1)
642
strcpy (pstrNextName, pFileList + (*pFileIndex * MAX_PATH));
657
BOOL LoadImageFile (HWND hwnd, PTSTR pstrPathName,
658
png_byte **ppbImage, int *pxImgSize, int *pyImgSize,
659
int *piChannels, png_color *pBkgColor)
661
static TCHAR szTmp [MAX_PATH];
663
// if there's an existing PNG, free the memory
671
// Load the entire PNG into memory
673
SetCursor (LoadCursor (NULL, IDC_WAIT));
676
PngLoadImage (pstrPathName, ppbImage, pxImgSize, pyImgSize, piChannels,
680
SetCursor (LoadCursor (NULL, IDC_ARROW));
682
if (*ppbImage != NULL)
684
sprintf (szTmp, "VisualPng - %s", strrchr(pstrPathName, '\\') + 1);
685
SetWindowText (hwnd, szTmp);
689
MessageBox (hwnd, TEXT ("Error in loading the PNG image"),
690
szProgName, MB_ICONEXCLAMATION | MB_OK);
701
BOOL DisplayImage (HWND hwnd, BYTE **ppDib,
702
BYTE **ppDiData, int cxWinSize, int cyWinSize,
703
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
707
BYTE *pDiData = *ppDiData;
708
// BITMAPFILEHEADER *pbmfh;
709
BITMAPINFOHEADER *pbmih;
711
png_color bkgBlack = {0, 0, 0};
712
png_color bkgGray = {127, 127, 127};
713
png_color bkgWhite = {255, 255, 255};
715
// allocate memory for the Device Independant bitmap
717
wDIRowBytes = (WORD) ((3 * cxWinSize + 3L) >> 2) << 2;
725
if (!(pDib = (BYTE *) malloc (sizeof(BITMAPINFOHEADER) +
726
wDIRowBytes * cyWinSize)))
728
MessageBox (hwnd, TEXT ("Error in displaying the PNG image"),
729
szProgName, MB_ICONEXCLAMATION | MB_OK);
730
*ppDib = pDib = NULL;
734
memset (pDib, 0, sizeof(BITMAPINFOHEADER));
736
// initialize the dib-structure
738
pbmih = (BITMAPINFOHEADER *) pDib;
739
pbmih->biSize = sizeof(BITMAPINFOHEADER);
740
pbmih->biWidth = cxWinSize;
741
pbmih->biHeight = -((long) cyWinSize);
743
pbmih->biBitCount = 24;
744
pbmih->biCompression = 0;
745
pDiData = pDib + sizeof(BITMAPINFOHEADER);
748
// first fill bitmap with gray and image border
750
InitBitmap (pDiData, cxWinSize, cyWinSize);
752
// then fill bitmap with image
757
pDiData, cxWinSize, cyWinSize,
758
pbImage, cxImgSize, cyImgSize, cImgChannels,
769
BOOL InitBitmap (BYTE *pDiData, int cxWinSize, int cyWinSize)
774
// initialize the background with gray
777
for (y = 0; y < cyWinSize; y++)
780
for (x = 0; x < cxWinSize; x++)
788
// rows start on 4 byte boundaries
789
while ((col % 4) != 0)
804
BYTE *pDiData, int cxWinSize, int cyWinSize,
805
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
808
BYTE *pStretchedImage;
812
const int cDIChannels = 3;
815
int cxNewSize, cyNewSize;
816
int cxImgPos, cyImgPos;
824
cxNewSize = cxWinSize - 2 * MARGIN;
825
cyNewSize = cyWinSize - 2 * MARGIN;
827
// stretch the image to it's window determined size
829
// the following two are the same, but the first has side-effects
830
// because of rounding
831
// if ((cyNewSize / cxNewSize) > (cyImgSize / cxImgSize))
832
if ((cyNewSize * cxImgSize) > (cyImgSize * cxNewSize))
834
cyNewSize = cxNewSize * cyImgSize / cxImgSize;
836
cyImgPos = (cyWinSize - cyNewSize) / 2;
840
cxNewSize = cyNewSize * cxImgSize / cyImgSize;
842
cxImgPos = (cxWinSize - cxNewSize) / 2;
845
pStretchedImage = malloc (cImgChannels * cxNewSize * cyNewSize);
846
pImg = pStretchedImage;
848
for (yNew = 0; yNew < cyNewSize; yNew++)
850
yOld = yNew * cyImgSize / cyNewSize;
851
for (xNew = 0; xNew < cxNewSize; xNew++)
853
xOld = xNew * cxImgSize / cxNewSize;
855
r = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 0);
856
g = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 1);
857
b = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 2);
861
if (cImgChannels == 4)
863
a = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld)
870
// calculate row-bytes
872
wImgRowBytes = cImgChannels * cxNewSize;
873
wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
875
// copy image to screen
877
for (yImg = 0, yWin = cyImgPos; yImg < cyNewSize; yImg++, yWin++)
879
if (yWin >= cyWinSize - cyImgPos)
881
src = pStretchedImage + yImg * wImgRowBytes;
882
dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels;
884
for (xImg = 0, xWin = cxImgPos; xImg < cxNewSize; xImg++, xWin++)
886
if (xWin >= cxWinSize - cxImgPos)
891
*dst++ = b; /* note the reverse order */
894
if (cImgChannels == 4)
903
if (pStretchedImage != NULL)
905
free (pStretchedImage);
906
pStretchedImage = NULL;
911
// process the image not-stretched
915
// calculate the central position
917
cxImgPos = (cxWinSize - cxImgSize) / 2;
918
cyImgPos = (cyWinSize - cyImgSize) / 2;
920
// check for image larger than window
922
if (cxImgPos < MARGIN)
924
if (cyImgPos < MARGIN)
927
// calculate both row-bytes
929
wImgRowBytes = cImgChannels * cxImgSize;
930
wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
932
// copy image to screen
934
for (yImg = 0, yWin = cyImgPos; yImg < cyImgSize; yImg++, yWin++)
936
if (yWin >= cyWinSize - MARGIN)
938
src = pbImage + yImg * wImgRowBytes;
939
dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels;
941
for (xImg = 0, xWin = cxImgPos; xImg < cxImgSize; xImg++, xWin++)
943
if (xWin >= cxWinSize - MARGIN)
948
*dst++ = b; /* note the reverse order */
951
if (cImgChannels == 4)