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
40LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
41BOOL    CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;
42
43BOOL CenterAbout (HWND hwndChild, HWND hwndParent);
44
45BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount,
46        int *pFileIndex);
47
48BOOL SearchPngList (TCHAR *pFileList, int FileCount, int *pFileIndex,
49        PTSTR pstrPrevName, PTSTR pstrNextName);
50
51BOOL LoadImageFile(HWND hwnd, PTSTR pstrPathName,
52        png_byte **ppbImage, int *pxImgSize, int *pyImgSize, int *piChannels,
53        png_color *pBkgColor);
54
55BOOL 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
60BOOL InitBitmap (
61        BYTE *pDiData, int cxWinSize, int cyWinSize);
62
63BOOL 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
70static char *szProgName = PROGNAME;
71static char *szAppName = LONGNAME;
72static char *szIconName = PROGNAME;
73static char szCmdFileName [MAX_PATH];
74
75// MAIN routine
76
77int 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
145LRESULT 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
429BOOL 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
457BOOL 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
511BOOL 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
615BOOL 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
657BOOL 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
701BOOL 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
769BOOL 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
803BOOL 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//-----------------
965