tagomago
tagomago

Reputation: 85

Drawn image inside panel seems to have wrong x,y offset

So, I have a JFrame with a menu, a toolbar and panel. I load images inside the panel but for some strange reason (to me at least) they are not correctly displayed in the panel. Sometimes they start under the toolbar, sometimes above. Furthermore the image is cut on bottom. The code is a cleaned example from the full one that can be compiled and tested. Thanks in advance.

Class of containing frame:

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.FileDialog;

    import java.awt.Toolkit;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.image.BufferedImage;
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.util.zip.ZipInputStream;

    import javax.imageio.ImageIO;
    import javax.swing.BorderFactory;
    import javax.swing.JButton;
    import javax.swing.JFileChooser;
    import javax.swing.JFrame;
    import javax.swing.JMenu;
    import javax.swing.JMenuBar;
    import javax.swing.JMenuItem;
    import javax.swing.JPanel;
    import javax.swing.JToolBar;
    import javax.swing.border.BevelBorder;
    import javax.swing.border.Border;
    public class SSCE extends JFrame {
        private JComicPanel panel;
        private JToolBar toolbar;
        private JButton buttonZoom;
        private JButton buttonPrev;
        private JButton buttonNext;
        private JMenuBar menuBar;

        private Dimension dim;
        private BufferedImage img;
        private int currentPage;
        private JFrame parentFrame;




                public SSCE(){

                    super("JComic");
                    BorderLayout layout = new BorderLayout();
                    setLayout(layout);

                    dim = Toolkit.getDefaultToolkit().getScreenSize();

                    setSize((int)(dim.width /2.5),dim.height);

                    this.setPreferredSize(new Dimension((int) (dim.width /2.5),dim.height));

                    createToolbar();
                    createPanel();

                    add(toolbar,BorderLayout.NORTH);    

                    add(panel,BorderLayout.CENTER);


                    createMenu();
                    add(menuBar);
                    setJMenuBar(menuBar);
                    panel.setVisible(true);
                    img = null;
                    pack();
                    parentFrame = this;

                }


                private void createPanel(){
                    Border raisedbevel, loweredbevel;
                    raisedbevel = BorderFactory.createRaisedBevelBorder();
                    loweredbevel = BorderFactory.createLoweredBevelBorder();
                    panel = new JComicPanel(img);
                    panel.setBorder(BorderFactory.createCompoundBorder(raisedbevel,loweredbevel));


                }
                private void createToolbar(){
                    toolbar = new JToolBar();
                    toolbar.setPreferredSize(new Dimension(dim.width,45));  
                    toolbar.setFloatable(false);
                    buttonZoom = new JButton("+");

                    toolbar.add(buttonZoom);
                    buttonPrev = new JButton("<-");
                    toolbar.add(buttonPrev);
                    buttonNext = new JButton("->");
                    toolbar.add(buttonNext);
                    toolbar.setBackground(Color.RED);


                }
                private void createMenu(){
                    JMenu menuFile,menuJComic;
                    JMenuItem fileOpen; 
                    JMenuItem quitJComic,aboutJComic;


                    menuBar = new JMenuBar();
                    menuJComic = new JMenu("JComic");
                    aboutJComic = new JMenuItem("About JComic...");
                    menuJComic.add(aboutJComic);
                    quitJComic = new JMenuItem("Quit");
                    quitJComic.addActionListener(
                            new ActionListener(){
                                public void actionPerformed(ActionEvent e) {
                                    System.exit(0);
                                }
                            }
                            );
                    menuJComic.add(quitJComic);

                    menuBar.add(menuJComic);

                    menuFile = new JMenu("File");

                    fileOpen = new JMenuItem("Open...");
                    fileOpen.addActionListener(
                            new ActionListener(){
                                public void actionPerformed(ActionEvent e) {
                                    try {


                                        img = ImageIO.read(new File("superman.jpg"));
                                        currentPage = 1;

                                        int offset = menuBar.getHeight() + toolbar.getHeight();
                                        panel.setImage(img,parentFrame,offset);


                                    }
                                    catch (IOException e1) {
                                        System.out.println(e1.getMessage());
                                    }

                                }
                            }
                            );

                    menuFile.add(fileOpen);
                    menuBar.add(menuFile);


                }

        /**
         * @param args
         */
        @SuppressWarnings("deprecation")
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            SSCE f = new SSCE();
            f.show();

        }

    }

Class of panel

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Image;
    import java.awt.RenderingHints;
    import java.awt.event.ComponentAdapter;
    import java.awt.event.ComponentEvent;
    import java.awt.image.BufferedImage;
    import java.io.File;

    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    import javax.swing.JPanel;

    public class JComicPanel extends JPanel{
        private BufferedImage img;
        private int offset;
        private final float scaling = 0.5f;
        public JComicPanel(BufferedImage img){
            super();
            this.img = img;
            addComponentListener(new ComponentAdapter() {
                @Override
                public void componentResized(ComponentEvent e)
                {
                    repaint();
                }
            });

        }




         public void setImage(BufferedImage img,JFrame parentFrame,int offset){



             try{

                 int w = img.getWidth();
                 int h = img.getHeight();
                 int newW = (int)(w * scaling);
                 int newH = (int)(h * scaling);
                 BufferedImage dimg =  new BufferedImage(newW, newH, img.getType());  
                 Graphics2D g = dimg.createGraphics();  
                 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);  
                 g.drawImage(img, 0, 0, newW, newH, 0, 0, w, h, null);  
                 this.img = img;
                 System.out.printf("dim %d x %d",newW,newH);


                 this.setSize(new Dimension(newW,newH+offset));

                 parentFrame.pack();
             }
             catch(Exception e){
             }


         }

         public Dimension getPreferredSize(){

             return new Dimension(img.getWidth(),img.getHeight());
         }

        public void paintComponent(Graphics g){

               // Draw our Image object.
               super.paintComponent(g);
               g.drawImage(img,0,0,getSize().width,getSize().height, this); // at location 50,10
               System.out.println("painting 2");
              }

    }

Image superman.jpg can be found here http://i50.tinypic.com/2yxnc3n.jpg. As you can see the image is below the toolbar, but in my full code it goes also above.

Upvotes: 0

Views: 2199

Answers (2)

kleopatra
kleopatra

Reputation: 51524

The technical reason is that you are temporarily adding the menuBar to the center of the contentPane, that is the same logical location as the previously added panel:

add(panel, BorderLayout.CENTER);
// following line is wrong - must be removed!!! 
add(menuBar); // no constraint == BorderLayout.CENTER
setJMenuBar(menuBar);

Doing so pushes the panel out off the control of the LayoutManager, but not out of the panel - the net effect is that it has no constraint.

That's not special to being a menuBar (which needs to be added to the layeredPane of the rootPane, not the contentPane - that's why it has its own specialized method setJMenuBar), the same mess would happen with any other arbitrary component at the center.

Apart from that, I would recommend you cleaning up your code:

  • remove all setXXSize (some reasons)
  • decide whether the frame should come up at its preferred (using pack) or an arbitrary fixed size (using setSize). The former is the correct thingy to do in most cases, the latter has its use-cases - but doing both doesn't make sense
  • components are visible by default

Upvotes: 2

MadProgrammer
MadProgrammer

Reputation: 347184

Let's start here...

public void setImage(BufferedImage img,JFrame parentFrame,int offset){
    try{
        // You're scaling the incoming image, which means you no longer
        // have a reference to the original image should you want to 
        // change that scale...
        // You're previous code...

        // Don't do this.  The parent container's layout manager will simple
        // override it, so it's useless...
        this.setSize(newW,newH);
        repaint();

        // This is a bad idea (personally)...
        parentFrame.setSize(newW,newH+offset);
    } catch(Exception e){
    }
 }

Calling setSize in this way will temporarily allow the component to assume the size you have set. If you were to call invalidate() after it, the component would actually be re-sized back to meet the requirements of the layout manager.

The larger problem is the fact that you are setting the parent frame's size, but you have no idea of the frame's layout requirements with regards to it's other components, such as the tool bar, menu bar and frame (for example)

This is drawing the image without consideration to the ratio of the original image

g.drawImage(img,0,0,getSize().width,getSize().height, this);

As it has being suggested, after scaling the image, it might be easier to simply set the image as the icon of a JLabel which has been added to the image pane using a BorderLayout

