如何在Java中快速截取屏幕截图?

时间:2010-05-26 10:18:48

标签: java screenshot

我正在实施一个简单的眼动仪,它需要快速截屏屏幕上发生的事情,同时从网络摄像头捕捉视频。

事情就是用机器人做的方式,这里提到:https://stackoverflow.com/questions/2475303/java-library-for-capturing-active-window-screenshot 非常慢。

顺便说一句,从网络摄像头检索视频的速度要快得多,并返回字节数组,这个数据处理速度非常快。

有人知道更快的解决方案吗?可以链接到Java的C ++库也可以提供帮助。

谢谢!

更新: 决定切换到OpenCV,现在正在寻找用它制作截图的方法:)

5 个答案:

答案 0 :(得分:11)

这是我在我的一个项目中使用的JNA的特定于Windows的版本。

我发现它比Robot快一个数量级,即使使用本机调用开销也是如此。

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.platform.win32.W32API;
import com.sun.jna.win32.W32APIOptions;

public class JNAScreenShot {

    public static BufferedImage getScreenshot(Rectangle bounds) {
        W32API.HDC windowDC = GDI.GetDC(USER.GetDesktopWindow());
        W32API.HBITMAP outputBitmap =
            GDI.CreateCompatibleBitmap(windowDC,
                                       bounds.width, bounds.height);
        try {
            W32API.HDC blitDC = GDI.CreateCompatibleDC(windowDC);
            try {
                W32API.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);
                }
                GDI32.BITMAPINFO bi = new GDI32.BITMAPINFO(40);
                bi.bmiHeader.biSize = 40;
                boolean ok =
                    GDI.GetDIBits(blitDC, outputBitmap, 0, bounds.height,
                                  (byte[]) null, bi, GDI32.DIB_RGB_COLORS);
                if (ok) {
                    GDI32.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(GDI32.HDC        blitDC,
                            GDI32.HBITMAP    outputBitmap,
                            GDI32.BITMAPINFO bi) {
        GDI32.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 {
    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);
    HWND GetDesktopWindow();
}

答案 1 :(得分:6)

robot.createScreenCapture(captureSize);电话大约需要20毫秒。

答案 2 :(得分:0)

可能是您可以使用JMF ..查看Screen Grabber code @ Oracle's site。我认为它可以帮助您解决问题。

答案 3 :(得分:0)

你一定要试试OpenCV

答案 4 :(得分:0)

要捕获全屏。

Robot robot = new Robot();
Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage screenFullImage = robot.createScreenCapture(screenRect);
ImageIO.write(screenFullImage, format, new File(destination));

捕获部分屏幕。

Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Rectangle rect = new Rectangle(0, 0, screenSize.width / 2, screenSize.height / 2);
Robot robot = new Robot();
BufferedImage screenFullImage = robot.createScreenCapture(rect);
ImageIO.write(screenFullImage, format, new File(destination));

参考:link