0

I'm creating a program in Java Swing, and I have run into a problem. The program has two images; one of the images is the full image, and it gets displayed in the image navigator. The other image is a zoomed-in portion of the full image and is displayed in another component.

I have in my main class something that goes like this whenever the part to view in the ZoomedImage container updates:

BufferedImage zoom = imageNavigator.getZoomedImage();
((ZoomedImage)container.getTopComponent()).updatePanel(zoom);

which calls:

public BufferedImage getZoomedImage(){
    BufferedImage img = image.getSubimage((int)(startX/scaleX), (int)(startY/scaleY), (int)(image.getWidth()*percentViewWidth), (int)(image.getHeight()*percentViewHeight));
    BufferedImage copyOfImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
    Graphics g = copyOfImage.createGraphics();
    g.drawImage(img, 0, 0, null);
    return copyOfImage;
}

I also call similar code when initializing the component.

startX/Y are the starting points of the box that shows the area it is zoomed in on. ScaleX/Y have to do with the scale between the size of the image being displayed and the size of the component displaying the image. percentViewWidth/Height are part of the zooming information.

After the components have all been packed, the functionality works fine. The zoomed image updates just fine whenever I make changes to what portion of the original image I'm looking at. The problem is that in order to do that, I have to depend on the size of the component holding the main image so that I can grab the correct section of the image. When it is first initializing, the size is 0, so I can't grab the correct section. How can I tell it to update the image immediately after it has a size?

Mandar Pandit
  • 2,171
  • 5
  • 36
  • 58
Joshua Zollinger
  • 757
  • 1
  • 8
  • 17
  • Maybe it's me, but I'm not getting a clear picture as to just what your problem currently is. – Hovercraft Full Of Eels Jun 14 '14 at 17:13
  • Sorry, let me try again. I have two components, each containing an image. One component (the imageNavigator) has the image as well as a box that highlights the area to zoom in on. The other component has a copy of the subimage inside that box. I have to have the size of the container so that I can do some calculations with it and grab the right subimage. But when the program first starts up, the size is 0, so the zoomed image isn't showing the proper location until an update occurs after initialization. – Joshua Zollinger Jun 14 '14 at 17:20
  • Please can you post a testable code for sooner help. – Braj Jun 14 '14 at 17:30
  • You might look at the approach examined [here](http://stackoverflow.com/q/11739989/230513). – trashgod Jun 14 '14 at 17:35

1 Answers1

1

If I understand the problem correctly, your approach is a bit backwards. Rather than having ImageNavigator tell ZoomedImage which subimage to paint, let ZoomedImage ask ImageNavigator at painting time (by overriding ZoomedImage's paintComponent method). When it is time for ZonedImage to do its painting, it will be fully laid out on screen and will know its size, so there will be no issue.

Alternatively, let ZoomedImage maintain the entire image and the coordinates of the displayed region, so it will have the complete information it needs to paint itself. When the user interacts with the ImageNavigator it can call ZoomedImage to set the coordinates of the displayed region.

To make this more concrete, here is a little example of the second approach. For brevity, I didn't make a "ZoomedImage" class -- I used a JScrollPane containing a JLabel of the image, but it does the job. On the right-hand side is the ImageNavigator that calls the "ZoomedImage component" (actually, the scrollpane's viewport) to get and set the coordinates of the displayed region. It draws a red rectangle highlighting that part. You can adjust the displayed region using the scrollbars, using the navigator, or by resizing the window. It's nothing too sophisticated but hopefully it contains some useful insight:

import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

class ImageNavigatorExample {
    public static void main(String[] args) throws Throwable {
        File imageFile = new File( // change file to one you have!
            "Shallow Waters in the South Pacific.jpg");
        BufferedImage image = javax.imageio.ImageIO.read(imageFile);

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        JScrollPane imageScroller = new JScrollPane(new JLabel(new ImageIcon(image)));
        imageScroller.setPreferredSize(new Dimension(640, 480));
        frame.add(imageScroller);
        frame.add(new ImageNavigator(image, imageScroller.getViewport()), BorderLayout.EAST);
        frame.pack();
        frame.setVisible(true);
    }


    private static class ImageNavigator extends JPanel {
        private final BufferedImage image;
        private final JViewport viewport;

        public ImageNavigator(BufferedImage image, final JViewport viewport) {
            this.image = image;
            this.viewport = viewport;
            viewport.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    repaint();
                }
            });
            MouseAdapter mouseAdapter = new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    int xImage = Math.round(e.getX() / scale());
                    int yImage = Math.round(e.getY() / scale());

                    viewport.setViewPosition(new Point(
                        Math.max(0, xImage - viewport.getExtentSize().width / 2),
                        Math.max(0, yImage - viewport.getExtentSize().height / 2)));
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    mousePressed(e);
                }
            };
            this.addMouseListener(mouseAdapter);
            this.addMouseMotionListener(mouseAdapter);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(150, image.getWidth() * 150 / image.getHeight());
        }

        private float scale() {
            return (float)this.getWidth() / image.getWidth();
        }

        @Override
        public void paintComponent(Graphics g1) {
            super.paintComponent(g1);

            Graphics2D g = (Graphics2D)g1.create();
            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g.scale(scale(), scale());
            g.drawImage(image, 0, 0, null);

            g.setColor(Color.red);
            g.setStroke(new BasicStroke(scale()));
            g.draw(viewport.getViewRect());
        }
    }
}

What it looks like:

screenshot

Boann
  • 48,794
  • 16
  • 117
  • 146