first working version
authorFrantišek Kučera <franta-hg@frantovo.cz>
Mon, 17 Nov 2014 17:02:48 +0100
changeset 78e9983260624
parent 6 b329573c76d7
child 8 4819598eb623
first working version
java/copy-image-resizer/src/cz/frantovo/copyImageResizer/RecursiveImageResizer.java
java/copy-image-resizer/src/cz/frantovo/copyImageResizer/RecursiveOptions.java
java/copy-image-resizer/src/cz/frantovo/copyImageResizer/SingleImageResizer.java
java/copy-image-resizer/src/cz/frantovo/copyImageResizer/SizeSpecification.java
java/copy-image-resizer/src/cz/frantovo/copyImageResizer/cli/CLIParser.java
     1.1 --- a/java/copy-image-resizer/src/cz/frantovo/copyImageResizer/RecursiveImageResizer.java	Mon Nov 17 01:17:02 2014 +0100
     1.2 +++ b/java/copy-image-resizer/src/cz/frantovo/copyImageResizer/RecursiveImageResizer.java	Mon Nov 17 17:02:48 2014 +0100
     1.3 @@ -18,12 +18,20 @@
     1.4  package cz.frantovo.copyImageResizer;
     1.5  
     1.6  import cz.frantovo.copyImageResizer.SingleImageResizer.ImageFormat;
     1.7 +import java.awt.image.BufferedImage;
     1.8  import java.io.File;
     1.9  import java.io.FileInputStream;
    1.10  import java.io.FileNotFoundException;
    1.11  import java.io.FileOutputStream;
    1.12 +import java.io.IOException;
    1.13 +import java.io.InputStream;
    1.14 +import java.nio.channels.Channels;
    1.15 +import java.nio.channels.FileChannel;
    1.16 +import java.nio.channels.ReadableByteChannel;
    1.17 +import java.nio.channels.WritableByteChannel;
    1.18  import java.util.logging.Level;
    1.19  import java.util.logging.Logger;
    1.20 +import javax.imageio.ImageIO;
    1.21  
    1.22  /**
    1.23   *
    1.24 @@ -39,24 +47,71 @@
    1.25  		resizeDirectory(options.getInput(), options);
    1.26  	}
    1.27  
    1.28 -	private void resizeFile(File file, RecursiveOptions options) throws ResizeException {
    1.29 -		log.log(Level.FINE, "Resizing file: {0}", relativize(options.getInput(), file));
    1.30 +	private void resizeFile(File inputFile, RecursiveOptions options) throws ResizeException {
    1.31 +		File inputFileRelative = relativize(options.getInput(), inputFile);
    1.32 +		log.log(Level.FINE, "Resizing file: {0}", inputFileRelative);
    1.33  
    1.34 -		ImageFormat format = ImageFormat.getMatching(file.getName());
    1.35 +		ImageFormat format = ImageFormat.getMatching(inputFile.getName());
    1.36  
    1.37  		if (format == null) {
    1.38 -			log.log(Level.FINE, "Skipping file: {0} (no image format matched this extension)", relativize(options.getInput(), file));
    1.39 +			log.log(Level.FINE, "Skipping file: {0} (no image format matched this extension)", inputFileRelative);
    1.40  		} else {
    1.41  			try {
    1.42 -				FileInputStream input = new FileInputStream(file);
    1.43 -				File relative = relativize(options.getInput(), file);
    1.44 -				File outputFile = new File(options.getOutput(), relative.getPath());
    1.45 -				FileOutputStream output = new FileOutputStream(outputFile);
    1.46 -				resizer.resize(input, output, format);
    1.47 +				for (SizeSpecification size : options.getSizes()) {
    1.48 +					File sizeRoot = new File(options.getOutput(), size.getDirectory());
    1.49 +					File outputFile = new File(sizeRoot, inputFileRelative.getPath());
    1.50 +					try (FileInputStream input = new FileInputStream(inputFile)) {
    1.51 +						BufferedImage image = readImage(input);
    1.52 +						if (shouldResize(image, size)) {
    1.53 +							try (FileOutputStream output = new FileOutputStream(outputFile)) {
    1.54 +								resizer.resize(image, output, size, format);
    1.55 +							}
    1.56 +						} else {
    1.57 +							log.log(Level.INFO, "File: {0} has already required size → just copy", inputFileRelative);
    1.58 +							justCopy(inputFile, outputFile);
    1.59 +						}
    1.60 +					}
    1.61 +				}
    1.62  			} catch (FileNotFoundException e) {
    1.63  				throw new ResizeException("Error while opening stream", e);
    1.64 +			} catch (IOException e) {
    1.65 +				throw new ResizeException("Error while closing stream", e);
    1.66  			}
    1.67  		}
    1.68 +	}
    1.69 +
    1.70 +	private static boolean shouldResize(BufferedImage input, SizeSpecification requested) {
    1.71 +		if (requested.isResizeSmaller()) {
    1.72 +			return input.getHeight() != requested.getHeight() || input.getWidth() != requested.getWidth();
    1.73 +		} else {
    1.74 +			return input.getHeight() > requested.getHeight() || input.getWidth() > requested.getWidth();
    1.75 +		}
    1.76 +	}
    1.77 +
    1.78 +	private static BufferedImage readImage(InputStream input) throws ResizeException {
    1.79 +		try {
    1.80 +			return ImageIO.read(input);
    1.81 +		} catch (IOException e) {
    1.82 +			throw new ResizeException("Unable to read image from stream", e);
    1.83 +		}
    1.84 +	}
    1.85 +
    1.86 +	private static void justCopy(File inputFile, File outputFile) throws ResizeException {
    1.87 +		try {
    1.88 +
    1.89 +			if (!outputFile.exists()) {
    1.90 +				outputFile.createNewFile();
    1.91 +			}
    1.92 +
    1.93 +			try (FileChannel input = new FileInputStream(inputFile).getChannel()) {
    1.94 +				try (FileChannel output = new FileOutputStream(outputFile).getChannel()) {
    1.95 +					output.transferFrom(input, 0, input.size());
    1.96 +				}
    1.97 +			}
    1.98 +
    1.99 +		} catch (IOException e) {
   1.100 +			throw new ResizeException("Unable copy stream/channel", e);
   1.101 +		}
   1.102  
   1.103  	}
   1.104  
   1.105 @@ -64,9 +119,12 @@
   1.106  
   1.107  		log.log(Level.FINE, "Resizing directory: {0}", directory);
   1.108  
   1.109 -		File relative = relativize(options.getInput(), directory);
   1.110 -		File output = new File(options.getOutput(), relative.getPath());
   1.111 -		output.mkdirs();
   1.112 +		for (SizeSpecification size : options.getSizes()) {
   1.113 +			File relative = relativize(options.getInput(), directory);
   1.114 +			File sizeRoot = new File(options.getOutput(), size.getDirectory());
   1.115 +			File dir = new File(sizeRoot, relative.getPath());
   1.116 +			dir.mkdirs();
   1.117 +		}
   1.118  
   1.119  		for (File entry : directory.listFiles()) {
   1.120  			if (entry.isDirectory()) {
     2.1 --- a/java/copy-image-resizer/src/cz/frantovo/copyImageResizer/RecursiveOptions.java	Mon Nov 17 01:17:02 2014 +0100
     2.2 +++ b/java/copy-image-resizer/src/cz/frantovo/copyImageResizer/RecursiveOptions.java	Mon Nov 17 17:02:48 2014 +0100
     2.3 @@ -63,34 +63,4 @@
     2.4  	public void addSize(SizeSpecification size) {
     2.5  		sizes.add(size);
     2.6  	}
     2.7 -
     2.8 -	public static class SizeSpecification {
     2.9 -
    2.10 -		private final int width;
    2.11 -		private final int height;
    2.12 -		private final String directory;
    2.13 -
    2.14 -		public SizeSpecification(int width, int height, String directory) {
    2.15 -			this.width = width;
    2.16 -			this.height = height;
    2.17 -			this.directory = directory;
    2.18 -		}
    2.19 -
    2.20 -		public int getWidth() {
    2.21 -			return width;
    2.22 -		}
    2.23 -
    2.24 -		public int getHeight() {
    2.25 -			return height;
    2.26 -		}
    2.27 -
    2.28 -		public String getDirectory() {
    2.29 -			return directory;
    2.30 -		}
    2.31 -
    2.32 -		@Override
    2.33 -		public String toString() {
    2.34 -			return width + "×" + height + " → " + directory;
    2.35 -		}
    2.36 -	}
    2.37  }
     3.1 --- a/java/copy-image-resizer/src/cz/frantovo/copyImageResizer/SingleImageResizer.java	Mon Nov 17 01:17:02 2014 +0100
     3.2 +++ b/java/copy-image-resizer/src/cz/frantovo/copyImageResizer/SingleImageResizer.java	Mon Nov 17 17:02:48 2014 +0100
     3.3 @@ -20,8 +20,9 @@
     3.4  import java.awt.Graphics2D;
     3.5  import java.awt.image.BufferedImage;
     3.6  import java.io.IOException;
     3.7 -import java.io.InputStream;
     3.8  import java.io.OutputStream;
     3.9 +import java.util.logging.Level;
    3.10 +import java.util.logging.Logger;
    3.11  import java.util.regex.Pattern;
    3.12  import javax.imageio.ImageIO;
    3.13  
    3.14 @@ -31,10 +32,11 @@
    3.15   */
    3.16  public class SingleImageResizer {
    3.17  
    3.18 -	public void resize(InputStream input, OutputStream output, ImageFormat outputFormat) throws ResizeException {
    3.19 +	private static final Logger log = Logger.getLogger(SingleImageResizer.class.getName());
    3.20 +
    3.21 +	public void resize(BufferedImage input, OutputStream output, SizeSpecification size, ImageFormat outputFormat) throws ResizeException {
    3.22  		try {
    3.23 -			BufferedImage image = ImageIO.read(input);
    3.24 -			BufferedImage resized = resize(image, 64, 64, image.getType());
    3.25 +			BufferedImage resized = resize(input, size.getWidth(), size.getHeight(), input.getType());
    3.26  
    3.27  			ImageIO.write(resized, outputFormat.getFormat(), output);
    3.28  		} catch (IOException e) {
    3.29 @@ -56,6 +58,11 @@
    3.30  			height = maxHeight;
    3.31  		}
    3.32  
    3.33 +		if (type == BufferedImage.TYPE_CUSTOM) {
    3.34 +			log.log(Level.FINE, "Setting default image type: from TYPE_CUSTOM to TYPE_INT_ARGB");
    3.35 +			type = BufferedImage.TYPE_INT_ARGB;
    3.36 +		}
    3.37 +
    3.38  		BufferedImage resized = new BufferedImage(width, height, type);
    3.39  		Graphics2D g = resized.createGraphics();
    3.40  		g.drawImage(original, 0, 0, width, height, null);
    3.41 @@ -78,6 +85,9 @@
    3.42  			this.regex = regex;
    3.43  		}
    3.44  
    3.45 +		/**
    3.46 +		 * @return format name for {@linkplain ImageIO#write(java.awt.image.RenderedImage, java.lang.String, java.io.File)
    3.47 +		 */
    3.48  		public String getFormat() {
    3.49  			return format;
    3.50  		}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/java/copy-image-resizer/src/cz/frantovo/copyImageResizer/SizeSpecification.java	Mon Nov 17 17:02:48 2014 +0100
     4.3 @@ -0,0 +1,58 @@
     4.4 +/**
     4.5 + * copy-image-resizer
     4.6 + * Copyright © 2014 František Kučera (frantovo.cz)
     4.7 + *
     4.8 + * This program is free software: you can redistribute it and/or modify
     4.9 + * it under the terms of the GNU General Public License as published by
    4.10 + * the Free Software Foundation, either version 3 of the License, or
    4.11 + * (at your option) any later version.
    4.12 + *
    4.13 + * This program is distributed in the hope that it will be useful,
    4.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    4.16 + * GNU General Public License for more details.
    4.17 + *
    4.18 + * You should have received a copy of the GNU General Public License
    4.19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
    4.20 + */
    4.21 +package cz.frantovo.copyImageResizer;
    4.22 +
    4.23 +/**
    4.24 + *
    4.25 + * @author Ing. František Kučera (frantovo.cz)
    4.26 + */
    4.27 +public class SizeSpecification {
    4.28 +
    4.29 +	private final int width;
    4.30 +	private final int height;
    4.31 +	private final String directory;
    4.32 +	private final boolean resizeSmaller;
    4.33 +
    4.34 +	public SizeSpecification(int width, int height, String directory, boolean resizeSmaller) {
    4.35 +		this.width = width;
    4.36 +		this.height = height;
    4.37 +		this.directory = directory;
    4.38 +		this.resizeSmaller = resizeSmaller;
    4.39 +	}
    4.40 +
    4.41 +	public int getWidth() {
    4.42 +		return width;
    4.43 +	}
    4.44 +
    4.45 +	public int getHeight() {
    4.46 +		return height;
    4.47 +	}
    4.48 +
    4.49 +	public String getDirectory() {
    4.50 +		return directory;
    4.51 +	}
    4.52 +
    4.53 +	public boolean isResizeSmaller() {
    4.54 +		return resizeSmaller;
    4.55 +	}
    4.56 +
    4.57 +	@Override
    4.58 +	public String toString() {
    4.59 +		return width + "×" + height + " → " + directory;
    4.60 +	}
    4.61 +}
     5.1 --- a/java/copy-image-resizer/src/cz/frantovo/copyImageResizer/cli/CLIParser.java	Mon Nov 17 01:17:02 2014 +0100
     5.2 +++ b/java/copy-image-resizer/src/cz/frantovo/copyImageResizer/cli/CLIParser.java	Mon Nov 17 17:02:48 2014 +0100
     5.3 @@ -18,6 +18,7 @@
     5.4  package cz.frantovo.copyImageResizer.cli;
     5.5  
     5.6  import cz.frantovo.copyImageResizer.RecursiveOptions;
     5.7 +import cz.frantovo.copyImageResizer.SizeSpecification;
     5.8  import java.io.File;
     5.9  import java.util.Arrays;
    5.10  import java.util.Collection;
    5.11 @@ -52,11 +53,21 @@
    5.12  		return options;
    5.13  	}
    5.14  
    5.15 -	private static String fetchNext(String[] args, int index) throws CLIParserException {
    5.16 +	/**
    5.17 +	 *
    5.18 +	 * @param args
    5.19 +	 * @param index
    5.20 +	 * @param option name of the option for error messages | if null, the previous argument is used
    5.21 +	 * as option name
    5.22 +	 * @return
    5.23 +	 * @throws CLIParserException
    5.24 +	 */
    5.25 +	private static String fetchNext(String[] args, int index, String option) throws CLIParserException {
    5.26  		if (index < args.length) {
    5.27  			return args[index];
    5.28  		} else {
    5.29 -			throw new CLIParserException("Expecting value for option: " + args[index - 1]);
    5.30 +			option = option == null ? args[index - 1] : option;
    5.31 +			throw new CLIParserException("Expecting value for option: " + option);
    5.32  		}
    5.33  	}
    5.34  
    5.35 @@ -66,7 +77,7 @@
    5.36  					@Override
    5.37  					public int parse(String[] args, int index, RecursiveOptions options) throws CLIParserException {
    5.38  						int originalIndex = index;
    5.39 -						String name = fetchNext(args, ++index);
    5.40 +						String name = fetchNext(args, ++index, args[originalIndex]);
    5.41  						options.setInput(new File(name));
    5.42  						return index - originalIndex;
    5.43  					}
    5.44 @@ -75,7 +86,7 @@
    5.45  					@Override
    5.46  					public int parse(String[] args, int index, RecursiveOptions options) throws CLIParserException {
    5.47  						int originalIndex = index;
    5.48 -						String name = fetchNext(args, ++index);
    5.49 +						String name = fetchNext(args, ++index, args[originalIndex]);
    5.50  						options.setOutput(new File(name));
    5.51  						return index - originalIndex;
    5.52  					}
    5.53 @@ -84,10 +95,11 @@
    5.54  					@Override
    5.55  					public int parse(String[] args, int index, RecursiveOptions options) throws CLIParserException {
    5.56  						int originalIndex = index;
    5.57 -						int width = parseInt(fetchNext(args, ++index));
    5.58 -						int height = parseInt(fetchNext(args, ++index));
    5.59 +						int width = parseInt(fetchNext(args, ++index, args[originalIndex]));
    5.60 +						int height = parseInt(fetchNext(args, ++index, args[originalIndex]));
    5.61 +						boolean resizeSmaller = parseBoolean(fetchNext(args, ++index, args[originalIndex]));
    5.62  						String directory = width + "x" + height;
    5.63 -						options.addSize(new RecursiveOptions.SizeSpecification(width, height, directory));
    5.64 +						options.addSize(new SizeSpecification(width, height, directory, resizeSmaller));
    5.65  						return index - originalIndex;
    5.66  					}
    5.67  				},
    5.68 @@ -95,10 +107,11 @@
    5.69  					@Override
    5.70  					public int parse(String[] args, int index, RecursiveOptions options) throws CLIParserException {
    5.71  						int originalIndex = index;
    5.72 -						int width = parseInt(fetchNext(args, ++index));
    5.73 -						int height = parseInt(fetchNext(args, ++index));
    5.74 -						String directory = fetchNext(args, ++index);
    5.75 -						options.addSize(new RecursiveOptions.SizeSpecification(width, height, directory));
    5.76 +						int width = parseInt(fetchNext(args, ++index, args[originalIndex]));
    5.77 +						int height = parseInt(fetchNext(args, ++index, args[originalIndex]));
    5.78 +						boolean resizeSmaller = parseBoolean(fetchNext(args, ++index, args[originalIndex]));
    5.79 +						String directory = fetchNext(args, ++index, args[originalIndex]);
    5.80 +						options.addSize(new SizeSpecification(width, height, directory, resizeSmaller));
    5.81  						return index - originalIndex;
    5.82  					}
    5.83  				};
    5.84 @@ -125,6 +138,17 @@
    5.85  			}
    5.86  		}
    5.87  
    5.88 +		private static boolean parseBoolean(String value) throws CLIParserException {
    5.89 +			switch (value) {
    5.90 +				case "true":
    5.91 +					return true;
    5.92 +				case "false":
    5.93 +					return false;
    5.94 +				default:
    5.95 +					throw new CLIParserException("Value „" + value + "“ is not a valid boolean. Expecting „true“ or „false“.");
    5.96 +			}
    5.97 +		}
    5.98 +
    5.99  		/**
   5.100  		 * Parse String arguments and fill values into the options object.
   5.101  		 *