java - 在 Java 中将 HCURSOR 保存到 BufferedImage

标签 java winapi icons jna

我需要将 HCURSOR 存储在 BufferedImage 及其真实大小和颜色中。

我发现了类似的问题12这与标准 32x32 光标一起工作正常,但如果我更改颜色或大小,则 BufferedImage 变得无效,给我这样的结果:

enter image description here

首先,我的问题是获得一个真实的光标大小。但后来我找到了通过 JNA 从注册表中获取它的方法。

然后我需要将它保存到BufferedImage。我尝试使用上面第一个链接中的代码片段 getImageByHICON()getIcon(),但某处有错误——图像仍然不正确或已损坏。也许我不明白如何正确使用它,因为我对 BufferedImage 的创建不太熟悉。

如果我有实际大小的游标和 CURSORINFO,如何将 HCURSOR 保存到 BufferedImage

这是我的完整代码:

import com.sun.jna.Memory;
import com.sun.jna.platform.win32.*;

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;

class CursorExtractor {

    public static void main(String[] args) {

        BufferedImage image = getCursor();

        JLabel icon = new JLabel();
        icon.setIcon(new ImageIcon(image));

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(icon);
        frame.pack();
        frame.setVisible(true);

        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Point pointerPos = new Point(1, 1);
        Cursor c = toolkit.createCustomCursor(image, pointerPos, "cursorname");
        frame.setCursor(c);
    }

    public static BufferedImage getCursor() {
        // Read an int (& 0xFFFFFFFFL for large unsigned int)
        int baseSize = Advapi32Util.registryGetIntValue(
                WinReg.HKEY_CURRENT_USER, "Control Panel\\Cursors", "CursorBaseSize");

        final User32.CURSORINFO cursorinfo = new User32.CURSORINFO();
        User32.INSTANCE.GetCursorInfo(cursorinfo);
        WinDef.HCURSOR hCursor = cursorinfo.hCursor;

        return getImageByHICON(baseSize, baseSize, hCursor);
    }

    public static BufferedImage getImageByHICON(final int width, final int height, final WinDef.HICON hicon) {
        final WinGDI.ICONINFO iconinfo = new WinGDI.ICONINFO();

        try {
            // get icon information

            if (!User32.INSTANCE.GetIconInfo(hicon, iconinfo)) {
                return null;
            }
            final WinDef.HWND hwdn = new WinDef.HWND();
            final WinDef.HDC dc = User32.INSTANCE.GetDC(hwdn);

            if (dc == null) {

                return null;
            }
            try {
                final int nBits = width * height * 4;
                // final BitmapInfo bmi = new BitmapInfo(1);

                final Memory colorBitsMem = new Memory(nBits);
                // // Extract the color bitmap
                final WinGDI.BITMAPINFO bmi = new WinGDI.BITMAPINFO();

                bmi.bmiHeader.biWidth = width;
                bmi.bmiHeader.biHeight = -height;
                bmi.bmiHeader.biPlanes = 1;
                bmi.bmiHeader.biBitCount = 32;
                bmi.bmiHeader.biCompression = WinGDI.BI_RGB;
                GDI32.INSTANCE.GetDIBits(dc, iconinfo.hbmColor, 0, height, colorBitsMem, bmi, WinGDI.DIB_RGB_COLORS);
                // g32.GetDIBits(dc, iconinfo.hbmColor, 0, size, colorBitsMem,
                // bmi,
                // GDI32.DIB_RGB_COLORS);
                final int[] colorBits = colorBitsMem.getIntArray(0, width * height);

                final BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
                bi.setRGB(0, 0, width, height, colorBits, 0, height);
                return bi;
            } finally {
                com.sun.jna.platform.win32.User32.INSTANCE.ReleaseDC(hwdn, dc);
            }
        } finally {
            User32.INSTANCE.DestroyIcon(new WinDef.HICON(hicon.getPointer()));
            GDI32.INSTANCE.DeleteObject(iconinfo.hbmColor);
            GDI32.INSTANCE.DeleteObject(iconinfo.hbmMask);
        }
    }
}

