MANDEL.C


/****************************************************************************
Microsoft RPC Version 2.0
Copyright Microsoft Corp. 1992, 1993, 1994- 1996
mandel Example

FILE: mandel.c

PURPOSE: Client side of the RPC distributed application

COMMENTS: Main code for the Windows Mandelbrot Set distributed
drawing program.

****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <windows.h> // Required for all Windows applications
#include <windowsx.h> // Allow portability from Win16, Win32

#ifdef RPC
#include "mdlrpc.h" // header file generated by the MIDL compiler
#endif
#include "mandel.h"


/* data structures */

#ifdef RPC
char szTitle[] = "Mandelbrot RPC";
#else
char szTitle[] = "Mandelbrot Standalone";
#endif

CPOINT cptUL = { (double) -2.05, (double) 1.4 };
double dPrec = (double) .01;

HANDLE hInst; // current instance
HWND hWND; // Main window handle

svr_table SvrTable;
int iLines = LINES;

int fContinueZoom = TRUE;
int fZoomIn = TRUE;

// split current picture into 16 regions
// zoom on most complex region; region with most colors represented
int Histogram[4][4][NCOLORS+1] = {0};
int ColorCount[4][4] = {0};
int Max[4][4] = {0};

int iHistMaxI = 2;
int iHistMaxJ = 3;

RECT rcZoom;
BOOL fRectDefined = FALSE;

#ifdef RPC
int fBound = FALSE; // flag indicates whether bound to svr
unsigned char * pszUuid = NULL;
unsigned char pszProtocolSequence[MAXPROTSEQ] = "ncacn_np";
unsigned char pszEndpoint[PATHLEN] = "\\pipe\\mandel";
unsigned char * pszOptions = NULL;
unsigned char * pszStringBinding;
unsigned char pszNetworkAddress[UNCLEN+1] = {'\0'};
#endif

/* function prototypes */

void DoSomeWork(HWND, BOOL);
void InitHistogram(void);
void CalcHistogram(int, int, DWORD, DWORD);
void PaintLine(HWND, svr_table *, HDC, int);
void DrawRect(HWND, PRECT, BOOL, HDC);
COLORREF MapColor(DWORD, DWORD);


/*
* FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
*
* PURPOSE: Calls initialization function, processes message loop
*
* COMMENTS:
*
* Windows recognizes this function by name as the initial entry point
* for the program. This function calls the application initialization
* routine, if no other instance of the program is running, and always
* calls the instance initialization routine. It then executes a message
* retrieval and dispatch loop that is the top-level control structure
* for the remainder of execution. The loop is terminated when a WM_QUIT
* message is received, at which time this function exits the application
* instance by returning the value passed by PostQuitMessage().
*
* If this function must abort before entering the message loop, it
* returns the conventional value NULL.
*/

int WINAPI WinMain(
HINSTANCE hInstance, /* current instance */
HINSTANCE hPrevInstance, /* previous instance */
LPSTR lpCmdLine, /* command line */
int nCmdShow) /* show-window type (open/icon) */
{

MSG msg;

UNREFERENCED_PARAMETER(lpCmdLine);

if (!hPrevInstance) /* Other instances of app running? */
if (!InitApplication(hInstance)) /* Initialize shared things */
return(FALSE); /* Exits if unable to initialize */

/* Perform initializations that apply to a specific instance */
if (!InitInstance(hInstance, nCmdShow))
return(FALSE);

/* Acquire and dispatch messages until a WM_QUIT message is received. */
while (GetMessage(&msg, /* message structure */
(HWND)NULL, /* handle of window receiving the message */
0, /* lowest message to examine */
0)) /* highest message to examine */
{
TranslateMessage(&msg); /* Translates virtual key codes */
DispatchMessage(&msg); /* Dispatches message to window */
}

return(msg.wParam); /* Returns the value from PostQuitMessage */

}


/*
* FUNCTION: InitApplication(HANDLE)
*
* PURPOSE: Initializes window data and registers window class
*
* COMMENTS:
*
* This function is called at initialization time only if no other
* instances of the application are running. This function performs
* initialization tasks that can be done once for any number of running
* instances.
*
* In this case, we initialize a window class by filling out a data
* structure of type WNDCLASS and calling the Windows RegisterClass()
* function. Since all instances of this application use the same window
* class, we only need to do this when the first instance is initialized.
*/

