Skip to content

Mirroring an Image in Java

Background info: I’ve made a program that uploads an image using JFileChooser and fills in the space of the JFrame. I then apply filters via buttons. Currently 3 of my 4 filters work, or all except my mirror one. It randomly (or I guess not so randomly as there’s always a reason, I just don’t know what I did) worked a couple times.

My Question: I’ve attempted following a tutorial but had to make slight changes so that I can save it to a new file via a button that has a JFile Chooser. Currently when I save it, it does not mirror however my other filters do what there supposed to with the same slight changes. What is wrong with my mirror logic? Any help is appreciated!

My code:

Global Variables:

// Save Button
private final JButton saveButton = new JButton("Save Files");
private File selFile1;
private File output = new File("Output.png");
private File selFile2;
private String path1;

Upload file:

// Browse Files Button ActionListener
BrowseButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        JFileChooser getFile = new JFileChooser();
        getFile.setCurrentDirectory(new File(System.getProperty("user.home")));
        // Filter files
        FileNameExtensionFilter filter1 = new FileNameExtensionFilter("*.Images", "jpg", "png");
        getFile.addChoosableFileFilter(filter1);
        int res = getFile.showSaveDialog(null);
        if(res == JFileChooser.APPROVE_OPTION) {
            selFile1 = getFile.getSelectedFile();
            path1 = selFile1.getAbsolutePath();
            label.setIcon(resize(path1));
            System.out.println("1st selFile1 = " + selFile1);
            try {
                getImg = ImageIO.read(selFile1);
            } catch (IOException ex) {
                System.out.println(ex);
            } // End try-catch
            
        } // End if
    } // End actionPerformer
}); // End ActionListener
}); // End ActionListener

Save File:

    // Save Files Button ActionListener
    saveButton.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            JFileChooser getFile = new JFileChooser();
            getFile.setCurrentDirectory(new File(System.getProperty("user.home")));
            // Filter files
            FileNameExtensionFilter filter1 = new FileNameExtensionFilter("*.Images", "jpg",
                    "png");
            getFile.addChoosableFileFilter(filter1);
            int res = getFile.showSaveDialog(null);
            if(res == JFileChooser.APPROVE_OPTION) {
                selFile2 = getFile.getSelectedFile();
                path1 = selFile2.getAbsolutePath();
                label.setIcon(resize(path1));
                System.out.println("1st selFile1 = " + selFile2);                    
                try {
                   
                    ImageIO.write(getImg, "png", selFile2);
                } catch (IOException ex) {
                    System.out.println(ex);
                } // End try-catch
                
            } // End if
        } // End actionPerformer
    }); // End ActionListener

grayScale method that works:

grayScale.setMnemonic('G'); //Hot key
        // ActionListener for grayscale button
        grayScale.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                   
            File f = selFile1;

            // Read image
            try{
                getImg = ImageIO.read(f);
            } catch(IOException ef){
                System.out.println(ef);
            } // End try-catch

            // Get image width and height
            int width = getImg.getWidth();
            int height = getImg.getHeight();

            // Convert to grayscale
            for(int y = 0; y < height; y++){
                for(int x = 0; x < width; x++){
                    int p = getImg.getRGB(x,y);

                    int a = (p>>24)&0xff;
                    int r = (p>>16)&0xff;
                    int g = (p>>8)&0xff;
                    int b = p&0xff;

                    // Calculate average
                    int avg = (r+g+b)/3;

                    // Replace RGB value with avg
                    p = (a<<24) | (avg<<16) | (avg<<8) | avg;

                    getImg.setRGB(x, y, p);
                }
            } // End for
            JOptionPane.showMessageDialog(comboBoxPanel, "Gray Scale Filter has been applied. Save to view!");
            // Write image
            try {
                f = output;
                ImageIO.write(getImg, "png", f);
            } catch(IOException ex){
                System.out.println(ex);
            } // End try-catch
            
            } // End actionPerformed
        }); //End ActionListener

Mirror method that doesn’t work:

