Lưu HCURSOR vào BufferedImage trong Java
Tôi cần phải lưu trữ HCURSORtrong BufferedImage với kích thước thực sự của nó và màu sắc .
Tôi đã tìm thấy các câu hỏi tương tự 1 và 2 hoạt động tốt với con trỏ 32x32 tiêu chuẩn, nhưng nếu tôi thay đổi màu sắc hoặc kích thước thì BufferedImage trở nên không hợp lệ, cho tôi kết quả như sau:
Đầu tiên, vấn đề của tôi là có được kích thước con trỏ thực. Nhưng sau đó tôi đã tìm ra cách để lấy nó thông qua JNA từ sổ đăng ký.
Sau đó, tôi cần phải lưu nó vào BufferedImage. Tôi đã cố gắng sử dụng các đoạn mã getImageByHICON()và getIcon()từ liên kết đầu tiên ở trên, nhưng có lỗi ở đâu đó - hình ảnh vẫn không chính xác hoặc bị hỏng. Có lẽ tôi không hiểu cách sử dụng nó một cách chính xác vì tôi không quen với BufferedImageviệc tạo ra.
Làm cách nào HCURSORđể lưu vào BufferedImagenếu tôi có kích thước con trỏ thực và CURSORINFO?
Đây là mã đầy đủ của tôi:
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);
}
}
}
Trả lời
Ban đầu tôi đã trả lời câu hỏi này, đề xuất rằng bạn sử dụng hàm GetSystemMetrics () , sử dụng hằng số SM_CXCURSOR(13) cho chiều rộng của con trỏ tính bằng pixel và SM_CYCURSOR(14) cho chiều cao. Tài liệu được liên kết cho biết "Hệ thống không thể tạo con trỏ có kích thước khác."
Nhưng sau đó tôi thấy bạn đã đăng một câu hỏi tương tự ở đây và nói rằng các giá trị đó không thay đổi từ 32x32. Điều gì xảy ra ở đó, như đã nói trong câu trả lời này , là con trỏ thực sự vẫn có kích thước đó, nhưng chỉ hình ảnh nhỏ hơn được hiển thị trên màn hình; phần còn lại của các pixel chỉ đơn giản là "vô hình". Điều này có vẻ đúng đối với hình ảnh "lớn hơn", trong đó "biểu tượng" được kết hợp với con trỏ vẫn có cùng kích thước 32x32, nhưng màn hình hiển thị một cái gì đó khác.
Điều thú vị là biểu tượng khi di chuột qua cửa sổ Swing luôn có kích thước 32x32. Lựa chọn của bạn để sử dụng Cursor c = toolkit.createCustomCursor(image, pointerPos, "cursorname");là thu nhỏ hình ảnh bitmap thành một con trỏ mới (nhỏ hơn) trong cửa sổ. Tôi có thể giữ con trỏ mặc định bằng cách đơn giản:
Cursor c = Cursor.getDefaultCursor();
Tôi đã thực hiện các thay đổi sau đối với mã của bạn để có được phiên bản pixellated xấu xí ở kích thước phù hợp:
- đã thay đổi đối số phương thức
widthvàheightthànhwvàh:getImageByHICON(final int w, final int h, final WinDef.HICON hicon) - khi bắt đầu khối thử, hãy đặt
int width = 32vàint height = 32. - sau khi tìm nạp
colorBitsMemtừGetDIBits()được chèn như sau:
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];
}
}
Vì vậy, với biểu tượng hệ thống 64x64, tôi thấy điều này trong cửa sổ xoay:
Kích thước đó phù hợp với con trỏ chuột của tôi. Các pixel, không nhiều.
Một lựa chọn khác, lấy cảm hứng từ câu trả lời này là sử dụng tỷ lệ bitmap tốt hơn so với phép toán số nguyên đơn giản của tôi với pixel. Trong getCursor()phương pháp của bạn , hãy làm:
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;
Điều đó mang lại cho tôi điều này:
Tuy nhiên, một tùy chọn khác, trong getCursor()lớp của bạn là 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()));
Cung cấp cái này: