Monday, February 21, 2011

Java multi-monitor screenshots

Random snippets I might want again one day ;)

Create a "C:\\CanDelete" directory before running any of this, or change the path building.

Take a screenshot of each monitor individually:
package com.blogspot.whileonefork.screencapture;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;


public class EachMonitor {
 public static void main(String[] argv) throws Exception {
  //Take a screenshot of every monitor
  GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
  GraphicsDevice[] screens = ge.getScreenDevices();
  
  for (GraphicsDevice screen : screens) {
   Robot robotForScreen = new Robot(screen);
   Rectangle screenBounds = screen.getDefaultConfiguration().getBounds();
   
   //The screen bounds have an annoying tendency to have an offset x/y; we want (0,0) => (width, height)
   screenBounds.x = 0;
   screenBounds.y = 0;
   BufferedImage screenShot = robotForScreen.createScreenCapture(screenBounds);
   String ext = "jpg";
   String outputFile = "C:\\CanDelete\\screenshot_" 
    + screen.getIDstring().replaceAll("[^A-Za-z0-9]", "_")
    + "."
    + ext;   
   ImageIO.write(screenShot, ext, new File(outputFile));
   System.out.println("Saved " + outputFile 
    + " of " + screen.getIDstring() 
    + " xy=(" + screenBounds.x + "," + screenBounds.y + ")"
    + " bounds=("+ screenBounds.width + "," + screenBounds.height + ")");
  }
 }
}


Take a single big screenshot of all monitors:

package com.blogspot.whileonefork.screencapture;

import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;

public class AllMonitors {
 public static void main(String[] argv) throws Exception {      
  /**
   * Take one big screenshot of the entire UI. 
   * On my Windows box monitors seem to act like they are side by side on X, extending on Y.
   * Seems, at least on windows, to ignore any offset config setup in display properties.
   */
  GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
  GraphicsDevice[] screens = ge.getScreenDevices();
  
  Rectangle allScreenBounds = new Rectangle();
  for (GraphicsDevice screen : screens) {
   Rectangle screenBounds = screen.getDefaultConfiguration().getBounds();
   
   allScreenBounds.width += screenBounds.width;
   allScreenBounds.height = Math.max(allScreenBounds.height, screenBounds.height);      
  }
  
  Robot robot = new Robot();
  BufferedImage screenShot = robot.createScreenCapture(allScreenBounds);
  
  String ext = "jpg";
  String outputFile = "C:\\CanDelete\\AllMonitors" 
   + "."
   + ext;   
  ImageIO.write(screenShot, ext, new File(outputFile));
  System.out.println("Saved " + outputFile 
   + " of all monitors " 
   + " xy=(" + allScreenBounds.x + "," + allScreenBounds.y + ")"
   + " bounds=("+ allScreenBounds.width + "," + allScreenBounds.height + ")");  
 } 
}


All this has been tested exactly once on a Windows box with two monitors of substantially different size; ymmv :)

11 comments:

Dustin Gamester said...

This seems to give errors when the first monitor is lower than the following monitors. If anyone else runs into this problem a corrected version can be found below. Also note that this version does not save a file, and instead returns a buffered image.


package book;

import java.awt.AWTException;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;

public class AllMonitors {

public BufferedImage getScreenshot() {

//get handle to graphics environment
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] screens = ge.getScreenDevices();

//if screens are detected proceed
if (screens.length > 0) {

try {
//get the first screen
GraphicsDevice a = screens[0];

//get the monitor resolution
Toolkit t = Toolkit.getDefaultToolkit();
Rectangle allScreenBounds = new Rectangle(t.getScreenSize());

//offset screen capture (in cases where first monitor is not the
//highest this will come into effect)
allScreenBounds.y = -1 * screens[0].getDefaultConfiguration().getBounds().y;

//take screenshot
Robot robot = new Robot();
BufferedImage screenShot = robot.createScreenCapture(allScreenBounds);

return screenShot;
} catch (AWTException ex) {
Logger.getLogger(AllMonitors.class.getName()).log(Level.SEVERE, null, ex);
}
}

//if no screens detected return null
return null;
}
}

Sorin Portase said...

The code to compute the virtual screen bounds can be found in the jdk's javadoc (http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/GraphicsConfiguration.html):

Rectangle virtualBounds = new Rectangle();
GraphicsEnvironment ge = GraphicsEnvironment.
getLocalGraphicsEnvironment();
GraphicsDevice[] gs =
ge.getScreenDevices();
for (int j = 0; j < gs.length; j++) {
GraphicsDevice gd = gs[j];
GraphicsConfiguration[] gc =
gd.getConfigurations();
for (int i=0; i < gc.length; i++) {
virtualBounds =
virtualBounds.union(gc[i].getBounds());
}
}

Scott Schafer said...

Thanks for this post, but especially +1000 to Dustin. If I had hair, I would have pulled it all out over this bug. Your comment saved me a lot of aggravation.

joe pelletier said...

add this to the end of the for loop and it will work with as many monitors as you have ::: allScreenBounds.x=Math.min(allScreenBounds.x, screenBounds.x);
allScreenBounds.y=Math.min(allScreenBounds.y, screenBounds.y);

joe pelletier said...

add this to the end of the for loop and it will work with as many monitors as you have ::: allScreenBounds.x=Math.min(allScreenBounds.x, screenBounds.x);
allScreenBounds.y=Math.min(allScreenBounds.y, screenBounds.y);

for IT the said...

I have read your blog its very attractive and impressive. I like it your blog.

Java Training in Chennai Core Java Training in Chennai Core Java Training in Chennai

Java Online Training Java Online Training Core Java 8 Training in Chennai Core java 8 online training JavaEE Training in Chennai Java EE Training in Chennai

Naviya Nair said...

I have read your blog its very attractive and impressive. I like it your blog.

Java Online Training Java EE Online Training Java EE Online Training Java 8 online training Core Java 8 online training

Java Online Training from India Java Online Training from India Core Java Training Online Core Java Training Online Java Training InstitutesJava Training Institutes

Asim Shaikh said...

Thanks for sharing the info, keep up the good work going.... I really enjoyed exploring your site. good resource...best modem router combo

Yogayogi said...

Great post! I am actually getting ready to across this information, It’s very helpful for this blog.Also great with all of the valuable information you have Keep up the good work you are doing well.
Data Science training in Chennai
Data science online training

priya said...

I appreciate that you produced this wonderful article to help us get more knowledge about this topic. I know, it is not an easy task to write such a big article in one day, I've tried that and I've failed. But, here you are, trying the big task and finishing it off and getting good comments and ratings. That is one hell of a job done!

Data Science course in Chennai
Data science course in bangalore
Data science course in pune
Data science online course
Data Science Interview questions and answers
Data Science Tutorial

manisha said...

Nice tutorial. Thanks for sharing the valuable information. it’s really helpful. Who want to learn this blog most helpful. Keep sharing on updated tutorials…
Best Devops Training in pune
Devops Training in Bangalore
Microsoft azure training in Bangalore
Power bi training in Chennai

Post a Comment