MFC 显示位图 缩放与拖动的矛盾

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MFC 显示位图 缩放与拖动的矛盾相关的知识,希望对你有一定的参考价值。

MFC多文档视图类,使用SetDIBitsToDevice显示位图,并实现拖动和缩放的功能。
对于缩放,1.如果是一次性对整张图进行重采样,计算获得的lpvBits存储了整张图缩放后的图像数据,然后完成缩放操作后,对其进行拖动显示会相当流畅,但是如果影像尺寸大(如10000*10000的图像放大到500%)整个缩放计算的过程就久;2.如果是只针对当前将要显示的部分进行缩放,即首先确定缩放后屏幕显示的范围,lpvBits只存储这个范围的数据,则当拖动的时候(即不断执行OnDraw),则lpvBits都重新计算一次,这样的话,每次缩放都很快,即使图像再大,缩放比例再大,循环次数也一样,因为不必一次性完成全图的缩放,只是计算缩放当前部分,而每当拖动的时候,都要重新计算lpvBits存储的值,这样就会导致拖动的时候比较卡,不像一次性完成全图缩放计算之后再拖动时那么流畅。
总结一下就是:1.缩放计算针对全图,则计算时间长,但计算之后拖动流畅;2.缩放计算针对当前要显示的区域,则计算时间短,但拖动时不流畅(即每次执行OnDraw都要重新计算一次lpvBits的值)。
请问一般怎么解决这个问题?因为我看到很多软件,缩放速度块,缩放后拖动也流畅。
是否除了SetDIBitsToDevice、StretchDIBits,还有其它用于位图显示的函数?能够便于处理上述问题?
PS:1.是缩放显示,不是对原图大小进行改变,只是显示,重采样的只是用于显示的数据;2.StretchDIBits能指定目标矩阵和源矩阵,然后该函数能完成拉伸,但是更卡,不考虑。

看了你的描述,我有几点想法看能不能帮到你吧。
第一,关于缩放。像ACDSee,缩放只支持固定的几种大小,50,75,100,125,150,300……如果你对缩放大小没有要求的话,可以像他这样,只支持几种固定的大小,然后在加载的时候,把你算好的位图数据提前存入相关的内存中,缩放到相应的大小直接显示就行。
第二,如果你想实现精确的缩比(比如1%比例的),你可以试一下使用GDI+,使用GDI的库,运行速度要快很多,具体能达到什么效果因为没实现过你这类的功能所以不敢确定。
第三,如果你不想用GDI的库,还想实现精确控制的话,你可以试试做一下多线程缓冲。比如你启动的时候另启一个缓冲线程,这个线程用来计算全部的lpvBits数据。当你第一次放大至很大时,只计算当前的缩放,而同时,使用消息也好还是事件也好,让缓冲线程计算当前放大页面的全部数据,计算完成后,使用标志位之类的随你,总之,OnDraw就不用再次计算lpvBits中的值了,直接使用缓冲线程的数据就可以了。大体这么个流程,具体实现个人感觉没什么太复杂的地方,理论上也是可行的。
PS:对bitmap只算是用过几次,发表一下个人的想法,能帮上最好,要是觉得我的想法不可行,可以继续交流。
参考技术A 你这样的两种方式都有缺点,我不清场你怎样使用的内存DC。提供你两个优化办法:
1·其实你第二种方法,我认为通过两个处理可以很好的优化,一个是以当前视口为中心,做3*3一共9个视口大小的区域图进行缩放计算,这样在移动的时候根本不需要重新计算,只需要松开鼠标后再做一次3*3的运算就可以了;另外一个下面一起解释。
2·StretchDIBits并非不可取,你在内存中建立第二个内存DC,将原图通过StretchDIBits缩放到第二内存DC中,这样每次刷新只需要内存DC到设备DC的BitBlt就可以了,会很快(同样最好是3*3);另外就是和1里面一样,你每次拖动比较卡的原因还有一个就是界面刷新问题,CScrollView有一个优化,就是对于BitBlt,滚动的时候不是全屏刷新,而是只刷新更新区域,这样显示就快,如果你在做自定义滚动的时候是全屏刷新,那么显示的闪烁和卡就是必然的。本回答被提问者采纳
参考技术B 您一些参考方案,我直接写:的
CBitmap的位图; / /自定义位图变量的
bitmap.LoadBitmap的(IDB_BITMAP1); / /获取位图的ID
的bmp位图;
bitmap.GetBitmap(BMP);
CDC直流;
dc.CreateCompatibleDC(PDC); / /创建一个DC
dc.SelectObject(位图) / /选择
了CRect矩形
GetClientRect(正确);
PDC->的StretchBlt(0,0,rect.Width(),rect.Height(的直流设备描述表)直流,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);
/ /显示位图
参考技术C CDC类的BitBlt()来显示 参考技术D CDC类的BitBlt()来显示

