/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "common/system.h"
#include "common/textconsole.h"
#include "bagel/mfc/afxwin.h"
#include "bagel/mfc/win_hand.h"
#include "bagel/mfc/gfx/blitter.h"
namespace Bagel {
namespace MFC {
IMPLEMENT_DYNCREATE(CDC, CObject)
CDC::~CDC() {
if (m_hDC && _permanent)
DeleteDC();
else if (m_hDC) {
auto *pMap = AfxGetApp()->afxMapHDC(true);
assert(!pMap->LookupTemporary(m_hDC));
}
}
CDC *CDC::FromHandle(HDC hDC) {
CHandleMap *pMap = AfxGetApp()->afxMapHDC(true);
assert(pMap != nullptr);
CDC *pObject = pMap->FromHandle(hDC);
assert(pObject == nullptr || pObject->m_hDC == hDC);
return pObject;
}
void CDC::AfxHookObject() {
CHandleMap *pMap = AfxGetApp()->afxMapHDC(true);
assert(pMap != nullptr);
if (!pMap->LookupPermanent(m_hDC)) {
pMap->SetPermanent(m_hDC, this);
_permanent = true;
}
}
void CDC::AfxUnhookObject() {
if (m_hDC && _permanent) {
CHandleMap *pMap = AfxGetApp()->afxMapHDC(true);
assert(pMap != nullptr);
pMap->RemoveHandle(m_hDC);
_permanent = false;
}
}
bool CDC::CreateDC(const char *lpszDriverName, const char *lpszDeviceName,
const char *lpszOutput, const void *lpInitData) {
error("TODO: CDC::CreateDC");
}
bool CDC::CreateCompatibleDC(CDC *pDC) {
assert(!m_hDC);
CDC::Impl *dc = new CDC::Impl();
if (pDC) {
dc->setFormat(pDC->impl()->getFormat());
} else {
CDC::Impl *src = (CDC::Impl *)MFC::GetDC(nullptr);
dc->setFormat(src->getFormat());
MFC::ReleaseDC(nullptr, src);
}
m_hDC = dc;
// This is where it becomes permanent
AfxHookObject();
return true;
}
bool CDC::DeleteDC() {
AfxUnhookObject();
CDC::Impl *dc = static_cast(m_hDC);
delete dc;
m_hDC = nullptr;
return true;
}
bool CDC::Attach(HDC hDC) {
assert(m_hDC == nullptr);
if (hDC == nullptr)
return false;
m_hDC = hDC;
AfxHookObject();
return true;
}
HDC CDC::Detach() {
HDC hDC = m_hDC;
if (hDC != nullptr)
AfxUnhookObject();
m_hDC = nullptr;
return hDC;
}
int CDC::SetStretchBltMode(int nStretchMode) {
int oldMode = _stretchMode;
_stretchMode = nStretchMode;
return oldMode;
}
int CDC::GetDeviceCaps(int nIndex) const {
return MFC::GetDeviceCaps(m_hDC, nIndex);
}
int CDC::GetMapMode() const {
error("TODO: CDC::GetMapMode");
}
int CDC::SetMapMode(int nMapMode) {
assert(nMapMode == MM_TEXT);
return MM_TEXT;
}
CPoint CDC::SetViewportOrg(int x, int y) {
auto *surface = impl()->getSurface();
CPoint oldPos = surface->getViewportOrg();
surface->setViewportOrg(CPoint(x, y));
return oldPos;
}
CPoint CDC::SetViewportOrg(POINT point) {
auto *surface = impl()->getSurface();
CPoint oldPos = surface->getViewportOrg();
surface->setViewportOrg(point);
return oldPos;
}
CPoint CDC::GetViewportOrg() const {
return impl()->getSurface()->getViewportOrg();
}
CPoint CDC::OffsetViewportOrg(int nWidth, int nHeight) {
auto *surface = impl()->getSurface();
surface->offsetViewportOrg(nWidth, nHeight);
return surface->getViewportOrg();
}
int CDC::GetClipBox(LPRECT lpRect) const {
Common::Rect rect = impl()->getSurface()->getClipRect();
*lpRect = RectToRECT(rect);
return rect.isEmpty() ? NULLREGION : SIMPLEREGION;
}
void CDC::setClipRect(const Common::Rect &r) {
CDC::Impl *dc = static_cast(m_hDC);
assert(dc);
dc->getSurface()->setClipRect(r);
}
void CDC::resetClipRect() {
CDC::Impl *dc = static_cast(m_hDC);
assert(dc);
dc->getSurface()->resetClip();
}
bool CDC::PtVisible(int x, int y) {
Gfx::Surface *surface = impl()->getSurface();
Common::Rect clipRect = surface->getClipRect();
// Clip rect is in device co-ordinates, but the
// point is in logical units, so need to convert
POINT pts[2];
pts[0].x = clipRect.left;
pts[0].y = clipRect.top;
pts[1].x = clipRect.right;
pts[1].y = clipRect.bottom;
DPtoLP(pts, 2);
return Common::Rect(pts[0].x, pts[0].y, pts[1].x, pts[1].y).contains(x, y);
}
bool CDC::PtVisible(POINT point) {
return PtVisible(point.x, point.y);
}
bool CDC::RectVisible(LPCRECT lpRect) {
Gfx::Surface *surface = impl()->getSurface();
Common::Rect clipRect = surface->getClipRect();
// Clip rect is in device co-ordinates, but the
// point is in logical units, so need to convert
POINT pts[2];
pts[0].x = clipRect.left;
pts[0].y = clipRect.top;
pts[1].x = clipRect.right;
pts[1].y = clipRect.bottom;
DPtoLP(pts, 2);
return Common::Rect(pts[0].x, pts[0].y, pts[1].x, pts[1].y).intersects(*lpRect);
}
int CDC::SelectClipRgn(CRgn *pRgn) {
// Custom clipping regions not supported in ScummVM yet
assert(!pRgn);
impl()->getSurface()->resetClip();
return SIMPLEREGION;
}
int CDC::ExcludeClipRect(int x1, int y1, int x2, int y2) {
error("TODO: CDC::ExcludeClipRect");
}
int CDC::ExcludeClipRect(LPCRECT lpRect) {
error("TODO: CDC::ExcludeClipRect");
}
int CDC::ExcludeUpdateRgn(CWnd *pWnd) {
error("TODO: CDC::ExcludeUpdateRgn");
}
int CDC::IntersectClipRect(int x1, int y1, int x2, int y2) {
CDC::Impl *dc = static_cast(m_hDC);
assert(dc);
return dc->getSurface()->intersectClipRect(
Common::Rect(x1, y1, x2, y2));
}
int CDC::IntersectClipRect(LPCRECT lpRect) {
return impl()->getSurface()->intersectClipRect(*lpRect);
}
int CDC::OffsetClipRgn(int x, int y) {
return impl()->getSurface()->offsetClipRect(x, y);
}
int CDC::OffsetClipRgn(SIZE size) {
return impl()->getSurface()->offsetClipRect(size.cx, size.cy);
}
int CDC::SelectClipRgn(CRgn *pRgn, int nMode) {
error("TODO: CDC::SelectClipRgn");
}
int CDC::SetROP2(int nDrawMode) {
return impl()->setROP2(nDrawMode);
}
bool CDC::DPtoLP(LPPOINT lpPoints, int nCount) {
// Currently we only support MM_TEXT mode,
// which has a 1 to 1 mapping, which simplifies matters
const CPoint WINDOW_ORG(0, 0);
const CPoint VIEWPORT_ORG = impl()->getSurface()->getViewportOrg();
for (int i = 0; i < nCount; ++i, ++lpPoints) {
lpPoints->x += WINDOW_ORG.x - VIEWPORT_ORG.x;
lpPoints->y += WINDOW_ORG.y - VIEWPORT_ORG.y;
}
return true;
}
bool CDC::DPtoLP(RECT *lpRect) {
// Currently we only support MM_TEXT mode,
// which has a 1 to 1 mapping, which simplifies matters
const CPoint WINDOW_ORG(0, 0);
const CPoint VIEWPORT_ORG = impl()->getSurface()->getViewportOrg();
lpRect->left += WINDOW_ORG.x - VIEWPORT_ORG.x;
lpRect->right += WINDOW_ORG.x - VIEWPORT_ORG.x;
lpRect->top += WINDOW_ORG.y - VIEWPORT_ORG.y;
lpRect->bottom += WINDOW_ORG.y - VIEWPORT_ORG.y;
return true;
}
bool CDC::LPtoDP(LPPOINT lpPoints, int nCount) {
// Currently we only support MM_TEXT mode,
// which has a 1 to 1 mapping, which simplifies matters
const CPoint WINDOW_ORG(0, 0);
const CPoint VIEWPORT_ORG = impl()->getSurface()->getViewportOrg();
for (; nCount > 0; --nCount, ++lpPoints) {
lpPoints->x += VIEWPORT_ORG.x - WINDOW_ORG.x;
lpPoints->y += VIEWPORT_ORG.y - WINDOW_ORG.y;
}
return true;
}
bool CDC::LPtoDP(RECT *lpRect) {
// Currently we only support MM_TEXT mode,
// which has a 1 to 1 mapping, which simplifies matters
const CPoint WINDOW_ORG(0, 0);
const CPoint VIEWPORT_ORG = impl()->getSurface()->getViewportOrg();
lpRect->left += VIEWPORT_ORG.x - WINDOW_ORG.x;
lpRect->right += VIEWPORT_ORG.x - WINDOW_ORG.x;
lpRect->top += VIEWPORT_ORG.y - WINDOW_ORG.y;
lpRect->bottom += VIEWPORT_ORG.y - WINDOW_ORG.y;
return true;
}
bool CDC::BitBlt(int x, int y, int nWidth, int nHeight, CDC *pSrcDC,
int xSrc, int ySrc, uint32 dwRop) {
impl()->bitBlt(x, y, nWidth, nHeight, pSrcDC, xSrc, ySrc, dwRop);
return true;
}
bool CDC::StretchBlt(int x, int y, int nWidth, int nHeight, CDC *pSrcDC,
int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, uint32 dwRop) {
impl()->stretchBlt(x, y, nWidth, nHeight, pSrcDC,
xSrc, ySrc, nSrcWidth, nSrcHeight, dwRop);
return true;
}
void CDC::Ellipse(LPCRECT lpRect) {
impl()->ellipse(lpRect);
}
void CDC::Ellipse(int x1, int y1, int x2, int y2) {
impl()->ellipse(x1, y1, x2, y2);
}
void CDC::FrameRect(LPCRECT lpRect, CBrush *pBrush) {
impl()->frameRect(*lpRect, pBrush);
}
void CDC::Draw3dRect(const CRect &rect, COLORREF clrTopLeft, COLORREF clrBottomRight) {
impl()->draw3dRect(rect, clrTopLeft, clrBottomRight);
}
void CDC::DrawFocusRect(const CRect &rect) {
impl()->drawFocusRect(rect);
}
void CDC::FillRect(LPCRECT lpRect, CBrush *pBrush) {
CBrush::Impl *brush = static_cast(pBrush->m_hObject);
assert(brush->_type == HS_HORIZONTAL ||
brush->_type == HS_VERTICAL);
FillSolidRect(lpRect, brush->getColor());
}
void CDC::FillSolidRect(LPCRECT lpRect, COLORREF color) {
auto *surf = impl();
surf->fillRect(*lpRect, color);
}
bool CDC::FloodFill(int x, int y, COLORREF crColor) {
impl()->floodFill(x, y, crColor);
return true;
}
bool CDC::FloodFill(int x, int y, COLORREF crColor,
unsigned int nFillType) {
impl()->floodFill(x, y, crColor, nFillType);
return true;
}
void CDC::Rectangle(LPCRECT lpRect) {
impl()->rectangle(lpRect);
}
void CDC::Rectangle(int x1, int y1, int x2, int y2) {
impl()->rectangle(x1, y1, x2, y2);
}
bool CDC::Pie(int x1, int y1, int x2, int y2,
int x3, int y3, int x4, int y4) {
error("TODO: CDC::Pie");
}
bool CDC::DrawEdge(LPRECT lpRect, unsigned int nEdge, unsigned int nFlags) {
CRect rect = *lpRect;
// Determine edge colors
COLORREF clrTL = GetSysColor(COLOR_3DHIGHLIGHT); // Top-left
COLORREF clrBR = GetSysColor(COLOR_3DSHADOW); // Bottom-right
// Adjust for EDGE_SUNKEN
if (nEdge == EDGE_SUNKEN || nEdge == EDGE_ETCHED)
SWAP(clrTL, clrBR);
// Create pens
CPen penTL(PS_SOLID, 1, clrTL);
CPen penBR(PS_SOLID, 1, clrBR);
CPen *pOldPen = SelectObject(&penTL);
// Draw top and left
if (nFlags & BF_TOP) {
MoveTo(rect.left, rect.top);
LineTo(rect.right - 1, rect.top);
}
if (nFlags & BF_LEFT) {
MoveTo(rect.left, rect.top);
LineTo(rect.left, rect.bottom - 1);
}
// Draw bottom and right
SelectObject(&penBR);
if (nFlags & BF_BOTTOM) {
MoveTo(rect.left, rect.bottom - 1);
LineTo(rect.right, rect.bottom - 1);
}
if (nFlags & BF_RIGHT) {
MoveTo(rect.right - 1, rect.top);
LineTo(rect.right - 1, rect.bottom);
}
// Optionally fill the middle area
if (nFlags & BF_MIDDLE) {
COLORREF fill = GetSysColor(COLOR_BTNFACE);
CBrush brush(fill);
CRect inner = rect;
inner.DeflateRect(1, 1);
FillRect(&inner, &brush);
}
// Optionally modify the rect (like real DrawEdge)
if (!(nFlags & BF_ADJUST))
lpRect->left += (nFlags & BF_LEFT) ? 1 : 0,
lpRect->top += (nFlags & BF_TOP) ? 1 : 0,
lpRect->right -= (nFlags & BF_RIGHT) ? 1 : 0,
lpRect->bottom -= (nFlags & BF_BOTTOM) ? 1 : 0;
// Restore old pen
SelectObject(pOldPen);
return true;
}
bool CDC::Pie(LPCRECT lpRect, const POINT &ptStart, const POINT &ptEnd) {
error("TODO: CDC::Pie");
}
bool CDC::FrameRgn(CRgn *pRgn, CBrush *pBrush,
int nWidth, int nHeight) {
impl()->frameRgn(pRgn, pBrush, nWidth, nHeight);
return true;
}
void CDC::MoveTo(int x, int y) {
impl()->moveTo(x, y);
}
void CDC::LineTo(int x, int y) {
impl()->lineTo(x, y);
}
COLORREF CDC::GetPixel(int x, int y) const {
return impl()->getPixel(x, y);
}
COLORREF CDC::GetPixel(const POINT &point) const {
return impl()->getPixel(point.x, point.y);
}
CGdiObject *CDC::SelectStockObject(int nIndex) {
HGDIOBJ hObject = MFC::GetStockObject(nIndex);
assert(hObject != nullptr);
HGDIOBJ hOldObj = SelectObject(hObject);
return CGdiObject::FromHandle(hOldObj);
}
CPen *CDC::SelectObject(CPen *pPen) {
HPEN hOld = impl()->Attach(pPen->m_hObject);
return (CPen *)CPen::FromHandle(hOld);
}
CBrush *CDC::SelectObject(CBrush *pBrush) {
HBRUSH hOld = impl()->Attach(pBrush->m_hObject);
return (CBrush *)CBrush::FromHandle(hOld);
}
CFont *CDC::SelectObject(CFont *pFont) {
HFONT hOld = impl()->Attach(pFont->m_hObject);
return (CFont *)CFont::FromHandle(hOld);
}
CBitmap *CDC::SelectObject(CBitmap *pBitmap) {
HBITMAP hOld = impl()->Attach(pBitmap->m_hObject);
return (CBitmap *)CBitmap::FromHandle(hOld);
}
int CDC::SelectObject(CRgn *pRgn) {
error("TODO: CDC::SelectObject");
}
CGdiObject *CDC::SelectObject(CGdiObject *pObject) {
HGDIOBJ hOld = impl()->Attach(pObject->m_hObject);
return CGdiObject::FromHandle(hOld);
}
HGDIOBJ CDC::SelectObject(HGDIOBJ hGdiObj) {
return impl()->Attach(hGdiObj);
}
CPalette *CDC::SelectPalette(CPalette *pPalette, bool bForceBackground) {
HPALETTE hOld = impl()->selectPalette(
!pPalette ? nullptr : pPalette->m_hObject,
bForceBackground);
return (CPalette *)CGdiObject::FromHandle(hOld);
}
COLORREF CDC::GetNearestColor(COLORREF crColor) const {
return impl()->GetNearestColor(crColor);
}
unsigned int CDC::RealizePalette() {
return impl()->realizePalette();
}
void CDC::UpdateColors() {
error("TODO: CDC::UpdateColors");
}
COLORREF CDC::SetBkColor(COLORREF crColor) {
return impl()->setBkColor(crColor);
}
COLORREF CDC::GetBkColor() const {
return impl()->getBkColor();
}
int CDC::SetBkMode(int nBkMode) {
return impl()->setBkMode(nBkMode);
}
COLORREF CDC::SetTextColor(COLORREF crColor) {
return impl()->setTextColor(crColor);
}
bool CDC::TextOut(int x, int y, const char *lpszString, int nCount) {
return impl()->textOut(x, y, lpszString, nCount);
}
bool CDC::TextOut(int x, int y, const CString &str) {
return impl()->textOut(x, y, str);
}
bool CDC::ExtTextOut(int x, int y, unsigned int nOptions, LPCRECT lpRect,
const char *lpszString, unsigned int nCount, int *lpDxWidths) {
return impl()->extTextOut(x, y, nOptions, lpRect, lpszString, nCount, lpDxWidths);
}
bool CDC::ExtTextOut(int x, int y, unsigned int nOptions, LPCRECT lpRect,
const CString &str, int *lpDxWidths) {
return impl()->extTextOut(x, y, nOptions, lpRect, str, lpDxWidths);
}
CSize CDC::TabbedTextOut(int x, int y, const char *lpszString, int nCount,
int nTabPositions, int *lpnTabStopPositions, int nTabOrigin) {
return impl()->tabbedTextOut(x, y, lpszString, nCount,
nTabPositions, lpnTabStopPositions, nTabOrigin);
}
CSize CDC::TabbedTextOut(int x, int y, const CString &str,
int nTabPositions, int *lpnTabStopPositions, int nTabOrigin) {
return impl()->tabbedTextOut(x, y, str, nTabPositions,
lpnTabStopPositions, nTabOrigin);
}
int CDC::DrawText(const char *lpszString, int nCount,
LPRECT lpRect, unsigned int nFormat) {
return impl()->drawText(lpszString, nCount, lpRect, nFormat);
}
int CDC::DrawText(const CString &str, LPRECT lpRect, unsigned int nFormat) {
return impl()->drawText(str, lpRect, nFormat);
}
CSize CDC::GetTextExtent(const char *lpszString, int nCount) const {
return impl()->getTextExtent(lpszString, nCount);
}
CSize CDC::GetTextExtent(const CString &str) const {
return impl()->getTextExtent(str);
}
CSize CDC::GetOutputTextExtent(const char *lpszString, int nCount) const {
return impl()->getOutputTextExtent(lpszString, nCount);
}
CSize CDC::GetOutputTextExtent(const CString &str) const {
return impl()->getOutputTextExtent(str);
}
CSize CDC::GetTabbedTextExtent(const char *lpszString, int nCount,
int nTabPositions, int *lpnTabStopPositions) const {
return impl()->getTabbedTextExtent(lpszString, nCount,
nTabPositions, lpnTabStopPositions);
}
CSize CDC::GetTabbedTextExtent(const CString &str,
int nTabPositions, int *lpnTabStopPositions) const {
return impl()->getTabbedTextExtent(str, nTabPositions,
lpnTabStopPositions);
}
CSize CDC::GetOutputTabbedTextExtent(const char *lpszString, int nCount,
int nTabPositions, int *lpnTabStopPositions) const {
return impl()->getOutputTabbedTextExtent(lpszString, nCount,
nTabPositions, lpnTabStopPositions);
}
CSize CDC::GetOutputTabbedTextExtent(const CString &str,
int nTabPositions, int *lpnTabStopPositions) const {
return impl()->getOutputTabbedTextExtent(str,
nTabPositions, lpnTabStopPositions);
}
bool CDC::GrayString(CBrush *pBrush,
bool(CALLBACK *lpfnOutput)(HDC, LPARAM, int), LPARAM lpData,
int nCount, int x, int y, int nWidth, int nHeight) {
return impl()->grayString(pBrush, lpfnOutput, lpData,
nCount, x, y, nWidth, nHeight);
}
unsigned int CDC::GetTextAlign() const {
return impl()->getTextAlign();
}
unsigned int CDC::SetTextAlign(unsigned int nFlags) {
return impl()->setTextAlign(nFlags);
}
bool CDC::GetTextMetrics(LPTEXTMETRIC lpMetrics) const {
return impl()->getTextMetrics(lpMetrics);
}
/*--------------------------------------------*/
CDC::Impl::Impl(CWnd *wndOwner) : m_pWnd(wndOwner), _drawMode(R2_COPYPEN) {
// By default the _bitmap will point to
// this dummy 1x1 bitmap
_defaultBitmap.CreateBitmap(1, 1, 1, 8, nullptr);
_bitmap = _defaultBitmap.bitmap();
// Defaults
CWinApp *app = AfxGetApp();
_font = app->getDefaultFont();
_pen = app->getDefaultPen();
_brush = app->getDefaultBrush();
_palette = app->getSystemDefaultPalette();
}
CDC::Impl::Impl(HDC srcDc) {
const CDC::Impl *src = (CDC::Impl *)srcDc;
// By default the _bitmap will point to
// this dummy 1x1 bitmap
_defaultBitmap.CreateBitmap(1, 1, 1, 8, nullptr);
_bitmap = _defaultBitmap.bitmap();
if (src) {
_font = src->_font;
_pen = src->_pen;
_brush = src->_brush;
_palette = src->_palette;
} else {
// Defaults
CWinApp *app = AfxGetApp();
_font = app->getDefaultFont();
_pen = app->getDefaultPen();
_brush = app->getDefaultBrush();
_palette = app->getSystemDefaultPalette();
}
}
HGDIOBJ CDC::Impl::Attach(HGDIOBJ gdiObj) {
CGdiObjectImpl *obj = (CGdiObjectImpl *)gdiObj;
CBitmap::Impl *bitmap = dynamic_cast(obj);
if (bitmap) {
HBITMAP result = _bitmap;
_bitmap = bitmap;
return result;
}
CFont::Impl *font = dynamic_cast(obj);
if (font) {
HFONT result = _font;
_font = font;
return result;
}
CPen::Impl *pen = dynamic_cast(obj);
if (pen) {
HPEN result = _pen;
_pen = pen;
return result;
}
CBrush::Impl *brush = dynamic_cast(obj);
if (brush) {
HBRUSH result = _brush;
_brush = brush;
return result;
}
if (gdiObj)
error("Unsupported gdi object");
return nullptr;
}
Gfx::Surface *CDC::Impl::getSurface() const {
assert(_bitmap);
return static_cast(_bitmap);
}
const Graphics::PixelFormat &CDC::Impl::getFormat() const {
return getSurface()->format;
}
void CDC::Impl::setFormat(const Graphics::PixelFormat &format) {
_defaultBitmap.bitmap()->create(1, 1, format);
_bitmap = _defaultBitmap.bitmap();
}
void CDC::Impl::setScreenRect() {
Graphics::Screen *scr = AfxGetApp()->getScreen();
_defaultBitmap.bitmap()->create(*scr, Common::Rect(0, 0, scr->w, scr->h));
_bitmap = _defaultBitmap.bitmap();
}
void CDC::Impl::setScreenRect(const Common::Rect &r) {
Graphics::Screen *scr = AfxGetApp()->getScreen();
assert(r.left >= 0 && r.top >= 0 &&
r.right <= scr->w && r.bottom <= scr->h);
_defaultBitmap.bitmap()->create(*scr, r);
_bitmap = _defaultBitmap.bitmap();
}
HPALETTE CDC::Impl::selectPalette(HPALETTE pal, bool bForceBackground) {
CWinApp *app = AfxGetApp();
HPALETTE oldPal = _palette;
//CWnd *pTopLevel = m_pWnd->GetTopLevelFrame();
_paletteRealized = false;
m_bForceBackground = bForceBackground;
if (!m_bForceBackground) {
m_bForceBackground = app->GetActiveWindow() != m_pWnd;
//CDC *dc = wnd->GetDC();
//m_bForceBackground = dc->m_hDC == this;
//wnd->ReleaseDC(dc);
}
if (pal) {
_palette = pal;
_hasLogicalPalette = app->getSystemDefaultPalette() != pal;
CBitmap::Impl *bitmap = (CBitmap::Impl *)_bitmap;
auto *newPal = static_cast(pal);
if (bitmap)
bitmap->setPalette(newPal->data(), 0, newPal->size());
}
return oldPal;
}
CPalette *CDC::Impl::selectPalette(CPalette *pal, bool bForceBackground) {
CPalette *oldPal = _cPalette;
_cPalette = pal;
selectPalette((HPALETTE)_cPalette->m_hObject, bForceBackground);
return oldPal;
}
unsigned int CDC::Impl::realizePalette() {
const auto *pal = static_cast(_palette);
if (m_pWnd == nullptr || !pal)
return 0;
if (!m_bForceBackground) {
// This window is active - update the system palette
AfxGetApp()->setPalette(*pal);
_paletteRealized = true;
return 1; // number of entries changed - simplified
} else {
_paletteRealized = true;
}
return 0;
}
COLORREF CDC::Impl::GetNearestColor(COLORREF crColor) const {
if (crColor <= 255 || (crColor >> 24) == 1)
return crColor & 0xff;
const auto *pal = static_cast(_palette);
if (pal)
return pal->findBestColor(
GetRValue(crColor),
GetGValue(crColor),
GetBValue(crColor));
return AfxGetApp()->getColor(crColor);
}
void CDC::Impl::fillSolidRect(LPCRECT lpRect, COLORREF clr) {
fillRect(*lpRect, clr);
}
void CDC::Impl::fillSolidRect(int x, int y, int cx, int cy, COLORREF clr) {
fillRect(Common::Rect(x, y, x + cx, y + cy), clr);
}
void CDC::Impl::fillRect(const Common::Rect &r, COLORREF crColor) {
static_cast(_bitmap)->fillRect(r,
GetNearestColor(crColor));
}
void CDC::Impl::drawRect(const Common::Rect &r, CBrush *brush) {
CBitmap::Impl *bitmap = (CBitmap::Impl *)_bitmap;
CPen::Impl *pen = (CPen::Impl *)_pen;
byte brushColor = brush->brush()->getColor();
uint penColor = getPenColor();
if (pen->_penStyle == PS_INSIDEFRAME &&
brush->brush()->_type != BS_HOLLOW) {
Common::Rect rInner(r.left + 1, r.top + 1,
r.right - 1, r.bottom - 1);
bitmap->fillRect(rInner, brushColor);
}
Gfx::frameRect(bitmap, r, penColor, _drawMode);
}
void CDC::Impl::frameRect(const Common::Rect &r, CBrush *brush) {
CBitmap::Impl *bitmap = (CBitmap::Impl *)_bitmap;
byte brushColor = brush->brush()->getColor();
bitmap->frameRect(r, brushColor);
}
void CDC::Impl::frameRgn(const CRgn *pRgn, CBrush *brush, int nWidth, int nHeight) {
// We don't currently support larger brush sizes
assert(nWidth == 1 && nHeight == 1);
uint brushColor = getBrushColor();
// Set up a pen using the specified brush color
CPen pen;
pen.CreatePen(PS_SOLID, 1, brushColor);
HPEN oldPen = Attach(pen.m_hObject);
// Iterate over drawing lines to each point
bool firstTime = true;
for (const POINT &pt : pRgn->_points) {
if (firstTime) {
firstTime = false;
moveTo(pt.x, pt.y);
} else {
lineTo(pt.x, pt.y);
}
}
// Final line segment back to original point
lineTo(pRgn->_points[0].x, pRgn->_points[0].y);
// Restore old pen
Attach(oldPen);
}
void CDC::Impl::rectangle(LPCRECT lpRect) {
CBrush *brush = CBrush::FromHandle(_brush);
drawRect(*lpRect, brush);
}
void CDC::Impl::rectangle(int x1, int y1, int x2, int y2) {
CBrush *brush = CBrush::FromHandle(_brush);
drawRect(Common::Rect(x1, y1, x2, y2), brush);
}
void CDC::Impl::floodFill(int x, int y, COLORREF crColor) {
CBitmap::Impl *bitmap = static_cast(_bitmap);
assert(bitmap->format.bytesPerPixel == 1);
if (x < 0 || y < 0 || x >= bitmap->w || y >= bitmap->h)
return;
uint color = GetNearestColor(crColor);
byte *startPixel = (byte *)bitmap->getBasePtr(x, y);
const byte oldColor = *startPixel;
if (color == oldColor)
return;
struct Point {
int x, y;
};
// Set up queue with initial point
Common::Queue queue;
queue.push({ x, y });
byte *pixelP;
int minX = x, maxX = x, minY = y, maxY = y;
while (!queue.empty()) {
Point startPt = queue.front();
queue.pop();
int curY = startPt.y;
int xStart = startPt.x;
int xEnd = startPt.x;
// Find the left edge, changing pixels as it goes
pixelP = (byte *)bitmap->getBasePtr(xStart, curY);
*pixelP = color;
for (; xStart > 0 && *(pixelP - 1) == oldColor; --xStart, --pixelP)
*(pixelP - 1) = color;
// Find the right edge, changing pixels as it goes
pixelP = (byte *)bitmap->getBasePtr(xEnd, curY);
for (; xEnd < (bitmap->w - 1) && *(pixelP + 1) == oldColor; ++xEnd, ++pixelP)
*(pixelP + 1) = color;
// Track modified area
minX = MIN(minX, xStart);
maxX = MAX(maxX, xEnd);
minY = MIN(minY, curY);
maxY = MAX(maxY, curY);
// Scan for line segments above or below
for (int deltaY = -1; deltaY <= 1; deltaY += 2) {
int lineY = curY + deltaY;
if (lineY < 0 || lineY >= bitmap->h)
continue;
// Loop looking for line segments
pixelP = (byte *)bitmap->getBasePtr(xStart, lineY);
int curX = xStart;
while (curX <= xEnd) {
// Find the start of any line segment
if (*pixelP == oldColor) {
// Add new line starting point to queue
queue.push({ curX, lineY });
// Move beyond it
do {
++curX;
++pixelP;
} while (curX <= xEnd && *pixelP == oldColor);
} else {
// Move to next pixel, looking for new line segment
++curX;
++pixelP;
}
}
}
}
bitmap->addDirtyRect(Common::Rect(minX, minY, maxX + 1, maxY + 1));
}
void CDC::Impl::floodFill(int x, int y, COLORREF crColor,
unsigned int nFillType) {
error("TODO: CDC::floodFill");
}
void CDC::Impl::draw3dRect(const CRect &rect, COLORREF clrTopLeft, COLORREF clrBottomRight) {
fillSolidRect(rect.left, rect.top, rect.Width() - 1, 1, clrTopLeft);
fillSolidRect(rect.left, rect.top + 1, 1, rect.Height() - 2, clrTopLeft);
fillSolidRect(rect.left, rect.bottom - 1, rect.Width(), 1, clrBottomRight);
fillSolidRect(rect.right - 1, rect.top, 1, rect.Height() - 1, clrBottomRight);
}
void CDC::Impl::drawFocusRect(const CRect &rect) {
CBrush brush(RGB(128, 128, 128));
drawRect(rect, &brush);
}
void CDC::Impl::ellipse(const Common::Rect &r, COLORREF crColor) {
CPen::Impl *pen = (CPen::Impl *)_pen;
CBitmap::Impl *bitmap = (CBitmap::Impl *)_bitmap;
uint brushColor = GetNearestColor(crColor);
uint penColor = getPenColor();
if (pen->_penStyle == PS_INSIDEFRAME)
bitmap->drawEllipse(r.left, r.top, r.right, r.bottom,
brushColor, true);
bitmap->drawEllipse(r.left, r.top, r.right, r.bottom,
penColor, false);
}
void CDC::Impl::ellipse(LPCRECT lpRect) {
ellipse(*lpRect, getBrushColor());
}
void CDC::Impl::ellipse(int x1, int y1, int x2, int y2) {
ellipse(Common::Rect(x1, y1, x2, y2), getBrushColor());
}
void CDC::Impl::bitBlt(int x, int y, int nWidth, int nHeight, CDC *pSrcDC,
int xSrc, int ySrc, uint32 dwRop) {
const Common::Rect srcRect(xSrc, ySrc, xSrc + nWidth, ySrc + nHeight);
Gfx::Surface dummySrc;
Gfx::Surface *src = &dummySrc;
uint32 *paletteMap = nullptr;
if (pSrcDC) {
auto *srcImpl = pSrcDC->impl();
src = srcImpl->getSurface();
// Get a palette map if necessary
paletteMap = getPaletteMap(srcImpl);
}
Gfx::Surface *dest = getSurface();
const Common::Point destPos(x, y);
uint bgColor = getBkPixel();
Gfx::blit(src, dest, srcRect, destPos, bgColor, dwRop, paletteMap);
delete[] paletteMap;
}
void CDC::Impl::stretchBlt(int x, int y, int nWidth, int nHeight, CDC *pSrcDC,
int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, uint32 dwRop) {
auto *srcImpl = pSrcDC->impl();
Gfx::Surface *src = srcImpl->getSurface();
Gfx::Surface *dest = getSurface();
const Common::Rect srcRect(xSrc, ySrc, xSrc + nSrcWidth, ySrc + nSrcHeight);
const Common::Rect destRect(x, y, x + nWidth, y + nHeight);
uint bgColor = getBkPixel();
uint32 *paletteMap = getPaletteMap(srcImpl);
Gfx::stretchBlit(src, dest, srcRect, destRect, bgColor, dwRop, nullptr);
delete[] paletteMap;
}
uint32 *CDC::Impl::getPaletteMap(const CDC::Impl *srcImpl) {
Graphics::Palette *srcPal, *destPal;
// If we have a logical palette, but are in the background (i.e. not the active one),
// then source pixels map from the local logical palette to the system one
if (_paletteRealized && m_bForceBackground) {
srcPal = dynamic_cast(_palette);
destPal = dynamic_cast(AfxGetApp()->getCurrentPalette());
}
// If we haven't realized our palette locally, or the source bitmap hasn't had any
// palette at all set, then return null indicating no palette mapping will occur
else if (!_paletteRealized || !srcImpl->_hasLogicalPalette)
return nullptr;
else {
srcPal = dynamic_cast(srcImpl->_palette);
destPal = dynamic_cast(_palette);
}
if (!srcPal || srcPal->empty() || !destPal || destPal->empty())
return nullptr;
assert(srcPal->size() == destPal->size());
// Create the map
Graphics::PaletteLookup palLookup(destPal->data(), destPal->size());
return palLookup.createMap(srcPal->data(), srcPal->size());
}
void CDC::Impl::moveTo(int x, int y) {
_linePos.x = x;
_linePos.y = y;
}
void CDC::Impl::lineTo(int x, int y) {
Gfx::Surface *dest = getSurface();
uint color = getPenColor();
dest->drawLine(_linePos.x, _linePos.y, x, y, color);
_linePos.x = x;
_linePos.y = y;
}
COLORREF CDC::Impl::getPixel(int x, int y) const {
Gfx::Surface *src = getSurface();
assert(src->format.bytesPerPixel == 1);
const byte pixel = src->getPixel(x, y);
assert(_palette);
const auto *pal = static_cast(_palette);
byte r, g, b;
pal->get(pixel, r, g, b);
return RGB(r, g, b);
}
int CDC::Impl::setROP2(int nDrawMode) {
int oldMode = _drawMode;
_drawMode = nDrawMode;
return oldMode;
}
uint CDC::Impl::getPenColor() const {
CPen::Impl *pen = (CPen::Impl *)_pen;
assert(pen->_penStyle == PS_SOLID ||
pen->_penStyle == PS_INSIDEFRAME);
return GetNearestColor(pen->_color);
}
uint CDC::Impl::getBrushColor() const {
CBrush::Impl *brush = static_cast(_brush);
assert(brush->_type == HS_HORIZONTAL ||
brush->_type == HS_VERTICAL);
return brush->getColor();
}
COLORREF CDC::Impl::setBkColor(COLORREF crColor) {
COLORREF oldColor = _bkColor;
_bkColor = crColor;
return oldColor;
}
COLORREF CDC::Impl::getBkColor() const {
return _bkColor;
}
COLORREF CDC::Impl::getBkPixel() const {
return _bkColor == RGB(255, 255, 255) ? 255 :
GetNearestColor(_bkColor);
}
int CDC::Impl::setBkMode(int nBkMode) {
int oldMode = _bkMode;
_bkMode = nBkMode;
return oldMode;
}
COLORREF CDC::Impl::setTextColor(COLORREF crColor) {
COLORREF oldColor = _textColor;
_textColor = crColor;
return oldColor;
}
bool CDC::Impl::textOut(int x, int y, const char *lpszString, int nCount,
int nTabPositions, const int *lpnTabStopPositions,
int nTabOrigin, CSize *size) {
Gfx::Surface *dest = getSurface();
RECT r;
if ((_textAlign & 6) == TA_RIGHT) {
r.left = 0;
r.right = x;
} else if ((_textAlign & 6) == TA_CENTER) {
r.left = 0;
r.right = dest->w;
} else {
// Left align
r.left = x;
r.right = dest->w;
}
if ((_textAlign & 24) == TA_BOTTOM) {
r.top = 0;
r.bottom = y;
} else {
r.top = y;
r.bottom = dest->h;
}
CString str(lpszString, nCount);
drawText(str, &r, DT_SINGLELINE | DT_NOPREFIX |
_textAlign, nTabPositions, lpnTabStopPositions,
nTabOrigin, size);
return true;
}
bool CDC::Impl::textOut(int x, int y, const CString &str,
int nTabPositions, const int *lpnTabStopPositions,
int nTabOrigin, CSize *size) {
Gfx::Surface *dest = getSurface();
RECT r;
r.left = x;
r.top = y;
r.right = dest->w;
r.bottom = dest->h;
drawText(str, &r, DT_SINGLELINE | DT_NOPREFIX,
nTabPositions, lpnTabStopPositions,
nTabOrigin, size);
return true;
}
bool CDC::Impl::extTextOut(int x, int y, unsigned int nOptions, LPCRECT lpRect,
const char *lpszString, unsigned int nCount, int *lpDxWidths) {
error("TODO: extTextOut");
}
bool CDC::Impl::extTextOut(int x, int y, unsigned int nOptions, LPCRECT lpRect,
const CString &str, int *lpDxWidths) {
error("TODO: extTextOut");
}
CSize CDC::Impl::tabbedTextOut(int x, int y, const char *lpszString, int nCount,
int nTabPositions, const int *lpnTabStopPositions, int nTabOrigin) {
CString str(lpszString, nCount);
CSize size;
textOut(x, y, str.c_str(), str.size(),
nTabPositions, lpnTabStopPositions, nTabOrigin, &size);
return size;
}
CSize CDC::Impl::tabbedTextOut(int x, int y, const CString &str,
int nTabPositions, const int *lpnTabStopPositions, int nTabOrigin) {
CSize size;
textOut(x, y, str, nTabPositions, lpnTabStopPositions, nTabOrigin, &size);
return size;
}
int CDC::Impl::drawText(const char *lpszString, int nCount,
LPRECT lpRect, unsigned int nFormat, int nTabPositions,
const int *lpnTabStopPositions, int nTabOrigin,
CSize *size) {
return drawText(CString(lpszString, nCount),
lpRect, nFormat, nTabPositions,
lpnTabStopPositions, nTabOrigin, size);
}
int CDC::Impl::drawText(const CString &str, LPRECT lpRect, unsigned int nFormat,
int nTabPositions, const int *lpnTabStopPositions,
int nTabOrigin, CSize *size) {
Graphics::Font *font = *(CFont::Impl *)_font;
Gfx::Surface *dest = getSurface();
uint textCol = GetNearestColor(_textColor);
CSize dummySize;
if (!size)
size = &dummySize;
Common::Array tabStops;
for (int i = 0; i < nTabPositions; ++i)
tabStops.push_back(lpnTabStopPositions[i]);
*size = renderText(str, dest, font, textCol, lpRect,
nFormat, tabStops, nTabOrigin,
GetNearestColor(_bkColor), _bkMode,
GetNearestColor(_textColor), _textAlign);
return size->cy;
}
CSize CDC::Impl::getTextExtent(const char *lpszString, int nCount) const {
CString str(lpszString, nCount);
Graphics::Font *font = *(CFont::Impl *)_font;
CSize s;
s.cx = font->getStringWidth(str);
s.cy = font->getFontHeight();
return s;
}
CSize CDC::Impl::getTextExtent(const CString &str) const {
Graphics::Font *font = *(CFont::Impl *)_font;
CSize s;
s.cx = font->getStringWidth(str);
s.cy = font->getFontHeight();
return s;
}
CSize CDC::Impl::getOutputTextExtent(const char *lpszString, int nCount) const {
// TODO: Proper implementation that handles tabs, etc.
return getTextExtent(lpszString, nCount);
}
CSize CDC::Impl::getOutputTextExtent(const CString &str) const {
// TODO: Proper implementation that handles tabs, etc.
return getTextExtent(str);
}
CSize CDC::Impl::getTabbedTextExtent(const char *lpszString, int nCount,
int nTabPositions, int *lpnTabStopPositions) const {
// TODO: Proper implementation that handles tabs, etc.
return getTextExtent(lpszString, nCount);
}
CSize CDC::Impl::getTabbedTextExtent(const CString &str,
int nTabPositions, int *lpnTabStopPositions) const {
error("TODO");
}
CSize CDC::Impl::getOutputTabbedTextExtent(const char *lpszString, int nCount,
int nTabPositions, int *lpnTabStopPositions) const {
error("TODO");
}
CSize CDC::Impl::getOutputTabbedTextExtent(const CString &str,
int nTabPositions, int *lpnTabStopPositions) const {
error("TODO");
}
bool CDC::Impl::grayString(CBrush *pBrush,
bool(CALLBACK *lpfnOutput)(HDC, LPARAM, int), LPARAM lpData,
int nCount, int x, int y, int nWidth, int nHeight) {
error("TODO");
}
unsigned int CDC::Impl::getTextAlign() const {
return _textAlign;
}
unsigned int CDC::Impl::setTextAlign(unsigned int nFlags) {
unsigned int oldAlign = _textAlign;
_textAlign = nFlags;
return oldAlign;
}
bool CDC::Impl::getTextMetrics(LPTEXTMETRIC lpMetrics) const {
TEXTMETRIC &tm = *lpMetrics;
Gfx::Font *font = *(CFont::Impl *)_font;
tm.tmInternalLeading = 0;
tm.tmExternalLeading = 0;
tm.tmAveCharWidth = font->getCharWidth();
if (!tm.tmAveCharWidth) {
tm.tmAveCharWidth = font->getStringWidth(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") / 52 + 1;
}
tm.tmHeight = font->getCharHeight();
if (!tm.tmHeight)
tm.tmHeight = font->getFontHeight();
tm.tmAscent = font->getFontAscent();
tm.tmDescent = font->getFontDescent();
tm.tmMaxCharWidth = font->getMaxCharWidth();
tm.tmWeight = FW_NORMAL;
tm.tmOverhang = 0;
tm.tmDigitizedAspectX = 1;
tm.tmDigitizedAspectY = 1;
const char first = (char)32;
const char last = (char)127;
tm.tmFirstChar = first;
tm.tmLastChar = last;
tm.tmDefaultChar = '?'; // Pick a fallback character
tm.tmBreakChar = ' '; // Typically space is used for breaking
// Assume fixed-pitch if all characters have same width
bool fixedPitch = true;
int firstWidth = font->getCharWidth(first);
for (int c = first + 1; c <= last; ++c) {
if (font->getCharWidth(c) != firstWidth) {
fixedPitch = false;
break;
}
}
tm.tmPitchAndFamily = (fixedPitch ? TMPF_FIXED_PITCH : 0);
tm.tmCharSet = ANSI_CHARSET;
return true;
}
} // namespace MFC
} // namespace Bagel