Why getScaledInstance() does not work? - java

So, I am trying to create a monopoly game. I am trying to load an image (of the board) onto a JPanel.
I first want to scale the image to a 1024*1024 image.
I already got the image to appear on the JPanel (so the file address works).
But whenever I use the getScaledInstance() method, the image doesn't appear
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.SystemColor;
//a class that represent the board (JFrame) and all of it components
public class Board extends JFrame {
private final int SCALE;
private JPanel panel;
public Board(int scale) {
getContentPane().setBackground(SystemColor.textHighlightText);
// SCALE = scale;
SCALE = 1;
// set up the JFrame
setResizable(false);
setTitle("Monopoly");
// set size to a scale of 1080p
setSize(1920 * SCALE, 1080 * SCALE);
getContentPane().setLayout(null);
panel = new JPanel() {
public void paint(Graphics g) {
Image board = new ImageIcon(
"C:\\Users\\Standard\\Pictures\\Work\\Monopoly 1.jpg")
.getImage();
board = board.getScaledInstance(1022, 1024, java.awt.Image.SCALE_SMOOTH);
g.drawImage(board, 0, 0, null);
}
};
panel.setBounds(592, 0, 1024, 1024);
getContentPane().add(panel);
}
public static void main(String[] args) {
Board board = new Board(1);
board.setVisible(true);
board.panel.repaint();
}
}
Whenever I remove the board.getScaledInstance() line of code, the image appears (though not scaled), but when I add the line of code, the image doesn't appear at all.
Why does this happen?