mirrored.setMnemonic('M'); // Hot Key
        // ActionListener for mirror button
        mirrored.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

            File f = selFile1;
            BufferedImage res = null;
            
            try{
                getImg = ImageIO.read(f);
                res = ImageIO.read(f);
            } catch(IOException ex){
                System.out.println(ex);
            } // End try-catch
            // Get source image dimension
            int width = getImg.getWidth();
            int height = getImg.getHeight();
            //BufferedImage for mirror image
            res = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            //create mirror image pixel by pixel
            for(int y = 0; y < height; y++){
                for(int lx = 0, rx = width - 1; lx < width; lx++, rx--){
                // lx starts from the left side of the image
                // rx starts from the right side of the image
                // Get source pixel value
                int p = getImg.getRGB(lx, y);
                // Set mirror image pixel value - both left and right
                res.setRGB(rx, y, p);
                }
            } // End for
            JOptionPane.showMessageDialog(comboBoxPanel, "Mirrored Filter has been applied. Save to view!");
            // Write image
            try{
              f = output;
              ImageIO.write(res, "png", f);
            } catch(IOException ex){
                System.out.println(ex);
            } // End try-catch
        } // End actionPerformed 
        }); // End actionListener

GUI (For Reference):

My GUI

Answer

So, speculation, you start out by doing…

File f = selFile1;
BufferedImage res = null;

try {
    getImg = ImageIO.read(f);
    res = ImageIO.read(f);
} catch (IOException ex) {
    System.out.println(ex);
} // End try-catch

And you end by doing this…

// Write image
try {
    f = output;
    ImageIO.write(res, "png", f);
} catch (IOException ex) {
    System.out.println(ex);
} // End try-catch

But, getImg is still pointing to the source (unmodified) image. If you’re using this to display the result to the user, then you’re displaying the wrong image. Based on you other “filter” operations, you’re modifying the getImg directly.

Now, having said that, your code is, questionable, at best.

Let me explain…

File f = selFile1;
BufferedImage res = null;

try {
    getImg = ImageIO.read(f);
    res = ImageIO.read(f);
} catch (IOException ex) {
    System.out.println(ex);
} // End try-catch

How do you know selFile1 is not null or that it points to a valid File? You’ve done no checking here. Also, what’s the point of reading the file twice, especially when you do res = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); immediately after it?

This raises another question, what happens if the image loading fails? You carry on trying to use the getImg reference anyway, which may contain “dirty” data or worse, be null.

Instead, I might do something more like…

if (selFile1 == null) {
    // Show error message
    // Use JOptionPane to show message to user
    return;
}

try {
    BufferedImage sourceImage = ImageIO.read(selFile1);
    // Get source image dimension
    int width = sourceImage.getWidth();
    int height = sourceImage.getHeight();
    //BufferedImage for mirror image
    BufferedImage mirroredImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    //create mirror image pixel by pixel
    for (int y = 0; y < height; y++) {
        for (int lx = 0, rx = width - 1; lx < width; lx++, rx--) {
            // lx starts from the left side of the image
            // rx starts from the right side of the image
            // Get source pixel value
            int p = sourceImage.getRGB(lx, y);
            // Set mirror image pixel value - both left and right
            mirroredImage.setRGB(rx, y, p);
        }
    } // End for
    JOptionPane.showMessageDialog(comboBoxPanel, "Mirrored Filter has been applied. Save to view!");
    // Write image
    ImageIO.write(mirroredImage, "png", selFile1);
} catch (IOException ex) {
    ex.printStackTrace();
    // Use JOptionPane to show message to user
} // End try-catch

Now you could also isolate the write within a inner try-catch, but the point is try and do some preemptive checking to prevent known issues from crashing your code.

Oh, and you might find…

BufferedImage mirrored = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = mirrored.createGraphics();
g2d.scale(-1, 1);
g2d.translate(-source.getWidth(), 0);
g2d.drawImage(source, 0, 0, null);
g2d.dispose();

And much simpler way to mirror an image