在Java中提取光标图像

7
我想知道在Java中有没有一种方法可以从Cursor对象中提取Image对象。
这样做的一个用途可能是:
```html

例如:

```
Image img = extractCursorImage(Cursor.getDefaultCursor());

然后您可以在工具栏按钮上绘制它(这就是我想要它的目的)。


我对创建一个平台无关的右侧光标(在Y轴上翻转)以进行行选择的相同事物感兴趣。它可能不会在所有情况下都完美,但肯定会很接近。 - Sam Harwell
3个回答

7

Cursor类相当抽象 - 所有重要的东西都委托给本地代码处理,因此您不能仅仅将其绘制到graphics上下文中。没有立即明显的方法来解决需要预定义图标或在本地代码中执行的问题。


你能帮我使用你提到的那个函数吗?

以下是使用JNA库绘制内置Windows光标的一些代码。如果您可以使用JNA,则可以避免使用C++编译器。

我可能会进行过多的本地调用,但是对于一次性图标生成而言,成本并不重要。

手形光标在Java中绘制 http://f.imagehost.org/0709/hand.png

显示光标作为Java图像的代码:

public class LoadCursor {

  public static void draw(BufferedImage image, int cursor,
      int diFlags) {
    int width = image.getWidth();
    int height = image.getHeight();

    User32 user32 = User32.INSTANCE;
    Gdi32 gdi32 = Gdi32.INSTANCE;

    Pointer hIcon = user32
        .LoadCursorW(Pointer.NULL, cursor);
    Pointer hdc = gdi32.CreateCompatibleDC(Pointer.NULL);
    Pointer bitmap = gdi32.CreateCompatibleBitmap(hdc,
        width, height);

    gdi32.SelectObject(hdc, bitmap);
    user32.DrawIconEx(hdc, 0, 0, hIcon, width, height, 0,
        Pointer.NULL, diFlags);

    for (int x = 0; x < width; x++) {
      for (int y = 0; y < width; y++) {
        int rgb = gdi32.GetPixel(hdc, x, y);
        image.setRGB(x, y, rgb);
      }
    }

    gdi32.DeleteObject(bitmap);
    gdi32.DeleteDC(hdc);
  }

  public static void main(String[] args) {
    final int width = 128;
    final int height = 128;

    BufferedImage image = new BufferedImage(width, height,
        BufferedImage.TYPE_INT_ARGB);
    draw(image, User32.IDC_HAND, User32.DI_NORMAL);
    BufferedImage mask = new BufferedImage(width, height,
        BufferedImage.TYPE_INT_RGB);
    draw(mask, User32.IDC_HAND, User32.DI_MASK);
    applyMask(image, mask);

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

  private static void applyMask(BufferedImage image,
      BufferedImage mask) {
    int width = image.getWidth();
    int height = image.getHeight();
    for (int x = 0; x < width; x++) {
      for (int y = 0; y < height; y++) {
        int masked = mask.getRGB(x, y);
        if ((masked & 0x00FFFFFF) == 0) {
          int rgb = image.getRGB(x, y);
          rgb = 0xFF000000 | rgb;
          image.setRGB(x, y, rgb);
        }
      }
    }
  }

}

User32.dll接口:

public interface User32 extends Library {

  public static User32 INSTANCE = (User32) Native
      .loadLibrary("User32", User32.class);

  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_ARROW = 32512;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_IBEAM = 32513;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_WAIT = 32514;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_CROSS = 32515;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_UPARROW = 32516;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_SIZENWSE = 32642;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_SIZENESW = 32643;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_SIZEWE = 32644;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_SIZENS = 32645;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_SIZEALL = 32646;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_NO = 32648;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_HAND = 32649;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_APPSTARTING = 32650;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_HELP = 32651;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_ICON = 32641;
  /** @see #LoadCursorW(Pointer, int) */
  public static final int IDC_SIZE = 32640;

  /** @see #DrawIconEx(Pointer, int, int, Pointer, int, int, int, Pointer, int) */
  public static final int DI_COMPAT = 4;
  /** @see #DrawIconEx(Pointer, int, int, Pointer, int, int, int, Pointer, int) */
  public static final int DI_DEFAULTSIZE = 8;
  /** @see #DrawIconEx(Pointer, int, int, Pointer, int, int, int, Pointer, int) */
  public static final int DI_IMAGE = 2;
  /** @see #DrawIconEx(Pointer, int, int, Pointer, int, int, int, Pointer, int) */
  public static final int DI_MASK = 1;
  /** @see #DrawIconEx(Pointer, int, int, Pointer, int, int, int, Pointer, int) */
  public static final int DI_NORMAL = 3;
  /** @see #DrawIconEx(Pointer, int, int, Pointer, int, int, int, Pointer, int) */
  public static final int DI_APPBANDING = 1;

