/**************************************************************************** ** COPYRIGHT (C): 1997 Cay S. Horstmann. All Rights Reserved. ** PROJECT: Practical OO Development with C++ and Java ** FILE: graphics.cpp ** PURPOSE: Java-style graphics context ** VERSION 1.0 ** PROGRAMMERS: Cay Horstmann (CSH), Jiaoyang Zhou (JYZ) ** RELEASE DATE: 3-15-97 (CSH) ** UPDATE HISTORY: ****************************************************************************/ #include "setup.h" #include #include #include #include #include const int NORM_WIDTH = 1; const int THICK_WIDTH = 3; const int DEFAULT_XMIN = 0; const int DEFAULT_YMIN = 0; const int DEFAULT_XMAX = 20; const int DEFAULT_YMAX = 15; EXPORT class Graphics; EXPORT extern void paint(Graphics& g); /*-------------------------------------------------------------------------*/ class Bitmap { public: Bitmap(); void init(HWND hwnd); void close() const; void repaint(PAINTSTRUCT&) const; void clear_bitmap(COLORREF color) const; HDC hdc() const; private: HDC _hdc; // a long-lived device context HBITMAP _hbm, _hbm_saved; // handles to the created bitmap objects int _disp_xmax; // width of the bitmap (in bits) int _disp_ymax; // height of the bitmap (in bits) }; /*-------------------------------------------------------------------------*/ EXPORT class Font { public: enum { BOLD = 1, ITALIC = 2 }; Font(); Font(string, int, int); string getName() const { return _name; } int getStyle() const { return _style; } int getSize() const { return _size; } private: string _name; int _size; int _style; }; Font::Font() /* default constructor */ : _name(""), _size(0), _style(0) {} Font::Font(string name, int size, int style) /* RECEIVES: name - the font name size - the point size style - the style */ : _name(name), _size(size), _style(style) {} void makeLogFont(const Font& f, LOGFONT& lf, HDC hdc) { lf.lfHeight = MulDiv(f.getSize(), GetDeviceCaps(hdc, LOGPIXELSY), 72); lf.lfWidth = 0; lf.lfWeight = 0; lf.lfEscapement = 0; lf.lfWeight = (f.getStyle() & Font::BOLD) ? FW_BOLD : FW_NORMAL; lf.lfItalic = (f.getStyle() & Font::ITALIC) ? 1 : 0; lf.lfUnderline = 0; lf.lfStrikeOut = 0; lf.lfCharSet = ANSI_CHARSET; lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = DEFAULT_QUALITY, lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; strcpy(lf.lfFaceName, f.getName().c_str()); } EXPORT class FontMetricsImpl { public: virtual int stringWidth(string s) = 0; virtual int getAscent() = 0; virtual int getDescent() = 0; }; EXPORT class FontMetrics { public: FontMetrics(FontMetricsImpl* fmi) : _impl(fmi) {} int stringWidth(string s) { return _impl->stringWidth(s); } int getAscent() { return _impl->getAscent(); } int getDescent() { return _impl->getDescent(); } private: FontMetricsImpl* _impl; }; class WindowsFontMetricsImpl : public FontMetricsImpl { public: WindowsFontMetricsImpl(HDC); int stringWidth(string s); int getAscent() { return _tm.tmAscent; } int getDescent() { return _tm.tmDescent; } private: HDC _hdc; TEXTMETRIC _tm; FontMetrics(HDC); friend class Graphics; }; WindowsFontMetricsImpl::WindowsFontMetricsImpl(HDC hdc) : _hdc(hdc) { GetTextMetrics(_hdc, &_tm); } EXPORT class Color { public: static Color black; static Color blue; static Color cyan; static Color darkGray; static Color gray; static Color green; static Color lightGray; static Color magenta ; static Color orange ; static Color pink ; static Color red ; static Color white ; static Color yellow ; Color(int r, int g, int b) : _red(r), _green(g), _blue(b) {} private: int _red; int _green; int _blue; friend class WinGraphics; }; Color Color::black(0,0,0); Color Color::blue(0,0,255); Color Color::cyan(0,128,128); Color Color::darkGray(64, 64, 64); Color Color::gray(128, 128, 128); Color Color::green(0, 255, 0); Color Color::lightGray(192, 192, 192); Color Color::magenta(128,0,128); Color Color::orange(255,96,0); Color Color::pink(255,128,128); Color Color::red(255,0,0); Color Color::white(255,255,255); Color Color::yellow(255,255,0); EXPORT class Graphics { public: virtual void drawLine(int xfrom, int yfrom, int xto, int yto) = 0; virtual void drawRect(int x, int y, int width, int height) = 0; virtual void fillRect(int x, int y, int width, int height) = 0; virtual void drawOval(int x, int y, int width, int height) = 0; virtual void drawString(string t, int x, int y) = 0; virtual void setColor(Color color) = 0; virtual void setFont(Font font) = 0; virtual Font getFont() = 0; virtual FontMetrics getFontMetrics() = 0; }; class WinGraphics : public Graphics // PURPOSE: Sends graphics primitives to a display window { public: WinGraphics(); void drawLine(int xfrom, int yfrom, int xto, int yto); void drawRect(int x, int y, int width, int height); void fillRect(int x, int y, int width, int height); void drawOval(int x, int y, int width, int height); void drawString(string t, int x, int y); void setColor(Color color); void setFont(Font font); Font getFont(); FontMetrics getFontMetrics() { return FontMetrics(new WindowsFontMetricsImpl(_hdc)); } private: HBRUSH _hbrush; // currently selected brush HPEN _hpen; HFONT _hfont; LOGFONT _font; Bitmap& _bm; // the bitmap to draw on HDC _hdc; // device context to the display TEXTMETRIC _tm; }; static Bitmap mainwin_bm; static HDC mainwin_hdc; static long FAR PASCAL win_proc(HWND hwnd, UINT message, UINT wParam, LONG lParam) /* PURPOSE: Window procedure for graphics window */ { static bool paint_flag = true; // tells WinProc to call chi_main() PAINTSTRUCT ps; // the display's paint struct switch (message) { case WM_PAINT: mainwin_hdc = BeginPaint(hwnd, &ps); if (paint_flag) { mainwin_bm.init(hwnd); paint_flag = false; WinGraphics g; paint(g); } else mainwin_bm.repaint(ps); EndPaint(hwnd, &ps); break; case WM_DESTROY: mainwin_bm.close(); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } /*-------------------------------------------------------------------------*/ int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow) { MSG msg; WNDCLASS wndclass; char title[80]; GetModuleFileName(hInstance, title, sizeof(title)); strncat(title, " ", sizeof(title) - strlen(title)); strncat(title, lpszCmdParam, sizeof(title) - strlen(title)); title[sizeof(title) - 1] = 0; if (!hPrevInstance) { wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = win_proc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); wndclass.hbrBackground = GetStockObject (WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = "GRAPHICSWIN"; RegisterClass (&wndclass); } HWND hwnd = CreateWindow("GRAPHICSWIN", title, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, SW_SHOWNORMAL); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } /*-------------------------------------------------------------------------*/ WinGraphics::WinGraphics() : _hfont(0), _hpen(0), _hbrush(0), _hdc(mainwin_hdc), _bm(mainwin_bm) { setFont(Font("Arial", 0, 12)); setColor(Color::black); } //........................................................................... void WinGraphics::drawOval(int x, int y, int width, int height) /* PURPOSE: Plot an ellipse on device context _hdc RECEIVES: x, y - center ra, rb - radii in x- and y- direction */ { HGDIOBJ oldBrush = SelectObject(_hdc, GetStockObject(HOLLOW_BRUSH)); Ellipse(_hdc, x - width / 2, y - height / 2, x + width / 2, y + height / 2); SelectObject(_hdc, oldBrush); oldBrush = SelectObject(_bm.hdc(), GetStockObject(HOLLOW_BRUSH)); Ellipse(_bm.hdc(), x - width / 2, y - height / 2, x + width / 2, y + height / 2); SelectObject(_hdc, oldBrush); } //........................................................................... void WinGraphics::drawLine(int xfrom, int yfrom, int xto, int yto) /* PURPOSE: plot a line segment with current pen style and color RECEIVES: _hdc - the DC on which to draw xfrom, yfrom - starting point xto, yto - ending point */ { MoveToEx(_hdc, xfrom, yfrom, 0); LineTo(_hdc, xto, yto); MoveToEx(_bm.hdc(), xfrom, yfrom, 0); LineTo(_bm.hdc(), xto, yto); } //........................................................................... void WinGraphics::drawRect(int xleft, int ytop, int width, int height) /* PURPOSE: Plot a rectangle RECEIVES: xleft, ytop - the top left corner width, height - the width and height of the rectangle REMARKS: Recode to take two corners (ie, Point p1, p2) */ { HGDIOBJ oldBrush = SelectObject(_hdc, GetStockObject(HOLLOW_BRUSH)); Rectangle(_hdc, xleft, ytop, xleft + width, ytop + height); SelectObject(_hdc, oldBrush); oldBrush = SelectObject(_bm.hdc(), GetStockObject(HOLLOW_BRUSH)); Rectangle(_bm.hdc(), xleft, ytop, xleft + width, ytop + height); SelectObject(_hdc, oldBrush); } //........................................................................... void WinGraphics::fillRect(int xleft, int ytop, int width, int height) /* PURPOSE: Plot a filled rectangle RECEIVES: xleft, ytop - the top left corner width, height - the width and height of the rectangle REMARKS: Recode to take two corners (ie, Point p1, p2) */ { Rectangle(_hdc, xleft, ytop, xleft + width, ytop + height); Rectangle(_bm.hdc(), xleft, ytop, xleft + width, ytop + height); } //........................................................................... void WinGraphics::setColor(Color color) /* PURPOSE: set the color of the brush that colors the insides of closed shapes (rectangles, polygons, ellipses) RECEIVES: color - the brush color */ { LOGBRUSH lb; lb.lbHatch = 0; lb.lbColor = RGB(color._red, color._green, color._blue); lb.lbStyle = BS_SOLID; HBRUSH brush = CreateBrushIndirect(&lb); SelectObject(_hdc, brush); SelectObject(_bm.hdc(), brush); if (_hbrush != 0) DeleteObject(_hbrush); HPEN pen = CreatePen(PS_SOLID, NORM_WIDTH, lb.lbColor); SelectObject(_hdc, pen); SelectObject(_bm.hdc(), pen); if (_hpen != 0) DeleteObject(_hpen); SetTextColor(_hdc, lb.lbColor); _hpen = pen; _hbrush = brush; } //........................................................................... void WinGraphics::setFont(Font f) { makeLogFont(f, _font, _hdc); HFONT font = CreateFontIndirect(&_font); SelectObject(_hdc, font); SelectObject(_bm.hdc(), font); DeleteObject(_hfont); _hfont = font; GetTextMetrics(_hdc, &_tm); } Font WinGraphics::getFont() { return Font(_font.lfFaceName, _font.lfHeight = MulDiv(_font.lfHeight, 72, GetDeviceCaps(_hdc, LOGPIXELSY)), (_font.lfWeight ? Font::BOLD : 0 ) + (_font.lfItalic ? Font::ITALIC : 0)); } //........................................................................... void WinGraphics::drawString(string s, int x, int y) /* PURPOSE: plot text in the current font RECEIVES: t - the text x, y - top left coordinate */ { const char* t = s.c_str(); SetBkMode(_hdc, TRANSPARENT); TextOut(_hdc, x, y - _tm.tmAscent, t, lstrlen(t)); SetBkMode(_bm.hdc(), TRANSPARENT); TextOut(_bm.hdc(), x, y - _tm.tmAscent, t, lstrlen(t)); } //........................................................................... int WindowsFontMetricsImpl::stringWidth(string s)//change from FontMetrics { const char* t = s.c_str(); SIZE sz; GetTextExtentPoint32(_hdc, t, s.length(), &sz); return sz.cx; } /*-------------------------------------------------------------------------*/ Bitmap::Bitmap() { _disp_xmax = GetSystemMetrics(SM_CXFULLSCREEN); _disp_ymax = GetSystemMetrics(SM_CYFULLSCREEN); } //............................................................................ void Bitmap::init(HWND hwnd) /* PURPOSE: Initializes the bitmap window structures. RECEIVES: Handle to the window in which it is created. RETURNS: A device context specific to this bitmap. REMARKS: We need to know a HDC of the main window in order to assure the bitmap has the proper number of color planes, bits per pixel, etc. */ { HDC hdc = GetDC(hwnd); _hdc = CreateCompatibleDC(hdc); _hbm = CreateCompatibleBitmap(hdc, _disp_xmax, _disp_ymax); ReleaseDC(hwnd, hdc); if (!_hbm) { MessageBox(hwnd, (LPSTR)"Memory problems in Bitmap Creation", (LPSTR)"Lib Error", MB_ICONHAND); SendMessage(hwnd, WM_DESTROY, 0, 0L); return; } _hbm_saved = SelectObject(_hdc, _hbm); clear_bitmap(RGB(255,255,255)); // clear to white } //............................................................................ void Bitmap::close() const /* PURPOSE: Deallocates resources for this bitmap */ { SelectObject(_hdc, _hbm_saved); DeleteDC(_hdc); DeleteObject(_hbm); } //............................................................................ void Bitmap::repaint(PAINTSTRUCT& ps) const /* PURPOSE: Repaints with the saved bitmap--use when resizing. RECEIVES: A pointer to the PAINTSTRUCT in WinProc */ { BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom, _hdc, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY); } //............................................................................ void Bitmap::clear_bitmap(COLORREF color) const /* PURPOSE: Clears the bitmap RECEIVES: color - the color to fill the bitmap */ { HBRUSH brush = CreateSolidBrush(color); HBRUSH saved_brush = SelectObject(_hdc, brush); PatBlt(_hdc, 0, 0, _disp_xmax, _disp_ymax, PATCOPY); SelectObject(_hdc, saved_brush); DeleteObject(brush); // note our greed in not releasing a DC } //........................................................................... HDC Bitmap::hdc() const /* RETURNS: the device context handle associated with this bitmap */ { return _hdc; }