views:

85

answers:

4

I have a JPanel upon which I draw a number of custom-written JComponents using the usual 'paintComponent(Graphics g)' method. I use a JLayeredPane to control the display order of the custom components, as follows:

public Class MyPanel extends JPanel {
    private JLayeredPane layeredPane = new JLayeredPane();
    private JComponent component1;
    private JComponent component2;

    public MyPanel() {
        super();
        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));

        component1 = new CustomComponent1();
        layeredPane.add (component1, new Integer(0));

        component2 = new CustomComponent2();
        layeredPane.add (component2, new Integer(1));

        add (layeredPane);
    }

    public void resizePanel(Graphics g, int newWidth, int newHeight) {
        component1.setBounds (f(x), f(y), f(newWidth), f(newHeight));
        component2.setBounds (f(x), f(y), f(newWidth), f(newHeight));
    }

    public void paintComponent(Graphics g) {
        if ((getWidth() != oldWidth) || (getHeight() != oldHeight)) {
            oldWidth = getWidth();
            oldHeight = getHeight();
            resizePanel (g, getWidth(), getHeight());
        }
        super.paintComponent(g);
    }

Now, I would like to export this panel to a JPEG file, but with a different size. When I use the following code, it successfully creates/exports a JPEG file of the desired size, but it also updates my screen image version of the panel to the new size! Yikes!

public void export(File file, int width, int height)
    throws IOException
{
    BufferedImage scaledImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2 = scaledImage.createGraphics();
    resizePanel (g2, width, height);
    super.paintComponent (g2);

    try {
        OutputStream out = new FileOutputStream(file);
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
        encoder.encode(scaledImage);
        out.close();
    } catch (FileNotFoundException e) {
        throw new IOException ("Unable to export chart to ("
                  + file.getAbsolutePath() + "): " + e.getLocalizedMessage());
    } finally {
        g2.dispose();
    }
}

How can I 'draw' an image suitable for exporting, but not actually cause this new image to be displayed?

Thanks!

+1  A: 

Don't resize your panel at all; just create a BufferedImage that's the panel's current size and paint it. Once you've got the snapshot, scale it to your desired dimensions in a second BufferedImage using drawImage() or AffineTransformOp.

Addendum: One advantage of AffineTransformOp is control over rotation, interpolation, scale and aspect ratio:

BufferedImage image = ...
int w = image.getWidth();
int h = image.getHeight();
AffineTransform scaleTransform = new AffineTransform();
// last-in-first-applied: rotate, scale
scaleTransform.scale(scaleX, scaleY);
scaleTransform.rotate(Math.PI / 2, w / 2, h / 2);
AffineTransformOp scaleOp = new AffineTransformOp(
        scaleTransform, AffineTransformOp.TYPE_BILINEAR);
BufferedImage new = scaleOp.filter(image, null);
trashgod
Well, this method looks slightly better than doing the 'g2.scale()' n redrawing the components, but the text is still horizontally scrunched a bit.I'll go with this method for now.Thanks again. R
redBeard
+1  A: 

Looks like a second excellent solution. The final solution I have looks like the following:

public void export(File file, int width, int height)
throws IOException
{
    BufferedImage scaledImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2 = scaledImage.createGraphics();
    g2.scale(((double) width)/((double)getWidth()), ((double) height)/((double)getHeight()));
    paintComponents(g2);
    try {
        ImageIO.write(scaledImage, "jpg", file);            
    } catch (FileNotFoundException e) {
        throw new IOException ("Unable to export chart to ("
         + file.getAbsolutePath() + "): " + e.getLocalizedMessage());
    } finally {
        g2.dispose();
    }
}
redBeard
trashgod
A: 

Well, I'm back to this problem again.....

The scene I'm painting includes some text and now the end user wants the graph exported in a 'portrait' aspect ratio. Since I'm not repainting the scene in the new dimensions but just scaling the image, this causes the text to be seriously squashed horizontally.

Anyway around that?

redBeard
I'd recommend `AffineTransformOp`.
trashgod
A: 

Well, the only way I could get everything to look OK (e.g., no scaling of text after it's been rasterized) is to draw the image on a new graphics context using the new size. Here's my new 'export()' method.

    public void export(File file, final int width, final int height)
    throws IOException
{

    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    final Graphics2D g2 = image.createGraphics();

    //  Must wait for the bloody image to be drawn as Swing 'paint()' methods
    //  merely schedule painting events.  The 'draw()' below may not complete
    //  the painting process before the 'write()' of the image is performed.

    //  thus, we wait....

    try {
        SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
                draw (g2, new Rectangle (0, 0, width, height));
            }
        });
        ImageIO.write(image, "png", file);
    } catch (Exception e) {
        throw new IOException ("Unable to export chart to ("
                + file.getAbsolutePath() + "): " + e.getLocalizedMessage());
    } finally {
        g2.dispose();
    }
}

The 'draw()' method called above calls the 'setbounds()' method on all of my sub-components which draws them onto the new buffered image. A bit of work, but the result is exactly what I need.

redBeard