在mfc中从中心缩放位图图像

【中文标题】在mfc中从中心缩放位图图像【英文标题】:Zoom Bitmap Image from center in mfc 【发布时间】:2015-06-02 05:33:34 【问题描述】:

我是 MFC 的新手。如何从中心缩放位图图像。在我的代码中,图像从左上角放大。但是图像应该从滚动条的中心放大..

void CRightUp::ImageDraw(int sHeight,int sWidth)
    CDC *screenDC = GetDC();
    CDC mDC;
    mDC.CreateCompatibleDC(screenDC);
    CBitmap bitmap;
    bitmap.CreateCompatibleBitmap(screenDC, sWidth, sHeight);
    CBitmap *pob = mDC.SelectObject(&bitmap);
    mDC.SetStretchBltMode(HALFTONE);
    m_img.StretchBlt(mDC.m_hDC, 0, 0, sWidth, sHeight, 0, 0,   m_img.GetWidth(), m_img.GetHeight(), SRCCOPY);
    mDC.SelectObject(pob);
    m_logoImage.SetBitmap((HBITMAP)bitmap.Detach());
    ReleaseDC(screenDC);

【问题讨论】:

什么是 m_logoImage?您每次都在创建一个新的 HBITMAP,而不仅仅是绘制它。为什么不直接处理 in OnPaint 方法呢? @Garr Godfrey m_logoImage CStatic 对象。我已经使用图片控件来显示图像了。 好吧,您可以通过将 CStatic 对象设置为窗口的大小并设置其对齐属性来做到这一点......或者我会发布一个仅绘制它的答案。 CRightUp 是 CWnd 的衍生产品吗? CFormView 继承自 CWnd。根据您拥有的子控件,我发布的答案应该有效 也就是说,如果您在 m_logoImage 上设置 SS_CENTERIMAGE 样式并将其设置为客户区的完整大小,您的技术应该可以工作。 【参考方案1】:

为什么要使用 m_logoImage:

void CRightUp::ImageDraw(int sHeight,int sWidth)
    RECT r;
    GetClientRect(&r);
    CDC *screenDC = GetDC();
    m_img.StretchBlt(screenDC->m_hDC, ((r.right-r.left)-sWidth)/2, 
         ((r.bottom-r.top)-sHeight)/2, 
         sWidth, sHeight, 0, 0,  
         m_img.GetWidth(), m_img.GetHeight(), SRCCOPY);
    ReleaseDC(screenDC);

这只会暂时绘制它。如果您快速调用 ImageDraw(例如,每 1/10 秒或更长时间),在完全缩放之前应该没问题。此时,您可以在 CStatic 或 OnPaint 或 OnEraseBackground 中处理它

【讨论】:

以上是关于MFC 显示位图 缩放与拖动的矛盾的主要内容,如果未能解决你的问题,请参考以下文章

Android 拍照或相册选择照片进行显示缩放位图 Demo

Vue环境下用ECharts绘制中国地图,并实现拖动缩放与各省份自动轮播高亮显示

缩放位图保持纵横比

如何解决MFC单文档滚动条拖动时的重影问题(高分)

每天学一个jquery插件-缩放与拖动

如何让MFC程序在高DPI设置时禁用显示缩放