/******************************Module*Header*******************************\
* Module Name: julia.c
*
* Main module for the Mandelbrot Dream
*       contains almost everything; windows procedure + misc stuff
*
* Created: 24-Oct-1991 18:34:08
* Author: Petrus Wong
*
* Copyright (c) 1993 Microsoft Corporation
*
* The Mandelbrot Dream serves to demonstrate the GDI and USER
* functionalities in the setting of fractals.
*
* The Mandelbrot Dream provides the following functions:
*       1.  Drawing the Mandelbrot set and the corresponding julia set
*       2.  Zooming into any of the set
*       3.  MDI fractal drawing windows
*       4.  Floating Point Math/Fix Point Math
*       5.  Shifting color table entries
*       6.  Changing palette entries and animating palatte aka color cycling
*       7.  Loading/Saving bitmap created with special effect
*       8.  Changing bitmap color with flood fill
*       9.  Boundary tracing and creating a clip region out of it for
*           creating special effect
*      10.  Enumerate printers for printing
*      11.  Load RLE (or convert .bmp files to RLE) for playing in viewer
*      12.  Save the RLE in memory to disk.
*
* Note: Users can now draw and saves the julia sets on disk as bmps in
*       the Julia windows.  These bitmaps can then be read into the memory
*       (and converted to RLE format) one by one for displaying in sequence
*       in the viewer window.  Eg Load the julia.rle in the viewer window
*       and select the play or play continuously menu item.     02-Jan-1993
*
* Note2: The fix point math in this sample makes use of the LargeInteger
*        64 bit math library.
*
* Dependencies:
*
*       none
*
\**************************************************************************/
#include <windows.h>
#include <stdlib.h>
#include <commdlg.h>
#include <stdarg.h>
#include <math.h>
#include <stdio.h>
#include <shellapi.h>
#include "julia.h"
#include <largeint.h>

//
// For T1 to create all pens in advance.  This is not a good approach
// because pens are per thread basis.  The drawing threads won't be able
// to use them if they are not created in their threads.    18-Sep-1992
//
//#define THRDONE

#define CYCLETHRD

#define PRTTHRD
#define NEWPRTAPI

#ifndef DEBUG
   #undef OutputDebugString
   #define OutputDebugString(LPCSTR)
#endif

//
// Forward declarations.
//
BOOL InitializeApp            (INT*);
LONG APIENTRY MainWndProc     (HWND, UINT, DWORD, LONG);
LONG APIENTRY ChildWndProc    (HWND, UINT, DWORD, LONG);
BOOL CALLBACK About           (HWND, UINT, DWORD, LONG);
LONG APIENTRY TextWndProc     (HWND, UINT, DWORD, LONG);
LONG APIENTRY JuliaWndProc    (HWND, UINT, DWORD, LONG);
LONG APIENTRY ViewerWndProc   (HWND, UINT, DWORD, LONG);
LONG APIENTRY ViewSurfWndProc (HWND, UINT, DWORD, LONG);
BOOL APIENTRY SuspendDrawThrd (HWND, LONG);
BOOL APIENTRY ResumeDrawThrd  (HWND, LONG);
BOOL StartDraw       (PINFO);
BOOL StartDrawFix    (PINFO);
BOOL StartDraw2      (PINFO);
BOOL StartMandelbrot (PINFO);
BOOL StartMandelbrotFix (PINFO);
HBITMAP SaveBitmap   (HWND, HPALETTE);
void DrawBitmap      (HDC, PINFO, int, int, int, int);
BOOL bDrawDIB        (HDC, PINFO, int, int, int, int);
LONG lMul(LONG, LONG);
LONG lDiv(LONG, LONG);
PINFO pGetInfoData(HWND);
BOOL bReleaseInfoData(HWND);
BOOL bCheckMutexMenuItem(PINFO, HMENU, UINT);
VOID vChkMenuItem(PINFO, HMENU, UINT);
BOOL bInitInfo(PINFO);
BOOL bResetGlobal(VOID);
HBRUSH hBrCreateBrush(HDC, DWORD);
BOOL bPrintBmp(PPRTDATA);
BOOL bStoreRleFile(HDC, PINFO, PSTR);
BOOL bFreeRleFile(PINFO);
BOOL bPlayRle(PINFO);
BOOL bSaveRleFile(HDC, PINFO, PSTR);
BOOL bPlayRleCont2(PINFO);
BOOL bSelectDIBPal(HDC, PINFO, LPBITMAPINFO, BOOL);
HBITMAP DIBfromDDB(HDC, HBITMAP, PINFO);

/******************************Public*Routine******************************\
*
* WinMain
*
* History:
*  13-Jun-1992 -by- Petrus Wong     Creates an array of pens
*  17-Apr-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

int WINAPI WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nShowCmd)
{
    MSG    msg;

    ghModule = GetModuleHandle(NULL);
    if (!InitializeApp(&giPen)) {
	MessageBox(ghwndMain, "memory: InitializeApp failure!", "Error", MB_OK);
        return 0;
    }

    if (!(ghAccel = LoadAccelerators (ghModule, MAKEINTRESOURCE(ACCEL_ID))))
	MessageBox(ghwndMain, "memory: Load Accel failure!", "Error", MB_OK);


    while (GetMessage(&msg, NULL, 0, 0)) {
        if (!TranslateAccelerator( ghwndMain, ghAccel, &msg) &&
            !TranslateMDISysAccel(  ghwndClient, &msg)          ) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }


    DeleteObject(ghPal);

    return 1;

    UNREFERENCED_PARAMETER(lpCmdLine);
    UNREFERENCED_PARAMETER(nShowCmd);
    UNREFERENCED_PARAMETER(hInstance);
    UNREFERENCED_PARAMETER(hPrevInstance);
}


/***************************************************************************\
* InitializeApp
*
* History:
* 13-Jun-1992   Petrus Wong     creates pens
* 29-May-1992   Petrus Wong     check if device supports palette for cycling
* 09-09-91      Petrus Wong	Created.
\***************************************************************************/

BOOL InitializeApp(INT *piPen)
{
    WNDCLASS wc;
    HDC      hDC;

#ifdef THRDONE
    INT      iNumClr;
#endif

    wc.style            = CS_OWNDC;
    wc.lpfnWndProc      = (WNDPROC)MainWndProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra	= sizeof(LONG);
    wc.hInstance        = ghModule;
    wc.hIcon            = LoadIcon(ghModule, MAKEINTRESOURCE(APPICON));
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground	= (HBRUSH)(COLOR_APPWORKSPACE);
    wc.lpszMenuName     = "MainMenu";
    wc.lpszClassName	= "MandelClass";

    if (!RegisterClass(&wc))
	return FALSE;

    wc.lpfnWndProc	= (WNDPROC)ChildWndProc;
    wc.hIcon            = LoadIcon(ghModule, MAKEINTRESOURCE(APPICON));
    wc.lpszMenuName	= NULL;
    wc.lpszClassName	= "ChildClass";

    if (!RegisterClass(&wc))
        return FALSE;

    wc.lpfnWndProc	= (WNDPROC)ViewerWndProc;
    wc.hIcon            = LoadIcon(ghModule, MAKEINTRESOURCE(VIEWICON));
    wc.lpszMenuName	= NULL;
    wc.lpszClassName	= "ViewerClass";

    if (!RegisterClass(&wc))
        return FALSE;

    wc.style		= CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc	= (WNDPROC)TextWndProc;
    wc.hIcon		= NULL;
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground	= (HBRUSH)(COLOR_BTNSHADOW);
    wc.lpszMenuName	= NULL;
    wc.lpszClassName	= "Text";

    if (!RegisterClass(&wc))
            return FALSE;

    wc.style		= CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc	= (WNDPROC)JuliaWndProc;
    wc.hIcon		= NULL;

    //
    // Nope.  Can't have this, screw up my Paint Can cursor
    //
    //wc.hCursor          = LoadCursor(NULL, IDC_ARROW);

    wc.hCursor          = NULL;
    wc.hbrBackground	= (HBRUSH)(COLOR_BACKGROUND);
    wc.lpszMenuName	= NULL;
    wc.lpszClassName	= "Julia";

    if (!RegisterClass(&wc))
            return FALSE;

    wc.style		= CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc	= (WNDPROC)ViewSurfWndProc;
    wc.hIcon		= NULL;
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground	= (HBRUSH)(COLOR_BACKGROUND);
    wc.lpszMenuName	= NULL;
    wc.lpszClassName	= "View";

    if (!RegisterClass(&wc))
            return FALSE;


    //
    // Notice, submenu is zero-based
    //
    hMenu	= LoadMenu(ghModule, "MainMenu");
    hChildMenu  = LoadMenu(ghModule, "ChildMenu");
    hViewMenu   = LoadMenu(ghModule, "ViewMenu");
    hViewSubOne = GetSubMenu(hViewMenu, 1);
    hSubMenuOne = GetSubMenu(hMenu, 1);
    hSubMenuThree = GetSubMenu(hChildMenu, 8);
    hPrinterMenu = GetSubMenu(hChildMenu, 7);

    //
    // Disable color-cycling for display devices that does not support
    // palette like the VGA.  As as 29-May-1992, the MIPS display driver
    // is the only one that supports palette
    //
    hDC = GetDC(NULL);
    if (!((GetDeviceCaps(hDC, RASTERCAPS)) & RC_PALETTE)) {
        EnableMenuItem(hChildMenu, MM_CYCLE, MF_GRAYED);
    }

#ifdef THRDONE

    if ((iNumClr = iCreatePenFrPal(hDC, NULL, 0, &ghPal)) != 0) {
        sprintf( gtext,"iNumClr = %d\n", iNumClr);
        OutputDebugString( gtext);

        if ((pInfo->prghPen = (PVOID*) GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(HPEN)*iNumClr)) == NULL) {
            MessageBox(ghwndMain, "Failed in Memory Allocation for pInfo->prghPen!", "Error", MB_OK);
        } else {
            if ((*piPen = iCreatePenFrPal(hDC, pInfo->prghPen, 0, &ghPal)) == 0)
                MessageBox(ghwndMain, "Failed in creating pen!", "Error", MB_OK);
        }
    }

#endif
    ReleaseDC(NULL, hDC);

    ghwndMain = CreateWindowEx(0L, "MandelClass", "Mandelbrot Dream",
	    WS_OVERLAPPED   | WS_CAPTION     | WS_BORDER       |
	    WS_THICKFRAME   | WS_MAXIMIZEBOX | WS_MINIMIZEBOX  |
	    WS_CLIPCHILDREN | WS_VISIBLE     | WS_SYSMENU,
	    80, 70, 550, 550,
	    NULL, hMenu, ghModule, NULL);

    if (ghwndMain == NULL)
	return FALSE;

    bInitPrinter(ghwndMain);

    SetWindowLong(ghwndMain, GWL_USERDATA, 0L);

    SetFocus(ghwndMain);    /* set initial focus */

    PostMessage(ghwndMain, WM_COMMAND, MM_MANDEL, 0L);
    PostMessage(ghwndMain, WM_COMMAND, MM_CREATE_MANDEL_THREAD, 0L);

    return TRUE;
}


/******************************Public*Routine******************************\
*
* MainWndProc
*
* History:
*  09-Sept-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

long APIENTRY MainWndProc(
    HWND hwnd,
    UINT message,
    DWORD wParam,
    LONG lParam)
{
    static int         iJuliaCount=1;
    static int	       iMandelCount=1;
    static int         iViewerCount=1;
    CLIENTCREATESTRUCT clientcreate;
    HWND               hwndChildWindow;
    static FARPROC     lpfnSuspendThrd, lpfnResumeThrd;


    switch (message) {

      case WM_CREATE:
	SetWindowLong(hwnd, 0, (LONG)NULL);

	clientcreate.hWindowMenu  = hSubMenuOne;
	clientcreate.idFirstChild = 1;

	ghwndClient = CreateWindow("MDICLIENT", NULL,
				    WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
				    0,0,0,0,
				    hwnd, NULL, ghModule, (LPVOID)&clientcreate);
        lpfnSuspendThrd = (FARPROC)MakeProcInstance (SuspendDrawThrd, ghModule);
        lpfnResumeThrd  = (FARPROC)MakeProcInstance (ResumeDrawThrd, ghModule);

        return 0L;

      case WM_DESTROY: {
        bCleanupPrinter();
	PostQuitMessage(0);
	return 0L;
      }

      //
      // Wait! User is going to zero out our app's visible region.  This
      // is going to mess up our drawing (we are not keeping any shadow
      // bitmap in this version yet.) So, let's suspend our drawing thread
      // first before user does that.  We will resume after user is done.
      //
      case WM_SYSCOMMAND: {
        LONG        lResult;

        //
        // We'll enumerate our children and suspend their drawing thread.
        //
        EnumChildWindows(ghwndClient, (WNDENUMPROC)lpfnSuspendThrd, lParam);

        //
        // Now, let user does it supposed to do
        //
        lResult = DefFrameProc(hwnd,  ghwndClient, message, wParam, lParam);

        //
        // User's done, we'll resume the suspended threads in our children
        //
        EnumChildWindows(ghwndClient, (WNDENUMPROC)lpfnResumeThrd, lParam);

        return lResult;
        break;
      }
#if 0
      //
      // Our window's size is going to change, we'll make sure the new
      // window is a square.
      //
      case WM_WINDOWPOSCHANGING: {
            PWINDOWPOS pWndPos;
            RECT       rect;
            LONG       lcx, lcy;

            GetWindowRect(hwnd, &rect);
            lcx = rect.right-rect.left;
            lcy = rect.bottom-rect.top;
            pWndPos = (PWINDOWPOS)lParam;
            if ((pWndPos->cy > lcy) || (pWndPos->cx > lcx))
                pWndPos->cx =  pWndPos->cy =
                   ((pWndPos->cx > pWndPos->cy) ? pWndPos->cy : pWndPos->cx);
            else if ((pWndPos->cy < lcy) || (pWndPos->cx < lcx))
                     pWndPos->cx =  pWndPos->cy =
                        ((pWndPos->cx > pWndPos->cy) ? pWndPos->cy : pWndPos->cx);
            break;
      }
#endif

      case WM_COMMAND:

	switch (LOWORD(wParam)) {
	    case IDM_TILE:
		SendMessage(ghwndClient, WM_MDITILE, 0L, 0L);
		return 0L;
	    case IDM_CASCADE:
		SendMessage(ghwndClient, WM_MDICASCADE, 0L, 0L);
		return 0L;
	    case IDM_ARRANGE:
		SendMessage(ghwndClient, WM_MDIICONARRANGE, 0L, 0L);
		return 0L;

            //
            // Create Julia or Mandelbrot set
            //
	    case MM_JULIA:
	    case MM_MANDEL: {
		HANDLE hInfo;
		PINFO  pInfo;
                MDICREATESTRUCT mdicreate;

		hInfo = LocalAlloc(LHND, (WORD) sizeof(INFO));
		if (hInfo == NULL) {
                    MessageBox(ghwndMain, "Failed to Allocate Info!", "Error", MB_OK);
                    return 0L;
                }
		if ((pInfo = (PINFO)LocalLock(hInfo)) == NULL) {
		    MessageBox(ghwndMain, "Failed in LocalLock, hInfo", "Error", MB_OK);
                    return 0L;
                }

                bInitInfo(pInfo);
                wsprintf((LPSTR) &(pInfo->CaptionBarText),
                         (LOWORD(wParam) == MM_JULIA) ? "Julia %d" : "Mandelbrot %d",
                         (LOWORD(wParam) == MM_JULIA) ? iJuliaCount : iMandelCount   );
                if (LOWORD(wParam) == MM_JULIA) {
                    c1 = 0.360284;
                    c2 = 0.100376;
                    lc1 = 738;          //.3603515
                    lc2 = 206;          //.1005859
                    pInfo->bMandel = FALSE;
                } else {
                    pInfo->bMandel = TRUE;
                }

                //
                // Fill in the MDICREATE structure for MDI child creation
                //
		mdicreate.szClass = "ChildClass";
                mdicreate.szTitle = (LPTSTR)&(pInfo->CaptionBarText);
                mdicreate.hOwner  = ghModule;
                mdicreate.x       =
                mdicreate.y       = CW_USEDEFAULT;
                mdicreate.cx      = 300;
                mdicreate.cy      = 300;
                mdicreate.style   = 0L;
                mdicreate.lParam  = (LONG) hInfo;

                /*Create Child Window*/
                hwndChildWindow =
                    (HANDLE) SendMessage(ghwndClient, WM_MDICREATE,
                                0L,
                                (LONG)(LPMDICREATESTRUCT)&mdicreate);

                if (hwndChildWindow == NULL) {
                    MessageBox(ghwndMain, "Failed in Creating Child Window", "Error", MB_OK);
                    return 0L;
                }

                (LOWORD(wParam) == MM_JULIA) ? iJuliaCount++ : iMandelCount++ ;
                LocalUnlock(hInfo);
		return ((LONG)hwndChildWindow);
	    }

            case MM_RLEVIEWER: {
		HANDLE hInfo;
		PINFO  pInfo;
                MDICREATESTRUCT mdicreate;

		hInfo = LocalAlloc(LHND, (WORD) sizeof(INFO));
		if (hInfo == NULL) {
                    MessageBox(ghwndMain, "Failed to Allocate Info!", "Error", MB_OK);
                    return 0L;
                }
		if ((pInfo = (PINFO)LocalLock(hInfo)) == NULL) {
		    MessageBox(ghwndMain, "Failed in LocalLock, hInfo", "Error", MB_OK);
                    return 0L;
                }

                bInitInfo(pInfo);
                wsprintf((LPSTR) &(pInfo->CaptionBarText), "Viewer %d", iViewerCount );

                //
                // Fill in the MDICREATE structure for MDI child creation
                //
		mdicreate.szClass = "ViewerClass";
                mdicreate.szTitle = (LPTSTR)&(pInfo->CaptionBarText);
                mdicreate.hOwner  = ghModule;
                mdicreate.x       =
                mdicreate.y       = CW_USEDEFAULT;
                mdicreate.cx      = 300;
                mdicreate.cy      = 300;
                mdicreate.style   = 0L;
                mdicreate.lParam  = (LONG) hInfo;

                /*Create Child Window*/
                hwndChildWindow =
                    (HANDLE) SendMessage(ghwndClient, WM_MDICREATE,
                                0L,
                                (LONG)(LPMDICREATESTRUCT)&mdicreate);

                if (hwndChildWindow == NULL) {
                    MessageBox(ghwndMain, "Failed in Creating Child Window", "Error", MB_OK);
                    return 0L;
                }

                iViewerCount++ ;
                LocalUnlock(hInfo);
		return ((LONG)hwndChildWindow);

            }

	    case MM_ABOUT:
		if (DialogBox(ghModule, "AboutBox", ghwndMain, (DLGPROC)About) == -1)
		   MessageBox(ghwndMain, "DEMO: About Dialog Creation Error!", "Error", MB_OK);
		return 0L;

            //
            // Only my children know how to deal with these messages, so
            // pass these to children for processing
            //
	    case MM_CREATE_JULIA_THREAD:
	    case MM_SET_XFORM_ATTR:
	    case MM_CREATE_MANDEL_THREAD:
	    case MM_OPT_4:              // currently not used
	    case MM_DRAW_SET:
            case MM_SETDIB2DEVICE:
	    case MM_BW:
	    case MM_SHIFT:
	    case MM_CUSTOM:
            case MM_CYCLE:
            case MM_TP_IDLE:
            case MM_TP_LOW:
            case MM_TP_BELOW_NORMAL:
            case MM_TP_NORMAL:
            case MM_TP_ABOVE_NORMAL:
            case MM_TP_HIGH:
            case MM_TP_TIME_CRITICAL:
            case MM_FLOAT:
            case MM_FIX:
            case MM_ITERATION_100:
            case MM_ITERATION_500:
            case MM_ITERATION_1000:
            case MM_ITERATION_5000:
            case MM_ITERATION_DOUBLE:
            case MM_STEP_ONE:
            case MM_STEP_TWO:
            case MM_STEP_THREE:
            case MM_SAVE:
            case MM_SAVE_MONO:
            case MM_LOAD:
            case MM_STRETCHBLT:
            case MM_BITBLT:
            case MM_BLACKONWHITE:
            case MM_COLORONCOLOR:
            case MM_WHITEONBLACK:
            case MM_HALFTONE:
            case MM_CLIP:
            case MM_RM_CLIP:
            case MM_SELCLIPRGN:
            case MM_ERASE:
            case MM_PORTRAIT:
            case MM_LANDSCAPE:
            case MM_PRINTER:
            case MM_PRINTER + 1:
            case MM_PRINTER + 2:
            case MM_PRINTER + 3:
            case MM_PRINTER + 4:
            case MM_PRINTER + 5:
            case MM_PRINTER + 6:
            case MM_PRINTER + 7:
            case MM_PRINTER + 8:
            case MM_PRINTER + 9:
            case MM_RLELOAD_DEMO:
            case MM_RLEPLAYCONT:
            case MM_RLELOAD:
            case MM_RLESAVE:
            case MM_CLEAR:
            case MM_RLEPLAY:
            {
		HWND hActiveChild;

		hActiveChild = (HANDLE) SendMessage(ghwndClient, WM_MDIGETACTIVE, 0L, 0L);
		if (hActiveChild)
		    SendMessage(hActiveChild, WM_COMMAND, wParam, lParam);
		return 0L;
	    }

	    default:
		return DefFrameProc(hwnd,  ghwndClient, message, wParam, lParam);
        }
    default:

	return DefFrameProc(hwnd,  ghwndClient, message, wParam, lParam);
    }
}

/***************************************************************************\
* ChildWndProc
*
* History:
* 04-17-91 ????         Created.
* 09-09-91 Petrus Wong	Rewrote.
\***************************************************************************/