BOOL InitApplication(HANDLE hInstance) /* current instance */
{

WNDCLASS wc;

/* Fill in window class structure with parameters that describe the */
/* main window. */
wc.style = 0; /* Class style(s). */
wc.lpfnWndProc = (WNDPROC)MainWndProc;
/* Function to retrieve messages for */
/* windows of this class. */
wc.cbClsExtra = 0; /* No per-class extra data. */
wc.cbWndExtra = 0; /* No per-window extra data. */
wc.hInstance = hInstance; /* Application that owns the class. */
wc.hIcon = LoadIcon(hInstance, "RPC_ICON");
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MandelMenu"; /* Name of menu resource in .RC file. */
wc.lpszClassName = "MandelClass"; /* Name used in call to CreateWindow. */

/* Register the window class and return success/failure code. */
return(RegisterClass(&wc));

}


/*
* FUNCTION: InitInstance(HANDLE, int)
*
* PURPOSE: Saves instance handle and creates main window.
*
* COMMENTS:
*
* This function is called at initialization time for every instance of
* this application. This function performs initialization tasks that
* cannot be shared by multiple instances.
*
* In this case, we save the instance handle in a static variable and
* create and display the main program window.
*/

BOOL InitInstance(HANDLE hInstance, /* Current instance identifier. */
int nCmdShow) /* Param for first ShowWindow() call. */
{
HMENU hMenu;
RECT rc;

/* Save the instance handle in static variable, which will be used in */
/* many subsequence calls from this application to Windows. */
hInst = hInstance;

/* Create a main window for this application instance. */
hWND = CreateWindow(
"MandelClass", /* See RegisterClass() call. */
szTitle, /* Text for window title bar. */
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX,
CW_USEDEFAULT, /* Default horizontal position. */
CW_USEDEFAULT, /* Default vertical position. */
WIDTH, /* Default width. */
HEIGHT, /* Default height. */
(HWND) NULL, /* Overlapped windows have no parent. */
(HMENU) NULL, /* Use the window class menu. */
hInstance, /* This instance owns this window. */
(LPVOID) NULL /* Pointer not needed. */
);

/* If window could not be created, return "failure" */
if (!hWND)
return(FALSE);

/* Make the window visible; update its client area; and return "success" */
ShowWindow(hWND, nCmdShow); /* Show the window */
UpdateWindow(hWND); /* Sends WM_PAINT message */
rc.top = rc.left = 0;
rc.bottom = HEIGHT-1;
rc.right = WIDTH-1;

SetNewCalc(cptUL, dPrec, rc);
hMenu = GetMenu(hWND);

#ifndef RPC
EnableMenuItem(hMenu, IDM_SERVER, MF_GRAYED); /* disable option */
#endif

return(TRUE); /* Returns the value from PostQuitMessage */
}


/*
* FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)
*
* PURPOSE: Processes messages
*
* MESSAGES:
* WM_COMMAND - application menu
* WM_DESTROY - destroy window
*
* COMMENTS:
*/