You're doing several things wrong:
You're overriding paint, not paintComponent. This is dangerous as you're overriding an image that does too much and has too much responsibility. Doing this without care can lead to significant image side effects, and will also lead to slow perceived animation due to paint not double buffering.
You're not calling the super painting method within your override, something that will lead to accumulation of painting artifacts and breaking of the Swing component painting chain.
You're reading in an image potentially multiple times and within a painting method, a method that must be as fast as possible since it's a major determinant in the perceived responsiveness of your application. Read it in once only, and then save it to a variable.
You're using null layouts and setBounds. While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
You're scaling the image within the paint method, again doing something that will slow down the perceived responsiveness of the GUI. Instead, scale the image once only, and save that scaled image to a variable.
Importantly, you're using the same variable, board, for both the original image and the scaled image, and this will lead to re-scaling of the image every time that paint is called.
As Mad points out, you should pass in this to your g.drawImage(...) method call, so that you don't draw an image before it's completely read in.
Also, don't read the image in as a File or as an ImageIcon when you're not using it as an ImageIcon. Use ImageIO to read it in as a BufferedImage, and use resources, not Files.
I would also simplify things, for example:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyBoard extends JPanel {
private static final String IMG_PATH = "http://ecx.images-amazon.com/"
+ "images/I/81oC5pYhh2L._SL1500_.jpg";
// scaling constants
private static final int IMG_WIDTH = 1024;
private static final int IMG_HEIGHT = IMG_WIDTH;
// original and scaled image variables
private BufferedImage initialImg;
private Image scaledImg;
public MyBoard() throws IOException {
URL url = new URL(IMG_PATH);
initialImg = ImageIO.read(url); // read in original image
// and scale it *once* and store in variable. Can even discard original
// if you wish
scaledImg = initialImg.getScaledInstance(IMG_WIDTH, IMG_HEIGHT,
Image.SCALE_SMOOTH);
}
// override paintComponent, not paint
#Override // and don't forget the #Override annotation
protected void paintComponent(Graphics g) {
super.paintComponent(g); // call the super's painting method
// just to be safe -- check that it's not null first
if (scaledImg != null) {
// use this as a parameter to avoid drawing an image before it's
// ready
g.drawImage(scaledImg, 0, 0, this);
}
}
// so our GUI is sized the same as the image
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet() || scaledImg == null) {
return super.getPreferredSize();
}
int w = scaledImg.getWidth(this);
int h = scaledImg.getHeight(this);
return new Dimension(w, h);
}
private static void createAndShowGui() {
MyBoard mainPanel = null;
try {
mainPanel = new MyBoard();
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
JFrame frame = new JFrame("My Board");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Related

Show multiple JPane images on a single JFrame

First off, I am new to JFrame and all the associated classes so I am still learning how to do this.
My current goal is to draw multiple images on a single JFrame. So far, I can get test2.png to draw, but not test1.png. Any suggestions or help understanding JFrame is appreciated. This is my main class:
package com.osj.oneshotjava;
import java.awt.Dimension;
import javax.swing.JFrame;
/**
*
* #author BCG04
*/
public class actorTest {
public static void main(String []args){
JFrame jFrame = new JFrame("OSJ actor test");
jFrame.setPreferredSize(new Dimension(640, 480)); // sets window size
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Actor2 Background = new Actor2(jFrame, "test1.png");
Actor2 testActor = new Actor2(jFrame, "test2.png");
jFrame.pack(); // automatically adjusts window size (also sets window size based on the maximum and minimum sizes)
jFrame.setVisible(true);
}
}
And this is Actor2:
package com.osj.oneshotjava;
import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
*
* #author BCG04
*/
public class Actor2 { //Purpose: make it easer to add multiple images to a single JFrame using only a single call to Actor2's constuctor rather than repeating the same section of code for each image.
private BufferedImage image = null;
private JLabel jLabel = null;
public Actor2(JFrame jFrame, String filename){
try
{ // try to load a image 'filename' into 'image'
image = ImageIO.read(new File(filename));
}
catch (Exception e)
{
e.printStackTrace(); // if loading fails, print the error
System.exit(1); // then exit with an error code 1 'unsuccessful exit'
}
ImageIcon imageIcon = new ImageIcon(image); // create a new ImageIcon that contains 'image'
JPanel jPanel = new JPanel();
jLabel = new JLabel();
jLabel.setIcon(imageIcon); // set JLabel 'jLabel' to contain 'imageIcon'
jPanel.add(jLabel);
jFrame.getContentPane().add(jPanel, BorderLayout.CENTER); // makes window visible?
}
public JLabel getJLabel(){
return jLabel;
}
}
Edit:
-removed Thread.sleep(1000); and setLocation(90, 90); since they were not relevant to the question or the problem and I originally had them in to test whether I could move images.
-removed jLabel.setBounds as it did not seem to do anything.
+added a comment clarifying Actor2's goal.
I'd like to clarify my end goal, I would like to create a simple 2d game that uses Java.
Here is a complete, self contained example that is close to what you're after. It is to demonstrate the use of a layout manager.
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
public class DuelingJLabels{
public static void startGui(){
JFrame frame = new JFrame();
JPanel scene = new JPanel();
Actor red = new Actor(Color.RED);
Actor blue = new Actor(Color.BLUE);
//scene.setLayout( null );
scene.add(red.image);
scene.add(blue.image);
//scene.setPreferredSize( new Dimension(512, 512) );
frame.add(scene, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
static class Actor{
int x, y;
JLabel image;
public Actor(Color c){
BufferedImage a = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
Graphics g = a.getGraphics();
g.setColor(c);
g.fillOval(0, 0, 64, 64);
image = new JLabel();
image.setIcon(new ImageIcon(a));
image.setLocation( x, y );
image.setSize( 64, 64);
image.addMouseListener( new MouseAdapter(){
#Override
public void mouseClicked(MouseEvent evt){
x = x+64;
if(x>=448){
x = 0;
y += 64;
}
image.setLocation(x, y);
}
});
}
}
public static void main(String[] args){
EventQueue.invokeLater( DuelingJLabels::startGui );
}
}
Take note of the line scene.setLayout(null); if you run the example with that line commented out, then you will see two circles side by side. That is because we are letting swing handle the layout. scene is a JPanel with a FlowLayout by default.
Now when you click the circles, nothing happens* because we tell the new position but the layout manager resets the position.
*Actually they move sometimes, but if you trigger a re-validation then they get moved by the layout manager.
So now remove the comment on scene.setLayout(null); and notice the difference.
The frame is tiny, and we have to manually resize it to see our scene.
There is only one circle.
If you click on the circle, it moves.
That's because we have told swing to not use a layout manager for the JPanel scene. That means it will not reposition the components in the scene for us, and it will not adjust the sizes for us either.
The other line that is commented setPreferredSize makes scene tell the parent component a size it would like to be at. If you uncomment that line then the JFrame will not start out incredibly small. You should only use that with custom components, otherwise you can end up conflicting with the layout manager.
Another tool, which I have found usefull is the JLayeredPane because it gives you some depth. I also think the example is good.
Finally, another technique for putting custom graphics arbitrarily is to #Override paintComponent. That way you can draw whatever, where-ever on your component.

Image drawing on a Java component

I'm trying to create a simple application that draws graphics...
package Tests.Drawing;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.border.EtchedBorder;
public class DrawingTest extends JFrame
{
private Canvas drwArea;
private JButton btnClear;
public static void main(String[] args)
{
DrawingTest StartForm = new DrawingTest();
StartForm.setVisible(true);
}
public DrawingTest()
{
//Window...
this.setTitle("Drawing objects test00");
this.setBounds(0,0,510,500);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(null);
//Drawing area...
drwArea = new Canvas();
drwArea.setBounds(0, 0, 400, 450);
drwArea.setBackground(Color.WHITE);
drwArea.setOpaque(true);
drwArea.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
drwArea.addMouseMotionListener(new MouseMotionAdapter()
{
#Override
public void mouseDragged(MouseEvent e)
{
//Write code to paint on the image...
}
});
this.getContentPane().add(drwArea);
//Clear button...
btnClear = new JButton("Clear");
btnClear.setBounds(410,50,70,30);
btnClear.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//Write code to clear the image...
}
});
this.getContentPane().add(btnClear);
}
private class Canvas extends JLabel
{
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
//The idea of overriding this method is
//achieving persistence...
}
}
}
I have seen that the typical component to draw on is a Jlabel (Is there anyone better by the way?). Using the method “getGraphics” I can use an object that has several methods to paint on the component. My problem is that instead of painting directly on the JLabel, I would like to paint on an image (in memory) and once the painting has finished, send the result to the JLabel. How can I do this? I'm a bit lost...
Thanks in advance.
I would like to paint on an image (in memory)
I suggest that you then create a BufferedImage object of the desired size, get its Graphics2D object by calling createGraphics() on it, and draw on it using this Graphics2D object.
and once the painting has finished, send the result to the JLabel
Then create an ImageIcon out of the BufferedImage above by simply calling
Icon myIcon = new ImageIcon(myBufferedImage);
and then setting the JLabel's icon via myLabel.setIcon(myIcon);
Also you could draw to a BufferedImage that is being displayed within a JPanel's paintComponent method, and this may be an even better way to go if you want to update the image while the program is running. For more on this, please have a look at some of these examples.
Other comments:
Don't draw with a Graphics object obtained by calling getGraphics() on a component. This will return a Graphics object that is short lived, risking disappearing graphics or worse, a NullPointerException. Instead, draw in the JPanel's paintComponent(...) method either directly, or indirectly by drawing on a BufferedImage (yes, you can get its Graphics object via getGraphics()) and then drawing the BufferedImage to the GUI within the paintComponent method.
While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.

