图形的保存与重绘(1)

1.创建图形

新建一个MFC单文档应用程序,仍然增加一个菜单,命名绘图,再添加几个菜单项:

IDM_POINT()IDM_LINE(直线)IDM_ RECTANGLE(矩形)IDM_ ELLIPSE (椭圆),并分别对这四个菜单项添加命令响应,并在CGraphic2View类中添加一个私有的成员变量UINT m_nDrawType;

并在构造方法中初始化:

CGraphic2View::CGraphic2View() 

    // TODO: add construction code here 
    m_nDrawType=0
    m_ptOrigin=0
    m_dcMetaFile.Create();//创建一个内存的CMetaFileDC对象
 

再编辑四个菜单项的命令响应:

void CGraphic2View::OnPoint()  

    // TODO: Add your command handler code here 
    m_nDrawType=1; 

 
void CGraphic2View::OnLine()  

m_nDrawType=2; 

 
void CGraphic2View::OnRectangle()  

    m_nDrawType=3; 

 
void CGraphic2View::OnEllipse()  

    m_nDrawType=4; 
}

再在CGraphic2View类中添加一个私有的成员变量m_ptOrigin用来保存原点坐标,并在构造函数中初始化,上面已列出。

接着再给CGraphic2View类添加两个WM_LBUTTONDOWNWM_LBUTTONUP消息响应函数,编辑:

void CGraphic2View::OnLButtonDown(UINT nFlags, CPoint point)  

m_ptOrigin=point;//将这个点保存起来 
    CView::OnLButtonDown(nFlags, point); 

 
void CGraphic2View::OnLButtonUp(UINT nFlags, CPoint point)  

    CClientDC dc(this); 
    CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH)); 
    //
创建透明画刷 
    dc.SelectObject(pBrush);  
    
    switch(m_nDrawType) 
    
    case 1
        dc.SetPixel(point,RGB(0,0,0));         break
    case 2
        dc.MoveTo(m_ptOrigin); 
        dc.LineTo(point);         break
    case 3
        dc.Rectangle(CRect(m_ptOrigin,point));         break
    case 4
        dc.Ellipse(CRect(m_ptOrigin,point));         break
    

    CView::OnLButtonUp(nFlags, point); 
}

运行可以发现当窗口尺寸发现变化是,所绘制的图形也消失了!

 

2.图形重绘

新建一个Generic Class (name:CGraph),并在这个类当中增加3个成员变量,同时为了对这3个成员进行赋值,定义一个带参数的构造函数,如下:

public
    CPoint m_ptOrigin; 
    CPoint m_ptEnd; 
    UINT m_nDrawType; 
    CGraph(); 
    CGraph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd); 

并在源文件Graph.cpp当中,对这个类的成员变量赋值:

CGraph::CGraph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd) 

    //
对这个类的成员变量赋值 
    this->m_nDrawType=m_nDrawType;//
图形类型 
    this->m_ptOrigin=m_ptOrigin;//
图形原点 
    this->m_ptEnd=m_ptEnd;//
图形终点 
}
 

CGraphic2View类当中,新增加一个成员变量CPtrArray m_ptrArray; 这个集合类型的变量用来保存CGraph对象,并在CGraphic2View::OnLButtonUp添加:

CGraph graph(m_nDrawType,m_ptOrigin,point);//构造一个对象

//:此时graph是一个局部变量,为它分配的内存在栈中,当这个函数结束时,对象也会被析构

m_ptrArray.Add(&graph);//graph对象保存到集合类的对象m_ptArray当中

接下来在CGraphic2View::OnDraw函数中添加:

CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH)); 
    //
创建透明画刷 
    pDC->SelectObject(pBrush); 
 
    //