LONG APIENTRY MainWndProc(
HWND hWnd, /* window handle */
UINT message, /* type of message */
UINT wParam, /* additional information */
LONG lParam) /* additional information */
{
DLGPROC lpProc; /* pointer to the dialog box function */
PAINTSTRUCT ps;
HDC hdc;
static HDC hdcMem;
static HBITMAP hbmMem;
static int width;
static int height;
RECT rc;
static BOOL fButtonDown = FALSE;
static POINT pSelected;
POINT pMove;
int iWidthNew;
int iHeightNew;
static int miOldLines;
double scaling;

switch (message) {

case WM_CREATE:

#ifdef WIN16
RpcWinSetYieldInfo (hWnd, FALSE, 0, 0L); // To make TCP/IP happy
#else
PostMessage(hWnd, WM_COMMAND, IDM_BIND, 0L); // bind to server
#endif

if (!InitRemote(hWnd))
return(FALSE);

InitHistogram();

hdc = GetDC(hWnd);
hdcMem = CreateCompatibleDC(hdc);
GetWindowRect(hWnd, &rc);
width = rc.right - rc.left;
height = rc.bottom - rc.top;
hbmMem = CreateCompatibleBitmap(hdc, width, height);
SelectObject(hdcMem, hbmMem);

ReleaseDC(hWnd,hdc);

rc.left = rc.top = 0;
rc.right = width+1;
rc.bottom = height + 1;
FillRect(hdcMem, &rc, GetStockObject(WHITE_BRUSH));

CheckMenuItem(GetMenu(hWnd), IDM_4LINES, MF_CHECKED);
CheckMenuItem(GetMenu(hWnd), IDM_CONTINUOUS, MF_CHECKED);
miOldLines = IDM_4LINES; // save to uncheck
break;

case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
BitBlt(hdc,
ps.rcPaint.left,
ps.rcPaint.top,
ps.rcPaint.right - ps.rcPaint.left,
ps.rcPaint.bottom - ps.rcPaint.top,
hdcMem,
ps.rcPaint.left,
ps.rcPaint.top,
SRCCOPY);
EndPaint(hWnd, &ps);
break;

case WM_COMMAND: // message: command from application menu
switch(wParam) {

case IDM_BIND:

#ifdef RPC
if (Bind(hWnd) != RPC_S_OK)
PostMessage(hWnd, WM_DESTROY, 0, 0L);
#endif
break;

case IDM_ABOUT:
lpProc = MakeProcInstance(About, hInst);

DialogBox(hInst, // current instance
"AboutBox", // resource to use
hWnd, // parent handle
lpProc); // About() instance address

FreeProcInstance(lpProc);
break;

case IDM_ZOOMOUT:
if (dPrec > (double)MAXPREC) // don't allow the zoom out
break;

rcZoom.left = WIDTH/4 + (WIDTH/8); // center square
rcZoom.top = HEIGHT/4 + (HEIGHT/8);
rcZoom.right = rcZoom.left + (WIDTH/4);
rcZoom.bottom = rcZoom.top + (HEIGHT/4);

cptUL.real -= (rcZoom.left * dPrec); // inverse of zoom in
cptUL.imag += (rcZoom.top * dPrec);
iWidthNew = (rcZoom.right - rcZoom.left + 1);
iHeightNew = (rcZoom.bottom - rcZoom.top + 1);
scaling = ((double) ((iWidthNew > iHeightNew) ? iWidthNew : iHeightNew) / (double) width);
dPrec /= scaling;

rc.left = rc.top = 0;
rc.bottom = height - 1;
rc.right = width - 1;

SetNewCalc(cptUL, dPrec, rc);
fRectDefined = FALSE;
DoSomeWork(hWnd, FALSE);
break;

case IDM_ZOOMIN: // zoom in on selected rectangle
// if no rectangle, don't zoom in
if (!fRectDefined)
break;

if (dPrec < (double)MINPREC) // don't allow zoom in
break;

DrawRect(hWnd, &rcZoom, TRUE, hdcMem); // draw new rect

// calculate new upper-left
cptUL.real += (rcZoom.left * dPrec);
cptUL.imag -= (rcZoom.top * dPrec);

iWidthNew = (rcZoom.right - rcZoom.left + 1);
iHeightNew = (rcZoom.bottom - rcZoom.top + 1);
scaling = ((double) ((iWidthNew > iHeightNew) ? iWidthNew : iHeightNew) / (double) width);

dPrec *= scaling;

rc.left = rc.top = 0;
rc.bottom = height - 1;
rc.right = width - 1;

SetNewCalc(cptUL, dPrec, rc);
IncPictureID();

fRectDefined = FALSE;
DoSomeWork(hWnd, FALSE);
break;

case IDM_CONTINUOUS: // continuous zoom in
if (fContinueZoom == TRUE) {
CheckMenuItem(GetMenu(hWnd), IDM_CONTINUOUS, MF_UNCHECKED);
fContinueZoom = FALSE;
}
else {
CheckMenuItem(GetMenu(hWnd), IDM_CONTINUOUS, MF_CHECKED);
fContinueZoom = TRUE;
}
break;

case IDM_REDRAW:
if (fContinueZoom == TRUE)
InitHistogram();

rc.left = rc.top = 0;
rc.right = width+1;
rc.bottom = height + 1;
FillRect(hdcMem, &rc, GetStockObject(WHITE_BRUSH));
InvalidateRect(hWnd, NULL, TRUE);

rc.left = rc.top = 0;
rc.bottom = height - 1;
rc.right = width - 1;
SetNewCalc( cptUL, dPrec, rc);

fRectDefined = FALSE;
DoSomeWork(hWnd, FALSE);
break;

case IDM_EXIT:
DestroyWindow(hWnd);
FreeDrawBuffer();
break;

case IDM_TOP:
cptUL.real = (double) -2.05;
cptUL.imag = (double) 1.4;
dPrec = .01;

rc.left = rc.top = 0;
rc.bottom = height - 1;
rc.right = width - 1;

SetNewCalc(cptUL, dPrec, rc);
ResetPictureID(); // incremented past original

fRectDefined = FALSE;
DoSomeWork(hWnd, FALSE);
break;

case IDM_1LINE:

case IDM_2LINES:

case IDM_4LINES:

CheckMenuItem(GetMenu(hWnd), miOldLines, MF_UNCHECKED);
miOldLines = wParam;
switch(wParam) {

case IDM_1LINE:
iLines = 1;
break;
case IDM_2LINES:
iLines = 2;
break;
case IDM_4LINES:
iLines = 4;
break;
}

CheckMenuItem(GetMenu(hWnd), miOldLines, MF_CHECKED);
break;

#ifdef RPC
case IDM_PROTSEQ:
lpProc = MakeProcInstance(Protseq, hInst);
DialogBox(hInst, // current instance
"ProtseqBox", // resource to use
hWnd, // parent handle
lpProc); // Server instance address
FreeProcInstance(lpProc);
break;

case IDM_SERVER:
lpProc = MakeProcInstance(Server, hInst);
DialogBox(hInst, // current instance
"ServerBox", // resource to use
hWnd, // parent handle
lpProc); // Server instance address
FreeProcInstance(lpProc);
break;

case IDM_ENDPOINT:
lpProc = MakeProcInstance(Endpoint, hInst);
DialogBox(hInst, // current instance
"EndpointBox",// resource to use
hWnd, // parent handle
lpProc); // Server instance address
FreeProcInstance(lpProc);
break;
#endif
case IDM_GO:
SetTimer(hWnd, 1, POLL_TIME, NULL); // set timer for polls
EnableMenuItem(GetMenu(hWnd), IDM_GO, MF_GRAYED); // disable GO
break;


default: // Lets Windows process it
return(DefWindowProc(hWnd, message, wParam, lParam));

}

break;

case WM_DESTROY: // message: window being destroyed
PostQuitMessage(0);
DeleteDC(hdcMem);
DeleteObject(hbmMem);
break;

case WM_DOSOMEWORK: // do another slice of calculation work
DoSomeWork(hWnd, FALSE);
break;

case WM_PAINTLINE: // The shared buffer contains a line of data; draw it
PaintLine(hWnd,
&SvrTable,
hdcMem,
height);
break;

case WM_TIMER: // timer means we should do another slice of work
DoSomeWork(hWnd, TRUE);
break;

case WM_LBUTTONDOWN: // left button down; start to define a zoom rectangle
if (fRectDefined)
DrawRect(hWnd, &rcZoom, FALSE, hdcMem); // undraw old rectangle

// initialize rectangle
rcZoom.left = rcZoom.right = pSelected.x = LOWORD(lParam);
rcZoom.top = rcZoom.bottom = pSelected.y = HIWORD(lParam);

// draw the new rectangle
DrawRect(hWnd, &rcZoom, TRUE, hdcMem);

fRectDefined = TRUE;
fButtonDown = TRUE;
SetCapture(hWnd); // capture all mouse events
break;

case WM_MOUSEMOVE: // mouse move
// if the button is down, change the rect
if (!fButtonDown)
break;

DrawRect(hWnd, &rcZoom, FALSE, hdcMem); // undraw old rect

pMove.x = LOWORD(lParam);
pMove.y = HIWORD(lParam);

// update the selection rectangle
if (pMove.x <= pSelected.x)
rcZoom.left = pMove.x;
if (pMove.x >= pSelected.x)
rcZoom.right = pMove.x;
if (pMove.y <= pSelected.y)
rcZoom.top = pMove.y;
if (pMove.y >= pSelected.y)
rcZoom.bottom = pMove.y;

DrawRect(hWnd, &rcZoom, TRUE, hdcMem); // draw new rect
break;

case WM_LBUTTONUP: // button up; end selection
fButtonDown = FALSE;
ReleaseCapture();
break;

default: // Passes it on if unproccessed
return(DefWindowProc(hWnd, message, wParam, lParam));

}

return(0L);
}