long APIENTRY ChildWndProc(
    HWND hwnd,
    UINT message,
    DWORD wParam,
    LONG lParam)
{
    static FARPROC     lpfnSuspendThrd, lpfnResumeThrd;
    static BOOL        bDIB2Device = FALSE;

    sprintf( gtext,"message = %lx\n", message);
    OutputDebugString( gtext);

    switch (message) {
	case WM_COMMAND: {
          PINFO       pInfo;
          HWND        hTextWnd;

	  switch (LOWORD(wParam)) {
            //
            // Create a Julia drawing thread
            //
	    case MM_CREATE_JULIA_THREAD: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                hTextWnd = pInfo->hTextWnd;
                sprintf( gtext,"(%g, %g) <-> (%g, %g)", pInfo->xFrom, pInfo->yFrom, pInfo->xTo, pInfo->yTo);
                SetWindowText(hTextWnd, gtext);
                sprintf( gtext,"(c1 = %g, c2 = %g)\n\n", pInfo->c1, pInfo->c2);
                OutputDebugString( gtext );

                if (pInfo->hThrd)
                    CloseHandle(pInfo->hThrd);

                pInfo->hThrd = CreateThread(NULL, 0,
                                 (gFloat ? (LPTHREAD_START_ROUTINE)StartDraw : (LPTHREAD_START_ROUTINE)StartDrawFix),
                                 pInfo,
                                 STANDARD_RIGHTS_REQUIRED,
                                 &pInfo->dwThreadId );

               if (pInfo->hThrd && pInfo->bDrawing) {
                   if (!SetThreadPriority(pInfo->hThrd, pInfo->iPriority))
                        MessageBox(ghwndMain, "Can't set Priority!",
                                   "Error", MB_OK);
               }

               bReleaseInfoData(hwnd);
               return 0L;
            }

            //
            // Reset pInfo reflecting new transformation
            //
	    case MM_SET_XFORM_ATTR: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                hTextWnd = pInfo->hTextWnd;
                SetWindowText(hTextWnd, "Click here for viewing info");

                pInfo->xFrom      = xFrom;
                pInfo->xTo        = xTo;
                pInfo->yFrom      = yFrom;
                pInfo->yTo        = yTo;
                pInfo->c1         = c1;
                pInfo->c2         = c2;
                pInfo->lxFrom      = lxFrom;
                pInfo->lxTo        = lxTo;
                pInfo->lyFrom      = lyFrom;
                pInfo->lyTo        = lyTo;
                pInfo->lc1         = lc1;
                pInfo->lc2         = lc2;

               bReleaseInfoData(hwnd);
	       return 0L;
	    }

            //
            // Create a Mandelbrot drawing thread
            //
	    case MM_CREATE_MANDEL_THREAD: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                hTextWnd = pInfo->hTextWnd;
                sprintf( gtext,"(%g, %g) <-> (%g, %g)", pInfo->xFrom, pInfo->yFrom, pInfo->xTo, pInfo->yTo);
                SetWindowText(hTextWnd, gtext);
                sprintf( gtext,"(c1 = %g, c2 = %g)\n\n", pInfo->c1, pInfo->c2);
                OutputDebugString( gtext );

                if (pInfo->hThrd)
                    CloseHandle(pInfo->hThrd);

                pInfo->hThrd = CreateThread(NULL, 0,
                                 (gFloat ? (LPTHREAD_START_ROUTINE)StartMandelbrot : (LPTHREAD_START_ROUTINE)StartMandelbrotFix),
                                 pInfo,
                                 STANDARD_RIGHTS_REQUIRED,
                                 &pInfo->dwThreadId );

               if (pInfo->hThrd && pInfo->bDrawing) {
                   if (!SetThreadPriority(pInfo->hThrd, pInfo->iPriority))
                        MessageBox(ghwndMain, "Can't set Priority!",
                                   "Error", MB_OK);
               }


               bReleaseInfoData(hwnd);
               return 0L;
	    }

            //
            // Create a Julia drawing thread using algorithm StartDraw2
            // Currently not used
            //
	    case MM_OPT_4: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                hTextWnd = pInfo->hTextWnd;
                SetWindowText(hTextWnd, "MM_OPT_4");

                sprintf( gtext,"xFrom = %g, xTo = %g, yFrom = %g, yTo = %g\n", pInfo->xFrom, pInfo->xTo, pInfo->yFrom, pInfo->yTo);
                OutputDebugString( gtext );

                if (pInfo->hThrd)
                    CloseHandle(pInfo->hThrd);

                pInfo->hThrd = CreateThread(NULL, 0,
                                 (LPTHREAD_START_ROUTINE)StartDraw2,
                                 pInfo,
                                 STANDARD_RIGHTS_REQUIRED,
                                 &pInfo->dwThreadId );

               if (pInfo->hThrd && pInfo->bDrawing) {
                   if (!SetThreadPriority(pInfo->hThrd, pInfo->iPriority))
                        MessageBox(ghwndMain, "Can't set Priority!",
                                   "Error", MB_OK);
               }

               bReleaseInfoData(hwnd);
	       return 0L;
	    }

            case MM_DRAW_SET: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                PostMessage(hwnd, WM_COMMAND,
                    pInfo->bMandel ? (DWORD)((WORD)MM_CREATE_MANDEL_THREAD) : (DWORD)((WORD)MM_CREATE_JULIA_THREAD),
                    (LONG)0L);

               bReleaseInfoData(hwnd);
               return 0L;
            }

            {
            int     iPriority;

                case MM_TP_IDLE:
                    if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                        return 0L;
                    }

                    iPriority = THREAD_PRIORITY_IDLE;
                    bCheckMutexMenuItem(pInfo, hChildMenu, MM_TP_IDLE);
                    DrawMenuBar(GetParent(GetParent(hwnd))) ;
                    goto CWP_SET_PRIORITY;

                case MM_TP_LOW:
                    if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                        return 0L;
                    }

                    iPriority = THREAD_PRIORITY_LOWEST;
                    bCheckMutexMenuItem(pInfo, hChildMenu, MM_TP_LOW);
                    DrawMenuBar(GetParent(GetParent(hwnd))) ;
                    goto CWP_SET_PRIORITY;

                case MM_TP_BELOW_NORMAL:
                    if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                        return 0L;
                    }

                    iPriority = THREAD_PRIORITY_BELOW_NORMAL;
                    bCheckMutexMenuItem(pInfo, hChildMenu, MM_TP_BELOW_NORMAL);
                    DrawMenuBar(GetParent(GetParent(hwnd))) ;
                    goto CWP_SET_PRIORITY;

                case MM_TP_NORMAL:
                    if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                        return 0L;
                    }

                    iPriority = THREAD_PRIORITY_NORMAL;
                    bCheckMutexMenuItem(pInfo, hChildMenu, MM_TP_NORMAL);
                    DrawMenuBar(GetParent(GetParent(hwnd))) ;
                    goto CWP_SET_PRIORITY;

                case MM_TP_ABOVE_NORMAL:
                    if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                        return 0L;
                    }

                    iPriority = THREAD_PRIORITY_ABOVE_NORMAL;
                    bCheckMutexMenuItem(pInfo, hChildMenu, MM_TP_ABOVE_NORMAL);
                    DrawMenuBar(GetParent(GetParent(hwnd))) ;
                    goto CWP_SET_PRIORITY;

                case MM_TP_HIGH:
                    if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                        return 0L;
                    }

                    iPriority = THREAD_PRIORITY_HIGHEST;
                    bCheckMutexMenuItem(pInfo, hChildMenu, MM_TP_HIGH);
                    DrawMenuBar(GetParent(GetParent(hwnd))) ;
                    goto CWP_SET_PRIORITY;

                case MM_TP_TIME_CRITICAL:
                    if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                        return 0L;
                    }

                    iPriority = THREAD_PRIORITY_TIME_CRITICAL;
                    bCheckMutexMenuItem(pInfo, hChildMenu, MM_TP_TIME_CRITICAL);
                    DrawMenuBar(GetParent(GetParent(hwnd))) ;

CWP_SET_PRIORITY:
                    {
                       HANDLE       hThrd;

                       hThrd = pInfo->hThrd;
                       pInfo->iPriority = iPriority;

                       if (hThrd && pInfo->bDrawing) {
                           if (!SetThreadPriority(hThrd, iPriority))
                                MessageBox(ghwndMain, "Can't set Priority!",
                                           "Error", MB_OK);
                       }

                    }
                    bReleaseInfoData(hwnd);
                    return 0L;

            }

            case MM_FLOAT: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_FLOAT);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                gFloat = TRUE;
                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_FIX: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_FIX);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                gFloat = FALSE;
                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_ITERATION_100: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_ITERATION_100);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                gIteration = 100;
                pInfo->iIteration = 100;
                SetWindowText(pInfo->hTextWnd, "Iteration = 100");
                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_ITERATION_500: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL){
                    return 0L;
                }
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_ITERATION_500);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                gIteration = 500;

                pInfo->iIteration = 500;
                SetWindowText(pInfo->hTextWnd, "Iteration = 500");
                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_ITERATION_1000: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_ITERATION_1000);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                gIteration = 1000;
                pInfo->iIteration = 1000;
                SetWindowText(pInfo->hTextWnd, "Iteration = 1000");
                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_ITERATION_5000: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_ITERATION_5000);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                gIteration = 5000;
                pInfo->iIteration = 5000;
                SetWindowText(pInfo->hTextWnd, "Iteration = 5000");
                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_ITERATION_DOUBLE: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_ITERATION_DOUBLE);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                gIteration *= 2;
                pInfo->iIteration = gIteration;
                sprintf( gtext,"Iteration = %d", pInfo->iIteration);
                SetWindowText(pInfo->hTextWnd, gtext);
                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_STEP_ONE: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_STEP_ONE);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                gStep = 1;
                pInfo->iStep = 1;

                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_STEP_TWO:  {
                if ((pInfo = pGetInfoData(hwnd)) == NULL){
                    return 0L;
                }
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_STEP_TWO);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                gStep = 2;
                pInfo->iStep = 2;

                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_STEP_THREE: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_STEP_THREE);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                gStep = 3;
                pInfo->iStep = 3;

                bReleaseInfoData(hwnd);
                return 0L;
            }

            case MM_LOAD: {
                HDC hDC;
                OPENFILENAME ofn;
                char szDirName[256];
                char szFile[256], szFileTitle[256];
                static char *szFilter;
                RECT    rc;

                if ((pInfo = pGetInfoData(hwnd)) == NULL){
                    return 0L;
                }

                szFilter =
                  "DIB files (*.bmp)\0*.bmp\0RLE files (*.rle)\0*.rle\0\0";

                GetSystemDirectory((LPSTR) szDirName, 256);
                strcpy(szFile, "*.bmp\0");
                ofn.lStructSize = sizeof(OPENFILENAME);
                ofn.hwndOwner = pInfo->hwnd;
                ofn.lpstrFilter = szFilter;
                ofn.lpstrCustomFilter = (LPSTR) NULL;
                ofn.nMaxCustFilter = 0L;
                ofn.nFilterIndex = 1;
                ofn.lpstrFile = szFile;
                ofn.nMaxFile = sizeof(szFile);
                ofn.lpstrFileTitle = szFileTitle;
                ofn.nMaxFileTitle = sizeof(szFileTitle);
                ofn.lpstrInitialDir = szDirName;
                ofn.lpstrTitle = (LPSTR) NULL;
                ofn.Flags = 0L;
                ofn.nFileOffset = 0;
                ofn.nFileExtension = 0;
                ofn.lpstrDefExt = "BMP";

                if (!GetOpenFileName(&ofn))
                    return 0L;

                GetClientRect(pInfo->hwnd, &rc);
                hDC = GetDC(pInfo->hwnd);
                if (LoadBitmapFile(hDC, pInfo, szFile))
                  bDrawDIB(hDC, pInfo, 0, 0, rc.right, rc.bottom);
                ReleaseDC(hwnd, hDC);

                bReleaseInfoData(hwnd);

                return 0L;
            }

            case MM_SAVE: {
                HDC hDC;
                OPENFILENAME ofn;
                char szDirName[256];
                char szFile[256], szFileTitle[256];
                static char *szFilter;
                szFilter = "DIB files (*.bmp)\0\0";

                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                hDC = GetDC(pInfo->hwnd);
#if 0
                {
                HPALETTE hPalTmp;

                hPalTmp = CopyPalette(pInfo->hPal);
                DeleteObject(pInfo->hPal);
                pInfo->hPal = hPalTmp;
                }
#endif
                //
                // saving special effects user might have created in window
                //
                if (pInfo->hBmpSaved)
                    DeleteObject(pInfo->hBmpSaved);
                pInfo->hBmpSaved = SaveBitmap(pInfo->hwnd, pInfo->hPal);
                pInfo->bUseDIB = FALSE;

                GetSystemDirectory((LPSTR) szDirName, 256);
                strcpy(szFile, "*.bmp\0");
                ofn.lStructSize = sizeof(OPENFILENAME);
                ofn.hwndOwner = pInfo->hwnd;
                ofn.lpstrFilter = szFilter;
                ofn.lpstrCustomFilter = (LPSTR) NULL;
                ofn.nMaxCustFilter = 0L;
                ofn.nFilterIndex = 0L;
                ofn.lpstrFile = szFile;
                ofn.nMaxFile = sizeof(szFile);
                ofn.lpstrFileTitle = szFileTitle;
                ofn.nMaxFileTitle = sizeof(szFileTitle);
                ofn.lpstrInitialDir = szDirName;
                ofn.lpstrTitle = (LPSTR) NULL;
                ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;
                ofn.nFileOffset = 0;
                ofn.nFileExtension = 0;
                ofn.lpstrDefExt = (LPSTR)NULL;

                if (!GetSaveFileName(&ofn)) {
                    ReleaseDC(pInfo->hwnd, hDC);
                    bReleaseInfoData(hwnd);
                    return 0L;
                }

                SelectPalette(hDC,
                   ((pInfo->iStretchMode == HALFTONE) ? pInfo->hHTPal : pInfo->hPal),
                   FALSE);
                RealizePalette(hDC);
                UpdateColors(hDC);

                //
                // test
                //
                ghPal = pInfo->hPal;

                SaveBitmapFile(hDC, pInfo->hBmpSaved, szFile);

                ReleaseDC(pInfo->hwnd, hDC);

                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_SAVE_MONO: {
                HDC hDC;
                OPENFILENAME ofn;
                char szDirName[256];
                char szFile[256], szFileTitle[256];
                static char *szFilter;

                if ((pInfo = pGetInfoData(hwnd)) == NULL){
                    return 0L;
                }

                szFilter = "DIB files (*.bmp)\0\0";

                GetSystemDirectory((LPSTR) szDirName, 256);
                strcpy(szFile, "*.bmp\0");
                ofn.lStructSize = sizeof(OPENFILENAME);
                ofn.hwndOwner = pInfo->hwnd;
                ofn.lpstrFilter = szFilter;
                ofn.lpstrCustomFilter = (LPSTR) NULL;
                ofn.nMaxCustFilter = 0L;
                ofn.nFilterIndex = 0L;
                ofn.lpstrFile = szFile;
                ofn.nMaxFile = sizeof(szFile);
                ofn.lpstrFileTitle = szFileTitle;
                ofn.nMaxFileTitle = sizeof(szFileTitle);
                ofn.lpstrInitialDir = szDirName;
                ofn.lpstrTitle = "Saving Monochrome Bitmap";
                ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;
                ofn.nFileOffset = 0;
                ofn.nFileExtension = 0;
                ofn.lpstrDefExt = (LPSTR)NULL;

                if (!GetSaveFileName(&ofn))
                    return 0L;

                hDC = GetDC(pInfo->hwnd);

                SaveBitmapFile(hDC, pInfo->hBmpMono, szFile);
                ReleaseDC(pInfo->hwnd, hDC);

                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_STRETCHBLT: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL){
                    return 0L;
                }
                gbStretch = TRUE;
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_STRETCHBLT);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                pInfo->bStretch = gbStretch;
                InvalidateRect(pInfo->hwnd, NULL, FALSE);

                bReleaseInfoData(hwnd);
                return 0L;

            }
            case MM_BITBLT: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                gbStretch = FALSE;
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_BITBLT);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                pInfo->bStretch = gbStretch;
                InvalidateRect(pInfo->hwnd, NULL, FALSE);

                bReleaseInfoData(hwnd);
                return 0L;

            }
            case MM_BLACKONWHITE: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                giStretchMode = BLACKONWHITE;
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_BLACKONWHITE);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                pInfo->iStretchMode = giStretchMode;
                InvalidateRect(pInfo->hwnd, NULL, FALSE);

                bReleaseInfoData(hwnd);
                return 0L;

            }
            case MM_COLORONCOLOR: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                giStretchMode = COLORONCOLOR;
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_COLORONCOLOR);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                pInfo->iStretchMode = giStretchMode;
                InvalidateRect(pInfo->hwnd, NULL, FALSE);

                bReleaseInfoData(hwnd);
                return 0L;

            }
            case MM_WHITEONBLACK: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                giStretchMode = WHITEONBLACK;
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_WHITEONBLACK);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                pInfo->iStretchMode = giStretchMode;
                InvalidateRect(pInfo->hwnd, NULL, FALSE);

                bReleaseInfoData(hwnd);
                return 0L;

            }
            case MM_HALFTONE: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                giStretchMode = HALFTONE;
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_HALFTONE);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                pInfo->iStretchMode = giStretchMode;
                InvalidateRect(pInfo->hwnd, NULL, FALSE);

                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_SETDIB2DEVICE: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                bDIB2Device = (bDIB2Device ? FALSE : TRUE);
                pInfo->bSetDIBsToDevice = bDIB2Device;
                CheckMenuItem(hChildMenu, MM_SETDIB2DEVICE, (bDIB2Device ? MF_CHECKED : MF_UNCHECKED));
                bReleaseInfoData(hwnd);
                return 0L;
            }
	    case MM_BW: {
                HDC hDC;

                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                hDC = GetDC(pInfo->hwnd);
                bChangeDIBColor(hDC, pInfo, MM_BW);
                ReleaseDC(hwnd, hDC);
                bReleaseInfoData(hwnd);
                return 0L;
            }
	    case MM_SHIFT: {
                HDC hDC;

                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                hDC = GetDC(pInfo->hwnd);
                bChangeDIBColor(hDC, pInfo, MM_SHIFT);
                ReleaseDC(hwnd, hDC);
                bReleaseInfoData(hwnd);
                return 0L;
            }
	    case MM_CUSTOM: {
                static DWORD argbCust[16] = {
                    RGB(255, 255, 255), RGB(255, 255, 255),
                    RGB(255, 255, 255), RGB(255, 255, 255),
                    RGB(255, 255, 255), RGB(255, 255, 255),
                    RGB(255, 255, 255), RGB(255, 255, 255),
                    RGB(255, 255, 255), RGB(255, 255, 255),
                    RGB(255, 255, 255), RGB(255, 255, 255),
                    RGB(255, 255, 255), RGB(255, 255, 255),
                    RGB(255, 255, 255), RGB(255, 255, 255)
                };
                CHOOSECOLOR cc;
                BOOL bResult;
                DWORD rgbOld;
                HBRUSH hBrush;
                HDC hDC;

                rgbOld = RGB(255, 255, 255);
                cc.lStructSize = sizeof(CHOOSECOLOR);
                cc.hwndOwner = ghwndMain;
                cc.hInstance = ghModule;
                cc.rgbResult = rgbOld;
                cc.lpCustColors = argbCust;
                cc.Flags = CC_RGBINIT | CC_SHOWHELP;
                cc.lCustData = 0;
                cc.lpfnHook = NULL;
                cc.lpTemplateName = NULL;

                bResult = ChooseColor(&cc);
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                if (bResult) {
                    hDC = GetDC(pInfo->hwnd);
                    hBrush = hBrCreateBrush(hDC, cc.rgbResult);
                    ReleaseDC(pInfo->hwnd, hDC);
                    if (pInfo->hBrush)
                        DeleteObject(pInfo->hBrush);
                    pInfo->hBrush = hBrush;
                    pInfo->bFill = TRUE;
                }
                bReleaseInfoData(hwnd);
                return 0L;
                }

#ifndef CYCLETHRD
            case MM_CYCLE: {
                HDC hDC;

                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                hDC = GetDC(pInfo->hwnd);

                if (pInfo->bClrCycle) {
                    CheckMenuItem(hChildMenu, MM_CYCLE, MF_UNCHECKED);
                    pInfo->bClrCycle = FALSE;
                } else {
                    CheckMenuItem(hChildMenu, MM_CYCLE, MF_CHECKED);
                    pInfo->bClrCycle = TRUE;
                    bChangeDIBColor(hDC, pInfo, MM_CYCLE);
                }

                ReleaseDC(hwnd, hDC);
                bReleaseInfoData(hwnd);
                return 0L;
            }
#else

            case MM_CYCLE: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                if (pInfo->bFirstTime) {
                    if (!SetEvent(pInfo->hQuitEvent)) {
                        MessageBox(ghwndMain, "Can't set Quit Event!",
                                   "Error", MB_OK);
                        return 0L;
                    }

                    if (pInfo->hCycleThrd)
                        CloseHandle(pInfo->hCycleThrd);

                    pInfo->hCycleThrd = CreateThread(NULL, 0,
                                     (LPTHREAD_START_ROUTINE)bCycle,
                                     (LPVOID)hwnd,
                                     STANDARD_RIGHTS_REQUIRED,
                                     &pInfo->dwCycleThrdID );
                    pInfo->bClrCycle = TRUE;
                    pInfo->bFirstTime = FALSE;
                    CheckMenuItem(hChildMenu, MM_CYCLE, MF_CHECKED);
                } else {
                    if (pInfo->bClrCycle) {
                        CheckMenuItem(hChildMenu, MM_CYCLE, MF_UNCHECKED);
                        pInfo->bClrCycle = FALSE;
                        pInfo->dwSuspend = SuspendThread(pInfo->hCycleThrd);
                    } else {
                        CheckMenuItem(hChildMenu, MM_CYCLE, MF_CHECKED);
                        pInfo->bClrCycle = TRUE;
                        pInfo->dwSuspend = ResumeThread(pInfo->hCycleThrd);
                    }
                    if (pInfo->dwSuspend == -1) {
                        (pInfo->bClrCycle ?
                         sprintf( gtext,"Error in resuming thread\n") :
                         sprintf( gtext,"Error in suspending thread\n")  );
                        OutputDebugString( gtext );
                    }
                }

                bReleaseInfoData(hwnd);
                return 0L;
            }
#endif
            case MM_CLIP: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                hTextWnd = pInfo->hTextWnd;
                sprintf( gtext,"(%g, %g) <-> (%g, %g)", pInfo->xFrom, pInfo->yFrom, pInfo->xTo, pInfo->yTo);
                SetWindowText(hTextWnd, gtext);
                sprintf( gtext,"(c1 = %g, c2 = %g)\n\n", pInfo->c1, pInfo->c2);
                OutputDebugString( gtext );
                if (!pInfo->bMandel) {
                    MessageBox(ghwndMain, "Boundary Tracing and Setting clip region to the boundary points only works for Mandelbrot Set.", "Error", MB_OK);
                    return 0L;
                }

                if (pInfo->hThrd)
                    CloseHandle(pInfo->hThrd);

                pInfo->hThrd = CreateThread(NULL, 0,
                                 (gFloat ? (LPTHREAD_START_ROUTINE)bBoundaryScanFix : (LPTHREAD_START_ROUTINE)bBoundaryScanFix),
                                 pInfo,
                                 STANDARD_RIGHTS_REQUIRED,
                                 &pInfo->dwThreadId );

               bReleaseInfoData(hwnd);
               return 0L;
	    }
            case MM_RM_CLIP: {
                HDC     hDC;

                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                hDC = GetDC(pInfo->hwnd);
                SelectClipRgn(hDC, (HRGN) NULL);
                ReleaseDC(pInfo->hwnd, hDC);
                bReleaseInfoData(hwnd);
                InvalidateRect(pInfo->hwnd, NULL, FALSE);
                return 0L;

            }
            case MM_SELCLIPRGN: {
                HDC     hDC;

                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                hDC = GetDC(pInfo->hwnd);

                if (pInfo->hRgnPath != (HRGN) NULL) {
                    SelectClipRgn(hDC, pInfo->hRgnPath);
                }

                ReleaseDC(pInfo->hwnd, hDC);
                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_ERASE: {
                HDC     hDC;
                RECT    rc;

                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }
                hDC = GetDC(pInfo->hwnd);
                if (pInfo->hRgnPath != (HRGN) NULL) {
                    SelectClipRgn(hDC, pInfo->hRgnPath);
                }
                SelectObject(hDC, GetStockObject(WHITE_BRUSH));
                GetClientRect(pInfo->hwnd, &rc);
                PatBlt(hDC, 0, 0, rc.right, rc.bottom, PATCOPY);
                ReleaseDC(pInfo->hwnd, hDC);
                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_PORTRAIT: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                giDmOrient = DMORIENT_PORTRAIT;
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_PORTRAIT);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_LANDSCAPE: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                giDmOrient = DMORIENT_LANDSCAPE;
                bCheckMutexMenuItem(pInfo, hChildMenu, MM_LANDSCAPE);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                bReleaseInfoData(hwnd);
                return 0L;
            }
            case MM_PRINTER:
            case MM_PRINTER + 1:
            case MM_PRINTER + 2:
            case MM_PRINTER + 3:
            case MM_PRINTER + 4:
            case MM_PRINTER + 5:
            case MM_PRINTER + 6:
            case MM_PRINTER + 7:
            case MM_PRINTER + 8:

#ifdef PRTTHRD
            case MM_PRINTER + 9: {
                PINFO       pInfo;
                PRTDATA     PrtData, *pPrtData;
                ULONG       sizINFO;
                PBYTE       pjTmpInfo, pjTmp;

                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                if (pInfo->hBmpSaved == NULL) {
                    MessageBox(ghwndMain, "No Saved bitmap to print", "Error", MB_OK);
                    return 0L;
                }

                //
                // Copy the info structure to PrtData
                //
                pPrtData = &PrtData;
                pjTmp    = (PBYTE)&(pPrtData->info);
                pjTmpInfo = (PBYTE)pInfo;
                sizINFO = sizeof(INFO);

                while(sizINFO--)
                {
                    *(((PBYTE)pjTmp)++) = *((pjTmpInfo)++);
                }

                PrtData.index = LOWORD(wParam) - MM_PRINTER;

                if (giDmOrient == DMORIENT_PORTRAIT) {
                    PrtData.bUseDefault = TRUE;
                } else {
                    PrtData.bUseDefault = FALSE;
                    PrtData.DevMode.dmSize = sizeof(DEVMODE);
                    PrtData.DevMode.dmDriverExtra = 0;
                    PrtData.DevMode.dmOrientation = DMORIENT_LANDSCAPE;
                    PrtData.DevMode.dmFields = DM_ORIENTATION;
                }

                if (pInfo->hPrtThrd)
                    CloseHandle(pInfo->hPrtThrd);

                pInfo->hPrtThrd = CreateThread(NULL, 0,
                                 (LPTHREAD_START_ROUTINE)bPrintBmp,
                                 &PrtData,
                                 STANDARD_RIGHTS_REQUIRED,
                                 &pInfo->dwPrtThrdID );

                bReleaseInfoData(hwnd);
                return 0L;
            }

#else
            case MM_PRINTER + 9: {
                HDC         hdcPrinter, hDC;
                int         index;
                DEVMODE     devmode;
                DEVMODE     *pdevmode;
                PINFO       pInfo;
                int         iWidth, iHeight;


                index = LOWORD(wParam) - MM_PRINTER;

                if (giDmOrient == DMORIENT_PORTRAIT)
                    pdevmode = NULL;
                else {
                    pdevmode = &devmode;
                    devmode.dmSize = sizeof(DEVMODE);
                    devmode.dmDriverExtra = 0;
                    devmode.dmOrientation = DMORIENT_LANDSCAPE;
                    devmode.dmFields = DM_ORIENTATION;
                }

                if (!(hdcPrinter = CreateDC( "", gpszPrinterNames[index],
                                             "", pdevmode)))
                {
                    return(0L);
                }

                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                iWidth = GetDeviceCaps(hdcPrinter, HORZRES);
                iHeight = GetDeviceCaps(hdcPrinter, VERTRES);

// !!! Why is it necessary to save the image over again?  May not want to
//     do this because user may want to print the nice HT bitmap. So,
//     use the DIB src.
#if 0
                if (pInfo->hBmpSaved)
                    DeleteObject(pInfo->hBmpSaved);

                pInfo->hBmpSaved = SaveBitmap(pInfo->hwnd, pInfo->hPal);
#endif
		Escape(hdcPrinter, STARTDOC, 20, "Mandelbrot", NULL);
                bDrawDIB(hdcPrinter, pInfo, 0, 0, iWidth, iHeight);
                Escape(hdcPrinter, NEWFRAME, 0, NULL, NULL);
                Escape(hdcPrinter, ENDDOC, 0, NULL, NULL);
                ReleaseDC(pInfo->hwnd, hDC);
                bReleaseInfoData(hwnd);
                DeleteDC(hdcPrinter);
                return 0L;
            }
#endif
            default:
               return 0L;

	  }

	}
	case WM_SETFOCUS:
	    break;

	case WM_MDIACTIVATE: {
            PINFO       pInfo;

            if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                return 0L;
            }

	    if ((HWND) lParam == hwnd) {                // being activated
		SendMessage(GetParent(hwnd), WM_MDISETMENU,
			    (DWORD)  hChildMenu,
			    (LONG)   hSubMenuThree) ;

                (pInfo->bClrCycle ?
                    CheckMenuItem(hChildMenu, MM_CYCLE, MF_CHECKED) :
                    CheckMenuItem(hChildMenu, MM_CYCLE, MF_UNCHECKED) );

                vChkMenuItem(pInfo, hChildMenu, MF_CHECKED);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
                goto MDI_ACT_EXIT;
            }

            if ((HWND) wParam == hwnd) {                // being deactivated

                vChkMenuItem(pInfo, hChildMenu, MF_UNCHECKED);
                DrawMenuBar(GetParent(GetParent(hwnd))) ;
            }
MDI_ACT_EXIT:
            bReleaseInfoData(hwnd);
	    return 0L;
        }

        case WM_QUERYNEWPALETTE:
        case WM_PALETTECHANGED: {
            PINFO       pInfo;

            if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                return 0L;
            }

	    SendMessage(pInfo->hwnd, message, wParam, lParam);
            bReleaseInfoData(hwnd);
	    return 0L;
        }

#if 0
        case WM_WINDOWPOSCHANGING: {
            PWINDOWPOS  pWndPos;
            PINFO       pInfo;
            HWND        hTextWnd;
            int         iCyText, iCxBorder, iCyBorder, iCyCaption;
            RECT        rect, rcl;
            LONG        lcx, lcy;

            if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                break;
            }

            hTextWnd = pInfo->hTextWnd;

            bReleaseInfoData(hwnd);

            iCyText = GetWindowLong(hTextWnd, GWL_USERDATA);
            iCxBorder = GetSystemMetrics(SM_CXBORDER);
            iCyBorder = GetSystemMetrics(SM_CYBORDER);
            iCyCaption = GetSystemMetrics(SM_CYCAPTION) - iCyBorder;
            GetClientRect(GetParent(hwnd), &rcl);
            GetWindowRect(hwnd, &rect);
            lcx = rect.right-rect.left;
            lcy = rect.bottom-rect.top;
            pWndPos = (PWINDOWPOS)lParam;
            if ((pWndPos->cy > lcy) || (pWndPos->cx > lcx)) {
                pWndPos->cx =
                   ((pWndPos->cx > pWndPos->cy) ? pWndPos->cy-iCyText : pWndPos->cx);
                pWndPos->cy = pWndPos->cx + iCyText;
            } else { if ((pWndPos->cy < lcy) || (pWndPos->cx < lcx)) {
                     pWndPos->cx =
                        ((pWndPos->cx > pWndPos->cy) ? pWndPos->cy-iCyText : pWndPos->cx);
                     pWndPos->cy = pWndPos->cx + iCyText;
                   }
            }
            break;
        }
#endif
	case WM_SIZE: {
            HANDLE      hThrd;
            PINFO       pInfo;
            HWND        hTextWnd, hJulia;
            BOOL        bMandel;
            WORD        wCx;
            int         iCyText;

            if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                break;
            }

            hTextWnd = pInfo->hTextWnd;
            hJulia   = pInfo->hwnd;
            hThrd    = pInfo->hThrd;
            bMandel  = pInfo->bMandel;
            bReleaseInfoData(hwnd);
            iCyText = GetWindowLong(hTextWnd, GWL_USERDATA);
            wCx = (WORD) (HIWORD(lParam) - iCyText);

            MoveWindow(hJulia, 0, 0,
			   LOWORD(lParam),
                           wCx,
			   TRUE);

	    MoveWindow(hTextWnd,
		       0,
		       wCx,
		       LOWORD(lParam),
                       iCyText,
		       TRUE);

            if (hThrd) {
                TerminateThread(hThrd, (DWORD)0L);
                /*
                PostMessage(hwnd, WM_COMMAND,
                    bMandel ? (DWORD)((WORD)MM_CREATE_MANDEL_THREAD) : (DWORD)((WORD)MM_CREATE_JULIA_THREAD),
                    (LONG)0L);
                  */
            }

	    break;
        }

        //
        // display info in the status window
        //
        case WM_USER+0xa: {
            PINFO       pInfo;
            static ULONG ulClick = 0;
            HWND        hTextWnd;

            ulClick++;
            if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                return 0L;
            }

            hTextWnd = pInfo->hTextWnd;
            switch (ulClick % 6) {
                case 0: sprintf( gtext,"%g <= x <= %g, %g <= y <= %g", pInfo->xFrom, pInfo->xTo, pInfo->yTo, pInfo->yFrom);
                        break;
                case 1: sprintf( gtext,"c1 = %g, c2 = %g", pInfo->c1, pInfo->c2);
                        break;
                case 2: sprintf( gtext,"Elapsed Time = %ld", (ULONG) pInfo->dwElapsed);
                        break;
                case 3: sprintf( gtext,"Iteration = %d", pInfo->iIteration);
                        break;
                case 4: sprintf( gtext,"Step = %d", pInfo->iStep);
                        break;
                case 5: (gFloat ? sprintf( gtext,"Floating point math")
                                       : sprintf( gtext,"Fix point math")) ;
                        break;

                default: break;
            }
            SetWindowText(hTextWnd, gtext);
            bReleaseInfoData(hwnd);
            return 0L;
        }

        case WM_SYSCOMMAND: {
            LONG        lResult;

            EnumChildWindows(ghwndClient, (WNDENUMPROC)lpfnSuspendThrd, lParam);

            lResult = DefMDIChildProc(hwnd, message, wParam, lParam);

            EnumChildWindows(ghwndClient, (WNDENUMPROC)lpfnResumeThrd, lParam);

            return lResult;
            break;
        }

	case WM_CREATE: {
            PINFO           pInfo;
            HANDLE          hInfo;
            HWND            hTextWnd, hJulia;
	    RECT            rcl;

            //
            // CR! MakeProcInstance is noop!
            //
            lpfnSuspendThrd = (FARPROC)MakeProcInstance (SuspendDrawThrd, ghModule);
            lpfnResumeThrd  = (FARPROC)MakeProcInstance (ResumeDrawThrd, ghModule);

            hTextWnd = CreateWindow("Text", NULL,
                                    WS_BORDER | SS_LEFT | WS_CHILD | WS_VISIBLE,
                                    0, 0, 0, 0,
                                    hwnd,
                                    (HMENU) 2,
                                    ghModule,
                                    NULL);

            GetClientRect(hwnd, &rcl);
            hJulia = CreateWindow("Julia", (LPSTR) NULL,
                          WS_CHILD      | WS_VISIBLE     |
                          WS_BORDER,
                          0,0, rcl.right-rcl.left,
                          rcl.bottom-rcl.top-GetWindowLong(hTextWnd, GWL_USERDATA),
                          hwnd, (HMENU)1, ghModule, (LPVOID)NULL);

            SetWindowText(hTextWnd, "Select the 'Draw Set' menu item to start drawing.");
            hInfo = (HANDLE) ((LPMDICREATESTRUCT) ((LPCREATESTRUCT) lParam)->lpCreateParams)->lParam ;
            if (hInfo) {
                if ((pInfo = (PINFO)LocalLock(hInfo)) == NULL) {
                    MessageBox(ghwndMain, "Failed in LocalLock, hNode", "Error", MB_OK);
                    break;
                } else {
                    HDC hDC;

                    if (!GetClientRect(hwnd, &pInfo->rcClient))
                        MessageBox(ghwndMain, "Failed in GetClientRect!", "Error", MB_OK);

                    pInfo->hTextWnd = hTextWnd;
                    pInfo->hwnd = hJulia;
                    hDC = GetDC(hJulia);
                    pInfo->hHTPal = CreateHalftonePalette(hDC);
                    ReleaseDC(hJulia, hDC);
#ifdef CYCLETHRD
                    //
                    // Creating a signal quit color cycling event
                    //
                    if ((pInfo->hQuitEvent = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL)
                        MessageBox(ghwndMain, "Failed in creating Quit Event!", "Error", MB_OK);
#endif
                    SetWindowLong(hwnd, 0, (LONG) hInfo);
                    LocalUnlock(hInfo);
                }
	    } else {
		MessageBox(ghwndMain, "Can't allocate hInfo!", "Error", MB_OK);
	    }

#if 0
            //
            // Initialize printers here will detect printers availiability
            // more often, but kind of overkill.
            //
            bInitPrinter(hwnd);
#endif
            break;
	}

        case WM_CLOSE: {
	    SendMessage(GetParent(hwnd), WM_MDISETMENU,
			    (DWORD) hMenu,
			    (LONG)  hSubMenuOne) ;
	    DrawMenuBar(GetParent(GetParent(hwnd))) ;
            break;
        }

        case WM_DESTROY: {
            PINFO            pInfo;
            if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                break;
            }

            if (pInfo->hThrd) {
                TerminateThread(pInfo->hThrd, (DWORD)0L);
                CloseHandle(pInfo->hThrd);
            }
            if (pInfo->hPrtThrd) {
                TerminateThread(pInfo->hPrtThrd, (DWORD)0L);
                CloseHandle(pInfo->hPrtThrd);
            }
            if (pInfo->hCycleThrd) {
                TerminateThread(pInfo->hCycleThrd, (DWORD)0L);
                CloseHandle(pInfo->hCycleThrd);
            }


#ifdef CYCLETHRD
            //
            // Cleanup color cycling
            //
            if (!ResetEvent(pInfo->hQuitEvent))
                MessageBox(ghwndMain, "Failed in reseting quit event!", "Error", MB_OK);
#endif
            if (pInfo->hBmpMono)
                DeleteObject(pInfo->hBmpMono);

            if (pInfo->hBmpSaved)
                DeleteObject(pInfo->hBmpSaved);

            if (pInfo->hBrush)
                DeleteObject(pInfo->hBrush);

            bReleaseInfoData(hwnd);
            LocalFree((HANDLE) GetWindowLong(hwnd, 0));
            break;
	}

	default:
	    return DefMDIChildProc(hwnd, message, wParam, lParam);

    } //switch
    return DefMDIChildProc(hwnd, message, wParam, lParam);
}

