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 *