实现BMP转透明背景ICON图标

 
Figure 1: Transition of a color bitmap to a cursor

Introduction

This article focuses on creating a color cursor from a HBITMAP. First it explains the steps performed by windows to display a cursor on the screen and how we can create the necessary information needed by windows to create our cursor. Then it explains the steps needed to convert a color HBITMAP to an HCURSOR. Finally it shows a utility class, which converts HBITMAP to HCURSOR.

How Windows displays a cursor?

In Windows, transparency of the cursor is achieved by the use of two masks. One is called the AND mask and the other is called the XOR mask. In order to display a cursor in the screen, the system first performs a logical AND operation on the screen with the AND mask. In this process, the pixels in the screen corresponding to the 1 bits in the AND mask remains unchanged and the pixels corresponding to the 0 bits in the AND mask becomes modified. Then the system will perform a logical XOR operation on the screen with the XOR mask. In this process, the pixels in screen corresponding to the 0 bits in the XOR mask remains unchanged and the pixels corresponding to the non 0 bits gets modified.

 
Figure 2: A sample color bitmap to be converted as a cursor

Now lets try to realize the above cursor to its AND/XOR masks so that the system can display the cursor using these masks. First let us create the AND mask. The above cursor contains a red colored rectangle in the center. So all the other pixels should be transparent. Assuming that the size of the cursor is 8*8 and the size of the rectangle is 4*4 we shall define the AND mask as shown below.

1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 0 0 0 0 1 1
1 1 0 0 0 0 1 1
1 1 0 0 0 0 1 1
1 1 0 0 0 0 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
Figure 3: AND mask for the sample color bitmap in Figure 2

In the above AND mask, the bits corresponding to the red rectangle are 0 and the rest of the bits are 1. This is because, we need only the red rectangle to be displayed as the cursor and the rest of the area should be transparent. When the system performs a logical AND operation of this mask to the screen, the pixels in the screen corresponding to the red rectangle becomes modified and the rest remains unchanged.

Now let us create the XOR mask for our cursor. As we need to display the red rectangle as cursor on the screen and the rest as transparent, we need to make the bits in the XOR mask corresponding to the red rectangle as Red (RGB (255,0,0)) and the rest as 0.

0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 R R R R 0 0
0 0 R R R R 0 0
0 0 R R R R 0 0
0 0 R R R R 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
Figure 4: XOR mask for the sample color bitmap in Figure 2

The R in the above XOR mask represents RGB (255,0,0). I.e., red color. When the system performs logical XOR of this XOR mask to the screen, the R pixels are updated in the screen and the pixels corresponding to the 0 bits remains unchanged.

So finally, after performing logical AND of the AND mask followed by logical XOR of the XOR mask to the screen, the screen under our cursor region look like as shown below.

S S S S S S S S
S S S S S S S S
S S R R R R S S
S S R R R R S S
S S R R R R S S
S S R R R R S S
S S S S S S S S
S S S S S S S S
Figure 5: State of the screen area under the cursor after applying AND and XOR masks

Where the S represents the original screen pixels and the R represents the red color pixels.

Converting HBITMAP to HCURSOR

Now let us try to create these AND/XOR masks from an HBITMAP. The following code fragment will do it for you.

void CColorCursor::GetMaskBitmaps(HBITMAP hSourceBitmap, 
              COLORREF clrTransparent, 
              HBITMAP &hAndMaskBitmap, 
              HBITMAP &hXorMaskBitmap)
{
  HDC hDC        = ::GetDC(NULL);
  HDC hMainDC      = ::CreateCompatibleDC(hDC); 
  HDC hAndMaskDC      = ::CreateCompatibleDC(hDC); 
  HDC hXorMaskDC      = ::CreateCompatibleDC(hDC); 

  //Get the dimensions of the source bitmap
  BITMAP bm;
  ::GetObject(hSourceBitmap,sizeof(BITMAP),&bm);

  
  hAndMaskBitmap  = ::CreateCompatibleBitmap(hDC,bm.bmWidth,bm.bmHeight);
  hXorMaskBitmap  = ::CreateCompatibleBitmap(hDC,bm.bmWidth,bm.bmHeight);

  //Select the bitmaps to DC
  HBITMAP hOldMainBitmap = (HBITMAP)::SelectObject(hMainDC,hSourceBitmap);
  HBITMAP hOldAndMaskBitmap  = (HBITMAP)::SelectObject(hAndMaskDC,
    hAndMaskBitmap);
  HBITMAP hOldXorMaskBitmap  = (HBITMAP)::SelectObject(hXorMaskDC,
    hXorMaskBitmap);

  //Scan each pixel of the souce bitmap and create the masks
  COLORREF MainBitPixel;
  for(int x=0;x<bm.bmWidth;++x)
  {
    for(int y=0;y<bm.bmHeight;++y)
    {
      MainBitPixel = ::GetPixel(hMainDC,x,y);
      if(MainBitPixel == clrTransparent)
      {
        ::SetPixel(hAndMaskDC,x,y,RGB(255,255,255));
        ::SetPixel(hXorMaskDC,x,y,RGB(0,0,0));
      }
      else
      {
        ::SetPixel(hAndMaskDC,x,y,RGB(0,0,0));
        ::SetPixel(hXorMaskDC,x,y,MainBitPixel);
      }
    }
  }
  
  ::SelectObject(hMainDC,hOldMainBitmap);
  ::SelectObject(hAndMaskDC,hOldAndMaskBitmap);
  ::SelectObject(hXorMaskDC,hOldXorMaskBitmap);

  ::DeleteDC(hXorMaskDC);
  ::DeleteDC(hAndMaskDC);
  ::DeleteDC(hMainDC);

  ::ReleaseDC(NULL,hDC);
}

