JavaJNA:如何手动将像素数据写入hbitmap?
我目前正在开发一个屏幕录制应用程序,它使用JNA来捕获视频的帧。在添加光标之前,我想在捕获的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
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;
}
}
}