/******************************Public*Routine******************************\
*
* ViewerWndProc
*
* Effects:
*
* Warnings:
*
* History:
*  11-Dec-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

LONG APIENTRY ViewerWndProc(
    HWND hwnd,
    UINT message,
    DWORD wParam,
    LONG lParam)
{
    static FARPROC     lpfnSuspendThrd, lpfnResumeThrd;

    switch (message) {
	case WM_COMMAND: {
          PINFO       pInfo;
          HWND        hTextWnd;

	  switch (LOWORD(wParam)) {
            case MM_RLELOAD_DEMO: {
                HDC             hDC;
                char            szDirName[256];
                char            szFile[400];
                HWND            hViewSurf;

                GetCurrentDirectory(256, (LPTSTR) szDirName);

                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                if ((hViewSurf = pInfo->hwnd) == NULL)
                    return 0L;

                hTextWnd = pInfo->hTextWnd;
                sprintf( gtext,"Loading bitmap(s) into memory");
                SetWindowText(hTextWnd, gtext);

                hDC = GetDC(hViewSurf);
                strcpy(szFile, szDirName);
                strcat(szFile, "\\rsc\\julia.rle");

                if (bStoreRleFile(hDC, pInfo, szFile)) {
                    pInfo->RleData.ulFiles++;
                    PostMessage(ghwndMain, WM_COMMAND, MM_RLEPLAYCONT, 0L);
                }

                ReleaseDC(hViewSurf, hDC);

                bReleaseInfoData(hwnd);
                return 0L;

            }


            case MM_RLELOAD: {
                HDC             hDC;
                OPENFILENAME    ofn;
                char            szDirName[256];
                char            szFile[256], szFileTitle[256];
                static char     *szFilter;
                RECT            rc;
                HWND            hViewSurf;

                szFilter =
                  "DIB files (*.bmp)\0*.bmp\0RLE files (*.rle)\0*.rle\0\0";

                GetSystemDirectory((LPSTR) szDirName, 256);
                strcpy(szFile, "*.bmp\0");
                ofn.lStructSize = sizeof(OPENFILENAME);
                ofn.hwndOwner = hwnd;
                ofn.lpstrFilter = szFilter;
                ofn.lpstrCustomFilter = (LPSTR) NULL;
                ofn.nMaxCustFilter = 0L;
                ofn.nFilterIndex = 1;
                ofn.lpstrFile = szFile;
                ofn.nMaxFile = sizeof(szFile);
                ofn.lpstrFileTitle = szFileTitle;
                ofn.nMaxFileTitle = sizeof(szFileTitle);
                ofn.lpstrInitialDir = szDirName;
                ofn.lpstrTitle = (LPSTR) NULL;
                ofn.Flags = 0L;
                ofn.nFileOffset = 0;
                ofn.nFileExtension = 0;
                ofn.lpstrDefExt = "BMP";

                if (!GetOpenFileName(&ofn))
                    return 0L;

                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                if ((hViewSurf = pInfo->hwnd) == NULL)
                    return 0L;

                hTextWnd = pInfo->hTextWnd;
                sprintf( gtext,"Loading bitmap(s) into memory");
                SetWindowText(hTextWnd, gtext);

                GetClientRect(hwnd, &rc);
                hDC = GetDC(hViewSurf);
                if (bStoreRleFile(hDC, pInfo, szFile))
                    pInfo->RleData.ulFiles++;
                ReleaseDC(hViewSurf, hDC);

                bReleaseInfoData(hwnd);
                return 0L;

            }

            case MM_RLESAVE: {
                HDC             hDC;
                OPENFILENAME    ofn;
                char            szDirName[256];
                char            szFile[256], szFileTitle[256];
                static char     *szFilter;
                HWND            hViewSurf;

                szFilter = "DIB files (*.rle)\0\0";

                GetSystemDirectory((LPSTR) szDirName, 256);
                strcpy(szFile, "*.rle\0");
                ofn.lStructSize = sizeof(OPENFILENAME);
                ofn.hwndOwner = hwnd;
                ofn.lpstrFilter = szFilter;
                ofn.lpstrCustomFilter = (LPSTR) NULL;
                ofn.nMaxCustFilter = 0L;
                ofn.nFilterIndex = 0L;
                ofn.lpstrFile = szFile;
                ofn.nMaxFile = sizeof(szFile);
                ofn.lpstrFileTitle = szFileTitle;
                ofn.nMaxFileTitle = sizeof(szFileTitle);
                ofn.lpstrInitialDir = szDirName;
                ofn.lpstrTitle = "Saving Memory RLE Bitmap in File";
                ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;
                ofn.nFileOffset = 0;
                ofn.nFileExtension = 0;
                ofn.lpstrDefExt = (LPSTR)NULL;

                if (!GetSaveFileName(&ofn))
                    return 0L;

                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                if ((hViewSurf = pInfo->hwnd) == NULL)
                    return 0L;

                hTextWnd = pInfo->hTextWnd;
                sprintf( gtext,"Saving loaded bitmap(s) into one RLE file");
                SetWindowText(hTextWnd, gtext);

                hDC = GetDC(hViewSurf);
                bSaveRleFile(hDC, pInfo, szFile);
                ReleaseDC(hViewSurf, hDC);

                bReleaseInfoData(hwnd);
                return 0L;
            }

            case MM_CLEAR: {
                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                hTextWnd = pInfo->hTextWnd;
                sprintf( gtext,"Discard loaded bitmap(s) from memory");
                SetWindowText(hTextWnd, gtext);

                bFreeRleFile(pInfo);

                bReleaseInfoData(hwnd);
                return 0L;
            }

            case MM_RLEPLAY: {
                HDC     hDC;
                HWND    hViewSurf;

                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                if ((hViewSurf = pInfo->hwnd) == NULL)
                    return 0L;

                hTextWnd = pInfo->hTextWnd;
                sprintf( gtext,"Play loaded bitmap(s)");
                SetWindowText(hTextWnd, gtext);

                hDC = GetDC(hViewSurf);
                EnableMenuItem(hViewSubOne, MM_CLEAR, MF_GRAYED);

                if (pInfo->hThrd0)
                    CloseHandle(pInfo->hThrd0);

                pInfo->hThrd0 = CreateThread(NULL, 0,
                             (LPTHREAD_START_ROUTINE)bPlayRle,
                             pInfo,
                             STANDARD_RIGHTS_REQUIRED,
                             &pInfo->dwThreadId );

                if (pInfo->hThrd0) {
                    if (!SetThreadPriority(pInfo->hThrd0, THREAD_PRIORITY_BELOW_NORMAL))
                         MessageBox(ghwndMain, "Can't set Priority!",
                                    "Error", MB_OK);
                }

                //bPlayRle(hDC, pInfo);
                EnableMenuItem(hViewSubOne, MM_CLEAR, MF_ENABLED);
                ReleaseDC(hViewSurf, hDC);

                bReleaseInfoData(hwnd);
                return 0L;
            }

            case MM_RLEPLAYCONT: {
                HWND    hViewSurf;

                if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                    return 0L;
                }

                if ((hViewSurf = pInfo->hwnd) == NULL)
                    return 0L;

                hTextWnd = pInfo->hTextWnd;
                sprintf( gtext,"Play loaded bitmap(s) continuously");
                SetWindowText(hTextWnd, gtext);

                if (pInfo->bFirstTime) {
                    if (!SetEvent(pInfo->hQuitEvent)) {
                        MessageBox(ghwndMain, "Can't set Quit Event!",
                                   "Error", MB_OK);
                        return 0L;
                    }

                    EnableMenuItem(hViewSubOne, MM_CLEAR, MF_GRAYED);

                    if (pInfo->hThrd)
                        CloseHandle(pInfo->hThrd);

                    pInfo->hThrd = CreateThread(NULL, 0,
                                 (LPTHREAD_START_ROUTINE)bPlayRleCont2,
                                 pInfo,
                                 STANDARD_RIGHTS_REQUIRED,
                                 &pInfo->dwThreadId );

                    if (pInfo->hThrd) {
                        if (!SetThreadPriority(pInfo->hThrd, THREAD_PRIORITY_BELOW_NORMAL))
                             MessageBox(ghwndMain, "Can't set Priority!",
                                        "Error", MB_OK);
                    }

                    pInfo->bPlayRleCont = TRUE;
                    pInfo->bFirstTime = FALSE;
                    CheckMenuItem(hViewMenu, MM_RLEPLAYCONT, MF_CHECKED);
                } else {
                    if (pInfo->bPlayRleCont) {
                        EnableMenuItem(hViewSubOne, MM_CLEAR, MF_ENABLED);
                        CheckMenuItem(hViewMenu, MM_RLEPLAYCONT, MF_UNCHECKED);
                        pInfo->bPlayRleCont = FALSE;
                        pInfo->dwSuspend = SuspendThread(pInfo->hThrd);
                    } else {
                        EnableMenuItem(hViewSubOne, MM_CLEAR, MF_GRAYED);
                        CheckMenuItem(hViewMenu, MM_RLEPLAYCONT, MF_CHECKED);
                        pInfo->bPlayRleCont = TRUE;
                        pInfo->dwSuspend = ResumeThread(pInfo->hThrd);
                    }
                    if (pInfo->dwSuspend == -1) {
                        (pInfo->bPlayRleCont ?
                         sprintf( gtext,"Error in resuming thread\n") :
                         sprintf( gtext,"Error in suspending thread\n")  );
                        OutputDebugString( gtext );
                    }
                }

                bReleaseInfoData(hwnd);
                return 0L;
            }

            default:
               return 0L;

	  } //switch

	} // WM_COMMAND
	case WM_SETFOCUS:
	    break;

	case WM_MDIACTIVATE: {
            PINFO       pInfo;

            if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                return 0L;
            }

	    if ((HWND) lParam == hwnd) {
		SendMessage(GetParent(hwnd), WM_MDISETMENU,
			    (WPARAM)  hViewMenu,
			    (LPARAM)  NULL) ;
		DrawMenuBar(GetParent(GetParent(hwnd))) ;
	    }

            bReleaseInfoData(hwnd);
	    return 0L;
        }
        case WM_QUERYNEWPALETTE:
        case WM_PALETTECHANGED: {
            PINFO       pInfo;

            if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                return 0L;
            }

	    SendMessage(pInfo->hwnd, message, wParam, lParam);
            bReleaseInfoData(hwnd);
	    return 0L;
        }

	case WM_SIZE: {
            PINFO       pInfo;
            HWND        hTextWnd, hView;
            WORD        wCx;
            int         iCyText;

            if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                break;
            }

            hTextWnd = pInfo->hTextWnd;
            hView    = pInfo->hwnd;
            bReleaseInfoData(hwnd);
            iCyText = GetWindowLong(hTextWnd, GWL_USERDATA);
            wCx = (WORD) (HIWORD(lParam) - iCyText);

            MoveWindow(hView, 0, 0,
			   LOWORD(lParam),
                           wCx,
			   TRUE);

	    MoveWindow(hTextWnd,
		       0,
		       wCx,
		       LOWORD(lParam),
                       iCyText,
		       TRUE);

	    break;
        }

        //
        // display info in the status window
        //
        case WM_USER+0xa: {
            PINFO       pInfo;
            static ULONG ulClick = 0;
            HWND        hTextWnd;

            ulClick++;
            if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                return 0L;
            }

            hTextWnd = pInfo->hTextWnd;
            switch (ulClick % 4) {
                case 0: sprintf( gtext,"%d Frames", pInfo->RleData.ulFrames);
                        break;
                case 1: sprintf( gtext,"%d Files", pInfo->RleData.ulFiles);
                        break;
                case 2: sprintf( gtext,
                        (pInfo->bPlayRleCont ? "Continuous Play"
                                             : "Single Play"));
                        break;
                case 3: sprintf( gtext,"");
                        break;
                default: break;
            }
            SetWindowText(hTextWnd, gtext);
            bReleaseInfoData(hwnd);
            return 0L;
        }

        case WM_SYSCOMMAND: {
            LONG        lResult;

            EnumChildWindows(ghwndClient, (WNDENUMPROC)lpfnSuspendThrd, lParam);

            lResult = DefMDIChildProc(hwnd, message, wParam, lParam);

            EnumChildWindows(ghwndClient, (WNDENUMPROC)lpfnResumeThrd, lParam);

            return lResult;
            break;
        }

	case WM_CREATE: {
            PINFO           pInfo;
            HANDLE          hInfo;
            HWND            hTextWnd, hView;
	    RECT            rcl;

            //
            // CR! MakeProcInstance is noop!
            //
            lpfnSuspendThrd = (FARPROC)MakeProcInstance (SuspendDrawThrd, ghModule);
            lpfnResumeThrd  = (FARPROC)MakeProcInstance (ResumeDrawThrd, ghModule);

            hTextWnd = CreateWindow("Text", NULL,
                                    WS_BORDER | SS_LEFT | WS_CHILD | WS_VISIBLE,
                                    0, 0, 0, 0,
                                    hwnd,
                                    (HMENU) 2,
                                    ghModule,
                                    NULL);

            GetClientRect(hwnd, &rcl);
            hView = CreateWindow("View", (LPSTR) NULL,
                          WS_CHILD      | WS_VISIBLE     |
                          WS_BORDER,
                          0,0, rcl.right-rcl.left,
                          rcl.bottom-rcl.top-GetWindowLong(hTextWnd, GWL_USERDATA),
                          hwnd, (HMENU)1, ghModule, (LPVOID)NULL);

            SetWindowText(hTextWnd, "Select the 'Draw Set' menu item to start drawing.");
            hInfo = (HANDLE) ((LPMDICREATESTRUCT) ((LPCREATESTRUCT) lParam)->lpCreateParams)->lParam ;
            if (hInfo) {
                if ((pInfo = (PINFO)LocalLock(hInfo)) == NULL) {
                    MessageBox(ghwndMain, "Failed in LocalLock, hNode", "Error", MB_OK);
                    break;
                } else {
                    HDC hDC;

                    if (!GetClientRect(hwnd, &pInfo->rcClient))
                        MessageBox(ghwndMain, "Failed in GetClientRect!", "Error", MB_OK);

                    pInfo->hTextWnd = hTextWnd;
                    pInfo->hwnd = hView;

                    hDC = GetDC(hView);
                    pInfo->hHTPal = CreateHalftonePalette(hDC);
                    ReleaseDC(hView, hDC);

                    //
                    // Creating a signal quit play continuous event
                    //
                    if ((pInfo->hQuitEvent = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL)
                        MessageBox(ghwndMain, "Failed in creating Quit Event!", "Error", MB_OK);

                    SetWindowLong(hwnd, 0, (LONG) hInfo);
                    LocalUnlock(hInfo);
                }
	    } else {
		MessageBox(ghwndMain, "Can't allocate hInfo!", "Error", MB_OK);
	    }

            break;
	}

        case WM_CLOSE: {
	    SendMessage(GetParent(hwnd), WM_MDISETMENU,
			    (DWORD) hMenu,
			    (LONG)  hSubMenuOne) ;
	    DrawMenuBar(GetParent(GetParent(hwnd))) ;
            break;
        }

        case WM_DESTROY: {
            PINFO            pInfo;
            if ((pInfo = pGetInfoData(hwnd)) == NULL) {
                break;
            }

            TerminateThread(pInfo->hThrd, (DWORD)0L);
            CloseHandle(pInfo->hThrd);

            //
            // Cleanup continuous play
            //
            if (!ResetEvent(pInfo->hQuitEvent))
                MessageBox(ghwndMain, "Failed in reseting quit event!", "Error", MB_OK);

            bFreeRleFile(pInfo);
            bReleaseInfoData(hwnd);
            LocalFree((HANDLE) GetWindowLong(hwnd, 0));
            break;
	}

	default:
	    return DefMDIChildProc(hwnd, message, wParam, lParam);

    } //switch
    return DefMDIChildProc(hwnd, message, wParam, lParam);
}

/******************************Public*Routine******************************\
*
* ViewSurfWndProc
*
* Effects:
*
* Warnings:
*
* History:
*  13-Dec-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

LONG APIENTRY ViewSurfWndProc (HWND hwnd, UINT message, DWORD wParam, LONG lParam)
{
    switch (message)
    {
       case WM_CREATE: {
	    break;
       }

       case WM_DESTROY: {
            break;
       }

       case WM_QUERYNEWPALETTE: {
             HWND        hParent;
             PINFO       pInfo;
             HDC         hDC;
             UINT        i;
             HPALETTE    hOldPal;

             if ((hParent=GetParent(hwnd)) == NULL) {
                 MessageBox(ghwndMain, "Can't get hParent!", "Error", MB_OK);
                 return 0L;
             }
             if ((pInfo = pGetInfoData(hParent)) == NULL) {
                  return 0L;
             }

             // If palette realization causes a palette change,
             // we need to do a full redraw.

             hDC = GetDC (hwnd);

             hOldPal = SelectPalette (hDC,
                       pInfo->RleData.hPal,
                       0);

             i = RealizePalette(hDC);

             SelectPalette (hDC, hOldPal, 0);
             ReleaseDC (hwnd, hDC);
             bReleaseInfoData(hParent);

             if (i) {
                 InvalidateRect (hwnd, (LPRECT) (NULL), TRUE);
                 return TRUE;
             } else
                 return FALSE;

       }
       case WM_PALETTECHANGED: {
             HWND        hParent;
             PINFO       pInfo;
             HDC         hDC;
             UINT        i;
             HPALETTE    hOldPal;

             if ((hParent=GetParent(hwnd)) == NULL) {
                 MessageBox(ghwndMain, "Can't get hParent!", "Error", MB_OK);
                 return 0L;
             }
             if ((pInfo = pGetInfoData(hParent)) == NULL) {
                  return 0L;
             }

             // if we were not responsible for palette change and if
             // palette realization causes a palette change, do a redraw.

             if ((HWND)wParam != hwnd){
                hDC = GetDC (hwnd);

                hOldPal = SelectPalette (hDC,
                        pInfo->RleData.hPal,
                        0);

                i = RealizePalette (hDC);

                if (i){
                    UpdateColors (hDC);
                }
                SelectPalette (hDC, hOldPal, 0);
                ReleaseDC (hwnd, hDC);
            }
            bReleaseInfoData(hParent);
            break;
       }

       case WM_PAINT:
         {
             PAINTSTRUCT ps;
             HDC         hDC;
             RECT        rc;

             GetClientRect(hwnd,&rc);
             hDC = BeginPaint(hwnd, &ps);
             EndPaint(hwnd, &ps);
             return 0L;
         }

    } // switch
    return DefWindowProc(hwnd, message, wParam, lParam);
}

/***************************************************************************\
* About
*
* About dialog proc.
*
* History:
* 04-13-91 ????         Created.
* 09-09-91 Petrus Wong	Rewrote.
\***************************************************************************/

BOOL CALLBACK About(
    HWND hDlg,
    UINT message,
    DWORD wParam,
    LONG lParam)
{
    switch (message) {
    case WM_INITDIALOG:
        return TRUE;

    case WM_COMMAND:
        if (wParam == IDOK)
            EndDialog(hDlg, wParam);
        break;
    }

    return FALSE;

    UNREFERENCED_PARAMETER(lParam);
    UNREFERENCED_PARAMETER(hDlg);
}

/*************************************************************************
*
* TextWndProc
*
* Text Window proc.
*
* History:
* 10-07-91  Petrus Wong	Created.
*
\***************************************************************************/

LONG APIENTRY TextWndProc (HWND hwnd, UINT message, DWORD wParam, LONG lParam)
{
    static HFONT hFont = (HFONT) NULL;

    switch (message)
    {
    case WM_CREATE:
        {
	    LOGFONT    lf;
	    HDC        hDC;
	    HFONT      hOldFont;
            TEXTMETRIC tm;
	    RECT       rect;
	    LONG       lHeight;

            SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), (PVOID) &lf, FALSE);

	    hDC = GetDC(hwnd);
	    // this is the height for 8 point size font in pixels
	    lf.lfHeight = 8 * GetDeviceCaps(hDC, LOGPIXELSY) / 72;

	    hFont = CreateFontIndirect(&lf);
	    hOldFont = SelectObject(hDC, hFont);
	    GetTextMetrics(hDC, &tm);
	    GetClientRect(GetParent(hwnd), &rect);

	    // base the height of the window on size of text
	    lHeight = tm.tmHeight+6*GetSystemMetrics(SM_CYBORDER)+2;
	    // saved the height for later reference
	    SetWindowLong(hwnd, GWL_USERDATA, lHeight);
	    SetWindowPos(hwnd, NULL,
		    0,
		    rect.bottom-lHeight,
		    rect.right-rect.left,
		    lHeight,
		    SWP_NOZORDER | SWP_NOMOVE);

            ReleaseDC(hwnd, hDC);
            break;
        }

    case WM_LBUTTONDOWN: {
        PostMessage(GetParent(hwnd), WM_USER+0xa, (DWORD)0L, (LONG)0L);
        break;
    }

    case WM_DESTROY:
	    if (hFont)
		DeleteObject(hFont);
	    break;

    case WM_SETTEXT:
            DefWindowProc(hwnd, message, wParam, lParam);
            InvalidateRect(hwnd,NULL,TRUE);
            UpdateWindow(hwnd);
            return 0L;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            RECT   rc;
            char   ach[128];
            int    len, nxBorder, nyBorder;
            HFONT  hOldFont = NULL;

            BeginPaint(hwnd, &ps);

            GetClientRect(hwnd,&rc);

            nxBorder = GetSystemMetrics(SM_CXBORDER);
	    rc.left  += 9*nxBorder;
            rc.right -= 9*nxBorder;

            nyBorder = GetSystemMetrics(SM_CYBORDER);
	    rc.top    += 3*nyBorder;
	    rc.bottom -= 3*nyBorder;

	    // 3D Text
            len = GetWindowText(hwnd, ach, sizeof(ach));
	    SetBkColor(ps.hdc, GetSysColor(COLOR_BTNFACE));

	    SetBkMode(ps.hdc, TRANSPARENT);
	    SetTextColor(ps.hdc, RGB(64,96,96));
	    if (hFont)
		hOldFont = SelectObject(ps.hdc, hFont);
	    ExtTextOut(ps.hdc, rc.left+2*nxBorder+2, rc.top+2, ETO_OPAQUE | ETO_CLIPPED,
	                &rc, ach, len, NULL);

	    SetTextColor(ps.hdc, RGB(128,128,128));
	    if (hFont)
		hOldFont = SelectObject(ps.hdc, hFont);
	    ExtTextOut(ps.hdc, rc.left+2*nxBorder+1, rc.top+1, ETO_CLIPPED,
			&rc, ach, len, NULL);

	    SetTextColor(ps.hdc, RGB(255,255,255));
	    if (hFont)
		hOldFont = SelectObject(ps.hdc, hFont);
	    ExtTextOut(ps.hdc, rc.left+2*nxBorder, rc.top, ETO_CLIPPED,
			&rc, ach, len, NULL);

	    SetBkMode(ps.hdc, OPAQUE);

	    if (hOldFont)
		SelectObject(ps.hdc, hOldFont);

            EndPaint(hwnd, &ps);
            return 0L;
        }
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}


