Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Split image into clickable regions

Is there any way to split an image to region (right now it's JLabel but I can change it if necessary)?
I use swing in my program and I have an image (square for this example) with some triangles, stars and trapezoids inside it (it can be JPG, PNG, etc).
The idea is that the user will click inside one of those shapes and then I will put another small icon on top of the area the user clicked. The user can click on multiple areas but at the end of the day, I need to know which shapes were clicked.

Seems possible anyone?

like image 951
lsxliron Avatar asked Dec 15 '22 19:12

lsxliron


2 Answers

Have a look at what I have made:

This is the image I used for testing:

Original Image

After image has been split:

After Image splitting

And here is the source:

import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class Test {

    private JFrame frame;
    private JLabel[] labels;
    private static String imagePath = "c:/test.jpg";
    private final int rows = 3; //You should decide the values for rows and cols variables
    private final int cols = 3;
    private final int chunks = rows * cols;
    private final int SPACING = 10;//spacing between split images

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Test().createAndShowUI();
            }
        });
    }

    private void createAndShowUI() {
        frame = new JFrame("Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        initComponents();
        frame.setResizable(false);
        frame.pack();
        frame.setVisible(true);
    }

    private void initComponents() {

        BufferedImage[] imgs = getImages();

        //set contentpane layout for grid
        frame.getContentPane().setLayout(new GridLayout(rows, cols, SPACING, SPACING));

        labels = new JLabel[imgs.length];

        //create JLabels with split images and add to frame contentPane
        for (int i = 0; i < imgs.length; i++) {
            labels[i] = new JLabel(new ImageIcon(Toolkit.getDefaultToolkit().createImage(imgs[i].getSource())));
            frame.getContentPane().add(labels[i]);
        }
    }

    private BufferedImage[] getImages() {
        File file = new File(imagePath); // I have bear.jpg in my working directory
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        }
        BufferedImage image = null;
        try {
            image = ImageIO.read(fis); //reading the image file
        } catch (IOException ex) {
            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        }
        int chunkWidth = image.getWidth() / cols; // determines the chunk width and height
        int chunkHeight = image.getHeight() / rows;
        int count = 0;
        BufferedImage imgs[] = new BufferedImage[chunks]; //Image array to hold image chunks
        for (int x = 0; x < rows; x++) {
            for (int y = 0; y < cols; y++) {
                //Initialize the image array with image chunks
                imgs[count] = new BufferedImage(chunkWidth, chunkHeight, image.getType());

                // draws the image chunk
                Graphics2D gr = imgs[count++].createGraphics();
                gr.drawImage(image, 0, 0, chunkWidth, chunkHeight, chunkWidth * y, chunkHeight * x, chunkWidth * y + chunkWidth, chunkHeight * x + chunkHeight, null);
                gr.dispose();
            }
        }
        return imgs;
    }
}

The only flaw is I haven't checked if the image is larger then the screen which could cause problems, that would be resolved by a simple image resize using getScaledInstance(int x,int y, int width, in height) on the image and the separating it into chunks.

Update

Sorry I missed the part if the question in Shapes, have a look at draw(Shape s) method of Graphics2D/Graphics.

I read this:

Any Shape object can be used as a clipping path that restricts the portion of the drawing area that will be rendered. The clipping path is part of the Graphics2D context; to set the clip attribute, you call Graphics2D.setClip and pass in the Shape that defines the clipping path you want to use.

See here for clipping an u]image to a shape: Clipping the Drawing Region

References:

  • How to Split an Image into Chunks - Java ImageIO
like image 82
David Kroukamp Avatar answered Jan 03 '23 10:01

David Kroukamp


You can use the getSubImage() method of BufferedImage, illustrated here and here. The example also uses JLabel, but you can add the Icon to a JButton that can be clicked. There are several ways for a button to remember details about it's icon:

  • Subclass JButton and add a suitable field.
  • Add a client property to the parent JComponent.
  • Use the name property of the parent Component.
like image 20
trashgod Avatar answered Jan 03 '23 09:01

trashgod