想要抓屏幕的一部分,并且保存起来用于网络传送,一般如下:
try
{
int width = 200;
int height = 300;
java.awt.Rectangle rectangle = new java.awt.Rectangle( 0, 0, width, height );
java.awt.Robot robot = new java.awt.Robot();
java.awt.image.BufferedImage image = robot.createScreenCapture( rectangle );
int[] rgbs = image.getRGB( 0, 0, width, height, null/*int[]*/, 0, width );
}
catch( Exception e ){ e.printStackTrace(); }
看Robot源代码发现每次都要新建BufferedImage,效率极低,搜索Robot中createScreenCapture方法,发现关键的一行:
pixels/*int[]*/ = peer/*?*/.getRGBPixels( screenRect/*Rectangle*/ );
那么peer从哪来?继续搜索找到:
Toolkit toolkit = Toolkit.getDefaultToolkit();
if( toolkit instanceof ComponentFactory/*?*/ ){ peer/*RobotPeer*/ = ( (ComponentFactory)toolkit ).createRobot( this, screen/*?*/ ); }
其中,ComponentFactory是SUN未公开的方法(见附注),是个接口(sun.awt.ComponentFactory),
peer是RobotPeer,也是个接口(java.awt.peer.RobotPeer),而screen如下:
GraphicsDevice screen = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
这样,我们就可以这样写来得到RGB的数组:
int width = 200;
int height = 300;
Rectangle rectangle = new Rectangle( 0, 0, width, height );
GraphicsDevice screen = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
Toolkit toolkit = Toolkit.getDefaultToolkit();
if( toolkit instanceof ComponentFactory )
{
RobotPeer peer = (ComponentFactory)toolkit.createRobot/*?*/( null/*?new Robot()*/, screen );
int[] rgbs = peer.getRGBPixels( rectangle );
}
至此,我们已经得到一个比较满意的解决方案了,但是,通过RobotPeer这个接口我们可以知道,
真正的Peer被SUN隐藏在未公开的类中,所以我们在sun.awt包中搜索createRobot字符串,
找到sun//awt//HeadlessToolkit.java,sun//awt//SunToolkit.java,sun//awt//windows//WToolkit.java这三个Toolkit类,
其中只有awt/windows/WToolkit.java真正实现了createRobot方法,如下:
return new WRobotPeer( graphicsdevice/*GraphicsDevice*/ ); //没有使用传进来的Robot参数,所以createRobot中参数Robot可以为null
也就是说,在Windows平台下,Robot的实现是sun//awt//windows//WRobotPeer.java,
而Toolkit的实现是sun//awt//windows//WToolkit.java(这个现在暂且不谈),
来看看WRobotPeer.java中是如何抓取屏幕得到RGB数组的吧,源代码如下:
public int[] getRGBPixels( Rectangle rectangle )
{
rectangle.translate( offset.x, offset.y );
int ai[] = new int[rectangle.width * rectangle.height];
getRGBPixels( rectangle.x, rectangle.y, rectangle.width, rectangle.height, ai );
return ai;
}
private native void getRGBPixels( int i, int j, int k, int l, int ai[] );
显然它最终是调用本地方法来实现的,不过它竟然每次都重建int数组(想想1024 X 768有多大),这个效率可是够“好”的。
我们需要的仅仅是getRGBPixels( int i, int j, int k, int l, int ai[] )这个私有的本地方法。
int width = 200;
int height = 300;
Rectangle rectangle = new Rectangle( 0, 0, width, height );
int[] rgbs = new int[rectangle.width * rectangle.height]; //这步必须做,否则会让JVM崩溃
GraphicsDevice screen = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
Toolkit toolkit = Toolkit.getDefaultToolkit();
RobotPeer peer = ( toolkit instanceof ComponentFactory ? ( (ComponentFactory)toolkit ).createRobot( null/*new Robot()*/, screen ) : null );
if( peer == null ){ System.err.println( "Could not get RobotPeer." ); return; }
Method method = peer.getClass().getDeclaredMethod( "getRGBPixels", int.class, int.class, int.class, int.class, int[].class );
method.setAccessible( true );
method.invoke( peer, rectangle.x, rectangle.y, rectangle.width, rectangle.height, rgbs );
结论:
其实如果SUN在RobotPeer中向外提供了getRGBPixels( int i, int j, int k, int l, int ai[] )这个方法,那么也就不会有那么多烦人的性能问题了。
附注:
怎样得到SUN未公开的类的源文件?
用反编译工具jad.exe,我用的是1.7版本的。
1. 进入X://JDK1.4.2//jre//lib//,找到rt.jar文件,用压缩软件winrar.exe打开并且解压,我们假定解压生成的目录是rt。
2. 进入X://JDK1.4.2//jre//lib//rt//,将jad.exe复制到此目录下,运行如下命令:
JAD.EXE -o -r -s java -d decompiled_src ./**/*.class
3. 运行完毕后(可能会看到失败的提示,不管它),会生成decompiled_src文件夹,此时,你就可以从里面找需要的类文件了,当然,有些类文件还是参照官方的为好。
测试:
如下是我用来测试三种不同实现方法性能的小例子:
import sun.awt.ComponentFactory;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.Rectangle;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.peer.RobotPeer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
class TestSnapshot
{
private Rectangle rectangle;
private int[] rgbs;
public TestSnapshot() throws Exception
{
this.rectangle = new Rectangle( 0, 0, 200, 100 );
this.rgbs = new int[rectangle.width * rectangle.height];
long start = 0L;
int times = 10000;
{
start = System.currentTimeMillis();
GraphicsDevice screen = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
Toolkit toolkit = Toolkit.getDefaultToolkit();
RobotPeer peer = ( toolkit instanceof ComponentFactory ? ( (ComponentFactory)toolkit ).createRobot( null/*new Robot()*/, screen ) : null );
if( peer == null ){ System.err.println( "Could not get RobotPeer." ); return; }
for( int n = 0; n < times; n ++ ){ rgbs = peer.getRGBPixels( rectangle ); }
System.err.println( System.currentTimeMillis() - start );
}
{
start = System.currentTimeMillis();
Robot robot = new Robot();
Field peerField = robot.getClass().getDeclaredField( "peer" );
peerField.setAccessible( true );
Object robotPeer = peerField.get( robot );
Method method = robotPeer.getClass().getDeclaredMethod( "getRGBPixels", int.class, int.class, int.class, int.class, int[].class );
method.setAccessible( true );
for( int n = 0; n < times; n ++ ){ method.invoke( robotPeer, rectangle.x, rectangle.y, rectangle.width, rectangle.height, rgbs ); }
System.err.println( System.currentTimeMillis() - start );
}
{
start = System.currentTimeMillis();
GraphicsDevice screen = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
Toolkit toolkit = Toolkit.getDefaultToolkit();
RobotPeer peer = ( toolkit instanceof ComponentFactory ? ( (ComponentFactory)toolkit ).createRobot( null/*new Robot()*/, screen ) : null );
if( peer == null ){ System.err.println( "Could not get RobotPeer." ); return; }
Method method = peer.getClass().getDeclaredMethod( "getRGBPixels", int.class, int.class, int.class, int.class, int[].class );
method.setAccessible( true );
for( int n = 0; n < times; n ++ ){ method.invoke( peer, rectangle.x, rectangle.y, rectangle.width, rectangle.height, rgbs ); }
System.err.println( System.currentTimeMillis() - start );
}
}
public static void main( String[] args ) throws Exception { new TestSnapshot(); }
}
参考文章:
http://dahai686123.blog.163.com/blog/static/286670492008331029413/