JavaJNA:如何手动将像素数据写入hbitmap?

JavaJNA:如何手动将像素数据写入hbitmap?,java,winapi,jna,Java,Winapi,Jna,我目前正在开发一个屏幕录制应用程序,它使用JNA来捕获视频的帧。在添加光标之前,我想在捕获的hbitmap上插入一个半透明标记,以便更好地突出显示光标在视频中的位置。经过多次搜索后,我无法找到一个示例,说明如何从buffereImage中获取像素数据,并手动将其写入hbitmap指向的对象。以下是我最好的尝试: GuiTest.java: import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.EventQ

我目前正在开发一个屏幕录制应用程序,它使用JNA来捕获视频的帧。在添加光标之前,我想在捕获的
hbitmap
上插入一个半透明标记,以便更好地突出显示光标在视频中的位置。经过多次搜索后,我无法找到一个示例,说明如何从
buffereImage
中获取像素数据,并手动将其写入
hbitmap
指向的对象。以下是我最好的尝试:

GuiTest.java:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.MemoryImageSource;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;

public class GuiTest extends JFrame
{
    private static final long serialVersionUID = 1L;
    private JLabel            lblNewLabel;

    public GuiTest()
    {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        initGui();
    }

    private void initGui()
    {
        setPreferredSize(new Dimension(400, 400));
        Rectangle area = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
        BufferedImage bi = new BufferedImage(area.width, area.height, BufferedImage.TYPE_INT_ARGB);
        int[] ia = new int[area.width * area.height];
        bi.getRGB(0, 0, area.width, area.height, ia, 0, area.width);
        MemoryImageSource mis = new MemoryImageSource(area.width, area.height, ia, 0, area.width);
        mis.setAnimated(true);
        mis.newPixels(captureScreen(area), ColorModel.getRGBdefault(), 0, area.width);
        lblNewLabel = new JLabel("");
        lblNewLabel.setIcon(new ImageIcon(Toolkit.getDefaultToolkit().createImage(mis)));
        getContentPane().add(lblNewLabel, BorderLayout.CENTER);
    }

    public int[] captureScreen(Rectangle recordArea)
    {   // This code captures the desktop. I wish I could say I understood it.
        Pointer handle = User32.INSTANCE.GetDesktopWindow();
        Pointer hdcSrc = User32.INSTANCE.GetWindowDC(handle);
        Pointer hdcDest = Gdi32.INSTANCE.CreateCompatibleDC(hdcSrc);
        Pointer hBitmap = Gdi32.INSTANCE.CreateCompatibleBitmap(hdcSrc, recordArea.width, recordArea.height);
        Pointer hOld = Gdi32.INSTANCE.SelectObject(hdcDest, hBitmap);
        Gdi32.INSTANCE.BitBlt(hdcDest, 0, 0, recordArea.width, recordArea.height, hdcSrc, 0, 0, Gdi32.SOURCE_COPY | Gdi32.CAPTURE_BLT);
        try
        { // Start broken code:
            BufferedImage MOUSE_SHADE = ImageIO.read(GuiTest.class.getResource("mouse_shade48.png"));
            Point cursorPosition = MouseInfo.getPointerInfo().getLocation();
            int[] pixels = MOUSE_SHADE.getRGB(0, 0, 48, 48, new int[48 * 48], 0, 48);
            Memory shadeMem = new Memory(recordArea.width * recordArea.height * 4);
            shadeMem.write(0, copyToOffsetCentered(pixels, new Rectangle(48, 48),
                    new int[recordArea.width * recordArea.height], new Rectangle(recordArea.width, recordArea.height),
                    cursorPosition.x, cursorPosition.y), 0, recordArea.width * recordArea.height);
            BitmapInfo shadebmi = new BitmapInfo(1);
            shadebmi.bmiHeader.biWidth = recordArea.width;
            shadebmi.bmiHeader.biHeight = -recordArea.height;
            shadebmi.bmiHeader.biPlanes = 1;
            shadebmi.bmiHeader.biBitCount = 32;
            shadebmi.bmiHeader.biCompression = Gdi32.BI_RGB;
            Pointer hdc = User32.INSTANCE.GetDC(null);
            Pointer hdcDest2 = Gdi32.INSTANCE.CreateCompatibleDC(hdc);
            Pointer hBitmap2 = Gdi32.INSTANCE.CreateCompatibleBitmap(hdc, recordArea.width, recordArea.height);
            Gdi32.INSTANCE.SetDIBits(hdc, hBitmap2, 0, recordArea.height, shadeMem, shadebmi, Gdi32.DIB_RGB_COLORS);
            Msimg32.INSTANCE.TransparentBlt(hdcDest, 0, 0, recordArea.width, recordArea.height, hdc, 0, 0, recordArea.width, recordArea.height, new BlendFunction());
            // I have also tried Gdi32.BitBlt
        }
        catch (IOException e1)
        {
            e1.printStackTrace();
        } // End broken code
        try
        {
            CursorData cursorData = new CursorData();
            cursorData.drawCursorToHandle(hdcDest);
            cursorData.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        Gdi32.INSTANCE.SelectObject(hdcDest, hOld);
        Gdi32.INSTANCE.DeleteDC(hdcDest);
        int nBits = recordArea.width * recordArea.height * 4;
        BitmapInfo bmi = new BitmapInfo(1);
        bmi.bmiHeader.biWidth = recordArea.width;
        bmi.bmiHeader.biHeight = -recordArea.height;
        bmi.bmiHeader.biPlanes = 1;
        bmi.bmiHeader.biBitCount = 32;
        bmi.bmiHeader.biCompression = Gdi32.BI_RGB;
        Memory colorBitsMem = new Memory(nBits);
        Gdi32.INSTANCE.GetDIBits(hdcSrc, hBitmap, 0, recordArea.height, colorBitsMem, bmi, Gdi32.DIB_RGB_COLORS);
        int[] colorBits = colorBitsMem.getIntArray(0, recordArea.width * recordArea.height);
        User32.INSTANCE.ReleaseDC(handle, hdcSrc);
        Gdi32.INSTANCE.DeleteObject(hBitmap);
        return colorBits;
    }

    private int[] copyToOffsetCentered(int[] src, Rectangle srcDim, int[] dest, Rectangle destDim, int dx, int dy)
    {
        int startx = dx - srcDim.width / 2;
        int endx = startx + srcDim.width;
        int starty = dy - srcDim.height / 2;
        int endy = starty + srcDim.height;

        for (int x = Math.max(startx, 0); x < Math.min(endx, destDim.width); x++)
        {
            for (int y = Math.max(starty, 0); y < Math.min(endy, destDim.height); y++)
            {
                int xSrc = x - startx;
                int ySrc = y - starty;
                dest[y * destDim.width + x] = src[ySrc * srcDim.width + xSrc];
            }
        }
        return dest;
    }

    /**
     * Launch the application.
     */
    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    GuiTest window = new GuiTest();
                    window.pack();
                    window.setVisible(true);
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
    }
}
鼠标_shade48.png:

