Simpan HCURSOR ke BufferedImage di Java
Aku perlu untuk menyimpan HCURSOR
di BufferedImage
dengan ukuran dan warna yang sebenarnya .
Saya telah menemukan pertanyaan serupa 1 dan 2 yang berfungsi dengan baik dengan kursor standar 32x32, tetapi jika saya mengubah warna atau ukuran maka BufferedImage menjadi tidak valid, memberi saya hasil seperti ini:

Pertama, masalah saya adalah mendapatkan ukuran kursor yang sebenarnya. Tapi kemudian saya menemukan cara untuk mendapatkannya melalui JNA dari registri.
Maka saya harus menyimpannya ke BufferedImage
. Saya mencoba menggunakan cuplikan kode getImageByHICON()
dan getIcon()
dari tautan pertama di atas, tetapi ada kesalahan di suatu tempat - gambar masih salah atau rusak. Mungkin saya tidak mengerti bagaimana menggunakannya dengan benar karena saya tidak begitu paham dengan BufferedImage
penciptaan.
Bagaimana saya bisa menyimpan HCURSOR
ke BufferedImage
jika saya memiliki ukuran kursor sebenarnya dan CURSORINFO
?
Ini kode lengkap saya:
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);
}
}
}
Jawaban
Saya awalnya menjawab pertanyaan ini menyarankan agar Anda menggunakan fungsi GetSystemMetrics () , menggunakan konstanta SM_CXCURSOR
(13) untuk lebar kursor dalam piksel, dan SM_CYCURSOR
(14) untuk tinggi. Dokumentasi tertaut menyatakan "Sistem tidak dapat membuat kursor dengan ukuran lain."
Tapi kemudian saya melihat Anda memposting pertanyaan serupa di sini , dan menyatakan bahwa nilai-nilai itu tidak berubah dari 32x32. Apa yang terjadi di sana, sebagaimana dicatat dalam jawaban ini , adalah bahwa kursor sebenarnya masih berukuran itu, tetapi hanya gambar yang lebih kecil yang ditampilkan di layar; piksel lainnya hanya "tidak terlihat". Hal yang sama tampaknya berlaku untuk gambar yang "lebih besar", di dalamnya "ikon" yang terkait dengan kursor masih berukuran 32x32, tetapi layar menampilkan sesuatu yang lain.
Menariknya, ikon saat melayang di atas jendela Swing selalu berukuran 32x32. Pilihan Anda untuk digunakan Cursor c = toolkit.createCustomCursor(image, pointerPos, "cursorname");
adalah memperkecil gambar bitmap ke kursor baru (lebih kecil) di jendela. Saya dapat menyimpan kursor default dengan sederhana:
Cursor c = Cursor.getDefaultCursor();
Saya membuat perubahan berikut pada kode Anda untuk mendapatkan versi berpiksel jelek dengan ukuran yang tepat:
- mengubah argumen metode
width
danheight
menjadiw
danh
:getImageByHICON(final int w, final int h, final WinDef.HICON hicon)
- di awal blok coba, setel
int width = 32
danint height = 32
. - setelah mengambil
colorBitsMem
dariGetDIBits()
disisipkan berikut ini:
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];
}
}
Jadi dengan ikon sistem 64x64, saya melihat ini di jendela ayun:

Ukuran itu cocok dengan kursor mouse saya. Pikselnya, tidak terlalu banyak.
Opsi lain, terinspirasi oleh jawaban ini adalah menggunakan skala bitmap yang lebih baik daripada matematika integer sederhana saya dengan piksel. Dalam getCursor()
metode Anda , lakukan:
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;
Yang memberi saya ini:

Namun opsi lain, di getCursor()
kelas Anda adalah 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()));
Memberikan ini:
