我需要将 HCURSOR
存储在 BufferedImage
及其真实大小和颜色中。
我发现了类似的问题1和 2这与标准 32x32 光标一起工作正常,但如果我更改颜色或大小,则 BufferedImage 变得无效,给我这样的结果:
首先,我的问题是获得一个真实的光标大小。但后来我找到了通过 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();
我对您的代码进行了以下更改,以获得尺寸合适的丑陋像素化版本:
- 将方法参数
width
和height
更改为w
和h
:getImageByHICON(final int w , final int h, final WinDef.HICON hicon)
- 在 try block 的开头,设置
int width = 32
和int 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 窗口中看到了这个:
该尺寸与我的鼠标光标匹配。像素,没那么多。
另一个选项,灵感来自 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;
这是给了我这个:
还有一个选项,在您的 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()));
给出这个:
关于java - 在 Java 中将 HCURSOR 保存到 BufferedImage,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63442094/