我已经验证了我的
int[]
是否正确创建和填充,我只是不知道如何将该数据放入
hbitmap
中,并将其与我已经拥有的捕获屏幕
hbitmap
进行适当的alpha混合。我目前看到的是,图像显示出来时,似乎正在跳过已损坏的代码。非常感谢您的帮助


此外,我意识到上面的代码缺少我正在使用的Msimg32、Gdi32和USER32JNA接口,因此如果您需要它们,我可以创建一个要点并将所有相关代码放在那里。

您可以发布您的CursorInfo结构和GetCursorInfo方法吗?可能有助于了解问题所在。事实上,我决定换一个方向,改变用于突出显示鼠标的图像,使其成为轮廓,而不是实心圆,因此我能够使用基于Java的混合,而不是混乱地处理大量我根本不太懂的本机Win32 API调用。
import java.awt.MouseInfo;
import java.awt.Point;
import com.sun.jna.Pointer;

public class CursorData
{
    public boolean isVisible;
    public Pointer iconHandle;
    public Point   position;

    //    private static final HBITMAP MOUSE_SHADE;

    //    static
    //    {
    //        BufferedImage bi = new BufferedImage(0, 0, 0);
    //        try
    //        {
    //            bi = ImageIO.read(Images.class.getResource("/icons/com/valsphere/mouse_shade48.png"));
    //        }
    //        catch (IOException e)
    //        {
    //            e.printStackTrace();
    //        }
    //        int[] pixels = bi.getRaster().getPixels(0, 0, 48, 48, new int[48 * 48]);
    //        
    //    }

    public CursorData()
    {
        updateCursorData();
    }

    public void updateCursorData()
    {
        CursorInfo cursorInfo = new CursorInfo();

        if (User32.INSTANCE.GetCursorInfo(cursorInfo))
        {
            isVisible = cursorInfo.flags.intValue() == User32.CURSOR_SHOWING;

            if (isVisible)
            {
                iconHandle = User32.INSTANCE.CopyIcon(cursorInfo.hCursor.getPointer());
                IconInfo iconInfo = new IconInfo();

                if (User32.INSTANCE.GetIconInfo(iconHandle, iconInfo))
                {
                    Point cursorPosition = MouseInfo.getPointerInfo().getLocation();
                    position = new Point(cursorPosition.x - iconInfo.xHotspot.intValue(), cursorPosition.y - iconInfo.yHotspot.intValue());

                    if (iconInfo.hbmMask != null)
                    {
                        Gdi32.INSTANCE.DeleteObject(iconInfo.hbmMask.getPointer());
                    }

                    if (iconInfo.hbmColor != null)
                    {
                        Gdi32.INSTANCE.DeleteObject(iconInfo.hbmColor.getPointer());
                    }
                }
            }
        }
    }

    public void drawCursorToHandle(Pointer hdcDest)
    {
        drawCursorToHandle(hdcDest, new Point());
    }

    public void drawCursorToHandle(Pointer hdcDest, Point cursorOffset)
    {
        if (iconHandle != null)
        {
            Point drawPosition = new Point(position.x - cursorOffset.x, position.y - cursorOffset.y);
            User32.INSTANCE.DrawIconEx(hdcDest, drawPosition.x, drawPosition.y, iconHandle, 0, 0, 0, null, User32.DI_NORMAL);
        }
    }

    public void close() throws Exception
    {
        if (iconHandle != null)
        {
            User32.INSTANCE.DestroyIcon(iconHandle);
            iconHandle = null;
        }
    }
}