บันทึก HCURSOR เป็น BufferedImage ใน Java
ฉันต้องเก็บHCURSOR
ในที่มีขนาดที่แท้จริงของมันและสีBufferedImage
ฉันพบคำถามที่คล้ายกัน1และ2ซึ่งทำงานได้ดีกับเคอร์เซอร์ 32x32 มาตรฐาน แต่ถ้าฉันเปลี่ยนสีหรือขนาดแล้ว BufferedImage จะไม่ถูกต้องทำให้ฉันได้ผลลัพธ์ดังนี้:

ประการแรกปัญหาของฉันคือการได้รับขนาดเคอร์เซอร์จริง แต่แล้วฉันก็พบวิธีรับผ่าน JNA จากรีจิสทรี
BufferedImage
แล้วฉันจะต้องบันทึกลงใน ฉันพยายามใช้ข้อมูลโค้ดgetImageByHICON()
และgetIcon()
จากลิงก์แรกด้านบน แต่มีข้อผิดพลาดเกิดขึ้น - รูปภาพยังไม่ถูกต้องหรือเสียหาย บางทีฉันอาจจะไม่เข้าใจวิธีใช้อย่างถูกต้องเพราะฉันไม่ค่อยคุ้นเคยกับBufferedImage
การสร้าง
ฉันHCURSOR
จะบันทึกได้BufferedImage
อย่างไรว่าฉันมีเคอร์เซอร์ขนาดจริงและCURSORINFO
?
นี่คือรหัสเต็มของฉัน:
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) สำหรับความสูง เอกสารที่เชื่อมโยงระบุว่า "ระบบไม่สามารถสร้างเคอร์เซอร์ขนาดอื่นได้"
แต่แล้วฉันเห็นคุณโพสต์คำถามที่คล้ายกันที่นี่และระบุว่าค่าเหล่านั้นจะไม่เปลี่ยนจาก 32x32 สิ่งที่เกิดขึ้นที่นั่นตามที่ระบุไว้ในคำตอบนี้คือเคอร์เซอร์ยังคงมีขนาดนั้นจริง ๆ แต่จะแสดงเฉพาะภาพที่เล็กกว่าบนหน้าจอ พิกเซลที่เหลือนั้น "มองไม่เห็น" ดูเหมือนว่าจะเป็นจริงสำหรับรูปภาพที่ "ใหญ่กว่า" ซึ่งภายใน "ไอคอน" ที่เชื่อมโยงกับเคอร์เซอร์จะยังคงมีขนาด 32x32 เท่าเดิม แต่หน้าจอจะแสดงอย่างอื่น
ที่น่าสนใจคือไอคอนเมื่อวางเมาส์เหนือหน้าต่างสวิงจะอยู่ที่ 32x32 เสมอ ตัวเลือกที่คุณจะใช้Cursor c = toolkit.createCustomCursor(image, pointerPos, "cursorname");
คือการปรับขนาดภาพบิตแมปลงเป็นเคอร์เซอร์ใหม่ (เล็กกว่า) ในหน้าต่าง ฉันสามารถเก็บเคอร์เซอร์เริ่มต้นด้วยวิธีง่ายๆ:
Cursor c = Cursor.getDefaultCursor();
ฉันได้ทำการเปลี่ยนแปลงต่อไปนี้กับโค้ดของคุณเพื่อให้ได้เวอร์ชัน Pixellated ที่น่าเกลียดในขนาดที่เหมาะสม:
- เปลี่ยนข้อโต้แย้งวิธีการ
width
และheight
ถึงw
และh
:getImageByHICON(final int w, final int h, final WinDef.HICON hicon)
- ที่เริ่มต้นของบล็อกลองตั้งและ
int width = 32
int height = 32
- หลังจากดึงข้อมูล
colorBitsMem
จากGetDIBits()
แทรกต่อไปนี้:
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 ฉันเห็นสิ่งนี้ในหน้าต่างสวิง:

ขนาดนั้นตรงกับเคอร์เซอร์ของเมาส์ พิกเซลไม่มาก
อีกทางเลือกหนึ่งที่ได้รับแรงบันดาลใจจากคำตอบนี้คือการใช้การปรับขนาดบิตแมปที่ดีกว่าการคำนวณจำนวนเต็มอย่างง่ายด้วยพิกเซล ใน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()));
ให้สิ่งนี้:
