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