/*
* FUNCTION: About(HWND, unsigned, WORD, LONG)
*
* PURPOSE: Processes messages for "About" dialog box
*
* MESSAGES:
*
* WM_INITDIALOG - initialize dialog box
* WM_COMMAND - Input received
*
* COMMENTS:
*
* No initialization is needed for this particular dialog box, but TRUE
* must be returned to Windows.
*
* Wait for user to click on "Ok" button, then close the dialog box.
*/

BOOL APIENTRY About(
HWND hDlg, /* window handle of the dialog box */
UINT message, /* type of message */
UINT wParam, /* message-specific information */
LONG lParam)
{
UNREFERENCED_PARAMETER(lParam);

switch (message) {

case WM_INITDIALOG: /* message: initialize dialog box */
return(TRUE);

case WM_COMMAND: /* message: received a command */
if (wParam == IDOK || wParam == IDCANCEL)
{
EndDialog(hDlg, TRUE); /* Exits the dialog box */
return(TRUE);
}
break;
}

return(FALSE); /* Didn't process a message */
}



/*
* FUNCTION: Protseq(HWND, unsigned, WORD, LONG)
*
* PURPOSE: Processes messages for "Protseq" dialog box
*
* MESSAGES:
*
* WM_INITDIALOG - initialize dialog box
* WM_COMMAND - Input received
*
* COMMENTS:
*
* No initialization is needed for this particular dialog box, but TRUE
* must be returned to Windows.
*
* Wait for user to click on "Ok" button, then close the dialog box.
*/
BOOL APIENTRY Protseq(
HWND hDlg, /* window handle of the dialog box */
UINT message, /* type of message */
UINT wParam, /* message-specific information */
LONG lParam)
{

UNREFERENCED_PARAMETER(lParam);

#ifdef RPC

switch (message) {

case WM_INITDIALOG: // message: initialize dialog box
SetDlgItemText((HANDLE)hDlg, IDD_PROTSEQNAME, pszProtocolSequence);
return(TRUE);

case WM_COMMAND: // message: received a command
switch(wParam) {

case IDCANCEL: // System menu close command?
EndDialog(hDlg, FALSE);
return(TRUE);

case IDOK: // "OK" box selected?
GetDlgItemText(hDlg, IDD_PROTSEQNAME, pszProtocolSequence, MAXPROTSEQ);

if (Bind(hDlg) != RPC_S_OK) {
EndDialog(hDlg, FALSE);
return(FALSE);
}
KillTimer(hWND, 1); // stop timer for polls
EnableMenuItem(GetMenu(hWND), IDM_GO, MF_ENABLED); // enable GO

EndDialog(hDlg, TRUE);
return(TRUE);

}

}

#endif

return(FALSE); // Didn't process a message
}


