gpt4 book ai didi

visual-c++ - DrawLine 和 DrawLines 之间的性能差异?

转载 作者:行者123 更新时间:2023-12-04 06:37:44 34 4
gpt4 key购买 nike

我在 C++ 中使用 GDI+ 绘制图表控件。我想知道上述两个函数之间是否存在性能差异。我不懒惰为写代码DrawLines() 但正是这样做使我的代码变得复杂。所以我权衡是否以降低可读性和潜在增加错误和错误为代价加快代码执行的机会。

任何帮助 wud 不胜感激。
埃拉吉。

最佳答案

对于大多数绘图事件,两者之间应该没有显着差异,但可以肯定的是,我编写了一个测试项目来比较它们之间的差异(好吧,实际上是其中的 3 个)。

对于我机器上的大量线 (x25000),DrawLines() (640ms) 比 DrawLine() (420ms) 快约 50%。老实说,我第一次也误读了这个问题,并用 C# 编写了我的初始测试。两者的性能大致相同,这是可以预料的,因为 .NET 图形基于 GDI+。

出于好奇,我尝试了常规 GDI,我希望它会更快。使用 win32 PolyLine() (530ms) 函数大约快 20%,有 45000 行。这比使用 GDI+ DrawLines() 快 116%。更令人惊叹的是,使用 win32 LineTo() 而不是 GDI+ DrawLine() 导致时间低于 125 毫秒。假设时间为 125 毫秒和 45000 行,此方法至少是 800% 快点。 (计时器分辨率和线程计时使得在不求助于 QueryPerformanceCounter 和其他频率更高的计时方法的情况下很难在此阈值下衡量性能。)

但是,我应该警告不要假设这是绘制代码的一个重要瓶颈。许多可以进行的性能改进与必须绘制的对象无关。我猜您的要求可能会要求在正常操作中为您的控制绘制几百个项目。在这种情况下,我建议您编写绘图代码尽可能简单且无错误,因为调试绘图问题可能会耗费大量时间,而且与改进控件的其余部分或应用程序相比可能不太有益。

此外,如果您需要主动更新数以千计的项目,通过转向后缓冲解决方案,您将获得更高的性能提升。除了管理屏幕外缓冲区之外,这还应该使开发用于绘制控件的代码变得更加容易。

这是我的源代码示例。他们每个人都处理鼠标点击,以在使用批量绘图和逐项绘图之间交替。

GDI+,托管在准系统 MFC SDI 应用程序中

这假设有人已经声明了 GDI+ header 并编写了代码来初始化/拆卸 GDI+。

在 ChildView.h

// Attributes
public:
bool m_bCompositeMode;

// Operations
public:
void RedrawScene(Graphics &g, int lineCount, int width, int height);
PointF *CreatePoints(int lineCount, int width, int height);
void ReportTime(Graphics &g, int lineCount, DWORD tickSpan);

public:
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);

在 ChildView.cpp 中,添加到 PreCreateWindow()

m_bCompositeMode = false;

ChildView.cpp 的剩余部分,包括 OnPaint() 和 Message Map 更改。
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()

void CChildView::OnPaint()
{
CPaintDC dc(this); // device context for painting
RECT rcClient;
::GetClientRect(this->GetSafeHwnd(), &rcClient);

Graphics g(dc.GetSafeHdc());
g.Clear(Color(0, 0, 0));

RedrawScene(g, 25000, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
}

void CChildView::RedrawScene(Graphics &g, int lineCount, int width, int height)
{
DWORD tickStart = 0;
DWORD tickEnd = 0;

Pen p(Color(0, 0, 0x7F));
PointF *pts = CreatePoints(lineCount, width, height);
tickStart = GetTickCount();
if (m_bCompositeMode)
{
g.DrawLines(&p, pts, lineCount);
}
else
{
int i = 0;
int imax = lineCount - 1;
for (i = 0; i < imax; i++)
{
g.DrawLine(&p, pts[i], pts[i + 1]);
}
}
tickEnd = GetTickCount();
delete[] pts;
ReportTime(g, lineCount, tickEnd - tickStart);
}

void CChildView::ReportTime(Graphics &g, int lineCount, DWORD tickSpan)
{
CString strDisp;
if(m_bCompositeMode)
{
strDisp.Format(_T("Graphics::DrawLines(Pen *, PointF *, INT) x%d took %dms"), lineCount, tickSpan);
}
else
{
strDisp.Format(_T("Graphics::DrawLine(Pen *, PointF, PointF) x%d took %dms"), lineCount, tickSpan);
}

// Note: sloppy, but simple.
Font font(L"Arial", 14.0f);
PointF ptOrigin(0.0f, 0.0f);
SolidBrush br(Color(255, 255, 255));
Status s = g.DrawString(strDisp, -1, &font, ptOrigin, &br);
}

PointF* CChildView::CreatePoints(int lineCount, int width, int height)
{
if(lineCount <= 0)
{
PointF *ptEmpty = new PointF[2];
ptEmpty[0].X = 0;
ptEmpty[0].Y = 0;
ptEmpty[1].X = 0;
ptEmpty[1].Y = 0;
return ptEmpty;
}

PointF *pts = new PointF[lineCount + 1];
int i = 1;
while(i < lineCount)
{
pts[i].X = (float)(rand() % width);
pts[i].Y = (float)(rand() % height);
i++;
}
return pts;
}

void CChildView::OnLButtonUp(UINT nFlags, CPoint point)
{
m_bCompositeMode = !m_bCompositeMode;
this->Invalidate();

CWnd::OnLButtonUp(nFlags, point);
}

C#.NET,托管在 basebones WinForms 应用程序中,默认类 Form1

设置表单的默认大小,如果比较两者,则等于 MFC 版本的大小。还可以添加大小更改处理程序。
public Form1()
{
InitializeComponent();
bCompositeMode = false;
}

bool bCompositeMode;

private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.Black);
RedrawScene(e.Graphics, 25000, this.ClientRectangle.Width, this.ClientRectangle.Height);
}

