Link to home
Start Free TrialLog in
Avatar of Kyle Hamilton
Kyle HamiltonFlag 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
Avatar of momi_sabag
momi_sabag
Flag of United States of America image

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
Avatar of 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
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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. :)
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?
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?
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
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?  :)
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
@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
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
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 :)
OK, can you post that resizing method?
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

..of course for AffineTransform I could calculate the ratio - newWidth/oldWidth - doh!
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
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.
thanks.
:)