HCURSOR'u Java'da BufferedImage'a Kaydet

Aug 17 2020

Ben saklamak gerek HCURSORde BufferedImage gerçek boyut ve renk ile .

Standart 32x32 imleci ile sorunsuz çalışan benzer sorular 1 ve 2 buldum , ancak rengi veya boyutu değiştirirsem BufferedImage geçersiz hale gelir ve bana şöyle bir sonuç verir:

Öncelikle benim sorunum gerçek bir imleç boyutu elde etmekti. Ama sonra bunu kayıt defterinden JNA aracılığıyla almanın yolunu buldum.

O halde onu kaydetmem gerek BufferedImage. Ben kullanım kod snippet'ından çalıştı getImageByHICON()ve getIcon()yukarıdaki ilk bağlantısından, ancak bir hata yerde var - görüntü hala yanlış veya kırık olduğunu. Belki de onu nasıl doğru kullanacağımı anlamıyorum çünkü BufferedImageyaratıma pek aşina değilim .

Nasıl kaydedebilirsiniz HCURSORiçin BufferedImageben imleci vardır gerçek boyut ve eğer CURSORINFO?

İşte tam kodum:

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);
        }
    }
}

Yanıtlar

1 DanielWiddis Aug 17 2020 at 09:46

Başlangıçta bu soruyu, GetSystemMetrics () işlevini, SM_CXCURSORimlecin piksel cinsinden genişliği için sabiti (13) SM_CYCURSORve yükseklik için (14) kullanarak kullanmanızı önererek yanıtladım . Bağlantılı dokümantasyon "Sistem, diğer boyutlarda imleçler oluşturamaz."

Ama sonra burada benzer bir soru sorduğunuzu görüyorum ve bu değerlerin 32x32'den değişmediğini belirttiniz. Burada olan şey, bu yanıtta belirtildiği gibi , imlecin hala aynı boyutta olması, ancak ekranda yalnızca daha küçük olan görüntünün gösterilmesidir; piksellerin geri kalanı basitçe "görünmez" dir. Aynı durum "daha büyük" görüntüler için de geçerli gibi görünmektedir, çünkü dahili olarak imleçle ilişkili "simge" hala 32x32 boyutundadır, ancak ekran başka bir şey görüntüler.

İlginç bir şekilde, Swing penceresinin üzerine gelindiğinde simge her zaman 32x32'dir. Kullanma seçiminiz Cursor c = toolkit.createCustomCursor(image, pointerPos, "cursorname");, bitmap görüntüsünü pencerede yeni (daha küçük) bir imlece ölçeklendirmektir. Varsayılan imleci basit bir şekilde tutabilirim:

Cursor c = Cursor.getDefaultCursor();

Doğru boyutta çirkin pikselli bir sürüm elde etmek için kodunuzda aşağıdaki değişiklikleri yaptım:

  • yöntem bağımsız değişkenleri değiştirildi widthve heightiçin wve h:getImageByHICON(final int w, final int h, final WinDef.HICON hicon)
  • try bloğunun başlangıcında, ayarlayın int width = 32ve int height = 32.
  • getirilirken sonra colorBitsMemgelen GetDIBits()eklenen aşağıda:
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 sistem simgesiyle bunu hareketli pencerede görüyorum:

Bu boyut fare imlecimle eşleşiyor. Pikseller, çok değil.

Bu cevaptan esinlenen başka bir seçenek, piksellerle basit tamsayı matematiğimden daha iyi bir bitmap ölçeklendirmesi kullanmaktır. Senin içinde getCursor()yöntemle yapın:

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;

Bana bunu veren:

getCursor()Sınıfınızdaki bir başka seçenek ise 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()));

Bunu verir: