详解用BitBlt实现透明贴图

虽然用GDI+可以画GIF或PNG格式的图片,但多知道点总没坏处。VC带了gdiplus.h和gdiplus.h.lib

 足球的背景是蓝色,不是黑色。

下面的代码片段来自印刷棋盘工具(Ellipse毛刺感很强),但注释拿足球做例子:

void TransBitBlt(HDC hdc, int x, int y, int w, int h, HDC hdcSrc, UINT color)
{ 
  // hdcSrc里已经放好足球在蓝色背景上的图片
  // BitBlt(hdc, x, y, w, h, hdcSrc, 0, 0, SRCCOPY); return; // 不透明方式只BitBlt一次,明显快
  CDCWithBmp image; image.CreateBmp(w, h);
  BitBlt(image, 0, 0, w, h, hdcSrc, 0, 0, SRCCOPY); // image里将会是背景透明的足球
  CDCWithBmp mask; mask.CreateMonoBmp(w, h); // 单色位图做mask
  SetBkColor(image, color); BitBlt(mask, 0, 0, w, h, image, 0, 0, SRCCOPY); // mask做好了,but how?
  // 单色位图每个像素都是0或1, 用一个bit表示。
  // 彩色位图转为单色位图时: bit = (color == BkColor ? 1 : 0);
  // 单色转彩色时: UINT24 color_table[2] = {TextColor, BkColor}; color = color_table[bit];
  // 于是mask里蓝色的背景变成1,足球变成0
  static const COLORREF ZEROS = RGB(0, 0, 0), ONES = RGB(255, 255, 255); // 注意复数s
  // 把image变成为背景为0,足球不变
  SetBkColor(image, ZEROS),SetTextColor(image, ONES); BitBlt(image, 0, 0, w, h, mask, 0, 0, SRCAND);
  // 在草地上挖出个足球形的黑洞
  SetBkColor(hdc, ONES),SetTextColor(hdc, ZEROS); BitBlt(hdc, x, y, w, h, mask, 0, 0, SRCAND);
  BitBlt(hdc, x, y, w, h, image, 0, 0, SRCPAINT); // SRCPAINT就是OR
}

// DC with bmp是馊主意吗? There is a theoretical limit of 65,536 GDI handles per session.
// 本想virtual public CDC和CBitmap,但:
// class CDC : public CObject
// class CBitmap : public CGdiObject
// class CGdiObject : public CObject
struct CDCWithBmp : public CDC {
  CBitmap m_bmp; HGDIOBJ m_oldbmp;
  CDCWithBmp() : m_oldbmp(0) {}
  ~CDCWithBmp() { if (m_hDC && m_oldbmp) SelectObject(m_oldbmp); }
  void CreateBmp(int w, int h) { CreateDC(); m_bmp.CreateCompatibleBitmap(ScreenDC(), w, h); m_oldbmp = SelectObject(m_bmp); }
  void CreateMonoBmp(int w, int h) { CreateDC(); m_bmp.CreateBitmap(w, h, 1, 1, NULL); m_oldbmp = SelectObject(m_bmp); }
  void AttachBmp(PVOID hbmp) { CreateDC(); m_bmp.Attach(hbmp); m_oldbmp = SelectObject(m_bmp); }
  PVOID DetachBmp() { return m_bmp.Detach(); } // 之后别忘了CreateBmp或AttachBmp
  void CreateDC() { if (!m_hDC) CreateCompatibleDC(ScreenDC()); }
  operator HDC () { return m_hDC; }
  static CDC* ScreenDC() { return CDC::FromHandle(GetDC(0)); }
};

如果足球图片用黑色而不是蓝色做透明色,那么足球上的黑块也透明了。所以透明色要选一种罕用的颜色。现在主流是绿幕。

