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)