// XTPMarkupDrawingContext.cpp: implementation of the CXTPMarkupDrawingContext class. // // This file is a part of the XTREME TOOLKIT PRO MFC class library. // (c)1998-2008 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 #include "Common/XTPImageManager.h" #include "Common/XTPvc80Helpers.h" #include "XTPMarkupDrawingContext.h" #include "XTPMarkupContext.h" #include "XTPMarkupBuilder.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CXTPMarkupDrawingContext::CXTPMarkupDrawingContext(HDC hDC) { m_hDC = hDC; m_bDeleteDC = FALSE; Init(); } CXTPMarkupDrawingContext::CXTPMarkupDrawingContext() { m_hDC = CreateCompatibleDC(0); m_bDeleteDC = TRUE; Init(); } void CXTPMarkupDrawingContext::Init() { m_hAttribDC = m_hDC; m_pSelectedFont = NULL; m_hOldFont = NULL; ::GetWindowExtEx(m_hDC, &m_sizeWinExt); ::GetViewportExtEx(m_hDC, &m_sizeVpExt); ::GetViewportOrgEx(m_hDC, &m_ptViewortOrg); m_rcClipBox.SetRectEmpty(); ::GetClipBox(m_hDC, &m_rcClipBox); ::SetBkMode(m_hDC, TRANSPARENT); } CXTPMarkupDrawingContext::~CXTPMarkupDrawingContext() { SetFont(NULL); if (m_bDeleteDC) { DeleteDC(m_hDC); } } POINT CXTPMarkupDrawingContext::TranslatePoint(const POINT& ptVisualOffset) const { POINT pt; pt.x = MulDiv(ptVisualOffset.x, abs(m_sizeVpExt.cx), abs(m_sizeWinExt.cx)); pt.y = MulDiv(ptVisualOffset.y, abs(m_sizeVpExt.cy), abs(m_sizeWinExt.cy)); return pt; } HDC CXTPMarkupDrawingContext::Detach() { SetFont(NULL); HDC hDC = m_hDC; m_hDC = NULL; return hDC; } void CXTPMarkupDrawingContext::SetViewportOrg(const POINT& ptViewortOrg) { SetViewportOrgEx(m_hDC, ptViewortOrg.x, ptViewortOrg.y, NULL); } POINT CXTPMarkupDrawingContext::OffsetViewport(const POINT& ptVisualOffset) { POINT ptViewortOrg = m_ptViewortOrg; POINT pt = TranslatePoint(ptVisualOffset); m_ptViewortOrg.x += pt.x; m_ptViewortOrg.y += pt.y; ::SetViewportOrgEx(m_hDC, m_ptViewortOrg.x, m_ptViewortOrg.y, NULL); ::GetClipBox(m_hDC, &m_rcClipBox); return ptViewortOrg; } void CXTPMarkupDrawingContext::SetViewport(const POINT& ptViewortOrg) { m_ptViewortOrg = ptViewortOrg; ::SetViewportOrgEx(m_hDC, m_ptViewortOrg.x, m_ptViewortOrg.y, NULL); ::GetClipBox(m_hDC, &m_rcClipBox); } HRGN CXTPMarkupDrawingContext::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 CXTPMarkupDrawingContext::IntersectClipRect(const RECT& rcLayoutClip) { ::IntersectClipRect(m_hDC, rcLayoutClip.left, rcLayoutClip.top, rcLayoutClip.right, rcLayoutClip.bottom); ::GetClipBox(m_hDC, m_rcClipBox); } void CXTPMarkupDrawingContext::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); } ::GetClipBox(m_hDC, m_rcClipBox); } } void CXTPMarkupDrawingContext::FillSolidRect(CRect rc, COLORREF clr) { CXTPMarkupSolidColorBrush brush(clr); brush.FillRect(this, rc); } void CXTPMarkupDrawingContext::DrawRectangle(CXTPMarkupBrush* pBrush, CRect rc) { pBrush->FillRect(this, rc); } void CXTPMarkupDrawingContext::DrawFrame(CXTPMarkupBrush* pBrush, CRect rc) { pBrush->FillRect(this, CRect(rc.left, rc.top, rc.right, rc.top + 1)); pBrush->FillRect(this, CRect(rc.left, rc.top, rc.left + 1, rc.bottom)); pBrush->FillRect(this, CRect(rc.left, rc.bottom - 1, rc.right, rc.bottom)); pBrush->FillRect(this, CRect(rc.right - 1, rc.top, rc.right, rc.bottom)); } void CXTPMarkupDrawingContext::DrawTextLine(LPCWSTR lpszText, UINT nCount, LPCRECT lpRect) { ::ExtTextOutW(m_hDC, lpRect->left, lpRect->top, 0, lpRect, lpszText, nCount, 0); } void CXTPMarkupDrawingContext::SetFont(CXTPMarkupFont* pFont) { if (!m_hDC) return; if (m_pSelectedFont == pFont) 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); } MARKUP_RELEASE(m_pSelectedFont); m_pSelectedFont = pFont; MARKUP_ADDREF(m_pSelectedFont); } SIZE CXTPMarkupDrawingContext::GetTextExtent(LPCWSTR lpszText, int nCount) const { SIZE sz; VERIFY(::GetTextExtentPoint32W(m_hAttribDC, lpszText, nCount, &sz)); return sz; } HFONT CXTPMarkupDrawingContext::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; } CXTPMarkupPrintingContext::CXTPMarkupPrintingContext(HDC hDC, HDC hAttribDC) : CXTPMarkupDrawingContext(hDC) { m_hAttribDC = hAttribDC; } void CXTPMarkupPrintingContext::DrawTextLine(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 CXTPMarkupPrintingContext::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 CXTPMarkupPrintingContext::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; } ////////////////////////////////////////////////////////////////////////// // CXTPMarkupBrush IMPLEMENT_MARKUPCLASS(NULL, CXTPMarkupBrush, CXTPMarkupObject); void CXTPMarkupBrush::RegisterMarkupClass() { } void CXTPMarkupBrush::FillRect(CXTPMarkupDrawingContext* /*pDC*/, CRect /*rc*/) { } COLORREF CXTPMarkupBrush::GetHintColor() const { return 0; } CXTPMarkupObject* CXTPMarkupBrush::ConvertFrom(CXTPMarkupObject* pObject) const { if (IsStringObject(pObject)) { COLORREF clr; if (CXTPMarkupColor::ConvertFromString(*((CXTPMarkupString*)pObject), clr)) { return new CXTPMarkupSolidColorBrush(clr); } } return NULL; } ////////////////////////////////////////////////////////////////////////// // CXTPMarkupSolidColorBrush CXTPMarkupDependencyProperty* CXTPMarkupSolidColorBrush::m_pColorProperty = NULL; IMPLEMENT_MARKUPCLASS(L"SolidColorBrush", CXTPMarkupSolidColorBrush, CXTPMarkupBrush); void CXTPMarkupSolidColorBrush::RegisterMarkupClass() { m_pColorProperty = CXTPMarkupDependencyProperty::Register(L"Color", MARKUP_TYPE(CXTPMarkupColor), MARKUP_TYPE(CXTPMarkupSolidColorBrush)); } CXTPMarkupSolidColorBrush::CXTPMarkupSolidColorBrush() { } CXTPMarkupSolidColorBrush::CXTPMarkupSolidColorBrush(COLORREF clr) { SetValue(m_pColorProperty, new CXTPMarkupColor(clr)); } BOOL CXTPMarkupSolidColorBrush::IsEqual(const CXTPMarkupObject* pObject) const { if (!pObject) return FALSE; if (pObject->GetType() != MARKUP_TYPE(CXTPMarkupSolidColorBrush)) return FALSE; return GetHintColor() == ((CXTPMarkupSolidColorBrush*)pObject)->GetHintColor(); } void CXTPMarkupSolidColorBrush::FillRect(CXTPMarkupDrawingContext* pDC, CRect rc) { CXTPMarkupColor* pColor = MARKUP_STATICCAST(CXTPMarkupColor, GetValue(m_pColorProperty)); if (pColor) { COLORREF clrOld = ::SetBkColor(pDC->GetSafeHdc(), *pColor); ::ExtTextOut(pDC->GetSafeHdc(), 0, 0, ETO_OPAQUE, rc, NULL, 0, NULL); ::SetBkColor(pDC->GetSafeHdc(), clrOld); } } COLORREF CXTPMarkupSolidColorBrush::GetHintColor() const { CXTPMarkupColor* pColor = MARKUP_STATICCAST(CXTPMarkupColor, GetValue(m_pColorProperty)); if (pColor) { return *pColor; } return 0; } ////////////////////////////////////////////////////////////////////////// // CXTPMarkupPoint IMPLEMENT_MARKUPCLASS(NULL, CXTPMarkupPoint, CXTPMarkupObject); void CXTPMarkupPoint::RegisterMarkupClass() { } CXTPMarkupPoint::CXTPMarkupPoint() { x = 0; y = 0; } CXTPMarkupPoint::CXTPMarkupPoint(double x, double y) { this->x = x; this->y = y; } CXTPMarkupPoint& CXTPMarkupPoint::operator=(const CXTPMarkupPoint& srcPoint) { x = srcPoint.x; y = srcPoint.y; return *this; } CXTPMarkupObject* CXTPMarkupPoint::ConvertFrom(CXTPMarkupObject* pObject) const { if (IsStringObject(pObject)) { LPCWSTR lpszValue = *(CXTPMarkupString*)pObject; double x = 0, y = 0; if (!CXTPMarkupBuilder::ConvertDouble(lpszValue, x, _T(','), FALSE)) return NULL; if (!CXTPMarkupBuilder::ConvertDouble(lpszValue, y)) return NULL; return new CXTPMarkupPoint(x, y); } return NULL; } ////////////////////////////////////////////////////////////////////////// // CXTPMarkupGradientStop CXTPMarkupDependencyProperty* CXTPMarkupGradientStop::m_pColorProperty = NULL; CXTPMarkupDependencyProperty* CXTPMarkupGradientStop::m_pOffsetProperty = NULL; IMPLEMENT_MARKUPCLASS(L"GradientStop", CXTPMarkupGradientStop, CXTPMarkupObject); void CXTPMarkupGradientStop::RegisterMarkupClass() { m_pColorProperty = CXTPMarkupDependencyProperty::Register(L"Color", MARKUP_TYPE(CXTPMarkupColor), MARKUP_TYPE(CXTPMarkupGradientStop)); m_pOffsetProperty = CXTPMarkupDependencyProperty::Register(L"Offset", MARKUP_TYPE(CXTPMarkupDouble), MARKUP_TYPE(CXTPMarkupGradientStop)); } CXTPMarkupGradientStop::CXTPMarkupGradientStop() { } CXTPMarkupGradientStop::CXTPMarkupGradientStop(COLORREF clr, double dOffset) { SetValue(m_pColorProperty, new CXTPMarkupColor(clr)); SetValue(m_pOffsetProperty, new CXTPMarkupDouble(dOffset)); } COLORREF CXTPMarkupGradientStop::GetColor() const { CXTPMarkupColor* pColor = MARKUP_STATICCAST(CXTPMarkupColor, GetValue(m_pColorProperty)); return pColor != NULL ? (COLORREF)*pColor : 0; } double CXTPMarkupGradientStop::GetOffset() const { CXTPMarkupDouble* pOffset = MARKUP_STATICCAST(CXTPMarkupDouble, GetValue(m_pOffsetProperty)); return pOffset != NULL ? (double)*pOffset : 0.0; } CXTPMarkupGradientStops::CXTPMarkupGradientStops() { m_pElementType = MARKUP_TYPE(CXTPMarkupGradientStop); } ////////////////////////////////////////////////////////////////////////// // CXTPMarkupLinearGradientBrush CXTPMarkupDependencyProperty* CXTPMarkupLinearGradientBrush::m_pStartPointProperty = NULL; CXTPMarkupDependencyProperty* CXTPMarkupLinearGradientBrush::m_pEndPointProperty = NULL; IMPLEMENT_MARKUPCLASS(L"LinearGradientBrush", CXTPMarkupLinearGradientBrush, CXTPMarkupBrush); void CXTPMarkupLinearGradientBrush::RegisterMarkupClass() { m_pStartPointProperty = CXTPMarkupDependencyProperty::Register(L"StartPoint", MARKUP_TYPE(CXTPMarkupPoint), MARKUP_TYPE(CXTPMarkupLinearGradientBrush)); m_pEndPointProperty = CXTPMarkupDependencyProperty::Register(L"EndPoint", MARKUP_TYPE(CXTPMarkupPoint), MARKUP_TYPE(CXTPMarkupLinearGradientBrush)); } CXTPMarkupLinearGradientBrush::CXTPMarkupLinearGradientBrush() { m_pGradientStops = new CXTPMarkupGradientStops(); } CXTPMarkupLinearGradientBrush::CXTPMarkupLinearGradientBrush(CXTPMarkupGradientStops* pGradientStops) { m_pGradientStops = pGradientStops; } CXTPMarkupLinearGradientBrush::~CXTPMarkupLinearGradientBrush() { MARKUP_RELEASE(m_pGradientStops); } void CXTPMarkupLinearGradientBrush::SetStartPoint(double x, double y) { SetValue(m_pStartPointProperty, new CXTPMarkupPoint(x, y)); } void CXTPMarkupLinearGradientBrush::SetEndPoint(double x, double y) { SetValue(m_pEndPointProperty, new CXTPMarkupPoint(x, y)); } void CXTPMarkupLinearGradientBrush::SetContentObject(CXTPMarkupBuilder* pBuilder, CXTPMarkupObject* pContent) { m_pGradientStops->SetContentObject(pBuilder, pContent); } int _cdecl CXTPMarkupLinearGradientBrush::_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 CXTPMarkupLinearGradientBrush::FillRect(CXTPMarkupDrawingContext* pDC , CRect rc) { HDC hDC = pDC->GetSafeHdc(); int cx = rc.Width(); int cy = rc.Height(); if (m_pGradientStops->GetCount() == 0) return; if (cx <= 0 || cy <= 0) return; if (m_pGradientStops->GetCount() == 1) { COLORREF clr = m_pGradientStops->GetItem(0)->GetColor(); COLORREF clrOld = ::SetBkColor(hDC, clr); ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, rc, NULL, 0, NULL); ::SetBkColor(hDC, clrOld); return; } CXTPMarkupPoint ptStartPoint(0, 0); if (GetValue(m_pStartPointProperty)) ptStartPoint = *MARKUP_STATICCAST(CXTPMarkupPoint, GetValue(m_pStartPointProperty)); CXTPMarkupPoint ptEndPoint(1, 1); if (GetValue(m_pEndPointProperty)) ptEndPoint = *MARKUP_STATICCAST(CXTPMarkupPoint, GetValue(m_pEndPointProperty)); 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 = pDC->GetClipBox(); 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 = m_pGradientStops->GetCount(); GRADIENTSTOP* pStops = new GRADIENTSTOP[nCount]; for (int i = 0; i < nCount; i++) { CXTPMarkupGradientStop* pItem = m_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; }