private void RedrawScene(Graphics g, int lineCount, int width, int height)
{
DateTime dtStart = DateTime.MinValue;
DateTime dtEnd = DateTime.MinValue;
using (Pen p = new Pen(Color.Navy))
{
Point[] pts = CreatePoints(lineCount, width, height);
dtStart = DateTime.Now;
if (bCompositeMode)
{
g.DrawLines(p, pts);
}
else
{
int i = 0;
int imax = pts.Length - 1;
for (i = 0; i < imax; i++)
{
g.DrawLine(p, pts[i], pts[i + 1]);
}
}
dtEnd = DateTime.Now;
}
ReportTime(g, lineCount, dtEnd - dtStart);
}

private void ReportTime(Graphics g, int lineCount, TimeSpan ts)
{
string strDisp = null;
if (bCompositeMode)
{
strDisp = string.Format("DrawLines(Pen, Point[]) x{0} took {1}ms", lineCount, ts.Milliseconds);
}
else
{
strDisp = string.Format("DrawLine(Pen, Point, Point) x{0} took {1}ms", lineCount, ts.Milliseconds);
}

// Note: sloppy, but simple.
using (Font font = new Font(FontFamily.GenericSansSerif, 14.0f, FontStyle.Regular))
{
g.DrawString(strDisp, font, Brushes.White, 0.0f, 0.0f);
}
}

private Point[] CreatePoints(int count, int width, int height)
{
Random rnd = new Random();
if (count <= 0) { return new Point[] { new Point(0,0), new Point(0,0)}; }
Point[] pts = new Point[count + 1];
pts[0] = new Point(0, 0);
int i = 1;
while (i <= count)
{
pts[i] = new Point(rnd.Next(width), rnd.Next(height));
i++;
}
return pts;
}

private void Form1_Click(object sender, EventArgs e)
{
bCompositeMode = !bCompositeMode;
Invalidate();
}

常规 GDI,托管在准系统 MFC SDI 应用程序中

在 ChildView.h
// Attributes
public:
bool m_bCompositeMode;

// Operations
public:
void RedrawScene(HDC hdc, int lineCount, int width, int height);
POINT *CreatePoints(int lineCount, int width, int height);
void ReportTime(HDC hdc, int lineCount, DWORD tickSpan);

public:
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);

在 ChildView.cpp 中

就像在 GDI+ 示例中一样更新 PreCreateWindow()。
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()

void CChildView::OnPaint()
{
CPaintDC dc(this); // device context for painting
HDC hdc = dc.GetSafeHdc();

HBRUSH brClear = (HBRUSH)::GetStockObject(BLACK_BRUSH);
RECT rcClient;
::GetClientRect(this->m_hWnd, &rcClient);
::FillRect(hdc, &rcClient, brClear);
::DeleteObject(brClear);

RedrawScene(hdc, 45000, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
}

void CChildView::RedrawScene(HDC hdc, int lineCount, int width, int height)
{
DWORD tickStart = 0;
DWORD tickEnd = 0;

HPEN p = ::CreatePen(PS_SOLID, 1, RGB(0, 0, 0x7F));
POINT *pts = CreatePoints(lineCount, width, height);
HGDIOBJ prevPen = SelectObject(hdc, p);
tickStart = GetTickCount();
if(m_bCompositeMode)
{
::Polyline(hdc, pts, lineCount);
}
else
{
::MoveToEx(hdc, pts[0].x, pts[0].y, &(pts[0]));
int i = 0;
int imax = lineCount;
for(i = 1; i < imax; i++)
{
::LineTo(hdc, pts[i].x, pts[i].y);
}
}
tickEnd = GetTickCount();
::SelectObject(hdc, prevPen);
delete pts;
::DeleteObject(p);

ReportTime(hdc, lineCount, tickEnd - tickStart);
}

POINT *CChildView::CreatePoints(int lineCount, int width, int height)
{
if(lineCount <= 0)
{
POINT *ptEmpty = new POINT[2];
memset(&ptEmpty, 0, sizeof(POINT) * 2);
return ptEmpty;
}

POINT *pts = new POINT[lineCount + 1];
int i = 1;
while(i < lineCount)
{
pts[i].x = rand() % width;
pts[i].y = rand() % height;
i++;
}
return pts;
}

void CChildView::ReportTime(HDC hdc, int lineCount, DWORD tickSpan)
{
CString strDisp;
if(m_bCompositeMode)
{
strDisp.Format(_T("PolyLine(HDC, POINT *, int) x%d took %dms"), lineCount, tickSpan);
}
else
{
strDisp.Format(_T("LineTo(HDC, HPEN, int, int) x%d took %dms"), lineCount, tickSpan);
}

HFONT font = (HFONT)::GetStockObject(SYSTEM_FONT);
HFONT fontPrev = (HFONT)::SelectObject(hdc, font);

RECT rcClient;
::GetClientRect(this->m_hWnd, &rcClient);
::ExtTextOut(hdc, 0, 0, ETO_CLIPPED, &rcClient, strDisp.GetString(), strDisp.GetLength(), NULL);
::SelectObject(hdc, fontPrev);
::DeleteObject(font);
}

void CChildView::OnLButtonUp(UINT nFlags, CPoint point)
{
m_bCompositeMode = !m_bCompositeMode;
this->Invalidate();

CWnd::OnLButtonUp(nFlags, point);
}

关于visual-c++ - DrawLine 和 DrawLines 之间的性能差异?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1832488/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com