Java graphics PaintComponent problems. Can't seem to find the mistake

I'm making a program for drawing out an Image and it seems I've made a mistake and my program just doesn't want to draw out the image. Can someone please point out the mistake for mi because i really don't see it.
package basic_game_programing;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Practise extends JPanel {
public Image image;
//#####PAINT__FUNCTION#####
public void PaintComponent(Graphics g){
super.paintComponent(g);
ImageIcon character = new ImageIcon("C:/Documents and Settings/Josip/Desktop/game Šlije/CompletedBlueGuy.PNG");
image = character.getImage();
g.drawImage(image,20,20,null);
g.fillRect(20, 20, 100, 100);
}
//######MAIN__FUCTION#######
public static void main(String[]args){
Practise panel = new Practise();
//SETTING UP THE FRAME
JFrame frame = new JFrame();
//
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,500);
frame.add(panel);
//SETTING UP THE PANEL
//
}
}
You're miscapitalizing paintComponent by using PaintComponent instead (note the first "P").
So Change PaintComponent to paintComponent.
Use the #Override annotation above the method to let the compiler tell you when you're making this kind of mistake.
Never read an image into a painting method since this slows down a method that needs to be fast, and makes the image be read in over and over when one read is enough.
The method should be protected not public.
Use ImageIO.read(...) to read in your image, and use resources and relative path within the jar file, rather than use files or ImageIcons.
Don't call setVisible(true) on the JFrame until after adding all components, else some might not show.
Do read the tutorials as most all of this is well explained there.
e.g.,
public class Practise extends JPanel {
private Image image;
public Practice() {
// read in your image here
image = ImageIO.read(.......); // fill in the ...
}
#Override // use override to have the compiler warn you of mistakes
protected void paintComponent(Graphics g){
super.paintComponent(g);
// never read within a painting method
// ImageIcon character = new ImageIcon("C:/Documents and Settings/Josip/Desktop/game Šlije/CompletedBlueGuy.PNG");
// image = character.getImage();
g.drawImage(image, 20, 20, this);
g.fillRect(20, 20, 100, 100);
}
}