You're assuming that the size of the panel is correct, but it won't be as the panel's layout manager will take over.

This is redundant as Swing will automatically repaint the component when it's resized.

addComponentListener(new ComponentAdapter() {
    @Override
    public void componentResized(ComponentEvent e)
    {
        repaint();
    }
});

You're best bet is to either drop the image pane into a scroll pane and or rescale the image to fit the size of the pane dynamically.

For example...

Scalable Page

enter image description hereenter image description here

public class ComicPage {

    public static void main(String[] args) {
        new ComicPage();
    }

    public ComicPage() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                try {
                    BufferedImage page = ImageIO.read(getClass().getResource("/1346.gif"));
                    ComicPagePane comicPagePane = new ComicPagePane();
                    comicPagePane.setComicPage(page);

                    JFrame frame = new JFrame();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(comicPagePane);
                    frame.setSize(200, 200);
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (Exception exp) {
                    exp.printStackTrace();
                }
            }
        });
    }

    public class ComicPagePane extends JPanel {

        private BufferedImage comicPage;
        private Image scaledInstance;

        public void setComicPage(BufferedImage page) {
            if (page != comicPage) {
                comicPage = page;
                scaledInstance = null;
                repaint();
            }
        }

        @Override
        public void invalidate() {
            scaledInstance = null;
            super.invalidate(); 
        }

        public BufferedImage getComicPage() {
            return comicPage;
        }

        public double getScaleFactor(int iMasterSize, int iTargetSize) {
            return (double) iTargetSize / (double) iMasterSize;
        }

        public double getScaleFactorToFit(BufferedImage img) {
            double dScale = 1d;

            double dScaleWidth = getScaleFactor(img.getWidth(), getWidth());
            double dScaleHeight = getScaleFactor(img.getHeight(), getHeight());

            dScale = Math.min(dScaleHeight, dScaleWidth);

            return dScale;
        }

        protected Image getScaledInstance(BufferedImage master) {
            if (scaledInstance == null) {
                double scaleFactor = getScaleFactorToFit(master);
                System.out.println("scaleFactor = " + NumberFormat.getNumberInstance().format(scaleFactor));

                // This is not the best scaling alorithm
                scaledInstance = master.getScaledInstance(
                        (int) Math.round(master.getWidth() * scaleFactor),
                        (int) Math.round(master.getHeight() * scaleFactor), Image.SCALE_SMOOTH);
            }
            return scaledInstance;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            BufferedImage comicPage = getComicPage();
            if (comicPage != null) {
                Graphics2D g2d = (Graphics2D) g.create();

                int width = getWidth();
                int height = getHeight();

                // Normally, I would put this into a background worker as this
                // operation can be expensive....
                Image scaledInstance = getScaledInstance(comicPage);

                int x = (width - scaledInstance.getWidth(this)) / 2;
                int y = (height - scaledInstance.getHeight(this)) / 2;
                g2d.drawImage(scaledInstance, x, y, this);
                g2d.dispose();
            }
        }
    }
}

Scrollable Page

enter image description here

public class ScrollableComicPage {

    public static void main(String[] args) {
        new ScrollableComicPage();
    }

    public ScrollableComicPage() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                try {
                    BufferedImage page = ImageIO.read(getClass().getResource("/1346.gif"));
                    ComicPagePage comicPagePane = new ComicPagePage();
                    comicPagePane.setComicPage(page);

                    JFrame frame = new JFrame();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new JScrollPane(comicPagePane));
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (Exception exp) {
                    exp.printStackTrace();
                }
            }
        });
    }

    public class ComicPagePage extends JPanel {

        private BufferedImage comicPage;

        @Override
        public Dimension getPreferredSize() {
            return comicPage != null ? new Dimension(comicPage.getWidth(), comicPage.getHeight()) : new Dimension(0, 0);
        }

        public void setComicPage(BufferedImage page) {
            if (page != comicPage) {

                comicPage = page;
                invalidate();
                repaint();
            }
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (comicPage != null) {
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.drawImage(comicPage, 0, 0, this);
                g2d.dispose();
            }
        }
    }
}

You can have a read through Java: maintaining aspect ratio of JPanel background image for more information on scaling techniques.

Upvotes: 5

Related Questions