MFC 中显示 bmp 格式的位图

2022年 8月 11日 47点热度 0人点赞 0条评论

file

原文地址: https://www.cnblogs.com/qingergege/p/4964951.html

最近在看 VisualC++ 图像处理的书籍, 表示一直在从基础做起, 今天就记录一个简单功能的实现, 显示 .bmp 格式的位图.

首先需要理解的是窗口创建的过程包括两个步骤: 首先擦除窗口的背景, 然后在对窗口进行重新绘制.

一般而言, 对于单文档或多文档的 MFC 程序, 显示图像的代码要放在 OnDraw 函数之中. 刚刚说过, 窗口重绘时, 要先将窗口的背景擦除, 也就是发送 WM_ERASEBKGND 消息, 然后用 OnEraseBkgnd() 函数处理这个消息, 所以我们的显示图像的代码也可以放在这个函数之中. 当然, 这里只是为了实现显示位图这一个功能, 在实际工程中, 要根据实际情况, 选择代码放置的地方.

下面先给出代码, 然后一行一行地详细解释:

BOOL CLoadBitmapView::OnEraseBkgnd(CDC* pDC) {
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    // return FALSE;
    HBITMAP hBit;
    // 载入图像
    hBit = (HBITMAP) LoadImage(NULL, _T("D:\\Axing.bmp"), IMAGE_BITMAP, 0, 0,
                               LR_LOADFROMFILE|LR_CREATEDIBSECTION);
    CBitmap cBit;
    cBit.Attach(hBit);
    CDC MemDC;
    // 创建与当前设备描述表相适应的内存 DC
    MemDC.CreateCompatibleDC(pDC);

    BITMAP bitmap;
    cBit.GetBitmap(&bitmap);
    CBitmap *oldBit;
    oldBit=MemDC.SelectObject(&cBit);
    CRect rect;
    GetClientRect(&rect);
    pDC->BitBlt(100, 100, rect.Width()/2.5, rect.Height(), &MemDC, 0, 0, SRCCOPY);
    return TRUE;
    // return CView::OnEraseBkgnd(pDC);
}

首先来解释一下 HBITMAP, CBitmap, BITMAP 三者之间的关系.

HBITMAP 是图片的句柄, CBitmap 是 MFC 定义的一个类, 在这个类中对 HBITMAP 进行了封装, BITMAP 则是一个结构体, 这个结构体中保存着位图的各种信息 (如宽度和高度等). 有三个函数是和这三个类型息息相关的.

  • Attach(), 这个函数是 CBitmap 类的成员函数, 作用就是将 HBITMAP 类型转换成 CBitmap 类型. 我们在代码中会用到它.
  • GetBitmap(), 这个函数也是 CBitmap 类的成员函数, 作用就是获取位图的信息, 并将位图的信息保存在 BITMAP 结构指针中.
  • GetObject(), 这个函数的作用就是, 从 HBITMAP 句柄中获取 BITMAP 结构.

这三个函数的具体用法, 详细说明在这里就不赘述了, 用的时候可以百度一下很方便的.

我们先建立了一个 HBITMAP 类型的句柄, 然后用 LoadImage() 函数来载入一个位图, 并保存位图的句柄. 之后, 创建一个 CBitmap 类的对象, 然后将 HBITMAP 转换成 CBitmap 对象 (用 Attach() 函数). 之后用 CReateCompatibleDC() 函数, 创建一个与当前设备上下文相兼容的内存 DC.

问题来了, 为什么要创建这个内存 DC 呢?

其实, 这个内存 DC 的作用就是缓冲. 如果需要对屏幕进行比较多的 GUI 操作, 如果直接对屏幕 DC 进行操作, 会导致同显示内存之间的过于频繁的数据交换, 于是程序运行效率将受到严重影响. 任何绘图的过程都是这样, 要完成一幅图像的显示, 总是在显示终端上依次绘制每个像素点, 以形成完整的图像. 我们在内存中虚拟一块画布 (就是内存 DC), 绘图时仅仅对内存进行操作. 待这幅图像绘制完成时在整体复制到屏幕上, 这样避免了外设和内存之间频繁的数据交换, 程序的运行效率会提高很多. 这就是使用 CreateCompatibleDC() 函数创建内存 DC 的作用.

接着, 我们将位图选入到内存 DC 中, 即 MemDC.SelectObject(&cBit), 这一步也是相当重要的. 因为当兼容的内存 DC 创建的时候, 他的显示表面是标准的一个单色像素宽和单色像素高. 在应用程可以使用内存 DC 进行绘图操作之前, 必须将一个具有正确高度和宽度的位图, 选入到内存 DC 中, 这时内存设备上下文显示表面的大小就由当前选入的位图决定了.

之后 CRect rect;GetClientRect(&rect); 用于获取客户区.

最后用 BitBlt() 函数将内存 DC 中的内容复制到当前 DC 中, 显示位图完毕.

这里要解释一下 BitBlt 函数, 它的函数原型为:

BOOL BitBlt(int x, int y, int nWidth, int nHeight, CDC*pSrcDC, int xSrc, int ySrc, DWORDdwRop);
  • x, y 表示目的 DC(当前设备上下文) 矩形区域的左上角坐标
  • nWidth, nHeight, 表示矩形区域的宽和高
  • pSRCDC 则是源 DC(内存 DC) 的指针
  • xSrcySrc 表示源 DC 矩形区域的左上角坐标

我们可以看到并没有参数表示源目的矩形区域的宽和高, 这样, 区域之间的复制只能是 1:1 的, 所以当图像比较大时, 只能显示图像的一部分. 为了解决这个问题, 可以用 StretchBlt() 函数, 这个函数和 BitBlt() 函数的功能基本一致, 只是可以对图像进行伸缩变换, 这是因为在 StretchBlt() 函数中增加了两个参数, 表示源 DC 矩形区域的大小. 所以上面的代码可以改成

pDC->StretchBlt(100, 100, rect.Width()/2.5, rect.Height(), &MemDC, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);

这样就可以显示整张位图了.

本文来自:https://blog.duhbb.com

本文链接地址:MFC 中显示 bmp 格式的位图,英雄不问来路,转载请注明出处,谢谢。

有话想说:那就赶紧去给我留言吧。

rainbow

这个人很懒,什么都没留下

文章评论