在任务栏右下角弹出气球式消息,指定时间后消失:
void CTrayDemoDlg::ShowBalloon(CString szText)
{
CTBalloon * pballoon = new CTBalloon(150,100);
pballoon->SetText(szText);//文字
pballoon->SetLifeTime(4); //停留时间(秒)
pballoon->CreateAndShow();
}
TBalloon.h
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
TBalloon.h
#if !defined(AFX_TBALLOON_H__8363E22A_9845_4086_B3A0_40117DCFDB1F__INCLUDED_)
#define AFX_TBALLOON_H__8363E22A_9845_4086_B3A0_40117DCFDB1F__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// /
// class CTBalloon
/// //
// CTBalloon window
#include " Gradient.h "
class CTBalloon : public CWnd
{
// Construction
public :
CTBalloon(UINT nWidth, UINT nHeight);
BOOL CreateAndShow();
void SetText(CString str);
void SetLifeTime(UINT secs);
static UINT m_sActiveCount;
// Attributes
private :
CRect m_current_rect;
CRect m_screen_rect;
UINT m_nWidth;
UINT m_nHeight;
UINT m_totaltime;
UINT m_lifetime;
BOOL m_dir;
CString m_text;
CGradient m_grad;
// Overrides
// ClassWizard generated virtual function overrides
// {{AFX_VIRTUAL(CTBalloon)
public :
protected :
virtual void PostNcDestroy();
// }}AFX_VIRTUAL
// Implementation
public :
virtual ~ CTBalloon();
// Generated message map functions
protected :
// {{AFX_MSG(CTBalloon)
afx_msg void OnPaint();
afx_msg void OnTimer(UINT nIDEvent);
// }}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/// //
// {{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_TBALLOON_H__8363E22A_9845_4086_B3A0_40117DCFDB1F__INCLUDED_)
#if !defined(AFX_TBALLOON_H__8363E22A_9845_4086_B3A0_40117DCFDB1F__INCLUDED_)
#define AFX_TBALLOON_H__8363E22A_9845_4086_B3A0_40117DCFDB1F__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// /
// class CTBalloon
/// //
// CTBalloon window
#include " Gradient.h "
class CTBalloon : public CWnd
{
// Construction
public :
CTBalloon(UINT nWidth, UINT nHeight);
BOOL CreateAndShow();
void SetText(CString str);
void SetLifeTime(UINT secs);
static UINT m_sActiveCount;
// Attributes
private :
CRect m_current_rect;
CRect m_screen_rect;
UINT m_nWidth;
UINT m_nHeight;
UINT m_totaltime;
UINT m_lifetime;
BOOL m_dir;
CString m_text;
CGradient m_grad;
// Overrides
// ClassWizard generated virtual function overrides
// {{AFX_VIRTUAL(CTBalloon)
public :
protected :
virtual void PostNcDestroy();
// }}AFX_VIRTUAL
// Implementation
public :
virtual ~ CTBalloon();
// Generated message map functions
protected :
// {{AFX_MSG(CTBalloon)
afx_msg void OnPaint();
afx_msg void OnTimer(UINT nIDEvent);
// }}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/// //
// {{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_TBALLOON_H__8363E22A_9845_4086_B3A0_40117DCFDB1F__INCLUDED_)
TBalloon.cpp
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
TBalloon.cpp
// /
// class CTBalloon
#include " stdafx.h "
// #include "TrayBalloon.h"
#include " TBalloon.h "
#include " Gradient.h "
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/// //
// CTBalloon
#define BALLOON_SHOW_TIMER 0x10
#define TIMER_MS 20
#define BALLOON_LIFETIME (3*1000)
#define STEP_SIZE 5
UINT CTBalloon::m_sActiveCount;
CTBalloon::CTBalloon(UINT nWidth, UINT nHeight): m_nWidth(nWidth), m_nHeight(nHeight)
{
SystemParametersInfo(SPI_GETWORKAREA, 0 , & m_screen_rect, 0 );
m_totaltime = 0 ;
m_lifetime = BALLOON_LIFETIME;
m_dir = TRUE;
}
CTBalloon:: ~ CTBalloon()
{
}
BEGIN_MESSAGE_MAP(CTBalloon, CWnd)
// {{AFX_MSG_MAP(CTBalloon)
ON_WM_PAINT()
ON_WM_TIMER()
// }}AFX_MSG_MAP
END_MESSAGE_MAP()
void CTBalloon::SetText(CString str)
{
m_text = str;
}
void CTBalloon::SetLifeTime(UINT secs)
{
m_lifetime = secs * 1000 ;
}
BOOL CTBalloon::CreateAndShow()
{
const wchar_t * p = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW );
CRect rect;
rect.top = m_screen_rect.bottom - m_nHeight;
rect.bottom = m_screen_rect.bottom;
rect.left = m_screen_rect.right - m_nWidth - 10 - (m_sActiveCount * 10 ) ;
rect.right = m_screen_rect.right - 10 - (m_sActiveCount * 10 );
DWORD dwStyle = WS_POPUP;
DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE | WS_EX_TOPMOST | WS_EX_WINDOWEDGE ;
BOOL ret = CreateEx(dwExStyle,p,NULL,dwStyle,rect,NULL,NULL);
m_sActiveCount ++ ;
m_current_rect.top = m_current_rect.bottom = m_screen_rect.bottom;
m_current_rect.left = rect.left;
m_current_rect.right = rect.right;
COLORREF lightblue = RGB( 154 , 190 , 255 );
COLORREF white = RGB( 255 , 255 , 255 );
m_grad.SetDirection(CGradient::RTL);
m_grad.SetGradientColorsX( 3 ,lightblue,lightblue,white);
SetTimer(BALLOON_SHOW_TIMER,TIMER_MS,NULL);
return ret;
}
/// //
// CTBalloon message handlers
void CTBalloon::OnPaint()
{
CPaintDC dc( this ); // device context for painting
CBrush br( 0xFF0000 );
CRect rect( 0 , 0 ,m_current_rect.Width(),m_current_rect.Height());
m_grad.DrawLinearGradient( & dc,rect);
dc.SelectStockObject(ANSI_VAR_FONT);
dc.SetBkMode(TRANSPARENT);
CRect temp = rect;
UINT height = dc.DrawText(m_text, - 1 ,temp,DT_CENTER | DT_WORDBREAK | DT_CALCRECT);
rect.top = (rect.Height() - height) / 2 ;
dc.DrawText(m_text, - 1 ,rect,DT_CENTER | DT_WORDBREAK);
// Do not call CWnd::OnPaint() for painting messages
}
void CTBalloon::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
m_totaltime += TIMER_MS;
if (m_totaltime > m_lifetime)
{
m_dir = FALSE;
}
if (m_dir)
{
m_current_rect.top = ((m_current_rect.top - STEP_SIZE) < (m_current_rect.bottom - (LONG)m_nHeight)) ? m_current_rect.bottom - m_nHeight : m_current_rect.top - STEP_SIZE;
}
else
{
m_current_rect.top = ((m_current_rect.top + STEP_SIZE) > (m_current_rect.bottom)) ? m_current_rect.bottom : m_current_rect.top + STEP_SIZE;
}
MoveWindow( & m_current_rect);
if (m_current_rect.top == m_current_rect.bottom)
{
KillTimer(BALLOON_SHOW_TIMER);
DestroyWindow();
return ;
}
CWnd * pWnd = GetFocus();
ShowWindow(SW_RESTORE);
if (pWnd)
pWnd -> SetFocus();
CWnd::OnTimer(nIDEvent);
}
void CTBalloon::PostNcDestroy()
{
// TODO: Add your specialized code here and/or call the base class
delete this ;
m_sActiveCount -- ;
}
// /
// class CTBalloon
#include " stdafx.h "
// #include "TrayBalloon.h"
#include " TBalloon.h "
#include " Gradient.h "
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/// //
// CTBalloon
#define BALLOON_SHOW_TIMER 0x10
#define TIMER_MS 20
#define BALLOON_LIFETIME (3*1000)
#define STEP_SIZE 5
UINT CTBalloon::m_sActiveCount;
CTBalloon::CTBalloon(UINT nWidth, UINT nHeight): m_nWidth(nWidth), m_nHeight(nHeight)
{
SystemParametersInfo(SPI_GETWORKAREA, 0 , & m_screen_rect, 0 );
m_totaltime = 0 ;
m_lifetime = BALLOON_LIFETIME;
m_dir = TRUE;
}
CTBalloon:: ~ CTBalloon()
{
}
BEGIN_MESSAGE_MAP(CTBalloon, CWnd)
// {{AFX_MSG_MAP(CTBalloon)
ON_WM_PAINT()
ON_WM_TIMER()
// }}AFX_MSG_MAP
END_MESSAGE_MAP()
void CTBalloon::SetText(CString str)
{
m_text = str;
}
void CTBalloon::SetLifeTime(UINT secs)
{
m_lifetime = secs * 1000 ;
}
BOOL CTBalloon::CreateAndShow()
{
const wchar_t * p = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW );
CRect rect;
rect.top = m_screen_rect.bottom - m_nHeight;
rect.bottom = m_screen_rect.bottom;
rect.left = m_screen_rect.right - m_nWidth - 10 - (m_sActiveCount * 10 ) ;
rect.right = m_screen_rect.right - 10 - (m_sActiveCount * 10 );
DWORD dwStyle = WS_POPUP;
DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE | WS_EX_TOPMOST | WS_EX_WINDOWEDGE ;
BOOL ret = CreateEx(dwExStyle,p,NULL,dwStyle,rect,NULL,NULL);
m_sActiveCount ++ ;
m_current_rect.top = m_current_rect.bottom = m_screen_rect.bottom;
m_current_rect.left = rect.left;
m_current_rect.right = rect.right;
COLORREF lightblue = RGB( 154 , 190 , 255 );
COLORREF white = RGB( 255 , 255 , 255 );
m_grad.SetDirection(CGradient::RTL);
m_grad.SetGradientColorsX( 3 ,lightblue,lightblue,white);
SetTimer(BALLOON_SHOW_TIMER,TIMER_MS,NULL);
return ret;
}
/// //
// CTBalloon message handlers
void CTBalloon::OnPaint()
{
CPaintDC dc( this ); // device context for painting
CBrush br( 0xFF0000 );
CRect rect( 0 , 0 ,m_current_rect.Width(),m_current_rect.Height());
m_grad.DrawLinearGradient( & dc,rect);
dc.SelectStockObject(ANSI_VAR_FONT);
dc.SetBkMode(TRANSPARENT);
CRect temp = rect;
UINT height = dc.DrawText(m_text, - 1 ,temp,DT_CENTER | DT_WORDBREAK | DT_CALCRECT);
rect.top = (rect.Height() - height) / 2 ;
dc.DrawText(m_text, - 1 ,rect,DT_CENTER | DT_WORDBREAK);
// Do not call CWnd::OnPaint() for painting messages
}
void CTBalloon::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
m_totaltime += TIMER_MS;
if (m_totaltime > m_lifetime)
{
m_dir = FALSE;
}
if (m_dir)
{
m_current_rect.top = ((m_current_rect.top - STEP_SIZE) < (m_current_rect.bottom - (LONG)m_nHeight)) ? m_current_rect.bottom - m_nHeight : m_current_rect.top - STEP_SIZE;
}
else
{
m_current_rect.top = ((m_current_rect.top + STEP_SIZE) > (m_current_rect.bottom)) ? m_current_rect.bottom : m_current_rect.top + STEP_SIZE;
}
MoveWindow( & m_current_rect);
if (m_current_rect.top == m_current_rect.bottom)
{
KillTimer(BALLOON_SHOW_TIMER);
DestroyWindow();
return ;
}
CWnd * pWnd = GetFocus();
ShowWindow(SW_RESTORE);
if (pWnd)
pWnd -> SetFocus();
CWnd::OnTimer(nIDEvent);
}
void CTBalloon::PostNcDestroy()
{
// TODO: Add your specialized code here and/or call the base class
delete this ;
m_sActiveCount -- ;
}
Gradient.h
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
Gradient.h
#if !defined(AFX_GRADIENT_H__2FB4902E_E00C_4892_AA03_60779F349F58__INCLUDED_)
#define AFX_GRADIENT_H__2FB4902E_E00C_4892_AA03_60779F349F58__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// /
// class CGradient
//
#include < afxtempl.h >
#ifndef UIBITS_API
#ifdef UIBITS_DLL
#define UIBITS_API __declspec(dllexport)
#else
#define UIBITS_API __declspec(dllimport)
#endif
#endif
struct CGradElement
{
CGradElement(COLORREF color = 0 , int length = 0 ){ this -> color = color; this -> length = length;}
COLORREF color;
int length;
};
typedef CArray < CGradElement, CGradElement &> CGradArray;
class /* UIBITS_API */ CGradient
{
// Construction
public :
CGradient();
virtual ~ CGradient();
// Attributes
public :
// linear gradient attributes
enum eDirection{LTR, RTL, TTB, BTT};
void SetDirection(eDirection fDirection){ if (m_fDirection != fDirection){m_fDirection = fDirection;}}
eDirection GetDirection() { return m_fDirection;}
void SetStretchGradient( float flStretchFactor = 1 ); // useful for animation
float GetStretchGradient() { return m_flStretchGrad;}
// linear gradient operations
virtual void DrawLinearGradient(CDC * pDC, CRect rcGrad, int nClipStart = 0 , int nClipEnd = - 1 , int nShift = 0 );
// attributes
void SetGradientColors(COLORREF clrStart, COLORREF clrEnd) { SetGradientColorsX( 2 , clrStart, clrEnd);}
void GetGradientColors(COLORREF & clrStart, COLORREF & clrEnd);
void SetGradientColorsX( int nCount, COLORREF clrFirst, COLORREF clrNext, ...);
const CDWordArray & GetGradientColorsX() { return m_ardwGradColors; }
void AddColor(COLORREF clr);
void SetColorsStretch( double flFirst, ...); // in percent, num of arguments should be one less then num of colors
void SetCreatePalette(BOOL fCreate = TRUE) {m_fCreatePalette = fCreate;}
BOOL GetCreatePalette() { return m_fCreatePalette;}
CPalette & GetPalette() {CreatePalette(); return m_Pal;}
// operations
virtual void CalcShiftedGradient(CGradArray & arElements, int nShift, int nGradWidth, int nClipStart = 0 , int nClipEnd = - 1 , UINT nMaxColors = (UINT) - 1 );
virtual void CalcMultiGradient(CGradArray & arElements, int nGradWidth, int nClipStart = 0 , int nClipEnd = - 1 , UINT nMaxColors = (UINT) - 1 );
virtual void CalcGradient(CGradArray & arElements, COLORREF clrStart, COLORREF clrEnd, int nGradWidth, int nClipStart = 0 , int nClipEnd = - 1 , UINT nMaxColors = (UINT) - 1 );
protected :
void CreatePalette();
void NormalizeColorsStretch();
// color atributes
CDWordArray m_ardwGradColors;
BOOL m_fCreatePalette;
CPalette m_Pal;
private :
CArray < double , double &> m_arflGradStretch;
protected :
// linear gradient
eDirection m_fDirection;
CRect ConvertToReal(CRect rcDraw, int nBandStart, int nBandEnd);
float m_flStretchGrad;
};
#endif // !defined(AFX_GRADIENT_H__2FB4902E_E00C_4892_AA03_60779F349F58__INCLUDED_)
#if !defined(AFX_GRADIENT_H__2FB4902E_E00C_4892_AA03_60779F349F58__INCLUDED_)
#define AFX_GRADIENT_H__2FB4902E_E00C_4892_AA03_60779F349F58__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// /
// class CGradient
//
#include < afxtempl.h >
#ifndef UIBITS_API
#ifdef UIBITS_DLL
#define UIBITS_API __declspec(dllexport)
#else
#define UIBITS_API __declspec(dllimport)
#endif
#endif
struct CGradElement
{
CGradElement(COLORREF color = 0 , int length = 0 ){ this -> color = color; this -> length = length;}
COLORREF color;
int length;
};
typedef CArray < CGradElement, CGradElement &> CGradArray;
class /* UIBITS_API */ CGradient
{
// Construction
public :
CGradient();
virtual ~ CGradient();
// Attributes
public :
// linear gradient attributes
enum eDirection{LTR, RTL, TTB, BTT};
void SetDirection(eDirection fDirection){ if (m_fDirection != fDirection){m_fDirection = fDirection;}}
eDirection GetDirection() { return m_fDirection;}
void SetStretchGradient( float flStretchFactor = 1 ); // useful for animation
float GetStretchGradient() { return m_flStretchGrad;}
// linear gradient operations
virtual void DrawLinearGradient(CDC * pDC, CRect rcGrad, int nClipStart = 0 , int nClipEnd = - 1 , int nShift = 0 );
// attributes
void SetGradientColors(COLORREF clrStart, COLORREF clrEnd) { SetGradientColorsX( 2 , clrStart, clrEnd);}
void GetGradientColors(COLORREF & clrStart, COLORREF & clrEnd);
void SetGradientColorsX( int nCount, COLORREF clrFirst, COLORREF clrNext, ...);
const CDWordArray & GetGradientColorsX() { return m_ardwGradColors; }
void AddColor(COLORREF clr);
void SetColorsStretch( double flFirst, ...); // in percent, num of arguments should be one less then num of colors
void SetCreatePalette(BOOL fCreate = TRUE) {m_fCreatePalette = fCreate;}
BOOL GetCreatePalette() { return m_fCreatePalette;}
CPalette & GetPalette() {CreatePalette(); return m_Pal;}
// operations
virtual void CalcShiftedGradient(CGradArray & arElements, int nShift, int nGradWidth, int nClipStart = 0 , int nClipEnd = - 1 , UINT nMaxColors = (UINT) - 1 );
virtual void CalcMultiGradient(CGradArray & arElements, int nGradWidth, int nClipStart = 0 , int nClipEnd = - 1 , UINT nMaxColors = (UINT) - 1 );
virtual void CalcGradient(CGradArray & arElements, COLORREF clrStart, COLORREF clrEnd, int nGradWidth, int nClipStart = 0 , int nClipEnd = - 1 , UINT nMaxColors = (UINT) - 1 );
protected :
void CreatePalette();
void NormalizeColorsStretch();
// color atributes
CDWordArray m_ardwGradColors;
BOOL m_fCreatePalette;
CPalette m_Pal;
private :
CArray < double , double &> m_arflGradStretch;
protected :
// linear gradient
eDirection m_fDirection;
CRect ConvertToReal(CRect rcDraw, int nBandStart, int nBandEnd);
float m_flStretchGrad;
};
#endif // !defined(AFX_GRADIENT_H__2FB4902E_E00C_4892_AA03_60779F349F58__INCLUDED_)
Gradient.cpp
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
Gradient.cpp
// /
// class CGradient
//
#include " stdafx.h "
#include " Gradient.h "
/ /
// Construction/Destruction
/ /
CGradient::CGradient()
{
m_fCreatePalette = FALSE;
m_fDirection = LTR;
m_flStretchGrad = 1 ;
}
CGradient:: ~ CGradient()
{
}
void CGradient::DrawLinearGradient(CDC * pDC, CRect rcGrad, int nClipStart, int nClipEnd, int nShift)
{
BOOL f256Color = pDC -> GetDeviceCaps(RASTERCAPS) & RC_PALETTE;
if ( ! f256Color && (pDC -> GetDeviceCaps(BITSPIXEL) * pDC -> GetDeviceCaps(PLANES) < 8 ))
{
// for 16 colors no gradient
ASSERT(m_ardwGradColors.GetSize() > 0 );
pDC -> FillSolidRect( & ConvertToReal(rcGrad, nClipStart, nClipEnd), m_ardwGradColors[ 0 ]);
return ;
}
int nGradWidth = 0 ;
if (m_fDirection == TTB || m_fDirection == BTT)
nGradWidth = rcGrad.Height(); // vert
else
nGradWidth = rcGrad.Width(); // horz
if (nClipEnd == - 1 ) nClipEnd = nGradWidth;
ASSERT(nGradWidth >= nClipEnd);
ASSERT(m_flStretchGrad >= 1 );
nGradWidth = int (nGradWidth * m_flStretchGrad);
CGradArray arElements;
CalcShiftedGradient(arElements, nShift, nGradWidth, nClipStart, nClipEnd, f256Color ? 236 : - 1 );
int nSteps = arElements.GetSize();
int nBandStart = nClipStart;
int nBandEnd = nClipStart;
// Start filling
CPalette * pOldPal = NULL;
if (f256Color && m_fCreatePalette && GetPalette().GetSafeHandle())
{
pOldPal = pDC -> SelectPalette( & GetPalette(), FALSE);
pDC -> RealizePalette();
}
for ( int i = 0 ; i < nSteps; i ++ , nBandStart = nBandEnd)
{
nBandEnd += arElements[i].length;
COLORREF nColor = arElements[i].color;
if (f256Color)
{
if (pOldPal) // in background draw dithered
nColor |= 0x02000000 ; // (PALETTERGB) without it will be dithering
CBrush br(nColor);
// CDC::FillSolidRect is faster, but it does not handle 8-bit color depth
pDC -> FillRect( & ConvertToReal(rcGrad, nBandStart, nBandEnd), & br);
br.DeleteObject();
}
else
pDC -> FillSolidRect( & ConvertToReal(rcGrad, nBandStart, nBandEnd), nColor);
}
if (pOldPal)
pDC -> SelectPalette(pOldPal, TRUE);
}
CRect CGradient::ConvertToReal(CRect rcDraw, int nBandStart, int nBandEnd)
{
BOOL fReverse = (m_fDirection == TTB || m_fDirection == RTL);
BOOL fVert = (m_fDirection == TTB || m_fDirection == BTT);
CRect rc(rcDraw);
if (fVert)
{
if (nBandEnd == - 1 ) nBandEnd = rcDraw.Height();
rc.top = rcDraw.top +
(fReverse ? nBandStart : (rcDraw.Height() - nBandEnd));
rc.bottom = rc.top + (nBandEnd - nBandStart);
}
else
{
if (nBandEnd == - 1 ) nBandEnd = rcDraw.Width();
rc.left = rcDraw.left +
(fReverse ? (rcDraw.Width() - nBandEnd) : nBandStart);
rc.right = rc.left + (nBandEnd - nBandStart);
}
return rc;
}
void CGradient::CalcShiftedGradient(CGradArray & arElements, int nShift, int nGradWidth,
int nClipStart, int nClipEnd, UINT nMaxColors)
{
// normalize shift
if (nGradWidth == 0 )
nShift = 0 ;
else
{
while (nShift < 0 ) nShift += nGradWidth;
nShift %= nGradWidth;
}
if (nShift == 0 || m_ardwGradColors.GetSize() < 2 )
{
CalcMultiGradient(arElements, nGradWidth, nClipStart, nClipEnd, nMaxColors);
return ;
}
CGradArray arElementsOrig;
CalcMultiGradient(arElementsOrig, nGradWidth, 0 , - 1 , nMaxColors);
int nCount = arElementsOrig.GetSize();
if (nCount < 2 )
{
// no gradient
arElements.Add(CGradElement(m_ardwGradColors[ 0 ], nClipEnd - nClipStart));
return ;
}
// do shift
CGradArray arElementsShift;
int nLength = 0 ;
for ( int i = nCount - 1 ; i >= 0 ; i -- )
{
CGradElement & el = arElementsOrig.ElementAt(i);
if (nLength + el.length > nShift)
{
// Separate
arElementsShift.InsertAt( 0 , CGradElement(el.color, nShift - nLength));
el.length -= nShift - nLength;
break ;
}
else
{
nLength += el.length;
arElementsShift.InsertAt( 0 , el);
arElementsOrig.RemoveAt(i);
if (nLength == nShift)
break ;
}
}
// combine shifted
arElementsShift.Append(arElementsOrig);
// do clip
if (nClipEnd == - 1 ) nClipEnd = nGradWidth;
nCount = arElementsShift.GetSize();
nLength = 0 ;
for ( int i = 0 ; i < nCount; i ++ )
{
CGradElement & el = arElementsShift.ElementAt(i);
if (nLength + el.length <= nClipStart)
{
nLength += el.length;
continue ; // skip before clip start
}
int nStart = max(nLength, nClipStart);
nLength += el.length;
int nEnd = min(nLength, nClipEnd);
arElements.Add(CGradElement(el.color, nEnd - nStart));
if (nLength >= nClipEnd)
break ; // skip after clip end
}
}
void CGradient::CalcMultiGradient(CGradArray & arElements, int nGradWidth,
int nClipStart, int nClipEnd, UINT nMaxColors)
{
if (nClipEnd == - 1 ) nClipEnd = nGradWidth;
int nSteps = m_ardwGradColors.GetSize() - 1 ;
if (nSteps < 0 )
{
ASSERT( 0 ); // at least 1 color should be added
return ;
}
if (nSteps == 0 )
{
arElements.Add(CGradElement(m_ardwGradColors[ 0 ], nClipEnd - nClipStart));
return ;
}
double flBandGradStart = 0 ;
nMaxColors /= nSteps;
for ( int i = 0 ; i < nSteps; i ++ )
{
int nBandGradStart = int (( double )nGradWidth * flBandGradStart / 100 );
flBandGradStart += m_arflGradStretch[i];
int nBandGradEnd = int (( double )nGradWidth * flBandGradStart / 100 );
if (i == nSteps - 1 ) // last step (because of problems with float)
nBandGradEnd = nGradWidth;
if (nBandGradEnd < nClipStart)
continue ; // skip - band before cliping rect
int nBandClipStart = nBandGradStart;
int nBandClipEnd = nBandGradEnd;
if (nBandClipStart < nClipStart)
nBandClipStart = nClipStart;
if (nBandClipEnd > nClipEnd)
nBandClipEnd = nClipEnd;
CalcGradient(arElements, m_ardwGradColors[i], m_ardwGradColors[i + 1 ],
nBandGradEnd - nBandGradStart,
nBandClipStart - nBandGradStart,
nBandClipEnd - nBandGradStart,
nMaxColors);
if (nBandClipEnd == nClipEnd)
break ; // stop filling - next band is out of clipping rect
}
}
void CGradient::CalcGradient(CGradArray & arElements, COLORREF clrStart, COLORREF clrEnd,
int nGradWidth, int nClipStart, int nClipEnd,
UINT nMaxColors)
{
if (nClipEnd == - 1 ) nClipEnd = nGradWidth;
// Split colors to RGB chanels, find chanel with maximum difference
// between the start and end colors. This distance will determine
// number of steps of gradient
int r = (GetRValue(clrEnd) - GetRValue(clrStart));
int g = (GetGValue(clrEnd) - GetGValue(clrStart));
int b = (GetBValue(clrEnd) - GetBValue(clrStart));
UINT nSteps = max(abs(r), max(abs(g), abs(b)));
nSteps = min(nSteps, nMaxColors);
// if number of pixels in gradient less than number of steps -
// use it as numberof steps
UINT nPixels = nGradWidth;
nSteps = min(nPixels, nSteps);
if (nSteps == 0 ) nSteps = 1 ;
float rStep = ( float )r / nSteps;
float gStep = ( float )g / nSteps;
float bStep = ( float )b / nSteps;
r = GetRValue(clrStart);
g = GetGValue(clrStart);
b = GetBValue(clrStart);
float nWidthPerStep = ( float )nGradWidth / nSteps;
CBrush br;
// Start filling
for (UINT i = 0 ; i < nSteps; i ++ )
{
int nFillStart = ( int )(nWidthPerStep * i);
int nFillEnd = ( int )(nWidthPerStep * (i + 1 ));
if (i == nSteps - 1 ) // last step (because of problems with float)
nFillEnd = nGradWidth;
if (nFillEnd < nClipStart)
continue ; // skip - band before cliping rect
// clip it
if (nFillStart < nClipStart)
nFillStart = nClipStart;
if (nFillEnd > nClipEnd)
nFillEnd = nClipEnd;
COLORREF clrFill = RGB(r + ( int )(i * rStep),
g + ( int )(i * gStep),
b + ( int )(i * bStep));
// add band
arElements.Add(CGradElement(clrFill, nFillEnd - nFillStart));
if (nFillEnd >= nClipEnd)
break ; // stop filling if we reach current position
}
}
void CGradient::SetGradientColorsX( int nCount, COLORREF clrFirst, COLORREF clrNext, ...)
{
ASSERT(nCount > 1 );
m_ardwGradColors.SetSize(nCount);
m_ardwGradColors.SetAt( 0 , clrFirst);
m_ardwGradColors.SetAt( 1 , clrNext);
if (nCount > 2 )
{
va_list pArgs;
va_start(pArgs, clrNext);
for ( int i = 2 ; i < nCount; i ++ )
m_ardwGradColors.SetAt(i, va_arg(pArgs, COLORREF));
va_end( pArgs );
}
nCount -- ;
m_arflGradStretch.SetSize(nCount);
for ( int i = 0 ; i < nCount; i ++ )
m_arflGradStretch[i] = ( double ) 100 / nCount;
// remove all dependent objects
m_Pal.DeleteObject();
}
void CGradient::GetGradientColors(COLORREF & clrStart, COLORREF & clrEnd)
{
if (m_ardwGradColors.GetSize() > 0 )
{
clrStart = m_ardwGradColors[ 0 ];
clrEnd = m_ardwGradColors[m_ardwGradColors.GetSize() > 1 ? 1 : 0 ];
}
else
clrStart = clrEnd = CLR_NONE;
}
void CGradient::AddColor(COLORREF clr)
{
m_ardwGradColors.Add(clr);
if (m_ardwGradColors.GetSize() > 1 )
{
double flNew = ( double ) 100 / m_arflGradStretch.GetSize();
m_arflGradStretch.Add(flNew);
NormalizeColorsStretch();
}
}
void CGradient::CreatePalette()
{
if ( ! m_fCreatePalette || m_Pal.GetSafeHandle())
return ;
int nNumColors = 236 ;
CGradArray arElements;
CalcMultiGradient(arElements, nNumColors, 0 , nNumColors, nNumColors);
ASSERT(nNumColors >= arElements.GetSize());
nNumColors = arElements.GetSize();
if ( ! nNumColors)
return ;
LPLOGPALETTE lpPal = (LPLOGPALETTE) new BYTE[ sizeof (LOGPALETTE) +
sizeof (PALETTEENTRY) *
nNumColors];
if ( ! lpPal)
return ;
lpPal -> palVersion = 0x300 ;
lpPal -> palNumEntries = nNumColors;
for ( int i = 0 ; i < nNumColors; i ++ )
{
COLORREF nColor = arElements[i].color;
lpPal -> palPalEntry[i].peRed = GetRValue(nColor);
lpPal -> palPalEntry[i].peGreen = GetGValue(nColor);
lpPal -> palPalEntry[i].peBlue = GetBValue(nColor);
lpPal -> palPalEntry[i].peFlags = 0 ;
}
m_Pal.CreatePalette(lpPal);
delete [](PBYTE)lpPal;
}
void CGradient::SetStretchGradient( float flStretchFactor)
{
ASSERT(flStretchFactor >= 1 );
if (flStretchFactor < 1 )
flStretchFactor = 1 ;
m_flStretchGrad = flStretchFactor;
}
void CGradient::NormalizeColorsStretch()
{
int nCount = m_arflGradStretch.GetSize();
ASSERT(nCount == m_ardwGradColors.GetSize() - 1 );
double flFull = 0 ;
for ( int i = 0 ; i < nCount; i ++ )
flFull += m_arflGradStretch[i];
for ( int i = 0 ; i < nCount; i ++ )
m_arflGradStretch[i] = m_arflGradStretch[i] * 100 / flFull;
}
void CGradient::SetColorsStretch( double flFirst, ...)
{
int nCount = m_ardwGradColors.GetSize() - 1 ;
if (nCount < 1 )
{
ASSERT( 0 ); // before you use this function add all necessary colors
return ;
}
m_arflGradStretch.SetSize(nCount);
m_arflGradStretch.SetAt( 0 , flFirst);
va_list pArgs;
va_start(pArgs, flFirst);
for ( int i = 1 ; i < nCount; i ++ )
m_arflGradStretch.SetAt(i, va_arg(pArgs, double ));
va_end( pArgs );
NormalizeColorsStretch(); // normalize
}
// /
// class CGradient
//
#include " stdafx.h "
#include " Gradient.h "
/ /
// Construction/Destruction
/ /
CGradient::CGradient()
{
m_fCreatePalette = FALSE;
m_fDirection = LTR;
m_flStretchGrad = 1 ;
}
CGradient:: ~ CGradient()
{
}
void CGradient::DrawLinearGradient(CDC * pDC, CRect rcGrad, int nClipStart, int nClipEnd, int nShift)
{
BOOL f256Color = pDC -> GetDeviceCaps(RASTERCAPS) & RC_PALETTE;
if ( ! f256Color && (pDC -> GetDeviceCaps(BITSPIXEL) * pDC -> GetDeviceCaps(PLANES) < 8 ))
{
// for 16 colors no gradient
ASSERT(m_ardwGradColors.GetSize() > 0 );
pDC -> FillSolidRect( & ConvertToReal(rcGrad, nClipStart, nClipEnd), m_ardwGradColors[ 0 ]);
return ;
}
int nGradWidth = 0 ;
if (m_fDirection == TTB || m_fDirection == BTT)
nGradWidth = rcGrad.Height(); // vert
else
nGradWidth = rcGrad.Width(); // horz
if (nClipEnd == - 1 ) nClipEnd = nGradWidth;
ASSERT(nGradWidth >= nClipEnd);
ASSERT(m_flStretchGrad >= 1 );
nGradWidth = int (nGradWidth * m_flStretchGrad);
CGradArray arElements;
CalcShiftedGradient(arElements, nShift, nGradWidth, nClipStart, nClipEnd, f256Color ? 236 : - 1 );
int nSteps = arElements.GetSize();
int nBandStart = nClipStart;
int nBandEnd = nClipStart;
// Start filling
CPalette * pOldPal = NULL;
if (f256Color && m_fCreatePalette && GetPalette().GetSafeHandle())
{
pOldPal = pDC -> SelectPalette( & GetPalette(), FALSE);
pDC -> RealizePalette();
}
for ( int i = 0 ; i < nSteps; i ++ , nBandStart = nBandEnd)
{
nBandEnd += arElements[i].length;
COLORREF nColor = arElements[i].color;
if (f256Color)
{
if (pOldPal) // in background draw dithered
nColor |= 0x02000000 ; // (PALETTERGB) without it will be dithering
CBrush br(nColor);
// CDC::FillSolidRect is faster, but it does not handle 8-bit color depth
pDC -> FillRect( & ConvertToReal(rcGrad, nBandStart, nBandEnd), & br);
br.DeleteObject();
}
else
pDC -> FillSolidRect( & ConvertToReal(rcGrad, nBandStart, nBandEnd), nColor);
}
if (pOldPal)
pDC -> SelectPalette(pOldPal, TRUE);
}
CRect CGradient::ConvertToReal(CRect rcDraw, int nBandStart, int nBandEnd)
{
BOOL fReverse = (m_fDirection == TTB || m_fDirection == RTL);
BOOL fVert = (m_fDirection == TTB || m_fDirection == BTT);
CRect rc(rcDraw);
if (fVert)
{
if (nBandEnd == - 1 ) nBandEnd = rcDraw.Height();
rc.top = rcDraw.top +
(fReverse ? nBandStart : (rcDraw.Height() - nBandEnd));
rc.bottom = rc.top + (nBandEnd - nBandStart);
}
else
{
if (nBandEnd == - 1 ) nBandEnd = rcDraw.Width();
rc.left = rcDraw.left +
(fReverse ? (rcDraw.Width() - nBandEnd) : nBandStart);
rc.right = rc.left + (nBandEnd - nBandStart);
}
return rc;
}
void CGradient::CalcShiftedGradient(CGradArray & arElements, int nShift, int nGradWidth,
int nClipStart, int nClipEnd, UINT nMaxColors)
{
// normalize shift
if (nGradWidth == 0 )
nShift = 0 ;
else
{
while (nShift < 0 ) nShift += nGradWidth;
nShift %= nGradWidth;
}
if (nShift == 0 || m_ardwGradColors.GetSize() < 2 )
{
CalcMultiGradient(arElements, nGradWidth, nClipStart, nClipEnd, nMaxColors);
return ;
}
CGradArray arElementsOrig;
CalcMultiGradient(arElementsOrig, nGradWidth, 0 , - 1 , nMaxColors);
int nCount = arElementsOrig.GetSize();
if (nCount < 2 )
{
// no gradient
arElements.Add(CGradElement(m_ardwGradColors[ 0 ], nClipEnd - nClipStart));
return ;
}
// do shift
CGradArray arElementsShift;
int nLength = 0 ;
for ( int i = nCount - 1 ; i >= 0 ; i -- )
{
CGradElement & el = arElementsOrig.ElementAt(i);
if (nLength + el.length > nShift)
{
// Separate
arElementsShift.InsertAt( 0 , CGradElement(el.color, nShift - nLength));
el.length -= nShift - nLength;
break ;
}
else
{
nLength += el.length;
arElementsShift.InsertAt( 0 , el);
arElementsOrig.RemoveAt(i);
if (nLength == nShift)
break ;
}
}
// combine shifted
arElementsShift.Append(arElementsOrig);
// do clip
if (nClipEnd == - 1 ) nClipEnd = nGradWidth;
nCount = arElementsShift.GetSize();
nLength = 0 ;
for ( int i = 0 ; i < nCount; i ++ )
{
CGradElement & el = arElementsShift.ElementAt(i);
if (nLength + el.length <= nClipStart)
{
nLength += el.length;
continue ; // skip before clip start
}
int nStart = max(nLength, nClipStart);
nLength += el.length;
int nEnd = min(nLength, nClipEnd);
arElements.Add(CGradElement(el.color, nEnd - nStart));
if (nLength >= nClipEnd)
break ; // skip after clip end
}
}
void CGradient::CalcMultiGradient(CGradArray & arElements, int nGradWidth,
int nClipStart, int nClipEnd, UINT nMaxColors)
{
if (nClipEnd == - 1 ) nClipEnd = nGradWidth;
int nSteps = m_ardwGradColors.GetSize() - 1 ;
if (nSteps < 0 )
{
ASSERT( 0 ); // at least 1 color should be added
return ;
}
if (nSteps == 0 )
{
arElements.Add(CGradElement(m_ardwGradColors[ 0 ], nClipEnd - nClipStart));
return ;
}
double flBandGradStart = 0 ;
nMaxColors /= nSteps;
for ( int i = 0 ; i < nSteps; i ++ )
{
int nBandGradStart = int (( double )nGradWidth * flBandGradStart / 100 );
flBandGradStart += m_arflGradStretch[i];
int nBandGradEnd = int (( double )nGradWidth * flBandGradStart / 100 );
if (i == nSteps - 1 ) // last step (because of problems with float)
nBandGradEnd = nGradWidth;
if (nBandGradEnd < nClipStart)
continue ; // skip - band before cliping rect
int nBandClipStart = nBandGradStart;
int nBandClipEnd = nBandGradEnd;
if (nBandClipStart < nClipStart)
nBandClipStart = nClipStart;
if (nBandClipEnd > nClipEnd)
nBandClipEnd = nClipEnd;
CalcGradient(arElements, m_ardwGradColors[i], m_ardwGradColors[i + 1 ],
nBandGradEnd - nBandGradStart,
nBandClipStart - nBandGradStart,
nBandClipEnd - nBandGradStart,
nMaxColors);
if (nBandClipEnd == nClipEnd)
break ; // stop filling - next band is out of clipping rect
}
}
void CGradient::CalcGradient(CGradArray & arElements, COLORREF clrStart, COLORREF clrEnd,
int nGradWidth, int nClipStart, int nClipEnd,
UINT nMaxColors)
{
if (nClipEnd == - 1 ) nClipEnd = nGradWidth;
// Split colors to RGB chanels, find chanel with maximum difference
// between the start and end colors. This distance will determine
// number of steps of gradient
int r = (GetRValue(clrEnd) - GetRValue(clrStart));
int g = (GetGValue(clrEnd) - GetGValue(clrStart));
int b = (GetBValue(clrEnd) - GetBValue(clrStart));
UINT nSteps = max(abs(r), max(abs(g), abs(b)));
nSteps = min(nSteps, nMaxColors);
// if number of pixels in gradient less than number of steps -
// use it as numberof steps
UINT nPixels = nGradWidth;
nSteps = min(nPixels, nSteps);
if (nSteps == 0 ) nSteps = 1 ;
float rStep = ( float )r / nSteps;
float gStep = ( float )g / nSteps;
float bStep = ( float )b / nSteps;
r = GetRValue(clrStart);
g = GetGValue(clrStart);
b = GetBValue(clrStart);
float nWidthPerStep = ( float )nGradWidth / nSteps;
CBrush br;
// Start filling
for (UINT i = 0 ; i < nSteps; i ++ )
{
int nFillStart = ( int )(nWidthPerStep * i);
int nFillEnd = ( int )(nWidthPerStep * (i + 1 ));
if (i == nSteps - 1 ) // last step (because of problems with float)
nFillEnd = nGradWidth;
if (nFillEnd < nClipStart)
continue ; // skip - band before cliping rect
// clip it
if (nFillStart < nClipStart)
nFillStart = nClipStart;
if (nFillEnd > nClipEnd)
nFillEnd = nClipEnd;
COLORREF clrFill = RGB(r + ( int )(i * rStep),
g + ( int )(i * gStep),
b + ( int )(i * bStep));
// add band
arElements.Add(CGradElement(clrFill, nFillEnd - nFillStart));
if (nFillEnd >= nClipEnd)
break ; // stop filling if we reach current position
}
}
void CGradient::SetGradientColorsX( int nCount, COLORREF clrFirst, COLORREF clrNext, ...)
{
ASSERT(nCount > 1 );
m_ardwGradColors.SetSize(nCount);
m_ardwGradColors.SetAt( 0 , clrFirst);
m_ardwGradColors.SetAt( 1 , clrNext);
if (nCount > 2 )
{
va_list pArgs;
va_start(pArgs, clrNext);
for ( int i = 2 ; i < nCount; i ++ )
m_ardwGradColors.SetAt(i, va_arg(pArgs, COLORREF));
va_end( pArgs );
}
nCount -- ;
m_arflGradStretch.SetSize(nCount);
for ( int i = 0 ; i < nCount; i ++ )
m_arflGradStretch[i] = ( double ) 100 / nCount;
// remove all dependent objects
m_Pal.DeleteObject();
}
void CGradient::GetGradientColors(COLORREF & clrStart, COLORREF & clrEnd)
{
if (m_ardwGradColors.GetSize() > 0 )
{
clrStart = m_ardwGradColors[ 0 ];
clrEnd = m_ardwGradColors[m_ardwGradColors.GetSize() > 1 ? 1 : 0 ];
}
else
clrStart = clrEnd = CLR_NONE;
}
void CGradient::AddColor(COLORREF clr)
{
m_ardwGradColors.Add(clr);
if (m_ardwGradColors.GetSize() > 1 )
{
double flNew = ( double ) 100 / m_arflGradStretch.GetSize();
m_arflGradStretch.Add(flNew);
NormalizeColorsStretch();
}
}
void CGradient::CreatePalette()
{
if ( ! m_fCreatePalette || m_Pal.GetSafeHandle())
return ;
int nNumColors = 236 ;
CGradArray arElements;
CalcMultiGradient(arElements, nNumColors, 0 , nNumColors, nNumColors);
ASSERT(nNumColors >= arElements.GetSize());
nNumColors = arElements.GetSize();
if ( ! nNumColors)
return ;
LPLOGPALETTE lpPal = (LPLOGPALETTE) new BYTE[ sizeof (LOGPALETTE) +
sizeof (PALETTEENTRY) *
nNumColors];
if ( ! lpPal)
return ;
lpPal -> palVersion = 0x300 ;
lpPal -> palNumEntries = nNumColors;
for ( int i = 0 ; i < nNumColors; i ++ )
{
COLORREF nColor = arElements[i].color;
lpPal -> palPalEntry[i].peRed = GetRValue(nColor);
lpPal -> palPalEntry[i].peGreen = GetGValue(nColor);
lpPal -> palPalEntry[i].peBlue = GetBValue(nColor);
lpPal -> palPalEntry[i].peFlags = 0 ;
}
m_Pal.CreatePalette(lpPal);
delete [](PBYTE)lpPal;
}
void CGradient::SetStretchGradient( float flStretchFactor)
{
ASSERT(flStretchFactor >= 1 );
if (flStretchFactor < 1 )
flStretchFactor = 1 ;
m_flStretchGrad = flStretchFactor;
}
void CGradient::NormalizeColorsStretch()
{
int nCount = m_arflGradStretch.GetSize();
ASSERT(nCount == m_ardwGradColors.GetSize() - 1 );
double flFull = 0 ;
for ( int i = 0 ; i < nCount; i ++ )
flFull += m_arflGradStretch[i];
for ( int i = 0 ; i < nCount; i ++ )
m_arflGradStretch[i] = m_arflGradStretch[i] * 100 / flFull;
}
void CGradient::SetColorsStretch( double flFirst, ...)
{
int nCount = m_ardwGradColors.GetSize() - 1 ;
if (nCount < 1 )
{
ASSERT( 0 ); // before you use this function add all necessary colors
return ;
}
m_arflGradStretch.SetSize(nCount);
m_arflGradStretch.SetAt( 0 , flFirst);
va_list pArgs;
va_start(pArgs, flFirst);
for ( int i = 1 ; i < nCount; i ++ )
m_arflGradStretch.SetAt(i, va_arg(pArgs, double ));
va_end( pArgs );
NormalizeColorsStretch(); // normalize
}