前言
提示:这里可以添加本文要记录的大概内容: 本人最近在学习计算机视觉的东西,然后在B站发现一个挺不错的课程计算机图形学全套算法讲解,于是想着每看一节课,便来总结一次,写一下博客,方便自己与后人的参考。
一、双缓冲绘图算法?
双缓冲是指一个显示缓冲区(显示设备上下文)和一个那日村缓冲区(内存设备上下文)。双缓冲机制是一种基本的动画技术,常用于解决单缓冲擦除图像时带来的屏幕闪烁问题。 本博客将以小球碰撞案例来展现该算法。 先上效果图:
二、使用步骤
1.准备环境
该项目使用的编辑器是VS 2017。
2.创建项目
打开VS2017,选择 新建项目 -> MFC 应用, 然后将项目名命名为:CV_ClassOne,点击确定,再选择 单个文档 -> MFC Standard
3. 创建小球类
# CSphere.h 头文件
#pragma once
#include <atltypes.h>
class CSphere
{
public:
CSphere();
~CSphere();
public:
void SetParameter(int R, CPoint point);
void Draw(CDC* pDC);
void MoveCenterPoint(CPoint distance);
int GetRadius();
CPoint GetCenterPoint();
private:
int radius;
CPoint centerPoint;
};
# CSphere.cpp 源文件
#include "pch.h"
#include "CSphere.h"
CSphere::CSphere()
{
radius = 10;
centerPoint = CPoint(200, 100);
}
CSphere::~CSphere()
{
}
void CSphere::SetParameter(int R, CPoint point)
{
radius = R;
centerPoint = point;
}
void CSphere::Draw(CDC * pDC)
{
CPoint leftTop = CPoint(centerPoint.x - radius, centerPoint.y - radius);
CPoint rightBottom = CPoint(centerPoint.x + radius, centerPoint.y + radius);
CPen pen(PS_SOLID, 2, RGB(255, 0, 0));
pDC->SelectObject(&pen);
CBrush brush(RGB(255, 0, 0));
pDC->SelectObject(&brush);
pDC->Ellipse(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y);
}
void CSphere::MoveCenterPoint(CPoint distance)
{
centerPoint.x += distance.x;
centerPoint.y += distance.y;
}
int CSphere::GetRadius()
{
return radius;
}
CPoint CSphere::GetCenterPoint()
{
return centerPoint;
}
4. 在view文件中设置所需参数及函数
先理清一下逻辑:
1. 需要添加一个小球对象,还需要添加小球移动的距离变量;
2. 需要添加一个定时器,用以更新小球的位置,同时调用绘图函数显示图像;
3.在绘图函数中调用双缓冲绘图算法函数;
4. 在双缓冲绘图算法函数中,实现将小球图像保存至内存DC中,再复制到显示DC中显示。
相关代码如下:
#pragma once
#include "CSphere.h"
class CCVClassOneView : public CView
{
protected:
CCVClassOneView() noexcept;
DECLARE_DYNCREATE(CCVClassOneView)
public:
CCVClassOneDoc* GetDocument() const;
public:
virtual void OnDraw(CDC* pDC);
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
public:
virtual ~CCVClassOneView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
DECLARE_MESSAGE_MAP()
protected:
CSphere sphere;
CPoint distance;
bool displaying;
public:
afx_msg void OnTimer(UINT_PTR nIDEvent);
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
public:
void DoubleBuffer(CDC* pDC);
void CollisionDetection();
};
#ifndef _DEBUG
inline CCVClassOneDoc* CCVClassOneView::GetDocument() const
{ return reinterpret_cast<CCVClassOneDoc*>(m_pDocument); }
#endif
#include "pch.h"
#include "framework.h"
#ifndef SHARED_HANDLERS
#include "CV_ClassOne.h"
#endif
#include "CV_ClassOneDoc.h"
#include "CV_ClassOneView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
IMPLEMENT_DYNCREATE(CCVClassOneView, CView)
BEGIN_MESSAGE_MAP(CCVClassOneView, CView)
ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)
ON_WM_TIMER()
ON_WM_LBUTTONDBLCLK()
END_MESSAGE_MAP()
CCVClassOneView::CCVClassOneView() noexcept
{
int R = GetSystemMetrics(SM_CXFULLSCREEN) / 20;
sphere.SetParameter(R, CPoint(200, 100));
distance = CPoint(1, 1);
displaying = false;
}
CCVClassOneView::~CCVClassOneView()
{
}
BOOL CCVClassOneView::PreCreateWindow(CREATESTRUCT& cs)
{
return CView::PreCreateWindow(cs);
}
void CCVClassOneView::DoubleBuffer(CDC* pDC)
{
CRect rect;
GetClientRect(&rect);
CDC memDC;
memDC.CreateCompatibleDC(pDC);
CBitmap newBitMap, *oldBitMap;
newBitMap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
oldBitMap = memDC.SelectObject(&newBitMap);
sphere.Draw(&memDC);
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);
memDC.SelectObject(oldBitMap);
newBitMap.DeleteObject();
memDC.DeleteDC();
}
void CCVClassOneView::CollisionDetection()
{
CRect rect;
GetClientRect(&rect);
CPoint centerPoint = sphere.GetCenterPoint();
int radius = sphere.GetRadius();
if (centerPoint.x - radius < 0)
distance.x = 1;
if (centerPoint.x + radius > rect.Width())
distance.x = -1;
if (centerPoint.y - radius < 0)
distance.y = 1;
if (centerPoint.y + radius > rect.Height())
distance.y = -1;
}
void CCVClassOneView::OnDraw(CDC* pDC)
{
CCVClassOneDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
DoubleBuffer(pDC);
}
BOOL CCVClassOneView::OnPreparePrinting(CPrintInfo* pInfo)
{
return DoPreparePrinting(pInfo);
}
void CCVClassOneView::OnBeginPrinting(CDC* , CPrintInfo* )
{
}
void CCVClassOneView::OnEndPrinting(CDC* , CPrintInfo* )
{
}
#ifdef _DEBUG
void CCVClassOneView::AssertValid() const
{
CView::AssertValid();
}
void CCVClassOneView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
CCVClassOneDoc* CCVClassOneView::GetDocument() const
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CCVClassOneDoc)));
return (CCVClassOneDoc*)m_pDocument;
}
#endif
void CCVClassOneView::OnTimer(UINT_PTR nIDEvent)
{
CollisionDetection();
sphere.MoveCenterPoint(distance);
Invalidate(FALSE);
CView::OnTimer(nIDEvent);
}
void CCVClassOneView::OnLButtonDblClk(UINT nFlags, CPoint point)
{
displaying = !displaying;
if (displaying)
SetTimer(1, 10, NULL);
else
KillTimer(1);
CView::OnLButtonDblClk(nFlags, point);
}
总结
以上就是所有相关代码。但是我仍有一个疑问:
在 void CCVClassOneView::DoubleBuffer(CDC* pDC) 函数最后,有将旧位图重新设置进内存DC中然后再释放内存DC这一步。但是我总感觉这一步是多余的,请问这一步是必须的嘛?希望有人能回答我!
如果其他人有什么问题,或者需要我整个项目的代码,也可以评论或者私信我。我会经常看的!
|