/**************************************************************************\
*
* JuliaWndProc
*
* History:
*  14-Jun-1992 -by- Petrus Wong     removed pens except red & black
*  22-Nov-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

LONG APIENTRY JuliaWndProc (HWND hwnd, UINT message, DWORD wParam, LONG lParam)
{
    //
    // These statics are shared by all the Julia windows.  But, this is
    // fine because only one Julia window is tracking at any one time.
    // Ideally, we should place this in the per-window INFO data structure.
    // But, this is not necessary.
    //
    static BOOL    bTrack = FALSE;
    static int     OrgX, OrgY;
    static int     PrevX, PrevY;
    static HDC     hDC;
    static HCURSOR hCurArrow, hCurPaintCan;

    switch (message)
    {
       case WM_CREATE: {

            hpnRed     = CreatePen(PS_SOLID, 0, RGB(0xFF, 0,    0));
            hpnGreen   = CreatePen(PS_SOLID, 0, RGB(0,    0xFF, 0));
            hpnBlack   = CreatePen(PS_SOLID, 0, RGB(0,    0,    0));

            hCurPaintCan = LoadCursor(ghModule, MAKEINTRESOURCE(PAINTCURSOR));
            hCurArrow = LoadCursor(NULL, IDC_ARROW);
	    break;
       }

       case WM_DESTROY: {
            HWND        hParent;
            PINFO       pInfo;

            DeleteObject(hpnRed);
            DeleteObject(hpnGreen);
            DeleteObject(hpnBlack);

            if ((hParent=GetParent(hwnd)) == NULL) {
                MessageBox(ghwndMain, "Can't get hParent!", "Error", MB_OK);
                break;
            }
            if ((pInfo = pGetInfoData(hParent)) == NULL) {
                break;
            }

            // we might have open resource if we ever load bitmap in this wnd.
            bFreeRleFile(pInfo);
            bReleaseInfoData(hParent);
            break;
       }

       case WM_QUERYNEWPALETTE: {
             HWND        hParent;
             PINFO       pInfo;
             HDC         hDC;
             UINT        i;
             HPALETTE    hOldPal;

             if ((hParent=GetParent(hwnd)) == NULL) {
                 MessageBox(ghwndMain, "Can't get hParent!", "Error", MB_OK);
                 return 0L;
             }
             if ((pInfo = pGetInfoData(hParent)) == NULL) {
                  return 0L;
             }

             // If palette realization causes a palette change,
             // we need to do a full redraw.

             hDC = GetDC (hwnd);

             hOldPal = SelectPalette (hDC,
                       ((pInfo->iStretchMode == HALFTONE) ? pInfo->hHTPal : pInfo->hPal),
                       0);

             i = RealizePalette(hDC);
             SelectPalette (hDC, hOldPal, 0);
             ReleaseDC (hwnd, hDC);
             bReleaseInfoData(hParent);

             if (i) {
                 InvalidateRect (hwnd, (LPRECT) (NULL), TRUE);
                 return TRUE;
             } else
                 return FALSE;

       }
       case WM_PALETTECHANGED: {
             HWND        hParent;
             PINFO       pInfo;
             HDC         hDC;
             UINT        i;
             HPALETTE    hOldPal;

             if ((hParent=GetParent(hwnd)) == NULL) {
                 MessageBox(ghwndMain, "Can't get hParent!", "Error", MB_OK);
                 return 0L;
             }
             if ((pInfo = pGetInfoData(hParent)) == NULL) {
                  return 0L;
             }

             // if we were not responsible for palette change and if
             // palette realization causes a palette change, do a redraw.

             if ((HWND)wParam != hwnd){
                hDC = GetDC (hwnd);

                hOldPal = SelectPalette (hDC,
                       ((pInfo->iStretchMode == HALFTONE) ? pInfo->hHTPal : pInfo->hPal),
                        0);

                i = RealizePalette (hDC);

                if (i){
                    UpdateColors (hDC);
                }
                SelectPalette (hDC, hOldPal, 0);
                ReleaseDC (hwnd, hDC);
            }
            bReleaseInfoData(hParent);
            break;
       }

       case WM_PAINT:
         {
             PAINTSTRUCT ps;
             HWND        hParent;
             PINFO       pInfo;
             HDC         hDC;
             RECT        rc;

             GetClientRect(hwnd,&rc);
             hDC = BeginPaint(hwnd, &ps);
             EndPaint(hwnd, &ps);
             if ((hParent=GetParent(hwnd)) == NULL) {
                 MessageBox(ghwndMain, "Can't get hParent!", "Error", MB_OK);
                 return 0L;
             }
             if ((pInfo = pGetInfoData(hParent)) == NULL) {
                  return 0L;
                }

             if (pInfo->hBmpSaved) {
                hDC = GetDC(hwnd);
                bDrawDIB(hDC, pInfo, 0, 0, rc.right, rc.bottom);
                ReleaseDC(hwnd, hDC);
             }
             bReleaseInfoData(hParent);

             //EndPaint(hwnd, &ps);
             return 0L;
         }

       case WM_RBUTTONDOWN: {
         RECT   rc;
	 HANDLE hParent;
	 PINFO	pInfo;
         int    x, y;
         HWND   hJulia;
         HANDLE hTextWnd;

         x = (int) LOWORD(lParam);
         y = (int) HIWORD(lParam);
         if ((hParent=GetParent(hwnd)) == NULL) {
             MessageBox(ghwndMain, "Can't get hParent!", "Error", MB_OK);
             break;
         }

         if ((pInfo = pGetInfoData(hParent)) == NULL) {
             break;
         }

         hTextWnd = pInfo->hTextWnd;

         if (GetWindowLong(GetParent(hwnd), GWL_STYLE) & WS_MAXIMIZE) {
             GetClientRect(ghwndMain, &rc);
             rc.bottom -= GetWindowLong(hTextWnd,GWL_USERDATA);
         } else {
             GetClientRect(hwnd, &rc);
         }

         if (pInfo->bMandel) {

             hJulia = (HWND) SendMessage(ghwndMain, WM_COMMAND,
                                         (DWORD)((WORD)MM_JULIA), 0L);
             if (hJulia) {
                 //GetClientRect(hwnd, &rc);
                 //
                 // calculate the c value corresponding to the point
                 //
                 c1 = Xform((double) x, 0.0, (double) rc.right, pInfo->xFrom, pInfo->xTo);
                 c2 = Xform((double) y, 0.0, (double) rc.bottom, pInfo->yFrom, pInfo->yTo);
                 lc1 = XformFix(x, 0, rc.right, pInfo->lxFrom, pInfo->lxTo);
                 lc2 = XformFix(y, 0, rc.bottom, pInfo->lyFrom, pInfo->lyTo);

                sprintf( gtext,"(c1 = %g, c2 = %g)\n", c1, c2);
                OutputDebugString( gtext );

                 //
                 // Reset globals to default values for creating Julia set
                 // (Entire set, not zoom in)
                 //
                 bResetGlobal();
                 PostMessage(hJulia, WM_COMMAND, (DWORD)((WORD) MM_SET_XFORM_ATTR), 0L);
             } else {
                 MessageBox(ghwndMain, "Can't create hJulia!", "Error", MB_OK);
             }
         }
         bReleaseInfoData(hParent);
         break;
       }
       case WM_LBUTTONDOWN: {
	 HANDLE hParent;
	 PINFO	pInfo;
         int    x, y;
         DWORD  dwRGB;
         HBRUSH hBrOld;

         x = (int) LOWORD(lParam);
         y = (int) HIWORD(lParam);
         if ((hParent=GetParent(hwnd)) == NULL) {
             MessageBox(ghwndMain, "Can't get hParent!", "Error", MB_OK);
             break;
         }

         if ((pInfo = pGetInfoData(hParent)) == NULL) {
             break;
         }

         if (pInfo->bFill) {
            hDC = GetDC(hwnd);
            hBrOld = SelectObject(hDC, pInfo->hBrush);

            dwRGB = GetPixel(hDC, x, y);

            ExtFloodFill(hDC, x, y, (COLORREF)dwRGB, FLOODFILLSURFACE);
            SelectObject(hDC, hBrOld);
            ReleaseDC(hwnd, hDC);
            pInfo->bFill = FALSE;

            if (pInfo->hBmpSaved)
                DeleteObject(pInfo->hBmpSaved);

            pInfo->hBmpSaved = SaveBitmap(hwnd, pInfo->hPal);
            pInfo->bUseDIB = FALSE;


            SetCursor(hCurArrow);
         } else {
            bTrack = TRUE;
            OrgX = PrevX = x = LOWORD(lParam);
            OrgY = PrevY = y = HIWORD(lParam);

            hDC = GetDC(hwnd);
            SetCapture(hwnd);
         }
         bReleaseInfoData(hParent);
         break;
       }
       case WM_MOUSEMOVE: {
         RECT rectClient;
         int NextX;
         int NextY;
	 HANDLE hParent;
	 PINFO	pInfo;

         if ((hParent=GetParent(hwnd)) == NULL) {
             MessageBox(ghwndMain, "Can't get hParent!", "Error", MB_OK);
             break;
         }

         if ((pInfo = pGetInfoData(hParent)) == NULL) {
             break;
         }

         if (pInfo->bFill) {
                 SetCursor(hCurPaintCan);
         } else {
                 SetCursor(hCurArrow);
         }

         bReleaseInfoData(hParent);

         // Update the selection region
         if (bTrack) {
             NextX = (SHORT) LOWORD(lParam);
             NextY = (SHORT) HIWORD(lParam);

             // Do not draw outside the window's client area

             GetClientRect (hwnd, &rectClient);
             if (NextX < rectClient.left) {
                 NextX = rectClient.left;
             } else if (NextX >= rectClient.right) {
                 NextX = rectClient.right - 1;
             }
             if (NextY < rectClient.top) {
                 NextY = rectClient.top;
             } else if (NextY >= rectClient.bottom) {
                 NextY = rectClient.bottom - 1;
             }
             if ((NextX != PrevX) || (NextY != PrevY)) {
                SetROP2(hDC, R2_NOT);           // Erases the previous box

                MoveToEx(hDC, OrgX, OrgY, NULL);
                LineTo(hDC, OrgX, PrevY);
                LineTo(hDC, PrevX, PrevY);
                LineTo(hDC, PrevX, OrgY);
                LineTo(hDC, OrgX, OrgY);
             // Get the current mouse position

                PrevX = NextX;
                PrevY = NextY;
                MoveToEx(hDC, OrgX, OrgY, NULL);   // Draws the new box
                LineTo(hDC, OrgX, PrevY);
                LineTo(hDC, PrevX, PrevY);
                LineTo(hDC, PrevX, OrgY);
                LineTo(hDC, OrgX, OrgY);
             }
         }
         break;

       }

       case WM_LBUTTONUP: {
	 RECT rc;
	 HANDLE hParent;
	 PINFO	pInfo;
         int NextX;
         int NextY;
         int iDistX, iDistY, iAbsDstX, iAbsDstY;
         HWND hZoom;
         HANDLE hTextWnd;

         if (!bTrack)
            break;

         // End the selection
            ReleaseCapture();
            bTrack = FALSE;

            MoveToEx(hDC, OrgX, OrgY, NULL);   // Erases the box
            LineTo(hDC, OrgX, PrevY);
            LineTo(hDC, PrevX, PrevY);
            LineTo(hDC, PrevX, OrgY);
            LineTo(hDC, OrgX, OrgY);

            NextX = LOWORD(lParam);
            NextY = HIWORD(lParam);

            iDistX = NextX - OrgX;
            iDistY = NextY - OrgY;
            iAbsDstX = (iDistX > 0 ? iDistX : -iDistX);
            iAbsDstY = (iDistY > 0 ? iDistY : -iDistY);
            if (iAbsDstX > iAbsDstY) {
                NextY = OrgY + (iDistY > 0 ? iAbsDstX : -iAbsDstX);
            } else if (iAbsDstX < iAbsDstY) {
                        NextX = OrgX + (iDistX > 0 ? iAbsDstY : -iAbsDstY);
            }

            MoveToEx(hDC, OrgX, OrgY, NULL);   // Draws the new box
            LineTo(hDC, OrgX, NextY);
            LineTo(hDC, NextX, NextY);
            LineTo(hDC, NextX, OrgY);
            LineTo(hDC, OrgX, OrgY);

            SetROP2(hDC, R2_COPYPEN);

            ReleaseDC(hwnd, hDC);
            if ((hParent=GetParent(hwnd)) == NULL) {
                MessageBox(ghwndMain, "Can't get hParent!", "Error", MB_OK);
                break;
            }

            if ((pInfo = pGetInfoData(hParent)) == NULL) {
                break;
            }

            hTextWnd = pInfo->hTextWnd;
            sprintf(gtext, "Mouse (%d, %d), (%d, %d)\n", OrgX, OrgY, NextX, NextY);
            //SetWindowText(hTextWnd, gtext);
            OutputDebugString(gtext);


            if (GetWindowLong(GetParent(hwnd), GWL_STYLE) & WS_MAXIMIZE) {

                GetClientRect(ghwndMain, &rc);
                rc.bottom -= GetWindowLong(hTextWnd,GWL_USERDATA);
                sprintf(gtext, "(%d, %d), (%d, %d)\n", rc.left, rc.top, rc.right, rc.bottom);
                //SetWindowText(hTextWnd, gtext);
                OutputDebugString(gtext);

            } else {
                GetClientRect(hwnd, &rc);
            }

            if ((OrgX == NextX) && (OrgY == NextY)) {
                bReleaseInfoData(hParent);
                break;
            }

            hZoom = (HWND) SendMessage(ghwndMain, WM_COMMAND,
                       pInfo->bMandel ? (DWORD)((WORD)MM_MANDEL) : (DWORD)((WORD)MM_JULIA),
                       0L);
            if (hZoom) {
                //GetClientRect(hwnd, &rc);
                xFrom = Xform((double) OrgX, 0.0, (double) rc.right, pInfo->xFrom, pInfo->xTo);
                xTo   = Xform((double) NextX, 0.0, (double) rc.right, pInfo->xFrom, pInfo->xTo);
                yFrom = Xform((double) OrgY, 0.0, (double) rc.bottom, pInfo->yFrom, pInfo->yTo);
                yTo   = Xform((double) NextY, 0.0, (double) rc.bottom, pInfo->yFrom, pInfo->yTo);
                lxFrom = XformFix(OrgX, 0, rc.right, pInfo->lxFrom, pInfo->lxTo);
                lxTo   = XformFix(NextX, 0, rc.right, pInfo->lxFrom, pInfo->lxTo);
                lyFrom = XformFix(OrgY, 0, rc.bottom, pInfo->lyFrom, pInfo->lyTo);
                lyTo   = XformFix(NextY, 0, rc.bottom, pInfo->lyFrom, pInfo->lyTo);
                if (!pInfo->bMandel) {
                    c1 = pInfo->c1;
                    c2 = pInfo->c2;
                    lc1 = pInfo->lc1;
                    lc2 = pInfo->lc2;
                }
                PostMessage(hZoom, WM_COMMAND, (DWORD)((WORD) MM_SET_XFORM_ATTR), 0L);
            } else {
                MessageBox(ghwndMain, "Can't create hZoom!", "Error", MB_OK);
            }
         bReleaseInfoData(hParent);
         break;
       } // case WM_LBUTTONUP
    } // switch
    return DefWindowProc(hwnd, message, wParam, lParam);
}


/******************************Public*Routine******************************\
*
* SuspendDrawThrd
*
* Effects: Enumerates all the MDI children.  Suspending the drawing thread
*          in those windows, if any.
*
* Warnings: assumes the MDI children has class name "ChildClass."
*
* History:
*  09-Dec-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/


BOOL APIENTRY SuspendDrawThrd (HWND hwnd, LONG lParam) {
   HANDLE      hThrd;
   PINFO       pInfo;
   DWORD       dwSuspend;
   BOOL        bDrawing;
   char        sz[30];

   GetClassName(hwnd, sz, 15);
   if (strcmp(sz, "ChildClass") != 0)
        return 1L;
       if ((pInfo = pGetInfoData(hwnd)) == NULL) {
           return 1L;
       }

       bDrawing = pInfo->bDrawing;
       hThrd    = pInfo->hThrd;
       bReleaseInfoData(hwnd);

       if (hThrd && bDrawing) {
           dwSuspend = SuspendThread(hThrd);
           sprintf( gtext,"\nSuspend: dwSuspend = %d, dwSuspend = %g\n", dwSuspend, dwSuspend);
           OutputDebugString( gtext );

           if (dwSuspend == -1) {
               sprintf( gtext,"Error in suspending thread\n");
               OutputDebugString( gtext );
           }
       }
   return 1L;
   UNREFERENCED_PARAMETER(lParam);
}


/******************************Public*Routine******************************\
*
* ResumeDrawThrd
*
* Effects: Enumerates all the MDI children.  Resuming the drawing thread
*          in those windows, if any.
*
* Warnings: Assumes the MDI children has class name "ChildClass." Also,
*           assumes the drawing has been suspended by SuspendDrawThrd.
*
* History:
*  09-Dec-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/


BOOL APIENTRY ResumeDrawThrd  (HWND hwnd, LONG lParam) {
   HANDLE      hThrd;
   PINFO       pInfo;
   DWORD       dwSuspend;
   BOOL        bDrawing;
   char        sz[30];

   GetClassName(hwnd, sz, 15);
   if (strcmp(sz, "ChildClass") != 0)
        return 1L;
      if ((pInfo = pGetInfoData(hwnd)) == NULL) {
          return 1L;
      }

      bDrawing = pInfo->bDrawing;
      hThrd    = pInfo->hThrd;
      bReleaseInfoData(hwnd);

      if (hThrd && bDrawing) {
          dwSuspend = ResumeThread(hThrd);
          sprintf( gtext,"Resume: dwSuspend = %d, dwSuspend = %g\n", dwSuspend, dwSuspend);
          OutputDebugString( gtext );

          if (dwSuspend == -1) {
              sprintf( gtext,"Error in resuming thread\n");
              OutputDebugString( gtext );
          }
      }
   return 1L;
   UNREFERENCED_PARAMETER(lParam);
}


/******************************Public*Routine******************************\
*
* lMul
*
* Effects: Fix point multiplication
*
* Warnings: 9.23 fix point representation used. This is only good for
*           multiplication in a limited range.  Will overflow.
*           CR! Will be implemented as macros in the future
*
* History:
*  16-Feb-1993      Petrus Wong     9.23 fix i LARGE_INTEGER support
*  20-Nov-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

LONG lMul(LONG l1, LONG l2)
{
    LARGE_INTEGER   liTmp;

    liTmp = EnlargedIntegerMultiply(l1, l2);
    liTmp = LargeIntegerShiftRight(liTmp, 23);
    return(liTmp.LowPart);
}

/******************************Public*Routine******************************\
*
* lDiv
*
* Effects: fix point division
*
* Warnings: 9.23 fix point representation used. This is only good for
*           division in a limited range.  Will overflow.
*           CR! Will be implemented as macros in the future
*
* History:
*  16-Feb-1993      Petrus Wong     9.23 fix i LARGE_INTEGER support
*  20-Nov-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

LONG lDiv(long l1, long l2)
{
    LARGE_INTEGER   liTmp, liTmp2;


    liTmp  = ConvertLongToLargeInteger(l1);
    liTmp2 = ConvertLongToLargeInteger(l2);
    liTmp  = LargeIntegerShiftLeft(liTmp, 23);
    liTmp  = LargeIntegerDivide(liTmp, liTmp2, NULL);
    return (liTmp.LowPart);
}


/**************************************************************************\
*
* StartDrawFix
*					     2
* Effects: Draw'g the Julia Set for Q (z) = z  + c, where z, c are complex
*				     c
*	   Fact:   |Q (0)| = |c| > 2, the orbit of 0 escapes immediately
*		     c
*	   claim:  |z| >= |c| = 2 + l, l > 0 escapes under Q
*							    c
*	   The Julia Set: Basin Boundaries algorithm.
*
* Warnings:
*
* History:
*  16-Feb-1993      Petrus Wong     9.23 fix
*  11-Feb-1993      Petrus Wong     Minimize device access
*  14-Dec-1992 -by- Petrus Wong     Enable shadow bitmap
*  14-Jun-1992 -by- Petrus Wong     Modified to use the pen array
*  20-Nov-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/
BOOL StartDrawFix(PINFO pInfo)
{
   DWORD       dwTick1;
   HDC         hDC;
   RECT        rc;

   int m, n, i, iPrev;
   int xCurr, yCurr;
   int iPen;

   LONG c1, c2;
   LONG x0, y0, x1, y1, x, y, z;

   int  iBand=1;
   int  iScan=0;
   int  iDiff=0;

#ifndef THRDONE
   INT      iNumClr;
#endif

   HDC          hDCMem;
   HBITMAP      hBitmap, hOldBitmap;

   iPen = pInfo->iPen + 1;
   iPrev = FIRST_PIXEL;	   // a big value to signal the first pixel
   //iPrev = pInfo->iIteration + FIRST_PIXEL;
   c1 = pInfo->lc1;
   c2 = pInfo->lc2;

   pInfo->bMandel = FALSE;
   pInfo->bDrawing = TRUE;
   hDC = GetDC(pInfo->hwnd);

#ifndef THRDONE
    if ((iNumClr = iCreatePenFrPal(hDC, NULL, 0, &(pInfo->hPal))) != 0) {
        sprintf( gtext,"iNumClr = %d\n", iNumClr);
        OutputDebugString( gtext);

        if ((pInfo->prghPen = (PVOID*) GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(HPEN)*iNumClr)) == NULL) {
            MessageBox(ghwndMain, "Failed in Memory Allocation for pInfo->prghPen!", "Error", MB_OK);
        } else {
            if ((pInfo->iPen = iCreatePenFrPal(hDC, pInfo->prghPen, 0, &(pInfo->hPal))) == 0)
                MessageBox(ghwndMain, "Failed in creating pen!", "Error", MB_OK);
        }
    }

   hDCMem = CreateCompatibleDC(hDC);

   SelectPalette(hDCMem, pInfo->hPal, FALSE);
   SelectPalette(hDC,    pInfo->hPal, FALSE);
   RealizePalette(hDC);
   iPen = pInfo->iPen + 1;
#endif

   GetClientRect(pInfo->hwnd, &rc);
   hBitmap = CreateCompatibleBitmap(hDC, (int)rc.right, (int)rc.bottom);
   hOldBitmap = SelectObject(hDCMem, hBitmap);
   yCurr = rc.top;

   dwTick1 = GetTickCount();
   iScan = (((iScan = BATCH / rc.right) == 0) ? 1 : iScan);

   for (n=rc.top; n<=rc.bottom; n+=pInfo->iStep, yCurr+=pInfo->iStep, iPrev = FIRST_PIXEL) {
       xCurr = rc.left;		      // since LineTo excludes last point
       MoveToEx(hDCMem, 0, yCurr, NULL);
       y0 = XformFix(n, rc.top, rc.bottom, pInfo->lyFrom, pInfo->lyTo);

       for (m=rc.left; m<=rc.right; m++, xCurr++) {
	   x0 = XformFix(m, rc.left, rc.right, pInfo->lxFrom, pInfo->lxTo);
           x = x0;
           y = y0;

           for (i=1; i<=pInfo->iIteration; i++) {
               x1 = lMul(x - y, x + y) + c1;    // Z = x1 + i x2

	       y1 = (lMul(x, y) * 2) + c2;
               x = x1;
	       y = y1;				//    2       2	    2 1/2     2
	       z = lMul(x, x) + lMul(y, y);	// |Z|	= ((x1	+ x2 )	 ) > 2

	       if (z > 33554432)
		    break;
           }

	   if (i != iPrev)
	   {
	      if (iPrev != FIRST_PIXEL)
	      {
		switch(iPrev)
		{
                   case 1:
                           SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[0]);
                           break;
                   default:

                       if (iPrev >= pInfo->iIteration) {
                          SelectObject(hDCMem, hpnBlack);
                          break;
                       }
                       SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[iPrev % iPen]);
                       break;
		}
		iPrev = i;
                LineTo(hDCMem,xCurr,yCurr);
	      }
	      else
		 iPrev = i;	// remember the color for the first pixel
	   }
       }
       switch(i) {
          case 1:
                  SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[0]);
                  break;
          default:
              if (iPrev >= pInfo->iIteration) {
                 SelectObject(hDCMem, hpnBlack);
                 break;
              }
              SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[i % iPen]);
              break;
       }

       LineTo(hDCMem,xCurr,yCurr);

       sprintf(gtext, "Drawing: %3.2f %%", ((FLOAT)n/(FLOAT)rc.bottom)*100);
       SetWindowText(pInfo->hTextWnd, gtext);

       if (n >= iScan * iBand) {
            BitBlt(hDC, 0, iScan * (iBand-1), rc.right, iScan, hDCMem, 0, iScan * (iBand-1), SRCCOPY);
            iBand++;
       }
   }

   if ((iDiff = iScan * iBand - rc.bottom) != 0) {
        BitBlt(hDC, 0, iScan * (iBand-1), rc.right, iScan-iDiff, hDCMem, 0, iScan * (iBand-1), SRCCOPY);
   }

   sprintf(gtext, "Drawing: DONE");
   SetWindowText(pInfo->hTextWnd, gtext);

   ReleaseDC(pInfo->hwnd, hDC);

   pInfo->dwElapsed = GetTickCount() - dwTick1;

   if (pInfo->hBmpSaved)
       DeleteObject(pInfo->hBmpSaved);

   pInfo->hBmpSaved = SelectObject(hDCMem, hOldBitmap);
   pInfo->bDrawing = FALSE;
#ifndef THRDONE
   if (pInfo->prghPen != NULL) {
       for (i = 0; i <= pInfo->iPen; i++) {
           DeleteObject((HPEN) (pInfo->prghPen)[i]);
       }
       GlobalFree(pInfo->prghPen);
   }
#endif
   DeleteDC(hDCMem);
   ExitThread(0);
   return TRUE;
}

/**************************************************************************\
*
* StartDraw
*					     2
* Effects: Draw'g the Julia Set for Q (z) = z  + c, where z, c are complex
*				     c
*	   Fact:   |Q (0)| = |c| > 2, the orbit of 0 escapes immediately
*		     c
*	   claim:  |z| >= |c| = 2 + l, l > 0 escapes under Q
*							    c
*	   The Julia Set: Basin Boundaries algorithm.
*
* Warnings:
*
* History:
*  11-Feb-1993      Petrus Wong     Minimize device access
*  14-Dec-1992 -by- Petrus Wong     Enable shadow bitmap
*  14-Jun-1992 -by- Petrus Wong     Modified to use the pen array and LineTo
*  20-Nov-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/
BOOL StartDraw(PINFO pInfo)
{
   DWORD       dwTick1;
   HDC         hDC;
   RECT        rc;

   int m, n, i, iPrev;
   int xCurr, yCurr;
   int iPen;

   double c1, c2;
   double x0, y0, x1, y1, x, y, z;

   int  iBand=1;
   int  iScan=0;
   int  iDiff=0;


   HDC          hDCMem;
   HBITMAP      hBitmap, hOldBitmap;

#ifndef THRDONE
   INT      iNumClr;
#endif

   iPen = pInfo->iPen + 1;
   iPrev = FIRST_PIXEL;	   // a big value to signal the first pixel
   //iPrev = pInfo->iIteration + FIRST_PIXEL;
   c1 = pInfo->c1;
   c2 = pInfo->c2;

   pInfo->bMandel = FALSE;
   pInfo->bDrawing = TRUE;
   hDC = GetDC(pInfo->hwnd);

#ifndef THRDONE
    if ((iNumClr = iCreatePenFrPal(hDC, NULL, 0, &(pInfo->hPal))) != 0) {
        sprintf( gtext,"iNumClr = %d\n", iNumClr);
        OutputDebugString( gtext);

        if ((pInfo->prghPen = (PVOID*) GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(HPEN)*iNumClr)) == NULL) {
            MessageBox(ghwndMain, "Failed in Memory Allocation for pInfo->prghPen!", "Error", MB_OK);
        } else {
            if ((pInfo->iPen = iCreatePenFrPal(hDC, pInfo->prghPen, 0, &(pInfo->hPal))) == 0)
                MessageBox(ghwndMain, "Failed in creating pen!", "Error", MB_OK);
        }
    }

   hDCMem = CreateCompatibleDC(hDC);

   SelectPalette(hDCMem, pInfo->hPal, FALSE);
   SelectPalette(hDC,    pInfo->hPal, FALSE);
   RealizePalette(hDC);
   iPen = pInfo->iPen + 1;
#endif

   GetClientRect(pInfo->hwnd, &rc);
   hBitmap = CreateCompatibleBitmap(hDC, (int)rc.right, (int)rc.bottom);
   hOldBitmap = SelectObject(hDCMem, hBitmap);
   yCurr = rc.top;

   dwTick1 = GetTickCount();

   iScan = (((iScan = BATCH / rc.right) == 0) ? 1 : iScan);

   for (n=rc.top; n<=rc.bottom; n+=pInfo->iStep, yCurr+=pInfo->iStep, iPrev = FIRST_PIXEL) {
       xCurr = rc.left;		      // since LineTo excludes last point
       MoveToEx(hDCMem, 0, yCurr, NULL);
       y0 = Xform((double) n, 0.0, (double) rc.bottom, pInfo->yFrom, pInfo->yTo);

       for (m=rc.left; m<=rc.right; m++, xCurr++) {
           x0 = Xform((double) m, 0.0, (double) rc.right, pInfo->xFrom, pInfo->xTo);
           x = x0;
           y = y0;

           for (i=1; i<=pInfo->iIteration; i++) {
               x1 = (x - y) * (x + y) + c1;     // Z = x1 + i x2

               y1 = 2 * x * y + c2;
               x = x1;
	       y = y1;				//    2       2	    2 1/2     2
	       z = x * x + y * y;	        // |Z|	= ((x1	+ x2 )	 ) > 2

	       if (z > 4.0)
		    break;
           }

	   if (i != iPrev)
	   {
	      if (iPrev != FIRST_PIXEL)
	      {
		switch(iPrev)
		{
                   case 1:
                           SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[0]);
                           break;
                   default:

                       if (iPrev >= pInfo->iIteration) {
                          SelectObject(hDCMem, hpnBlack);
                          break;
                       }
                       SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[iPrev % iPen]);
                       break;
		}
		iPrev = i;
                LineTo(hDCMem,xCurr,yCurr);
	      }
	      else
		 iPrev = i;	// remember the color for the first pixel
	   }
       }
       switch(i) {
          case 1:
                  SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[0]);
                  break;
          default:
              if (iPrev >= pInfo->iIteration) {
                 SelectObject(hDCMem, hpnBlack);
                 break;
              }
              SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[i % iPen]);
              break;
       }

       LineTo(hDCMem,xCurr,yCurr);

       sprintf(gtext, "Drawing: %3.2f %%", ((FLOAT)n/(FLOAT)rc.bottom)*100);
       SetWindowText(pInfo->hTextWnd, gtext);

       if (n >= iScan * iBand) {
            BitBlt(hDC, 0, iScan * (iBand-1), rc.right, iScan, hDCMem, 0, iScan * (iBand-1), SRCCOPY);
            iBand++;
       }
   }

   if ((iDiff = iScan * iBand - rc.bottom) != 0) {
        BitBlt(hDC, 0, iScan * (iBand-1), rc.right, iScan-iDiff, hDCMem, 0, iScan * (iBand-1), SRCCOPY);
   }

   sprintf(gtext, "Drawing: DONE");
   SetWindowText(pInfo->hTextWnd, gtext);

   ReleaseDC(pInfo->hwnd, hDC);

   pInfo->dwElapsed = GetTickCount() - dwTick1;

   if (pInfo->hBmpSaved)
       DeleteObject(pInfo->hBmpSaved);

   pInfo->hBmpSaved = SelectObject(hDCMem, hOldBitmap);
   pInfo->bDrawing = FALSE;
#ifndef THRDONE
   if (pInfo->prghPen != NULL) {
       for (i = 0; i <= pInfo->iPen; i++) {
           DeleteObject((HPEN) (pInfo->prghPen)[i]);
       }
       GlobalFree(pInfo->prghPen);
   }
#endif
   DeleteDC(hDCMem);

   ExitThread(0);
   return TRUE;
}

/**************************************************************************\
*
* StartDraw2
*					     2
* Effects: Draw'g the Julia Set for Q (z) = z  + c, where z, c are complex
*
*	   The Julia Set: Backward iteration algorithm.
*
* Warnings:
*
* History:
*  20-Nov-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL StartDraw2(PINFO pInfo)
{
   HDC         hDC;
   RECT        rc;
   int	       m, n, i;
   double      c1, c2;
   double      x0, y0, w0, w1, pi, theta, r;

   pi = 22.0/7.0;
   c1 = 0.360284;
   c2 = 0.100376;
   x0 = 1.5;
   y0 = 1.5;

   hDC = GetDC(pInfo->hwnd);

   GetClientRect(pInfo->hwnd, &rc);
   for (i=0; i<=15000; i++) {
    w0 = x0 - c1;
    w1 = y0 - c2;
    sprintf( gtext,"w(%g, %g) xy(%g, %g)\n", w0, w1, x0, y0);
    OutputDebugString( gtext );

    if (w0 == 0.0) {
	theta = pi/2;
        sprintf( gtext,"(w0 == 0.0) theta = %g\n\n", theta);
        OutputDebugString( gtext );

    } else {
	if (w0 > 0.0) {
	    theta = atan(w1/w0);
            sprintf( gtext,"(w0 > 0.0) theta = %g\n\n", theta);
            OutputDebugString( gtext );

	} else { // w0 < 0.0
	    theta = pi + atan(w1/w0);
            sprintf( gtext,"(w0 < 0.0) theta = %g\n\n", theta);
            OutputDebugString( gtext );
	}
    }
    r = sqrt(w0 * w0 + w1 + w1);
    //theta = theta/2.0 + ((int) ((2.0*rand()/(RAND_MAX+1.0)))*pi);
    //theta = theta/2.0 + ((2.0*rand()/((double)RAND_MAX+1.0))*pi);
    r = sqrt(r);
    x0 = r*cos(theta);
    y0 = r*sin(theta);
    if (i > 50) {
	m = Xform2(x0, pInfo->xFrom, pInfo->xTo, 0.0, (double) rc.right);
	n = Xform2(y0, pInfo->yFrom, pInfo->yTo, 0.0, (double) rc.bottom);
	SetPixel(hDC, m, n, 0x000000ff);
    }
   }

   ReleaseDC(pInfo->hwnd, hDC);

   ExitThread(0);
   return TRUE;
}

/**************************************************************************\
*
* StartMandelbrotFix
*					     2
* Effects: Draw'g the Mandelbrot Set for Q (z) = z  + c, where z, c complex
*
* Warnings:
*
* History:
*  16-Feb-1993      Petrus Wong     9.23 fix
*  11-Feb-1993      Petrus Wong     Minimize device access
*  14-Dec-1992 -by- Petrus Wong     Enable shadow bitmap
*  14-Jun-1992 -by- Petrus Wong     Modified to use the pen array
*  20-Nov-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/
BOOL StartMandelbrotFix(PINFO pInfo)
{
   DWORD       dwTick1;
   HDC         hDC;
   RECT        rc;
   int	       m, n, i, iPrev;
   int         xCurr, yCurr;
   int         iPen;

   LONG c1, c2;
   LONG x1, y1, x, y, r;
#ifndef THRDONE
   INT      iNumClr;
#endif

   HDC          hDCMem;
   HBITMAP      hBitmap, hOldBitmap;

   int  iBand=1;
   int  iScan=0;
   int  iDiff=0;

   iPen = pInfo->iPen + 1;
   pInfo->bMandel = TRUE;
   pInfo->bDrawing = TRUE;
   hDC = GetDC(pInfo->hwnd);

#ifndef THRDONE
    if ((iNumClr = iCreatePenFrPal(hDC, NULL, 0, &(pInfo->hPal))) != 0) {
        sprintf( gtext,"iNumClr = %d\n", iNumClr);
        OutputDebugString( gtext);

        if ((pInfo->prghPen = (PVOID*) GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(HPEN)*iNumClr)) == NULL) {
            MessageBox(ghwndMain, "Failed in Memory Allocation for pInfo->prghPen!", "Error", MB_OK);
        } else {
            if ((pInfo->iPen = iCreatePenFrPal(hDC, pInfo->prghPen, 0, &(pInfo->hPal))) == 0)
                MessageBox(ghwndMain, "Failed in creating pen!", "Error", MB_OK);
        }
    }

   hDCMem = CreateCompatibleDC(hDC);

   SelectPalette(hDCMem, pInfo->hPal, FALSE);
   SelectPalette(hDC,    pInfo->hPal, FALSE);
   RealizePalette(hDC);
   iPen = pInfo->iPen + 1;
#endif

   GetClientRect(pInfo->hwnd, &rc);

   hBitmap = CreateCompatibleBitmap(hDC, (int)rc.right, (int)rc.bottom);
   hOldBitmap = SelectObject(hDCMem, hBitmap);

   dwTick1 = GetTickCount();
   yCurr = rc.top;

   iScan = (((iScan = BATCH / rc.right) == 0) ? 1 : iScan);

   for (n=rc.top; n<=rc.bottom; n+=pInfo->iStep, yCurr+=pInfo->iStep, iPrev = FIRST_PIXEL) {
       xCurr = rc.left;                   // since LineTo excludes last point
       MoveToEx(hDCMem, 0, yCurr, NULL);
       c2 = XformFix(n, rc.top, rc.bottom, pInfo->lyFrom, pInfo->lyTo);

       for (m=rc.left; m<=rc.right; m++, xCurr++) {
           c1 = XformFix(m, rc.left, rc.right, pInfo->lxFrom, pInfo->lxTo);
	   x = c1;
	   y = c2;

	   for (i=1; i<=pInfo->iIteration; i++) {
               x1 = lMul(x - y, x + y) + c1;
               y1 = (lMul(x, y) * 2) + c2;
	       r = lMul(x1, x1) + lMul(y1, y1);
	       x = x1;
	       y = y1;
	       if (r > 33554432) {
                   break;
               }
           }

           if (i != iPrev) {
               if (iPrev != FIRST_PIXEL) {
                 switch(iPrev) {
                   case 1:
                           SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[0]); break;
                   default:
                       if (iPrev >= pInfo->iIteration) {
                            SelectObject(hDCMem, hpnBlack);
                            break;
                       }
                       SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[iPrev % iPen]);
                       break;
                 }
                 iPrev = i;
                 LineTo(hDCMem, xCurr, yCurr);
               }
               else
		  iPrev = i;	// remember the color for the first pixel
           }
       }

       switch(i) {
           case 1:
               SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[0]);
               break;
           default:
               if (i >= pInfo->iIteration) {
                    SelectObject(hDCMem, hpnBlack);
                    break;
               }
               SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[i % iPen]);
               break;
       }
       LineTo(hDCMem,xCurr,yCurr);

       sprintf(gtext, "Drawing: %3.2f %%", ((FLOAT)n/(FLOAT)rc.bottom)*100);
       SetWindowText(pInfo->hTextWnd, gtext);

       if (n >= iScan * iBand) {
            BitBlt(hDC, 0, iScan * (iBand-1), rc.right, iScan, hDCMem, 0, iScan * (iBand-1), SRCCOPY);
            iBand++;
       }

   }

   if ((iDiff = iScan * iBand - rc.bottom) != 0) {
        BitBlt(hDC, 0, iScan * (iBand-1), rc.right, iScan-iDiff, hDCMem, 0, iScan * (iBand-1), SRCCOPY);
   }

   sprintf(gtext, "Drawing: DONE");
   SetWindowText(pInfo->hTextWnd, gtext);

   ReleaseDC(pInfo->hwnd, hDC);

   pInfo->dwElapsed = GetTickCount() - dwTick1;

   if (pInfo->hBmpSaved)
       DeleteObject(pInfo->hBmpSaved);

   pInfo->hBmpSaved = SelectObject(hDCMem, hOldBitmap);
   pInfo->bDrawing = FALSE;

#ifndef THRDONE
   if (pInfo->prghPen != NULL) {
       for (i = 0; i <= pInfo->iPen; i++) {
           DeleteObject((HPEN) (pInfo->prghPen)[i]);
       }
       GlobalFree(pInfo->prghPen);
   }
#endif

   DeleteDC(hDCMem);

   ExitThread(0);
   return TRUE;
}

/**************************************************************************\
*
* StartMandelbrot
*					     2
* Effects: Draw'g the Mandelbrot Set for Q (z) = z  + c, where z, c complex
*
* Warnings:
*
* History:
*  11-Feb-1993      Petrus Wong     Minimize device access
*  14-Dec-1992 -by- Petrus Wong     Enable shadow bitmap
*  14-Jun-1992 -by- Petrus Wong     Modified to use the pen array and LineTo
*  20-Nov-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/
BOOL StartMandelbrot(PINFO pInfo)
{
   DWORD       dwTick1;
   HDC         hDC;
   RECT        rc;
   int	       m, n, i, iPrev;
   int         xCurr, yCurr;
   int         iPen;

#ifndef THRDONE
   INT      iNumClr;
   char     text[256];
#endif

   double c1, c2;
   double x1, y1, x, y, r;

   HDC          hDCMem;
   HBITMAP      hBitmap, hOldBitmap;

   int  iBand=1;
   int  iScan=0;
   int  iDiff=0;

   iPen = pInfo->iPen + 1;
   pInfo->bMandel = TRUE;
   pInfo->bDrawing = TRUE;
   hDC = GetDC(pInfo->hwnd);

#ifndef THRDONE
    if ((iNumClr = iCreatePenFrPal(hDC, NULL, 0, &(pInfo->hPal))) != 0) {
        sprintf( gtext,"iNumClr = %d\n", iNumClr);
        OutputDebugString( gtext);

        if ((pInfo->prghPen = (PVOID*) GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(HPEN)*iNumClr)) == NULL) {
            MessageBox(ghwndMain, "Failed in Memory Allocation for pInfo->prghPen!", "Error", MB_OK);
        } else {
            if ((pInfo->iPen = iCreatePenFrPal(hDC, pInfo->prghPen, 1, &(pInfo->hPal))) == 0)
                MessageBox(ghwndMain, "Failed in creating pen!", "Error", MB_OK);
        }
    }

   hDCMem = CreateCompatibleDC(hDC);

   SelectPalette(hDCMem, pInfo->hPal, FALSE);
   SelectPalette(hDC,    pInfo->hPal, FALSE);
   RealizePalette(hDC);
   iPen = pInfo->iPen + 1;
   wsprintf(text, "iPen = %d\n", iPen);
   //MessageBox(ghwndMain, text, "Error", MB_OK);
#endif

   GetClientRect(pInfo->hwnd, &rc);

   hBitmap = CreateCompatibleBitmap(hDC, (int)rc.right, (int)rc.bottom);
   hOldBitmap = SelectObject(hDCMem, hBitmap);

#if 0
 {
   int iWidth, i, j;

   iWidth = rc.right/iNumClr;

   for (i = 0; i < iNumClr; i++) {
       SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[i % iNumClr]);
       for (j = 0; j < iWidth; j++) {
            MoveToEx(hDCMem, i*iWidth+j, 0, NULL);
            LineTo(hDCMem, i*iWidth+j, rc.bottom);
       }

   }
 }
#endif

   dwTick1 = GetTickCount();

   iScan = (((iScan = BATCH / rc.right) == 0) ? 1 : iScan);

   yCurr = rc.top;
   for (n=rc.top; n<=rc.bottom; n+=pInfo->iStep, yCurr+=pInfo->iStep, iPrev = FIRST_PIXEL) {
       xCurr = rc.left;                   // since LineTo excludes last point
       MoveToEx(hDCMem, 0, yCurr, NULL);
       c2 = Xform((double) n, 0.0, (double) rc.bottom, pInfo->yFrom, pInfo->yTo);

       for (m=rc.left; m<=rc.right; m++, xCurr++) {
           c1 = Xform((double) m, 0.0, (double) rc.right, pInfo->xFrom, pInfo->xTo);
	   x = c1;
	   y = c2;

	   for (i=1; i<=pInfo->iIteration; i++) {
               x1 = (x - y) * (x + y) + c1;
               y1 = 2 * x * y + c2;
	       r = x1 * x1 + y1 * y1;
	       x = x1;
	       y = y1;
	       if (r > 4.0) {
                   break;
               }
           }

           if (i != iPrev) {
               if (iPrev != FIRST_PIXEL) {
                 switch(iPrev) {
                   case 1:
                           SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[0]);
                    break;
                   default:
                       if (iPrev >= pInfo->iIteration) {
                            SelectObject(hDCMem, hpnBlack);
                            break;
                       }
                       SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[iPrev % iPen]);
                       break;
                 }
                 iPrev = i;
                 LineTo(hDCMem, xCurr, yCurr);
               }
               else
		  iPrev = i;	// remember the color for the first pixel
           }
       }

       switch(i) {
              case 1:
                      SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[0]);
               break;
              default:
                  if (i >= pInfo->iIteration) {
                       SelectObject(hDCMem, hpnBlack);
                       break;
                  }
                  SelectObject(hDCMem, (HPEN)(pInfo->prghPen)[i % iPen]);
                  break;
       }

       LineTo(hDCMem,xCurr,yCurr);

       sprintf(gtext, "Drawing: %3.2f %%", ((FLOAT)n/(FLOAT)rc.bottom)*100);
       SetWindowText(pInfo->hTextWnd, gtext);

       if (n >= iScan * iBand) {
            BitBlt(hDC, 0, iScan * (iBand-1), rc.right, iScan, hDCMem, 0, iScan * (iBand-1), SRCCOPY);
            iBand++;
       }
   }

   if ((iDiff = iScan * iBand - rc.bottom) != 0) {
        BitBlt(hDC, 0, iScan * (iBand-1), rc.right, iScan-iDiff, hDCMem, 0, iScan * (iBand-1), SRCCOPY);
   }

   sprintf(gtext, "Drawing: DONE");
   SetWindowText(pInfo->hTextWnd, gtext);

   ReleaseDC(pInfo->hwnd, hDC);

   pInfo->dwElapsed = GetTickCount() - dwTick1;

   if (pInfo->hBmpSaved)
       DeleteObject(pInfo->hBmpSaved);

   pInfo->hBmpSaved = SelectObject(hDCMem, hOldBitmap);
   pInfo->bDrawing = FALSE;
#ifndef THRDONE
   if (pInfo->prghPen != NULL) {
       for (i = 0; i <= pInfo->iPen; i++) {
           DeleteObject((HPEN) (pInfo->prghPen)[i]);
       }
       GlobalFree(pInfo->prghPen);
   }
#endif

   DeleteDC(hDCMem);

   ExitThread(0);
   return TRUE;
}


/******************************Public*Routine******************************\
*
* Xform
*
* Effects: Given m, find x.
*
*          Xform(Pt) : Src |--> Dest
* eg.
*          Src |----|--------|      |-->   Dest |----|--------|
*              -2   x        2                  0    m       320
*
*          x = (m - 0)/(320 - 0) * (2 - -2) + -2
*
* Warnings: 1. This will become a macro in the future for speed.  For now,
*	       it is here for debugging purposes.
*
* History:
*  25-Nov-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/
#if 0
double Xform(
   double Pt,
   double SrcFrom,
   double SrcTo,
   double DestFrom,
   double DestTo)
{
 //sprintf( gtext,"%g = Xform(%g, %g, %g, %g, %g)\n", ((Pt-SrcFrom)/(SrcTo-SrcFrom)*(DestTo-DestFrom))+DestFrom, Pt, SrcFrom, SrcTo, DestFrom, DestTo);
 //OutputDebugString( gtext );
 return(((Pt-SrcFrom)/(SrcTo-SrcFrom)*(DestTo-DestFrom))+DestFrom);
}
#endif

/******************************Public*Routine******************************\
*
* Xform2
*
* Effects: Given x, find m.
*
*          Xform(Pt) : Src |--> Dest
* eg.
*          Src |----|--------|      |-->   Dest |----|--------|
*	       -2   x	     2			0    m	     320
*
*	   m = (x - -2)/(2 - -2) * 320
*
* Warnings: 1. This will become a macro in the future for speed.  For now,
*	       it is here for debugging purposes.
*
* History:
*  25-Nov-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/
#if 0
int Xform2(
   double Pt,
   double SrcFrom,
   double SrcTo,
   double DestFrom,
   double DestTo)
{
 //sprintf( gtext,"%g = Xform(%g, %g, %g, %g, %g)\n", ((Pt-SrcFrom)/(SrcTo-SrcFrom)*(DestTo-DestFrom))+DestFrom, Pt, SrcFrom, SrcTo, DestFrom, DestTo);
 //OutputDebugString( gtext );
 return((int) ((Pt-SrcFrom)/(SrcTo-SrcFrom)*(DestTo-DestFrom)+DestFrom));
}
#endif

/******************************Public*Routine******************************\
*
* SaveBitmap
*
* Effects:  Returns the handle of a bitmap corresponding to the window.
*           using utilizing BitBlt.
*
* Warnings:
*   // should we set the dirty flag here?
*
*
* History:
*  03-Dec-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

HBITMAP SaveBitmap(HWND hWnd, HPALETTE hPal) {
    HDC     hdcMem, hDC;
    HBITMAP hBitmap, hOldBitmap;
    RECT    rc;
    HPALETTE hPalOld, hPalMemOld;
    int     ii;

    hDC = GetDC(hWnd);
    GetClientRect(hWnd, &rc);

    if (hPal) {

        hPalOld = SelectPalette(hDC, hPal, FALSE);
        ii=RealizePalette(hDC);
        if (ii){
            UpdateColors (hDC);
        }

    }

    hdcMem = CreateCompatibleDC(hDC);

    if (hPal) {

        hPalMemOld = SelectPalette(hdcMem, hPal, FALSE);
        RealizePalette(hdcMem);
    }

    hBitmap = CreateCompatibleBitmap(hDC, (int)rc.right-rc.left, (int)rc.bottom-rc.top);

    hOldBitmap = SelectObject(hdcMem, hBitmap);


    BitBlt(hdcMem, 0, 0, (int)rc.right-rc.left, (int)rc.bottom-rc.top, hDC, 0, 0, SRCCOPY);
    hBitmap = SelectObject(hdcMem, hOldBitmap);

    DeleteDC(hdcMem);
    ReleaseDC(hWnd, hDC);

    return(hBitmap);

}

/******************************Public*Routine******************************\
*
* DrawBitmap
*
* Effects:
*
* Warnings:
*
* History:
*  03-Dec-1991 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

void DrawBitmap(HDC hdc, PINFO pInfo, int xStart, int yStart, int cx, int cy) {
    BITMAP bm;
    HDC    hdcMem;
    POINT  ptSize, ptOrg;
    HPALETTE hPalOld, hPalMemOld;
    char    text[128];

    if ((pInfo->hPal) && !(pInfo->bUseMono)) {

        hPalOld = SelectPalette(hdc,
             ((pInfo->iStretchMode == HALFTONE) ? pInfo->hHTPal : pInfo->hPal),
             FALSE);
        //RealizePalette(hdc);
        wsprintf(text, "Realized Palette = %d", RealizePalette(hdc));
        MessageBox(ghwndMain, text, "", MB_OK);

        UpdateColors (hdc);
    }

    hdcMem = CreateCompatibleDC(hdc);

    if ((pInfo->hPal) && !(pInfo->bUseMono)) {

        hPalMemOld = SelectPalette(hdcMem,
             ((pInfo->iStretchMode == HALFTONE) ? pInfo->hHTPal : pInfo->hPal),
              FALSE);
        //RealizePalette(hdcMem);
        wsprintf(text, "Realized Palette = %d", RealizePalette(hdcMem));
        MessageBox(ghwndMain, text, "", MB_OK);


        UpdateColors (hdcMem);



    }

    SelectObject(hdcMem, (pInfo->bUseMono ? pInfo->hBmpMono : pInfo->hBmpSaved));
    SetMapMode(hdcMem, GetMapMode(hdc));

    GetObject((pInfo->bUseMono ? pInfo->hBmpMono : pInfo->hBmpSaved), sizeof(BITMAP), (LPSTR)&bm);
    ptSize.x = bm.bmWidth;
    ptSize.y = bm.bmHeight;
    DPtoLP(hdc, &ptSize, 1);

    ptOrg.x = 0;
    ptOrg.y = 0;
    DPtoLP(hdcMem, &ptOrg, 1);

    if (pInfo->bStretch) {
        SetStretchBltMode(hdc, pInfo->iStretchMode);
        // cx+1 and cy+1: temporary work around for a bug!
        StretchBlt(hdc, xStart, yStart, cx+1, cy+1,
               hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y,SRCCOPY);
    } else {
        BitBlt(hdc, xStart, yStart, ptSize.x, ptSize.y,
               hdcMem, xStart, yStart, SRCCOPY);
    }

    if (pInfo->bUseMono)
        pInfo->bUseMono = FALSE;

    DeleteDC(hdcMem);
    UNREFERENCED_PARAMETER(cx);
    UNREFERENCED_PARAMETER(cy);
}

/******************************Public*Routine******************************\
*
* bDrawDIB
*
* Effects:
*
* Warnings:
*
* History:
*  17-Jun-1993      Petrus Wong     updated for latest HT changes
*  06-Feb-1993      Petrus Wong     rewritten for HT support
*  14-Dec-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bDrawDIB(HDC hDC, PINFO pInfo, int xStart, int yStart, int cx, int cy)
{
    HBITMAP     hBmpOld, hBmp;
    BOOL        bSuccess;
    PBITMAPINFO pbmi;
    HDC         hdcMem;
    BITMAP      bm;

    bSuccess = TRUE;
    if (pInfo->bUseMono) {
        DrawBitmap(hDC, pInfo, xStart, yStart, cx, cy);
        return bSuccess;
    }

    if ((hBmp = pInfo->hBmpSaved) == NULL) {
        MessageBox(ghwndMain, "There's no Bitmap to draw!", "Error", MB_OK);
        return FALSE;
    }

    if (pInfo->bStretch) {
        SetStretchBltMode(hDC, pInfo->iStretchMode);

        if (pInfo->bUseDIB) { // this'll give a src for halftone...
            pbmi = (PBITMAPINFO) (pInfo->RleData.rgpbmi[0]);

            // Select and realize the appropriate palette to destination DC
            if ((pInfo->iStretchMode == HALFTONE) && (pInfo->hHTPal)) {
                SelectPalette(hDC, pInfo->hHTPal, FALSE);
                RealizePalette(hDC);
            } else {
                if ((pInfo->iStretchMode != HALFTONE) && (pInfo->hPal)) {
                    SelectPalette(hDC, pInfo->hPal, FALSE);
                    RealizePalette(hDC);
                }
            }
            if (pInfo->bCoreHdr) {
                StretchDIBits(hDC, xStart, yStart, cx, cy,
                          0, 0, ((LPBITMAPCOREINFO)pbmi)->bmciHeader.bcWidth,
                                ((LPBITMAPCOREINFO)pbmi)->bmciHeader.bcHeight,
                          (LPSTR)pInfo->RleData.rgpjFrame[0], pbmi, DIB_RGB_COLORS, SRCCOPY);
            } else {
                StretchDIBits(hDC, xStart, yStart, cx, cy,
                          0, 0, pbmi->bmiHeader.biWidth, pbmi->bmiHeader.biHeight,
                          (LPSTR)pInfo->RleData.rgpjFrame[0], pbmi, DIB_RGB_COLORS, SRCCOPY);
            }
        } else { // image is dirty or created on this device

            HBITMAP     hDIB;

            //
            // Get the source DIB info from the DDB for halftoning
            //
            hDIB = DIBfromDDB(hDC, hBmp, pInfo);

            hdcMem = CreateCompatibleDC(hDC);
            hBmpOld = SelectObject(hdcMem, hDIB);

            if ((pInfo->iStretchMode == HALFTONE) && (pInfo->hHTPal)) {
                SelectPalette(hDC, pInfo->hHTPal, FALSE);
                RealizePalette(hDC);
            } else {
                if ((pInfo->iStretchMode != HALFTONE) && (pInfo->hPal)) {
                    SelectPalette(hDC, pInfo->hPal, FALSE);
                    RealizePalette(hDC);
                }
            }

            //
            // Use StretchBlt here instead of StretchDIBits just for
            // demonstrating using the API with halftone
            //
            GetObject(hDIB, sizeof(BITMAP), (LPSTR)&bm);
            StretchBlt(hDC, xStart, yStart, cx, cy, hdcMem,
                          0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);

            DeleteDC(hdcMem);

        }
        return bSuccess;
    }

    //
    // Not stretching...No halftone
    //

    GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bm);

    // Select and realize the appropriate palette to destination DC
    if (pInfo->hPal) {
        SelectPalette(hDC, pInfo->hPal, FALSE);
        RealizePalette(hDC);
    }

    if (pInfo->bUseDIB) { // this'll give a src
        pbmi = (PBITMAPINFO) (pInfo->RleData.rgpbmi[0]);

        // default to use SetDIBitsToDevice
        SetDIBitsToDevice(hDC, xStart, yStart, bm.bmWidth, bm.bmHeight,
                      0, 0, 0, bm.bmHeight, pInfo->RleData.rgpjFrame[0],
                      pbmi, DIB_RGB_COLORS);
    } else { // image is dirty or created on this device

        hdcMem = CreateCompatibleDC(hDC);
        hBmpOld = SelectObject(hdcMem, hBmp);

        BitBlt(hDC, xStart, yStart, bm.bmWidth, bm.bmHeight,
               hdcMem, 0, 0, SRCCOPY);

        DeleteDC(hdcMem);
    }

    return bSuccess;
}

/******************************Public*Routine******************************\
*
* pGetInfoData(HWND hwnd)
*
* Effects:  calls LocalLock, returning pointer to Info structure.
*           assumes hwnd contains handle to Info structure at index 0.
*           should call bReleaseInfoData when done.
*           Global alert: ghwndMain used.
*
* Warnings:
*
* History:
*  27-Jan-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

PINFO pGetInfoData(HWND hwnd)
{
    HANDLE hInfo;
    PINFO  pInfo;

    hInfo = (HANDLE) GetWindowLong(hwnd, 0);
    if (hInfo == NULL) {
        MessageBox(ghwndMain, "Null Info handle retrieved - GetInfo", "Error", MB_OK);
        return NULL;
    }

    if ((pInfo = (PINFO)LocalLock(hInfo)) == NULL) {
        return NULL;
    }

    return pInfo;
}

/******************************Public*Routine******************************\
*
* bReleaseInfoData(HWND hwnd)
*
* Effects: calls LocalUnlock.  Returns whatever LocalUnlock returns.
*
* Warnings: assumes LocalLock was called previously.
*           assumes hwnd contains handle to Info structure at index 0.
*           Global alert: ghwndMain used.
*
* History:
*  27-Jan-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bReleaseInfoData(HWND hwnd)
{
    HANDLE hInfo;

    hInfo = (HANDLE) GetWindowLong(hwnd, 0);
    if (hInfo == NULL) {
        MessageBox(ghwndMain, "Null Info handle retrieved - ReleaseInfo", "Error", MB_OK);
        return FALSE;
    }

    return (LocalUnlock(hInfo));
}


/******************************Public*Routine******************************\
*
* bCheckMutexMenuItem
*
* Effects: Put a check mark on uiCheckItem in the hMenu.  Remove previous
*          check mark, if any, on items in the same group.  Returns TRUE
*          if successful, FALSE otherwise (item not exists).
*
* Warnings:
*
* History:
*  12-Jan-1993      Petrus Wong             rewritten
*  28-Jan-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bCheckMutexMenuItem(PINFO pInfo, HMENU hMenu, UINT uiCheckItem)
{

    switch(uiCheckItem) {
        case MM_TP_IDLE:
        case MM_TP_LOW:
        case MM_TP_BELOW_NORMAL:
        case MM_TP_NORMAL:
        case MM_TP_ABOVE_NORMAL:
        case MM_TP_HIGH:
        case MM_TP_TIME_CRITICAL:
            switch (pInfo->iPriority) {
                case THREAD_PRIORITY_IDLE:
                    CheckMenuItem(hMenu, MM_TP_IDLE, MF_UNCHECKED);
                    break;
                case THREAD_PRIORITY_LOWEST:
                    CheckMenuItem(hMenu, MM_TP_LOW, MF_UNCHECKED);
                    break;
                case THREAD_PRIORITY_BELOW_NORMAL:
                    CheckMenuItem(hMenu, MM_TP_BELOW_NORMAL, MF_UNCHECKED);
                    break;
                case THREAD_PRIORITY_NORMAL:
                    CheckMenuItem(hMenu, MM_TP_NORMAL, MF_UNCHECKED);
                    break;
                case THREAD_PRIORITY_ABOVE_NORMAL:
                    CheckMenuItem(hMenu, MM_TP_ABOVE_NORMAL, MF_UNCHECKED);
                    break;
                case THREAD_PRIORITY_HIGHEST:
                    CheckMenuItem(hMenu, MM_TP_HIGH, MF_UNCHECKED);
                    break;
                case THREAD_PRIORITY_TIME_CRITICAL:
                    CheckMenuItem(hMenu, MM_TP_TIME_CRITICAL, MF_UNCHECKED);
                    break;
                default:
                    break;
            }
            break;
        case MM_FLOAT:
        case MM_FIX:
            CheckMenuItem(hMenu, MM_FLOAT, MF_UNCHECKED);
            CheckMenuItem(hMenu, MM_FIX,   MF_UNCHECKED);
            break;
        case MM_ITERATION_100:
        case MM_ITERATION_500:
        case MM_ITERATION_1000:
        case MM_ITERATION_5000:
        case MM_ITERATION_DOUBLE:
            switch (pInfo->iIteration) {
                case 100:
                    CheckMenuItem(hMenu, MM_ITERATION_100, MF_UNCHECKED);
                    break;
                case 500:
                    CheckMenuItem(hMenu, MM_ITERATION_500, MF_UNCHECKED);
                    break;
                case 1000:
                    CheckMenuItem(hMenu, MM_ITERATION_1000, MF_UNCHECKED);
                    break;
                case 5000:
                    CheckMenuItem(hMenu, MM_ITERATION_5000, MF_UNCHECKED);
                    break;
                default:
                    CheckMenuItem(hMenu, MM_ITERATION_DOUBLE, MF_UNCHECKED);
                    break;
            }
            break;
        case MM_STEP_ONE:
        case MM_STEP_TWO:
        case MM_STEP_THREE:
            switch (pInfo->iStep) {
                case 1:
                    CheckMenuItem(hMenu, MM_STEP_ONE, MF_UNCHECKED);
                    break;
                case 2:
                    CheckMenuItem(hMenu, MM_STEP_TWO, MF_UNCHECKED);
                    break;
                default:
                    CheckMenuItem(hMenu, MM_STEP_THREE, MF_UNCHECKED);
                    break;
            }
            break;
        case MM_STRETCHBLT:
        case MM_BITBLT:
                CheckMenuItem(hMenu, MM_STRETCHBLT,   MF_UNCHECKED);
                CheckMenuItem(hMenu, MM_BITBLT,       MF_UNCHECKED);
            break;
        case MM_BLACKONWHITE:
        case MM_COLORONCOLOR:
        case MM_WHITEONBLACK:
        case MM_HALFTONE:
            switch (pInfo->iStretchMode) {
                case BLACKONWHITE:
                    CheckMenuItem(hMenu, MM_BLACKONWHITE, MF_UNCHECKED);
                    break;
                case COLORONCOLOR:
                    CheckMenuItem(hMenu, MM_COLORONCOLOR, MF_UNCHECKED);
                    break;
                case WHITEONBLACK:
                    CheckMenuItem(hMenu, MM_WHITEONBLACK, MF_UNCHECKED);
                    break;
                case HALFTONE:
                    CheckMenuItem(hMenu, MM_HALFTONE, MF_UNCHECKED);
                    break;
                default:
                    break;
            }
            break;
        case MM_PORTRAIT:
        case MM_LANDSCAPE:
                CheckMenuItem(hMenu, MM_PORTRAIT,   MF_UNCHECKED);
                CheckMenuItem(hMenu, MM_LANDSCAPE,  MF_UNCHECKED);
            break;
        default:
            return FALSE;
    }
    CheckMenuItem(hMenu, uiCheckItem, MF_CHECKED);
    return TRUE;
}


/******************************Public*Routine******************************\
*
* vChkMenuItem
*
* Effects:  Helper function for checking or unchecking menu items based on
*           values set in the pInfo.  Gets called when processing
*           WM_MDIACTIVATE
*
* Warnings:
*
* History:
*  12-Jan-1993 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

VOID vChkMenuItem(PINFO pInfo, HMENU hMenu, UINT uiFlag)
{
    (pInfo->bStretch ?
        CheckMenuItem(hMenu, MM_STRETCHBLT, uiFlag) :
        CheckMenuItem(hMenu, MM_BITBLT, uiFlag) );

    switch (pInfo->iPriority) {
        case THREAD_PRIORITY_IDLE:
            CheckMenuItem(hMenu, MM_TP_IDLE, uiFlag);
            break;
        case THREAD_PRIORITY_LOWEST:
            CheckMenuItem(hMenu, MM_TP_LOW, uiFlag);
            break;
        case THREAD_PRIORITY_BELOW_NORMAL:
            CheckMenuItem(hMenu, MM_TP_BELOW_NORMAL, uiFlag);
            break;
        case THREAD_PRIORITY_NORMAL:
            CheckMenuItem(hMenu, MM_TP_NORMAL, uiFlag);
            break;
        case THREAD_PRIORITY_ABOVE_NORMAL:
            CheckMenuItem(hMenu, MM_TP_ABOVE_NORMAL, uiFlag);
            break;
        case THREAD_PRIORITY_HIGHEST:
            CheckMenuItem(hMenu, MM_TP_HIGH, uiFlag);
            break;
        case THREAD_PRIORITY_TIME_CRITICAL:
            CheckMenuItem(hMenu, MM_TP_TIME_CRITICAL, uiFlag);
            break;
        default:
            break;
    }

    switch (pInfo->iStretchMode) {
        case BLACKONWHITE:
            CheckMenuItem(hMenu, MM_BLACKONWHITE, uiFlag);
            break;
        case COLORONCOLOR:
            CheckMenuItem(hMenu, MM_COLORONCOLOR, uiFlag);
            break;
        case WHITEONBLACK:
            CheckMenuItem(hMenu, MM_WHITEONBLACK, uiFlag);
            break;
        case HALFTONE:
            CheckMenuItem(hMenu, MM_HALFTONE, uiFlag);
            break;
        default:
            break;
    }

    switch (pInfo->iStep) {
        case 1:
            CheckMenuItem(hMenu, MM_STEP_ONE, uiFlag);
            break;
        case 2:
            CheckMenuItem(hMenu, MM_STEP_TWO, uiFlag);
            break;
        default:
            CheckMenuItem(hMenu, MM_STEP_THREE, uiFlag);
            break;
    }

    switch (pInfo->iIteration) {
        case 100:
            CheckMenuItem(hMenu, MM_ITERATION_100, uiFlag);
            break;
        case 500:
            CheckMenuItem(hMenu, MM_ITERATION_500, uiFlag);
            break;
        case 1000:
            CheckMenuItem(hMenu, MM_ITERATION_1000, uiFlag);
            break;
        case 5000:
            CheckMenuItem(hMenu, MM_ITERATION_5000, uiFlag);
            break;
        default:
            CheckMenuItem(hMenu, MM_ITERATION_DOUBLE, uiFlag);
            break;
    }
    return;
}

/******************************Public*Routine******************************\
*
* bInitInfo
*
* Effects: Initialize the Info data structure
*
* Warnings:
*
* History:
*  16-Feb-1993      Petrus Wong         changed to 9.23 fix value
*  08-Feb-1993      Petrus Wong         Added bUseDIB flag
*  14-Dec-1992 -by- Petrus Wong         Added Rle and pens stuff
*  28-Jan-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bInitInfo(PINFO pInfo)
{
    pInfo->hParent      = ghwndClient;
    pInfo->xFrom        = -2.0;
    pInfo->xTo          = 1.0;
    pInfo->yFrom        = 1.5;
    pInfo->yTo          = -1.5;
    pInfo->lxFrom       = -16777216;                 // 9.23 fix point
    pInfo->lxTo         =  8388608;                  // representation of
    pInfo->lyFrom       =  12582912;                 // -2, 1.0, 1.5, and -1.5
    pInfo->lyTo         = -12582912;                 //
    pInfo->iIteration   = gIteration;
    pInfo->iStep        = gStep;
    pInfo->bStretch     = gbStretch;
    pInfo->iStretchMode = giStretchMode;
    wsprintf((LPSTR) &(pInfo->CaptionBarText), "");
    pInfo->hwnd         = NULL;
    pInfo->hTextWnd     = NULL;
    pInfo->rcClient.top    = 0;
    pInfo->rcClient.left   = 0;
    pInfo->rcClient.bottom = 0;
    pInfo->rcClient.right  = 0;
    pInfo->hdcClient    = NULL;
    pInfo->hRgnPath     = NULL;
    pInfo->hThrd0       = NULL;
    pInfo->hThrd        = NULL;
    pInfo->bDrawing     = FALSE;
    pInfo->dwThreadId   = 0;
    pInfo->dwElapsed    = 0L;
    pInfo->c1           = 0.0;
    pInfo->c2           = 0.0;
    pInfo->lc1          = 0L;
    pInfo->lc2          = 0L;
    pInfo->hBmpSaved    = NULL;
    pInfo->bSizeChng    = FALSE;
    pInfo->bMandel      = TRUE;
    pInfo->bSetDIBsToDevice = FALSE;
    pInfo->bFill        = FALSE;
    pInfo->hBrush       = NULL;
    pInfo->hQuitEvent   = NULL;
    pInfo->hCycleThrd   = NULL;
    pInfo->dwCycleThrdID = 0;
    pInfo->bClrCycle    = FALSE;
    pInfo->bFirstTime   = TRUE;
    pInfo->dwSuspend    = 0;
    pInfo->hBmpMono     = NULL;
    pInfo->bUseMono     = FALSE;
    pInfo->hPrtThrd     = NULL;
    pInfo->dwPrtThrdID  = 0;
    pInfo->hPal         = NULL;
    pInfo->hCyclePal    = NULL;
    pInfo->RleData.ulFiles  = 0;
    pInfo->RleData.ulFrames = 0;
    pInfo->bPlayRleCont = FALSE;
    pInfo->prghPen      = NULL;
    pInfo->iPen         = 0;
    pInfo->iPriority    = THREAD_PRIORITY_NORMAL;
    pInfo->bUseDIB      = FALSE;
    pInfo->bCoreHdr     = FALSE;

    return TRUE;
}

/******************************Public*Routine******************************\
*
* bResetGlobal
*
* Effects: Set (l) x/y From/To to their default values
*          Used for Julia set
*
* Warnings:
*
* History:
*  16-Feb-1993      Petrus Wong     changed to 9.23 fix value
*  28-Jan-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bResetGlobal(VOID)
{
    xFrom  =  -2.0;
    xTo    =   2.0;
    yFrom  =   2.0;
    yTo    =  -2.0;
    lxFrom =  -16777216;
    lxTo   =   16777216;
    lyFrom =   16777216;
    lyTo   =  -16777216;
    return TRUE;
}

/******************************Public*Routine******************************\
*
* hBrCreateBrush
*
* Effects: Creates a brush with the specified RGB
*
* Warnings:
*
* History:
*  04-Mar-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

HBRUSH hBrCreateBrush(HDC hDC, DWORD dwRGB)
{
    HDC hdcMem;
    HBRUSH hbr;
    HBRUSH hbrOld;
    HBITMAP hbmPat;
    HBITMAP hbmOld;

    hbr = CreateSolidBrush(dwRGB);
    hdcMem = CreateCompatibleDC(hDC);

    //
    // Minimum size for a bitmap to be used in a fill pattern is 8x8
    //
    hbmPat = CreateCompatibleBitmap(hDC, 8, 8);

    hbmOld = SelectObject(hdcMem, hbmPat);
    hbrOld = SelectObject(hdcMem, hbr);
    PatBlt(hdcMem, 0, 0, 8, 8, PATCOPY);

    //
    // Deselect hbmPat and hbr
    //
    SelectObject(hdcMem, hbmOld);
    SelectObject(hdcMem, hbrOld);

    DeleteDC(hdcMem);
    DeleteObject(hbr);

    hbr = CreatePatternBrush(hbmPat);

    DeleteObject(hbmPat);

    return hbr;
}


/******************************Public*Routine******************************\
*
* bPrintBmp
*
* Effects: A Thread routine for printing bitmap
*
* Warnings:
*
* History:
*  31-May-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bPrintBmp(PPRTDATA pPrtData) {
    HDC         hdcPrinter;
    int         iWidth, iHeight;

#ifdef NEWPRTAPI
    DOCINFO     DocInfo;
#endif

    if (pPrtData->bUseDefault) {
          hdcPrinter = CreateDC( "", gpszPrinterNames[pPrtData->index],
                                 "", NULL);
    } else {
          hdcPrinter = CreateDC( "", gpszPrinterNames[pPrtData->index],
                                 "", &(pPrtData->DevMode));
    }

    if (!hdcPrinter)
    {
        ExitThread(0);
        return(FALSE);
    }

    iWidth = GetDeviceCaps(hdcPrinter, HORZRES);
    iHeight = GetDeviceCaps(hdcPrinter, VERTRES);

#ifdef NEWPRTAPI

    DocInfo.cbSize      = sizeof(DOCINFO);
    DocInfo.lpszDocName = pPrtData->info.CaptionBarText;
    DocInfo.lpszOutput   = NULL;
    StartDoc(hdcPrinter, &DocInfo);
    StartPage(hdcPrinter);
    bDrawDIB(hdcPrinter, &(pPrtData->info), 0, 0, iWidth, iHeight);
    EndPage(hdcPrinter);
    EndDoc(hdcPrinter);

#else

    Escape(hdcPrinter, STARTDOC, 20, "Mandelbrot", NULL);
    bDrawDIB(hdcPrinter, &(pPrtData->info), 0, 0, iWidth, iHeight);
    Escape(hdcPrinter, NEWFRAME, 0, NULL, NULL);
    Escape(hdcPrinter, ENDDOC, 0, NULL, NULL);

#endif

    DeleteDC(hdcPrinter);
    ExitThread(0);
    return(TRUE);

}



/******************************Public*Routine******************************\
*
* StoreRleFile
*
* Effects:
*
* Warnings:
*
* History:
*  05-Dec-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bStoreRleFile(HDC hDC, PINFO pInfo, PSTR pszFileName)
{
  BOOL                bSuccess;
  HANDLE              hFile, hMapFile;
  LPVOID              pMapFile;
  LPBITMAPINFOHEADER  pbmh;
  LPBITMAPINFO        pbmi;
  PBYTE               pjTmp, pjDIBits, pjRleBits;
  ULONG               sizBMI, sizImage, ulfSize;
  DWORD               dwOffBits;
  LONG                lScan;
  INT                 iNumClr;
  ULONG               ulFrames, ulFiles, ulOffset;
  PFILEINFO           pFileInfo;
  DWORD               dwFileSizeLow, dwFileSizeHigh;
  WORD                wBitCount;
  BOOL                bCoreHdr;

  bSuccess = TRUE;

  if ((hFile = CreateFile(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
          OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL)) == (HANDLE)-1) {
      ErrorOut("Fail in file open");
      bSuccess = FALSE;
      goto ErrExit1;
  }

  dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
  if ((dwFileSizeLow == 0xFFFFFFFF) && (GetLastError() != NO_ERROR)) {
      ErrorOut("GetFileSize failed");
      bSuccess = FALSE;
      goto ErrExit2;
  }

  //
  // Create a map file of the opened file
  //
  if ((hMapFile = CreateFileMapping(hFile, NULL,
                           PAGE_READONLY, 0, 0, NULL)) == (HANDLE)-1) {
      ErrorOut("Fail in creating map file");
      bSuccess = FALSE;
      goto ErrExit2;
  }

  //
  // Map a view of the whole file
  //
  if ((pMapFile = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0)) == NULL) {
      ErrorOut("Fail in mapping view of the Map File object");
      bSuccess = FALSE;
      goto ErrExit3;
  }

  ulFiles = pInfo->RleData.ulFiles;
  pFileInfo = &(pInfo->RleData.rgFileInfo[ulFiles]);
  pFileInfo->hFile = hFile;
  pFileInfo->hMapFile = hMapFile;
  pFileInfo->lpvMapView = pMapFile;

  ulFrames = pInfo->RleData.ulFrames;
  ulOffset = 0;

  while (TRUE) {
    //
    // First check that it is a bitmap file
    //
    if (*((PWORD)pMapFile) != 0x4d42) {              // 'BM'
        MessageBox(ghwndMain, "This is not a DIB bitmap file!", "Error", MB_OK);
        bSuccess = FALSE;
        goto ErrExit3;
    }
#ifdef DEBUG
    {
    static ULONG ulCnt=0;
    char buf[128];

    ulCnt++;
    wsprintf(buf, "ulCnt = %ld", ulCnt);
    MessageBox(ghwndMain, buf, "debug", MB_OK);
    }
#endif
    //
    // Saving the bfSize field in the BITMAPFILEHEADER for incrementing to the
    // next frame.  bfSize does not start at DWORD boundary...
    //
    {
      PBYTE   pjTmp;
      FILEHDR fHdr, *pfHdr;
      ULONG   ulSiz;

      pjTmp = (PBYTE)pMapFile+2;
      pfHdr = &fHdr;

      ulSiz = sizeof(FILEHDR);
      while (ulSiz--) {
          *(((PBYTE)pfHdr)++) = *(((PBYTE)pjTmp)++);
      }
      ulfSize = fHdr.bfSize;
      dwOffBits = fHdr.bfOffbits;
    }

    //
    // advance pMapFile to point pass BITMAPFILEHEADER
    //
    //pMapFile = (PBYTE)pMapFile + sizeof(BITMAPFILEHEADER);

    //
    // Since the file header doesn't end on DWORD boundary...
    //
    pbmh = (LPBITMAPINFOHEADER)((PBYTE)pMapFile + sizeof(BITMAPFILEHEADER));

    {
        BITMAPCOREHEADER bmch, *pbmch;
        BITMAPINFOHEADER bmih, *pbmih;
        PBYTE            pjTmp;
        ULONG            ulSiz;

        pbmch = &bmch;
        pbmih = &bmih;

        pjTmp = (PBYTE)pbmh;
        ulSiz = sizeof(BITMAPCOREHEADER);
        while (ulSiz--) {
            *(((PBYTE)pbmch)++) = *(((PBYTE)pjTmp)++);
        }

        pjTmp = (PBYTE)pbmh;
        ulSiz = sizeof(BITMAPINFOHEADER);
        while (ulSiz--) {
            *(((PBYTE)pbmih)++) = *(((PBYTE)pjTmp)++);
        }

        //
        // Use the size to determine if it is a BitmapCoreHeader or
        // BitmapInfoHeader
        //
        if (bmch.bcSize == sizeof(BITMAPCOREHEADER))
        {
            wBitCount = bmch.bcBitCount;
            iNumClr = ((wBitCount == 24) ? 0 : (1 << wBitCount));
            sizBMI = sizeof(BITMAPCOREHEADER)+sizeof(RGBTRIPLE)*iNumClr;
            bCoreHdr = TRUE;
        }
        else            // BITMAPINFOHEADER
        {
            wBitCount = bmih.biBitCount;
            switch (wBitCount) {
                case 16:
                case 32:
                    sizBMI = sizeof(BITMAPINFOHEADER)+sizeof(DWORD)*3;
                    break;
                case 24:
                    sizBMI = sizeof(BITMAPINFOHEADER);
                    break;
                default:
                    iNumClr = (1 << wBitCount);
                    sizBMI = sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*iNumClr;
                    break;
            }
            bCoreHdr = FALSE;
        }
    }

    if ((pbmi = (LPBITMAPINFO) LocalAlloc(LMEM_FIXED,sizBMI)) == NULL) {
        MessageBox(ghwndMain, "Fail in Memory Allocation!", "Error", MB_OK);
        bSuccess = FALSE;
        goto ErrExit3;
    }

    //
    // Make sure we pass in a DWORD aligned BitmapInfo to CreateDIBitmap
    // Otherwise, exception on the MIPS platform
    // CR!!!  Equivalent to memcpy
    //
    pjTmp = (PBYTE)pbmi;

    while(sizBMI--)
    {
        *(((PBYTE)pjTmp)++) = *(((PBYTE)pbmh)++);
    }

    //
    // assuming CreateDIBitmap() is doing a byte fetch...
    //
    pjDIBits = (PBYTE)pMapFile + dwOffBits;

    if (pbmi->bmiHeader.biCompression == BI_RGB)
    {
    //
    // Converting to RLE bits...
    //

        MessageBox(ghwndMain, "Converting to RLE format!", "Store", MB_OK);
        lScan = pbmi->bmiHeader.biHeight;
        sizImage = pbmi->bmiHeader.biSizeImage;

        //
        // select the palette into the DC first...
        //
        bSelectDIBPal(hDC, pInfo, pbmi, bCoreHdr);
        if ((pInfo->hBmpSaved = CreateDIBitmap(hDC, (LPBITMAPINFOHEADER)pbmi,
                            CBM_INIT, pjDIBits, pbmi, DIB_RGB_COLORS)) == NULL) {
            ErrorOut("Fail in creating DIB bitmap from file!");
            bSuccess = FALSE;
            goto ErrExit4;
        }

        // We want to retrieve the RLE format...
        pbmi->bmiHeader.biCompression = ((wBitCount==4) ? BI_RLE4 : BI_RLE8);

        pbmi->bmiHeader.biSizeImage = 0;
        pbmi->bmiHeader.biXPelsPerMeter = 0;
        pbmi->bmiHeader.biYPelsPerMeter = 0;
        pbmi->bmiHeader.biClrUsed = 0;
        pbmi->bmiHeader.biClrImportant = 0;

        if (GetDIBits(hDC, pInfo->hBmpSaved, 0, lScan, NULL, pbmi,
            DIB_RGB_COLORS) == 0) {
             MessageBox(ghwndMain, "Not all scans are converted!", "Error", MB_OK);
             bSuccess = FALSE;
             goto ErrExit4;
        }

        if (pbmi->bmiHeader.biSizeImage == 0) {
            if (sizImage == 0) {
                MessageBox(ghwndMain, "biSizeImage == 0!", "Error", MB_OK);
                bSuccess = FALSE;
                goto ErrExit4;
            }
            MessageBox(ghwndMain,
                "Engine returns zero image size, making one up!",
                "Error", MB_OK);
            sizImage = (sizImage *3)/2;
        } else {
            sizImage = pbmi->bmiHeader.biSizeImage;
        }

        if ((pjRleBits = (PBYTE) LocalAlloc(LMEM_FIXED, sizImage)) == NULL) {
             MessageBox(ghwndMain, "Fail in Memory Allocation!", "Error", MB_OK);
             bSuccess = FALSE;
             goto ErrExit4;
        }

        pbmi->bmiHeader.biSizeImage = sizImage;
        if (GetDIBits(hDC, pInfo->hBmpSaved, 0, lScan, pjRleBits, pbmi,
            DIB_RGB_COLORS) < lScan) {
             MessageBox(ghwndMain, "Not all scans are converted!", "Error", MB_OK);
             bSuccess = FALSE;
             goto ErrExit5;
        }

        SetDIBitsToDevice(hDC, 0, 0, pbmi->bmiHeader.biWidth, pbmi->bmiHeader.biHeight,
                      0, 0, 0, lScan, pjRleBits, pbmi, DIB_RGB_COLORS);

        pInfo->RleData.rgpjFrame[ulFrames] = pjRleBits;
    } else {
        bSelectDIBPal(hDC, pInfo, pbmi, bCoreHdr);
        lScan = pbmi->bmiHeader.biHeight;
        if (bCoreHdr) {
           lScan = ((LPBITMAPCOREINFO)pbmi)->bmciHeader.bcHeight;
           SetDIBitsToDevice(hDC, 0, 0, ((LPBITMAPCOREINFO)pbmi)->bmciHeader.bcWidth, ((LPBITMAPCOREINFO)pbmi)->bmciHeader.bcHeight,
                      0, 0, 0, lScan, pjDIBits, pbmi, DIB_RGB_COLORS);
        } else {
           lScan = pbmi->bmiHeader.biHeight;
           SetDIBitsToDevice(hDC, 0, 0, pbmi->bmiHeader.biWidth, pbmi->bmiHeader.biHeight,
                      0, 0, 0, lScan, pjDIBits, pbmi, DIB_RGB_COLORS);
        }
        pInfo->RleData.rgpjFrame[ulFrames] = pjDIBits;
    }

    pInfo->RleData.ulSize[ulFrames] = sizImage;
    pInfo->RleData.rgpbmi[ulFrames] = pbmi;

    //
    // Go to next frame
    //
    pMapFile = (PBYTE)pMapFile + ulfSize;
    ulOffset += ulfSize;
    ulFrames++;

    //
    // end condition
    //
    if (ulOffset >= dwFileSizeLow) {
        break;
    }

  }

    //
    // set pbmi to the very first bmi
    //
    pInfo->RleData.pbmi = pInfo->RleData.rgpbmi[0];

    pInfo->RleData.hPal = pInfo->hPal;
    pInfo->RleData.ulFrames = ulFrames;
    return (bSuccess);

ErrExit5:
    LocalFree(pjRleBits);
ErrExit4:
    LocalFree(pbmi);
ErrExit3:
    CloseHandle(pFileInfo->hMapFile);
ErrExit2:
    CloseHandle(pFileInfo->hFile);
ErrExit1:
    return (bSuccess);

}

/******************************Public*Routine******************************\
*
* bSelectDIBPal
*
* Effects: Creates a logical palette from the DIB and select it into the DC
*          and realize the palette. Saving the hPal in the pInfo->hPal
*
* Warnings: Based on Windows NT DIB support.  If PM support 16,24,32 bpp
*           we need to modify this routine.
*
* History:
*  22-Jan-1993      Petrus Wong         PM support
*  31-Dec-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bSelectDIBPal(HDC hDC, PINFO pInfo, LPBITMAPINFO pbmi, BOOL bCoreHdr)
{
  LOGPALETTE    *plogPal;
  UINT          uiSizPal;
  INT           i, iNumClr;
  WORD          wBitCount;

  if (bCoreHdr) {
    wBitCount = ((LPBITMAPCOREINFO)pbmi)->bmciHeader.bcBitCount;
  } else {
    wBitCount = pbmi->bmiHeader.biBitCount;
  }

  switch (wBitCount) {
    case 16:
    case 24:
    case 32:                            // Does PM supports these?
        return FALSE;
    default:
        iNumClr = (1 << wBitCount);
        break;
  }

  uiSizPal = sizeof(WORD)*2 + sizeof(PALETTEENTRY)*iNumClr;
  if ((plogPal = (LOGPALETTE *) LocalAlloc(LMEM_FIXED,uiSizPal)) == NULL) {
      ErrorOut("Fail in Allocating palette!");
      pInfo->hPal = NULL;
      return FALSE;
  }

  plogPal->palVersion = 0x300;
  plogPal->palNumEntries = (WORD) iNumClr;

  if (bCoreHdr) {
    for (i=0; i<iNumClr; i++) {
        plogPal->palPalEntry[i].peRed   = ((LPBITMAPCOREINFO)pbmi)->bmciColors[i].rgbtRed;
        plogPal->palPalEntry[i].peGreen = ((LPBITMAPCOREINFO)pbmi)->bmciColors[i].rgbtGreen;
        plogPal->palPalEntry[i].peBlue  = ((LPBITMAPCOREINFO)pbmi)->bmciColors[i].rgbtBlue;
        plogPal->palPalEntry[i].peFlags = PC_RESERVED;
    }
  } else {
    for (i=0; i<iNumClr; i++) {
        plogPal->palPalEntry[i].peRed   = pbmi->bmiColors[i].rgbRed;
        plogPal->palPalEntry[i].peGreen = pbmi->bmiColors[i].rgbGreen;
        plogPal->palPalEntry[i].peBlue  = pbmi->bmiColors[i].rgbBlue;
        plogPal->palPalEntry[i].peFlags = PC_RESERVED;
    }
  }

  DeleteObject(pInfo->hPal);
  pInfo->hPal = CreatePalette((LPLOGPALETTE)plogPal);
  if ((pInfo->hPal) == NULL) {
      ErrorOut("Fail in creating palette!");
      return FALSE;
  }

  if ((GetDeviceCaps(hDC, RASTERCAPS)) & RC_PALETTE) {
    SelectPalette(hDC, pInfo->hPal, FALSE);
    RealizePalette(hDC);
  }

  GlobalFree(plogPal);

  return TRUE;
}

/******************************Public*Routine******************************\
*
* bFreeRleFile
*
* Effects:
*
* Warnings:
*
* History:
*  05-Dec-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bFreeRleFile(PINFO pInfo)
{
    ULONG               ulFiles;
    ULONG               ulFrames;
    ULONG               i;
    PFILEINFO           pFileInfo;

    ulFiles = pInfo->RleData.ulFiles;
    ulFrames = pInfo->RleData.ulFrames;

    for (i = 0; i < ulFrames; i++) {
        LocalFree(pInfo->RleData.rgpjFrame[i]);
        LocalFree(pInfo->RleData.rgpbmi[i]);
    }

    for (i = 0; i < ulFiles; i++) {
        pFileInfo = &(pInfo->RleData.rgFileInfo[i]);
        CloseHandle(pFileInfo->hFile);
        CloseHandle(pFileInfo->hMapFile);
        UnmapViewOfFile(pFileInfo->lpvMapView);
    }

    pInfo->RleData.ulFiles = 0;
    pInfo->RleData.ulFrames = 0;
    return TRUE;
}

/******************************Public*Routine******************************\
*
* bPlayRle
*
* Effects:
*
* Warnings:
*
* History:
*  05-Dec-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bPlayRle(PINFO pInfo)
{
    ULONG               ulFrames;
    ULONG               i;
    LPBITMAPINFO        pbmi;
    HDC                 hDC;
    HWND                hViewSurf;
    int                 ii;

//    RECT                rc;

    hDC = GetDC(hViewSurf=pInfo->hwnd);

    SelectPalette(hDC, pInfo->RleData.hPal, FALSE);
    ii=RealizePalette(hDC);
    if (ii){
        UpdateColors (hDC);
    }

    ulFrames = pInfo->RleData.ulFrames;

    for (i = 0; i < ulFrames; i++) {
        pbmi = pInfo->RleData.rgpbmi[i];
        SetDIBitsToDevice(hDC,
                  0, 0, pbmi->bmiHeader.biWidth, pbmi->bmiHeader.biHeight,
                  0, 0, 0, pbmi->bmiHeader.biHeight,
                  pInfo->RleData.rgpjFrame[i], pbmi, DIB_RGB_COLORS);

#if 0
        GetClientRect(pInfo->hwnd, &rc);
        StretchDIBits(hDC,
               0, 0, rc.right, rc.bottom,
               0, 0, pbmi->bmiHeader.biWidth, pbmi->bmiHeader.biHeight,
               (LPSTR)pInfo->RleData.rgpjFrame[i], pbmi, DIB_RGB_COLORS,
               SRCCOPY);
#endif
    }

    ReleaseDC(hViewSurf, hDC);

    return TRUE;
}


/******************************Public*Routine******************************\
*
* bSaveRleFile
*
* Effects:
*
* Warnings:
*
* History:
*  14-Dec-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bSaveRleFile(HDC hDC, PINFO pInfo, PSTR pszFileName)
{
    int                 hFile;
    OFSTRUCT            ofReOpenBuff;
    BOOL                bSuccess;
    BITMAPFILEHEADER    bfh;
    ULONG               ulFrames;
    ULONG               i;
    LPBITMAPINFO        pbmi;
    ULONG               ulNumClr;
    ULONG               ulSize;

    bSuccess = TRUE;
    ulFrames = pInfo->RleData.ulFrames;

    if (ulFrames == 0) {
        MessageBox(GetFocus(), "There's no RLE to save!", "Error", MB_OK);
        return FALSE;
    }

    // Let's open the file and get ready for writing
    if ((hFile = OpenFile(pszFileName, (LPOFSTRUCT)&ofReOpenBuff,
                 OF_CREATE | OF_WRITE)) == -1) {
        MessageBox(GetFocus(), "Failed in OpenFile!", "Error", MB_OK);
        ErrorOut("OpenFile");
        return FALSE;
    }

    for (i = 0; i < ulFrames; i++) {

        pbmi = pInfo->RleData.rgpbmi[i];

        switch(pbmi->bmiHeader.biBitCount) {
            case 24:
                ulSize = 0;
                break;
            case 16:
            case 32:
                ulSize = sizeof(DWORD)*3;
                break;
            default:
                ulNumClr = 1<<pbmi->bmiHeader.biBitCount;
                ulSize = sizeof(RGBQUAD)*ulNumClr;
                break;
        }
#if 0
        ulNumClr = ((pbmi->bmiHeader.biBitCount == 24)
                       ? 0
                       : (1<<pbmi->bmiHeader.biBitCount));
#endif
        // fill in the info for the BitmapFileHeader
        bfh.bfType    = 0x4D42;                            // 'BM'
        bfh.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+
                        ulSize;
                        //sizeof(RGBQUAD)*ulNumClr;
        bfh.bfSize    = bfh.bfOffBits + pbmi->bmiHeader.biSizeImage;
        bfh.bfReserved1 =
        bfh.bfReserved2 = 0;

        // Write out the file header now
        if (_lwrite(hFile, (LPSTR)&bfh, sizeof(BITMAPFILEHEADER)) == -1) {
            MessageBox(GetFocus(), "Failed in Writing File Header!",
                        "Error", MB_OK);
            bSuccess = FALSE;
            goto ErrExit1;
        }

        // Now write out the BitmapInfoHeader and color table, if any
        if (_lwrite(hFile, (LPSTR)pbmi, sizeof(BITMAPINFOHEADER) +
                                        ulSize) == -1) {
                                        //sizeof(RGBQUAD)*ulNumClr) == -1) {
            MessageBox(GetFocus(), "Failed in Writing Bitmap Info!",
                        "Error", MB_OK);
            bSuccess = FALSE;
            goto ErrExit1;
        }

        // write the bits also
        if (_lwrite(hFile, (LPSTR)pInfo->RleData.rgpjFrame[i],
                           pbmi->bmiHeader.biSizeImage) == -1) {
            MessageBox(GetFocus(), "Failed in Writing RLE bits!",
                        "Error", MB_OK);
            bSuccess = FALSE;
            goto ErrExit1;
        }

    }

ErrExit1:
    _lclose(hFile);
    return bSuccess;
}


/******************************Public*Routine******************************\
*
* bPlayRleCont
*
* Effects:
*
* Warnings:
*
* History:
*  14-Dec-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bPlayRleCont(HDC hDC, PINFO pInfo)
{
    BOOL    bQuit;
    MSG     msg;
    int     ii;

    bQuit = FALSE;

    SelectPalette(hDC, pInfo->RleData.hPal, FALSE);
    ii=RealizePalette(hDC);
    if (ii){
        UpdateColors (hDC);
    }

    while (TRUE && !bQuit) {

        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {

            if ((msg.message == WM_QUIT) || (msg.message == WM_CLOSE) ||
                ((msg.message == WM_SYSCOMMAND) && (msg.wParam == SC_CLOSE))) {
                bQuit = TRUE;
                PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam);
            } else {
                if (!TranslateAccelerator(msg.hwnd, ghAccel, &msg)) {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
           }
        }

        if (!pInfo->bPlayRleCont) {
            bQuit = TRUE;
        }

        if (!bQuit) {
            bPlayRle(pInfo);
        }

    }
    return TRUE;
}


/******************************Public*Routine******************************\
*
* bPlayRleCont2
*
* Effects:
*
* Warnings:
*
* History:
*  14-Dec-1992 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

BOOL bPlayRleCont2(PINFO pInfo)
{
    HDC             hDC;
    HWND            hViewSurf;
    DWORD           dwWait;
    BOOL            bQuit;
    int             ii;

    hDC = GetDC(hViewSurf=pInfo->hwnd);

    SelectPalette(hDC, pInfo->RleData.hPal, FALSE);
    ii=RealizePalette(hDC);
    if (ii){
        UpdateColors (hDC);
    }

    bQuit = FALSE;
    while (TRUE) {
        //
        // If parent gets a WM_CLOSE, we will return
        //
        dwWait = WaitForSingleObject(pInfo->hQuitEvent, 0);
        if (dwWait == WAIT_TIMEOUT) {
            MessageBox(ghwndMain,
                "Continuous Play Thread Quitting!",
                "Continuous Play Thread", MB_OK);
            break;
        }

        if (!pInfo->bPlayRleCont) {
            bQuit = TRUE;
        }

        if (!bQuit)
            bPlayRle(pInfo);
    }

    ReleaseDC(hViewSurf, hDC);

    ExitThread(0);
    return TRUE;

}


/******************************Public*Routine******************************\
*
* DIBfromDDB
*
* Effects:  Call GetDIBits to retrieve the DIB info from DDB
*
* Warnings:
*
* History:
*  17-Jun-1993 -by- Petrus Wong
* Wrote it.
\**************************************************************************/

