Java में HCURSOR को बफर्डमैजेज में सेव करें

Aug 17 2020

मैं स्टोर करने के लिए की जरूरत HCURSORमें BufferedImage अपनी असली आकार और रंग के साथ

मुझे इसी तरह के सवाल 1 और 2 मिले हैं जो मानक 32x32 कर्सर के साथ ठीक काम करते हैं, लेकिन अगर मैं रंग या आकार बदलता हूं, तो BufferedImage अमान्य हो जाता है, जिससे मुझे इस तरह का परिणाम मिलता है:

सबसे पहले, मेरी समस्या एक वास्तविक कर्सर आकार प्राप्त करना था। लेकिन फिर मुझे रजिस्ट्री से जेएनए के माध्यम से इसे प्राप्त करने का तरीका मिला।

फिर मुझे इसे बचाने की जरूरत है 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);
        }
    }
}

जवाब

1 DanielWiddis Aug 17 2020 at 09:46

मैंने मूल रूप से इस सवाल का जवाब दिया कि आप GetSystemMetrics () फ़ंक्शन का उपयोग करते हैं SM_CXCURSOR, पिक्सेल में कर्सर की चौड़ाई के लिए निरंतर (13) और SM_CYCURSORऊंचाई के लिए (14) का उपयोग करते हैं। लिंक किए गए प्रलेखन में कहा गया है "सिस्टम अन्य आकारों के कर्सर नहीं बना सकता है।"

लेकिन फिर मैं आपको यहां एक समान प्रश्न पोस्ट करता हूं , और कहा कि वे मान 32x32 से नहीं बदलते हैं। वहाँ क्या होता है, जैसा कि इस उत्तर में उल्लेख किया गया है , यह है कि कर्सर अभी भी वास्तव में उस आकार का है, लेकिन स्क्रीन पर केवल छोटी छवि प्रदर्शित होती है; बाकी पिक्सेल केवल "अदृश्य" हैं। वही "बड़ी" छवियों के लिए सही प्रतीत होता है, आंतरिक रूप से कर्सर से जुड़ा "आइकन" अभी भी वही 32x32 आकार का है, लेकिन स्क्रीन कुछ और प्रदर्शित करता है।

दिलचस्प बात यह है कि स्विंग विंडो के ऊपर मंडराते समय आइकन हमेशा 32x32 होता है। आपकी पसंद का उपयोग Cursor c = toolkit.createCustomCursor(image, pointerPos, "cursorname");विंडो में बिटमैप छवि को एक नए (छोटे) कर्सर तक बढ़ा रहा है। मैं एक सरल के साथ डिफ़ॉल्ट कर्सर रख सकते हैं:

Cursor c = Cursor.getDefaultCursor();

मैंने सही आकार में एक बदसूरत पिक्सेलेटेड संस्करण प्राप्त करने के लिए आपके कोड में निम्नलिखित परिवर्तन किए हैं:

  • परिवर्तित विधि तर्क 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()));

यह देता है: