Imported Upstream version 1.6.40
[platform/upstream/libpng.git] / contrib / visupng / VisualPng.c
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
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 command line, 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
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
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  */
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  */
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
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
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  */
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  */