JNA库截图比机器人类慢?
由于 Robot.createScreenCaputure()
方法很慢,我决定使用本机库。我搜索并找到了这个论坛并找到了一个特定的代码片段使用JNA 图书馆。这是一个旧版本,所以我重写了代码:
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import com.sun.jna.Native;
import com.sun.jna.win32.W32APIOptions;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.platform.win32.WinGDI;
public class JNAScreenShot {
public static BufferedImage getScreenshot(Rectangle bounds) {
WinDef.HDC windowDC = GDI.GetDC(USER.GetDesktopWindow());
WinDef.HBITMAP outputBitmap =
GDI.CreateCompatibleBitmap(windowDC,
bounds.width, bounds.height);
try {
WinDef.HDC blitDC = GDI.CreateCompatibleDC(windowDC);
try {
WinNT.HANDLE oldBitmap =
GDI.SelectObject(blitDC, outputBitmap);
try {
GDI.BitBlt(blitDC,
0, 0, bounds.width, bounds.height,
windowDC,
bounds.x, bounds.y,
GDI32.SRCCOPY);
} finally {
GDI.SelectObject(blitDC, oldBitmap);
}
WinGDI.BITMAPINFO bi = new WinGDI.BITMAPINFO(40);
bi.bmiHeader.biSize = 40;
boolean ok =
GDI.GetDIBits(blitDC, outputBitmap, 0, bounds.height,
(byte[]) null, bi, WinGDI.DIB_RGB_COLORS);
if (ok) {
WinGDI.BITMAPINFOHEADER bih = bi.bmiHeader;
bih.biHeight = -Math.abs(bih.biHeight);
bi.bmiHeader.biCompression = 0;
return bufferedImageFromBitmap(blitDC, outputBitmap, bi);
} else {
return null;
}
} finally {
GDI.DeleteObject(blitDC);
}
} finally {
GDI.DeleteObject(outputBitmap);
}
}
private static BufferedImage bufferedImageFromBitmap(WinDef.HDC blitDC,
WinDef.HBITMAP outputBitmap,
WinGDI.BITMAPINFO bi) {
WinGDI.BITMAPINFOHEADER bih = bi.bmiHeader;
int height = Math.abs(bih.biHeight);
final ColorModel cm;
final DataBuffer buffer;
final WritableRaster raster;
int strideBits =
(bih.biWidth * bih.biBitCount);
int strideBytesAligned =
(((strideBits - 1) | 0x1F) + 1) >> 3;
final int strideElementsAligned;
switch (bih.biBitCount) {
case 16:
strideElementsAligned = strideBytesAligned / 2;
cm = new DirectColorModel(16, 0x7C00, 0x3E0, 0x1F);
buffer =
new DataBufferUShort(strideElementsAligned * height);
raster =
Raster.createPackedRaster(buffer,
bih.biWidth, height,
strideElementsAligned,
((DirectColorModel) cm).getMasks(),
null);
break;
case 32:
strideElementsAligned = strideBytesAligned / 4;
cm = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF);
buffer =
new DataBufferInt(strideElementsAligned * height);
raster =
Raster.createPackedRaster(buffer,
bih.biWidth, height,
strideElementsAligned,
((DirectColorModel) cm).getMasks(),
null);
break;
default:
throw new IllegalArgumentException("Unsupported bit count: " + bih.biBitCount);
}
final boolean ok;
switch (buffer.getDataType()) {
case DataBuffer.TYPE_INT: {
int[] pixels = ((DataBufferInt) buffer).getData();
ok = GDI.GetDIBits(blitDC, outputBitmap, 0, raster.getHeight(), pixels, bi, 0);
}
break;
case DataBuffer.TYPE_USHORT: {
short[] pixels = ((DataBufferUShort) buffer).getData();
ok = GDI.GetDIBits(blitDC, outputBitmap, 0, raster.getHeight(), pixels, bi, 0);
}
break;
default:
throw new AssertionError("Unexpected buffer element type: " + buffer.getDataType());
}
if (ok) {
return new BufferedImage(cm, raster, false, null);
} else {
return null;
}
}
private static final User32 USER = User32.INSTANCE;
private static final GDI32 GDI = GDI32.INSTANCE;
}
interface GDI32 extends com.sun.jna.platform.win32.GDI32,
com.sun.jna.platform.win32.WinGDI,
com.sun.jna.platform.win32.WinDef {
GDI32 INSTANCE =
(GDI32) Native.loadLibrary(GDI32.class);
boolean BitBlt(HDC hdcDest, int nXDest, int nYDest,
int nWidth, int nHeight, HDC hdcSrc,
int nXSrc, int nYSrc, int dwRop);
HDC GetDC(HWND hWnd);
boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines,
byte[] pixels, BITMAPINFO bi, int usage);
boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines,
short[] pixels, BITMAPINFO bi, int usage);
boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines,
int[] pixels, BITMAPINFO bi, int usage);
int SRCCOPY = 0xCC0020;
}
interface User32 extends com.sun.jna.platform.win32.User32 {
User32 INSTANCE = (User32) Native.loadLibrary(User32.class, W32APIOptions.UNICODE_OPTIONS);
com.sun.jna.platform.win32.WinDef.HWND GetDesktopWindow();
}
还有一个测试代码,看看它比机器人类快多少:
import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
public class testClass {
public static void main(String[] args) {
BufferedImage bi = null, bj = null;
Rectangle rect = new Rectangle(0, 0, 810, 384);
long startTime, finishTime;
startTime = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
bi = JNAScreenShot.getScreenshot(rect);
}
finishTime = System.currentTimeMillis();
System.out.println("With JNA Library: " + (finishTime - startTime)/10);
Robot robo = null;
startTime = System.currentTimeMillis();
try {
robo = new Robot();
} catch (AWTException a) {
}
for (int i = 0; i < 10; i++) {
bj = robo.createScreenCapture(rect);
}
finishTime = System.currentTimeMillis();
System.out.println("With Robot Class " + (finishTime - startTime)/10);
}
}
结果是
JNA 图书馆:77
与37级机器人
Guys,请有人解释一下为什么是这样以及如何我可以固定它?
Since Robot.createScreenCaputure()
method is slow, I decided to use native library. I searched and found this forum and find a specific code snipplet which uses JNA Library. It's an old version so that I rewrote the code:
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import com.sun.jna.Native;
import com.sun.jna.win32.W32APIOptions;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.platform.win32.WinGDI;
public class JNAScreenShot {
public static BufferedImage getScreenshot(Rectangle bounds) {
WinDef.HDC windowDC = GDI.GetDC(USER.GetDesktopWindow());
WinDef.HBITMAP outputBitmap =
GDI.CreateCompatibleBitmap(windowDC,
bounds.width, bounds.height);
try {
WinDef.HDC blitDC = GDI.CreateCompatibleDC(windowDC);
try {
WinNT.HANDLE oldBitmap =
GDI.SelectObject(blitDC, outputBitmap);
try {
GDI.BitBlt(blitDC,
0, 0, bounds.width, bounds.height,
windowDC,
bounds.x, bounds.y,
GDI32.SRCCOPY);
} finally {
GDI.SelectObject(blitDC, oldBitmap);
}
WinGDI.BITMAPINFO bi = new WinGDI.BITMAPINFO(40);
bi.bmiHeader.biSize = 40;
boolean ok =
GDI.GetDIBits(blitDC, outputBitmap, 0, bounds.height,
(byte[]) null, bi, WinGDI.DIB_RGB_COLORS);
if (ok) {
WinGDI.BITMAPINFOHEADER bih = bi.bmiHeader;
bih.biHeight = -Math.abs(bih.biHeight);
bi.bmiHeader.biCompression = 0;
return bufferedImageFromBitmap(blitDC, outputBitmap, bi);
} else {
return null;
}
} finally {
GDI.DeleteObject(blitDC);
}
} finally {
GDI.DeleteObject(outputBitmap);
}
}
private static BufferedImage bufferedImageFromBitmap(WinDef.HDC blitDC,
WinDef.HBITMAP outputBitmap,
WinGDI.BITMAPINFO bi) {
WinGDI.BITMAPINFOHEADER bih = bi.bmiHeader;
int height = Math.abs(bih.biHeight);
final ColorModel cm;
final DataBuffer buffer;
final WritableRaster raster;
int strideBits =
(bih.biWidth * bih.biBitCount);
int strideBytesAligned =
(((strideBits - 1) | 0x1F) + 1) >> 3;
final int strideElementsAligned;
switch (bih.biBitCount) {
case 16:
strideElementsAligned = strideBytesAligned / 2;
cm = new DirectColorModel(16, 0x7C00, 0x3E0, 0x1F);
buffer =
new DataBufferUShort(strideElementsAligned * height);
raster =
Raster.createPackedRaster(buffer,
bih.biWidth, height,
strideElementsAligned,
((DirectColorModel) cm).getMasks(),
null);
break;
case 32:
strideElementsAligned = strideBytesAligned / 4;
cm = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF);
buffer =
new DataBufferInt(strideElementsAligned * height);
raster =
Raster.createPackedRaster(buffer,
bih.biWidth, height,
strideElementsAligned,
((DirectColorModel) cm).getMasks(),
null);
break;
default:
throw new IllegalArgumentException("Unsupported bit count: " + bih.biBitCount);
}
final boolean ok;
switch (buffer.getDataType()) {
case DataBuffer.TYPE_INT: {
int[] pixels = ((DataBufferInt) buffer).getData();
ok = GDI.GetDIBits(blitDC, outputBitmap, 0, raster.getHeight(), pixels, bi, 0);
}
break;
case DataBuffer.TYPE_USHORT: {
short[] pixels = ((DataBufferUShort) buffer).getData();
ok = GDI.GetDIBits(blitDC, outputBitmap, 0, raster.getHeight(), pixels, bi, 0);
}
break;
default:
throw new AssertionError("Unexpected buffer element type: " + buffer.getDataType());
}
if (ok) {
return new BufferedImage(cm, raster, false, null);
} else {
return null;
}
}
private static final User32 USER = User32.INSTANCE;
private static final GDI32 GDI = GDI32.INSTANCE;
}
interface GDI32 extends com.sun.jna.platform.win32.GDI32,
com.sun.jna.platform.win32.WinGDI,
com.sun.jna.platform.win32.WinDef {
GDI32 INSTANCE =
(GDI32) Native.loadLibrary(GDI32.class);
boolean BitBlt(HDC hdcDest, int nXDest, int nYDest,
int nWidth, int nHeight, HDC hdcSrc,
int nXSrc, int nYSrc, int dwRop);
HDC GetDC(HWND hWnd);
boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines,
byte[] pixels, BITMAPINFO bi, int usage);
boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines,
short[] pixels, BITMAPINFO bi, int usage);
boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines,
int[] pixels, BITMAPINFO bi, int usage);
int SRCCOPY = 0xCC0020;
}
interface User32 extends com.sun.jna.platform.win32.User32 {
User32 INSTANCE = (User32) Native.loadLibrary(User32.class, W32APIOptions.UNICODE_OPTIONS);
com.sun.jna.platform.win32.WinDef.HWND GetDesktopWindow();
}
And a test code to see how much it faster faster than Robot Class:
import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
public class testClass {
public static void main(String[] args) {
BufferedImage bi = null, bj = null;
Rectangle rect = new Rectangle(0, 0, 810, 384);
long startTime, finishTime;
startTime = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
bi = JNAScreenShot.getScreenshot(rect);
}
finishTime = System.currentTimeMillis();
System.out.println("With JNA Library: " + (finishTime - startTime)/10);
Robot robo = null;
startTime = System.currentTimeMillis();
try {
robo = new Robot();
} catch (AWTException a) {
}
for (int i = 0; i < 10; i++) {
bj = robo.createScreenCapture(rect);
}
finishTime = System.currentTimeMillis();
System.out.println("With Robot Class " + (finishTime - startTime)/10);
}
}
And the result is
With JNA Library: 77
With Robot Class 37
Guys, please someone explain why is that and how can I fasten it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
不要过早尝试优化。创建一个合理的接口来获取您想要的数据(屏幕截图),然后基于 Robot、JNA 或 JNI 创建您想要的实现。
我猜想不同的实现会根据其运行的环境给出完全不同的结果。
编程规则一:首先让它工作。然后进行分析,找到瓶颈并消除或减轻瓶颈的影响。
Don't try to optimise too early. Create a sensible interface to get the data you want (A screenshot) then create the implementation you want based on either Robot, JNA or JNI.
I would guess that the different implementations give completely different results based on the environment that it is running.
Rule One of Programming: First make it work. Then profile, find a bottleneck and remove or mitigate the effect of the bottleneck.
JNA调用需要很多时间,而JNI直接使用c++。
JNA calls take much time, instead JNI uses c++ directly.
首先,检查本机库是否实际上比您的代码更快。情况可能并非如此。
假设您已经检查过这一点,我想说这里的问题是使用 JNA 的调用非常慢。
要跳过每个周期一次调用问题,我建议编写一个如下所示的 C 函数:
现在编译此代码并使用 JNA 调用 callWithJNAfunction(...)。
如果问题是 JNA 调用缓慢,它会变得更快。
First, check if the native library is actually faster than your code. It might not be the case.
Assuming you already did check that, I would say the problem here is that calls with JNA are really slow.
To skip the one call per cycle problem I would suggest writing a C function like this:
Now compile this code and call the callWithJNAfunction(...) with JNA.
If the problem is the slowness of the JNA calls, it will get faster.