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
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
42LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
43BOOL    CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;
44
45BOOL CenterAbout (HWND hwndChild, HWND hwndParent);
46
47BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount,
48        int *pFileIndex);
49
50BOOL SearchPngList (TCHAR *pFileList, int FileCount, int *pFileIndex,
51        PTSTR pstrPrevName, PTSTR pstrNextName);
52
53BOOL LoadImageFile(HWND hwnd, PTSTR pstrPathName,
54        png_byte **ppbImage, int *pxImgSize, int *pyImgSize, int *piChannels,
55        png_color *pBkgColor);
56
57BOOL 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
62BOOL InitBitmap (
63        BYTE *pDiData, int cxWinSize, int cyWinSize);
64
65BOOL 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
72static char *szProgName = PROGNAME;
73static char *szAppName = LONGNAME;
74static char *szIconName = PROGNAME;
75static char szCmdFileName [MAX_PATH];
76
77/* MAIN routine */
78
79int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
80                    PSTR szCmdLine, int iCmdShow)
81{
82    HACCEL   hAccel;
83    HWND     hwnd;
84    MSG      msg;
85    WNDCLASS wndclass;
86    int ixBorders, iyBorders;
87
88    wndclass.style         = CS_HREDRAW | CS_VREDRAW;
89    wndclass.lpfnWndProc   = WndProc;
90    wndclass.cbClsExtra    = 0;
91    wndclass.cbWndExtra    = 0;
92    wndclass.hInstance     = hInstance;
93    wndclass.hIcon         = LoadIcon (hInstance, szIconName) ;
94    wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
95    wndclass.hbrBackground = NULL; /* (HBRUSH) GetStockObject (GRAY_BRUSH); */
96    wndclass.lpszMenuName  = szProgName;
97    wndclass.lpszClassName = szProgName;
98
99    if (!RegisterClass (&wndclass))
100    {
101        MessageBox (NULL, TEXT ("Error: this program requires Windows NT!"),
102            szProgName, MB_ICONERROR);
103        return 0;
104    }
105
106    /* if filename given on commandline, store it */
107    if ((szCmdLine != NULL) && (*szCmdLine != '\0'))
108        if (szCmdLine[0] == '"')
109            strncpy (szCmdFileName, szCmdLine + 1, strlen(szCmdLine) - 2);
110        else
111            strcpy (szCmdFileName, szCmdLine);
112    else
113        strcpy (szCmdFileName, "");
114
115    /* calculate size of window-borders */
116    ixBorders = 2 * (GetSystemMetrics (SM_CXBORDER) +
117                     GetSystemMetrics (SM_CXDLGFRAME));
118    iyBorders = 2 * (GetSystemMetrics (SM_CYBORDER) +
119                     GetSystemMetrics (SM_CYDLGFRAME)) +
120                     GetSystemMetrics (SM_CYCAPTION) +
121                     GetSystemMetrics (SM_CYMENUSIZE) +
122                     1; /* WvS: don't ask me why?  */
123
124    hwnd = CreateWindow (szProgName, szAppName,
125        WS_OVERLAPPEDWINDOW,
126        CW_USEDEFAULT, CW_USEDEFAULT,
127        512 + 2 * MARGIN + ixBorders, 384 + 2 * MARGIN + iyBorders,
128/*      CW_USEDEFAULT, CW_USEDEFAULT, */
129        NULL, NULL, hInstance, NULL);
130
131    ShowWindow (hwnd, iCmdShow);
132    UpdateWindow (hwnd);
133
134    hAccel = LoadAccelerators (hInstance, szProgName);
135
136    while (GetMessage (&msg, NULL, 0, 0))
137    {
138        if (!TranslateAccelerator (hwnd, hAccel, &msg))
139        {
140            TranslateMessage (&msg);
141            DispatchMessage (&msg);
142        }
143    }
144    return msg.wParam;
145}
146
147LRESULT 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
431BOOL 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 */
459BOOL 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 */
513BOOL 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
618BOOL 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
661BOOL 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 */
705BOOL 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 Independant 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 (!(pDib = (BYTE *) malloc (sizeof(BITMAPINFOHEADER) +
730        wDIRowBytes * cyWinSize)))
731    {
732        MessageBox (hwnd, TEXT ("Error in displaying the PNG image"),
733            szProgName, MB_ICONEXCLAMATION | MB_OK);
734        *ppDib = pDib = NULL;
735        return FALSE;
736    }
737    *ppDib = pDib;
738    memset (pDib, 0, sizeof(BITMAPINFOHEADER));
739
740    /* initialize the dib-structure */
741
742    pbmih = (BITMAPINFOHEADER *) pDib;
743    pbmih->biSize = sizeof(BITMAPINFOHEADER);
744    pbmih->biWidth = cxWinSize;
745    pbmih->biHeight = -((long) cyWinSize);
746    pbmih->biPlanes = 1;
747    pbmih->biBitCount = 24;
748    pbmih->biCompression = 0;
749    pDiData = pDib + sizeof(BITMAPINFOHEADER);
750    *ppDiData = pDiData;
751
752    /* first fill bitmap with gray and image border */
753
754    InitBitmap (pDiData, cxWinSize, cyWinSize);
755
756    /* then fill bitmap with image */
757
758    if (pbImage)
759    {
760        FillBitmap (
761            pDiData, cxWinSize, cyWinSize,
762            pbImage, cxImgSize, cyImgSize, cImgChannels,
763            bStretched);
764    }
765
766    return TRUE;
767}
768
769/*--------------
770 *  InitBitmap
771 *--------------
772 */
773BOOL InitBitmap (BYTE *pDiData, int cxWinSize, int cyWinSize)
774{
775    BYTE *dst;
776    int x, y, col;
777
778    /* initialize the background with gray */
779
780    dst = pDiData;
781    for (y = 0; y < cyWinSize; y++)
782    {
783        col = 0;
784        for (x = 0; x < cxWinSize; x++)
785        {
786            /* fill with GRAY */
787            *dst++ = 127;
788            *dst++ = 127;
789            *dst++ = 127;
790            col += 3;
791        }
792        /* rows start on 4 byte boundaries */
793        while ((col % 4) != 0)
794        {
795            dst++;
796            col++;
797        }
798    }
799
800    return TRUE;
801}
802
803/*--------------
804 *  FillBitmap
805 *--------------
806 */
807BOOL FillBitmap (
808        BYTE *pDiData, int cxWinSize, int cyWinSize,
809        BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
810        BOOL bStretched)
811{
812    BYTE *pStretchedImage;
813    BYTE *pImg;
814    BYTE *src, *dst;
815    BYTE r, g, b, a;
816    const int cDIChannels = 3;
817    WORD wImgRowBytes;
818    WORD wDIRowBytes;
819    int cxNewSize, cyNewSize;
820    int cxImgPos, cyImgPos;
821    int xImg, yImg;
822    int xWin, yWin;
823    int xOld, yOld;
824    int xNew, yNew;
825
826    if (bStretched)
827    {
828        cxNewSize = cxWinSize - 2 * MARGIN;
829        cyNewSize = cyWinSize - 2 * MARGIN;
830
831        /* stretch the image to it's window determined size */
832
833        /* the following two are mathematically the same, but the first
834         * has side-effects because of rounding
835         */
836/*      if ((cyNewSize / cxNewSize) > (cyImgSize / cxImgSize)) */
837        if ((cyNewSize * cxImgSize) > (cyImgSize * cxNewSize))
838        {
839            cyNewSize = cxNewSize * cyImgSize / cxImgSize;
840            cxImgPos = MARGIN;
841            cyImgPos = (cyWinSize - cyNewSize) / 2;
842        }
843        else
844        {
845            cxNewSize = cyNewSize * cxImgSize / cyImgSize;
846            cyImgPos = MARGIN;
847            cxImgPos = (cxWinSize - cxNewSize) / 2;
848        }
849
850        pStretchedImage = malloc (cImgChannels * cxNewSize * cyNewSize);
851        pImg = pStretchedImage;
852
853        for (yNew = 0; yNew < cyNewSize; yNew++)
854        {
855            yOld = yNew * cyImgSize / cyNewSize;
856            for (xNew = 0; xNew < cxNewSize; xNew++)
857            {
858                xOld = xNew * cxImgSize / cxNewSize;
859
860                r = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 0);
861                g = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 1);
862                b = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 2);
863                *pImg++ = r;
864                *pImg++ = g;
865                *pImg++ = b;
866                if (cImgChannels == 4)
867                {
868                    a = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld)
869                        + 3);
870                    *pImg++ = a;
871                }
872            }
873        }
874
875        /* calculate row-bytes */
876
877        wImgRowBytes = cImgChannels * cxNewSize;
878        wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
879
880        /* copy image to screen */
881
882        for (yImg = 0, yWin = cyImgPos; yImg < cyNewSize; yImg++, yWin++)
883        {
884            if (yWin >= cyWinSize - cyImgPos)
885                break;
886            src = pStretchedImage + yImg * wImgRowBytes;
887            dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels;
888
889            for (xImg = 0, xWin = cxImgPos; xImg < cxNewSize; xImg++, xWin++)
890            {
891                if (xWin >= cxWinSize - cxImgPos)
892                    break;
893                r = *src++;
894                g = *src++;
895                b = *src++;
896                *dst++ = b; /* note the reverse order  */
897                *dst++ = g;
898                *dst++ = r;
899                if (cImgChannels == 4)
900                {
901                    a = *src++;
902                }
903            }
904        }
905
906        /* free memory */
907
908        if (pStretchedImage != NULL)
909        {
910            free (pStretchedImage);
911            pStretchedImage = NULL;
912        }
913
914    }
915
916    /* process the image not-stretched */
917
918    else
919    {
920        /* calculate the central position */
921
922        cxImgPos = (cxWinSize - cxImgSize) / 2;
923        cyImgPos = (cyWinSize - cyImgSize) / 2;
924
925        /* check for image larger than window */
926
927        if (cxImgPos < MARGIN)
928            cxImgPos = MARGIN;
929        if (cyImgPos < MARGIN)
930            cyImgPos = MARGIN;
931
932        /* calculate both row-bytes */
933
934        wImgRowBytes = cImgChannels * cxImgSize;
935        wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
936
937        /* copy image to screen */
938
939        for (yImg = 0, yWin = cyImgPos; yImg < cyImgSize; yImg++, yWin++)
940        {
941            if (yWin >= cyWinSize - MARGIN)
942                break;
943            src = pbImage + yImg * wImgRowBytes;
944            dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels;
945
946            for (xImg = 0, xWin = cxImgPos; xImg < cxImgSize; xImg++, xWin++)
947            {
948                if (xWin >= cxWinSize - MARGIN)
949                    break;
950                r = *src++;
951                g = *src++;
952                b = *src++;
953                *dst++ = b; /* note the reverse order  */
954                *dst++ = g;
955                *dst++ = r;
956                if (cImgChannels == 4)
957                {
958                    a = *src++;
959                }
960            }
961        }
962    }
963
964    return TRUE;
965}
966
967/*-----------------
968 *  end of source
969 *-----------------
970 */
971