最佳答案

我最初回答这个问题时建议您使用 GetSystemMetrics()函数,使用常量 SM_CXCURSOR (13) 作为光标宽度(以像素为单位),SM_CYCURSOR (14) 作为高度。链接的文档指出“系统无法创建其他大小的游标。”

但后来我看到你发了一个类似的问题here ,并声明这些值不会从 32x32 改变。那里发生了什么,as noted in this answer , 是光标实际上仍然是那个大小,但屏幕上只显示较小的图像;其余像素只是“不可见”。 “更大”的图像似乎也是如此,因为在内部与光标关联的“图标”仍然是相同的 32x32 大小,但屏幕显示其他内容。

有趣的是,将鼠标悬停在 Swing 窗口上时,图标始终为 32x32。您选择使用 Cursor c = toolkit.createCustomCursor(image, pointerPos, "cursorname"); 将位图图像缩小为窗口中的新(较小)光标。我可以通过简单的方式保留默认光标:

Cursor c = Cursor.getDefaultCursor();

我对您的代码进行了以下更改,以获得尺寸合适的丑陋像素化版本:

  • 将方法参数 widthheight 更改为 wh:getImageByHICON(final int w , final int h, final WinDef.HICON hicon)
  • 在 try block 的开头,设置 int width = 32int height = 32
  • GetDIBits() 获取 colorBitsMem 后插入以下内容:
final int[] colorBitsBase = colorBitsMem.getIntArray(0, width * height);
final int[] colorBits = new int[w * h];
for (int row = 0; row < h; row++) {
    for (int col = 0; col < w; col++) {
        int r = row * 32 / h;
        int c = col * 32 / w;
        colorBits[row * w + col] = colorBitsBase[r * 32 + c];
    }
}

所以对于 64x64 系统图标,我在 swing 窗口中看到了这个:

image of icon

该尺寸与我的鼠标光标匹配。像素,没那么多。

另一个选项,灵感来自 this answer是使用比我的像素简单整数数学更好的位图缩放。在您的 getCursor() 方法中,执行以下操作:

BufferedImage before = getImageByHICON(32, 32, hCursor);

int w = before.getWidth();
int h = before.getHeight();
BufferedImage after = new BufferedImage(baseSize, baseSize, BufferedImage.TYPE_INT_ARGB);
AffineTransform at = new AffineTransform();
at.scale(baseSize / 32d, baseSize / 32d);
AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
after = scaleOp.filter(before, after);

return after;

这是给了我这个:

enter image description here

还有一个选项,在您的 getCursor() 类中是 CopyImage() .

WinDef.HCURSOR hCursor = cursorinfo.hCursor;
HANDLE foo = User32.INSTANCE.CopyImage(hCursor, 2, baseSize, baseSize, 0x00004000);
return getImageByHICON(baseSize, baseSize, new WinDef.HCURSOR(foo.getPointer()));

给出这个:

image of icon

关于java - 在 Java 中将 HCURSOR 保存到 BufferedImage,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63442094/

相关文章:

Java Swing JTable 和 JScrollPane 适合列宽

winapi - 扫描电脑内存

java - 无法将图标的图像从 JLabel 传输到另一个

windows - VB6中多分辨率图标的使用

java - 运行 Flutter 应用时出现 "Could not connect to the Gradle daemon"和 IP 地址问题

java - Netty:如何处理ChannelOutboundHandler写入中未履行的Promise

java - 如何在类路径中递归搜索资源

c++ - exe导入dll序号与DLL导出函数名比较?

node.js - Node js,%1 不是有效的 Win32 应用程序,js-bson : failed to load c++ bson extension, windows

css - 如何在 ionic framework v4 和 Angular 7 中调整图标大小