/*
* FUNCTION: Server(HWND, unsigned, WORD, LONG)
*
* PURPOSE: Processes messages for "Server" dialog box
*
* MESSAGES:
*
* WM_INITDIALOG - initialize dialog box
* WM_COMMAND - Input received
*
* COMMENTS:
*
* No initialization is needed for this particular dialog box, but TRUE
* must be returned to Windows.
*
* Wait for user to click on "Ok" button, then close the dialog box.
*/

BOOL APIENTRY Server(
HWND hDlg, /* window handle of the dialog box */
UINT message, /* type of message */
UINT wParam, /* message-specific information */
LONG lParam)
{

UNREFERENCED_PARAMETER(lParam);

#ifdef RPC

switch (message) {

case WM_INITDIALOG: /* message: initialize dialog box */
SetDlgItemText(hDlg, IDD_SERVERNAME, pszNetworkAddress);
return(TRUE);

case WM_COMMAND: /* message: received a command */
switch(wParam) {

case IDCANCEL: /* System menu close command? */
EndDialog(hDlg, FALSE);
return(TRUE);

case IDOK: /* "OK" box selected? */
GetDlgItemText( hDlg, IDD_SERVERNAME, pszNetworkAddress, UNCLEN);

if (Bind(hDlg) != RPC_S_OK) {
EndDialog(hDlg, FALSE);
return(FALSE);
}
KillTimer(hWND, 1); // stop timer for polls
EnableMenuItem(GetMenu(hWND), IDM_GO, MF_ENABLED); // enable GO

EndDialog(hDlg, TRUE);
return(TRUE);
}

}

#endif

return(FALSE); /* Didn't process a message */

}

