文章来源:http://blog.csdn.net/qiuchengw/article/details/6324791
这是的确重复造轮子,但是造了一个更适合自己使用的轮子.
项目的需要,要实现一个画图的功能,在网上搜了N久的代码,可惜那些代码都太"粗糙"了,没有符合自己要求的,所以只好自己写了一个 .
这是效果图(图像可填充背景色,也可只有边界色,也可两者都有):
这是代码(由于是自己项目的一部分,就不传工程了):
QShap 是基类, 继承此类,可实现自己的图形.本代码只实现了自己需要的一些图形,line/curve/ellipse/rectangle
// !!!!注意
// 此份画图的代码只是为了 "看起来像" ,图形大小不是精确的(会有1到2个像素的大小偏差):
.h
#pragma once
#include "afxcolorbutton.h"
#include "ResLoader.h"
#include "afxlistctrl.h"
#include "qstatic.h"
// !!!!注意
// 此份画图的代码只是为了 "看起来像" ,图形大小不是精确的(会有1到2个像素的大小偏差)
const CPoint POINT_NULL = CPoint(-1,-1);
const CRect RECT_NULL = CRect(-1,-1,-1,-1);
const CRect RECT_EMPTY = CRect(0,0,0,0);
enum ENUM_SHAPE_TYPE
{
SHAPE_NONE = 1,
SHAPE_RECT,
SHAPE_ELLIPSE,
SHAPE_CURVE,
SHAPE_LINE,
SHAPE_ICON,
MAX_SHAPE_VALUE,
};
// Shape 及其派生类都是用于CPaintPane的专用类.
class QShape
{
public:
QShape(ENUM_SHAPE_TYPE eShape);
virtual ~QShape(){}
virtual void Draw(CDC *pDC) = 0;
virtual CRect GetRect()const;
COLORREF GetBorderColor()const { return m_crBoder; };
COLORREF GetFillColor()const { return m_crFill; }
void SetBorderColor(COLORREF cr=COLOR_BLACK) { m_crBoder = cr; }
void SetFillColor(COLORREF cr=COLOR_NONE) { m_crFill = cr; }
ENUM_SHAPE_TYPE GetShape()const { return m_ShapeType; }
// 在鼠标按下的时候调用SetStartPoint,
// 移动的调用UpdateEndPoint
// 弹起的时候调用SetEndPoint
virtual void SetStartPoint(const CPoint&point) {ASSERT(POINT_NULL != point); m_pt1 = point; };
virtual void SetEndPoint(const CPoint&point){ASSERT(POINT_NULL != point); m_pt2 = point; };
virtual void UpdateEndPoint(const CPoint&point){ SetEndPoint(point); };
public:
CPoint m_pt1,m_pt2;
COLORREF m_crBoder;
COLORREF m_crFill;
private:
ENUM_SHAPE_TYPE m_ShapeType;
};
// the shape factory
QShape* CreateShape( ENUM_SHAPE_TYPE eShape ,LPARAM lParam = 0);
class QRectangle : public QShape
{
public:
QRectangle():QShape(SHAPE_RECT){ }
virtual void Draw(CDC *pDC);
};
class QEllipse : public QShape
{
public:
QEllipse():QShape(SHAPE_ELLIPSE){}
virtual void Draw(CDC *pDC);
};
class QCurve : public QShape
{
public:
QCurve();
virtual void Draw(CDC *pDC);
// 在curve中,pt1,和pt2标志曲线所占据的矩形,不是开始和结束点
virtual void UpdateEndPoint(const CPoint&point);
virtual void SetEndPoint(const CPoint&point) { UpdateEndPoint(point); };
virtual CRect GetRect()const;
private:
typedef CArray<CPoint,CPoint> CurvePoint;
CurvePoint m_Points;
};
class QLine : public QShape
{
public :
QLine():QShape(SHAPE_LINE) { }
virtual void Draw(CDC *pDC);
};
class QIcon : public QShape
{
public:
QIcon(HICON hIcon);
virtual void Draw(CDC *pDC);
virtual CRect GetRect()const ;
virtual void SetEndPoint(const CPoint&point){};
virtual void UpdateEndPoint(const CPoint&point){ };
private:
HICON m_hIcon;
};
// CPainterPane
class CPainterPane : public CWnd
{
typedef std::vector<QShape *> SHAPES;
typedef SHAPES::iterator ShapeItr;
DECLARE_DYNAMIC(CPainterPane)
public:
CPainterPane();
virtual ~CPainterPane();
BOOL SetMainBk( LPCTSTR pszDll,UINT nID,LPCTSTR pszType,COLORREF crTrans=COLOR_BLACK );
BOOL InitPane(CRect rect,CWnd *pParent,UINT nID);
void SetDrawing( ENUM_SHAPE_TYPE eShape,LPARAM lParam =0);
void SetShapeColor(COLORREF crBorder,COLORREF crFill=COLOR_NONE);
// 清除当前所画的所有内容,不包括背景
void ClearDrawed();
// 撤销nTime次的操作
void Undo(int nTimes = 1);
int GetShapesCount() { return m_Shapes.size(); }
protected:
void DrawClient(CDC *pDC,const CRect &rect);
// 在内存dc中画
void DrawMainBack(CDC* pDC , const CRect &rect );
void DrawShapes( CDC* pDC ,const CRect &rect );
void CopyDC(CDC *pDst,CDC *pSrc,const CRect &rect);
// 将pDCSrc 更新显示到界面
void CopyDCToSurface(CDC *pDCSrc,const CRect&rect);
void Reset();
BOOL _IsDrawing()const;
BOOL _BeginDrawing( CPoint ptStart );
void _UpdateDrawing(CPoint ptEnd);
void _EndDrawing(CPoint ptEnd);
protected:
DECLARE_MESSAGE_MAP()
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnPaint();
class MouseTrack
{
protected:
typedef std::vector<CPoint> LstTrack;
typedef LstTrack::iterator LstTrackItr;
LstTrack m_LstTrack; // 第一个点是鼠标按下的位置,最后一个点是最后一次鼠标移动到的位置
BOOL m_bDown; // 是否已经按下? 需要调用Down设置
public:
MouseTrack() { m_bDown = FALSE; }
void Down(CPoint pt) { Reset(); m_LstTrack.push_back(pt); m_bDown = TRUE; }
void Move(CPoint pt) { ASSERT(m_bDown); m_LstTrack.push_back(pt); }
void Up(CPoint pt) { m_LstTrack.push_back(pt); m_bDown = FALSE; }
void Reset() { m_bDown = FALSE; m_LstTrack.clear(); }
BOOL IsDown()const { return m_bDown; }
CPoint PointDown()const
{
if (m_LstTrack.size() > 0)
return m_LstTrack[0];
return POINT_NULL;
}
// idx 既可以是正数索引,也可以是负数索引,不能为0
// -1 为最后一次的矩形 -2 倒数第二次
// 1 第一次, 2 第二次
CPoint PointX(int idx=-1)const
{
LstTrack::size_type n = m_LstTrack.size();
idx = (idx < 0) ? n + idx : idx;
if (n < 2 || idx <= 0 || idx >= n)
return POINT_NULL;
return m_LstTrack[idx];
}
// 获取鼠标按下和第idx次移动的矩形,
// idx 既可以是正数索引,也可以是负数索引,不能为0
// -1 为最后一次的矩形 -2 倒数第二次
// 1 第一次, 2 第二次
CRect GetRect(int idx = -1)const
{
CPoint pt2 = PointX(idx);
if (POINT_NULL == pt2)
return RECT_NULL;
CRect rect(m_LstTrack[0],pt2);
rect.NormalizeRect();
return rect;
}
};
MouseTrack m_MouseTrack;
SHAPES m_Shapes;
private:
HBITMAP m_hbmpMainBack;
CBitmap m_bmpMemBack;
CDC m_dcMemBack;
CBitmap m_bmpMemDrawed;
CDC m_dcMemDrawed;
CBitmap m_bmpMemDrawing;
CDC m_dcMemDrawing;
HCURSOR m_hCurCross,m_hCurArrow;
ENUM_SHAPE_TYPE m_eDrawing; //
LPARAM m_lShapeParam;
QShape* m_pShapeDrawing; // 当前正在画的图形
COLORREF m_crShapeBack,m_crShapeBorder;
};
// CPainterList
struct _SHAPE_ITEMS
{
int iImg;
CString sItem;
DWORD dwData;
};
enum
{
ACTION_CLEAR = MAX_SHAPE_VALUE + 1,
ACTION_UNDO,
ACTION_SAVE,
};
class CPainterList : public CMFCListCtrl
{
DECLARE_DYNAMIC(CPainterList)
public:
CPainterList();
virtual ~CPainterList();
void SetAssoicatePainter(CPainterPane *pPainter) { m_pPainter = pPainter; };
BOOL Init( UINT nImgeList ,COLORREF crMask);
void AddButton(LPCTSTR sItem,int iImage,DWORD dwData);
void AddButton(const _SHAPE_ITEMS *pItem);
protected:
DECLARE_MESSAGE_MAP()
afx_msg void OnNMClick(NMHDR *pNMHDR, LRESULT *pResult);
afx_msg void OnLvnItemchanging(NMHDR *pNMHDR, LRESULT *pResult);
virtual COLORREF OnGetCellTextColor(int iRow, int iCol);
virtual COLORREF OnGetCellBkColor(int iRow, int iCol);
virtual void PreSubclassWindow();
BOOL IsSelected(int iRow);
private:
CPainterPane* m_pPainter;
CImageList m_ImageList;
};
// CPainterDlg 对话框
class CPainterDlg : public CDialogEx
{
DECLARE_DYNAMIC(CPainterDlg)
public:
CPainterDlg(CWnd* pParent = NULL); // 标准构造函数
virtual ~CPainterDlg();
// 对话框数据
enum { IDD = IDD_PAINTER };
void SetImageParams( LPCTSTR pszDll,LPCTSTR pszType,UINT nImgID ,COLORREF crTrans);
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
virtual BOOL OnInitDialog();
afx_msg void OnColorChanged();
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
enum
{
IDC_PAINTPANE_COLORBAR = 111,
};
static const _SHAPE_ITEMS scm_ShapeItems[];
private:
CMFCColorBar m_ColorBar;
CPainterPane m_Painter;
CString m_sDll;
CString m_sType;
UINT m_nImgeID;
COLORREF m_crTrans;
CPainterList m_ListBtns;
QGroupBox m_Box1;
CImageList m_ImageList;
QGroupBox m_Box2;
};
.cpp
// PainterDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "QAAS.h"
#include "PainterDlg.h"
#include "afxdialogex.h"
QShape* CreateShape( ENUM_SHAPE_TYPE eShape ,LPARAM lParam)
{
if (SHAPE_NONE == eShape)
{
ASSERT(FALSE);
return NULL;
}
QShape * pShape = NULL;
switch (eShape)
{
case SHAPE_RECT:
{
pShape = new QRectangle();
break;
}
case SHAPE_ELLIPSE:
{
pShape = new QEllipse();
break;
}
case SHAPE_LINE:
{
pShape = new QLine();
break;
}
case SHAPE_CURVE:
{
pShape = new QCurve();
break;
}
case SHAPE_ICON:
{
pShape = new QIcon((HICON)lParam);
break;
}
default:
{
ASSERT(FALSE);
pShape = NULL;
break;
}
}
return pShape;
}
QShape::QShape( ENUM_SHAPE_TYPE eShape )
:m_ShapeType(eShape)
{
m_crBoder = m_crFill = COLOR_NONE;
m_pt1 = m_pt2 = POINT_NULL;
}
CRect QShape::GetRect() const
{
CRect rect(m_pt1,m_pt2);
rect.NormalizeRect();
return rect;
}
void QRectangle::Draw( CDC *pDC )
{
ASSERT(pDC != NULL);
CRect rect = GetRect();
if (COLOR_NONE != m_crFill)
{
pDC->FillSolidRect(&rect,m_crFill);
}
pDC->FrameRect(&rect,&CBrush(m_crBoder));
}
void QEllipse::Draw( CDC *pDC )
{
ASSERT(pDC != NULL);
using namespace Gdiplus;
CRect rect = GetRect();
rect.DeflateRect(1,1); // 防止切边问题,注释此行可以重现重现问题
Gdiplus::Graphics gra(pDC->GetSafeHdc());
gra.SetSmoothingMode(SmoothingModeAntiAlias); // 开启反锯齿效果
if (COLOR_NONE != m_crFill)
{
gra.FillEllipse(&SolidBrush(DwordToColor(m_crFill)),(RectF)QRect(rect));
}
Pen pen(DwordToColor(m_crBoder));
gra.DrawEllipse(&pen,(RectF)QRect(rect));
}
// 曲线实现比较特殊,要特别处理
QCurve::QCurve():QShape(SHAPE_CURVE)
{
// 在curve中,pt1,和pt2标志曲线所占据的矩形,不是开始和结束点
m_pt1.x = m_pt1.y = INT_MAX;
m_pt2.x = m_pt2.y = -1;
}
CRect QCurve::GetRect()const
{
// 还没有添加点
if ((m_pt1.x == INT_MAX) || (m_pt2.x == -1))
{
return CRect(0,0,0,0);
}
CRect rect = QShape::GetRect();
rect.InflateRect(1,1); // 为防止切边
return rect;
}
void QCurve::Draw( CDC *pDC )
{
int nCount = m_Points.GetCount();
if (nCount < 2)
return;
CPen pen(PS_SOLID,1,m_crBoder);
CPen *pOldPen = (CPen*)pDC->SelectObject(&pen);
pDC->MoveTo(m_Points.GetAt(0));
for (int i = 1; i < nCount; i++)
{
pDC->LineTo(m_Points[i]);
}
pDC->SelectObject(pOldPen);
}
void QCurve::UpdateEndPoint( const CPoint&point )
{
// 在curve中,pt1,和pt2标志曲线所占据的矩形,不是开始和结束点
m_pt1.x = min(m_pt1.x,point.x);
m_pt1.y = min(m_pt1.y,point.y);
m_pt2.x = max(m_pt2.x,point.x);
m_pt2.y = max(m_pt2.y,point.y);
m_Points.Add(point);
}
void QLine::Draw( CDC *pDC )
{
if (POINT_NULL == m_pt1 || POINT_NULL == m_pt2)
return;
CAADraw aa;
aa.DrawLine(pDC->GetSafeHdc(),m_pt1.x,m_pt1.y,m_pt2.x,m_pt2.y,m_crBoder);
}
QIcon::QIcon( HICON hIcon )
:QShape(SHAPE_ICON)
{
ASSERT(hIcon != NULL);
m_hIcon = hIcon;
}
void QIcon::Draw( CDC *pDC )
{
if (NULL != m_hIcon)
{
pDC->DrawIcon(m_pt1,m_hIcon);
}
}
CRect QIcon::GetRect() const
{
if (NULL != m_hIcon)
{
CRect rect;
ICONINFO ii;
if (GetIconInfo(m_hIcon,&ii))
{
return CRect(m_pt1,CPoint(ii.xHotspot * 2 + m_pt1.x, ii.yHotspot * 2 + m_pt1.y));
}
}
return RECT_EMPTY;
}
//////////////////////////////////////////////////////////////////
// brief : 2011/03/25
// copyright: qiuchengw @ 2011
//////////////////////////////////////////////////////////////////
// CPainterPane
IMPLEMENT_DYNAMIC(CPainterPane, CWnd)
CPainterPane::CPainterPane()
{
m_crShapeBack = COLOR_NONE;
m_crShapeBorder = COLOR_BLACK;
m_eDrawing = SHAPE_NONE;
m_pShapeDrawing = NULL;
m_hCurCross = NULL;
m_hCurArrow = NULL;
}
CPainterPane::~CPainterPane()
{
Reset();
DeleteObject(m_hCurCross);
DeleteObject(m_hCurArrow);
}
void CPainterPane::Reset()
{
if (m_dcMemBack.m_hDC)
m_dcMemBack.DeleteDC();
if (m_dcMemDrawed.m_hDC)
m_dcMemDrawed.DeleteDC();
if (m_dcMemDrawing.m_hDC)
m_dcMemDrawing.DeleteDC();
if (m_bmpMemDrawed.m_hObject)
m_bmpMemDrawed.DeleteObject();
if (m_bmpMemBack.m_hObject)
m_bmpMemBack.DeleteObject();
if (m_bmpMemDrawing.m_hObject)
m_bmpMemDrawing.DeleteObject();
for (ShapeItr itr = _BeginItr(m_Shapes); itr != _EndItr(m_Shapes); ++itr)
{
delete *itr;
}
m_Shapes.clear();
m_eDrawing = SHAPE_NONE;
m_pShapeDrawing = NULL;
m_crShapeBack = COLOR_NONE;
m_crShapeBorder = COLOR_BLACK;
}
BEGIN_MESSAGE_MAP(CPainterPane, CWnd)
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_ERASEBKGND()
ON_WM_PAINT()
END_MESSAGE_MAP()
// CPainterPane 消息处理程序
void CPainterPane::OnLButtonDown(UINT nFlags, CPoint point)
{
if (SHAPE_NONE != m_eDrawing)
{
if (!_BeginDrawing(point))
{
ASSERT(FALSE);
// SetDrawing(SHAPE_NONE);
}
}
}
void CPainterPane::OnLButtonUp(UINT nFlags, CPoint point)
{
if (_IsDrawing())
{
_EndDrawing(point);
}
}
void CPainterPane::OnMouseMove(UINT nFlags, CPoint point)
{
SetCursor((SHAPE_NONE != m_eDrawing) ? m_hCurCross : m_hCurArrow);
if (_IsDrawing())
{
_UpdateDrawing(point);
}
}
BOOL CPainterPane::_BeginDrawing( CPoint ptStart )
{
ASSERT(SHAPE_NONE != m_eDrawing);
CRect rcClip;
GetWindowRect(&rcClip);
// 矩形各边界减小1为了避免当鼠标移动到此窗体的边框上
// 然后释放鼠标按钮就接收不到WM_LBUTTONUP消息的情况
rcClip.DeflateRect(1,1);
if ( ClipCursor(&rcClip))
{
if (NULL != (m_pShapeDrawing = CreateShape(m_eDrawing,m_lShapeParam)))
{
m_pShapeDrawing->SetStartPoint(ptStart);
m_pShapeDrawing->SetBorderColor(m_crShapeBorder);
m_pShapeDrawing->SetFillColor(m_crShapeBack);
m_MouseTrack.Down(ptStart);
return TRUE;
}
}
// 新创建响应的图形
ClipCursor(NULL);
return TRUE;
}
void CPainterPane::_UpdateDrawing( CPoint ptMove )
{
ASSERT(m_pShapeDrawing != NULL);
// 鼠标按下到上次移动的矩形位置
CRect rcLast = m_MouseTrack.GetRect();
// 鼠标按下到此次移动到的矩形位置
m_MouseTrack.Move(ptMove);
// 设置图形的矩形为当前的移动的位置
m_pShapeDrawing->UpdateEndPoint(ptMove);
// 当前图像的矩形
CRect rcDrawing = m_pShapeDrawing->GetRect();
// 取两个矩形的并集进行更新,它们的并集就是当前的脏矩形
if (rcDrawing.UnionRect(&rcDrawing,&rcLast))
{
// 边界增大一点是为了不使图形切边
rcDrawing.InflateRect(1,1);
// 先拷贝需要更新区域的已画好的图形到正在画的区域中
CopyDC(&m_dcMemDrawing,&m_dcMemDrawed,rcDrawing);
// 画图形
m_pShapeDrawing->Draw(&m_dcMemDrawing);
// 更新到显示dc,
// 只有当鼠标左键弹起,标志这个图形画好的时候才能更新到m_dcMemDrawed 上
CopyDCToSurface(&m_dcMemDrawing,rcDrawing);
}
}
void CPainterPane::CopyDCToSurface(CDC *pDC,const CRect&rect)
{
CDC *pWndDC = GetDC();
if (pDC->GetSafeHdc() != pWndDC->GetSafeHdc())
{
CopyDC(pWndDC,pDC,rect);
}
ReleaseDC(pWndDC);
}
void CPainterPane::_EndDrawing( CPoint ptEnd )
{
// 画图形已经完成了.
ClipCursor(NULL);
_UpdateDrawing(ptEnd);
// 更新图像最后的坐标
m_pShapeDrawing->SetEndPoint(ptEnd);
// 此处或许会有一个像素的偏差,因为没有再次调用_UpdateDrawing(ptEnd);
m_MouseTrack.Up(ptEnd);
CRect rect = m_pShapeDrawing->GetRect();
// 保存所画的图像到列表记录
m_Shapes.push_back(m_pShapeDrawing);
// 所画的图像更新到已画的图的内存dc中
CopyDC(&m_dcMemDrawed,&m_dcMemDrawing,rect);
// 将更新好的图像显示出来
CopyDCToSurface(&m_dcMemDrawed,rect);
}
BOOL CPainterPane::InitPane( CRect rect,CWnd *pParent,UINT nID )
{
m_hCurCross = AfxGetApp()->LoadStandardCursor(IDC_CROSS);
m_hCurArrow = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
LPCTSTR pszClass = AfxRegisterWndClass(CS_VREDRAW|CS_HREDRAW,m_hCurArrow);
if (!CWnd::Create(pszClass,L"",WS_VISIBLE|WS_CHILD|WS_BORDER,rect,pParent,nID))
return FALSE;
return TRUE;
}
BOOL CPainterPane::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
void CPainterPane::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect rect;
dc.GetClipBox(&rect);
DrawClient(&dc,rect);
}
void CPainterPane::DrawClient( CDC *pDC,const CRect &rect )
{
ASSERT(pDC != NULL);
// 使用背景清除需要更新的区域
// DrawMainBack(&m_dcMemDrawed,rect);
// DrawShapes(&m_dcMemDrawed,rect);
// 从内存中拷贝到显示
CopyDC(pDC,&m_dcMemDrawed,rect);
}
void CPainterPane::DrawShapes( CDC* pDC ,const CRect &rect )
{
CRect rc;
for (ShapeItr itr = _BeginItr(m_Shapes); itr != _EndItr(m_Shapes); ++itr)
{
rc = rect;
if (rc.IntersectRect(&rc,&rect))
{
(*itr)->Draw(pDC);
}
}
}
void CPainterPane::DrawMainBack(CDC* pDC , const CRect &rect )
{
// 使用背景清除pDC
CopyDC(pDC,&m_dcMemBack,rect);
}
BOOL CPainterPane::SetMainBk( LPCTSTR pszDll,UINT nID,LPCTSTR pszType,COLORREF crTrans )
{
Reset();
Gdiplus::Image *pImg = NULL;
if (CResLoader::LoadFromDll(pszDll,pszType,nID,pImg))
{
CDC *pDC = GetDC();
m_dcMemBack.CreateCompatibleDC(pDC);
m_bmpMemBack.CreateCompatibleBitmap(pDC,pImg->GetWidth(),pImg->GetHeight());
m_dcMemBack.SelectObject(&m_bmpMemBack);
m_dcMemDrawed.CreateCompatibleDC(pDC);
m_bmpMemDrawed.CreateCompatibleBitmap(pDC,pImg->GetWidth(),pImg->GetHeight());
m_dcMemDrawed.SelectObject(&m_bmpMemDrawed);
m_dcMemDrawing.CreateCompatibleDC(pDC);
m_bmpMemDrawing.CreateCompatibleBitmap(pDC,pImg->GetWidth(),pImg->GetHeight());
m_dcMemDrawing.SelectObject(&m_bmpMemDrawing);
ImageAttributes imgati;
if (COLOR_NONE != crTrans)
{
imgati.SetColorKey(DwordToColor(crTrans),DwordToColor(crTrans));
}
Gdiplus::Graphics(m_dcMemBack.m_hDC).DrawImage(pImg,QRect(0,0,pImg->GetWidth(),pImg->GetHeight()),
0,0,pImg->GetWidth(),pImg->GetHeight(),UnitPixel,&imgati);
CopyDC(&m_dcMemDrawed,&m_dcMemBack,RECT_EMPTY);
CopyDC(&m_dcMemDrawing,&m_dcMemBack,RECT_EMPTY);
CopyDC(pDC,&m_dcMemBack,RECT_EMPTY);
ReleaseDC(pDC);
return TRUE;
}
return FALSE;
}
void CPainterPane::CopyDC( CDC *pDst,CDC *pSrc,const CRect &rect )
{
ASSERT(pDst != NULL && pSrc != NULL);
if (pDst->GetSafeHdc() != NULL && pSrc->GetSafeHdc() != NULL)
{
CRect rc = rect;
if (rc.Width() == 0 || rc.Height() == 0)
{
rc.left = rc.top = 0;
rc.bottom = rc.right = 1024; // 只是假设背景图像不会大于 1024 * 1024
}
pDst->BitBlt(rc.left,rc.top,rc.Width(),rc.Height(),pSrc,rc.left,rc.top,SRCCOPY);
}
}
void CPainterPane::SetDrawing( ENUM_SHAPE_TYPE eShape,LPARAM lParam )
{
ASSERT(!_IsDrawing());
m_eDrawing = eShape;
m_lShapeParam = lParam;
}
BOOL CPainterPane::_IsDrawing() const
{
/*
#ifdef _DEBUG
if (m_pShapeDrawing != NULL)
{
ASSERT(m_pShapeDrawing->GetShape() == m_eDrawing);
return TRUE;
}
return FALSE;
#else
return (m_pShapeDrawing != NULL);
#endif
*/
return (m_eDrawing != SHAPE_NONE) && (m_MouseTrack.IsDown());
}
void CPainterPane::SetShapeColor( COLORREF crBorder,COLORREF crFill/*=COLOR_NONE*/ )
{
m_crShapeBorder = crBorder;
m_crShapeBack = crFill;
}
void CPainterPane::ClearDrawed()
{
if (GetShapesCount() > 0)
{
for (ShapeItr itr = _BeginItr(m_Shapes); itr != _EndItr(m_Shapes); ++itr)
{ // 将所有的已画的图形都删掉
delete *itr;
}
m_Shapes.clear();
// 使用主背景清除已画的图形
CopyDC(&m_dcMemDrawed,&m_dcMemBack,RECT_EMPTY);
CopyDC(&m_dcMemDrawing,&m_dcMemBack,RECT_EMPTY);
// 更新到显示
Invalidate(TRUE);
}
ASSERT(GetShapesCount() == 0);
}
void CPainterPane::Undo( int nTimes /*= 1*/ )
{
ASSERT(nTimes > 0);
if (GetShapesCount() > 0 )
{
CRect rcUpdate = RECT_EMPTY; // 所要清除的图像的区域并集,最后要更新
for (SHAPES::size_type i = GetShapesCount() - 1 ; i >= 0 && nTimes > 0; --i,--nTimes)
{
rcUpdate.UnionRect(&rcUpdate,&(m_Shapes[i]->GetRect()));
delete m_Shapes[i];
m_Shapes.pop_back();
}
// 更新已清除的图形的并集区域
rcUpdate.InflateRect(1,1);
DrawMainBack(&m_dcMemDrawed,rcUpdate);
DrawShapes(&m_dcMemDrawed,rcUpdate);
InvalidateRect(&rcUpdate,FALSE);
}
}
//////////////////////////////////////////////////////////////////
// brief : 2011/03/24
// copyright: qiuchengw @ 2011
//////////////////////////////////////////////////////////////////
// CPainterDlg 对话框
const _SHAPE_ITEMS CPainterDlg::scm_ShapeItems[] =
{
{ 0, L"直线", SHAPE_LINE },
{ 1, L"矩形", SHAPE_RECT },
{ 2, L"椭圆", SHAPE_ELLIPSE },
{ 3, L"曲线", SHAPE_CURVE },
{ 4, L"撤销上一步",ACTION_UNDO },
{ 5, L"清除所有", ACTION_CLEAR },
{ 6, L"保存", ACTION_SAVE },
};
IMPLEMENT_DYNAMIC(CPainterDlg, CDialogEx)
CPainterDlg::CPainterDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CPainterDlg::IDD, pParent)
{
}
CPainterDlg::~CPainterDlg()
{
}
void CPainterDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST_BTNS, m_ListBtns);
DDX_Control(pDX, IDC_BOX1, m_Box1);
DDX_Control(pDX, IDC_BOX2, m_Box2);
}
BEGIN_MESSAGE_MAP(CPainterDlg, CDialogEx)
ON_BN_CLICKED(IDC_PAINTPANE_COLORBAR, &CPainterDlg::OnColorChanged)
ON_WM_PAINT()
END_MESSAGE_MAP()
BOOL CPainterDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
SetBackgroundColor(RGB(240,240,240));
CRect rect = GetDlgItemRect(IDC_RECT_COLORBAR,this);
m_ColorBar.CreateControl(this,rect,IDC_PAINTPANE_COLORBAR,5);
m_ColorBar.ShowWindow(SW_SHOW);
m_ColorBar.SetColor(RGB(0,0,0));
m_Box1.SetHeaderHeight(1);
m_Box1.SetBorderColor(RGB(130,135,144));
m_Box2.SetHeaderHeight(1);
m_Box2.SetBorderColor(RGB(130,135,144));
rect = GetDlgItemRect(IDC_RECT_PAINTPANE,this);
if (!m_Painter.InitPane(rect,this,112))
return FALSE;
m_Painter.ShowWindow(SW_SHOW);
m_Painter.SetMainBk(m_sDll,m_nImgeID,m_sType,m_crTrans);
m_ListBtns.Init(IDB_PAINTER,RGB(239,239,239));
for (int i = 0; i < _countof(scm_ShapeItems); ++i)
{
m_ListBtns.AddButton(&scm_ShapeItems[i]);
}
m_ListBtns.SetAssoicatePainter(&m_Painter);
return TRUE;
}
void CPainterDlg::SetImageParams( LPCTSTR pszDll,LPCTSTR pszType,UINT nImgID ,COLORREF crTrans)
{
m_sDll = pszDll;
m_sType = pszType;
m_nImgeID = nImgID;
m_crTrans = crTrans;
}
void CPainterDlg::OnColorChanged()
{
COLORREF cr = m_ColorBar.GetColor();
m_Painter.SetShapeColor(cr,COLOR_NONE);
}
void CPainterDlg::OnPaint()
{
CPaintDC dc(this);
m_Box1.Draw(&dc);
m_Box2.Draw(&dc);
}
// CPainterList
IMPLEMENT_DYNAMIC(CPainterList, CMFCListCtrl)
CPainterList::CPainterList()
{
m_pPainter = NULL;
}
CPainterList::~CPainterList()
{
}
BEGIN_MESSAGE_MAP(CPainterList, CMFCListCtrl)
ON_NOTIFY_REFLECT(NM_CLICK, &CPainterList::OnNMClick)
ON_NOTIFY_REFLECT(LVN_ITEMCHANGING, &CPainterList::OnLvnItemchanging)
END_MESSAGE_MAP()
void g_listAutoSizeColumns(CListCtrl& rList,int col /*=-1*/)
{
// Call this after your list control is filled
if(!rList.GetHeaderCtrl())return;
rList.ShowWindow(SW_HIDE);
rList.SetRedraw(FALSE);
int mincol = col < 0 ? 0 : col;
int maxcol = col < 0 ? (rList.GetHeaderCtrl()->GetItemCount())-1 : col;
for (col = mincol; col <= maxcol; col++) {
rList.SetColumnWidth(col,LVSCW_AUTOSIZE);
int wc1 = rList.GetColumnWidth(col);
rList.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);
int wc2 = rList.GetColumnWidth(col);
int iFrom=rList.GetTopIndex();
int nCountPerpage=rList.GetCountPerPage();
if(nCountPerpage <=0)
nCountPerpage=rList.GetItemCount();
int iTo=iFrom+nCountPerpage;
int nImgWidth=0;
int iIndent=0;
LVITEM li;
li.mask=LVIF_IMAGE|LVIF_INDENT;
IMAGEINFO ImageInfo;
for(int iItem=iFrom;iItem <iTo;iItem++){
li.iItem=iItem;
li.iSubItem=col;
rList.GetItem(&li);
iIndent=max(li.iIndent,iIndent);
if(li.iImage!=-1){
CImageList* pImageList=rList.GetImageList(LVSIL_SMALL);
if(pImageList-> GetSafeHandle()){
pImageList-> GetImageInfo(li.iImage,&ImageInfo);
nImgWidth=max(nImgWidth,ImageInfo.rcImage.right-ImageInfo.rcImage.left);
}
}
}
int wc = max(20,max(wc1,wc2));
if(col==0)
wc+=nImgWidth*(iIndent+1);
else
wc+=nImgWidth;
rList.SetColumnWidth(col,wc);
}
rList.SetRedraw(TRUE);
rList.ShowWindow(SW_SHOW);
}
// CPainterList 消息处理程序
void CPainterList::PreSubclassWindow()
{
CMFCListCtrl::PreSubclassWindow();
ModifyStyle(0,LVS_SHOWSELALWAYS|LVS_SINGLESEL|LVS_LIST);
}
COLORREF CPainterList::OnGetCellTextColor( int iRow, int iCol )
{
if (IsSelected(iRow))
return RGB(255,0,0);
else
return RGB(0,0,0);
}
COLORREF CPainterList::OnGetCellBkColor( int iRow, int iCol )
{
if (IsSelected(iRow))
return RGB(255,255,225);
else
return RGB(255,255,255);
}
BOOL CPainterList::IsSelected( int iRow )
{
POSITION pos = GetFirstSelectedItemPosition();
while (pos != NULL)
{
if (GetNextSelectedItem(pos) == iRow)
return TRUE;
}
return FALSE;
}
void CPainterList::OnNMClick(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
if (NULL == m_pPainter)
return;
int iSel = pNMItemActivate->iItem;
if (-1 == iSel)
{
// 没有选中项
m_pPainter->SetDrawing(SHAPE_NONE);
}
else
{
DWORD dwData = GetItemData(iSel);
switch (dwData)
{
case SHAPE_LINE:
case SHAPE_RECT:
case SHAPE_ELLIPSE:
case SHAPE_CURVE:
{
m_pPainter->SetDrawing((ENUM_SHAPE_TYPE)dwData);
break;
}
case SHAPE_ICON:
{
m_pPainter->SetDrawing(ENUM_SHAPE_TYPE(dwData),(LPARAM)m_ImageList.ExtractIcon(7));
break;
}
case ACTION_CLEAR:
{
if (YesNoMessageBox(L"清除后不能恢复,确定清除吗?"))
m_pPainter->ClearDrawed();
break;
}
case ACTION_UNDO:
{
m_pPainter->Undo(1);
break;
}
}
}
*pResult = 0;
}
void CPainterList::OnLvnItemchanging(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
int iSel = pNMLV->iItem;
if (-1 != iSel)
{
if (GetItemData(iSel) > MAX_SHAPE_VALUE)
{
*pResult = 1;
return ;
}
}
*pResult = 0;
}
BOOL CPainterList::Init( UINT nImgeList ,COLORREF crMask)
{
if (m_ImageList.GetSafeHandle() != NULL)
return TRUE;
if (!m_ImageList.Create(24,24,ILC_COLORDDB|ILC_MASK,8,0))
return FALSE;
CBitmap bmp;
if (!bmp.LoadBitmap(nImgeList))
return FALSE;
m_ImageList.Add(&bmp,crMask);
SetImageList(&m_ImageList,LVSIL_SMALL);
return TRUE;
}
void CPainterList::AddButton( LPCTSTR sItem,int iImage,DWORD dwData )
{
int idx = GetItemCount();
idx = InsertItem(idx,sItem,iImage);
SetItemData(idx,dwData);
g_listAutoSizeColumns(*this,0);
}
void CPainterList::AddButton( const _SHAPE_ITEMS *pItem )
{
AddButton(pItem->sItem,pItem->iImg,pItem->dwData);
}
----------------------------------------
2011/5/18 更新:
_UpdateDrawing 对于QIcon需要特殊对待,可以提高绘图效率.
void CPainterPane::_UpdateDrawing( CPoint ptMove )
{
ASSERT(m_pShapeDrawing != NULL);
// 鼠标按下到上次移动的矩形位置
CRect rcLast = m_MouseTrack.GetRect();
// 鼠标按下到此次移动到的矩形位置
m_MouseTrack.Move(ptMove);
// 设置图形的矩形为当前的移动的位置
m_pShapeDrawing->UpdateEndPoint(ptMove);
// 当前图像的矩形
CRect rcDrawing = m_pShapeDrawing->GetRect();
// 取两个矩形的并集进行更新,它们的并集就是当前的脏矩形
BOOL bOK = TRUE;
if (SHAPE_ICON != m_eDrawing)
{
if(bOK = rcDrawing.UnionRect(&rcDrawing,&rcLast))
{
// 边界增大一点是为了不使图形切边
rcDrawing.InflateRect(1,1);
}
}
if (bOK)
{
// 先拷贝需要更新区域的已画好的图形到正在画的区域中
CopyDC(&m_dcMemDrawing,&m_dcMemDrawed,rcDrawing);
// 画图形
m_pShapeDrawing->Draw(&m_dcMemDrawing);
// 更新到显示dc,
// 只有当鼠标左键弹起,标志这个图形画好的时候才能更新到m_dcMemDrawed 上
CopyDCToSurface(&m_dcMemDrawing,rcDrawing);
}
}
同时: QIcon::Draw是错误的.因为DrawIcon绘制出的图标大小是GetSystemMetrics(SM_CXICON)GetSystemMetrics(SM_CYICON)得到的大小,可以使用DrawIconEx代替之.
void QIcon::Draw( CDC *pDC )
{
if (NULL != m_hIcon)
{
CRect rc = GetRect();
DrawIconEx(pDC->GetSafeHdc(),m_pt1.x,m_pt1.y,m_hIcon,rc.Width(),
rc.Height(),0,NULL,DI_NORMAL);
}
}
同时: QIcon::Draw是错误的.因为DrawIcon绘制出的图标大小是GetSystemMetrics(SM_CXICON)GetSystemMetrics(SM_CYICON)得到的大小,可以使用DrawIconEx代替之.