Image not drawn in JPanel.paintComponent

I am actually want to load image but only applet dialog open and no error occurred but the image is not loading.the code is below here
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Ball extends JPanel{
Image image;
public Ball(){
super();
image = Toolkit.getDefaultToolkit().getImage("/D:\\lolololo\\tuto\\bin\\sa.jpg");
}
private Image getResource(String string) {
return image;
// TODO Auto-generated method stub
}
public void paintComponent(Graphics g){
// Draw our Image object.
g.drawImage(image,50,10,574,960, this); // at location 50,10
// 200 wide and high
}
public void main(String arg[]){
JFrame frame = new JFrame("ShowImage");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800,500);
Ball panel = new Ball();
frame.setContentPane(panel);
frame.setVisible(true);
}
}
The way you are loading the image is wrong. This will never work when you extract as Runnable jar.
Create package ("res") inside inside your src
Now load the image this way
image = ImageIO.read(Ball.class.getResource("/res/sa.jpg"));
This will work.
As indicated by Andrew in his comment main class should be
public static void main(String arg[]) {}
+1 to #AndrewThompsons comments.
1) Below is incorrect, you do not honer the paint chain by calling the supers implementation of super.paintComponent(...):
public void paintComponent(Graphics g) {
// Draw our Image object.
g.drawImage(image,50,10,574,960, this); // at location 50,10
// 200 wide and high
}
As per docs for paintComponent:
Further, if you do not invoker super's implementation you must honor
the opaque property, that is if this component is opaque, you must
completely fill in the background in a non-opaque color. If you do not
honor the opaque property you will likely see visual artifacts.
should be:
public class Ball extends JPanel {
BufferedImage image;
public Ball() {
super();
try {
image=ImageIO.read(new File("c:/test.jpg"));//change to your path of file
}catch(Exception ex) {//file did not load properly
ex.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
// Draw our Image object.
g.drawImage(image,0,0,image.getWidth(),image.getHeight(), this); // at location 50,10
// 200 wide and high
}
#Override
public Dimension getPreferredSize() {
return new Dimension(image.getWidth(),image.getHeight());//replace with size of image
}
}
Also notice:
I overrode getPreferredSize() of JPanel and returned Dimensions I wanted (i.e Image size) or the JPanel will only be as big as the components added to it and not the image (so if no components 0,0).
I also chose BufferedImage vs Image and surrounded the statement with a try catch to check if any errors are thrown.
I see you also had g.drawImage(image,50,10...) why 50 and 10 and not 0,0?
2) Also this (as #AndrewThompson has said):
image = Toolkit.getDefaultToolkit().getImage("/D:\\lolololo\\tuto\\bin\\sa.jpg");
No need for the / thats only if its located in your classes package etc not when its on your HDD at a set location.
3) also as said by #AndrewThompson a main method should be:
public static void main(String[] args){}
Notice the static modifer applied other wise its just another method.
4) Also dont use JFrame#setSize(..), rather use LayoutManager and/or override getPreferredSize and than simply call pack() on JFrame instance before setting it visible.
5) Also please have a read on Concurrency in Swing. especially the Event-Dispatch-Thread