HBITMAP DIBfromDDB(HDC hDC, HBITMAP hBmp, PINFO pInfo)
{
    LPBITMAPINFO        pbmi;
    HBITMAP             hDIB;
    INT                 iBitCount, iNumClr;
    BITMAP              bm;
    DWORD               sizImage;
    PBYTE               pjBits;
    ULONG               sizBMI;
    LONG                lScan;

    iBitCount = GetDeviceCaps(hDC, BITSPIXEL);
    switch (iBitCount) {
        case 16:
        case 32:
            sizBMI = sizeof(BITMAPINFOHEADER)+sizeof(DWORD)*3;
            break;
        case 24:
            sizBMI = sizeof(BITMAPINFOHEADER);
            break;
        default:
            iNumClr = (1 << iBitCount);
            sizBMI = sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*iNumClr;
            break;
    }

    if ((pbmi = (LPBITMAPINFO) LocalAlloc(LMEM_FIXED,sizBMI)) == NULL) {
        MessageBox(ghwndMain, "Fail in Memory Allocation!", "Error", MB_OK);
        goto ErrExit1;
    }

    pbmi->bmiHeader.biSize = 0x28;              // GDI need this to work
    pbmi->bmiHeader.biBitCount = 0;             // don't get the color table
    pbmi->bmiHeader.biCompression = BI_RGB;
    pbmi->bmiHeader.biSizeImage = 0;
    pbmi->bmiHeader.biXPelsPerMeter = 0;
    pbmi->bmiHeader.biYPelsPerMeter = 0;
    pbmi->bmiHeader.biClrUsed = 0;
    pbmi->bmiHeader.biClrImportant = 0;

    GetObject(hBmp, sizeof(BITMAP), &bm);

    //
    // Important!  Select the correct palette corresponding to the DDB
    //
    SelectPalette(hDC, pInfo->hPal, FALSE);
    if (GetDIBits(hDC, hBmp, 0, bm.bmHeight, NULL, pbmi,
        DIB_RGB_COLORS) == 0) {
         MessageBox(ghwndMain, "Not all scans are returned!", "Error", MB_OK);
         goto ErrExit2;
    }

    sizImage = pbmi->bmiHeader.biSizeImage;
    if (sizImage == 0) {
        MessageBox(ghwndMain, "biSizeImage == 0!", "Error", MB_OK);
        goto ErrExit2;
    }

    if ((pjBits = (PBYTE) LocalAlloc(LMEM_FIXED, sizImage)) == NULL) {
         MessageBox(ghwndMain, "Fail in Memory Allocation!", "Error", MB_OK);
         goto ErrExit2;
    }

    lScan = pbmi->bmiHeader.biHeight;

    if (GetDIBits(hDC, pInfo->hBmpSaved, 0, lScan, pjBits, pbmi,
        DIB_RGB_COLORS) < lScan) {
         MessageBox(ghwndMain, "Not all scans are returned!", "Error", MB_OK);
         goto ErrExit3;
    }

    //
    // Saving the DIB...free memory when the windows is closed.
    //
    pInfo->RleData.rgpjFrame[0] = pjBits;
    pInfo->RleData.rgpbmi[0]    = pbmi;
    pInfo->RleData.pbmi         = (PBITMAPINFO) &(pInfo->RleData.rgpbmi[0]);
    pInfo->RleData.ulFrames     = 1;
    pInfo->RleData.ulFiles      = 1;

    // set flag to use original DIB as source for blting so HT can be done
    pInfo->bUseDIB = TRUE;

    pInfo->bCoreHdr = FALSE;

    // hDIB = CreateDIBitmap(hDC, NULL, CBM_INIT, pjBits, pbmi, DIB_RGB_COLORS);
    hDIB = CreateDIBSection(hDC, pbmi, DIB_RGB_COLORS, &pjBits, NULL, 0);

    return hDIB;

ErrExit3:
    LocalFree(pjBits);
ErrExit2:
    LocalFree(pbmi);
ErrExit1:
    return ((HBITMAP)NULL);
}
