2011-08-09 8 views
10

Dado que el método Robot.createScreenCaputure() es lento, decidí usar la biblioteca nativa. Busqué y encontré esto forum y encontré un code snipplet específico que usa JNA Library. Es una versión antigua de modo que volvió a escribir el código:JNA biblioteca más lenta captura de pantalla que la clase de 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.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(); 
} 

Y un código de prueba para ver cuánto más rápido más rápido que la Clase robot:

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); 
    } 
} 

Y el resultado es

Con JNA Biblioteca: 77
Con Clase Robot 37

Chicos, por favor alguien explica ¿por qué es eso y cómo puedo sujetarlo?

+2

Pruebe hacer 1000 iteraciones en lugar de 10. Los resultados serán más precisos. – tskuzzy

+0

Estoy bastante seguro de que JNA incurre en una gran sobrecarga al hacer la transición entre el código administrado por JVM y el código nativo. En su lugar, puede estar interesado en buscar soluciones basadas en JNI (incurre en una sobrecarga más pequeña). –

+0

tskuzzy He hecho lo que me dijiste, pero el resultado es 36 vs 21 segundos. Eso no funcionó –

Respuesta

0

llamadas JNA llevan mucho tiempo, en su lugar JNI utiliza C++ directamente.

+1

Feliz de votar, pero ¿debería ser "en su lugar ** JNI ** usa C++ directamente"? –

5

No intente optimizar demasiado pronto. Cree una interfaz sensata para obtener los datos que desea (una captura de pantalla) y luego cree la implementación que desee en función de Robot, JNA o JNI.

Supongo que las diferentes implementaciones dan resultados completamente diferentes en función del entorno que se está ejecutando.

Primera Regla de Programación: Primero haz que funcione. Luego haga un perfil, encuentre un cuello de botella y elimine o mitigue el efecto del cuello de botella.

0

Primero, compruebe si la biblioteca nativa es realmente más rápida que su código. Puede que no sea el caso.

Suponiendo que ya lo haya verificado, diría que el problema aquí es que las llamadas con JNA son realmente lentas. para saltar el una llamada por ciclo problema que sugeriría escribir una función C de esta manera:

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

Ahora compilar el código y llame a la callWithJNAfunction (...) con el JNA.

Si el problema es la lentitud de las llamadas JNA, se volverá más rápido.

Cuestiones relacionadas