1 /*------------------------------------
2  *  VisualPng.C -- Shows a PNG image
3  *------------------------------------
4  *
5  * Copyright 2000,2017 Willem van Schaik.
6  *
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
10  */
11 
12 /* switches */
13 
14 /* defines */
15 
16 #define PROGNAME  "VisualPng"
17 #define LONGNAME  "Win32 Viewer for PNG-files"
18 #define VERSION   "1.0 of 2000 June 07"
19 
20 /* constants */
21 
22 #define MARGIN 8
23 
24 /* standard includes */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <windows.h>
30 #include <zlib.h>
31 
32 /* application includes */
33 
34 #include "png.h"
35 #include "pngfile.h"
36 #include "resource.h"
37 
38 /* macros */
39 
40 /* function prototypes */
41 
42 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
43 BOOL    CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;
44 
45 BOOL CenterAbout (HWND hwndChild, HWND hwndParent);
46 
47 BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount,
48         int *pFileIndex);
49 
50 BOOL SearchPngList (TCHAR *pFileList, int FileCount, int *pFileIndex,
51         PTSTR pstrPrevName, PTSTR pstrNextName);
52 
53 BOOL LoadImageFile(HWND hwnd, PTSTR pstrPathName,
54         png_byte **ppbImage, int *pxImgSize, int *pyImgSize, int *piChannels,
55         png_color *pBkgColor);
56 
57 BOOL DisplayImage (HWND hwnd, BYTE **ppDib,
58         BYTE **ppDiData, int cxWinSize, int cyWinSize,
59         BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
60         BOOL bStretched);
61 
62 BOOL InitBitmap (
63         BYTE *pDiData, int cxWinSize, int cyWinSize);
64 
65 BOOL FillBitmap (
66         BYTE *pDiData, int cxWinSize, int cyWinSize,
67         BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
68         BOOL bStretched);
69 
70 /* a few global variables */
71 
72 static char *szProgName = PROGNAME;
73 static char *szAppName = LONGNAME;
74 static char *szIconName = PROGNAME;
75 static char szCmdFileName [MAX_PATH];
76 
77 /* MAIN routine */
78 
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)79 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
80                     PSTR szCmdLine, int iCmdShow)
81 {
82     HACCEL   hAccel;
83     HWND     hwnd;
84     MSG      msg;
85     WNDCLASS wndclass;
86     int ixBorders, iyBorders;
87 
88     wndclass.style         = CS_HREDRAW | CS_VREDRAW;
89     wndclass.lpfnWndProc   = WndProc;
90     wndclass.cbClsExtra    = 0;
91     wndclass.cbWndExtra    = 0;
92     wndclass.hInstance     = hInstance;
93     wndclass.hIcon         = LoadIcon (hInstance, szIconName) ;
94     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
95     wndclass.hbrBackground = NULL; /* (HBRUSH) GetStockObject (GRAY_BRUSH); */
96     wndclass.lpszMenuName  = szProgName;
97     wndclass.lpszClassName = szProgName;
98 
99     if (!RegisterClass (&wndclass))
100     {
101         MessageBox (NULL, TEXT ("Error: this program requires Windows NT!"),
102             szProgName, MB_ICONERROR);
103         return 0;
104     }
105 
106     /* if filename given on commandline, store it */
107     if ((szCmdLine != NULL) && (*szCmdLine != '\0'))
108         if (szCmdLine[0] == '"')
109             strncpy (szCmdFileName, szCmdLine + 1, strlen(szCmdLine) - 2);
110         else
111             strcpy (szCmdFileName, szCmdLine);
112     else
113         strcpy (szCmdFileName, "");
114 
115     /* calculate size of window-borders */
116     ixBorders = 2 * (GetSystemMetrics (SM_CXBORDER) +
117                      GetSystemMetrics (SM_CXDLGFRAME));
118     iyBorders = 2 * (GetSystemMetrics (SM_CYBORDER) +
119                      GetSystemMetrics (SM_CYDLGFRAME)) +
120                      GetSystemMetrics (SM_CYCAPTION) +
121                      GetSystemMetrics (SM_CYMENUSIZE) +
122                      1; /* WvS: don't ask me why?  */
123 
124     hwnd = CreateWindow (szProgName, szAppName,
125         WS_OVERLAPPEDWINDOW,
126         CW_USEDEFAULT, CW_USEDEFAULT,
127         512 + 2 * MARGIN + ixBorders, 384 + 2 * MARGIN + iyBorders,
128 /*      CW_USEDEFAULT, CW_USEDEFAULT, */
129         NULL, NULL, hInstance, NULL);
130 
131     ShowWindow (hwnd, iCmdShow);
132     UpdateWindow (hwnd);
133 
134     hAccel = LoadAccelerators (hInstance, szProgName);
135 
136     while (GetMessage (&msg, NULL, 0, 0))
137     {
138         if (!TranslateAccelerator (hwnd, hAccel, &msg))
139         {
140             TranslateMessage (&msg);
141             DispatchMessage (&msg);
142         }
143     }
144     return msg.wParam;
145 }
146 
WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)147 LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,
148         LPARAM lParam)
149 {
150     static HINSTANCE          hInstance ;
151     static HDC                hdc;
152     static PAINTSTRUCT        ps;
153     static HMENU              hMenu;
154 
155     static BITMAPFILEHEADER  *pbmfh;
156     static BITMAPINFOHEADER  *pbmih;
157     static BYTE              *pbImage;
158     static int                cxWinSize, cyWinSize;
159     static int                cxImgSize, cyImgSize;
160     static int                cImgChannels;
161     static png_color          bkgColor = {127, 127, 127};
162 
163     static BOOL               bStretched = TRUE;
164 
165     static BYTE              *pDib = NULL;
166     static BYTE              *pDiData = NULL;
167 
168     static TCHAR              szImgPathName [MAX_PATH];
169     static TCHAR              szTitleName [MAX_PATH];
170 
171     static TCHAR             *pPngFileList = NULL;
172     static int                iPngFileCount;
173     static int                iPngFileIndex;
174 
175     BOOL                      bOk;
176 
177     switch (message)
178     {
179     case WM_CREATE:
180         hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;
181         PngFileInitialize (hwnd);
182 
183         strcpy (szImgPathName, "");
184 
185         /* in case we process file given on command-line */
186 
187         if (szCmdFileName[0] != '\0')
188         {
189             strcpy (szImgPathName, szCmdFileName);
190 
191             /* read the other png-files in the directory for later */
192             /* next/previous commands */
193 
194             BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount,
195                           &iPngFileIndex);
196 
197             /* load the image from file */
198 
199             if (!LoadImageFile (hwnd, szImgPathName,
200                 &pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
201                 return 0;
202 
203             /* invalidate the client area for later update */
204 
205             InvalidateRect (hwnd, NULL, TRUE);
206 
207             /* display the PNG into the DIBitmap */
208 
209             DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
210                 pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
211         }
212 
213         return 0;
214 
215     case WM_SIZE:
216         cxWinSize = LOWORD (lParam);
217         cyWinSize = HIWORD (lParam);
218 
219         /* invalidate the client area for later update */
220 
221         InvalidateRect (hwnd, NULL, TRUE);
222 
223         /* display the PNG into the DIBitmap */
224 
225         DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
226             pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
227 
228         return 0;
229 
230     case WM_INITMENUPOPUP:
231         hMenu = GetMenu (hwnd);
232 
233         if (pbImage)
234             EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_ENABLED);
235         else
236             EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_GRAYED);
237 
238         return 0;
239 
240     case WM_COMMAND:
241         hMenu = GetMenu (hwnd);
242 
243         switch (LOWORD (wParam))
244         {
245         case IDM_FILE_OPEN:
246 
247             /* show the File Open dialog box */
248 
249             if (!PngFileOpenDlg (hwnd, szImgPathName, szTitleName))
250                 return 0;
251 
252             /* read the other png-files in the directory for later */
253             /* next/previous commands */
254 
255             BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount,
256                           &iPngFileIndex);
257 
258             /* load the image from file */
259 
260             if (!LoadImageFile (hwnd, szImgPathName,
261                 &pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
262                 return 0;
263 
264             /* invalidate the client area for later update */
265 
266             InvalidateRect (hwnd, NULL, TRUE);
267 
268             /* display the PNG into the DIBitmap */
269 
270             DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
271                 pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
272 
273             return 0;
274 
275         case IDM_FILE_SAVE:
276 
277             /* show the File Save dialog box */
278 
279             if (!PngFileSaveDlg (hwnd, szImgPathName, szTitleName))
280                 return 0;
281 
282             /* save the PNG to a disk file */
283 
284             SetCursor (LoadCursor (NULL, IDC_WAIT));
285             ShowCursor (TRUE);
286 
287             bOk = PngSaveImage (szImgPathName, pDiData, cxWinSize, cyWinSize,
288                   bkgColor);
289 
290             ShowCursor (FALSE);
291             SetCursor (LoadCursor (NULL, IDC_ARROW));
292 
293             if (!bOk)
294                 MessageBox (hwnd, TEXT ("Error in saving the PNG image"),
295                 szProgName, MB_ICONEXCLAMATION | MB_OK);
296             return 0;
297 
298         case IDM_FILE_NEXT:
299 
300             /* read next entry in the directory */
301 
302             if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex,
303                 NULL, szImgPathName))
304             {
305                 if (strcmp (szImgPathName, "") == 0)
306                     return 0;
307 
308                 /* load the image from file */
309 
310                 if (!LoadImageFile (hwnd, szImgPathName, &pbImage,
311                         &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
312                     return 0;
313 
314                 /* invalidate the client area for later update */
315 
316                 InvalidateRect (hwnd, NULL, TRUE);
317 
318                 /* display the PNG into the DIBitmap */
319 
320                 DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
321                     pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
322             }
323 
324             return 0;
325 
326         case IDM_FILE_PREVIOUS:
327 
328             /* read previous entry in the directory */
329 
330             if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex,
331                 szImgPathName, NULL))
332             {
333 
334                 if (strcmp (szImgPathName, "") == 0)
335                     return 0;
336 
337                 /* load the image from file */
338 
339                 if (!LoadImageFile (hwnd, szImgPathName, &pbImage, &cxImgSize,
340                     &cyImgSize, &cImgChannels, &bkgColor))
341                     return 0;
342 
343                 /* invalidate the client area for later update */
344 
345                 InvalidateRect (hwnd, NULL, TRUE);
346 
347                 /* display the PNG into the DIBitmap */
348 
349                 DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
350                     pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
351             }
352 
353             return 0;
354 
355         case IDM_FILE_EXIT:
356 
357             /* more cleanup needed... */
358 
359             /* free image buffer */
360 
361             if (pDib != NULL)
362             {
363                 free (pDib);
364                 pDib = NULL;
365             }
366 
367             /* free file-list */
368 
369             if (pPngFileList != NULL)
370             {
371                 free (pPngFileList);
372                 pPngFileList = NULL;
373             }
374 
375             /* let's go ... */
376 
377             exit (0);
378 
379             return 0;
380 
381         case IDM_OPTIONS_STRETCH:
382             bStretched = !bStretched;
383             if (bStretched)
384                 CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_CHECKED);
385             else
386                 CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_UNCHECKED);
387 
388             /* invalidate the client area for later update */
389 
390             InvalidateRect (hwnd, NULL, TRUE);
391 
392             /* display the PNG into the DIBitmap */
393 
394             DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
395                 pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
396 
397             return 0;
398 
399         case IDM_HELP_ABOUT:
400             DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;
401             return 0;
402 
403         } /* end switch */
404 
405         break;
406 
407     case WM_PAINT:
408         hdc = BeginPaint (hwnd, &ps);
409 
410         if (pDib)
411             SetDIBitsToDevice (hdc, 0, 0, cxWinSize, cyWinSize, 0, 0,
412                 0, cyWinSize, pDiData, (BITMAPINFO *) pDib, DIB_RGB_COLORS);
413 
414         EndPaint (hwnd, &ps);
415         return 0;
416 
417     case WM_DESTROY:
418         if (pbmfh)
419         {
420             free (pbmfh);
421             pbmfh = NULL;
422         }
423 
424         PostQuitMessage (0);
425         return 0;
426     }
427 
428     return DefWindowProc (hwnd, message, wParam, lParam);
429 }
430 
AboutDlgProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam)431 BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message,
432                             WPARAM wParam, LPARAM lParam)
433 {
434      switch (message)
435      {
436      case WM_INITDIALOG :
437           ShowWindow (hDlg, SW_HIDE);
438           CenterAbout (hDlg, GetWindow (hDlg, GW_OWNER));
439           ShowWindow (hDlg, SW_SHOW);
440           return TRUE ;
441 
442      case WM_COMMAND :
443           switch (LOWORD (wParam))
444           {
445           case IDOK :
446           case IDCANCEL :
447                EndDialog (hDlg, 0) ;
448                return TRUE ;
449           }
450           break ;
451      }
452      return FALSE ;
453 }
454 
455 /*---------------
456  *  CenterAbout
457  *---------------
458  */
CenterAbout(HWND hwndChild,HWND hwndParent)459 BOOL CenterAbout (HWND hwndChild, HWND hwndParent)
460 {
461    RECT    rChild, rParent, rWorkArea;
462    int     wChild, hChild, wParent, hParent;
463    int     xNew, yNew;
464    BOOL  bResult;
465 
466    /* Get the Height and Width of the child window */
467    GetWindowRect (hwndChild, &rChild);
468    wChild = rChild.right - rChild.left;
469    hChild = rChild.bottom - rChild.top;
470 
471    /* Get the Height and Width of the parent window */
472    GetWindowRect (hwndParent, &rParent);
473    wParent = rParent.right - rParent.left;
474    hParent = rParent.bottom - rParent.top;
475 
476    /* Get the limits of the 'workarea' */
477    bResult = SystemParametersInfo(
478       SPI_GETWORKAREA,  /* system parameter to query or set */
479       sizeof(RECT),
480       &rWorkArea,
481       0);
482    if (!bResult) {
483       rWorkArea.left = rWorkArea.top = 0;
484       rWorkArea.right = GetSystemMetrics(SM_CXSCREEN);
485       rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN);
486    }
487 
488    /* Calculate new X position, then adjust for workarea */
489    xNew = rParent.left + ((wParent - wChild) /2);
490    if (xNew < rWorkArea.left) {
491       xNew = rWorkArea.left;
492    } else if ((xNew+wChild) > rWorkArea.right) {
493       xNew = rWorkArea.right - wChild;
494    }
495 
496    /* Calculate new Y position, then adjust for workarea */
497    yNew = rParent.top  + ((hParent - hChild) /2);
498    if (yNew < rWorkArea.top) {
499       yNew = rWorkArea.top;
500    } else if ((yNew+hChild) > rWorkArea.bottom) {
501       yNew = rWorkArea.bottom - hChild;
502    }
503 
504    /* Set it, and return */
505    return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE |
506           SWP_NOZORDER);
507 }
508 
509 /*----------------
510  *  BuildPngList
511  *----------------
512  */
BuildPngList(PTSTR pstrPathName,TCHAR ** ppFileList,int * pFileCount,int * pFileIndex)513 BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount,
514      int *pFileIndex)
515 {
516     static TCHAR              szImgPathName [MAX_PATH];
517     static TCHAR              szImgFileName [MAX_PATH];
518     static TCHAR              szImgFindName [MAX_PATH];
519 
520     WIN32_FIND_DATA           finddata;
521     HANDLE                    hFind;
522 
523     static TCHAR              szTmp [MAX_PATH];
524     BOOL                      bOk;
525     int                       i, ii;
526     int                       j, jj;
527 
528     /* free previous file-list */
529 
530     if (*ppFileList != NULL)
531     {
532         free (*ppFileList);
533         *ppFileList = NULL;
534     }
535 
536     /* extract foldername, filename and search-name */
537 
538     strcpy (szImgPathName, pstrPathName);
539     strcpy (szImgFileName, strrchr (pstrPathName, '\\') + 1);
540 
541     strcpy (szImgFindName, szImgPathName);
542     *(strrchr (szImgFindName, '\\') + 1) = '\0';
543     strcat (szImgFindName, "*.png");
544 
545     /* first cycle: count number of files in directory for memory allocation */
546 
547     *pFileCount = 0;
548 
549     hFind = FindFirstFile(szImgFindName, &finddata);
550     bOk = (hFind != (HANDLE) -1);
551 
552     while (bOk)
553     {
554         *pFileCount += 1;
555         bOk = FindNextFile(hFind, &finddata);
556     }
557     FindClose(hFind);
558 
559     /* allocation memory for file-list */
560 
561     *ppFileList = (TCHAR *) malloc (*pFileCount * MAX_PATH);
562 
563     /* second cycle: read directory and store filenames in file-list */
564 
565     hFind = FindFirstFile(szImgFindName, &finddata);
566     bOk = (hFind != (HANDLE) -1);
567 
568     i = 0;
569     ii = 0;
570     while (bOk)
571     {
572         strcpy (*ppFileList + ii, szImgPathName);
573         strcpy (strrchr(*ppFileList + ii, '\\') + 1, finddata.cFileName);
574 
575         if (strcmp(pstrPathName, *ppFileList + ii) == 0)
576             *pFileIndex = i;
577 
578         ii += MAX_PATH;
579         i++;
580 
581         bOk = FindNextFile(hFind, &finddata);
582     }
583     FindClose(hFind);
584 
585     /* finally we must sort the file-list */
586 
587     for (i = 0; i < *pFileCount - 1; i++)
588     {
589         ii = i * MAX_PATH;
590         for (j = i+1; j < *pFileCount; j++)
591         {
592             jj = j * MAX_PATH;
593             if (strcmp (*ppFileList + ii, *ppFileList + jj) > 0)
594             {
595                 strcpy (szTmp, *ppFileList + jj);
596                 strcpy (*ppFileList + jj, *ppFileList + ii);
597                 strcpy (*ppFileList + ii, szTmp);
598 
599                 /* check if this was the current image that we moved */
600 
601                 if (*pFileIndex == i)
602                     *pFileIndex = j;
603                 else
604                     if (*pFileIndex == j)
605                         *pFileIndex = i;
606             }
607         }
608     }
609 
610     return TRUE;
611 }
612 
613 /*----------------
614  *  SearchPngList
615  *----------------
616  */
617 
SearchPngList(TCHAR * pFileList,int FileCount,int * pFileIndex,PTSTR pstrPrevName,PTSTR pstrNextName)618 BOOL SearchPngList (
619         TCHAR *pFileList, int FileCount, int *pFileIndex,
620         PTSTR pstrPrevName, PTSTR pstrNextName)
621 {
622     if (FileCount > 0)
623     {
624         /* get previous entry */
625 
626         if (pstrPrevName != NULL)
627         {
628             if (*pFileIndex > 0)
629                 *pFileIndex -= 1;
630             else
631                 *pFileIndex = FileCount - 1;
632 
633             strcpy (pstrPrevName, pFileList + (*pFileIndex * MAX_PATH));
634         }
635 
636         /* get next entry */
637 
638         if (pstrNextName != NULL)
639         {
640             if (*pFileIndex < FileCount - 1)
641                 *pFileIndex += 1;
642             else
643                 *pFileIndex = 0;
644 
645             strcpy (pstrNextName, pFileList + (*pFileIndex * MAX_PATH));
646         }
647 
648         return TRUE;
649     }
650     else
651     {
652         return FALSE;
653     }
654 }
655 
656 /*-----------------
657  *  LoadImageFile
658  *-----------------
659  */
660 
LoadImageFile(HWND hwnd,PTSTR pstrPathName,png_byte ** ppbImage,int * pxImgSize,int * pyImgSize,int * piChannels,png_color * pBkgColor)661 BOOL LoadImageFile (HWND hwnd, PTSTR pstrPathName,
662                 png_byte **ppbImage, int *pxImgSize, int *pyImgSize,
663                 int *piChannels, png_color *pBkgColor)
664 {
665     static TCHAR szTmp [MAX_PATH];
666 
667     /* if there's an existing PNG, free the memory */
668 
669     if (*ppbImage)
670     {
671         free (*ppbImage);
672         *ppbImage = NULL;
673     }
674 
675     /* Load the entire PNG into memory */
676 
677     SetCursor (LoadCursor (NULL, IDC_WAIT));
678     ShowCursor (TRUE);
679 
680     PngLoadImage (pstrPathName, ppbImage, pxImgSize, pyImgSize, piChannels,
681                   pBkgColor);
682 
683     ShowCursor (FALSE);
684     SetCursor (LoadCursor (NULL, IDC_ARROW));
685 
686     if (*ppbImage != NULL)
687     {
688         sprintf (szTmp, "VisualPng - %s", strrchr(pstrPathName, '\\') + 1);
689         SetWindowText (hwnd, szTmp);
690     }
691     else
692     {
693         MessageBox (hwnd, TEXT ("Error in loading the PNG image"),
694             szProgName, MB_ICONEXCLAMATION | MB_OK);
695         return FALSE;
696     }
697 
698     return TRUE;
699 }
700 
701 /*----------------
702  *  DisplayImage
703  *----------------
704  */
DisplayImage(HWND hwnd,BYTE ** ppDib,BYTE ** ppDiData,int cxWinSize,int cyWinSize,BYTE * pbImage,int cxImgSize,int cyImgSize,int cImgChannels,BOOL bStretched)705 BOOL DisplayImage (HWND hwnd, BYTE **ppDib,
706         BYTE **ppDiData, int cxWinSize, int cyWinSize,
707         BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
708         BOOL bStretched)
709 {
710     BYTE                       *pDib = *ppDib;
711     BYTE                       *pDiData = *ppDiData;
712     /* BITMAPFILEHEADER        *pbmfh; */
713     BITMAPINFOHEADER           *pbmih;
714     WORD                        wDIRowBytes;
715     png_color                   bkgBlack = {0, 0, 0};
716     png_color                   bkgGray  = {127, 127, 127};
717     png_color                   bkgWhite = {255, 255, 255};
718 
719     /* allocate memory for the Device Independent bitmap */
720 
721     wDIRowBytes = (WORD) ((3 * cxWinSize + 3L) >> 2) << 2;
722 
723     if (pDib)
724     {
725         free (pDib);
726         pDib = NULL;
727     }
728 
729     if (cyWinSize > ((size_t)(-1))/wDIRowBytes) {
730     {
731         MessageBox (hwnd, TEXT ("Visual PNG: image is too big");
732     }
733     if (!(pDib = (BYTE *) malloc (sizeof(BITMAPINFOHEADER) +
734         wDIRowBytes * cyWinSize)))
735     {
736         MessageBox (hwnd, TEXT ("Error in displaying the PNG image"),
737             szProgName, MB_ICONEXCLAMATION | MB_OK);
738         *ppDib = pDib = NULL;
739         return FALSE;
740     }
741     *ppDib = pDib;
742     memset (pDib, 0, sizeof(BITMAPINFOHEADER));
743 
744     /* initialize the dib-structure */
745 
746     pbmih = (BITMAPINFOHEADER *) pDib;
747     pbmih->biSize = sizeof(BITMAPINFOHEADER);
748     pbmih->biWidth = cxWinSize;
749     pbmih->biHeight = -((long) cyWinSize);
750     pbmih->biPlanes = 1;
751     pbmih->biBitCount = 24;
752     pbmih->biCompression = 0;
753     pDiData = pDib + sizeof(BITMAPINFOHEADER);
754     *ppDiData = pDiData;
755 
756     /* first fill bitmap with gray and image border */
757 
758     InitBitmap (pDiData, cxWinSize, cyWinSize);
759 
760     /* then fill bitmap with image */
761 
762     if (pbImage)
763     {
764         FillBitmap (
765             pDiData, cxWinSize, cyWinSize,
766             pbImage, cxImgSize, cyImgSize, cImgChannels,
767             bStretched);
768     }
769 
770     return TRUE;
771 }
772 
773 /*--------------
774  *  InitBitmap
775  *--------------
776  */
777 BOOL InitBitmap (BYTE *pDiData, int cxWinSize, int cyWinSize)
778 {
779     BYTE *dst;
780     int x, y, col;
781 
782     /* initialize the background with gray */
783 
784     dst = pDiData;
785     for (y = 0; y < cyWinSize; y++)
786     {
787         col = 0;
788         for (x = 0; x < cxWinSize; x++)
789         {
790             /* fill with GRAY */
791             *dst++ = 127;
792             *dst++ = 127;
793             *dst++ = 127;
794             col += 3;
795         }
796         /* rows start on 4 byte boundaries */
797         while ((col % 4) != 0)
798         {
799             dst++;
800             col++;
801         }
802     }
803 
804     return TRUE;
805 }
806 
807 /*--------------
808  *  FillBitmap
809  *--------------
810  */
811 BOOL FillBitmap (
812         BYTE *pDiData, int cxWinSize, int cyWinSize,
813         BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
814         BOOL bStretched)
815 {
816     BYTE *pStretchedImage;
817     BYTE *pImg;
818     BYTE *src, *dst;
819     BYTE r, g, b, a;
820     const int cDIChannels = 3;
821     WORD wImgRowBytes;
822     WORD wDIRowBytes;
823     int cxNewSize, cyNewSize;
824     int cxImgPos, cyImgPos;
825     int xImg, yImg;
826     int xWin, yWin;
827     int xOld, yOld;
828     int xNew, yNew;
829 
830     if (bStretched)
831     {
832         cxNewSize = cxWinSize - 2 * MARGIN;
833         cyNewSize = cyWinSize - 2 * MARGIN;
834 
835         /* stretch the image to it's window determined size */
836 
837         /* the following two are mathematically the same, but the first
838          * has side-effects because of rounding
839          */
840 /*      if ((cyNewSize / cxNewSize) > (cyImgSize / cxImgSize)) */
841         if ((cyNewSize * cxImgSize) > (cyImgSize * cxNewSize))
842         {
843             cyNewSize = cxNewSize * cyImgSize / cxImgSize;
844             cxImgPos = MARGIN;
845             cyImgPos = (cyWinSize - cyNewSize) / 2;
846         }
847         else
848         {
849             cxNewSize = cyNewSize * cxImgSize / cyImgSize;
850             cyImgPos = MARGIN;
851             cxImgPos = (cxWinSize - cxNewSize) / 2;
852         }
853 
854         if (cyNewSize > ((size_t)(-1))/(cImgChannels * cxNewSize)) {
855         {
856             MessageBox (hwnd, TEXT ("Visual PNG: stretched image is too big");
857         }
858         pStretchedImage = malloc (cImgChannels * cxNewSize * cyNewSize);
859         pImg = pStretchedImage;
860 
861         for (yNew = 0; yNew < cyNewSize; yNew++)
862         {
863             yOld = yNew * cyImgSize / cyNewSize;
864             for (xNew = 0; xNew < cxNewSize; xNew++)
865             {
866                 xOld = xNew * cxImgSize / cxNewSize;
867 
868                 r = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 0);
869                 g = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 1);
870                 b = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 2);
871                 *pImg++ = r;
872                 *pImg++ = g;
873                 *pImg++ = b;
874                 if (cImgChannels == 4)
875                 {
876                     a = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld)
877                         + 3);
878                     *pImg++ = a;
879                 }
880             }
881         }
882 
883         /* calculate row-bytes */
884 
885         wImgRowBytes = cImgChannels * cxNewSize;
886         wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
887 
888         /* copy image to screen */
889 
890         for (yImg = 0, yWin = cyImgPos; yImg < cyNewSize; yImg++, yWin++)
891         {
892             if (yWin >= cyWinSize - cyImgPos)
893                 break;
894             src = pStretchedImage + yImg * wImgRowBytes;
895             dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels;
896 
897             for (xImg = 0, xWin = cxImgPos; xImg < cxNewSize; xImg++, xWin++)
898             {
899                 if (xWin >= cxWinSize - cxImgPos)
900                     break;
901                 r = *src++;
902                 g = *src++;
903                 b = *src++;
904                 *dst++ = b; /* note the reverse order  */
905                 *dst++ = g;
906                 *dst++ = r;
907                 if (cImgChannels == 4)
908                 {
909                     a = *src++;
910                 }
911             }
912         }
913 
914         /* free memory */
915 
916         if (pStretchedImage != NULL)
917         {
918             free (pStretchedImage);
919             pStretchedImage = NULL;
920         }
921 
922     }
923 
924     /* process the image not-stretched */
925 
926     else
927     {
928         /* calculate the central position */
929 
930         cxImgPos = (cxWinSize - cxImgSize) / 2;
931         cyImgPos = (cyWinSize - cyImgSize) / 2;
932 
933         /* check for image larger than window */
934 
935         if (cxImgPos < MARGIN)
936             cxImgPos = MARGIN;
937         if (cyImgPos < MARGIN)
938             cyImgPos = MARGIN;
939 
940         /* calculate both row-bytes */
941 
942         wImgRowBytes = cImgChannels * cxImgSize;
943         wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
944 
945         /* copy image to screen */
946 
947         for (yImg = 0, yWin = cyImgPos; yImg < cyImgSize; yImg++, yWin++)
948         {
949             if (yWin >= cyWinSize - MARGIN)
950                 break;
951             src = pbImage + yImg * wImgRowBytes;
952             dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels;
953 
954             for (xImg = 0, xWin = cxImgPos; xImg < cxImgSize; xImg++, xWin++)
955             {
956                 if (xWin >= cxWinSize - MARGIN)
957                     break;
958                 r = *src++;
959                 g = *src++;
960                 b = *src++;
961                 *dst++ = b; /* note the reverse order  */
962                 *dst++ = g;
963                 *dst++ = r;
964                 if (cImgChannels == 4)
965                 {
966                     a = *src++;
967                 }
968             }
969         }
970     }
971 
972     return TRUE;
973 }
974 
975 /*-----------------
976  *  end of source
977  *-----------------
978  */
979