4

I'm trying to save the main viewport and headers of a JScrollPane (larger than screen) to PNG image files. I created 3 classes extending JPanel (MainTablePanel, MapsHeaderPanel and ItemsHeaderPanel) and set them to the viewports. Each of them has this method:

public BufferedImage createImage() {
    BufferedImage bi = new BufferedImage(getSize().width, getSize().height, BufferedImage.TYPE_INT_ARGB); 
    Graphics g = bi.createGraphics();
    paint(g); 
    g.dispose();
    return bi;
}

Each class has also a paint method, which paints the background and then call the super.paint() to paint some label. For example:

public void paint(Graphics g){
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, getWidth(), getHeight());
        g.setColor(new Color(255, 255, 0, 50));
        // for loop that paints some vertical yellow lines
        for(int i=0; i<getWidth(); i+=K.mW){
            g.fillRect(i-1, 0, 2, getHeight());
            if(i%(K.mW*5)==0){
                g.fillRect(i-2, 0, 4, getHeight());
            }
        }
        // called to pain some rotated JLabels
        super.paint(g);
    }

From an external JFrame I then tried to save them to PNG file, using this code:

BufferedImage tableImg = mainTableP.createImage();
BufferedImage topImg = mapsHeaderP.createImage();
BufferedImage leftImg = itemsHeaderP.createImage();

ImageIO.write(tableImg, "png", new File(s.homeDir+"/table.png"));
ImageIO.write(topImg, "png", new File(s.homeDir+"/top.png"));
ImageIO.write(leftImg, "png", new File(s.homeDir+"/left.png"));

This is a screenshot of the application running: screenshot

And this is the header exported: top

If I comment the "super.paint(g)" instruction, I obtain a correct image (thus without all JLables, clearly). It seems like the second paint (super.paint(g)) is painted shifted into the BufferedImage and taking elements outside its JPanel. Somebody could explain me this behaviour? Thank you.

========== EDIT for SSCCE ====================================

This should compile. You can execute it as it is, and in c:\ you'll find two images (top.png and left.png) that should be the same as the two headers. Unfortunately, they are not. Background is not painted. Moreover (especially if you look at left.png) you can see that the labels are painted twice and shifted (note, for example, "Left test 21").

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.*;

public class Main {

    public static void main(String[] args) {

        JFrame frame = new JFrame();
        frame.setLayout(null);
        frame.setSize(800, 600);

        JScrollPane scrollP = new JScrollPane();
        scrollP.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        scrollP.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

        MyPanel top = new MyPanel();
        for(int i=0; i<30; i++){
            JLabel label = new JLabel("Test "+i);
            label.setOpaque(false);
            label.setBounds(50*i, 40, 50, 20);
            label.setForeground(Color.GREEN);
            top.add(label);
        }
        top.setLayout(null);
        top.setOpaque(false);
        top.setPreferredSize(new Dimension(50*30, 200));
        top.validate();

        MyPanel left = new MyPanel();
        for(int i=0; i<30; i++){
            JLabel label = new JLabel("Left test "+i);
            label.setBounds(0, 50*i, 100, 20);
            label.setForeground(Color.RED);
            left.add(label);
        }
        left.setLayout(null);
        left.setOpaque(false);
        left.setPreferredSize(new Dimension(200, 50*30));

        MyPanel center = new MyPanel();
        center.setLayout(null);
        center.setOpaque(false);
        center.setPreferredSize(new Dimension(50*30, 50*30));

        scrollP.setViewportView(center);
        scrollP.setColumnHeaderView(top);
        scrollP.setRowHeaderView(left);

        scrollP.setBounds(0, 50, 750, 500);
        frame.add(scrollP);

        frame.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);

        try{
            BufferedImage topImg = top.createImage();
            ImageIO.write(topImg, "png", new File("C:/top.png"));

            BufferedImage leftImg = left.createImage();
            ImageIO.write(leftImg, "png", new File("C:/left.png"));
        }catch(Exception e){
            e.printStackTrace();
        }
    }


}

class MyPanel extends JPanel{
    public void paint(Graphics g){
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, getWidth(), getHeight());
        g.setColor(new Color(255, 255, 0, 50));
        for(int i=0; i<getWidth(); i+=50){
            g.fillRect(i-1, 0, 2, getHeight());
        }
        super.paint(g); // COMMENT this line to obtain background images
    }

    public BufferedImage createImage() {
        BufferedImage bi = new BufferedImage(getSize().width, getSize().height, BufferedImage.TYPE_INT_ARGB); 
        Graphics g = bi.createGraphics();
        paint(g);
        g.dispose();
        return bi;
    }
}
  • what if you call `super.paint(g);` as first line in `paint();`? – Nikolay Kuznetsov Dec 13 '12 at 11:19
  • @Nikolay Kuznetsov probably not about viewport – mKorbel Dec 13 '12 at 11:33
  • 1
    @Sebastian Ikaros Rizzo this question isn't answerable, for better help sooner post an [SSCCE](http://sscce.org/), short, runnable, compilable – mKorbel Dec 13 '12 at 11:37
  • 1
    Check [this thread](http://stackoverflow.com/questions/7369814/why-does-the-jtable-header-not-appear-in-the-image) for tips on rendering components. – Andrew Thompson Dec 13 '12 at 12:23
  • @NikolayKuznetsov background is painted over labels, and I obtain the very same image as not calling super.paint(g) at all – Sebastian Ikaros Rizzo Dec 13 '12 at 14:17
  • When I run the example I get two images with the complete component that is being scrolled over for the top and left headers. Could you post images from the example too? Is there something to do with the version of java in use? – Sarah Happy Dec 14 '12 at 23:01

1 Answers1

0

I was unable to reproduce any anomalous behaviour, but I am going to guess that you forgot to shift the image paint origin (g.translate()) to match your expectations. Also, for safety, do not use Image type ARGB. Use image type RGB. It is entirely possible that your specific java implementation is to blame. Implementations are under no contract to only call paint on subcomponents only once.

warren
  • 563
  • 2
  • 13