• 使用StretchBlt函数实现绘制透明位图


    转载:https://www.write-bug.com/article/1953.html

    StretchBlt函数

    从源矩形中复制一个位图到目标矩形,必要时按目标设备设置的模式进行图像的拉伸或压缩。

    函数声明

    BOOL StretchBlt(
          HDC hdcDest, 
          int nXOriginDest, 
          int nYOriginDest, 
          int nWidthDest, 
          int nHeightDest, 
          HDC hdcSrc, 
          int nXOriginSrc, 
          int nYOriginSrc, 
          int nWidthSrc, 
          int nHeightSrc, 
          DWORD dwRop
      );

    参数

    • hdcDest:指向目标设备环境的句柄。

    • nXOriginDest:指定目标矩形左上角的X轴坐标,按逻辑单位表示坐标。

    • nYOriginDest:指定目标矩形左上角的Y轴坐标,按逻辑单位表示坐标。

    • nWidthDest:指定目标矩形的宽度,按逻辑单位表示宽度。

    • nHeightDest:指定目标矩形的高度,按逻辑单位表示高度。

    • hdcSrc:指向源设备环境的句柄。

    • nXOriginSrc:指向源矩形区域左上角的X轴坐标,按逻辑单位表示坐标。

    • nYOriginSrc:指向源矩形区域左上角的Y轴坐标,按逻辑单位表示坐标。

    • nWidthSrc:指定源矩形的宽度,按逻辑单位表示宽度。

    • nHeightSrc:指定源矩形的高度,按逻辑单位表示高度。

    • dwRop:指定要进行的光栅操作。光栅操作码定义了系统如何在输出操作中组合颜色,这些操作包括刷子、源位图和目标位图等对象。

      下面列出了一些常见的光栅操作代码:

    VALUEMEANING
    BLACKNESS 表示使用与物理调色板的索引0相关的色彩来填充目标矩形区域,对缺省的物理调色板而言,该颜色为黑色
    DSTINVERT 表示使目标矩形区域颜色取反
    MERGECOPY 表示使用布尔型的AND(与)操作符将源矩形区域的颜色与特定模式组合一起
    MERGEPAINT 通过使用布尔型的OR(或)操作符将反向的源矩形区域的颜色与目标矩形区域的颜色合并
    NOTSRCCOPY 将源矩形区域颜色取反,于拷贝到目标矩形区域
    NOTSRCERASE 使用布尔类型的OR(或)操作符组合源和目标矩形区域的颜色值,然后将合成的颜色取反
    PATCOPY 将特定的模式拷贝到目标位图上
    PATPAINT 通过使用布尔OR(或)操作符将源矩形区域取反后的颜色值与特定模式的颜色合并。然后使用OR(或)操作符将该操作的结果与目标矩形区域内的颜色合并
    PATINVERT 通过使用XOR(异或)操作符将源和目标矩形区域内的颜色合并
    SRCAND 通过使用AND(与)操作符来将源和目标矩形区域内的颜色合并
    SRCCOPY 将源矩形区域直接拷贝到目标矩形区域
    SRCERASE 通过使用AND(与)操作符将目标矩形区域颜色取反后与源矩形区域的颜色值合并
    SRCINVERT 通过使用布尔型的XOR(异或)操作符将源和目标矩形区域的颜色合并
    SRCPAINT 通过使用布尔型的OR(或)操作符将源和目标矩形区域的颜色合并
    WHITENESS 使用与物理调色板中索引1有关的颜色填充目标矩形区域。对于缺省物理调色板来说,这个颜色就是白色

    返回值

    • 如果函数成功,那么返回值非零;如果函数失败,则返回值为零。

    什么是位图透明处理?

    至于如何使用 BitBlt 函数绘制位图,我在《BMP位图的绘制》这篇有讲过,当然这只是其中一种绘图方法而已,并不是唯一的。

    那我就先来介绍下,什么是透明位图的绘制。用下面的图片直观表示吧:

    如果我们有两张图片,一张是背景图:

    一张是人物图:

    现在,我想把人物绘制到背景图上,那么按照通常的方法就是,把人物的图片直接在背景图上显示就好,效果如下所示:

    大家可能会看到有些问题了吧,我们其实想要的是下面这张图片的效果:

    没错,所谓的透明位图的绘制就像上面图片表示出来的那样。就是我们只绘制图片特定的地方,而且形状都是不规则的,其他的不进行绘制。就对上上面的例子来说,就是把人物抠出来绘制,背景的黑色不绘制,即背景像似透明一样,所以这便是位图的透明绘制。

    透明原理

    我们知道,黑色的RGB值为(0, 0, 0),二进制为:

    1. 00000000 00000000 00000000

    白色的RGB值为(255, 255, 255),二进制为:

    1. 11111111 11111111 11111111

    所以,请注意理解下面两个结论:

    • 任何RGB值的颜色,只要和黑色RGB(0, 0, 0)做“与”操作,都会变为黑色;任何RGB值的颜色,只要和白色RGB(255, 255, 255)做“与”操作,还是原来的颜色

    • 任何RGB值的颜色,只要和黑色RGB(0, 0, 0)做“或”操作,还是原来的颜色;任何RGB值的颜色,只要和白色RGB(255, 255, 255)做“或”操作,都会变为白色

    现在,我们先来看下下面这张图片的分析:

    上图左边为“前景图”,右边黑白图称为“屏蔽图”。

    如果,我们要将人物从图片中抠出来,那么就需要准备类似上面所示的两张图片,一张是前景图,一张是屏蔽图。其中,前景图背景为黑色;屏蔽图背景为白色,人物部分为黑色。

    根据上面我们总结的两个结论,透明原理解析如下:

    首先,我们把人物图片中的“屏蔽图”里的每一个RGB值和背景图里的绘制部分的每个RGB值进行“与”操作,这时图片效果为:“屏蔽图”的黑色部分在背景图上还是保持黑色,白色部分保持原来的背景图的颜色不变。

    然后,我们继续在上述的基础上,把人物图片中的“前景图”里的每一个RGB值和上述结果的背景图里的绘制部分的每个RGB值进行“或”操作,那么就会得到下面图片的效果:“前景图”的黑色部分还是保持背景图原来的颜色,人物部分在背景图上,就成功绘制上去了。

    这样,便实现了人物的抠图了,即背景透明处理。

    编程实现原理

    对于,StretchBlt 函数来说,它包含了 BitBlt 函数的所有功能,同时它还能对绘制的位图进行伸缩绘制。同样,它的最后一个参数指定了源矩形区域的颜色数据,如何与目标矩形区域的颜色数据组合以完成最后的颜色。我们只要将“屏蔽图”先做“与”操作,然后再使用“前景图”做“或”操作就好。

    主要实现步骤如下所示:

    • 首先将整个背景图贴到目的地DC中

    • 屏蔽图与背景图做“AND”(Raster值为SRCAND)运算,贴到目的地DC中

    • 前景图与背景图做“OR”(Raster值SRCPAINT)运算,贴到目的地DC中

    编码实现

    绘制背景位图

    BOOL PaintBmp(HWND hWnd)
    {
        // 获取窗口的客户区域的显示设备上下文环境的句柄
        HDC hDC = ::GetDC(hWnd);
        // 创建一个与hDC兼容的内存设备上下文环境
        HDC hBuf = ::CreateCompatibleDC(hDC);
        // 加载位图, 获取位图句柄
        HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, "image\bg.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
        // 选择位图句柄到hBuf中, 并获取返回的原来位图句柄
        HBITMAP hOldBmp = (HBITMAP)::SelectObject(hBuf, hBmp);
        // 绘图
        ::BitBlt(hDC, 0, 0, 764, 397, hBuf, 0, 0, SRCCOPY);
        // 还原位图对象
        ::SelectObject(hBuf, hOldBmp);
        // 释放位图
        ::DeleteObject(hBmp);
        // 释放兼容的内存设备上下文环境
        ::DeleteDC(hBuf);
        // 释放设备上下文环境
        ::ReleaseDC(hWnd, hDC);
        return TRUE;
    }

    绘制透明位图

    BOOL PaintTransparentBmp(HWND hWnd)
    {
        // 获取窗口的客户区域的显示设备上下文环境的句柄
        HDC hDC = ::GetDC(hWnd);
        // 创建一个与hDC兼容的内存设备上下文环境
        HDC hBuf = ::CreateCompatibleDC(hDC);
        // 加载位图, 获取位图句柄
        HBITMAP hBmp1 = (HBITMAP)::LoadImage(NULL, "image\1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
        HBITMAP hBmp2 = (HBITMAP)::LoadImage(NULL, "image\2.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
        // 选择位图句柄到hBuf中, 并获取返回的原来位图句柄
        HBITMAP hOldBmp = (HBITMAP)::SelectObject(hBuf, hBmp1);
        // "与"操作绘制
        ::StretchBlt(hDC, 260, 170, 200, 200, hBuf, 0, 0, 101, 121, SRCAND);
        // 选择位图句柄到hBuf中
        ::SelectObject(hBuf, hBmp2);
        // "或"操作绘制
        ::StretchBlt(hDC, 260, 170, 200, 200, hBuf, 0, 0, 101, 121, SRCPAINT);
        // 还原位图对象
        ::SelectObject(hBuf, hOldBmp);
        // 释放位图
        ::DeleteObject(hBmp1);
        ::DeleteObject(hBmp2);
        // 释放兼容的内存设备上下文环境
        ::DeleteDC(hBuf);
        // 释放设备上下文环境
        ::ReleaseDC(hWnd, hDC);
        return TRUE;
    }

    程序测试

    调用上述封装好的函数,直接测试。人物位图成功背景透明绘制到背景图上,而且人物图片成功放大绘制了。

    总结

    要注意理解上面的位图透明实现的原理,这算是透明处理最本质的原理了,其他函数可能也会实现同样的效果,但是原理还是本文讲的这个。如果仍然不了解绘图过程,那么建议多动手练几遍,加深印象。

    其中,StretchBlt 函数包含了 BitBlt 的所有功能,它自己还额外具备图片的伸缩功能,使用起来比 BitBlt 更灵活。

    参考

    参考自《Windows黑客编程技术详解》一书

  • 相关阅读:
    第六周学习进度总结
    构建之法阅读笔记03
    文件操作
    数组相关
    compareTo
    我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? 比如n=3时,2*3的矩形块有3种覆盖方法:
    从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行
    整数中1出现的次数
    Java泛型(看着好有用)
    输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。
  • 原文地址:https://www.cnblogs.com/Toya/p/13433511.html
Copyright © 2020-2023  润新知