简体   繁体   English

JNA库比机器人类的屏幕截图慢?

[英]JNA library slower screenshot than robot class?

Since Robot.createScreenCaputure() method is slow, I decided to use native library. 由于Robot.createScreenCaputure()方法很慢,我决定使用本机库。 I searched and found this forum and find a specific code snipplet which uses JNA Library . 我搜索并找到了这个论坛 ,找到了一个使用JNA Library的特定代码snipplet 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: 还有一个测试代码,可以看到比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 随着JNA图书馆:77
With Robot Class 37 机器人类37

Guys, please someone explain why is that and how can I fasten it? 伙计们,请有人解释为什么 ,以及如何能我系呢?

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. 创建一个合理的界面来获取您想要的数据(截图)然后根据Robot,JNA或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. 然后配置文件,找到瓶颈并删除或减轻瓶颈的影响。

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. 假设你已经检查过,我会说这里的问题是用JNA调用真的很慢。 To skip the one call per cycle problem I would suggest writing a C function like this: 要跳过每个周期一个调用问题,我建议写一个像这样的C函数:

    void callWithJNAfunction(int rectPosX, int rectPosY, rectSideX, rectSideY,int numberOfCycles) {
    for (int i = 0; i < numberOfCycles; i++) {
        //code in C that calls the library
    }

Now compile this code and call the callWithJNAfunction(...) with JNA. 现在编译此代码并使用JNA调用callWithJNAfunction(...)。

If the problem is the slowness of the JNA calls, it will get faster. 如果问题是JNA调用的缓慢,它将变得更快。

JNA调用需要很长时间,而JNI直接使用c ++。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM