Timers and Animation

theForger's Win32 API教程第二版(简体中文)

主页

基础
  1. 开始学习
  2. 一个简单的窗口
  3. 处理消息
  4. 理解消息循环
  5. 使用资源
  6. 菜单和图标
  7. 对话框
  8. 非模态对话框
  9. 标准控件
  10. 对话框常见问题
创建一个简单应用
  1. 在运行时创建控件
  2. 文件与常用对话框
  3. 工具栏与状态栏
  4. 多文档界面
图形设备接口
  1. 位图,设备上下文
  2. 透明位图
  3. 定时器与动画
  4. 文本,字体与顏色
工具与文档
  1. 参考
  2. 免费的Visual C++(最新更新)
附表
  1. 常见错误的解決方法
  2. API vs. MFC
  3. 关于资源文件的说明

透明位图

[images/bmp_two.gif]

范例:bmp_two

透明

  给位图加上透明效果很简单,除了我们要显示透明效果的彩色图像外,还要用到它的黑白色的掩图.

  以下是要正确显示我们的这种效果的条件:第一,彩色的图像的我们要显示为透明的地方要都是黑色的.其次,掩图中我们要显示为透明效果的地方要都是白的,并在其它的地方都是黑色的.彩色图像和它的掩图在例图中的最左边显示.

BitBlt操作

  我们怎么样做到透明效果的?首先我们将BitBlt()的最后一个参数设为SRCAND来显示掩图,然后再把其设为SRCPAINT用BitBlt()来显示彩图.结果就是我们想要透明的地方并沒有改变而其它的地方如通常一样显示.

    SelectObject(hdcMem, g_hbmMask);
    BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCAND);

    SelectObject(hdcMem, g_hbmBall);
    BitBlt(hdc, 0, bm.bmHeight, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCPAINT);

  相当简单吧?有点幸运,但是有个问题...掩图从哪里来?大致上有两个方法来获取掩图...

·就用你创建彩图的绘图工具来自己创建,当你使用得不多的话这样做也是合理的.这样的话你就把你创建的掩图加入资源并用LoadBitmap()来装入它.

·在你程序运行的时候来创建,把你原来的图片中的一种顏色选为''透明''色,创建一个这种顏色存在的地方为白色的,其它的地方都为黑色的图片.

  因为第一种方法沒有什么可以说的,如果你愿意的话就可以採取这种方法.第二种牵涉一些BitBlt()的技巧,所以我来演示一下这种方法.

创建掩图

  最简单的方法,就是遍历彩图中的每个像素,检查它的值并把相应的像素设为掩图中的黑或白...但是SetPixel()绘图起来很慢,并且这也是不太实际.

  用BitBlt()来把彩图转为黑白色则有效率得多.如果你用BitBlt()带上SRCCOPY参数把保存有一个彩图的HDC写向一个保存有一个黑白图片的HDC,它就会检查彩图中设为背景色的顏色,并把这种顏色的像素都设为白,任何不是背景色的其它像素都会设为黑色.

  这对我们来说是个很好的功能,因为我们需要做的就是把我们彩图中要透明效果的顏色指定为背景色,并把彩图用BitBlt()从彩图绘向掩图就行了.注意这只能对单色的掩图有效(黑和白)...就是说每个像素的顏色深度为1个bit的位图.如果你对一个只有黑白像素的彩图这样操作,但是位图的深度大于1 个bit(比如16或是24bit)就不行.

  记住上面的我们要成功的首要条件沒有?就是我们要透明效果的地方都要是黑色的.因为我们用于此例的位图已经满足这个条件了,不需要再来怎么操作了,但是如果你把这里的代码用于另外一个位图相使一个不同的顏色为透明效果(一般是粉红色)那我们就要进行第二步的操作,就是用我们刚刚创建的掩图来把原图变一点,以使我们要透明效果的地方都为黑色.別的什么地方也是黑色也不要紧,因为掩图上它们不是白色的,它们就不会为透明的.我们可以用BitBlt()带上SRCINVERT参数来把新的掩图写向原彩图,把它的掩图中为白色的地方设为黑色.

  这里可能有点复杂,所以就用一个小函数来为我们完成这个操作,如下面:

HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent)
{
    HDC hdcMem, hdcMem2;
    HBITMAP hbmMask;
    BITMAP bm;

    // Create monochrome (1 bit) mask bitmap.  

    GetObject(hbmColour, sizeof(BITMAP), &bm);
    hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);

    // Get some HDCs that are compatible with the display driver

    hdcMem = CreateCompatibleDC(0);
    hdcMem2 = CreateCompatibleDC(0);

    SelectBitmap(hdcMem, hbmColour);
    SelectBitmap(hdcMem2, hbmMask);

    // Set the background colour of the colour image to the colour
    // you want to be transparent.
    SetBkColor(hdcMem, crTransparent);

    // Copy the bits from the colour image to the B+W mask... everything
    // with the background colour ends up white while everythig else ends up
    // black...Just what we wanted.

    BitBlt(hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);

    // Take our new mask and use it to turn the transparent colour in our
    // original colour image to black so the transparency effect will
    // work right.
    BitBlt(hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem2, 0, 0, SRCINVERT);

    // Clean up.

    DeleteDC(hdcMem);
    DeleteDC(hdcMem2);

    return hbmMask;
}

注意:这个函数用了SelectObject()来暂时地把我们传向一个HDC的位图选入.一个位图不能同时被选入多个HDC,所以要确认这个位图在你调用这个函数的时候沒有被选入另外一个HDC,否则就会出错.现在有了这个可爱的小函数,我们就装入一个原图的时候立马上就可以创建一个掩图了:

    case WM_CREATE:
        g_hbmBall = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BALL));
        if(g_hbmBall == NULL)
            MessageBox(hwnd, "Could not load IDB_BALL!", "Error", MB_OK | MB_ICONEXCLAMATION);

        g_hbmMask = CreateBitmapMask(g_hbmBall, RGB(0, 0, 0));
        if(g_hbmMask == NULL)
            MessageBox(hwnd, "Could not create mask!", "Error", MB_OK | MB_ICONEXCLAMATION);
    break;

  第二个参数当然是我们要在原图中显示为透明效果的顏色,这个地方为黑色.

这些是怎么工作的啊?

  ...你可能会问这句话.如果你编了这么久的C或是C++程序的话,你应该熟悉这些二进制的操作如OR,XOR,AND,NOT等等.我不打算完全讲解这些东西,但是我要说说我怎样在这个例子中用它们的.如果我的解释不夠清楚(好像就是这样的),去复习一下二进制操作的知识可能就明白了.理解不是现在使用它们的必要条件,你如果想完全信任它们你也可以略过理解这一步.

SRCAND

  BitBlt()的SRCAND光柵操作或称为ROP代码意味著用AND来组合位.就是说:只有源和目的的位都是1结果才为1.我们用这个操作来把我们的彩图中的那些有顏色部分在掩图中都设为黑色.我们要顏色的地方在掩图中都是黑色(在二进制上都为0),然后掩图中所有的为黑色的像素在结果中被设为0,所以也是黑色.所有用AND1操作的值都不会受影响,如果原来是1就得出1,原来是0就得出0 ...所以我们的掩图中为白色的部分在BitBlt()调用之后完全沒有被影响. 结果就是样例图片中的右上角的那个图像.

SRCPAINT

  SRCPAINT是用的OR操作,所以如果两者之一或两者都为1,则结果就为1. 我们对彩图进行这个操作. 当我们的彩图的黑色部分(透明部分)用OR来跟目的数据组合,结果就是沒有影响,因为任何值跟0进行OR运算的数都会不受影响.

  但是,我们彩图的其它部分不是黑色的,如果目的数据也不是黑色的,我们就得到源与目的色彩的混合,结果就是我们样例图片第二行的第二个球.这就是我们首先要用掩图把我们要留为彩色的地方设为黑色的全部理由,所以我们用OR来操作彩图时,彩色的像素不会跟任何其它的像素混合.

SRCINVERT

  这是用来把我们源彩图中的要透明效果的地方设为黑色的XOR操作(如果不是黑色的话).用掩图中的黑像素与目的像素组合对目的像素不会有任何影响,而用掩图中的白色像素(我们设一个特定的背景色来生成的)与背景色的像素来组合就会把它取消掉,把它变为黑色.

  这就是一点关于彩图与单色的操作把戏,把我的头想得都有点痛了,但是怎么说还是有它的意义的...说真的.

  在与章配合的bmp_two工程中的样例代码包含了可以绘出本章开始的样例图片的程序.先是用SRCCOPY参数把掩图与原彩图原样画出来,再分別用SRCAND和SRCPAINT对两个图片操作,最后把它们合成最终结果.

  本范例中把背景色设为灰色以使透明效果更明显,要是用白色或是黑色作背景色的话就很难看出来是否真的起了效果.

Copyright © 1998-2008, Brook Miles (forgey). All rights reserved.