// Copyright 1999 // College of Computer Science // Northeastern University Boston MA 02115 // This software may be used for educational purposes as long as this copyright // notice is retained at the top of all files // Should this software be modified, the words "Modified from Original" must be // included as a comment below this notice // All publication rights are retained. This software or its documentation may // not be published in any media either in whole or in part. /////////////////////////////////////////////////////////////////////////////// // SystemBase.cpp /////////////////////////////////////////////////////////////////////////////// #include "CoreTools.h" // needed for core tools compile in Win32 #include "SystemBase.h" /////////////////////////////////////////////////////////////////////// // BEGIN PLATFORM DEPENDENT CODE // #if defined(CORE_PLATFORM_WIN32) HINSTANCE CoreTools_hInstance; WNDCLASSEX CoreTools_wndclass; char CoreTools_wndclassName[] = "CoreTools_wndclass"; // define Core Tools Windows callback function "WndProc" LRESULT CALLBACK CoreTools_WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { // essential variables in general GraphicsWindow* gwp; // essential variables for WM_SIZE case short xsize, ysize; static bool toosmall = false; // essential variables for WM_MOVE case RectData R; NativeRectData N; short xspot, yspot; short x1, y1, x2, y2; static bool badshift = false; // always start by obtaining the graphics window pointer gwp = GraphicsWindow::GWPtr(hwnd); switch (message) { case (WM_CREATE): return 0; case (WM_PAINT): gwp->DrawListPaint(); return 0; case (WM_SIZE): if (SIZE_RESTORED == wParam) { // lParam contains client size data xsize = LOWORD(lParam); ysize = HIWORD(lParam); // test if window is too small // but avoid doing resize action if already attempted if (toosmall) toosmall = false; else toosmall = (xsize < MinimumClientSize) || (ysize < MinimumClientSize); if (toosmall) { // grow the window xsize = (xsize < MinimumClientSize) ? MinimumClientSize : xsize; ysize = (ysize < MinimumClientSize) ? MinimumClientSize : ysize; gwp->SetClientSize(xsize, ysize); } else { // update the window specs gwp->Update_Window_Data_WIN32(); } } return 0; case (WM_MOVE): // lParam contains coordinates of upper left of client window // but this is useless since we need to work with full window // hence we will get this information directly ::GetWindowRect(hwnd, &N); R = N; R.Get(x1, y1, x2, y2); // test if window has been shifted too far off the screen // but avoid doing another shift action if already attempted if (badshift) badshift = false; else { xspot = x1; yspot = y1; AdjustWindowSpot(xspot, yspot); badshift = (xspot != x1) || (yspot != y1); } if (badshift) { gwp->SetWindowSpot(xspot, yspot); // change the window spot } else { gwp->Update_Window_Data_WIN32(); // update the window specs } // force repaint for test code // this will not be done in production code! N = gwp->GetClientRect().Native(); InvalidateRect(hwnd, &N, TRUE); return 0; case (WM_CLOSE): gwp->PrepareToCloseWindow(); // delete structures etc // now break so DefWindowProc will initiate the standard closing sequence break; case (WM_DESTROY): // send the message that will eventually terminate both window and thread PostQuitMessage(0); return 0; default: break; } return DefWindowProc(hwnd, message, wParam, lParam); } // define and register wndclass for Core Tools applications // choose settings so that: // window is repainted on being resized // each window has its own device context void Define_CoreTools_wndclass() { CoreTools_wndclass.cbSize = sizeof(CoreTools_wndclass); CoreTools_wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; CoreTools_wndclass.lpfnWndProc = CoreTools_WndProc; CoreTools_wndclass.cbClsExtra = 0; CoreTools_wndclass.cbWndExtra = 0; CoreTools_wndclass.hInstance = CoreTools_hInstance; CoreTools_wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); CoreTools_wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); CoreTools_wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); CoreTools_wndclass.lpszMenuName = NULL; CoreTools_wndclass.lpszClassName = CoreTools_wndclassName; CoreTools_wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if (!RegisterClassEx(&CoreTools_wndclass)) { MessageBox(NULL, "RegisterClassEx Failure", CoreTools_wndclassName, MB_ICONERROR); throw("RegisterClassEx Failure"); } } // entry-point for Windows DLL int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved) { switch (fdwReason) { case DLL_THREAD_ATTACH: break; case DLL_PROCESS_ATTACH: CoreTools_hInstance = hInstance; Define_CoreTools_wndclass(); break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: break; default: // do nothing break; } return TRUE; } #endif // end WIN32 specific // END PLATFORM DEPENDENT CODE // /////////////////////////////////////////////////////////////////////// // initialization of the GraphicsWindowList called GWList class GWListClass { Array List; public: // append graphics window pointer bool Append(GraphicsWindow* gwp) { return List.Append(gwp); }; // return effective size of the window list int Size() const { return List.ValidSize(); }; // return graphics window pointer at the index GraphicsWindow* WindowPtr(int index) { // handle -1 default used in certain calls if (index < 0) index = GraphicsWindowIndex(); // now get pointer if possible int size = Size(); if ((0 <= index) && (index < size)) return List[index]; else return 0; }; // make the pointer at the index be null // this allows the window destructor to remove its own pointer in this list void MakeNull(int index) { int size = List.ValidSize(); if ((0 <= index) && (index < size)) List[index] = 0; } // make sure all graphics window objects are deleted when list dies ~GWListClass() { GraphicsWindow* gwp; int size = List.ValidSize(); int i; for (i = 0; i < size; i++) { gwp = List[i]; if (gwp) delete gwp; } }; } GWList; // access functions for GraphicsWindowList data int GraphicsWindowListSize() { return GWList.Size(); } GraphicsWindow* GraphicsWindowPtr(int index) { return GWList.WindowPtr(index); } // initialization of default graphics window index int DefaultGraphicsWindowIndex = 0; int GraphicsWindowIndex() { return DefaultGraphicsWindowIndex; } void SetGraphicsWindow(int index) { GraphicsWindow* gwp = GraphicsWindowPtr(index); if (gwp && gwp->IsOpen()) DefaultGraphicsWindowIndex = index; } void SetGraphicsWindow(const GraphicsWindow& G) { if (G.IsOpen()) DefaultGraphicsWindowIndex = G.Index(); } // get the area of the screen available for windows RectData GetScreenRect() { RectData ScreenRect; #if defined(CORE_PLATFORM_WIN32) NativeRectData NativeScreenRect; SystemParametersInfo(SPI_GETWORKAREA, 0, &NativeScreenRect, 0); ScreenRect = NativeScreenRect; #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) #endif // end MacOS specific return ScreenRect; } // GraphicsWindow functions void GraphicsWindow::Initialize(short xsize, short ysize, short xspot, short yspot) { // before opening any graphics window make sure console window is initialized InitializeConsole(); // WindowIndex will become the position of this window in the graphics window list WindowIndex = GWList.Size(); // insert pointer to this window in the graphics window list GWList.Append(this); // initialize title bar name sprintf(TitleBarName, "Graphics %d", WindowIndex); // initialize clientsize object ClientSize.SetMinAndDef(MinimumClientSize, DefaultClientSize); ClientSize.Set(xsize, ysize); // initialize upper left corner location WindowSpot.Set(xspot, yspot); // open the window WindowIsOpen = false; #if defined(CORE_PLATFORM_WIN32) // make sure hwnd variable is null before original window creation in WIN32 hwnd = 0; // also initialize the DrawList critical section InitializeCriticalSection(& DrawListCS); #endif #if defined(CORE_PLATFORM_MACOS) #endif // end MacOS specific OpenWindow(); } void GraphicsWindow::PrepareToOpenWindow() { // exit if window is open if (WindowIsOpen) return; WindowIsOpen = true; // create internal data structures } void GraphicsWindow::OpenWindow() { // exit if window is open if (WindowIsOpen) return; // set WindowIsOpen to true // create internal data structures PrepareToOpenWindow(); // open window via platform specific methods // // if the functions fail then they must call: // PrepareToCloseWindow(); // to delete internal data structures and set WindowIsOpen back to false #if defined(CORE_PLATFORM_WIN32) OpenWindow_WIN32(); #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) OpenWindow_MACOS(); #endif // end MacOS specific // initialize the graphics state if (WindowIsOpen) { PrepareGraphics(); WindowState.StartUp(); NativeState.SetFirst(); NativeState.StartUp(); ReleaseGraphics(); } } void GraphicsWindow::PrepareToCloseWindow() { // exit if window is not open if (!WindowIsOpen) return; WindowIsOpen = false; // delete internal data structures DrawListClear(); } void GraphicsWindow::CloseWindow() { // exit if window is not open if (!WindowIsOpen) return; // set WindowIsOpen to false // delete internal data structures PrepareToCloseWindow(); // release the graphics state PrepareGraphics(); NativeState.CleanUp(); ReleaseGraphics(); // close window via platform specific methods #if defined(CORE_PLATFORM_WIN32) CloseWindow_WIN32(); #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) CloseWindow_MACOS(); #endif // end MacOS specific } GraphicsWindow::~GraphicsWindow() { // close window will delete the data in this window's DrawList CloseWindow(); // zero pointer to this window object in the GraphicsWindowList GWList.MakeNull(WindowIndex); #if defined(CORE_PLATFORM_WIN32) // make sure that any existing hwnd for this object has been closed if (hwnd) CloseHandle(hwnd); // also delete the critical section DeleteCriticalSection(& DrawListCS); #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) #endif // end MacOS specific } void GraphicsWindow::ChangeWindowSpecs() { // exit if window is not open if (!WindowIsOpen) return; #if defined(CORE_PLATFORM_WIN32) Adjust_CoreTools_Window_WIN32(); #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) Adjust_CoreTools_Window_MACOS(); #endif // end MacOS specific } void GraphicsWindow::DrawListInsert(GraphicsObject* clone) { DrawListLock(); DrawList.Append(clone); // insert the clone DrawListUnlock(); } void GraphicsWindow::DrawListClear() { DrawListLock(); int size = DrawList.ValidSize(); int i; // step 1: delete clone objects in DrawList for (i = 0; i < size; i++) { delete DrawList[i]; // delete i-th clone object } // step 2: empty the DrawList DrawList.ValidateNone(); DrawList.ChangeMemory(1); DrawListUnlock(); } void GraphicsWindow::DrawListLock() { #if defined(CORE_PLATFORM_WIN32) EnterCriticalSection(&DrawListCS); #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) #endif // end MacOS specific } void GraphicsWindow::DrawListUnlock() { #if defined(CORE_PLATFORM_WIN32) LeaveCriticalSection(&DrawListCS); #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) #endif // end MacOS specific } void GraphicsWindow::PrepareClip() { #if defined(CORE_PLATFORM_WIN32) if (WindowState.EnableClip) SelectClipRgn(hdc, NativeState.ClipRgn); else SelectClipRgn(hdc, NULL); #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) #endif // end MacOS specific } void GraphicsWindow::PrepareText() { #if defined(CORE_PLATFORM_WIN32) // text color RGBdata Color; switch(WindowState.PenMode) { case PM_Normal: case PM_Invert: // cannot support invert so do normal SetTextColor(hdc, WindowState.TextColor.Native()); break; case PM_White: Color.Set(255, 255, 255); SetTextColor(hdc, Color.Native()); break; case PM_Black: Color.Set( 0, 0, 0); SetTextColor(hdc, Color.Native()); break; default: break; } // text align SetTextAlign(hdc, NativeState.TextAlign); // background mode switch (WindowState.BackgroundMode) { case BM_Opaque: SetBkMode(hdc, OPAQUE); // background color if needed SetBkColor(hdc, WindowState.BackgroundColor.Native()); break; case BM_Transparent: default: SetBkMode(hdc, TRANSPARENT); break; } #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) #endif // end MacOS specific } void GraphicsWindow::PrepareGraphics() { DrawListLock(); #if defined(CORE_PLATFORM_WIN32) hdc = GetDC(hwnd); #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) #endif // end MacOS specific PrepareClip(); } void GraphicsWindow::ReleaseGraphics() { #if defined(CORE_PLATFORM_WIN32) ReleaseDC(hwnd, hdc); #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) #endif // end MacOS specific DrawListUnlock(); } void GraphicsWindow::PreparePainting() { DrawListLock(); #if defined(CORE_PLATFORM_WIN32) hdc = BeginPaint(hwnd, &ps); #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) #endif // end MacOS specific } void GraphicsWindow::ReleasePainting() { #if defined(CORE_PLATFORM_WIN32) EndPaint(hwnd, &ps); #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) #endif // end MacOS specific DrawListUnlock(); } void GraphicsWindow::DrawListPaint() { PreparePainting(); WindowState.StartUp(); NativeState.StartUp(); EraseWindow(); int size = DrawList.ValidSize(); int i; for (i = 0; i < size; i++) DrawList[i]->DrawDetails(*this); #if defined(CORE_PLATFORM_WIN32) // WIN32 test code for repaint operation // will not be called in production code // // TestPaint_WIN32(); #endif // end WIN32 specific ReleasePainting(); } void GraphicsWindow::MakeForeground() const { // exit if window is not open if (!WindowIsOpen) return; #if defined(CORE_PLATFORM_WIN32) SetForegroundWindow(hwnd); #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) #endif // end MacOS specific } void GraphicsWindow::SetClientSize(short xsize, short ysize) { // set desired client extent ClientSize.Set(xsize, ysize); // now do platform specific window manipulation ChangeWindowSpecs(); } void GraphicsWindow::SetWindowSpot(short xspot, short yspot) { // make sure that window spot is reasonable AdjustWindowSpot(xspot, yspot); // set window upper left location WindowSpot.Set(xspot, yspot); // now do platform specific window manipulation ChangeWindowSpecs(); } void GraphicsWindow::SetWindowSpecs(short xsize, short ysize, short xspot, short yspot) { // set desired client extent ClientSize.Set(xsize, ysize); // set window upper left location WindowSpot.Set(xspot, yspot); // now do platform specific window manipulation ChangeWindowSpecs(); } void GraphicsWindow::PlaceRightConsole() { short x1, y1, x2, y2; GetConsoleCoordinates(x1, y1, x2, y2); SetWindowSpot(x2, y1); } void GraphicsWindow::PlaceBelowConsole() { short x1, y1, x2, y2; GetConsoleCoordinates(x1, y1, x2, y2); SetWindowSpot(x1, y2); } void GraphicsWindow::PlaceRight(const GraphicsWindow& G) { short x1, y1, x2, y2; G.GetCoordinates(x1, y1, x2, y2); SetWindowSpot(x2, y1); } void GraphicsWindow::PlaceBelow(const GraphicsWindow& G) { short x1, y1, x2, y2; G.GetCoordinates(x1, y1, x2, y2); SetWindowSpot(x1, y2); } void GraphicsWindow::PlaceRight(int index) { GraphicsWindow* gwp = GraphicsWindowPtr(index); if(gwp) PlaceRight(*gwp); } void GraphicsWindow::PlaceBelow(int index) { GraphicsWindow* gwp = GraphicsWindowPtr(index); if(gwp) PlaceBelow(*gwp); } void GraphicsWindow::ClearWindow() { DrawListClear(); Refresh(); } void GraphicsWindow::EraseWindow() { DrawListLock(); #if defined(CORE_PLATFORM_WIN32) // set up hdc for painting client white hdc = GetDC(hwnd); // invalidate clip region SelectClipRgn(hdc, NULL); // get client rect NativeRectData Client; ::GetClientRect(hwnd, &Client); // manually paint client rect white HBRUSH WhiteBrush = (HBRUSH) GetStockObject(WHITE_BRUSH); ::FillRect(hdc, &Client, WhiteBrush); // release hdc ReleaseDC(hwnd, hdc); #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) #endif // end MacOS specific DrawListUnlock(); } void GraphicsWindow::Refresh() { EraseWindow(); #if defined(CORE_PLATFORM_WIN32) ::InvalidateRect(hwnd, NULL, false); #endif // end WIN32 specific #if defined(CORE_PLATFORM_MACOS) #endif // end MacOS specific } /////////////////////////////////////////////////////////////////////// // BEGIN PLATFORM DEPENDENT CODE // #if defined(CORE_PLATFORM_WIN32) GraphicsWindow* GraphicsWindow::GWPtr(HWND hwnd) { int size = GraphicsWindowListSize(); int i; GraphicsWindow* gwp; for (i = 0; i < size; i++) { gwp = GraphicsWindowPtr(i); // i-th window pointer gwp if (gwp && gwp->hwnd == hwnd) // do hwnd's match? return gwp; // if so return gwp } return 0; // otherwise return null } void GraphicsWindow::OpenWindow_WIN32() { // create separate thread to initialize and control window CreateThreadAndWindow(); } void GraphicsWindow::CreateThreadAndWindow() { // step 1: create window-is-ready event // set initial state to FALSE (not ready) // set manual reset to TRUE (once set leave event in that state) eventWindowIsReady = CreateEvent(NULL, TRUE, FALSE, NULL); // step 2: create thread // note that: this pointer will be passed to CreateWindowAndLoop when it is called // in the new thread ThreadHandle = CreateThread(NULL, 0, CreateWindowAndLoop, this, 0, &ThreadID); // step 3: block application thread until CreateWindowAndLoop has completed window WaitForSingleObject(eventWindowIsReady, INFINITE); // step 4: close the handle to eventWindowIsReady since we will not need this CloseHandle(eventWindowIsReady); }; // Insert here the static or global routines for window and message loop DWORD WINAPI GraphicsWindow::CreateWindowAndLoop(LPVOID lp) { // step 1: extract the pointer to the graphics window GraphicsWindow* pThis = (GraphicsWindow*) lp; // step 2: create the window in this thread context pThis->Create_CoreTools_Window_WIN32(); // error check on step 3 if (!(pThis->hwnd)) { MessageBox(NULL, "Window Create Failure", pThis->TitleBarName, MB_ICONERROR); // delete internal data structures and set WindowIsOpen to false pThis->PrepareToCloseWindow(); // enable event to free application SetEvent(pThis->eventWindowIsReady); // immediate exit from the thread routine return 0; } // step 3: adjust client area if necessary to best meet caller requirements pThis->Adjust_CoreTools_Window_WIN32(); // step 4: show window and call for initial update operation ::ShowWindow (pThis->hwnd, SW_SHOWNORMAL); ::SetForegroundWindow(pThis->hwnd); ::UpdateWindow (pThis->hwnd); // step 5: release application thread SetEvent(pThis->eventWindowIsReady); // step 6: start and run the message loop for this window thread return ThreadWindowMessageLoop(); } DWORD WINAPI GraphicsWindow::ThreadWindowMessageLoop() { MSG msg; while (true) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (WM_QUIT == msg.message) break; TranslateMessage(&msg); DispatchMessage (&msg); } else { WaitMessage(); // put the thread to sleep until new message arrives } } return msg.wParam; } // Continue with other GraphicsWindow routines void GraphicsWindow::Create_CoreTools_Window_WIN32() { // extract desired client info short xsize; short ysize; short xspot; short yspot; ClientSize.Get(xsize, ysize); WindowSpot.Get(xspot, yspot); // make sure that any existing hwnd for this object has been closed if (hwnd) { CloseHandle(hwnd); hwnd = 0; } // create window hwnd = CreateWindow( CoreTools_wndclassName, // wndclass name ties with registered wndlass TitleBarName, // "Graphics xxx" WS_OVERLAPPEDWINDOW, // standard overlapped main window xspot, // x for upperleft corner yspot, // y for upperleft corner xsize, // xsize will initially be of full window ysize, // ysize will initially be of full window NULL, // no parent NULL, // no menu CoreTools_hInstance, // handle to application instance NULL // optional pointer ); } void GraphicsWindow::Adjust_CoreTools_Window_WIN32() { // extract desired client info short xsize; short ysize; short xspot; short yspot; ClientSize.Get(xsize, ysize); WindowSpot.Get(xspot, yspot); // using existing window information determine extra space needed for borders etc // use native rects to get size information from WIN32 short xExtra; short yExtra; NativeRectData entire; // entire window NativeRectData client; // client region normalized with (0,0) at topleft ::GetWindowRect(hwnd, &entire); ::GetClientRect(hwnd, &client); xExtra = (entire.right - entire.left) - (client.right /* - client.left */); yExtra = (entire.bottom - entire.top) - (client.bottom /* - client.top */); // now adjust coordinates of entire window to produce desired client window int x1 = xspot; int y1 = yspot; int x2 = xsize + xExtra; int y2 = ysize + yExtra; // now grow the window SetWindowPos(hwnd, HWND_BOTTOM, x1, y1, x2, y2, SWP_NOZORDER); // the parameter SWP_NOZORDER says ignore Z-order parameter 2 // // notice that we do not actually show the window in the above call // the window is actually opened and brought to the foreground // by the subsequent calls in CreateWindowAndLoop // // ::ShowWindow (pThis->hwnd, SW_SHOWNORMAL); // ::SetForegroundWindow(pThis->hwnd); // ::UpdateWindow (pThis->hwnd); // since WIN32 may not have achieved our wishes // find out the actual window information // and set our internal member data accordingly Update_Window_Data_WIN32(); } void GraphicsWindow::Update_Window_Data_WIN32() { // use native rects to get size information from WIN32 NativeRectData entire; // entire window NativeRectData client; // client region normalized with (0,0) at topleft // find client rect and entire window rect ::GetClientRect(hwnd, &client); ::GetWindowRect(hwnd, &entire); // save this information ClientRect = client; ClientSize.Set(client.right, client.bottom); WindowRect = entire; WindowSpot.Set(entire.left, entire.top ); } void GraphicsWindow::CloseWindow_WIN32() { // a window in another thread can only be closed via SendMessage SendMessage(hwnd, WM_CLOSE, 0, 0); } void GraphicsWindow::WIN32_Show_Region(HRGN Region, HBRUSH NormalBrush) { HBRUSH SpecialBrush; switch (WindowState.PenMode) { case PM_Normal: FillRgn(hdc, Region, NormalBrush); break; case PM_White: SpecialBrush = (HBRUSH) GetStockObject(WHITE_BRUSH); FillRgn(hdc, Region, SpecialBrush); break; case PM_Black: SpecialBrush = (HBRUSH) GetStockObject(BLACK_BRUSH); FillRgn(hdc, Region, SpecialBrush); break; case PM_Invert: InvertRgn(hdc, Region); break; default: break; } } HRGN GraphicsWindow::WIN32_Line_Region(const PointData& P, const PointData& Q) { // grab coordinates int x1, y1, x2, y2; P.Get(x1, y1); Q.Get(x2, y2); int xpen = NativeState.xpen; int ypen = NativeState.ypen; // define region variable HRGN LinePixels; // vertical line if (x1 == x2) { if (y1 <= y2) LinePixels = CreateRectRgn(x1, y1, x2 + xpen, y2 + ypen); else LinePixels = CreateRectRgn(x1, y2, x2 + xpen, y1 + ypen); return LinePixels; } // horizontal line if (y1 == y2) { if (x1 <= x2) LinePixels = CreateRectRgn(x1, y1, x2 + xpen, y2 + ypen); else LinePixels = CreateRectRgn(x2, y1, x1 + xpen, y2 + ypen); return LinePixels; } /* The six sided polygon method is reasonably fast but creates artifacts We will use a more painstaking approach below. // slant line ... create region as six-sided polygon // define point array to set up miniature polygon region PointData Points[6]; if (x1 < x2) { if (y1 < y2) { Points[0].Set(x1, y1); Points[1].Set(x1 + xpen, y1); Points[2].Set(x2 + xpen, y2); Points[3].Set(x2 + xpen, y2 + ypen); Points[4].Set(x2, y2 + ypen); Points[5].Set(x1, y1 + ypen); } else { Points[0].Set(x1, y1); Points[1].Set(x2, y2); Points[2].Set(x2 + xpen, y2); Points[3].Set(x2 + xpen, y2 + ypen); Points[4].Set(x1 + xpen, y1 + ypen); Points[5].Set(x1, y1 + ypen); } } else { if (y1 < y2) { Points[0].Set(x2, y2); Points[1].Set(x1, y1); Points[2].Set(x1 + xpen, y1); Points[3].Set(x1 + xpen, y1 + ypen); Points[4].Set(x2 + xpen, y2 + ypen); Points[5].Set(x2, y2 + ypen); } else { Points[0].Set(x2, y2); Points[1].Set(x2 + xpen, y2); Points[2].Set(x1 + xpen, y1); Points[3].Set(x1 + xpen, y1 + ypen); Points[4].Set(x1, y1 + ypen); Points[5].Set(x2, y2 + xpen); } } const NativePointData* polygonpointer = (const NativePointData*) (&Points); LinePixels = CreatePolygonRgn(polygonpointer, 6, ALTERNATE); */ // Initialize LinePixels to an empty region // Set up Bresenham algorithm // See Foley, van Dam, Feiner, Hughes: Computer Graphics, 2nd Ed., Page 78 short decision; // decision variable: along axes versus along diagonal short x = x1; // initial point to plot short y = y1; // initial point to plot short dx = AbsValue(x2 - x1); short dy = AbsValue(y2 - y1); short dmax; // max of dx and dy short dmin; // min of dx and dy short xa; // increment of x if along axes short ya; // increment of y if along axes short ia; // increment of decision if along axes short xd; // increment of x if along diagonal short yd; // increment of y if along diagonal short id; // increment of decision if along diagonal if (dy <= dx) { dmax = dx; dmin = dy; // along x axis xa = (x1 < x2) ? 1 : -1; ya = 0; // along diagonal xd = xa; yd = (y1 < y2) ? 1 : -1; } else { dmax = dy; dmin = dx; // along y axis xa = 0; ya = (y1 < y2) ? 1 : -1; // along diagonal xd = (x1 < x2) ? 1 : -1; yd = ya; } decision = dmin + dmin - dmax; ia = decision + dmax; // dmin + dmin id = decision - dmax; // dmin + dmin - dmax - dmax LinePixels = CreateRectRgn(0, 0, 0, 0); HRGN Next = CreateRectRgn(x1, y1, x1 + xpen, y1 + ypen); for (short i = 0; i <= dmax; i++) { // add the next rectangular block to the line region CombineRgn(LinePixels, LinePixels, Next, RGN_OR); // do Bresenham update if (decision <= 0) { decision += ia; OffsetRgn(Next, xa, ya); } else { decision += id; OffsetRgn(Next, xd, yd); } } DeleteObject(Next); return LinePixels; } void GraphicsWindow::TestPaint_WIN32() const { // test paint code // see Petzold's HelloWin program for use of DrawText short xsize, ysize; // client size short x1, y1, x2, y2; // window bounds RectData R; // temporary variable NativeRectData N; // temporary variable char buffer[81]; // temporary variable for strings to paint // get client size GetClientSize(xsize, ysize); // get window bounds R = GetWindowRect(); R.Get(x1, y1, x2, y2); // output the window's specs on two lines 1/3 and 2/3 way down in window // in test mode this will overwrite other window contents sprintf(buffer, "%d %d", xsize, ysize); // buffer = client size R.Set(0, 0, xsize, (2 * ysize) / 3); // R is upper two thirds N = R.Native(); // N is native R DrawText(hdc, buffer, -1, &N, DT_SINGLELINE | DT_CENTER | DT_VCENTER); // center string in N sprintf(buffer, "%d %d %d %d", x1, y1, x2, y2); // buffer = window bounds R.Set(0, ysize / 3, xsize, ysize); // R is lower two thirds N = R.Native(); // N is native R DrawText(hdc, buffer, -1, &N, DT_SINGLELINE | DT_CENTER | DT_VCENTER); // center string in N } #endif // end WIN32 specific /////////////////////////////////////////////////////////////////////// #if defined(CORE_PLATFORM_MACOS) // GraphicsWindow member functions void GraphicsWindow::OpenWindow_MACOS() { } void GraphicsWindow::CloseWindow_MACOS() { } void GraphicsWindow::Create_CoreTools_Window_MACOS() { } void GraphicsWindow::Adjust_CoreTools_Window_MACOS() { } void GraphicsWindow::Update_Window_Data_MACOS() { } #endif // end MacOS specific // END PLATFORM DEPENDENT CODE // ///////////////////////////////////////////////////////////////////////