将保存的图形对象取出来 
    for(int i=0;i<m_ptrArray.GetSize();i++) 
    
        switch(((CGraph*)m_ptrArray.GetAt(i))->m_nDrawType) 
        
        case 1
            pDC->SetPixel(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd,RGB(0,0,0)); 
            break
        case 2
            pDC->MoveTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin); 
            pDC->LineTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd); 
            break
        case 3
            pDC->Rectangle(CRect(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin, 
                ((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd)); 
            break
        case 4
            pDC->Ellipse(CRect(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin, 
                ((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd)); 
            break
        default
            break
        
    }

运行,当改变窗口大小时,图像仍然消息了,下面继续修改。

CGraphic2View::OnLButtonUp中修改:

//CGraph graph(m_nDrawType,m_ptOrigin,point);//构造一个对象 
    //
:此时graph是一个局部变量,为它分配的内存在栈中,当这个函数结束时,对象也会被析构 
    //m_ptrArray.Add(&graph);//
graph对象保存到集合类的对象m_ptArray当中 
    CGraph *pGraph;//
这也是一个局部的指针变量,内存在栈中 
    pGraph=new CGraph(m_nDrawType,m_ptOrigin,point); 
    //
这一句调用它的构造函数为pGraph指针类型的变量在堆中分配一个内存空间,构造一个CGraph类的对象 
    //
因为用new所分配的内存都是在堆中分配的,在堆中分配的对象的内存,如果不去显示的调用Delete去释放 
    //
这个对象内存,那么这个对象的生命周期是和应用程序保持一致的 
    m_ptrArray.Add(pGraph);//
将之前在堆中构造的CGraph类的对象地地址索引保存到集合类的对象m_ptArray当中

运行,绘制图形,改变窗口大小,图形没有消失,OK!

 

3.将图形输出放置到WM_PAINT消息响应函数中

CGraphic2View类上添加一个WM_PAINT消息处理,编辑:

void CGraphic2View::OnPaint()  

    CPaintDC dc(this); // device context for painting 
     
    // TODO: Add your message handler code here 
    OnPrepareDC(&dc); //
调整显示上下文的属性
    OnDraw(&dc);//调用
OnDraw 
    // Do not call CView::OnPaint() for painting messages 
}

4.给窗口添加滚动条

首先将CGraphic2View的基类CView手动改成CScorllView,CGraphic2View的头文件中和源文件中将所有的CView都替换成CScrollView,编译运行,会发现出现了非法操作,

    先介绍一下坐标空间的转换,下图

Coordinate Spaces and Transformations

Applications use coordinate spaces and transformations to scale, rotate, translate, shear, and reflect graphics output. Acoordinate space is a planar space that locates two-dimensional objects by using two reference axes that are perpendicular to each other. There are four coordinate spaces: world, page, device, and physical device (client area, desktop, or page of printer paper).

transformation is an algorithm that alters ("transforms") the size, orientation, and shape of objects. Transformations also transfer a graphics object from one coordinate space to another. Ultimately, the object appears on the physical device, which is usually a screen or printer.

一个例子:

Using Coordinate Spaces and Transformations

This section contains an example that demonstrates the following tasks:

  • Drawing graphics with predefined units.
  • Centering graphics in the application's client area.
  • Scaling graphics output to half its original size.
  • Translating graphics output 3/4 of an inch to the right.
  • Rotating graphics 30 degrees.
  • Shearing graphics output along the x-axis.
  • Reflecting graphics output about an imaginary horizontal axis drawn through its midpoint.

The following example was used to create the illustrations that appear earlier in this overview.

void TransformAndDraw(int iTransform, HWND hWnd)

{

HDC hDC;

XFORM xForm;

RECT rect;

 

// Retrieve a DC handle for the application's window.

 

hDC = GetDC(hWnd);

 

// Set the mapping mode to LOENGLISH. This moves the

// client area origin from the upper left corner of the

// window to the lower left corner (this also reorients

// the y-axis so that drawing operations occur in a true

// Cartesian space). It guarantees portability so that

// the object drawn retains its dimensions on any display.

 

SetGraphicsMode(hDC, GM_ADVANCED);

SetMapMode(hDC, MM_LOENGLISH);

 

// Set the appropriate world transformation (based on the

// user's menu selection).

 

switch (iTransform)

{

case SCALE: // Scale to 1/2 of the original size.

xForm.eM11 = (FLOAT) 0.5;

xForm.eM12 = (FLOAT) 0.0;

xForm.eM21 = (FLOAT) 0.0;

xForm.eM22 = (FLOAT) 0.5;

xForm.eDx = (FLOAT) 0.0;

xForm.eDy = (FLOAT) 0.0;

SetWorldTransform(hDC, &xForm);

break;

 

case TRANSLATE: // Translate right by 3/4 inch.

xForm.eM11 = (FLOAT) 1.0;

xForm.eM12 = (FLOAT) 0.0;

xForm.eM21 = (FLOAT) 0.0;

xForm.eM22 = (FLOAT) 1.0;

xForm.eDx = (FLOAT) 75.0;

xForm.eDy = (FLOAT) 0.0;

SetWorldTransform(hDC, &xForm);

break;

 

case ROTATE: // Rotate 30 degrees counterclockwise.

xForm.eM11 = (FLOAT) 0.8660;

xForm.eM12 = (FLOAT) 0.5000;

xForm.eM21 = (FLOAT) -0.5000;

xForm.eM22 = (FLOAT) 0.8660;

xForm.eDx = (FLOAT) 0.0;

xForm.eDy = (FLOAT) 0.0;

SetWorldTransform(hDC, &xForm);

break;

 

case SHEAR: // Shear along the x-axis with a

// proportionality constant of 1.0.

xForm.eM11 = (FLOAT) 1.0;

xForm.eM12 = (FLOAT) 1.0;

xForm.eM21 = (FLOAT) 0.0;

xForm.eM22 = (FLOAT) 1.0;

xForm.eDx = (FLOAT) 0.0;

xForm.eDy = (FLOAT) 0.0;

SetWorldTransform(hDC, &xForm);

break;

 

case REFLECT: // Reflect about a horizontal axis.

xForm.eM11 = (FLOAT) 1.0;

xForm.eM12 = (FLOAT) 0.0;

xForm.eM21 = (FLOAT) 0.0;

xForm.eM22 = (FLOAT) -1.0;

xForm.eDx = (FLOAT) 0.0;

xForm.eDy = (FLOAT) 0.0;

SetWorldTransform(hDC, &xForm);

break;

 

case NORMAL: // Set the unity transformation.

xForm.eM11 = (FLOAT) 1.0;

xForm.eM12 = (FLOAT) 0.0;

xForm.eM21 = (FLOAT) 0.0;

xForm.eM22 = (FLOAT) 1.0;

xForm.eDx = (FLOAT) 0.0;

xForm.eDy = (FLOAT) 0.0;

SetWorldTransform(hDC, &xForm);

break;

 

}

 

// Find the midpoint of the client area.

 

GetClientRect(hWnd, (LPRECT) &rect);

DPtoLP(hDC, (LPPOINT) &rect, 2);

 

// Select a hollow brush.

 

SelectObject(hDC, GetStockObject(HOLLOW_BRUSH));

 

// Draw the exterior circle.

 

Ellipse(hDC, (rect.right / 2 - 100), (rect.bottom / 2 + 100),

(rect.right / 2 + 100), (rect.bottom / 2 - 100));

 

// Draw the interior circle.

 

Ellipse(hDC, (rect.right / 2 -94), (rect.bottom / 2 + 94),

(rect.right / 2 + 94), (rect.bottom / 2 - 94));

 

// Draw the key.

 

Rectangle(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 113),

(rect.right / 2 + 13), (rect.bottom / 2 + 50));

Rectangle(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 96),

(rect.right / 2 + 13), (rect.bottom / 2 + 50));

 

// Draw the horizontal lines.

 

MoveToEx(hDC, (rect.right/2 - 150), (rect.bottom / 2 + 0), NULL);

LineTo(hDC, (rect.right / 2 - 16), (rect.bottom / 2 + 0));

 

MoveToEx(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 0), NULL);

LineTo(hDC, (rect.right / 2 + 13), (rect.bottom / 2 + 0));

 

MoveToEx(hDC, (rect.right / 2 + 16), (rect.bottom / 2 + 0), NULL);

LineTo(hDC, (rect.right / 2 + 150), (rect.bottom / 2 + 0));

 

// Draw the vertical lines.

 

MoveToEx(hDC, (rect.right/2 + 0), (rect.bottom / 2 - 150), NULL);

LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 - 16));

 

MoveToEx(hDC, (rect.right / 2 + 0), (rect.bottom / 2 - 13), NULL);

LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 13));

 

MoveToEx(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 16), NULL);

LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 150));

 

ReleaseDC(hWnd, hDC);

}


posted on 2012-09-04 22:06  龙猫先生  阅读(233)  评论(0编辑  收藏  举报

导航