/*
* FUNCTION: Endpoint(HWND, unsigned, WORD, LONG)
*
* PURPOSE: Processes messages for "Endpoint" dialog box
*
* MESSAGES:
*
* WM_INITDIALOG - initialize dialog box
* WM_COMMAND - Input received
*
* COMMENTS:
*
* No initialization is needed for this particular dialog box, but TRUE
* must be returned to Windows.
*
* Wait for user to click on "Ok" button, then close the dialog box.
*/

BOOL APIENTRY Endpoint(
HWND hDlg, /* window handle of the dialog box */
UINT message, /* type of message */
UINT wParam, /* message-specific information */
LONG lParam)
{

UNREFERENCED_PARAMETER(lParam);

#ifdef RPC

switch (message) {

case WM_INITDIALOG: // message: initialize dialog box
SetDlgItemText(hDlg, IDD_ENDPOINTNAME, pszEndpoint);
return(TRUE);

case WM_COMMAND: // message: received a command
switch(wParam) {

case IDCANCEL:
EndDialog(hDlg, FALSE);
return(TRUE);

case IDOK:
GetDlgItemText(hDlg, IDD_ENDPOINTNAME, pszEndpoint, PATHLEN);

if (Bind(hDlg) != RPC_S_OK) {
EndDialog(hDlg, FALSE);
return(FALSE);
}
KillTimer(hWND, 1); // stop timer for polls
EnableMenuItem(GetMenu(hWND), IDM_GO, MF_ENABLED); // enable GO

EndDialog(hDlg, TRUE);
return(TRUE);

}

}

#endif

return(FALSE); // Didn't process a message
}


/*
* DoSomeWork --
*
* This function does our work for us. It does it in little pieces, and
* will schedule itself as it sees fit.
*/

void
DoSomeWork(HWND hwnd,
BOOL fTimer)
{
static WORD wIteration = 0;

if (fTimer) {
wIteration++;

// on every nth tick, we send out a poll
if (wIteration == 120) { // tune this?
wIteration = 0;
return;
}

// on the half-poll, we check for responses
if ((wIteration == 2) || (wIteration == 10)) {
return;
}
}

if (CheckDrawStatus(hwnd))
SendMessage(hwnd, WM_DOSOMEWORK, 0, 0L);

return;
}


/*
* DrawRect --
*
* This function draws (or undraws) the zoom rectangle.
*/

void
DrawRect(HWND hwnd,
PRECT prc,
BOOL fDrawIt,
HDC hdcBM)
{
HDC hdc;
DWORD dwRop;

hdc = GetDC(hwnd);

if (fDrawIt)
dwRop = NOTSRCCOPY;
else
dwRop = SRCCOPY;

// top side
BitBlt(hdc, prc->left, prc->top, (prc->right - prc->left) + 1,
1, hdcBM, prc->left, prc->top, dwRop);

// bottom side
BitBlt(hdc, prc->left, prc->bottom, (prc->right - prc->left) + 1,
1, hdcBM, prc->left, prc->bottom, dwRop);

// left side
BitBlt(hdc,prc->left, prc->top, 1, (prc->bottom - prc->top) + 1,
hdcBM, prc->left, prc->top, dwRop);

// right side
BitBlt(hdc,prc->right, prc->top, 1, (prc->bottom - prc->top) + 1,
hdcBM, prc->right, prc->top, dwRop);

ReleaseDC(hwnd, hdc);
}


/*
* PaintLine --
*
* This function paints a buffer of data into the bitmap.
*/

void
PaintLine(HWND hwnd,
svr_table * pst,
HDC hdcBM,
int cHeight)
{
LPWORD pwDrawData;
int y;
int x;
DWORD dwThreshold;
RECT rc;
WORD lines;

lines = (WORD) pst->cLines;

// picture ID had better match, or else we skip it
if (CheckDrawingID(pst->cPicture))
{
// figure out our threshold
dwThreshold = QueryThreshold();

// get a pointer to the draw buffer
pwDrawData = (LPWORD) LockDrawBuffer();
if (pwDrawData == NULL) {
ReturnDrawBuffer();
return;
}

// starting x coordinate
x = (int) pst->dwLine;

// now loop through the rectangle
while (lines-- > 0)
{
// bottom to top, since that's the order of the data in the buffer
y = (int) cHeight-1;

while (y >= 0)
{
// draw a pixel
SetPixel(hdcBM, x,y, MapColor(*pwDrawData, dwThreshold));

if (fContinueZoom == TRUE)
CalcHistogram(x, y, *pwDrawData, dwThreshold);

// now increment buffer pointer and y coord

y--; 
pwDrawData++;
}

// increment X coordinate
x++;
}

// figure out the rectangle to invalidate
rc.top = 0;
rc.bottom = cHeight;
rc.left = (int)(pst->dwLine);
rc.right = (int)(pst->dwLine) + pst->cLines;

UnlockDrawBuffer();

// and invalidate it on the screen so we redraw it
InvalidateRect(hwnd, &rc, FALSE);
}

// free this for someone else to use
ReturnDrawBuffer();

// and change the pipe state, if necessary
if (pst->iStatus == SS_PAINTING)
pst->iStatus = SS_IDLE;

}


#define CLR_BLACK RGB(0,0,0)
#define CLR_DARKBLUE RGB(0,0,127)
#define CLR_BLUE RGB(0,0,255)
#define CLR_CYAN RGB(0,255,255)
#define CLR_DARKGREEN RGB(0,127,0)
#define CLR_GREEN RGB(0,255,0)
#define CLR_YELLOW RGB(255,255,0)
#define CLR_RED RGB(255,0,0)
#define CLR_DARKRED RGB(127,0,0)
#define CLR_WHITE RGB(255,255,255)
#define CLR_PALEGRAY RGB(194,194,194)
#define CLR_DARKGRAY RGB(127,127,127)


static COLORREF ColorMapTable[] = { // size = NCOLORS
CLR_DARKBLUE,
CLR_BLUE,
CLR_CYAN,
CLR_DARKGREEN,
CLR_GREEN,
CLR_YELLOW,
CLR_RED,
CLR_DARKRED,
CLR_WHITE,
CLR_PALEGRAY,
CLR_DARKGRAY};


/*
* MapColor --
*
* This function maps an iteration count into a corresponding RGB color.
*/

COLORREF
MapColor(DWORD dwIter,
DWORD dwThreshold)
{

/* if it's beyond the threshold, call it black */
if (dwIter >= dwThreshold) {
return(CLR_BLACK);
}

/* get a modulus based on the number of colors */
dwIter = (dwIter / 3) % NCOLORS; // 11;

/* and return the appropriate color */
return(ColorMapTable[dwIter]);

}


/*
* CalcHistogram --
*
* This function is used to select the region that is the
* most complex and will be used to zoom in for the next picture;
* it contains the most colors. The number of colors are counted.
*/