  /** http://msdn.microsoft.com/en-us/library/ms648391(VS.85).aspx */
  public Pointer LoadCursorW(Pointer hInstance,
      int lpCursorName);

  /** http://msdn.microsoft.com/en-us/library/ms648065(VS.85).aspx */
  public boolean DrawIconEx(Pointer hdc, int xLeft,
      int yTop, Pointer hIcon, int cxWidth, int cyWidth,
      int istepIfAniCur, Pointer hbrFlickerFreeDraw,
      int diFlags);

}

Gdi32.dll接口:

public interface Gdi32 extends Library {

  public static Gdi32 INSTANCE = (Gdi32) Native
      .loadLibrary("Gdi32", Gdi32.class);

  /** http://msdn.microsoft.com/en-us/library/dd183489(VS.85).aspx */
  public Pointer CreateCompatibleDC(Pointer hdc);

  /** http://msdn.microsoft.com/en-us/library/dd183488(VS.85).aspx */
  public Pointer CreateCompatibleBitmap(Pointer hdc,
      int nWidth, int nHeight);

  /** http://msdn.microsoft.com/en-us/library/dd162957(VS.85).aspx */
  public Pointer SelectObject(Pointer hdc, Pointer hgdiobj);

  /** http://msdn.microsoft.com/en-us/library/dd145078(VS.85).aspx */
  public int SetPixel(Pointer hdc, int X, int Y, int crColor);

  /** http://msdn.microsoft.com/en-us/library/dd144909(VS.85).aspx */
  public int GetPixel(Pointer hdc, int nXPos, int nYPos);

  /** http://msdn.microsoft.com/en-us/library/dd183539(VS.85).aspx */
  public boolean DeleteObject(Pointer hObject);

  /** http://msdn.microsoft.com/en-us/library/dd183533(VS.85).aspx */
  public boolean DeleteDC(Pointer hdc);

}

也许有一种使用JNI的方法来查找包含图形的Windows资源并提取它(再次使用JNI)?我不熟悉使用JNI,所以如果有人知道一种方法,我将不胜感激。 - Savvas Dalkitsis
可能是什么操作系统? - McDowell
Windows。我猜版本不会有影响,因为可能会使用Win32 API。顺便说一句,我正在使用Vista。 - Savvas Dalkitsis
你可以使用LoadCursor函数,并将返回值视为HICON来实现这一点,我想。http://msdn.microsoft.com/en-us/library/ms648391(VS.85).aspx - McDowell
感谢您的评论...最后一条评论看起来对我来说可能是正确的。就像我说的,我对JNI一无所知。:D 您熟悉它吗?如果是这样,您能帮助我使用您提到的函数吗? - Savvas Dalkitsis

1

Linux的解决方案:

private BufferedImage getCursorImage(){
    X11 x11 = X11.INSTANCE;
    Xfixes xfixes = Xfixes.INSTANCE;

    X11.Display display = x11.XOpenDisplay(null);

    Xfixes.XFixesCursorImage cursorImage = xfixes.XFixesGetCursorImage(display);

    ByteBuffer buf = cursorImage.pixels.getPointer().getByteBuffer(0,
            cursorImage.width * cursorImage.height * NativeLong.SIZE);
    buf.order(ByteOrder.LITTLE_ENDIAN);
    BufferedImage bim = new BufferedImage(cursorImage.width, cursorImage.height, BufferedImage.TYPE_INT_ARGB);
    WritableRaster raster = bim.getRaster();
    for (int y = 0; y < cursorImage.height; y++) {
        for (int x = 0; x < cursorImage.width; x++) {
            long z = NativeLong.SIZE == 8 ? buf.getLong() : buf.getInt();
            int b = (int) ((z >> 24) & 0xFF);
            int a = (int) ((z >> 16) & 0xFF);
            int g = (int) ((z >> 8) & 0xFF);
            int r = (int) (z & 0xFF);
            raster.setPixel(x, y, new int[] { a, r, g, b });
        }
    }

    x11.XCloseDisplay(display);
    return bim;
}

JNA接口:
public interface Xfixes extends Library {

    Xfixes INSTANCE = Native.load("Xfixes", Xfixes.class);

    @Structure.FieldOrder({ "x", "y", "width", "height", "xhot", "yhot", "cursor_serial", "pixels", "atom", "name"})
    class XFixesCursorImage extends Structure {
        public short x;
        public short y;
        public short width;
        public short height;
        public short xhot;
        public short yhot;
        public NativeLong cursor_serial;

        public NativeLongByReference pixels;

        public NativeLong atom;
        public Pointer name;

        public XFixesCursorImage() {
            super();
        }
    }

    XFixesCursorImage XFixesGetCursorImage(X11.Display dpy);
}

0

可能可以从JRE中找到系统光标图像。查看源代码也可能会很有趣。


1
嗯,我实际上在考虑更通用的东西,不仅限于系统光标,但那听起来像是一个不错的替代方案。 - Savvas Dalkitsis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接