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