The above code creates two memory DC and two memory bitmaps for the AND/XOR masks. Then it examines the source bitmap pixels and creates the masks as we have explained in the theory part.

Now what we need is to use these masks and create a cursor using the well known CreateIconIndirect() SDK call as shown below.

ICONINFO iconinfo = {0};
iconinfo.fIcon        = FALSE;
iconinfo.xHotspot       = 0;
iconinfo.yHotspot       = 0;
iconinfo.hbmMask        = hAndMask;
iconinfo.hbmColor       = hXorMask;

HCURSOR hCursor = ::CreateIconIndirect(&iconinfo);

That's it. We have successfully created a color cursor from a bitmap.

Using the code

It is always better to create a utility class for doing these things for us. So I created one called CColorCursorhaving the following interfaces.

static void GetMaskBitmaps(HBITMAP hSourceBitmap,COLORREF clrTransparent,
         HBITMAP &hAndMaskBitmap,HBITMAP &hXorMaskBitmap);
static HCURSOR CreateCursorFromBitmap(HBITMAP hSourceBitmap,COLORREF 
         clrTransparent, DWORD   xHotspot,DWORD   yHotspot);

The first interface is called from the second one to create the masks. The first one is also made public because we can use it to get an idea of what is happening inside. I used the first interface in my test application to display the AND/XOR mask as shown in the first figure and the second one to create cursor directly.

Now we are approaching towards the end of this article. I will finish it by showing the usage of this utility class.

#include "ColorCursor.h"

....

HBITMAP hSourceBitmap  = c.
HCURSOR hCursor = CColorCursor::CreateCursorFromBitmap(
    hSourceBitmap,RGB(0,0,0),0,0);

A word of caution

The utility class explained above will try to create the cursor in the same size of the input source bitmap. But in Windows, there are some limitations for the size of the cursor. So it is always safe to pass bitmaps having standard size. Otherwise the result may be unpredictable.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将Java中的BMP换为PNG图并将背景透明,你可以使用Java图像处理库来实现。下面是一个大致的步骤: 1. 使用Java的图像处理库导入BMP图像。 2. 创建一个新的BufferedImage对象,并将BMP图像绘制到该对象上。 3. 创建一个新的BufferedImage对象,设置其类型为BufferedImage.TYPE_INT_ARGB,并设置宽度和高度与BMP图像一致。 4. 获取BMP图像的像素数据。 5. 遍历每个像素点,检查其RGB值是否与背景颜色相匹配。如果是背景颜色,则将该像素点的alpha值设置为0(透明)。 6. 将修改后的像素数据绘制到新的BufferedImage对象上。 7. 使用Java的图像处理库将新的BufferedImage对象保存为PNG图像文件。 以下是示例代码: ```java import java.awt.image.BufferedImage; import javax.imageio.ImageIO; public class BmpToPng { public static void main(String[] args) { try { // 导入BMP图像 BufferedImage bmpImage = ImageIO.read(new File("input.bmp")); // 创建新的BufferedImage对象 BufferedImage pngImage = new BufferedImage(bmpImage.getWidth(), bmpImage.getHeight(), BufferedImage.TYPE_INT_ARGB); // 获取BMP图像的像素数据 int[] rgbData = bmpImage.getRGB(0, 0, bmpImage.getWidth(), bmpImage.getHeight(), null, 0, bmpImage.getWidth()); // 背景颜色(假设为红色) int backgroundColor = 0xFFFF0000; // 遍历每个像素点 for (int i = 0; i < rgbData.length; i++) { // 检查像素颜色是否与背景颜色相匹配 if (rgbData[i] == backgroundColor) { // 设置像素的alpha值为0(透明) rgbData[i] &= 0x00FFFFFF; } } // 绘制修改后的像素数据到新的BufferedImage对象上 pngImage.setRGB(0, 0, bmpImage.getWidth(), bmpImage.getHeight(), rgbData, 0, bmpImage.getWidth()); // 保存为PNG图像文件 ImageIO.write(pngImage, "png", new File("output.png")); System.out.println("换成功!"); } catch (IOException e) { System.out.println("换失败:" + e.getMessage()); } } } ``` 请注意,你需要将代码中的"input.bmp"替换为实际的BMP图像文件路径,也可以根据需要修改背景颜色的数值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值