Create an image from a non-visible AWT Component?

I'm trying to create an image (screen-shot) of a non-visible AWT component. I can't use the Robot classes' screen capture functionality because the component is not visible on the screen. Trying to use the following code:
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
component.paintAll(g);
Works sometimes, but does not work if the component contains things such as a text box or button, or some sort of OpenGL / 3D component (these things are left out of the image!). How can I take a proper screenshot of the whole thing?
(disclamer: woops.. this doesn't seem to work for AWT )-:
I can't believe no one has suggested SwingUtilities.paintComponent or CellRendererPane.paintComponent which are made for this very purpose. From the documentation of the former:
Paints a component to the specified Graphics. This method is primarily useful to render Components that don't exist as part of the visible containment hierarchy, but are used for rendering.
Here is an example method that paints a non-visible component onto an image:
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class ComponentPainter {
public static BufferedImage paintComponent(Component c) {
// Set it to it's preferred size. (optional)
c.setSize(c.getPreferredSize());
layoutComponent(c);
BufferedImage img = new BufferedImage(c.getWidth(), c.getHeight(),
BufferedImage.TYPE_INT_RGB);
CellRendererPane crp = new CellRendererPane();
crp.add(c);
crp.paintComponent(img.createGraphics(), c, crp, c.getBounds());
return img;
}
// from the example of user489041
public static void layoutComponent(Component c) {
synchronized (c.getTreeLock()) {
c.doLayout();
if (c instanceof Container)
for (Component child : ((Container) c).getComponents())
layoutComponent(child);
}
}
}
Here is a snippet of code that tests the above class:
JPanel p = new JPanel();
p.add(new JButton("Button 1"));
p.add(new JButton("Button 2"));
p.add(new JCheckBox("A checkbox"));
JPanel inner = new JPanel();
inner.setBorder(BorderFactory.createTitledBorder("A border"));
inner.add(new JLabel("Some label"));
p.add(inner);
BufferedImage img = ComponentPainter.paintComponent(p);
ImageIO.write(img, "png", new File("test.png"));
And here is the resulting image:
Component has a method paintAll(Graphics) (as you already have found). That method will paint itself on the passed graphics. But we have to pre-configure the graphics before we call the paint method. That's what I found about the AWT Component rendering at java.sun.com:
When AWT invokes this method, the
Graphics object parameter is
pre-configured with the appropriate
state for drawing on this particular
component:
The Graphics object's color is set to the component's foreground property.
The Graphics object's font is set to the component's font property.
The Graphics object's translation is set such that the coordinate (0,0) represents the upper left corner of the component.
The Graphics object's clip rectangle is set to the area of the component that is in need of repainting.
So, this is our resulting method:
public static BufferedImage componentToImage(Component component, Rectangle region)
{
BufferedImage img = new BufferedImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
Graphics g = img.getGraphics();
g.setColor(component.getForeground());
g.setFont(component.getFont());
component.paintAll(g);
g.dispose();
if (region == null)
{
return img;
}
return img.getSubimage(region.x, region.y, region.width, region.height);
}
This is also the better way instead of using Robot for the visible components.
EDIT:
A long time ago I used the code I posted here above, and it worked, but now not. So I searched further. I have a tested, working way. It is dirty, but works. The Idea of it is making a JDialog, put it somewhere out of the Screen bounds, set it visible, and then draw it on the graphics.
Here is the code:
public static BufferedImage componentToImageWithSwing(Component component, Rectangle region) {
BufferedImage img = new BufferedImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g = img.createGraphics();
// Real render
if (component.getPreferredSize().height == 0 && component.getPreferredSize().width == 0)
{
component.setPreferredSize(component.getSize());
}
JDialog f = new JDialog();
JPanel p = new JPanel();
p.add(component);
f.add(p);
f.pack();
f.setLocation(-f.getWidth() - 10, -f.getHeight() -10);
f.setVisible(true);
p.paintAll(g);
f.dispose();
// ---
g.dispose();
if (region == null) {
return img;
}
return img.getSubimage(region.x, region.y, region.width, region.height);
}
So, this will work also on Windows and Mac. The other answer was to draw it on a virtual screen. But this doesn't need it.
Excellent question, I've thought about this myself from time to time!
As you already have written, that rending heavy weight components such as 3D and AWT onto an image is a big problem. These components are (almost) directly transferred to the graphic card so they cannot be re-rendered to an image using the normal paintComponent stuff, you need help from the operative system or doing your own rendering of these components.
1. Making your own to image renderer
For each component that does not have a to image rendering method you need to create your own. For example using jogl you can take a off-screen screenshot using this method (SO post).
2. Rendering onto a virtual screen
Prerequisites:
Can you start the program/component in a headless environment?
Are you using Linux?
Then you can use Xvfb to render the whole program onto a virtual screen and then taking a screenshot from that virtual screen like this:
Xvfb :1 &
DISPLAY=:1 java YourMainClass
xwd -display :1 -root -out image.xwd
Maybe you need to tweek Xvfb a little bit by passing the size of the program you want to render to it (-screen 0 1024x768x24).
The Screen Image class shows how this can be done for Swing components. I've never tried it with AWT components, buy I could guess the concept would be the same.
How about something like this. The JFrame that holds all of the components is not visible.
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
/**
* Captures an invisible awt component
* #author dvargo
*/
public class ScreenCapture
{
private static List<String> types = Arrays.asList( ImageIO.getWriterFileSuffixes() );
/**
* Build GUI
* #param args
*/
public static void main(String [] args)
{
JFrame invisibleFrame = new JFrame();
invisibleFrame.setSize(300, 300);
JPanel colorPanel = new JPanel();
colorPanel.setBackground(Color.red);
colorPanel.setSize(invisibleFrame.getSize());
JTextArea textBox = new JTextArea("Here is some text");
colorPanel.add(textBox);
invisibleFrame.add(colorPanel);
JButton theButton = new JButton("Click Me");
colorPanel.add(theButton);
theButton.setVisible(true);
textBox.setVisible(true);
colorPanel.setVisible(true);
//take screen shot
try
{
BufferedImage screenShot = createImage((JComponent) colorPanel, new Rectangle(invisibleFrame.getBounds()));
writeImage(screenShot, "filePath");
}
catch (IOException ex)
{
Logger.getLogger(ScreenCapture.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Create a BufferedImage for Swing components.
* All or part of the component can be captured to an image.
*
* #param component component to create image from
* #param region The region of the component to be captured to an image
* #return image the image for the given region
*/
public static BufferedImage createImage(Component component, Rectangle region) {
// Make sure the component has a size and has been layed out.
// (necessary check for components not added to a realized frame)
if (!component.isDisplayable()) {
Dimension d = component.getSize();
if (d.width == 0 || d.height == 0) {
d = component.getPreferredSize();
component.setSize(d);
}
layoutComponent(component);
}
BufferedImage image = new BufferedImage(region.width, region.height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
// Paint a background for non-opaque components,
// otherwise the background will be black
if (!component.isOpaque()) {
g2d.setColor(component.getBackground());
g2d.fillRect(region.x, region.y, region.width, region.height);
}
g2d.translate(-region.x, -region.y);
component.paint(g2d);
g2d.dispose();
return image;
}
public static void layoutComponent(Component component) {
synchronized (component.getTreeLock()) {
component.doLayout();
if (component instanceof Container) {
for (Component child : ((Container) component).getComponents()) {
layoutComponent(child);
}
}
}
}
/**
* Write a BufferedImage to a File.
*
* #param image image to be written
* #param fileName name of file to be created
* #exception IOException if an error occurs during writing
*/
public static void writeImage(BufferedImage image, String fileName)
throws IOException
{
if (fileName == null) return;
int offset = fileName.lastIndexOf( "." );
if (offset == -1)
{
String message = "file suffix was not specified";
throw new IOException( message );
}
String type = fileName.substring(offset + 1);
if (types.contains(type))
{
ImageIO.write(image, type, new File( fileName ));
}
else
{
String message = "unknown writer file suffix (" + type + ")";
throw new IOException( message );
}
}
}

Categories