/*---------------------------------------------------------------- PMMANDEL.C -- OS/2 Presentation Manager Mandelbrot Set Program (c) Charles Petzold, 1990 Compile using Microsoft C 6.0 for OS/2 1.2 ----------------------------------------------------------------*/ #define INCL_DOS #define INCL_WIN #define INCL_GPI #include #include #include #include #include #include "pmmandel.h" #define STACKSIZE (4096 * sizeof (int)) #define DEF_MAX_ITERATIONS 500 #define WM_IMAGE_COMPLETED (WM_USER + 1) typedef struct { double dxMin, dxMax, dyMin, dyMax ; } COORDDLGSTRUC ; typedef COORDDLGSTRUC *PCOORDDLGSTRUC ; typedef struct { BOOL fEnable16, fEnable256 ; SHORT sNumColors ; } COLORDLGSTRUC ; typedef COLORDLGSTRUC *PCOLORDLGSTRUC ; typedef struct { COORDDLGSTRUC cords ; COLORDLGSTRUC clrds ; BOOL fRestart, fTerminate ; HPS hpsClient, hpsMemory ; HWND hwndClient ; SHORT cxMaxClient, cyMaxClient, cxClient, cyClient, cyChar ; ULONG semTriggerDraw, semDoingDraw ; USHORT nMaxIterations ; } THREADPARAMS ; typedef THREADPARAMS *PTHREADPARAMS ; MRESULT EXPENTRY ClientWndProc (HWND, USHORT, MPARAM, MPARAM) ; MRESULT EXPENTRY CoordDlgProc (HWND, USHORT, MPARAM, MPARAM) ; MRESULT EXPENTRY ColorDlgProc (HWND, USHORT, MPARAM, MPARAM) ; MRESULT EXPENTRY IterDlgProc (HWND, USHORT, MPARAM, MPARAM) ; MRESULT EXPENTRY AboutDlgProc (HWND, USHORT, MPARAM, MPARAM) ; VOID InvertBlock (HPS, POINTL, POINTL) ; VOID CreateIcon (HPS) ; VOID CalcThread (PTHREADPARAMS) ; USHORT Mandelbrot (struct complex, USHORT) ; CHAR szMessageTitle [] = "PM Mandelbrot Set" ; HAB hab ; int main (void) { static char szClientClass [] = "PMMandel" ; static ULONG flFrameFlags = FCF_TITLEBAR | FCF_SYSMENU | FCF_SIZEBORDER | FCF_MINMAX | FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON | FCF_MENU ; HMQ hmq ; HWND hwndClient, hwndFrame ; QMSG qmsg ; hab = WinInitialize (0) ; hmq = WinCreateMsgQueue (hab, 0) ; WinRegisterClass (hab, szClientClass, ClientWndProc, CS_SIZEREDRAW, 0) ; hwndFrame = WinCreateStdWindow (HWND_DESKTOP, WS_VISIBLE, &flFrameFlags, szClientClass, NULL, 0L, 0, ID_RESOURCE, &hwndClient) ; if (hwndFrame != NULL) { while (WinGetMsg (hab, &qmsg, NULL, 0, 0)) WinDispatchMsg (hab, &qmsg) ; WinDestroyWindow (hwndFrame) ; } WinDestroyMsgQueue (hmq) ; WinTerminate (hab) ; return 0 ; } MRESULT EXPENTRY ClientWndProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2) { static BOOL fBlocking ; static CHAR szBuffer [80] ; static CHAR szCoordFormat[] = " Real: %#11.8f, %#11.8f " " Img: %#11.8f, %#11.8f " ; static HBITMAP hbm ; static HDC hdcClient, hdcMemory ; static int iThreadStack [STACKSIZE / sizeof (int)] ; static POINTL ptlBeg, ptlEnd ; static SHORT cyDesc, xMousePosBegin ; static THREADPARAMS tp = { -2.25, 1.75, -1.5, 1.5 } ; static TID tid ; BITMAPINFOHEADER bmp ; double dTemp ; LONG lNumColors, alBitmapFormats[2] ; POINTL ptl, aptl[TXTBOX_COUNT] ; SIZEL sizl ; switch (msg) { case WM_CREATE: // Create presention space for window hdcClient = WinOpenWindowDC (hwnd) ; sizl.cx = 0 ; sizl.cy = 0 ; tp.hpsClient = GpiCreatePS (hab, hdcClient, &sizl, PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC) ; // Get font and text dimensions for status line GpiQueryTextBox (tp.hpsClient, (LONG) sprintf (szBuffer, szCoordFormat, 0.0, 0.0, 0.0, 0.0), szBuffer, TXTBOX_COUNT, aptl) ; xMousePosBegin = (SHORT) aptl[TXTBOX_CONCAT].x ; cyDesc = - (SHORT) aptl[TXTBOX_BOTTOMLEFT].y ; tp.cyChar = (SHORT) (aptl[TXTBOX_TOPLEFT].y + cyDesc) ; // Create memory DC and PS for bitmap hdcMemory = DevOpenDC (hab, OD_MEMORY, "*", 0L, NULL, NULL) ; sizl.cx = 0 ; sizl.cy = 0 ; tp.hpsMemory = GpiCreatePS (hab, hdcMemory, &sizl, PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC) ; // Determine size and format of shadow bitmap tp.cxMaxClient = (SHORT) WinQuerySysValue (HWND_DESKTOP, SV_CXFULLSCREEN) ; tp.cyMaxClient = (SHORT) (WinQuerySysValue (HWND_DESKTOP, SV_CYFULLSCREEN) - WinQuerySysValue (HWND_DESKTOP, SV_CYMENU)) - tp.cyChar ; GpiQueryDeviceBitmapFormats (tp.hpsClient, 2L, alBitmapFormats); // Create shadow bitmap for storing window image // and set it in memory presentation space bmp.cbFix = sizeof bmp ; bmp.cx = tp.cxMaxClient ; bmp.cy = tp.cyMaxClient ; bmp.cPlanes = (SHORT) alBitmapFormats[0] ; bmp.cBitCount = (SHORT) alBitmapFormats[1] ; hbm = GpiCreateBitmap (tp.hpsMemory, &bmp, 0L, NULL, NULL) ; if (hbm == NULL) { WinMessageBox (HWND_DESKTOP, hwnd, "Could not create bitmap for storing image.", szMessageTitle, 0, MB_OK | MB_MOVEABLE | MB_ICONEXCLAMATION) ; return MRFROMSHORT (1) ; } GpiSetBitmap (tp.hpsMemory, hbm) ; GpiErase (tp.hpsMemory) ; // Determine color capability of video display // and use them to set color dialog structure DevQueryCaps (hdcClient, CAPS_COLORS, 1L, &lNumColors) ; if (lNumColors == 2) { tp.clrds.fEnable16 = FALSE ; tp.clrds.fEnable256 = FALSE ; tp.clrds.sNumColors = 2 ; } else if (lNumColors >= 16 && lNumColors < 256) { tp.clrds.fEnable16 = TRUE ; tp.clrds.fEnable256 = FALSE ; tp.clrds.sNumColors = 16 ; } else { tp.clrds.fEnable16 = TRUE ; tp.clrds.fEnable256 = TRUE ; tp.clrds.sNumColors = 256 ; } // Initialize structure fields for thread tp.fRestart = TRUE ; tp.fTerminate = FALSE ; tp.nMaxIterations = DEF_MAX_ITERATIONS ; tp.hwndClient = hwnd ; DosSemSet (&tp.semDoingDraw) ; DosSemClear (&tp.semTriggerDraw) ; // Start second thread tid = _beginthread (CalcThread, iThreadStack, STACKSIZE, &tp) ; if (tid == -1) { WinMessageBox (HWND_DESKTOP, hwnd, "Could not create second thread.", szMessageTitle, 0, MB_OK | MB_MOVEABLE | MB_ICONEXCLAMATION) ; return MRFROMSHORT (1) ; } return 0 ; case WM_SIZE: // Save new size of client window DosEnterCritSec () ; tp.cxClient = SHORT1FROMMP (mp2) ; tp.cyClient = SHORT2FROMMP (mp2) ; DosExitCritSec () ; return 0 ; case WM_COMMAND: switch (COMMANDMSG(&msg)->cmd) { case IDM_COORDINATES: DosEnterCritSec () ; if (WinDlgBox (HWND_DESKTOP, hwnd, CoordDlgProc, 0, IDD_COORDINATES, &tp.cords)) { tp.fRestart = TRUE ; DosSemClear (&tp.semTriggerDraw) ; WinInvalidateRect (hwnd, NULL, FALSE) ; } DosExitCritSec () ; return 0 ; case IDM_COLORS: DosEnterCritSec () ; if (WinDlgBox (HWND_DESKTOP, hwnd, ColorDlgProc, 0, IDD_COLORS, &tp.clrds)) { tp.fRestart = TRUE ; DosSemClear (&tp.semTriggerDraw) ; WinInvalidateRect (hwnd, NULL, FALSE) ; } DosExitCritSec () ; return 0 ; case IDM_ITERATIONS: DosEnterCritSec () ; if (WinDlgBox (HWND_DESKTOP, hwnd, IterDlgProc, 0, IDD_ITERATIONS, &tp.nMaxIterations)) { tp.fRestart = TRUE ; DosSemClear (&tp.semTriggerDraw) ; WinInvalidateRect (hwnd, NULL, FALSE) ; } DosExitCritSec () ; return 0 ; case IDM_ABOUT: WinDlgBox (HWND_DESKTOP, hwnd, AboutDlgProc, 0, IDD_ABOUT, NULL) ; return 0 ; } break ; case WM_BUTTON1DBLCLK: if (!fBlocking) { DosEnterCritSec () ; fBlocking = TRUE ; WinSetCapture (HWND_DESKTOP, hwnd) ; ptlBeg.x = ptlEnd.x = MOUSEMSG(&msg)->x ; ptlBeg.y = ptlEnd.y = MOUSEMSG(&msg)->y ; } break ; case WM_MOUSEMOVE: // If blocking, erase prev block and invert block if (fBlocking) { InvertBlock (tp.hpsClient, ptlBeg, ptlEnd) ; ptlEnd.x = MOUSEMSG(&msg)->x ; ptlEnd.y = MOUSEMSG(&msg)->y ; InvertBlock (tp.hpsClient, ptlBeg, ptlEnd) ; } else DosEnterCritSec () ; // Display mouse coordinates in status line GpiSetBackMix (tp.hpsClient, BM_OVERPAINT) ; ptl.x = xMousePosBegin ; ptl.y = tp.cyClient - tp.cyChar + cyDesc ; GpiCharStringAt (tp.hpsClient, &ptl, (LONG) sprintf (szBuffer, "Mou: %#11.8f, %#11.8f", tp.cords.dxMin + MOUSEMSG(&msg)->x * (tp.cords.dxMax - tp.cords.dxMin) / tp.cxMaxClient, tp.cords.dyMin + MOUSEMSG(&msg)->y * (tp.cords.dyMax - tp.cords.dyMin) / tp.cyMaxClient), szBuffer) ; if (!fBlocking) DosExitCritSec () ; break ; case WM_BUTTON1DOWN: if (fBlocking) { fBlocking = FALSE ; WinSetCapture (HWND_DESKTOP, NULL) ; ptlEnd.x = MOUSEMSG(&msg)->x ; ptlEnd.y = MOUSEMSG(&msg)->y ; InvertBlock (tp.hpsClient, ptlBeg, ptlEnd) ; // calculate new coordinates dTemp = tp.cords.dxMin + min (ptlBeg.x, ptlEnd.x) * (tp.cords.dxMax - tp.cords.dxMin) / tp.cxMaxClient ; tp.cords.dxMax = tp.cords.dxMin + max (ptlBeg.x, ptlEnd.x) * (tp.cords.dxMax - tp.cords.dxMin) / tp.cxMaxClient ; tp.cords.dxMin = dTemp ; dTemp = tp.cords.dyMin + min (ptlBeg.y, ptlEnd.y) * (tp.cords.dyMax - tp.cords.dyMin) / tp.cyMaxClient ; tp.cords.dyMax = tp.cords.dyMin + max (ptlBeg.y, ptlEnd.y) * (tp.cords.dyMax - tp.cords.dyMin) / tp.cyMaxClient ; tp.cords.dyMin = dTemp ; // restart the calculation tp.fRestart = TRUE ; DosSemClear (&tp.semTriggerDraw) ; WinInvalidateRect (hwnd, NULL, FALSE) ; DosExitCritSec () ; } break ; case WM_CHAR: // stop the blocking if (fBlocking) { fBlocking = FALSE ; InvertBlock (tp.hpsClient, ptlBeg, ptlEnd) ; WinSetCapture (HWND_DESKTOP, NULL) ; DosExitCritSec () ; } return 0 ; case WM_IMAGE_COMPLETED: WinMessageBox (HWND_DESKTOP, hwnd, "The image is completed.", szMessageTitle, 0, MB_OK | MB_MOVEABLE | MB_ICONASTERISK) ; return 0 ; case WM_PAINT: DosEnterCritSec () ; WinBeginPaint (hwnd, tp.hpsClient, NULL) ; GpiErase (tp.hpsClient) ; // Copy bitmap to client window aptl[0].x = 0 ; aptl[0].y = 0 ; aptl[1].x = tp.cxClient ; aptl[1].y = tp.cyClient - tp.cyChar ; aptl[2].x = 0 ; aptl[2].y = 0 ; GpiBitBlt (tp.hpsClient, tp.hpsMemory, 3L, aptl, ROP_SRCCOPY, BBO_IGNORE) ; // Display status line ptl.x = 0 ; ptl.y = tp.cyClient - tp.cyChar + cyDesc ; GpiCharStringAt (tp.hpsClient, &ptl, (LONG) sprintf (szBuffer, szCoordFormat, tp.cords.dxMin, tp.cords.dxMax, tp.cords.dyMin, tp.cords.dyMax), szBuffer) ; WinEndPaint (tp.hpsClient) ; DosExitCritSec () ; return 0 ; case WM_DESTROY: tp.fTerminate = TRUE ; tp.fRestart = TRUE ; DosSemClear (&tp.semTriggerDraw) ; DosSemWait (&tp.semDoingDraw, SEM_INDEFINITE_WAIT) ; GpiSetBitmap (tp.hpsMemory, NULL) ; GpiDestroyPS (tp.hpsClient) ; GpiDestroyPS (tp.hpsMemory) ; DevCloseDC (hdcMemory) ; GpiDeleteBitmap (hbm) ; return 0 ; } return WinDefWindowProc (hwnd, msg, mp1, mp2) ; } MRESULT EXPENTRY CoordDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2) { static CHAR szBuffer [40] ; static PCOORDDLGSTRUC pcords ; switch (msg) { case WM_INITDLG: pcords = (PCOORDDLGSTRUC) PVOIDFROMMP (mp2) ; sprintf (szBuffer, "%10.7f", pcords->dxMin) ; WinSetDlgItemText (hwnd, IDD_XMIN, szBuffer) ; sprintf (szBuffer, "%10.7f", pcords->dxMax) ; WinSetDlgItemText (hwnd, IDD_XMAX, szBuffer) ; sprintf (szBuffer, "%10.7f", pcords->dyMin) ; WinSetDlgItemText (hwnd, IDD_YMIN, szBuffer) ; sprintf (szBuffer, "%10.7f", pcords->dyMax) ; WinSetDlgItemText (hwnd, IDD_YMAX, szBuffer) ; return 0 ; case WM_COMMAND: switch (SHORT1FROMMP (mp1)) { case DID_OK: WinQueryDlgItemText (hwnd, IDD_XMIN, sizeof szBuffer, szBuffer) ; pcords->dxMin = atof (szBuffer) ; WinQueryDlgItemText (hwnd, IDD_XMAX, sizeof szBuffer, szBuffer) ; pcords->dxMax = atof (szBuffer) ; WinQueryDlgItemText (hwnd, IDD_YMIN, sizeof szBuffer, szBuffer) ; pcords->dyMin = atof (szBuffer) ; WinQueryDlgItemText (hwnd, IDD_YMAX, sizeof szBuffer, szBuffer) ; pcords->dyMax = atof (szBuffer) ; WinDismissDlg (hwnd, TRUE) ; return 0 ; case DID_CANCEL: WinDismissDlg (hwnd, FALSE) ; return 0 ; } break ; } return WinDefDlgProc (hwnd, msg, mp1, mp2) ; } MRESULT EXPENTRY ColorDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2) { static PCOLORDLGSTRUC pclrds ; static SHORT sNumColors, sID ; switch (msg) { case WM_INITDLG: pclrds = (PCOLORDLGSTRUC) PVOIDFROMMP (mp2) ; if (!pclrds->fEnable16) WinEnableWindow (WinWindowFromID (hwnd, IDD_COLOR16), FALSE) ; if (!pclrds->fEnable256) WinEnableWindow (WinWindowFromID (hwnd, IDD_COLOR256), FALSE) ; sNumColors = pclrds->sNumColors ; if (sNumColors == 2) sID = IDD_COLOR2 ; else if (sNumColors == 16) sID = IDD_COLOR16 ; else if (sNumColors == 256) sID = IDD_COLOR256 ; WinSendDlgItemMsg (hwnd, sID, BM_SETCHECK, MPFROM2SHORT (TRUE, 0), NULL) ; WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, sID)) ; return MRFROMSHORT (1) ; case WM_CONTROL: switch (SHORT1FROMMP (mp1)) { case IDD_COLOR2: sNumColors = 2 ; break ; case IDD_COLOR16: sNumColors = 16 ; break ; case IDD_COLOR256: sNumColors = 256 ; break ; } return 0 ; case WM_COMMAND: switch (SHORT1FROMMP (mp1)) { case DID_OK: pclrds->sNumColors = sNumColors ; WinDismissDlg (hwnd, TRUE) ; return 0 ; case DID_CANCEL: WinDismissDlg (hwnd, FALSE) ; return 0 ; } break ; } return WinDefDlgProc (hwnd, msg, mp1, mp2) ; } MRESULT EXPENTRY IterDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2) { static CHAR szBuffer [10] ; static PUSHORT pnMaxIterations ; switch (msg) { case WM_INITDLG: pnMaxIterations = (PUSHORT) PVOIDFROMMP (mp2) ; WinSendDlgItemMsg (hwnd, IDD_ITER, EM_SETTEXTLIMIT, MPFROMSHORT (sizeof szBuffer - 1), NULL) ; WinSetDlgItemShort (hwnd, IDD_ITER, *pnMaxIterations, FALSE) ; return 0 ; case WM_COMMAND: switch (SHORT1FROMMP (mp1)) { case DID_OK: WinQueryDlgItemShort (hwnd, IDD_ITER, pnMaxIterations, FALSE) ; if (*pnMaxIterations == 0) { WinMessageBox (HWND_DESKTOP, hwnd, "Zero Iterations? Get real!", szMessageTitle, 0, MB_OK | MB_MOVEABLE | MB_ICONEXCLAMATION) ; return 0 ; } WinDismissDlg (hwnd, TRUE) ; return 0 ; case DID_CANCEL: WinDismissDlg (hwnd, FALSE) ; return 0 ; } break ; } return WinDefDlgProc (hwnd, msg, mp1, mp2) ; } MRESULT EXPENTRY AboutDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2) { switch (msg) { case WM_COMMAND: switch (SHORT1FROMMP (mp1)) { case DID_OK: case DID_CANCEL: WinDismissDlg (hwnd, TRUE) ; return 0 ; } break ; } return WinDefDlgProc (hwnd, msg, mp1, mp2) ; } VOID InvertBlock (HPS hps, POINTL ptlBeg, POINTL ptlEnd) { RECTL rcl ; if (ptlBeg.x != ptlEnd.x && ptlBeg.y != ptlEnd.y) { rcl.xLeft = min (ptlBeg.x, ptlEnd.x) ; rcl.xRight = max (ptlBeg.x, ptlEnd.x) ; rcl.yBottom = min (ptlBeg.y, ptlEnd.y) ; rcl.yTop = max (ptlBeg.y, ptlEnd.y) ; WinInvertRect (hps, &rcl) ; } } VOID CalcThread (PTHREADPARAMS ptp) { static LONG alColor16 [] = { CLR_DARKBLUE, CLR_DARKGREEN, CLR_DARKCYAN, CLR_DARKRED, CLR_DARKPINK, CLR_BROWN, CLR_DARKGRAY, CLR_PALEGRAY, CLR_BLUE, CLR_GREEN, CLR_CYAN, CLR_RED, CLR_PINK, CLR_YELLOW, CLR_WHITE } ; static LONG alColor256[] = { 0x000055, 0x0000AA, 0x0000FF, 0x002400, 0x002455, 0x0024AA, 0x0024FF, 0x004900, 0x004955, 0x0049AA, 0x0049FF, 0x006D00, 0x006D55, 0x006DAA, 0x006DFF, 0x009200, 0x009255, 0x0092AA, 0x0092FF, 0x00B600, 0x00B655, 0x00B6AA, 0x00B6FF, 0x00DB00, 0x00DB55, 0x00DBAA, 0x00DBFF, 0x00FF00, 0x00FF55, 0x00FFAA, 0x00FFFF, 0x240000, 0x240055, 0x2400AA, 0x2400FF, 0x242400, 0x242455, 0x2424AA, 0x2424FF, 0x244900, 0x244955, 0x2449AA, 0x2449FF, 0x246D00, 0x246D55, 0x246DAA, 0x246DFF, 0x249200, 0x249255, 0x2492AA, 0x2492FF, 0x24B600, 0x24B655, 0x24B6AA, 0x24B6FF, 0x24DB00, 0x24DB55, 0x24DBAA, 0x24DBFF, 0x24FF00, 0x24FF55, 0x24FFAA, 0x24FFFF, 0x490000, 0x490055, 0x4900AA, 0x4900FF, 0x492400, 0x492455, 0x4924AA, 0x4924FF, 0x494900, 0x494955, 0x4949AA, 0x4949FF, 0x496D00, 0x496D55, 0x496DAA, 0x496DFF, 0x499200, 0x499255, 0x4992AA, 0x4992FF, 0x49B600, 0x49B655, 0x49B6AA, 0x49B6FF, 0x49DB00, 0x49DB55, 0x49DBAA, 0x49DBFF, 0x49FF00, 0x49FF55, 0x49FFAA, 0x49FFFF, 0x6D0000, 0x6D0055, 0x6D00AA, 0x6D00FF, 0x6D2400, 0x6D2455, 0x6D24AA, 0x6D24FF, 0x6D4900, 0x6D4955, 0x6D49AA, 0x6D49FF, 0x6D6D00, 0x6D6D55, 0x6D6DAA, 0x6D6DFF, 0x6D9200, 0x6D9255, 0x6D92AA, 0x6D92FF, 0x6DB600, 0x6DB655, 0x6DB6AA, 0x6DB6FF, 0x6DDB00, 0x6DDB55, 0x6DDBAA, 0x6DDBFF, 0x6DFF00, 0x6DFF55, 0x6DFFAA, 0x6DFFFF, 0x920000, 0x920055, 0x9200AA, 0x9200FF, 0x922400, 0x922455, 0x9224AA, 0x9224FF, 0x924900, 0x924955, 0x9249AA, 0x9249FF, 0x926D00, 0x926D55, 0x926DAA, 0x926DFF, 0x929200, 0x929255, 0x9292AA, 0x9292FF, 0x92B600, 0x92B655, 0x92B6AA, 0x92B6FF, 0x92DB00, 0x92DB55, 0x92DBAA, 0x92DBFF, 0x92FF00, 0x92FF55, 0x92FFAA, 0x92FFFF, 0xB60000, 0xB60055, 0xB600AA, 0xB600FF, 0xB62400, 0xB62455, 0xB624AA, 0xB624FF, 0xB64900, 0xB64955, 0xB649AA, 0xB649FF, 0xB66D00, 0xB66D55, 0xB66DAA, 0xB66DFF, 0xB69200, 0xB69255, 0xB692AA, 0xB692FF, 0xB6B600, 0xB6B655, 0xB6B6AA, 0xB6B6FF, 0xB6DB00, 0xB6DB55, 0xB6DBAA, 0xB6DBFF, 0xB6FF00, 0xB6FF55, 0xB6FFAA, 0xB6FFFF, 0xDB0000, 0xDB0055, 0xDB00AA, 0xDB00FF, 0xDB2400, 0xDB2455, 0xDB24AA, 0xDB24FF, 0xDB4900, 0xDB4955, 0xDB49AA, 0xDB49FF, 0xDB6D00, 0xDB6D55, 0xDB6DAA, 0xDB6DFF, 0xDB9200, 0xDB9255, 0xDB92AA, 0xDB92FF, 0xDBB600, 0xDBB655, 0xDBB6AA, 0xDBB6FF, 0xDBDB00, 0xDBDB55, 0xDBDBAA, 0xDBDBFF, 0xDBFF00, 0xDBFF55, 0xDBFFAA, 0xDBFFFF, 0xFF0000, 0xFF0055, 0xFF00AA, 0xFF00FF, 0xFF2400, 0xFF2455, 0xFF24AA, 0xFF24FF, 0xFF4900, 0xFF4955, 0xFF49AA, 0xFF49FF, 0xFF6D00, 0xFF6D55, 0xFF6DAA, 0xFF6DFF, 0xFF9200, 0xFF9255, 0xFF92AA, 0xFF92FF, 0xFFB600, 0xFFB655, 0xFFB6AA, 0xFFB6FF, 0xFFDB00, 0xFFDB55, 0xFFDBAA, 0xFFDBFF, 0xFFFF00, 0xFFFF55, 0xFFFFAA, 0xFFFFFF } ; struct complex cmplxPoint ; HAB hab ; LONG lColorUse ; POINTL aptl[3] ; SHORT i, x, y ; USHORT nIteration ; hab = WinInitialize (0) ; while (!ptp->fTerminate) { DosSemWait (&ptp->semTriggerDraw, SEM_INDEFINITE_WAIT) ; if (ptp->fTerminate) break ; DosSemSet (&ptp->semDoingDraw) ; DosEnterCritSec () ; GpiErase (ptp->hpsMemory) ; if (ptp->clrds.sNumColors == 256) { GpiCreateLogColorTable (ptp->hpsClient, LCOL_PURECOLOR, LCOLF_RGB, 0L, 0L, NULL) ; GpiCreateLogColorTable (ptp->hpsMemory, LCOL_PURECOLOR, LCOLF_RGB, 0L, 0L, NULL) ; } else { GpiCreateLogColorTable (ptp->hpsClient, LCOL_RESET, LCOLF_INDRGB, 0L, 0L, NULL) ; GpiCreateLogColorTable (ptp->hpsMemory, LCOL_RESET, LCOLF_INDRGB, 0L, 0L, NULL) ; } ptp->fRestart = FALSE ; DosExitCritSec () ; // Loop for fat-bits down to pixels. for (i = 64 ; i > 0 ; i /= 2) { // Loop for rows for (y = 0 ; y < ptp->cyMaxClient ; y += i) { // Loop for columns for (x = 0 ; x < ptp->cxMaxClient ; x += i) { // If bit already drawn, skip it if ((i != 64) && (x & 1 == 0) && (y & 1 == 0)) continue ; // Calculate the complex point DosEnterCritSec () ; cmplxPoint.x = ptp->cords.dxMin + x * (ptp->cords.dxMax - ptp->cords.dxMin) / ptp->cxMaxClient ; cmplxPoint.y = ptp->cords.dyMin + y * (ptp->cords.dyMax - ptp->cords.dyMin) / ptp->cyMaxClient ; // Find the number of iterations nIteration = Mandelbrot (cmplxPoint, ptp->nMaxIterations) ; // Determine the color to use if (ptp->clrds.sNumColors == 2) lColorUse = (nIteration == ptp->nMaxIterations ? CLR_BLACK : CLR_WHITE) ; else if (ptp->clrds.sNumColors == 16) lColorUse = (nIteration == ptp->nMaxIterations ? CLR_BLACK : alColor16 [nIteration % 15]) ; else if (ptp->clrds.sNumColors == 256) lColorUse = (nIteration == ptp->nMaxIterations ? RGB_BLACK : alColor256 [nIteration % 255]) ; GpiSetColor (ptp->hpsMemory, lColorUse) ; // Draw on the bitmap aptl[0].x = x ; aptl[0].y = y ; if (i > 1) { aptl[1].x = min (x + i, ptp->cxMaxClient) ; aptl[1].y = min (y + i, ptp->cyMaxClient) ; GpiBitBlt (ptp->hpsMemory, NULL, 2L, aptl, ROP_PATCOPY, 0L) ; } else GpiSetPel (ptp->hpsMemory, aptl) ; DosExitCritSec () ; if (ptp->fRestart) break ; } if (ptp->fRestart) break ; DosEnterCritSec () ; // For each row, draw the bitmap on the window if (y < ptp->cyClient - ptp->cyChar) { aptl[0].x = 0 ; aptl[0].y = y ; aptl[1].x = ptp->cxClient ; aptl[1].y = min (y + i, ptp->cyClient - ptp->cyChar) ; aptl[2].x = 0 ; aptl[2].y = y ; GpiBitBlt (ptp->hpsClient, ptp->hpsMemory, 3L, aptl, ROP_SRCCOPY, 0L); } DosExitCritSec () ; } if (ptp->fRestart) break ; } if (!ptp->fRestart) { DosSemSet (&ptp->semTriggerDraw) ; WinPostMsg (ptp->hwndClient, WM_IMAGE_COMPLETED, NULL, NULL) ; } DosSemClear (&ptp->semDoingDraw) ; } WinTerminate (hab) ; _endthread () ; } USHORT Mandelbrot (struct complex cmplxPoint, USHORT nMaxIterations) { struct complex cmplxSquare, cmplxTemp, cmplxAccum ; USHORT n ; cmplxAccum = cmplxPoint ; // "Cheat" a little for speed if (cmplxPoint.x >= -0.750 && cmplxPoint.x <= 0.250 && cmplxPoint.y >= -0.625 && cmplxPoint.y <= 0.625) { cmplxTemp = cmplxPoint ; cmplxTemp.x += .125 ; if (cabs (cmplxTemp) < .625) return nMaxIterations ; } else if (cmplxPoint.x >= -1.250 && cmplxPoint.x <= -0.750 && cmplxPoint.y >= -0.250 && cmplxPoint.y <= 0.250) { cmplxTemp = cmplxPoint ; cmplxTemp.x += 1.0 ; if (cabs (cmplxTemp) < .25) return nMaxIterations ; } // Done cheating for (n = 0 ; n < nMaxIterations ; n++) { cmplxSquare.x = cmplxAccum.x * cmplxAccum.x ; cmplxSquare.y = cmplxAccum.y * cmplxAccum.y ; if (cmplxSquare.x + cmplxSquare.y >= 4.0) break ; cmplxAccum.y = 2 * cmplxAccum.x * cmplxAccum.y + cmplxPoint.y ; cmplxAccum.x = cmplxSquare.x - cmplxSquare.y + cmplxPoint.x ; } return n ; }