void
CalcHistogram(int x,
int y,
DWORD dwIter,
DWORD dwThreshold)
{

/* if it's beyond the threshold, call it black */
if (dwIter >= dwThreshold) {
Histogram[x/(WIDTH/4)][y/(HEIGHT/4)][NCOLORS]++;
return;
}

/* get a modulus based on the number of colors */
dwIter = (dwIter / 3) % NCOLORS; // 11;

/* and bump the count for the appropriate color */
Histogram[x/(WIDTH/4)][y/(HEIGHT/4)][dwIter]++; // region of map

return;

}


/*
* InitHistogram --
*
* This function initializes the histogram data structures.
*/

void InitHistogram(void)
{
int i, j, k;

for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
for (k = 0; k <= NCOLORS; k++)
Histogram[i][j][k] = 0; // count of colors
}


/*
* CountHistogram --
*
* This function determines the number of colors represented
* within a region. The region with the most colors is
* selected using the maxi and maxj values. X and Y coordinates
* corresponding to these regions are stored in the HistRegion
* table and are used for the next picture.
*/

void CountHistogram(void)
{

int i, j, k;

/* count the number of colors in each region */
/* find the color that dominates each region */
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
ColorCount[i][j] = 0;
Max[i][j] = 0;
for (k = 0; k <= NCOLORS; k++) {
if (Histogram[i][j][k] > Max[i][j])
Max[i][j] = Histogram[i][j][k];
if (Histogram[i][j][k] != 0) // count of colors
ColorCount[i][j]++;
}
}
}

iHistMaxI = 0;
iHistMaxJ = 0;

/* if several regions have the same number of colors, */
/* select the region with the most variety: the smallest max */
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
if ( (ColorCount[i][j] >= ColorCount[iHistMaxI][iHistMaxJ])
&& (Max[i][j] < Max[iHistMaxI][iHistMaxJ]) ) {
iHistMaxI = i;
iHistMaxJ = j;
}
}
}

InitHistogram(); // initialize for next time

}


#ifdef RPC

void __RPC_FAR * __RPC_API midl_user_allocate(size_t len)
{
UNREFERENCED_PARAMETER(len);
return(NULL);
}


void __RPC_API midl_user_free(void __RPC_FAR * ptr)
{
UNREFERENCED_PARAMETER(ptr);
return;
}


/*
* FUNCTION: Bind(HWND)
*
* PURPOSE: Make RPC API calls to bind to the server application
*
* COMMENTS:
*
* The binding calls are made from InitInstance() and whenever
* the user changes the server name or endpoint. If the bind
* operation is successful, the global flag fBound is set to TRUE.
*
* The global flag fBound is used to determine whether to call
* the RPC API function RpcBindingFree.
*/

RPC_STATUS Bind(HWND hWnd)
{
RPC_STATUS status;
char pszFail[MSGLEN];

if (fBound == TRUE) { // unbind only if bound
status = RpcStringFree(&pszStringBinding);
if (status) {
sprintf(pszFail, "RpcStringFree failed 0x%x", status);
MessageBox(hWnd,
pszFail,
"RPC Sample Application",
MB_ICONSTOP);
return(status);
}

status = RpcBindingFree(&hMandel);
if (status) {
sprintf(pszFail, "RpcBindingFree failed 0x%x", status);
MessageBox(hWnd,
pszFail,
"RPC Sample Application",
MB_ICONSTOP);
return(status);
}

fBound = FALSE; // unbind successful; reset flag
}

status = RpcStringBindingCompose(pszUuid,
pszProtocolSequence,
pszNetworkAddress,
pszEndpoint,
pszOptions,
&pszStringBinding);
if (status) {
sprintf(pszFail, "RpcStringBindingCompose returned: (0x%x)\nNetwork Address = %s\n",
status, pszNetworkAddress);
MessageBox(hWnd, pszFail, "RPC Sample Application", MB_ICONINFORMATION);
return(status);
}

status = RpcBindingFromStringBinding(pszStringBinding,
&hMandel);
if (status) {
sprintf(pszFail, "RpcBindingFromStringBinding returned: (0x%x)\nString = %s\n",
status, pszStringBinding);
MessageBox(hWnd, pszFail, "RPC Sample Application", MB_ICONINFORMATION);
return(status);
}

fBound = TRUE; // bind successful; reset flag

return(status);
}

#endif


/* end mandel.c */