You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1727 lines
51 KiB
C++

2 years ago
// XTPMarkupDrawingContext.cpp: implementation of the CXTPMarkupDrawingContext class.
//
// This file is a part of the XTREME TOOLKIT PRO MFC class library.
// (c)1998-2012 Codejock Software, All Rights Reserved.
//
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
// CONSENT OF CODEJOCK SOFTWARE.
//
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
// SINGLE COMPUTER.
//
// CONTACT INFORMATION:
// support@codejock.com
// http://www.codejock.com
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <math.h>
#include "Common/XTPImageManager.h"
#include "XTPMarkupObject.h"
#include "XTPMarkupInputElement.h"
#include "XTPMarkupUIElement.h"
#include "XTPMarkupFrameworkElement.h"
#include "XTPMarkupDrawingContext.h"
#include "XTPMarkupDeviceContext.h"
#include "XTPMarkupShape.h"
#include "XTPMarkupThickness.h"
#include <Markup/Path/XTPMarkupPathData.h>
#include "GraphicLibrary/GdiPlus/GdiPlus.h"
using namespace Gdiplus;
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
CXTPMarkupDeviceContext::CXTPMarkupDeviceContext(HDC hDC, HDC hAttribDC)
{
m_hDC = hDC;
m_hAttribDC = hAttribDC;
if (m_hDC)
{
::GetWindowExtEx(m_hDC, &m_sizeWinExt);
::GetViewportExtEx(m_hDC, &m_sizeVpExt);
}
else
{
m_sizeWinExt = m_sizeVpExt = CSize(1, 1);
}
if ((m_sizeWinExt.cx == 0) || (m_sizeWinExt.cy == 0))
{
m_sizeVpExt = m_sizeWinExt = CSize(1, 1);
}
m_sizeWinExt.cx = abs(m_sizeWinExt.cx);
m_sizeWinExt.cy = abs(m_sizeWinExt.cy);
m_sizeVpExt.cx = abs(m_sizeVpExt.cx);
m_sizeVpExt.cy = abs(m_sizeVpExt.cy);
m_hOldFont = NULL;
}
CXTPMarkupDeviceContext::~CXTPMarkupDeviceContext()
{
}
void CXTPMarkupDeviceContext::CreatePen(CPen& penStroke, CXTPMarkupStrokeStyle* pStrokeStyle)
{
if (pStrokeStyle && pStrokeStyle->nStrokeThickness > 0 && pStrokeStyle->pStrokeBrush)
penStroke.CreatePen(PS_SOLID, pStrokeStyle->nStrokeThickness, pStrokeStyle->pStrokeBrush->GetHintColor());
}
void CXTPMarkupDeviceContext::Ellipse(CRect rc, CXTPMarkupStrokeStyle* pStrokeStyle, CXTPMarkupBrush* pFillBrush)
{
CPen penStroke;
CreatePen(penStroke, pStrokeStyle);
CBrush brushFill;
if (pFillBrush) brushFill.CreateSolidBrush(pFillBrush->GetHintColor());
HGDIOBJ hOldPen = penStroke.GetSafeHandle() ? ::SelectObject(GetSafeHdc(), penStroke) : ::SelectObject(GetSafeHdc(), ::GetStockObject(NULL_PEN));
HGDIOBJ hOldBrush = brushFill.GetSafeHandle() ? ::SelectObject(GetSafeHdc(), brushFill) : ::SelectObject(GetSafeHdc(), ::GetStockObject(NULL_BRUSH));
::Ellipse(GetSafeHdc(), rc.left, rc.top, rc.right, rc.bottom);
if (hOldPen) ::SelectObject(GetSafeHdc(), hOldPen);
if (hOldBrush) ::SelectObject(GetSafeHdc(), hOldBrush);
}
void CXTPMarkupDeviceContext::Polyline(const POINT* points, int nCount, CXTPMarkupStrokeStyle* pStrokeStyle)
{
CPen penStroke;
CreatePen(penStroke, pStrokeStyle);
HGDIOBJ hOldPen = penStroke.GetSafeHandle() ? ::SelectObject(GetSafeHdc(), penStroke) : ::SelectObject(GetSafeHdc(), ::GetStockObject(NULL_PEN));
::Polyline(GetSafeHdc(), points, nCount);
if (hOldPen) ::SelectObject(GetSafeHdc(), hOldPen);
}
void CXTPMarkupDeviceContext::Polygon(const POINT* points, int nCount, CXTPMarkupStrokeStyle* pStrokeStyle, CXTPMarkupBrush* pFillBrush)
{
CPen penStroke;
CreatePen(penStroke, pStrokeStyle);
CBrush brushFill;
if (pFillBrush) brushFill.CreateSolidBrush(pFillBrush->GetHintColor());
HGDIOBJ hOldPen = penStroke.GetSafeHandle() ? ::SelectObject(GetSafeHdc(), penStroke) : ::SelectObject(GetSafeHdc(), ::GetStockObject(NULL_PEN));
HGDIOBJ hOldBrush = brushFill.GetSafeHandle() ? ::SelectObject(GetSafeHdc(), brushFill) : ::SelectObject(GetSafeHdc(), ::GetStockObject(NULL_BRUSH));
::Polygon(GetSafeHdc(), points, (int)nCount);
if (hOldPen) ::SelectObject(GetSafeHdc(), hOldPen);
if (hOldBrush) ::SelectObject(GetSafeHdc(), hOldBrush);
}
void CXTPMarkupDeviceContext::DrawPath(CXTPMarkupPathData *pData, CXTPMarkupStrokeStyle* pStrokeStyle, CXTPMarkupBrush* pFillBrush)
{
int nCount = pData->GetCoint();
if (nCount == 0)
return;
::BeginPath(m_hDC);
const MARKUP_POINTF* pPoints = pData->GetPoints();
const BYTE* pTypes = pData->GetTypes();
for (int i = 0; i < nCount; i++)
{
switch (pTypes[i] & 7)
{
case xtpMarkupPathPointTypeStart:
::MoveToEx(m_hDC, (int)pPoints[i].x, (int)pPoints[i].y, NULL);
break;
case xtpMarkupPathPointTypeLine:
::LineTo(m_hDC, (int)pPoints[i].x, (int)pPoints[i].y);
break;
case xtpMarkupPathPointTypeBezier:
POINT pt[] = {(int)pPoints[i].x, (int)pPoints[i].y, (int)pPoints[i + 1].x, (int)pPoints[i + 1].y, (int)pPoints[i + 2].x, (int)pPoints[i + 2].y};
::PolyBezierTo(m_hDC, pt, 3);
i += 2;
break;
}
if (pTypes[i] & xtpMarkupPathPointTypeCloseSubpath)
::CloseFigure(m_hDC);
}
::EndPath(m_hDC);
CBrush brushFill;
CPen penStroke;
HGDIOBJ hOldBrush = NULL;
HGDIOBJ hOldPen = NULL;
if (pFillBrush)
{
brushFill.CreateSolidBrush(pFillBrush->GetHintColor());
hOldBrush = brushFill.GetSafeHandle() ? ::SelectObject(GetSafeHdc(), brushFill) : ::SelectObject(GetSafeHdc(), ::GetStockObject(NULL_BRUSH));
}
CreatePen(penStroke, pStrokeStyle);
if (penStroke.GetSafeHandle())
{
hOldPen = penStroke.GetSafeHandle() ? ::SelectObject(GetSafeHdc(), penStroke) : ::SelectObject(GetSafeHdc(), ::GetStockObject(NULL_PEN));
}
if (brushFill.GetSafeHandle() && penStroke.GetSafeHandle())
{
::StrokeAndFillPath(m_hDC);
}
else if (brushFill.GetSafeHandle())
{
::FillPath(m_hDC);
}
else if (penStroke.GetSafeHandle())
{
::StrokePath(m_hDC);
}
if (hOldBrush) ::SelectObject(GetSafeHdc(), hOldBrush);
if (hOldPen) ::SelectObject(GetSafeHdc(), hOldPen);
}
POINT CXTPMarkupDeviceContext::TranslatePoint(const POINT& ptVisualOffset) const
{
POINT pt;
pt.x = ptVisualOffset.x * m_sizeVpExt.cx / m_sizeWinExt.cx;
pt.y = ptVisualOffset.y * m_sizeVpExt.cy / m_sizeWinExt.cy;
return pt;
}
void CXTPMarkupDeviceContext::OffsetViewport(const POINT& ptVisualOffset)
{
POINT pt = TranslatePoint(ptVisualOffset);
OffsetViewportOrgEx(m_hDC, pt.x, pt.y, NULL);
}
void CXTPMarkupDeviceContext::DrawRectangle(CRect rc, CXTPMarkupBrush* pBrush, CXTPMarkupThickness* pThickness)
{
if (!pThickness)
return;
if (pThickness->GetLeft() > 0)
FillRectangle(CRect(rc.left, rc.top, rc.left + pThickness->GetLeft(), rc.bottom), pBrush);
if (pThickness->GetRight() > 0)
FillRectangle(CRect(rc.right - pThickness->GetRight(), rc.top, rc.right, rc.bottom), pBrush);
if (pThickness->GetTop() > 0)
FillRectangle(CRect(rc.left, rc.top, rc.right, rc.top + pThickness->GetTop()), pBrush);
if (pThickness->GetBottom() > 0)
FillRectangle(CRect(rc.left, rc.bottom - pThickness->GetBottom(), rc.right, rc.bottom), pBrush);
}
int _cdecl CXTPMarkupDeviceContext::_GradientStopCompare(const void *arg1, const void *arg2)
{
double& dOffset1 = ((GRADIENTSTOP*)arg1)->dOffset;
double& dOffset2 = ((GRADIENTSTOP*)arg2)->dOffset;
return dOffset1 > dOffset2 ? 1 : dOffset1 < dOffset2 ? -1 : ((GRADIENTSTOP*)arg1)->nIndex - ((GRADIENTSTOP*)arg2)->nIndex;
};
void CXTPMarkupDeviceContext::FillRoundRectangle(CRect rc, CXTPMarkupBrush* pBrush, double* pCornerRadius)
{
if (!pCornerRadius)
{
FillRectangle(rc, pBrush);
return;
}
float l = (float)rc.left, r = (float)rc.right, t = (float)rc.top, b = (float)rc.bottom;
CXTPMarkupPathGeometryBuilder builder;
builder.BeginFigure(l, t + (float)pCornerRadius[0]);
builder.BezierTo(l, t, l + (float)pCornerRadius[1], t);
builder.LineTo(r - (float)pCornerRadius[2], t);
builder.BezierTo(r, t, r, t + (float)pCornerRadius[3]);
builder.LineTo(r, b - (float)pCornerRadius[4]);
builder.BezierTo(r, b, r - (float)pCornerRadius[5], b);
builder.LineTo(l + (float)pCornerRadius[6], b);
builder.BezierTo(l, b, l, b - (float)pCornerRadius[7]);
builder.CloseFigure();
CXTPMarkupPathData* pData = builder.CreateData();
pData->m_nPixelOffsetMode = 4;
CXTPMarkupStrokeStyle style;
memset(&style, 0, sizeof(style));
DrawPath(pData, &style, pBrush);
delete pData;
}
void CXTPMarkupDeviceContext::DrawRoundRectangle(CRect rc, CXTPMarkupBrush* pBrush, CXTPMarkupThickness* pThickness, double* pCornerRadius)
{
if (!pCornerRadius)
{
DrawRectangle(rc, pBrush, pThickness);
return;
}
int nStroke = pThickness->GetLeft();
float f = float(nStroke) / 2;
float l = (float)rc.left + f, r = (float)rc.right - f, t = (float)rc.top + f, b = (float)rc.bottom - f;
CXTPMarkupPathGeometryBuilder builder;
builder.BeginFigure(l, t + (float)pCornerRadius[0]);
builder.BezierTo(l, t, l + (float)pCornerRadius[1], t);
builder.LineTo(r - (float)pCornerRadius[2], t);
builder.BezierTo(r, t, r, t + (float)pCornerRadius[3]);
builder.LineTo(r, b - (float)pCornerRadius[4]);
builder.BezierTo(r, b, r - (float)pCornerRadius[5], b);
builder.LineTo(l + (float)pCornerRadius[6], b);
builder.BezierTo(l, b, l, b - (float)pCornerRadius[7]);
builder.CloseFigure();
CXTPMarkupPathData* pData = builder.CreateData();
pData->m_nPixelOffsetMode = 4;
CXTPMarkupStrokeStyle style;
memset(&style, 0, sizeof(style));
style.nStrokeThickness = nStroke;
style.pStrokeBrush = pBrush;
DrawPath(pData, &style, NULL);
delete pData;
}
void CXTPMarkupDeviceContext::FillRectangle(CRect rc, CXTPMarkupBrush* pBrush)
{
if (IsSolidBrush(pBrush))
{
CXTPMarkupSolidColorBrush* pSolidColorBrush = (CXTPMarkupSolidColorBrush*)pBrush;
CXTPMarkupColor* pColor = pSolidColorBrush->GetColor();
if (pColor)
{
COLORREF clrOld = ::SetBkColor(GetSafeHdc(), (COLORREF)(*pColor) & 0xFFFFFF);
::ExtTextOut(GetSafeHdc(), 0, 0, ETO_OPAQUE, rc, NULL, 0, NULL);
::SetBkColor(GetSafeHdc(), clrOld);
}
return;
}
if (IsLinearGradientBrush(pBrush))
{
CXTPMarkupLinearGradientBrush* pLinearGradientBrush = (CXTPMarkupLinearGradientBrush*)pBrush;
HDC hDC = GetSafeHdc();
int cx = rc.Width();
int cy = rc.Height();
CXTPMarkupGradientStops* pGradientStops = pLinearGradientBrush->GetGradientStops();
if (pGradientStops->GetCount() == 0)
return;
if (cx <= 0 || cy <= 0)
return;
if (pGradientStops->GetCount() == 1)
{
COLORREF clr = pGradientStops->GetItem(0)->GetColor() & 0xFFFFFF;
COLORREF clrOld = ::SetBkColor(hDC, clr);
::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, rc, NULL, 0, NULL);
::SetBkColor(hDC, clrOld);
return;
}
CXTPMarkupPoint ptStartPoint(0, 0);
CXTPMarkupPoint* pt = MARKUP_STATICCAST(CXTPMarkupPoint, pBrush->GetValue(CXTPMarkupLinearGradientBrush::m_pStartPointProperty));
if (pt) ptStartPoint = *pt;
CXTPMarkupPoint ptEndPoint(1, 1);
pt = MARKUP_STATICCAST(CXTPMarkupPoint, pBrush->GetValue(CXTPMarkupLinearGradientBrush::m_pEndPointProperty));
if (pt) ptEndPoint = *pt;
ptStartPoint.x *= cx;
ptStartPoint.y *= cy;
ptEndPoint.x *= cx;
ptEndPoint.y *= cy;
BOOL bHorizontal = ptStartPoint.y == ptEndPoint.y;
BOOL bVertical = ptStartPoint.x == ptEndPoint.x;
if (bHorizontal && bVertical)
return;
CRect rcClipBox;
::GetClipBox(m_hDC, rcClipBox);
//if (pDC->IsPrinting())
// rcClipBox.InflateRect(1, 1);
rcClipBox.OffsetRect(-rc.TopLeft());
if (!rcClipBox.IntersectRect(&rcClipBox, CRect(0, 0, cx, cy)))
return;
LPDWORD lpBits;
HBITMAP hBitmap = CXTPImageManager::Create32BPPDIBSection(0, cx, cy, (LPBYTE*)&lpBits);
int nCount = pGradientStops->GetCount();
GRADIENTSTOP* pStops = new GRADIENTSTOP[nCount];
for (int i = 0; i < nCount; i++)
{
CXTPMarkupGradientStop* pItem = pGradientStops->GetItem(i);
COLORREF clr = pItem->GetColor();
pStops[i].clr.rgbRed = GetRValue(clr);
pStops[i].clr.rgbGreen = GetGValue(clr);
pStops[i].clr.rgbBlue = GetBValue(clr);
pStops[i].clr.rgbReserved = 0;
pStops[i].dOffset = pItem->GetOffset();
pStops[i].nIndex = i;
}
GRADIENTSTOP *pStopFirst = pStops, *pStopLast = pStops + nCount - 1, *pStop;
qsort(pStopFirst, nCount, sizeof(GRADIENTSTOP), _GradientStopCompare);
for (pStop = pStopFirst + 1; pStop <= pStopLast; pStop++)
{
pStop->dDiff = pStop->dOffset - (pStop - 1)->dOffset;
}
CXTPMarkupPoint ptCenter((double)cx / 2, (double)cy /2);
double len = sqrt(pow(ptStartPoint.x - ptEndPoint.x, 2) + pow(ptStartPoint.y - ptEndPoint.y, 2));
double cosA = (ptEndPoint.x - ptStartPoint.x) / len;
double sinA = (ptEndPoint.y - ptStartPoint.y) / len;
double dDiff = - ptCenter.x * cosA - ptCenter.y * sinA + ptCenter.x;
double dStart = ptStartPoint.x * cosA + ptStartPoint.y * sinA + dDiff;
double dEnd = ptEndPoint.x * cosA + ptEndPoint.y * sinA + dDiff;
LPDWORD lpPixel = lpBits;
double fDist = 1.0 / (dEnd - dStart);
cosA = cosA * fDist;
sinA = sinA * fDist;
dDiff = (dDiff - dStart) * fDist;
if (bVertical)
{
lpPixel += (cy - rcClipBox.bottom) * cx;
for (int y = (cy - rcClipBox.bottom); y < (cy - rcClipBox.top); y++)
{
double fAmount = double(cy - y) * sinA + dDiff;
DWORD dwPixel = 0;
if (fAmount <= pStopFirst->dOffset)
{
dwPixel = *((LPDWORD)&pStopFirst->clr);
}
else if (fAmount >= pStopLast->dOffset)
{
dwPixel = *((LPDWORD)&pStopLast->clr);
}
else for (pStop = pStopFirst + 1; pStop <= pStopLast; pStop++)
{
if (fAmount < pStop->dOffset && pStop->dDiff != 0)
{
const RGBQUAD& clrFrom = (pStop - 1)->clr;
const RGBQUAD& clrTo = pStop->clr;
double fAmountA = (pStop->dOffset - fAmount) / pStop->dDiff;
double fAmountB = (1.0 - fAmountA);
((LPBYTE)&dwPixel)[2] = (BYTE)(clrFrom.rgbRed * fAmountA + clrTo.rgbRed * fAmountB);
((LPBYTE)&dwPixel)[1] = (BYTE)(clrFrom.rgbGreen * fAmountA + clrTo.rgbGreen * fAmountB);
((LPBYTE)&dwPixel)[0] = (BYTE)(clrFrom.rgbBlue * fAmountA + clrTo.rgbBlue * fAmountB);
break;
}
}
for (int x = 0; x < cx; x++)
{
*lpPixel = dwPixel;
lpPixel++;
}
}
}
else if (bHorizontal)
{
dDiff += double(cy) * sinA;
lpPixel += rcClipBox.left;
for (int x = rcClipBox.left; x < rcClipBox.right; x++)
{
double fAmount = (double)x * cosA + dDiff;
if (fAmount <= pStopFirst->dOffset)
{
*lpPixel = *((LPDWORD)&pStopFirst->clr);
}
else if (fAmount >= pStopLast->dOffset)
{
*lpPixel = *((LPDWORD)&pStopLast->clr);
}
else for (pStop = pStopFirst + 1; pStop <= pStopLast; pStop++)
{
if (fAmount < pStop->dOffset && pStop->dDiff != 0)
{
const RGBQUAD& clrFrom = (pStop - 1)->clr;
const RGBQUAD& clrTo = pStop->clr;
double fAmountA = (pStop->dOffset - fAmount) / pStop->dDiff;
double fAmountB = (1.0 - fAmountA);
((LPBYTE)lpPixel)[2] = (BYTE)(clrFrom.rgbRed * fAmountA + clrTo.rgbRed * fAmountB);
((LPBYTE)lpPixel)[1] = (BYTE)(clrFrom.rgbGreen * fAmountA + clrTo.rgbGreen * fAmountB);
((LPBYTE)lpPixel)[0] = (BYTE)(clrFrom.rgbBlue * fAmountA + clrTo.rgbBlue * fAmountB);
break;
}
}
lpPixel++;
}
lpPixel += (cx - rcClipBox.right);
int y = max(1, cy - rcClipBox.bottom);
if (y > 1)
{
lpPixel += cx * (cy - rcClipBox.bottom - 1);
}
for (; y < (cy - rcClipBox.top); y++)
{
memcpy(lpPixel, lpBits, sizeof(DWORD) * cx);
lpPixel += cx;
}
}
else
{
lpPixel += (cy - rcClipBox.bottom) * cx;
for (int y = (cy - rcClipBox.bottom); y < (cy - rcClipBox.top); y++)
{
lpPixel += rcClipBox.left;
for (int x = rcClipBox.left; x < rcClipBox.right; x++)
{
double fAmount = (double)x * cosA + double(cy - y) * sinA + dDiff;
if (fAmount <= pStopFirst->dOffset)
{
*lpPixel = *((LPDWORD)&pStopFirst->clr);
}
else if (fAmount >= pStopLast->dOffset)
{
*lpPixel = *((LPDWORD)&pStopLast->clr);
}
else for (pStop = pStopFirst + 1; pStop <= pStopLast; pStop++)
{
if (fAmount < pStop->dOffset && pStop->dDiff != 0)
{
const RGBQUAD& clrFrom = (pStop - 1)->clr;
const RGBQUAD& clrTo = pStop->clr;
double fAmountA = (pStop->dOffset - fAmount) / pStop->dDiff;
double fAmountB = (1.0 - fAmountA);
((LPBYTE)lpPixel)[2] = (BYTE)(clrFrom.rgbRed * fAmountA + clrTo.rgbRed * fAmountB);
((LPBYTE)lpPixel)[1] = (BYTE)(clrFrom.rgbGreen * fAmountA + clrTo.rgbGreen * fAmountB);
((LPBYTE)lpPixel)[0] = (BYTE)(clrFrom.rgbBlue * fAmountA + clrTo.rgbBlue * fAmountB);
break;
}
}
lpPixel++;
}
lpPixel += (cx - rcClipBox.right);
}
}
HDC hBitmapDC = ::CreateCompatibleDC(hDC);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hBitmapDC, hBitmap);
BitBlt(hDC, rc.left + rcClipBox.left, rc.top + rcClipBox.top, rcClipBox.Width(), rcClipBox.Height(), hBitmapDC, rcClipBox.left, rcClipBox.top, SRCCOPY);
SelectObject(hBitmapDC, hOldBitmap);
DeleteDC(hBitmapDC);
DeleteObject(hBitmap);
delete[] pStops;
}
}
void CXTPMarkupDeviceContext::DrawLine(int x1, int y1, int x2, int y2, CXTPMarkupStrokeStyle* pStrokeStyle)
{
CPen penStroke;
CreatePen(penStroke, pStrokeStyle);
HGDIOBJ hOldPen = penStroke.GetSafeHandle() ? ::SelectObject(m_hDC, penStroke) : ::SelectObject(m_hDC, ::GetStockObject(NULL_PEN));
::MoveToEx(m_hDC, x1, y1, NULL);
::LineTo(m_hDC, x2, y2);
if (hOldPen) ::SelectObject(m_hDC, hOldPen);
}
SIZE CXTPMarkupDeviceContext::MeasureString(LPCWSTR lpszText, int nCount) const
{
SIZE sz;
VERIFY(::GetTextExtentPoint32W(m_hAttribDC, lpszText, nCount, &sz));
return sz;
}
void CXTPMarkupDeviceContext::SetTextColor(CXTPMarkupBrush* pBrush)
{
::SetTextColor(m_hDC, pBrush ? pBrush->GetHintColor() : 0);
}
void CXTPMarkupDeviceContext::SetTextFont(CXTPMarkupFont* pFont)
{
if (!m_hDC)
return;
if (pFont == NULL && m_hOldFont)
{
SelectFontObject(m_hOldFont);
m_hOldFont = NULL;
}
else if (pFont != NULL && m_hOldFont == NULL)
{
m_hOldFont = SelectFontObject(pFont->m_hFont);
}
else if (pFont != NULL)
{
SelectFontObject(pFont->m_hFont);
}
}
HFONT CXTPMarkupDeviceContext::SelectFontObject(HFONT hFont)
{
HFONT hOldObj = 0;
if (m_hDC != m_hAttribDC)
hOldObj = (HFONT)::SelectObject(m_hDC, hFont);
if (m_hAttribDC != NULL)
hOldObj = (HFONT)::SelectObject(m_hAttribDC, hFont);
return hOldObj;
}
void CXTPMarkupDeviceContext::DrawString(LPCWSTR lpszString, UINT nCount, LPCRECT lpRect)
{
if (m_hAttribDC == m_hDC)
{
::ExtTextOutW(m_hDC, lpRect->left, lpRect->top, 0, lpRect, lpszString, nCount, 0);
return;
}
LPINT lpDxWidths = NULL;
ASSERT(m_hDC != NULL);
ASSERT(m_hAttribDC != NULL);
ASSERT(lpszString != NULL);
ASSERT(lpDxWidths == NULL ||
AfxIsValidAddress(lpDxWidths, sizeof(int) * nCount, FALSE));
ASSERT(AfxIsValidAddress(lpszString, nCount, FALSE));
int* pDeltas = NULL;
LPWSTR pOutputString = NULL;
int nRightFixup = 0;
if (nCount == 0) // Do nothing
return;
pDeltas = new int[nCount];
pOutputString = new WCHAR[nCount];
int x = lpRect->left;
ComputeDeltas(x, (LPWSTR)lpszString, nCount, FALSE, 0, NULL, 0,
pOutputString, pDeltas, nRightFixup);
lpDxWidths = pDeltas;
lpszString = pOutputString;
::ExtTextOutW(m_hDC, x, lpRect->top, 0, lpRect, lpszString, nCount, lpDxWidths);
delete[] pDeltas;
delete[] pOutputString;
}
int CXTPMarkupDeviceContext::ComputeNextTab(int x, UINT nTabStops, LPINT lpnTabStops,
int nTabOrigin, int nTabWidth) const
{
x -= nTabOrigin; // normalize position to tab origin
for (UINT i = 0; i < nTabStops; i++, lpnTabStops++)
{
if (*lpnTabStops > x)
return *lpnTabStops + nTabOrigin;
}
return (x / nTabWidth + 1) * nTabWidth + nTabOrigin;
}
CSize CXTPMarkupDeviceContext::ComputeDeltas(int& x, LPCWSTR lpszString, UINT &nCount,
BOOL bTabbed, UINT nTabStops, LPINT lpnTabStops, int nTabOrigin,
LPWSTR lpszOutputString, int* pnDxWidths, int& nRightFixup) const
{
TEXTMETRIC tmAttrib;
TEXTMETRIC tmScreen;
::GetTextMetrics(m_hAttribDC, &tmAttrib);
::GetTextMetrics(m_hDC, &tmScreen);
CSize sizeExtent;
::GetTextExtentPoint32A(m_hAttribDC, "A", 1, &sizeExtent);
CPoint ptCurrent;
UINT nAlignment = ::GetTextAlign(m_hAttribDC);
BOOL bUpdateCP = (nAlignment & TA_UPDATECP) != 0;
if (bUpdateCP)
{
::GetCurrentPositionEx(m_hDC, &ptCurrent);
x = ptCurrent.x;
}
LPCWSTR lpszCurChar = lpszString;
LPCWSTR lpszStartRun = lpszString;
int* pnCurDelta = pnDxWidths;
int nStartRunPos = x;
int nCurrentPos = x;
int nStartOffset = 0;
int nTabWidth = 0;
if (bTabbed)
{
if (nTabStops == 1)
{
nTabWidth = lpnTabStops[0];
}
else
{
// Get default size of a tab
nTabWidth = LOWORD(::GetTabbedTextExtentA(m_hAttribDC,
"\t", 1, 0, NULL));
}
}
for (UINT i = 0; i < nCount; i++)
{
BOOL bSpace = ((_TUCHAR)*lpszCurChar == (_TUCHAR)tmAttrib.tmBreakChar);
if (bSpace || (bTabbed && *lpszCurChar == '\t'))
{
// bSpace will be either TRUE (==1) or FALSE (==0). For spaces
// we want the space included in the GetTextExtent, for tabs we
// do not want the tab included
int nRunLength = (int)(lpszCurChar - lpszStartRun) + (bSpace ? 1 : 0);
CSize sizeExtent2;
::GetTextExtentPoint32W(m_hAttribDC, lpszStartRun, nRunLength,
&sizeExtent2);
int nNewPos = nStartRunPos + sizeExtent2.cx
- tmAttrib.tmOverhang;
// now, if this is a Tab (!bSpace), compute the next tab stop
if (!bSpace)
{
nNewPos = ComputeNextTab(nNewPos, nTabStops, lpnTabStops,
nTabOrigin, nTabWidth);
}
// Add this width to previous width
if (pnCurDelta == pnDxWidths)
nStartOffset += nNewPos - nCurrentPos;
else
*(pnCurDelta-1) += nNewPos - nCurrentPos;
nCurrentPos = nNewPos;
nStartRunPos = nCurrentPos; // set start of run
// *lpszCurChar must be SBC: tmBreakChar or '\t'
lpszStartRun = lpszCurChar + 1;
}
else
{
// For the non-tabbed or non-tab-character case
int cxScreen;
if (_istlead(*lpszCurChar))
{
cxScreen = tmScreen.tmAveCharWidth;
*pnCurDelta = tmAttrib.tmAveCharWidth;
}
else
{
::GetCharWidth(m_hDC, (_TUCHAR)*lpszCurChar,
(_TUCHAR)*lpszCurChar, &cxScreen);
if (!::GetCharWidth(m_hAttribDC, (_TUCHAR)*lpszCurChar,
(_TUCHAR)*lpszCurChar, pnCurDelta))
{
// If printer driver fails the above call, use the average width
*pnCurDelta = tmAttrib.tmAveCharWidth;
}
}
*pnCurDelta -= tmAttrib.tmOverhang;
cxScreen -= tmScreen.tmOverhang;
nCurrentPos += *pnCurDelta; // update current position
// Center character in allotted space
if (pnCurDelta != pnDxWidths)
{
int diff = (*pnCurDelta - cxScreen) / 2;
*pnCurDelta -= diff;
*(pnCurDelta - 1) += diff;
}
*lpszOutputString++ = *lpszCurChar;
if (_istlead(*lpszCurChar))
{
*lpszOutputString++ = *(lpszCurChar+1); // copy trailing byte
*(pnCurDelta + 1) = *pnCurDelta; // double wide
nCurrentPos += *pnCurDelta;
pnCurDelta++;
i++;
}
pnCurDelta++;
}
lpszCurChar++;
}
nAlignment &= TA_CENTER|TA_RIGHT;
sizeExtent.cx = nCurrentPos - x;
nRightFixup = 0;
if (nAlignment == TA_LEFT)
x += nStartOffset; // Full left side adjustment
else if (nAlignment == TA_CENTER)
x += nStartOffset/2; // Adjust Center by 1/2 left side adjustment
else if (nAlignment == TA_RIGHT)
nRightFixup = nStartOffset; // Right adjust needed later if TA_UPDATECP
if (bUpdateCP)
::MoveToEx(m_hDC, x, ptCurrent.y, NULL);
nCount = (UINT)(pnCurDelta - pnDxWidths); // number of characters output
return sizeExtent;
}
BOOL CXTPMarkupDeviceContext::IsSolidBrush(CXTPMarkupBrush* pBrush) const
{
return pBrush && (pBrush->GetType() == MARKUP_TYPE(CXTPMarkupSolidColorBrush));
}
BOOL CXTPMarkupDeviceContext::IsLinearGradientBrush(CXTPMarkupBrush* pBrush) const
{
return pBrush && (pBrush->GetType() == MARKUP_TYPE(CXTPMarkupLinearGradientBrush));
}
void CXTPMarkupDeviceContext::IntersectClipRect(const RECT& rcLayoutClip)
{
::IntersectClipRect(m_hDC, rcLayoutClip.left, rcLayoutClip.top, rcLayoutClip.right, rcLayoutClip.bottom);
}
void CXTPMarkupDeviceContext::GetClipBox(LPRECT lpRect)
{
::GetClipBox(m_hDC, lpRect);
}
HRGN CXTPMarkupDeviceContext::SaveClipRegion()
{
HRGN hrgnClip = ::CreateRectRgn(0, 0, 0, 0);
if (hrgnClip != NULL)
{
if (GetClipRgn(m_hDC, hrgnClip) != 1)
{
DeleteObject(hrgnClip);
hrgnClip = (HRGN)-1;
}
}
return hrgnClip;
}
void CXTPMarkupDeviceContext::RestoreClipRegion(HRGN hrgnClip)
{
if (hrgnClip != NULL)
{
if (hrgnClip == (HRGN)-1)
{
ExtSelectClipRgn(m_hDC, NULL, RGN_COPY);
}
else
{
ExtSelectClipRgn(m_hDC, hrgnClip, RGN_COPY);
DeleteObject(hrgnClip);
}
}
}
HDC CXTPMarkupDeviceContext::GetDC()
{
return m_hDC;
}
void CXTPMarkupDeviceContext::ReleaseDC(HDC /*hDC*/)
{
}
//////////////////////////////////////////////////////////////////////////
// CXTPMarkupGdiPlusDeviceContext
#define DECLARE_GDIPLUS_METHOD(val, calltype, proc, proc_param, call_param)\
inline GpStatus calltype proc##proc_param\
{\
typedef Status (calltype* PFN##proc)##proc_param;\
if (!m_ptrMethods[val])\
{\
m_ptrMethods[val] = GetProcAddress(m_hModule, #proc);\
}\
return ((PFN##proc)m_ptrMethods[val])##call_param;\
}
#define DECLARE_GDIPLUS_METHOD_VOID(val, calltype, proc, proc_param, call_param)\
inline void calltype proc##proc_param\
{\
typedef void (calltype* PFN##proc)##proc_param;\
if (!m_ptrMethods[val])\
{\
m_ptrMethods[val] = GetProcAddress(m_hModule, #proc);\
}\
((PFN##proc)m_ptrMethods[val])##call_param;\
}
class CXTPMarkupGdiPlusDeviceContext::CGdiPlus
{
public:
CGdiPlus()
{
m_hModule = NULL;
m_nGdiplusToken = NULL;
m_nCount = 0;
ZeroMemory(&m_ptrMethods, sizeof(m_ptrMethods));
}
public:
void Register(BOOL bInit);
DECLARE_GDIPLUS_METHOD(0, WINAPI, GdiplusStartup, (ULONG_PTR *token, const GdiplusStartupInput *input, GdiplusStartupOutput *output), (token, input, output))
DECLARE_GDIPLUS_METHOD_VOID(1, WINAPI, GdiplusShutdown, (ULONG_PTR token), (token))
DECLARE_GDIPLUS_METHOD(2, WINGDIPAPI, GdipSetPixelOffsetMode, (GpGraphics* graphics, PixelOffsetMode pixelOffsetMode), (graphics, pixelOffsetMode))
DECLARE_GDIPLUS_METHOD(3, WINGDIPAPI, GdipSetPageUnit, (GpGraphics *graphics, GpUnit unit), (graphics, unit))
DECLARE_GDIPLUS_METHOD(4, WINGDIPAPI, GdipSetSmoothingMode, (GpGraphics *graphics, SmoothingMode smoothingMode), (graphics, smoothingMode))
DECLARE_GDIPLUS_METHOD(5, WINGDIPAPI, GdipSetLinePresetBlend, (GpLineGradient *brush, GDIPCONST ARGB *blend, GDIPCONST REAL* positions, INT count), (brush, blend, positions, count))
DECLARE_GDIPLUS_METHOD(6, WINGDIPAPI, GdipCreateFromHDC2, (HDC hdc, HANDLE hDevice, GpGraphics **graphics), (hdc, hDevice, graphics))
DECLARE_GDIPLUS_METHOD(7, WINGDIPAPI, GdipCreateFromHDC, (HDC hdc, GpGraphics **graphics), (hdc, graphics))
DECLARE_GDIPLUS_METHOD(8, WINGDIPAPI, GdipCreatePath2, (GDIPCONST GpPointF* points, GDIPCONST BYTE* data, INT dataCount, GpFillMode fillMode, GpPath **path), (points, data, dataCount, fillMode, path))
DECLARE_GDIPLUS_METHOD(9, WINGDIPAPI, GdipCreateSolidFill, (ARGB color, GpSolidFill **brush), (color, brush))
DECLARE_GDIPLUS_METHOD(10, WINGDIPAPI, GdipCreateLineBrush, (GDIPCONST GpPointF* point1, GDIPCONST GpPointF* point2, ARGB color1, ARGB color2, GpWrapMode wrapMode, GpLineGradient **lineGradient), (point1, point2, color1, color2, wrapMode, lineGradient))
DECLARE_GDIPLUS_METHOD(11, WINGDIPAPI, GdipCreatePen1, (ARGB color, REAL width, GpUnit unit, GpPen **pen), (color, width, unit, pen))
DECLARE_GDIPLUS_METHOD(12, WINGDIPAPI, GdipCreatePen2, (GpBrush *brush, REAL width, GpUnit unit, GpPen **pen), (brush, width, unit, pen))
DECLARE_GDIPLUS_METHOD(13, WINGDIPAPI, GdipDeleteGraphics, (GpGraphics *graphics), (graphics))
DECLARE_GDIPLUS_METHOD(14, WINGDIPAPI, GdipDeleteBrush, (GpBrush *brush), (brush))
DECLARE_GDIPLUS_METHOD(15, WINGDIPAPI, GdipDeletePen, (GpPen *pen), (pen))
DECLARE_GDIPLUS_METHOD(16, WINGDIPAPI, GdipDeletePath, (GpPath* path), (path))
DECLARE_GDIPLUS_METHOD(17, WINGDIPAPI, GdipDrawEllipse, (GpGraphics *graphics, GpPen *pen, REAL x, REAL y, REAL width, REAL height), (graphics, pen, x, y, width, height))
DECLARE_GDIPLUS_METHOD(18, WINGDIPAPI, GdipDrawLinesI, (GpGraphics *graphics, GpPen *pen, GDIPCONST GpPoint *points, INT count), (graphics, pen, points, count))
DECLARE_GDIPLUS_METHOD(19, WINGDIPAPI, GdipDrawPolygonI, (GpGraphics *graphics, GpPen *pen, GDIPCONST GpPoint *points, INT count), (graphics, pen, points, count))
DECLARE_GDIPLUS_METHOD(20, WINGDIPAPI, GdipDrawPath, (GpGraphics *graphics, GpPen *pen, GpPath *path), (graphics, pen, path))
DECLARE_GDIPLUS_METHOD(21, WINGDIPAPI, GdipDrawLineI, (GpGraphics *graphics, GpPen *pen, INT x1, INT y1, INT x2, INT y2), (graphics, pen, x1, y1, x2, y2))
DECLARE_GDIPLUS_METHOD(22, WINGDIPAPI, GdipFillEllipse, (GpGraphics *graphics, GpBrush *brush, REAL x, REAL y, REAL width, REAL height), (graphics, brush, x, y, width, height))
DECLARE_GDIPLUS_METHOD(23, WINGDIPAPI, GdipFillPolygonI, (GpGraphics *graphics, GpBrush *brush, GDIPCONST GpPoint *points, INT count, GpFillMode fillMode), (graphics, brush, points, count, fillMode))
DECLARE_GDIPLUS_METHOD(24, WINGDIPAPI, GdipFillRectangleI, (GpGraphics *graphics, GpBrush *brush, INT x, INT y, INT width, INT height), (graphics, brush, x, y, width, height))
DECLARE_GDIPLUS_METHOD(25, WINGDIPAPI, GdipFillPath, (GpGraphics *graphics, GpBrush *brush, GpPath *path), (graphics, brush, path))
DECLARE_GDIPLUS_METHOD(26, WINGDIPAPI, GdipGetClipBoundsI, (GpGraphics *graphics, GpRect *rect), (graphics, rect))
DECLARE_GDIPLUS_METHOD(27, WINGDIPAPI, GdipCreateFontFromLogfontA, (HDC hdc, LOGFONTA *logfont, GpFont **font), (hdc, logfont, font))
DECLARE_GDIPLUS_METHOD(28, WINGDIPAPI, GdipCreateFontFromLogfontW, (HDC hdc, LOGFONTW *logfont, GpFont **font), (hdc, logfont, font))
DECLARE_GDIPLUS_METHOD(29, WINGDIPAPI, GdipDeleteFont, (GpFont* font), (font))
DECLARE_GDIPLUS_METHOD(30, WINGDIPAPI, GdipMeasureString, ( GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *layoutRect, GDIPCONST GpStringFormat *stringFormat, RectF *boundingBox, INT *codepointsFitted, INT *linesFilled ),
(graphics, string, length, font, layoutRect, stringFormat, boundingBox, codepointsFitted, linesFilled))
DECLARE_GDIPLUS_METHOD(31, WINGDIPAPI, GdipTranslateWorldTransform, (GpGraphics *graphics, REAL dx, REAL dy, GpMatrixOrder order), (graphics, dx, dy, order))
DECLARE_GDIPLUS_METHOD(32, WINGDIPAPI, GdipDeleteStringFormat, (GpStringFormat *format), (format))
DECLARE_GDIPLUS_METHOD(33, WINGDIPAPI, GdipCreateStringFormat, ( INT formatAttributes, LANGID language, GpStringFormat **format ), (formatAttributes, language, format))
DECLARE_GDIPLUS_METHOD(34, WINGDIPAPI, GdipGetDC, (GpGraphics* graphics, HDC * hdc), (graphics, hdc))
DECLARE_GDIPLUS_METHOD(35, WINGDIPAPI, GdipReleaseDC, (GpGraphics* graphics, HDC hdc), (graphics, hdc))
DECLARE_GDIPLUS_METHOD(36, WINGDIPAPI, GdipDrawString, ( GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *layoutRect, GDIPCONST GpStringFormat *stringFormat, GDIPCONST GpBrush *brush ),
( graphics, string, length, font, layoutRect, stringFormat, brush ))
DECLARE_GDIPLUS_METHOD(37, WINGDIPAPI, GdipStringFormatGetGenericTypographic, (GpStringFormat **format), ( format ))
DECLARE_GDIPLUS_METHOD(38, WINGDIPAPI, GdipCloneStringFormat, (GDIPCONST GpStringFormat *format, GpStringFormat **newFormat), (format, newFormat))
DECLARE_GDIPLUS_METHOD(39, WINGDIPAPI, GdipSetStringFormatFlags, (GpStringFormat *format, INT flags), (format, flags))
DECLARE_GDIPLUS_METHOD(40, WINGDIPAPI, GdipSetPenDashArray, (GpPen *pen, GDIPCONST REAL *dash, INT count), (pen, dash, count))
DECLARE_GDIPLUS_METHOD(41, WINGDIPAPI, GdipSetPenLineJoin, (GpPen *pen, GpLineJoin lineJoin), (pen, lineJoin))
DECLARE_GDIPLUS_METHOD(42, WINGDIPAPI, GdipSetPenStartCap, (GpPen *pen, GpLineCap startCap), (pen, startCap))
DECLARE_GDIPLUS_METHOD(43, WINGDIPAPI, GdipSetPenEndCap, (GpPen *pen, GpLineCap endCap), (pen, endCap))
DECLARE_GDIPLUS_METHOD(44, WINGDIPAPI, GdipCreateRegion, (GpRegion **region), (region))
DECLARE_GDIPLUS_METHOD(45, WINGDIPAPI, GdipGetClip, (GpGraphics *graphics, GpRegion *region), (graphics, region))
DECLARE_GDIPLUS_METHOD(46, WINGDIPAPI, GdipSetClipRegion, (GpGraphics *graphics, GpRegion *region, CombineMode combineMode), (graphics, region, combineMode))
DECLARE_GDIPLUS_METHOD(47, WINGDIPAPI, GdipDeleteRegion, (GpRegion *region), (region))
DECLARE_GDIPLUS_METHOD(48, WINGDIPAPI, GdipSetClipRectI, (GpGraphics *graphics, INT x, INT y, INT width, INT height, CombineMode combineMode), (graphics, x, y, width, height, combineMode))
public:
HMODULE m_hModule;
ULONG_PTR m_nGdiplusToken;
int m_nCount;
LPVOID m_ptrMethods[50];
};
CXTPMarkupGdiPlusDeviceContext::CGdiPlus* CXTPMarkupGdiPlusDeviceContext::GetGdiPlus()
{
static CGdiPlus GdiPlus;
return &GdiPlus;
}
void CXTPMarkupGdiPlusDeviceContext::CGdiPlus::Register(BOOL bInit)
{
if (bInit)
{
m_nCount++;
if (m_nCount > 1)
return;
ASSERT(m_nGdiplusToken == 0 && m_hModule == 0);
ZeroMemory(&m_ptrMethods, sizeof(m_ptrMethods));
m_hModule = LoadLibrary(_T("GdiPlus.dll"));
if (m_hModule)
{
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&m_nGdiplusToken, &gdiplusStartupInput, NULL);
}
}
else
{
m_nCount--;
if (m_nCount != 0)
return;
if (m_hModule)
{
GdiplusShutdown(m_nGdiplusToken);
FreeLibrary(m_hModule);
}
ZeroMemory(&m_ptrMethods, sizeof(m_ptrMethods));
m_hModule = NULL;
m_nGdiplusToken = 0;
}
}
void CXTPMarkupGdiPlusDeviceContext::Register(BOOL bInit)
{
GetGdiPlus()->Register(bInit);
}
CXTPMarkupGdiPlusDeviceContext::CXTPMarkupGdiPlusDeviceContext(HDC hDC, HDC hAttribDC)
: CXTPMarkupDeviceContext(hDC, hAttribDC)
{
}
CXTPMarkupGdiPlusDeviceContext::~CXTPMarkupGdiPlusDeviceContext()
{
}
GpGraphics* CXTPMarkupGdiPlusDeviceContext::GetGraphics()
{
GpGraphics* pGpGraphics = NULL;
if (GetGdiPlus()->m_nGdiplusToken)
{
if (m_hDC != m_hAttribDC)
GetGdiPlus()->GdipCreateFromHDC2(m_hDC, m_hAttribDC, &pGpGraphics);
else
GetGdiPlus()->GdipCreateFromHDC(m_hDC, &pGpGraphics);
}
if (!pGpGraphics)
return NULL;
GetGdiPlus()->GdipSetPageUnit(pGpGraphics, UnitPixel);
GetGdiPlus()->GdipSetSmoothingMode(pGpGraphics, SmoothingModeHighQuality);
GetGdiPlus()->GdipSetPixelOffsetMode(pGpGraphics, PixelOffsetModeHalf);
return pGpGraphics;
}
void CXTPMarkupGdiPlusDeviceContext::ReleaseGraphics(GpGraphics* pGpGraphics)
{
GetGdiPlus()->GdipDeleteGraphics(pGpGraphics);
}
void CXTPMarkupGdiPlusDeviceContext::OffsetViewport(const POINT& ptViewortOrg)
{
CXTPMarkupDeviceContext::OffsetViewport(ptViewortOrg);
}
void CXTPMarkupGdiPlusDeviceContext::Ellipse(CRect rc, CXTPMarkupStrokeStyle* pStrokeStyle, CXTPMarkupBrush* pFillBrush)
{
GpGraphics* pGpGraphics = GetGraphics();
if (!pGpGraphics)
{
CXTPMarkupDeviceContext::Ellipse(rc, pStrokeStyle, pFillBrush);
return;
}
int nStrokeThickness = pStrokeStyle->nStrokeThickness > 0 ? pStrokeStyle->nStrokeThickness : 0;
GetGdiPlus()->GdipSetSmoothingMode(pGpGraphics, SmoothingModeHighQuality);
GetGdiPlus()->GdipSetPixelOffsetMode(pGpGraphics, PixelOffsetModeHalf);
RectF rcBound((REAL)rc.left, (REAL)rc.top, (REAL)rc.Width(), (REAL)rc.Height());
rcBound.Inflate(-(REAL)((REAL)nStrokeThickness / 2.0), -(REAL)((REAL)nStrokeThickness / 2.0));
if (pFillBrush)
{
GpBrush* pGpBrush = CreateGpBrush(pFillBrush, &rc);
GetGdiPlus()->GdipFillEllipse(pGpGraphics, pGpBrush, rcBound.X, rcBound.Y, rcBound.Width, rcBound.Height);
GetGdiPlus()->GdipDeleteBrush(pGpBrush);
}
if (nStrokeThickness > 0)
{
GpPen* pGpPen = CreateGpPen(pStrokeStyle, rc);
GetGdiPlus()->GdipDrawEllipse(pGpGraphics, pGpPen, rcBound.X, rcBound.Y, rcBound.Width, rcBound.Height);
GetGdiPlus()->GdipDeletePen(pGpPen);
}
ReleaseGraphics(pGpGraphics);
}
void CXTPMarkupGdiPlusDeviceContext::Polyline(const POINT* points, int nCount, CXTPMarkupStrokeStyle* pStrokeStyle)
{
GpGraphics* pGpGraphics = GetGraphics();
if (!pGpGraphics)
{
CXTPMarkupDeviceContext::Polyline(points, nCount, pStrokeStyle);
return;
}
GetGdiPlus()->GdipSetSmoothingMode(pGpGraphics, SmoothingModeHighQuality);
GetGdiPlus()->GdipSetPixelOffsetMode(pGpGraphics, (pStrokeStyle->nStrokeThickness % 2) ? PixelOffsetModeDefault : PixelOffsetModeHalf);
GpPen* pGpPen = CreateGpPen(pStrokeStyle, GetPointsBoundRect(points, nCount));
if (pGpPen)
{
GetGdiPlus()->GdipDrawLinesI(pGpGraphics, pGpPen, (GpPoint*)points, nCount);
GetGdiPlus()->GdipDeletePen(pGpPen);
}
ReleaseGraphics(pGpGraphics);
}
CRect CXTPMarkupGdiPlusDeviceContext::GetPointsBoundRect(const POINT* points, int nCount) const
{
CRect rc(INT_MAX, INT_MAX, -INT_MAX, -INT_MAX);
for (int i = 0; i < nCount; i++)
{
const POINT& pt = points[i];
rc.left = min(rc.left, pt.x);
rc.top = min(rc.top, pt.y);
rc.right = max(rc.right, pt.x);
rc.bottom = max(rc.bottom, pt.y);
}
return rc;
}
void CXTPMarkupGdiPlusDeviceContext::Polygon(const POINT* points, int nCount, CXTPMarkupStrokeStyle* pStrokeStyle, CXTPMarkupBrush* pFillBrush)
{
GpGraphics* pGpGraphics = GetGraphics();
if (!pGpGraphics)
{
CXTPMarkupDeviceContext::Polygon(points, nCount, pStrokeStyle, pFillBrush);
return;
}
int nStrokeThickness = pStrokeStyle->nStrokeThickness > 0 ? pStrokeStyle->nStrokeThickness : 0;
GetGdiPlus()->GdipSetSmoothingMode(pGpGraphics, SmoothingModeHighQuality);
CRect rcBound = GetPointsBoundRect(points, nCount);
if (pFillBrush)
{
GpBrush* pGpBrush = CreateGpBrush(pFillBrush, rcBound);
if (pGpBrush)
{
GetGdiPlus()->GdipSetPixelOffsetMode(pGpGraphics, PixelOffsetModeHalf);
GetGdiPlus()->GdipFillPolygonI(pGpGraphics, pGpBrush, (GpPoint*)points, nCount, FillModeAlternate);
GetGdiPlus()->GdipDeleteBrush(pGpBrush);
}
}
if (nStrokeThickness > 0)
{
GpPen* pGpPen = CreateGpPen(pStrokeStyle, rcBound);
if (pGpPen)
{
GetGdiPlus()->GdipSetPixelOffsetMode(pGpGraphics, (nStrokeThickness % 2) ? PixelOffsetModeDefault : PixelOffsetModeHalf);
GetGdiPlus()->GdipDrawPolygonI(pGpGraphics, pGpPen, (GpPoint*)points, nCount);
GetGdiPlus()->GdipDeletePen(pGpPen);
}
}
ReleaseGraphics(pGpGraphics);
}
void CXTPMarkupGdiPlusDeviceContext::DrawPath(CXTPMarkupPathData *pData, CXTPMarkupStrokeStyle* pStrokeStyle, CXTPMarkupBrush* pFillBrush)
{
GpGraphics* pGpGraphics = GetGraphics();
if (!pGpGraphics)
{
CXTPMarkupDeviceContext::DrawPath(pData, pStrokeStyle, pFillBrush);
return;
}
GetGdiPlus()->GdipSetSmoothingMode(pGpGraphics, SmoothingModeHighQuality);
if (pData->GetCoint() > 0)
{
GpPath* pGpPath = NULL;
GetGdiPlus()->GdipCreatePath2((GpPointF*)pData->GetPoints(), pData->GetTypes(), pData->GetCoint(), FillModeAlternate, &pGpPath);
if (pGpPath)
{
if (pFillBrush)
{
GetGdiPlus()->GdipSetPixelOffsetMode(pGpGraphics, PixelOffsetModeHalf);
GpBrush* pGpBrush = CreateGpBrush(pFillBrush, pData->GetBounds());
GetGdiPlus()->GdipFillPath(pGpGraphics, pGpBrush, pGpPath);
GetGdiPlus()->GdipDeleteBrush(pGpBrush);
}
if (pStrokeStyle && pStrokeStyle->nStrokeThickness > 0)
{
if (pData->m_nPixelOffsetMode == 0)
GetGdiPlus()->GdipSetPixelOffsetMode(pGpGraphics, PixelOffsetModeHalf);
else
GetGdiPlus()->GdipSetPixelOffsetMode(pGpGraphics, (PixelOffsetMode)pData->m_nPixelOffsetMode);
GpPen* pGpPen = CreateGpPen(pStrokeStyle, pData->GetBounds());
GetGdiPlus()->GdipDrawPath(pGpGraphics, pGpPen, pGpPath);
GetGdiPlus()->GdipDeletePen(pGpPen);
}
GetGdiPlus()->GdipDeletePath(pGpPath);
}
}
ReleaseGraphics(pGpGraphics);
}
void CXTPMarkupGdiPlusDeviceContext::DrawLine(int x1, int y1, int x2, int y2, CXTPMarkupStrokeStyle* pStrokeStyle)
{
GpGraphics* pGpGraphics = GetGraphics();
if (!pGpGraphics)
{
CXTPMarkupDeviceContext::DrawLine(x1, y1, x2, y2, pStrokeStyle);
return;
}
GetGdiPlus()->GdipSetSmoothingMode(pGpGraphics, x1 == x2 || y1 == y2 ? SmoothingModeHighSpeed : SmoothingModeHighQuality);
GetGdiPlus()->GdipSetPixelOffsetMode(pGpGraphics, (pStrokeStyle->nStrokeThickness % 2) ? PixelOffsetModeDefault : PixelOffsetModeHalf);
GpPen* pGpPen = CreateGpPen(pStrokeStyle, CRect(x1, y1, x2, y2));
GetGdiPlus()->GdipDrawLineI(pGpGraphics, pGpPen, x1, y1, x2, y2);
GetGdiPlus()->GdipDeletePen(pGpPen);
ReleaseGraphics(pGpGraphics);
}
GpBrush* CXTPMarkupGdiPlusDeviceContext::CreateGpBrush(CXTPMarkupBrush* pBrush, LPCRECT lpRect) const
{
if (IsSolidBrush(pBrush))
{
CXTPMarkupSolidColorBrush* pSolidColorBrush = (CXTPMarkupSolidColorBrush*)pBrush;
CXTPMarkupColor* pColor = pSolidColorBrush->GetColor();
if (!pColor)
return NULL;
GpSolidFill* pGpBrush = NULL;
COLORREF clr = *pColor;
GetGdiPlus()->GdipCreateSolidFill(RGB(GetBValue(clr), GetGValue(clr), GetRValue(clr)) | (clr & 0xFF000000) , &pGpBrush);
return pGpBrush;
}
if (IsLinearGradientBrush(pBrush))
{
CXTPMarkupLinearGradientBrush* pLinearGradientBrush = (CXTPMarkupLinearGradientBrush*)pBrush;
PointF ptStartPoint(0, 0);
CXTPMarkupPoint* pt = MARKUP_STATICCAST(CXTPMarkupPoint, pBrush->GetValue(CXTPMarkupLinearGradientBrush::m_pStartPointProperty));
if (pt) ptStartPoint = PointF((REAL)pt->x, (REAL)pt->y);
PointF ptEndPoint(1, 1);
pt = MARKUP_STATICCAST(CXTPMarkupPoint, pBrush->GetValue(CXTPMarkupLinearGradientBrush::m_pEndPointProperty));
if (pt) ptEndPoint = PointF((REAL)pt->x, (REAL)pt->y);
CXTPMarkupGradientStops* pGradientStops = pLinearGradientBrush->GetGradientStops();
int nCount = pGradientStops->GetCount();
if (nCount == 0)
return NULL;
GRADIENTSTOP* pStops = new GRADIENTSTOP[nCount];
int i;
for (i = 0; i < nCount; i++)
{
CXTPMarkupGradientStop* pItem = pGradientStops->GetItem(i);
COLORREF clr = pItem->GetColor();
pStops[i].clr.rgbRed = GetRValue(clr);
pStops[i].clr.rgbGreen = GetGValue(clr);
pStops[i].clr.rgbBlue = GetBValue(clr);
pStops[i].clr.rgbReserved = ((BYTE)((clr)>>24));
pStops[i].dOffset = pItem->GetOffset();
pStops[i].nIndex = i;
}
qsort(pStops, nCount, sizeof(GRADIENTSTOP), _GradientStopCompare);
GpLineGradient* pGpBrush = NULL;
PointF ptStart(lpRect->left + ptStartPoint.X * (lpRect->right - lpRect->left), lpRect->top + ptStartPoint.Y * (lpRect->bottom - lpRect->top));
PointF ptEnd(lpRect->left + ptEndPoint.X * (lpRect->right - lpRect->left), lpRect->top + ptEndPoint.Y * (lpRect->bottom - lpRect->top));
GetGdiPlus()->GdipCreateLineBrush(&ptStart, &ptEnd,
*(ARGB*)&pStops[0].clr, *(ARGB*)&pStops[nCount - 1].clr, WrapModeTileFlipXY, &pGpBrush);
ARGB* pBlend = new ARGB[nCount + 2];
REAL* pPositions = new REAL[nCount + 2];
for (i = 0; i < nCount; i++)
{
pBlend[i + 1] = *(ARGB*)&pStops[i].clr;
pPositions[i + 1] = (REAL)pStops[i].dOffset;
}
pBlend[0] = *(ARGB*)&pStops[0].clr;
pPositions[0] = 0;
pBlend[nCount + 1] = *(ARGB*)&pStops[nCount - 1].clr;
pPositions[nCount + 1] = 1;
GetGdiPlus()->GdipSetLinePresetBlend(pGpBrush, pBlend, pPositions, nCount + 2);
delete[] pPositions;
delete[] pBlend;
delete[] pStops;
return pGpBrush;
}
return NULL;
}
GpPen* CXTPMarkupGdiPlusDeviceContext::CreateGpPen(CXTPMarkupStrokeStyle* pStrokeStyle, LPCRECT lpRect) const
{
if (!pStrokeStyle || !pStrokeStyle->pStrokeBrush)
return NULL;
CXTPMarkupBrush* pBrush = pStrokeStyle->pStrokeBrush;
GpPen* pGpPen = 0;
if (IsSolidBrush(pBrush))
{
CXTPMarkupSolidColorBrush* pSolidColorBrush = (CXTPMarkupSolidColorBrush*)pBrush;
CXTPMarkupColor* pColor = pSolidColorBrush->GetColor();
if (!pColor)
return NULL;
COLORREF clr = *pColor;
GetGdiPlus()->GdipCreatePen1(RGB(GetBValue(clr), GetGValue(clr), GetRValue(clr)) | (clr & 0xFF000000) , (REAL)pStrokeStyle->nStrokeThickness, UnitWorld, &pGpPen);
}
else
{
GpBrush* pGpBrush = CreateGpBrush(pBrush, lpRect);
if (pGpBrush)
{
GetGdiPlus()->GdipCreatePen2(pGpBrush, (REAL)pStrokeStyle->nStrokeThickness, UnitWorld, &pGpPen);
GetGdiPlus()->GdipDeleteBrush(pGpBrush);
}
}
if (!pGpPen)
return NULL;
if (pStrokeStyle->pStrokeDashArray)
{
if (pStrokeStyle->pStrokeDashArray->GetCount() == 1)
{
REAL dist = pStrokeStyle->pStrokeDashArray->GetAt(0);
REAL rDashStyle[] = {dist, dist};
GetGdiPlus()->GdipSetPenDashArray(pGpPen, rDashStyle, 2);
}
else
{
GetGdiPlus()->GdipSetPenDashArray(pGpPen, pStrokeStyle->pStrokeDashArray->GetData(), pStrokeStyle->pStrokeDashArray->GetCount());
}
}
if (pStrokeStyle->nStrokeLineJoin != xtpMarkupLineJoinMiter)
{
GetGdiPlus()->GdipSetPenLineJoin(pGpPen, (LineJoin)pStrokeStyle->nStrokeLineJoin);
}
if (pStrokeStyle->nStrokeStartLineCap != xtpMarkupLineCapFlat)
{
GetGdiPlus()->GdipSetPenStartCap(pGpPen, (LineCap)pStrokeStyle->nStrokeStartLineCap);
}
if (pStrokeStyle->nStrokeEndLineCap != xtpMarkupLineCapFlat)
{
GetGdiPlus()->GdipSetPenEndCap(pGpPen, (LineCap)pStrokeStyle->nStrokeEndLineCap);
}
return pGpPen;
}
void CXTPMarkupGdiPlusDeviceContext::FillRectangle(CRect rc, CXTPMarkupBrush* pBrush)
{
if (m_hDC && IsGDIBrush(pBrush))
{
CXTPMarkupDeviceContext::FillRectangle(rc, pBrush);
return;
}
GpGraphics* pGpGraphics = GetGraphics();
if (!pGpGraphics)
{
CXTPMarkupDeviceContext::FillRectangle(rc, pBrush);
return;
}
GpBrush* pGpBrush = CreateGpBrush(pBrush, rc);
if (pGpBrush)
{
GetGdiPlus()->GdipSetSmoothingMode(pGpGraphics, SmoothingModeHighSpeed);
GetGdiPlus()->GdipSetPixelOffsetMode(pGpGraphics, PixelOffsetModeHalf);
GetGdiPlus()->GdipFillRectangleI(pGpGraphics, pGpBrush, rc.left, rc.top, rc.Width(), rc.Height());
GetGdiPlus()->GdipDeleteBrush(pGpBrush);
}
ReleaseGraphics(pGpGraphics);
}
BOOL CXTPMarkupGdiPlusDeviceContext::IsGDIBrush(CXTPMarkupBrush* pBrush) const
{
if (!pBrush)
return TRUE;
CXTPMarkupSolidColorBrush* pSolidColorBrush = MARKUP_DYNAMICCAST(CXTPMarkupSolidColorBrush, pBrush);
if (!pSolidColorBrush)
return FALSE;
CXTPMarkupColor* pColor = pSolidColorBrush->GetColor();
if (!pColor)
return TRUE;
return ((*pColor) & 0xFF000000) == 0xFF000000;
}
void CXTPMarkupGdiPlusDeviceContext::DrawRectangle(CRect rc, CXTPMarkupBrush* pBrush, CXTPMarkupThickness* pThickness)
{
if (!pThickness)
return;
if (m_hDC && IsGDIBrush(pBrush))
{
CXTPMarkupDeviceContext::DrawRectangle(rc, pBrush, pThickness);
return;
}
GpGraphics* pGpGraphics = GetGraphics();
if (!pGpGraphics)
{
CXTPMarkupDeviceContext::DrawRectangle(rc, pBrush, pThickness);
return;
}
GetGdiPlus()->GdipSetSmoothingMode(pGpGraphics, SmoothingModeHighSpeed);
GpBrush* pGpBrush = CreateGpBrush(pBrush, rc);
if (pThickness->GetLeft() > 0)
GetGdiPlus()->GdipFillRectangleI(pGpGraphics, pGpBrush, rc.left, rc.top, pThickness->GetLeft(), rc.Height());
if (pThickness->GetRight() > 0)
GetGdiPlus()->GdipFillRectangleI(pGpGraphics, pGpBrush, rc.right - pThickness->GetRight(), rc.top, pThickness->GetRight(), rc.Height());
if (pThickness->GetTop() > 0)
GetGdiPlus()->GdipFillRectangleI(pGpGraphics, pGpBrush, rc.left + pThickness->GetLeft(), rc.top, rc.Width() - pThickness->GetLeft() - pThickness->GetRight(), pThickness->GetTop());
if (pThickness->GetBottom() > 0)
GetGdiPlus()->GdipFillRectangleI(pGpGraphics, pGpBrush, rc.left + pThickness->GetLeft(), rc.bottom - pThickness->GetBottom(), rc.Width() - pThickness->GetLeft() - pThickness->GetRight(), pThickness->GetBottom());
GetGdiPlus()->GdipDeleteBrush(pGpBrush);
ReleaseGraphics(pGpGraphics);
}
void CXTPMarkupGdiPlusDeviceContext::FillRoundRectangle(CRect rc, CXTPMarkupBrush* pBrush, double* pCornerRadius)
{
CXTPMarkupDeviceContext::FillRoundRectangle(rc, pBrush, pCornerRadius);
}
void CXTPMarkupGdiPlusDeviceContext::DrawRoundRectangle(CRect rc, CXTPMarkupBrush* pBrush, CXTPMarkupThickness* pThickness, double* pCornerRadius)
{
CXTPMarkupDeviceContext::DrawRoundRectangle(rc, pBrush, pThickness, pCornerRadius);
}
//////////////////////////////////////////////////////////////////////////
// CXTPMarkupGdiPlusExtendedDeviceContext
CXTPMarkupGdiPlusExtendedDeviceContext::CXTPMarkupGdiPlusExtendedDeviceContext(GpGraphics* pGpGraphics)
: CXTPMarkupGdiPlusDeviceContext(0, 0)
{
m_pGraphics = pGpGraphics;
m_pGpFont = NULL;
m_pGpTextBrush = NULL;
GpStringFormat* pGpGenericTypographicStringFormat = NULL;
GetGdiPlus()->GdipStringFormatGetGenericTypographic(&pGpGenericTypographicStringFormat);
m_pGpStringFormat = NULL;
GetGdiPlus()->GdipCloneStringFormat(pGpGenericTypographicStringFormat, &m_pGpStringFormat);
GetGdiPlus()->GdipSetStringFormatFlags(m_pGpStringFormat, StringFormatFlagsNoWrap + StringFormatFlagsMeasureTrailingSpaces + StringFormatFlagsNoClip);
}
CXTPMarkupGdiPlusExtendedDeviceContext::~CXTPMarkupGdiPlusExtendedDeviceContext()
{
if (m_pGpFont)
{
GetGdiPlus()->GdipDeleteFont(m_pGpFont);
m_pGpFont = NULL;
}
if (m_pGpTextBrush)
{
GetGdiPlus()->GdipDeleteBrush(m_pGpTextBrush);
m_pGpTextBrush = NULL;
}
GetGdiPlus()->GdipDeleteStringFormat(m_pGpStringFormat);
}
GpGraphics* CXTPMarkupGdiPlusExtendedDeviceContext::GetGraphics()
{
return m_pGraphics;
}
void CXTPMarkupGdiPlusExtendedDeviceContext::ReleaseGraphics(GpGraphics* /*pGpGraphics*/)
{
}
void CXTPMarkupGdiPlusExtendedDeviceContext::GetClipBox(LPRECT lpRect)
{
GpRect rect;
GetGdiPlus()->GdipGetClipBoundsI(m_pGraphics, &rect);
lpRect->left = rect.GetLeft();
lpRect->top = rect.GetTop();
lpRect->right = rect.GetRight();
lpRect->bottom = rect.GetBottom();
}
HRGN CXTPMarkupGdiPlusExtendedDeviceContext::SaveClipRegion()
{
GpRegion* pGpRegion = NULL;
GetGdiPlus()->GdipCreateRegion(&pGpRegion);
GetGdiPlus()->GdipGetClip(m_pGraphics, pGpRegion);
return (HRGN)pGpRegion;
}
void CXTPMarkupGdiPlusExtendedDeviceContext::RestoreClipRegion(HRGN hrgnClip)
{
GpRegion* pGpRegion = (GpRegion*)hrgnClip;
if (pGpRegion != NULL)
{
GetGdiPlus()->GdipSetClipRegion(m_pGraphics, pGpRegion, CombineModeReplace);
GetGdiPlus()->GdipDeleteRegion(pGpRegion);
}
}
void CXTPMarkupGdiPlusExtendedDeviceContext::IntersectClipRect(const RECT& rcLayoutClip)
{
GetGdiPlus()->GdipSetClipRectI(m_pGraphics, rcLayoutClip.left, rcLayoutClip.top,
rcLayoutClip.right - rcLayoutClip.left, rcLayoutClip.bottom - rcLayoutClip.top, CombineModeIntersect);
}
void CXTPMarkupGdiPlusExtendedDeviceContext::OffsetViewport(const POINT& ptViewortOrg)
{
GetGdiPlus()->GdipTranslateWorldTransform(m_pGraphics, (REAL)ptViewortOrg.x, (REAL)ptViewortOrg.y, MatrixOrderPrepend);
HDC hDC = GetDC();
POINT pt = TranslatePoint(ptViewortOrg);
OffsetViewportOrgEx(hDC, pt.x, pt.y, NULL);
ReleaseDC(hDC);
}
void CXTPMarkupGdiPlusExtendedDeviceContext::SetTextFont(CXTPMarkupFont* pFont)
{
if (m_pGpFont)
{
GetGdiPlus()->GdipDeleteFont(m_pGpFont);
m_pGpFont = NULL;
}
if (pFont)
{
HDC hDC = GetDC();
#ifdef _UNICODE
GetGdiPlus()->GdipCreateFontFromLogfontW(hDC, &pFont->m_lf, &m_pGpFont);
#else
GetGdiPlus()->GdipCreateFontFromLogfontA(hDC, &pFont->m_lf, &m_pGpFont);
#endif
ReleaseDC(hDC);
}
}
void CXTPMarkupGdiPlusExtendedDeviceContext::SetTextColor(CXTPMarkupBrush* pBrush)
{
if (m_pGpTextBrush)
{
GetGdiPlus()->GdipDeleteBrush(m_pGpTextBrush);
m_pGpTextBrush = NULL;
}
m_pGpTextBrush = CreateGpBrush(pBrush, CRect(0, 0, 1, 1));
}
SIZE CXTPMarkupGdiPlusExtendedDeviceContext::MeasureString(LPCWSTR lpszText, int nCount) const
{
RectF rc;
RectF layoutRect(0, 0, 0.0f, 0.0f);
GetGdiPlus()->GdipMeasureString(m_pGraphics, lpszText, nCount, m_pGpFont, &layoutRect, m_pGpStringFormat, &rc, 0, 0);
SIZE sz;
sz.cx = (int)ceil(rc.Width);
sz.cy = (int)ceil(rc.Height);
return sz;
}
void CXTPMarkupGdiPlusExtendedDeviceContext::DrawString(LPCWSTR lpszString, UINT nCount, LPCRECT lpRect)
{
RectF rc((REAL)lpRect->left, (REAL)lpRect->top, (REAL)(lpRect->right - lpRect->left), (REAL)(lpRect->bottom - lpRect->top));
GetGdiPlus()->GdipDrawString(m_pGraphics, lpszString, nCount, m_pGpFont, &rc, m_pGpStringFormat, m_pGpTextBrush);
}
HDC CXTPMarkupGdiPlusExtendedDeviceContext::GetDC()
{
HDC hDC;
GetGdiPlus()->GdipGetDC(m_pGraphics, &hDC);
return hDC;
}
void CXTPMarkupGdiPlusExtendedDeviceContext::ReleaseDC(HDC hDC)
{
GetGdiPlus()->GdipReleaseDC(m_pGraphics, hDC);
}