VC提高GDI绘图速率的方法
acmilan2015/07/18软件综合 IP:四川
GDI绘图效率不高的主要解决方法:

1.不要想着用GDI或GDI+在绘图期间(比如OnPaint)做一些耗时动作,如高斯模糊等,这样会导致界面严重闪烁!

高斯模糊等耗时操作应该进行预处理,并在绘图时简单地转化为位图映射这种迅速高效的绘图操作,而不是在每次绘图时都处理一遍。
<code class="lang-cpp">// 错误的做法
void OnPaint()
{
     using namespace Gdiplus;
     CPaintDC dc(this);
     Graphics graphics(dc);
     Image myImage(L"img1.jpg");
               
     REAL srcWidth = (REAL)myImage.GetWidth();
     REAL srcHeight = (REAL)myImage.GetHeight();
     RectF srcRect(0.0f, 0.0f, srcWidth, srcHeight);
     Matrix myMatrix(1.0f, 0.0f, 0.0f, 1.0f, 200.0f, 20.0f);
               
     BlurParams myBlurParams;
     myBlurParams.expandEdge = TRUE;
     myBlurParams.radius = 3;
               
     Blur myBlur;
     myBlur.SetParameters(&myBlurParams);
               
     // Draw the image without the blur effect.
     graphics.DrawImage(&myImage, 20.0, 20.0, srcWidth, srcHeight);
               
     // Draw the image with the blur effect.
     graphics.DrawImage(&myImage, &srcRect, &myMatrix, &myBlur, NULL, UnitPixel);
}
                       
// 正确的做法
void OnPaint()
{
    using namespace Gdiplus;
    CPaintDC dc(this);
    Graphics graphics(dc);
    Image myImage(L"img1.jpg");
                       
    // 画出没有模糊效果的图片
    graphics.DrawImage(&myImage, 20, 20);
                       
    static Bitmap *g_bmp = NULL;
    if (g_bmp == NULL) { // 如果图片还没有被模糊处理过,那么就先处理它,否则直接显示
                        
        REAL srcWidth = (REAL)myImage.GetWidth();
        REAL srcHeight = (REAL)myImage.GetHeight();
                
        BlurParams myBlurParams;
        myBlurParams.expandEdge = TRUE;
        myBlurParams.radius = 3;
                       
        Blur myBlur;
        myBlur.SetParameters(&myBlurParams);
                       
        g_bmp = ::new Bitmap(srcWidth, srcHeight, &graphics);
        Graphics graphics2(g_bmp);
                     
        // 在内存位图上画出高斯模糊过的图片
        graphics2.DrawImage(&myImage, NULL, NULL, &myBlur, NULL, UnitPixel);
    }
                       
    // 画出已经处理过的图片
    graphics.DrawImage(g_bmp, 500, 20);
}</code>

2.如果界面仍有闪烁,那么最好把OnEraseBkgnd重写成空函数,改在OnPaint中填充背景颜色,因为消息之间可能有延时。
<code class="lang-cpp">BOOL CChildView::OnEraseBkgnd(CDC* pDC)
{
    // 抗闪烁 - 不自动清空背景,务必在OnPaint中清空背景,否则易出现显示不正确!
    return TRUE;
    // 默认 - 自动清空背景,但是在某些情况下可能会闪烁
    //return CWnd::OnEraseBkgnd(pDC);
}</code>

3.在OnPaint代码中加入这个CFastDC类,实现缓冲绘图,并自动清空背景:
<code class="lang-cpp">struct CFastDC : public CDC
{
    CDC *m_phydc;
    CBitmap m_bitmap;
    CRect m_rc;
                                                      
    CFastDC() {
        m_phydc = NULL;
    }
                                                      
    CFastDC(CDC *phyDC, CRect rc, COLORREF cr = RGB(255, 255, 255)) {
        m_phydc = NULL;
        BeginFastDC(phyDC, rc, cr);
    }
                                                      
    ~CFastDC() {
        if (m_phydc != NULL)
            EndFastDC();
    }
                                                      
    CDC *BeginFastDC(CDC *phyDC, CRect rc, COLORREF cr = RGB(255, 255, 255))
    {
        ASSERT(m_phydc == NULL);
        m_phydc = phyDC;
        m_rc = rc;
        CreateCompatibleDC(phyDC); // 新建兼容DC
        m_bitmap.CreateCompatibleBitmap(phyDC, m_rc.right-m_rc.left, m_rc.bottom-m_rc.top);
        SelectObject(m_bitmap); // 新建承载图像的兼容位图并附着在兼容DC上
        FillColor(cr);
        return this;
    }
                                                      
    void FillColor(COLORREF cr)
    {
        CRect rc2;
        rc2.top = rc2.left = 0;
        rc2.right = m_rc.right - m_rc.left;
        rc2.bottom = m_rc.bottom - m_rc.top;
        FillSolidRect(rc2, cr); // 填充背景
    }
                                                      
    BOOL EndFastDC()
    {
        ASSERT(m_phydc != NULL);
        return m_phydc->BitBlt(m_rc.left, m_rc.top,
            m_rc.right-m_rc.left, m_rc.bottom-m_rc.top,
            this, 0, 0, SRCCOPY);
    }
};</code>

在OnPaint中需要绘图时,首先使用GetClientRect(rc)获得窗口客户区大小,然后用CFastDC包装住实体DC(如CClientDC、CPaintDC等),即可实现缓冲绘图。
<code class="lang-cpp">void OnPaint() {
    CRect rc;
    GetClientRect(rc); // 获取窗口大小
    CPaintDC dc(this); // 获取绘图DC
    CFastDC memdc(&dc, rc, GetSysColor(COLOR_3DFACE)); // 新建一个指定背景色和大小的内存DC
                                                  
    // TODO: 使用memdc而不是dc作图
    // ...
                                                  
    // 当CFastDC析构时,它会自动尝试将自己的图像映射到物理DC上
}</code>

CFastDC会建立一对兼容DC/Bitmap来承载绘图结果,并在析构时将图像映射到实体DC上,经过测试,使用这种方法很大程度上可以提高绘图速率。

[修改于 8年10个月前 - 2015/07/18 19:34:16]

来自:计算机科学 / 软件综合
2
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
acmilan 作者
8年10个月前 IP:四川
779697
无论是GDI、GDI+还是DirectDraw还是Direct2D,如果不注意细节的话,都非常容易闪屏。
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,5年3个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}