Enregistrer HCURSOR dans BufferedImage en Java
Je dois stocker HCURSOR
dans BufferedImage
sa taille réelle et la couleur .
J'ai trouvé des questions similaires 1 et 2 qui fonctionnent bien avec le curseur standard 32x32, mais si je change de couleur ou de taille, BufferedImage devient invalide, me donnant un résultat comme celui-ci:
Tout d'abord, mon problème était d'obtenir une taille réelle de curseur. Mais ensuite, j'ai trouvé le moyen de l'obtenir via JNA à partir du registre.
Ensuite, je dois l'enregistrer dans BufferedImage
. J'ai essayé d'utiliser des extraits de code getImageByHICON()
et getIcon()
du premier lien ci-dessus, mais il y a une erreur quelque part - l'image est toujours incorrecte ou cassée. Peut-être que je ne comprends pas comment l'utiliser correctement car je ne suis pas très familier avec la BufferedImage
création.
Comment puis - je économiser HCURSOR
pour BufferedImage
si je curseurs taille réelle et CURSORINFO
?
Voici mon code complet:
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);
}
}
}
Réponses
J'ai initialement répondu à cette question en suggérant d'utiliser la fonction GetSystemMetrics () , en utilisant la constante SM_CXCURSOR
(13) pour la largeur du curseur en pixels, et SM_CYCURSOR
(14) pour la hauteur. La documentation liée indique «Le système ne peut pas créer de curseurs d'autres tailles».
Mais ensuite, je vois que vous avez posté une question similaire ici , et déclaré que ces valeurs ne changent pas de 32x32. Ce qui se passe là-bas, comme indiqué dans cette réponse , c'est que le curseur est toujours de cette taille, mais seule la plus petite image est affichée à l'écran; le reste des pixels est simplement "invisible". La même chose semble être vraie pour les images "plus grandes", en ce que "l'icône" associée au curseur a toujours la même taille 32x32, mais l'écran affiche autre chose.
Fait intéressant, l'icône lors du survol de la fenêtre Swing est toujours 32x32. Votre choix à utiliser Cursor c = toolkit.createCustomCursor(image, pointerPos, "cursorname");
consiste à réduire l'image bitmap à un nouveau curseur (plus petit) dans la fenêtre. Je peux garder le curseur par défaut avec un simple:
Cursor c = Cursor.getDefaultCursor();
J'ai apporté les modifications suivantes à votre code pour obtenir une vilaine version pixellisée à la bonne taille:
- modification des arguments de méthode
width
etheight
versw
eth
:getImageByHICON(final int w, final int h, final WinDef.HICON hicon)
- au début du bloc try, définissez
int width = 32
etint height = 32
. - après avoir récupéré le
colorBitsMem
deGetDIBits()
inséré ce qui suit:
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];
}
}
Donc, avec une icône système 64x64, je vois ceci dans la fenêtre pivotante:
Cette taille correspond au curseur de ma souris. Les pixels, pas tellement.
Une autre option, inspirée par cette réponse, consiste à utiliser une meilleure mise à l'échelle bitmap que mon simple calcul entier avec des pixels. Dans votre getCursor()
méthode, faites:
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;
Ce qui me donne ceci:
Encore une autre option, dans votre getCursor()
classe est 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()));
Donne ceci: