Avatar of Kyle Hamilton
Kyle Hamilton
Flag for United States of America asked on

image resize and save - Java

Hi,

I'm trying to write a (hopefully) simple java application that allows the user to select a bunch of images on their hard-drive, resize them, and save them to a folder on their hard-drive. (it will not be web based - just a local app)

I've gotten as far as selecting the image files, and displaying the file names as a string in JScrollPane. (by using a toString method I wrote: toString(files))

I have a separate SaveImage class that does the saving, but only one hardcoded file at a time:

saveButton.addActionListener(new SaveImage("strawberry.jpg"));

How do I pass all the file names to the save button?

(please bear with me, I'm trying to learn)

Thanks,
Koza
Java

Avatar of undefined
Last Comment
CEHJ

8/22/2022 - Mon
momi_sabag

sounds like the button click should fire some method that will loop through all the selected files and save them one at a time
you might want to do that in a separate thread so that the user interface won't hang
Kyle Hamilton

ASKER
OK. I made a loop, but I'm being asked to save each image individually. I would like to be able click the save button and have all the images be saved at once to one folder. Is this possible?

for(int i = 0; i < biArr.length; i++){
    	
             File saveFile = new File("image"+i);
             JFileChooser chooser = new JFileChooser();
             chooser.setSelectedFile(saveFile);
             int rval = chooser.showSaveDialog(chooser);
             if (rval == JFileChooser.APPROVE_OPTION) {
                 saveFile = chooser.getSelectedFile();
                 /* Write the filtered image in the selected format,
                  * to the file chosen by the user.
                  */
                 try {
                     ImageIO.write(biArr[i], "jpg", saveFile);
                 } catch (IOException ex) {
                 }
             }
         }

Open in new window



Thanks.

I haven't gotten as far as multithreading in my Java ventures yet. Is this something you can show me or would that be too complex?
ASKER CERTIFIED SOLUTION
CEHJ

Log in or sign up to see answer
Become an EE member today7-DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform
Sign up - Free for 7 days
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
Not exactly the question you had in mind?
Sign up for an EE membership and get your own personalized solution. With an EE membership, you can ask unlimited troubleshooting, research, or opinion questions.
ask a question
Kyle Hamilton

ASKER
I added the multiple file selection, but I'm not sure what else I need to do to make it work.

Would you be so kind and help me with my code?

The biArr is the BufferedImage array. If I use setSelectedFiles(), do I still need a loop?

public void actionPerformed(ActionEvent e) {

    	for(int i = 0; i < biArr.length; i++){
    	
             File saveFile = new File("image"+i);
             JFileChooser chooser = new JFileChooser();
             
             chooser.setMultiSelectionEnabled(true); // now what?
             
            // chooser.setSelectedFiles(biArr); // // I need an array of files, but what i have is an array of BufferedImages, so this is incorrect.

             chooser.setSelectedFile(saveFile);
             
             int rval = chooser.showSaveDialog(chooser);
             
             if (rval == JFileChooser.APPROVE_OPTION) {
                 saveFile = chooser.getSelectedFile();
                 
                 try {
                   ImageIO.write(biArr[i], "jpg", saveFile); // what about here?
                 } catch (IOException ex) {
                 }
             }
         }
    }

Open in new window

Please help. :)
Experts Exchange is like having an extremely knowledgeable team sitting and waiting for your call. Couldn't do my job half as well as I do without it!
James Murphy
CEHJ

If I use setSelectedFiles(), do I still need a loop?

Why would you call setSelectedFiles? Surely your objective is to get the files the user has selected?
Kyle Hamilton

ASKER
What I'm trying to do is this:

1. User selects a bunch of images from his hard drive.
2. Users selects new image size (probably some checkboxes)
3. User clicks save and images get resized and saved to directory of user's choice.

My problem is I'm not familiar with java GUI so I'm finding this more difficult than it probably should be. I have the first step working. I haven't done anything with the resize functionality yet. And the save step almost works except that user is prompted for a directory for each file.

I took most of my existing code from examples on oracle's website and I need help customizing it.

I really appreciate your patience. Would it help if posted all my code for this?
CEHJ

Would it help if posted all my code for this?

Not really, let's just take it a step at a time. For step 1. you need a method (most probably) that returns List<File>. Get that done first
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
Kyle Hamilton

ASKER
Done. That's actually the part I have working as desired.. I think.

It returns a File[] array. Actually it doesn't return it, it just writes the files to my "private File[] files" array in my FileChooser class (which also contains my main method, and the createAndShowGUI method, if that info is of any interest)

I then wrote a little toString method that displays the file names in the pane. Just so I could see what files were selected.

What's next?  :)
CEHJ

The next thing is probably to write a method that takes three parameters

a. That File[] array
b. The desired size
c.  A File object representing the target directory

More from me tomorrow - very late here
Kyle Hamilton

ASKER
@CEHJ,

I sooo appreciate you helping me. I'm really exited to get this working. I'll see what I can do with the above suggestion, and will let you know how I got on... :)))

Thanks!
Koza
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
CEHJ

Before that, i think on reflection it would be a good idea to get the central functionality working. Google scaling an image. For best results, use AffineTransform with rendering hints. You want a method that accepts BufferedImage as a parameter and returns the same
Kyle Hamilton

ASKER
Great. I got myself a couple of nice scaling methods. Both use Graphics2D with hints, but not AffineTransform. My first problem with AffineTransform was that it took a ratio as parameters for resizing, and I need specific pixel dimensions. I got both graphics2d methods working, and eventually I'll have to decide which one to go with - I'm not sure which one I like better yet. One gives me a slightly "sharper" outcome and the other is more smooth. Probably for really small images the sharper one might be better, and for larger images, the smoother one...

Anyway, I got that working. Here's a great article on resizing:
http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html

Ready for the next step :)
CEHJ

OK, can you post that resizing method?
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
Kyle Hamilton

ASKER
I'm going to go with this one. It gives me interpolation options:

/**
     * Convenience method that returns a scaled instance of the
     * provided {@code BufferedImage}.
     *
     * @param img the original image to be scaled
     * @param targetWidth the desired width of the scaled instance,
     *    in pixels
     * @param targetHeight the desired height of the scaled instance,
     *    in pixels
     * @param hint one of the rendering hints that corresponds to
     *    {@code RenderingHints.KEY_INTERPOLATION} (e.g.
     *    {@code RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR},
     *    {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR},
     *    {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC})
     * @param higherQuality if true, this method will use a multi-step
     *    scaling technique that provides higher quality than the usual
     *    one-step technique (only useful in downscaling cases, where
     *    {@code targetWidth} or {@code targetHeight} is
     *    smaller than the original dimensions, and generally only when
     *    the {@code BILINEAR} hint is specified)
     * @return a scaled version of the original {@code BufferedImage}
     */
    public BufferedImage scaledImage(BufferedImage img,
                                           int targetWidth,
                                           int targetHeight,
                                           Object hint,
                                           boolean higherQuality)
    {
        int type = (img.getTransparency() == Transparency.OPAQUE) ?
            BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
        BufferedImage ret = (BufferedImage)img;
        int w, h;
        if (higherQuality) {
            // Use multi-step technique: start with original size, then
            // scale down in multiple passes with drawImage()
            // until the target size is reached
            w = img.getWidth();
            h = img.getHeight();
        } else {
            // Use one-step technique: scale directly from original
            // size to target size with a single drawImage() call
            w = targetWidth;
            h = targetHeight;
        }
        
        do {
            if (higherQuality && w > targetWidth) {
                w /= 2;
                if (w < targetWidth) {
                    w = targetWidth;
                }
            }

            if (higherQuality && h > targetHeight) {
                h /= 2;
                if (h < targetHeight) {
                    h = targetHeight;
                }
            }

            BufferedImage tmp = new BufferedImage(w, h, type);
            Graphics2D g2 = tmp.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
            g2.drawImage(ret, 0, 0, w, h, null);
            g2.dispose();

            ret = tmp;
        } while (w != targetWidth || h != targetHeight);

        return ret;
    }

Open in new window

Kyle Hamilton

ASKER
..of course for AffineTransform I could calculate the ratio - newWidth/oldWidth - doh!
CEHJ

That should be ok without AffineTransform. It should be called something like 'getScaledImage' though. You should be getting close to being able to integrate it now. Feel free to get in touch via my profile btw
Experts Exchange has (a) saved my job multiple times, (b) saved me hours, days, and even weeks of work, and often (c) makes me look like a superhero! This place is MAGIC!
Walt Forbes
Kyle Hamilton

ASKER
Yes it was called that. I changed it cause it was easier for me type and remember ( in relation to the other methods I was trying out). I'll probably change it back  now.

Everything is working but I'm back to my original quandary in that I would like all the images to be saved to the user's directory of choice in one step. As it is I am prompted for the directory for each image.
Kyle Hamilton

ASKER
thanks.
CEHJ

:)
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.