diff ext/libpng-1.2.29/contrib/visupng/VisualPng.c @ 0:4a0efb7baf70

* Datasets becomes the new trunk and retires after that :-)
author mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222
date Sun, 29 Jun 2008 18:44:17 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ext/libpng-1.2.29/contrib/visupng/VisualPng.c	Sun Jun 29 18:44:17 2008 +0000
@@ -0,0 +1,961 @@
+//------------------------------------
+//  VisualPng.C -- Shows a PNG image
+//------------------------------------
+
+// Copyright 2000, Willem van Schaik.  For conditions of distribution and
+// use, see the copyright/license/disclaimer notice in png.h
+
+// switches
+
+// defines
+
+#define PROGNAME  "VisualPng"
+#define LONGNAME  "Win32 Viewer for PNG-files"
+#define VERSION   "1.0 of 2000 June 07"
+
+// constants
+
+#define MARGIN 8
+
+// standard includes
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+
+// application includes
+
+#include "png.h"
+#include "pngfile.h"
+#include "resource.h"
+
+// macros
+
+// function prototypes
+
+LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
+BOOL    CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;
+
+BOOL CenterAbout (HWND hwndChild, HWND hwndParent);
+
+BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount,
+        int *pFileIndex);
+
+BOOL SearchPngList (TCHAR *pFileList, int FileCount, int *pFileIndex,
+        PTSTR pstrPrevName, PTSTR pstrNextName);
+
+BOOL LoadImageFile(HWND hwnd, PTSTR pstrPathName,
+        png_byte **ppbImage, int *pxImgSize, int *pyImgSize, int *piChannels,
+        png_color *pBkgColor);
+
+BOOL DisplayImage (HWND hwnd, BYTE **ppDib,
+        BYTE **ppDiData, int cxWinSize, int cyWinSize,
+        BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
+        BOOL bStretched);
+
+BOOL InitBitmap (
+        BYTE *pDiData, int cxWinSize, int cyWinSize);
+
+BOOL FillBitmap (
+        BYTE *pDiData, int cxWinSize, int cyWinSize,
+        BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
+        BOOL bStretched);
+
+// a few global variables
+
+static char *szProgName = PROGNAME;
+static char *szAppName = LONGNAME;
+static char *szIconName = PROGNAME;
+static char szCmdFileName [MAX_PATH];
+
+// MAIN routine
+
+int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
+                    PSTR szCmdLine, int iCmdShow)
+{
+    HACCEL   hAccel;
+    HWND     hwnd;
+    MSG      msg;
+    WNDCLASS wndclass;
+    int ixBorders, iyBorders;
+
+    wndclass.style         = CS_HREDRAW | CS_VREDRAW;
+    wndclass.lpfnWndProc   = WndProc;
+    wndclass.cbClsExtra    = 0;
+    wndclass.cbWndExtra    = 0;
+    wndclass.hInstance     = hInstance;
+    wndclass.hIcon         = LoadIcon (hInstance, szIconName) ;
+    wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
+    wndclass.hbrBackground = NULL; // (HBRUSH) GetStockObject (GRAY_BRUSH);
+    wndclass.lpszMenuName  = szProgName;
+    wndclass.lpszClassName = szProgName;
+
+    if (!RegisterClass (&wndclass))
+    {
+        MessageBox (NULL, TEXT ("Error: this program requires Windows NT!"),
+            szProgName, MB_ICONERROR);
+        return 0;
+    }
+
+    // if filename given on commandline, store it
+    if ((szCmdLine != NULL) && (*szCmdLine != '\0'))
+        if (szCmdLine[0] == '"')
+            strncpy (szCmdFileName, szCmdLine + 1, strlen(szCmdLine) - 2);
+        else
+            strcpy (szCmdFileName, szCmdLine);
+    else
+        strcpy (szCmdFileName, "");
+
+    // calculate size of window-borders
+    ixBorders = 2 * (GetSystemMetrics (SM_CXBORDER) +
+                     GetSystemMetrics (SM_CXDLGFRAME));
+    iyBorders = 2 * (GetSystemMetrics (SM_CYBORDER) +
+                     GetSystemMetrics (SM_CYDLGFRAME)) +
+                     GetSystemMetrics (SM_CYCAPTION) +
+                     GetSystemMetrics (SM_CYMENUSIZE) +
+                     1; /* WvS: don't ask me why? */
+
+    hwnd = CreateWindow (szProgName, szAppName,
+        WS_OVERLAPPEDWINDOW,
+        CW_USEDEFAULT, CW_USEDEFAULT,
+        512 + 2 * MARGIN + ixBorders, 384 + 2 * MARGIN + iyBorders,
+//      CW_USEDEFAULT, CW_USEDEFAULT,
+        NULL, NULL, hInstance, NULL);
+
+    ShowWindow (hwnd, iCmdShow);
+    UpdateWindow (hwnd);
+
+    hAccel = LoadAccelerators (hInstance, szProgName);
+
+    while (GetMessage (&msg, NULL, 0, 0))
+    {
+        if (!TranslateAccelerator (hwnd, hAccel, &msg))
+        {
+            TranslateMessage (&msg);
+            DispatchMessage (&msg);
+        }
+    }
+    return msg.wParam;
+}
+
+LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,
+        LPARAM lParam)
+{
+    static HINSTANCE          hInstance ;
+    static HDC                hdc;
+    static PAINTSTRUCT        ps;
+    static HMENU              hMenu;
+
+    static BITMAPFILEHEADER  *pbmfh;
+    static BITMAPINFOHEADER  *pbmih;
+    static BYTE              *pbImage;
+    static int                cxWinSize, cyWinSize;
+    static int                cxImgSize, cyImgSize;
+    static int                cImgChannels;
+    static png_color          bkgColor = {127, 127, 127};
+
+    static BOOL               bStretched = TRUE;
+
+    static BYTE              *pDib = NULL;
+    static BYTE              *pDiData = NULL;
+
+    static TCHAR              szImgPathName [MAX_PATH];
+    static TCHAR              szTitleName [MAX_PATH];
+
+    static TCHAR             *pPngFileList = NULL;
+    static int                iPngFileCount;
+    static int                iPngFileIndex;
+
+    BOOL                      bOk;
+
+    switch (message)
+    {
+    case WM_CREATE:
+        hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;
+        PngFileInitialize (hwnd);
+
+        strcpy (szImgPathName, "");
+
+        // in case we process file given on command-line
+
+        if (szCmdFileName[0] != '\0')
+        {
+            strcpy (szImgPathName, szCmdFileName);
+
+            // read the other png-files in the directory for later
+            // next/previous commands
+
+            BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount,
+                          &iPngFileIndex);
+
+            // load the image from file
+
+            if (!LoadImageFile (hwnd, szImgPathName,
+                &pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
+                return 0;
+
+            // invalidate the client area for later update
+
+            InvalidateRect (hwnd, NULL, TRUE);
+
+            // display the PNG into the DIBitmap
+
+            DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
+                pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
+        }
+
+        return 0;
+
+    case WM_SIZE:
+        cxWinSize = LOWORD (lParam);
+        cyWinSize = HIWORD (lParam);
+
+        // invalidate the client area for later update
+
+        InvalidateRect (hwnd, NULL, TRUE);
+
+        // display the PNG into the DIBitmap
+
+        DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
+            pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
+
+        return 0;
+
+    case WM_INITMENUPOPUP:
+        hMenu = GetMenu (hwnd);
+
+        if (pbImage)
+            EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_ENABLED);
+        else
+            EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_GRAYED);
+
+        return 0;
+
+    case WM_COMMAND:
+        hMenu = GetMenu (hwnd);
+
+        switch (LOWORD (wParam))
+        {
+        case IDM_FILE_OPEN:
+
+            // show the File Open dialog box
+
+            if (!PngFileOpenDlg (hwnd, szImgPathName, szTitleName))
+                return 0;
+
+            // read the other png-files in the directory for later
+            // next/previous commands
+
+            BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount,
+                          &iPngFileIndex);
+
+            // load the image from file
+
+            if (!LoadImageFile (hwnd, szImgPathName,
+                &pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
+                return 0;
+
+            // invalidate the client area for later update
+
+            InvalidateRect (hwnd, NULL, TRUE);
+
+            // display the PNG into the DIBitmap
+
+            DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
+                pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
+
+            return 0;
+
+        case IDM_FILE_SAVE:
+
+            // show the File Save dialog box
+
+            if (!PngFileSaveDlg (hwnd, szImgPathName, szTitleName))
+                return 0;
+
+            // save the PNG to a disk file
+
+            SetCursor (LoadCursor (NULL, IDC_WAIT));
+            ShowCursor (TRUE);
+
+            bOk = PngSaveImage (szImgPathName, pDiData, cxWinSize, cyWinSize,
+                  bkgColor);
+
+            ShowCursor (FALSE);
+            SetCursor (LoadCursor (NULL, IDC_ARROW));
+
+            if (!bOk)
+                MessageBox (hwnd, TEXT ("Error in saving the PNG image"),
+                szProgName, MB_ICONEXCLAMATION | MB_OK);
+            return 0;
+
+        case IDM_FILE_NEXT:
+
+            // read next entry in the directory
+
+            if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex,
+                NULL, szImgPathName))
+            {
+                if (strcmp (szImgPathName, "") == 0)
+                    return 0;
+                
+                // load the image from file
+                
+                if (!LoadImageFile (hwnd, szImgPathName, &pbImage,
+                        &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor))
+                    return 0;
+                
+                // invalidate the client area for later update
+                
+                InvalidateRect (hwnd, NULL, TRUE);
+                
+                // display the PNG into the DIBitmap
+                
+                DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
+                    pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
+            }
+            
+            return 0;
+
+        case IDM_FILE_PREVIOUS:
+
+            // read previous entry in the directory
+
+            if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex,
+                szImgPathName, NULL))
+            {
+                
+                if (strcmp (szImgPathName, "") == 0)
+                    return 0;
+                
+                // load the image from file
+                
+                if (!LoadImageFile (hwnd, szImgPathName, &pbImage, &cxImgSize,
+                    &cyImgSize, &cImgChannels, &bkgColor))
+                    return 0;
+                
+                // invalidate the client area for later update
+                
+                InvalidateRect (hwnd, NULL, TRUE);
+                
+                // display the PNG into the DIBitmap
+                
+                DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
+                    pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
+            }
+
+            return 0;
+
+        case IDM_FILE_EXIT:
+
+            // more cleanup needed...
+
+            // free image buffer
+
+            if (pDib != NULL)
+            {
+                free (pDib);
+                pDib = NULL;
+            }
+
+            // free file-list
+
+            if (pPngFileList != NULL)
+            {
+                free (pPngFileList);
+                pPngFileList = NULL;
+            }
+
+            // let's go ...
+
+            exit (0);
+
+            return 0;
+
+        case IDM_OPTIONS_STRETCH:
+            bStretched = !bStretched;
+            if (bStretched)
+                CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_CHECKED);
+            else
+                CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_UNCHECKED);
+
+            // invalidate the client area for later update
+
+            InvalidateRect (hwnd, NULL, TRUE);
+
+            // display the PNG into the DIBitmap
+
+            DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize,
+                pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched);
+
+            return 0;
+
+        case IDM_HELP_ABOUT:
+            DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;
+            return 0;
+
+        } // end switch
+
+        break;
+
+    case WM_PAINT:
+        hdc = BeginPaint (hwnd, &ps);
+
+        if (pDib)
+            SetDIBitsToDevice (hdc, 0, 0, cxWinSize, cyWinSize, 0, 0,
+                0, cyWinSize, pDiData, (BITMAPINFO *) pDib, DIB_RGB_COLORS);
+
+        EndPaint (hwnd, &ps);
+        return 0;
+
+    case WM_DESTROY:
+        if (pbmfh)
+        {
+            free (pbmfh);
+            pbmfh = NULL;
+        }
+
+        PostQuitMessage (0);
+        return 0;
+    }
+
+    return DefWindowProc (hwnd, message, wParam, lParam);
+}
+
+BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message,
+                            WPARAM wParam, LPARAM lParam)
+{
+     switch (message)
+     {
+     case WM_INITDIALOG :
+          ShowWindow (hDlg, SW_HIDE);
+          CenterAbout (hDlg, GetWindow (hDlg, GW_OWNER));
+          ShowWindow (hDlg, SW_SHOW);
+          return TRUE ;
+
+     case WM_COMMAND :
+          switch (LOWORD (wParam))
+          {
+          case IDOK :
+          case IDCANCEL :
+               EndDialog (hDlg, 0) ;
+               return TRUE ;
+          }
+          break ;
+     }
+     return FALSE ;
+}
+
+//---------------
+//  CenterAbout
+//---------------
+
+BOOL CenterAbout (HWND hwndChild, HWND hwndParent)
+{
+   RECT    rChild, rParent, rWorkArea;
+   int     wChild, hChild, wParent, hParent;
+   int     xNew, yNew;
+   BOOL  bResult;
+
+   // Get the Height and Width of the child window
+   GetWindowRect (hwndChild, &rChild);
+   wChild = rChild.right - rChild.left;
+   hChild = rChild.bottom - rChild.top;
+
+   // Get the Height and Width of the parent window
+   GetWindowRect (hwndParent, &rParent);
+   wParent = rParent.right - rParent.left;
+   hParent = rParent.bottom - rParent.top;
+
+   // Get the limits of the 'workarea'
+   bResult = SystemParametersInfo(
+      SPI_GETWORKAREA,  // system parameter to query or set
+      sizeof(RECT),
+      &rWorkArea,
+      0);
+   if (!bResult) {
+      rWorkArea.left = rWorkArea.top = 0;
+      rWorkArea.right = GetSystemMetrics(SM_CXSCREEN);
+      rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN);
+   }
+
+   // Calculate new X position, then adjust for workarea
+   xNew = rParent.left + ((wParent - wChild) /2);
+   if (xNew < rWorkArea.left) {
+      xNew = rWorkArea.left;
+   } else if ((xNew+wChild) > rWorkArea.right) {
+      xNew = rWorkArea.right - wChild;
+   }
+
+   // Calculate new Y position, then adjust for workarea
+   yNew = rParent.top  + ((hParent - hChild) /2);
+   if (yNew < rWorkArea.top) {
+      yNew = rWorkArea.top;
+   } else if ((yNew+hChild) > rWorkArea.bottom) {
+      yNew = rWorkArea.bottom - hChild;
+   }
+
+   // Set it, and return
+   return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE |
+          SWP_NOZORDER);
+}
+
+//----------------
+//  BuildPngList
+//----------------
+
+BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount,
+     int *pFileIndex)
+{
+    static TCHAR              szImgPathName [MAX_PATH];
+    static TCHAR              szImgFileName [MAX_PATH];
+    static TCHAR              szImgFindName [MAX_PATH];
+
+    WIN32_FIND_DATA           finddata;
+    HANDLE                    hFind;
+
+    static TCHAR              szTmp [MAX_PATH];
+    BOOL                      bOk;
+    int                       i, ii;
+    int                       j, jj;
+
+    // free previous file-list
+
+    if (*ppFileList != NULL)
+    {
+        free (*ppFileList);
+        *ppFileList = NULL;
+    }
+
+    // extract foldername, filename and search-name
+
+    strcpy (szImgPathName, pstrPathName);
+    strcpy (szImgFileName, strrchr (pstrPathName, '\\') + 1);
+
+    strcpy (szImgFindName, szImgPathName);
+    *(strrchr (szImgFindName, '\\') + 1) = '\0';
+    strcat (szImgFindName, "*.png");
+
+    // first cycle: count number of files in directory for memory allocation
+
+    *pFileCount = 0;
+
+    hFind = FindFirstFile(szImgFindName, &finddata);
+    bOk = (hFind != (HANDLE) -1);
+
+    while (bOk)
+    {
+        *pFileCount += 1;
+        bOk = FindNextFile(hFind, &finddata);
+    }
+    FindClose(hFind);
+
+    // allocation memory for file-list
+
+    *ppFileList = (TCHAR *) malloc (*pFileCount * MAX_PATH);
+
+    // second cycle: read directory and store filenames in file-list
+
+    hFind = FindFirstFile(szImgFindName, &finddata);
+    bOk = (hFind != (HANDLE) -1);
+
+    i = 0;
+    ii = 0;
+    while (bOk)
+    {
+        strcpy (*ppFileList + ii, szImgPathName);
+        strcpy (strrchr(*ppFileList + ii, '\\') + 1, finddata.cFileName);
+
+        if (strcmp(pstrPathName, *ppFileList + ii) == 0)
+            *pFileIndex = i;
+
+        ii += MAX_PATH;
+        i++;
+
+        bOk = FindNextFile(hFind, &finddata);
+    }
+    FindClose(hFind);
+
+    // finally we must sort the file-list
+
+    for (i = 0; i < *pFileCount - 1; i++)
+    {
+        ii = i * MAX_PATH;
+        for (j = i+1; j < *pFileCount; j++)
+        {
+            jj = j * MAX_PATH;
+            if (strcmp (*ppFileList + ii, *ppFileList + jj) > 0)
+            {
+                strcpy (szTmp, *ppFileList + jj);
+                strcpy (*ppFileList + jj, *ppFileList + ii);
+                strcpy (*ppFileList + ii, szTmp);
+
+                // check if this was the current image that we moved
+
+                if (*pFileIndex == i)
+                    *pFileIndex = j;
+                else
+                    if (*pFileIndex == j)
+                        *pFileIndex = i;
+            }
+        }
+    }
+
+    return TRUE;
+}
+
+//----------------
+//  SearchPngList
+//----------------
+
+BOOL SearchPngList (
+        TCHAR *pFileList, int FileCount, int *pFileIndex,
+        PTSTR pstrPrevName, PTSTR pstrNextName)
+{
+    if (FileCount > 0)
+    {
+        // get previous entry
+        
+        if (pstrPrevName != NULL)
+        {
+            if (*pFileIndex > 0)
+                *pFileIndex -= 1;
+            else
+                *pFileIndex = FileCount - 1;
+            
+            strcpy (pstrPrevName, pFileList + (*pFileIndex * MAX_PATH));
+        }
+        
+        // get next entry
+        
+        if (pstrNextName != NULL)
+        {
+            if (*pFileIndex < FileCount - 1)
+                *pFileIndex += 1;
+            else
+                *pFileIndex = 0;
+            
+            strcpy (pstrNextName, pFileList + (*pFileIndex * MAX_PATH));
+        }
+        
+        return TRUE;
+    }
+    else
+    {
+        return FALSE;
+    }
+}
+
+//-----------------
+//  LoadImageFile
+//-----------------
+
+BOOL LoadImageFile (HWND hwnd, PTSTR pstrPathName,
+                png_byte **ppbImage, int *pxImgSize, int *pyImgSize,
+                int *piChannels, png_color *pBkgColor)
+{
+    static TCHAR szTmp [MAX_PATH];
+
+    // if there's an existing PNG, free the memory
+
+    if (*ppbImage)
+    {
+        free (*ppbImage);
+        *ppbImage = NULL;
+    }
+
+    // Load the entire PNG into memory
+
+    SetCursor (LoadCursor (NULL, IDC_WAIT));
+    ShowCursor (TRUE);
+
+    PngLoadImage (pstrPathName, ppbImage, pxImgSize, pyImgSize, piChannels,
+                  pBkgColor);
+
+    ShowCursor (FALSE);
+    SetCursor (LoadCursor (NULL, IDC_ARROW));
+
+    if (*ppbImage != NULL)
+    {
+        sprintf (szTmp, "VisualPng - %s", strrchr(pstrPathName, '\\') + 1);
+        SetWindowText (hwnd, szTmp);
+    }
+    else
+    {
+        MessageBox (hwnd, TEXT ("Error in loading the PNG image"),
+            szProgName, MB_ICONEXCLAMATION | MB_OK);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+//----------------
+//  DisplayImage
+//----------------
+
+BOOL DisplayImage (HWND hwnd, BYTE **ppDib,
+        BYTE **ppDiData, int cxWinSize, int cyWinSize,
+        BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
+        BOOL bStretched)
+{
+    BYTE                       *pDib = *ppDib;
+    BYTE                       *pDiData = *ppDiData;
+    // BITMAPFILEHEADER        *pbmfh;
+    BITMAPINFOHEADER           *pbmih;
+    WORD                        wDIRowBytes;
+    png_color                   bkgBlack = {0, 0, 0};
+    png_color                   bkgGray  = {127, 127, 127};
+    png_color                   bkgWhite = {255, 255, 255};
+
+    // allocate memory for the Device Independant bitmap
+
+    wDIRowBytes = (WORD) ((3 * cxWinSize + 3L) >> 2) << 2;
+
+    if (pDib)
+    {
+        free (pDib);
+        pDib = NULL;
+    }
+
+    if (!(pDib = (BYTE *) malloc (sizeof(BITMAPINFOHEADER) +
+        wDIRowBytes * cyWinSize)))
+    {
+        MessageBox (hwnd, TEXT ("Error in displaying the PNG image"),
+            szProgName, MB_ICONEXCLAMATION | MB_OK);
+        *ppDib = pDib = NULL;
+        return FALSE;
+    }
+    *ppDib = pDib;
+    memset (pDib, 0, sizeof(BITMAPINFOHEADER));
+
+    // initialize the dib-structure
+
+    pbmih = (BITMAPINFOHEADER *) pDib;
+    pbmih->biSize = sizeof(BITMAPINFOHEADER);
+    pbmih->biWidth = cxWinSize;
+    pbmih->biHeight = -((long) cyWinSize);
+    pbmih->biPlanes = 1;
+    pbmih->biBitCount = 24;
+    pbmih->biCompression = 0;
+    pDiData = pDib + sizeof(BITMAPINFOHEADER);
+    *ppDiData = pDiData;
+
+    // first fill bitmap with gray and image border
+
+    InitBitmap (pDiData, cxWinSize, cyWinSize);
+
+    // then fill bitmap with image
+
+    if (pbImage)
+    {
+        FillBitmap (
+            pDiData, cxWinSize, cyWinSize,
+            pbImage, cxImgSize, cyImgSize, cImgChannels,
+            bStretched);
+    }
+
+    return TRUE;
+}
+
+//--------------
+//  InitBitmap
+//--------------
+
+BOOL InitBitmap (BYTE *pDiData, int cxWinSize, int cyWinSize)
+{
+    BYTE *dst;
+    int x, y, col;
+
+    // initialize the background with gray
+
+    dst = pDiData;
+    for (y = 0; y < cyWinSize; y++)
+    {
+        col = 0;
+        for (x = 0; x < cxWinSize; x++)
+        {
+            // fill with GRAY
+            *dst++ = 127;
+            *dst++ = 127;
+            *dst++ = 127;
+            col += 3;
+        }
+        // rows start on 4 byte boundaries
+        while ((col % 4) != 0)
+        {
+            dst++;
+            col++;
+        }
+    }
+
+    return TRUE;
+}
+
+//--------------
+//  FillBitmap
+//--------------
+
+BOOL FillBitmap (
+        BYTE *pDiData, int cxWinSize, int cyWinSize,
+        BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels,
+        BOOL bStretched)
+{
+    BYTE *pStretchedImage;
+    BYTE *pImg;
+    BYTE *src, *dst;
+    BYTE r, g, b, a;
+    const int cDIChannels = 3;
+    WORD wImgRowBytes;
+    WORD wDIRowBytes;
+    int cxNewSize, cyNewSize;
+    int cxImgPos, cyImgPos;
+    int xImg, yImg;
+    int xWin, yWin;
+    int xOld, yOld;
+    int xNew, yNew;
+
+    if (bStretched)
+    {
+        cxNewSize = cxWinSize - 2 * MARGIN;
+        cyNewSize = cyWinSize - 2 * MARGIN;
+
+        // stretch the image to it's window determined size
+
+        // the following two are the same, but the first has side-effects
+        // because of rounding
+//      if ((cyNewSize / cxNewSize) > (cyImgSize / cxImgSize))
+        if ((cyNewSize * cxImgSize) > (cyImgSize * cxNewSize))
+        {
+            cyNewSize = cxNewSize * cyImgSize / cxImgSize;
+            cxImgPos = MARGIN;
+            cyImgPos = (cyWinSize - cyNewSize) / 2;
+        }
+        else
+        {
+            cxNewSize = cyNewSize * cxImgSize / cyImgSize;
+            cyImgPos = MARGIN;
+            cxImgPos = (cxWinSize - cxNewSize) / 2;
+        }
+
+        pStretchedImage = malloc (cImgChannels * cxNewSize * cyNewSize);
+        pImg = pStretchedImage;
+
+        for (yNew = 0; yNew < cyNewSize; yNew++)
+        {
+            yOld = yNew * cyImgSize / cyNewSize;
+            for (xNew = 0; xNew < cxNewSize; xNew++)
+            {
+                xOld = xNew * cxImgSize / cxNewSize;
+
+                r = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 0);
+                g = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 1);
+                b = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 2);
+                *pImg++ = r;
+                *pImg++ = g;
+                *pImg++ = b;
+                if (cImgChannels == 4)
+                {
+                    a = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld)
+                        + 3);
+                    *pImg++ = a;
+                }
+            }
+        }
+
+        // calculate row-bytes
+
+        wImgRowBytes = cImgChannels * cxNewSize;
+        wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
+
+        // copy image to screen
+
+        for (yImg = 0, yWin = cyImgPos; yImg < cyNewSize; yImg++, yWin++)
+        {
+            if (yWin >= cyWinSize - cyImgPos)
+                break;
+            src = pStretchedImage + yImg * wImgRowBytes;
+            dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels;
+
+            for (xImg = 0, xWin = cxImgPos; xImg < cxNewSize; xImg++, xWin++)
+            {
+                if (xWin >= cxWinSize - cxImgPos)
+                    break;
+                r = *src++;
+                g = *src++;
+                b = *src++;
+                *dst++ = b; /* note the reverse order */
+                *dst++ = g;
+                *dst++ = r;
+                if (cImgChannels == 4)
+                {
+                    a = *src++;
+                }
+            }
+        }
+
+        // free memory
+
+        if (pStretchedImage != NULL)
+        {
+            free (pStretchedImage);
+            pStretchedImage = NULL;
+        }
+
+    }
+
+    // process the image not-stretched
+
+    else
+    {
+        // calculate the central position
+
+        cxImgPos = (cxWinSize - cxImgSize) / 2;
+        cyImgPos = (cyWinSize - cyImgSize) / 2;
+
+        // check for image larger than window
+
+        if (cxImgPos < MARGIN)
+            cxImgPos = MARGIN;
+        if (cyImgPos < MARGIN)
+            cyImgPos = MARGIN;
+
+        // calculate both row-bytes
+
+        wImgRowBytes = cImgChannels * cxImgSize;
+        wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2;
+
+        // copy image to screen
+
+        for (yImg = 0, yWin = cyImgPos; yImg < cyImgSize; yImg++, yWin++)
+        {
+            if (yWin >= cyWinSize - MARGIN)
+                break;
+            src = pbImage + yImg * wImgRowBytes;
+            dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels;
+
+            for (xImg = 0, xWin = cxImgPos; xImg < cxImgSize; xImg++, xWin++)
+            {
+                if (xWin >= cxWinSize - MARGIN)
+                    break;
+                r = *src++;
+                g = *src++;
+                b = *src++;
+                *dst++ = b; /* note the reverse order */
+                *dst++ = g;
+                *dst++ = r;
+                if (cImgChannels == 4)
+                {
+                    a = *src++;
+                }
+            }
+        }
+    }
+
+    return TRUE;
+}
+
+//-----------------
+//  end of source
+//-----------------