如果一张图片只有两种颜色,那么每个像素都可以用1个bit表示。这是bitmap这个名字的来历。早期内存非常贵,有图就不错了。这种二值图像,可以把0显示为黑色RGB(0, 0, 0),1显示为白色RGB(255, 255, 255),也可以0显示为RGB(0, 255, 0)等等。即先用0或1查颜色表(或者叫palette调色板)。颜色“丰富”的图片,可以每个像素用4位表示,能显示16种颜色,具体啥颜色还是查表。到了24位,就可以不查表了,RGB各用8位表示。

灰度图像与二值图像不同。灰度图像可以有各种亮度的灰色,与“五彩斑斓的黑”不同,这个没毛病。

BitBlt是Bit Block Transfer的缩写,就是把一个矩形内的图片数据复制到另外一个矩形。我有破电脑后遗症,倾向于苦思冥想如何优化,其实BitBlt的软件实现可能就是for循环里if和赋值. 高速一点点是memcpy,which可用rep movsb (repeat move string byte), SIMD(单指令多数据),最快还是硬件,这个虽然我不会,但对会者来说很简单。汇编里的move是copy,源不变。C里的memmove也不修改源。RISC-V Vector扩展可以用vsetvl指令在运行时调整元素数据宽度等[链接]。

BitBlt彩色转单色时估计真是for循环里if(而且写得不咋样)。画32个36x36的,每次都全部重来,明显比mask只在初始化时生成慢一点。

H代表Handle. DC是device context(设备上下文)的缩写。设备就是显卡。VGA是640x480, 每个像素16种颜色,如何把一张8位颜色深度(color depth)的图片显示出来?24位的呢?这个我懒得想,只知道CreateBitmap和CreateCompatibleBitmap()都返回HBITMAP, 后者和DC兼容(格式一样),BitBlt时不用转换,速度快。

这个洋网站引用的是CSDN,哈哈。

CreateCompatibleBitmap | CreateBitmap... The CreateBitmap function creates a device-dependent bitmap... for performance reasons applications should use CreateBitmap to create monochrome bitmaps and CreateCompatibleBitmap to create color bitmaps... If the bitmap is monochrome, zeros represent the foreground color and ones represent the background color for the destination device context.

Windows supports two types of bitmap: device-independent (DIB) and device-dependent(DDB). A device-independent bitmap is an external format, which allows bitmaps to be moved from one device to another. Device-dependent bitmaps are designed and optimised for use with a specific device (device-dependent) and hence are unsuitable for use in an environment for which they were not created. [linkCreateDIBSection | CreateDIBitmap | SetDIBitsToDevice | StretchDIBits

Device-Dependent Bitmaps... a DDB is often called a compatible bitmap and it usually has better GDI performance than a DIB. 结合前文,CreateBitmap和CreateColorfulBitmap,或者CreateBitmap和CreateMonoBitbitmap不好吗?

BitBlt的参数,有了SRCAND,再有SRCOR天经地义,结果来了个SRCPAINT不知所云。为啥没有TransparentBlt这个函数,或者在BitBlt的文档里当例子给出?Richard Stallman才有真正的互联网精神。卖菜和卖机器人编程不是互联网精神。免费教小孩写下4子棋和象棋的程序(写好注释)可能算。:-)

如何查看颜色的RGB值?啥软件都没有的话,可以用系统自带的画图。用吸管工具在点上点一下,再点“编辑颜色”。

还有,there are two types of DDBs: discardable and nondiscardable. A discardable DDB is a bitmap that the system discards if the bitmap is not selected into a DC and if system memory is low. The CreateDiscardableBitmap function creates discardable bitmaps. The CreateBitmap, CreateCompatibleBitmap, and CreateBitmapIndirect functions create nondiscardable bitmaps.

最后,.BMP文件用的是DIB. 以前有RGB 5:5:5的显卡(不是金龙鱼,which不是国货),是RGB各用5位,合起来15位空一位不用,每个像素占用两个字节。以位为边界复制矩形块极其反人类(C和汇编)。

posted @ 2022-12-04 18:55  Fun_with_Words  阅读(194)  评论(0编辑  收藏  举报









 张牌。