写真をリサイズして余白付き正方形にする Java プログラム

# はじめに
## やりたかったこと
- Instagram に写真を投稿するとき、余白付きの正方形にしたい
- 大きい画像はサーバーサイドで再圧縮されるのでリサイズしたい
- PC で編集してから投稿したかったのでスマホアプリはめんどい
    - 既存のデスクトップアプリにありそうだけど勉強も兼ねて作ってみた

## 環境
- Java SE 8u201
- JavaFX 8
- macOS Mojave 10.14.3

# コード
## 何ができるか
- 画像を 1080 * 1350 以内にアスペクト比維持でリサイズ
- 1080 * 1080 正方形に収めて余白をつける（白 or 黒）（オプション）
    - 余白不要ならば `f`
    - 白余白なら `w`、黒余白なら `b`
- コマンドラインでオプションと元画像ディレクトリ与えたら PNG で出力する
    - フォルダならその中の画像ファイル全てを対象に
    - 出力は `resized_` を前につけて元画像と同じディレクトリに

## 全文
```java:image-resizer.java
package main;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.AreaAveragingScaleFilter;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.awt.image.ImageProducer;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.imageio.ImageIO;


import javafx.application.Application;
import javafx.stage.Stage;

public class Main extends Application {

	public static boolean makeSquare = true;;
	
	public static ArrayList<File> dirFiles = new ArrayList<File>();
	public static BufferedImage inputImage;
	public static double maxWidth = 1080;
	public static double maxHeight = 1350;
	
	public static int outputWidth;
	public static int outputHeight;

	public static int[] startCoodinate = {0, 0};
	public static Color background;
	
	public static String outputPreffix = "resized_";
	public static File output;
	public static String extension = "png";
	public static List<String> readble = Arrays.asList("jpeg", "jpg", "gif", "png", "bmp");
	
	
	public static void main(String[] args) {
		launch(args);
	}

	@Override
	public void start(Stage primaryStage) throws Exception {
        Parameters params = getParameters();
		List<String> args = params.getRaw();
		
		switch(args.get(0)) {
		case "f":
			makeSquare = false;
			break;
		case "w":
			background = Color.white;
			break;
		case "b":
			background = Color.black;
			break;
		default:
			System.out.println("Input 'f', 'w' or 'b' as 1st argument.");
			System.exit(0);
		}
		
		if (args.size() > 1) {
			for (int i = 1; i < args.size(); i++) {
				System.out.println("Processing " + args.get(i) + ".");
				if (checkDirectory(args.get(i))) {
					for (File f: dirFiles) {
						System.out.println("Processing " + f.getName() + ".");
						initialize(f);
						scaleImage(inputImage, output, outputWidth, outputHeight);
					}
					dirFiles.clear();
				} else {
					try {
						initialize(new File(args.get(i)));
						scaleImage(inputImage, output, outputWidth, outputHeight);
					} catch (Exception e){
						System.out.println(new File(args.get(i)).getName() + " could not be loaded.");
					}
				}
			}
			System.exit(0);
		} else {
			System.out.println("Input file or directory name as 2nd and later arguments.");
			System.exit(0);
		}
	}
	
	public static void initialize(File input) throws Exception {
		inputImage = ImageIO.read(input);
	    calculateSize(inputImage);
	        
	    String fileNameWithoutExtension = input.getName().substring(0, input.getName().lastIndexOf(".") + 1);
	    output = new File(input.getParent(), outputPreffix + fileNameWithoutExtension + extension);
	}
	
	public static void calculateSize(BufferedImage org) {
		if (makeSquare) {
			maxHeight = maxWidth;
		}
		double scale = Math.min(maxWidth / org.getWidth(), maxHeight / org.getHeight());
		outputWidth = (int)(org.getWidth() * scale);
		outputHeight = (int)(org.getHeight() * scale);
		if (makeSquare) {
			startCoodinate[0] = (int)Math.max(0, (maxWidth - outputWidth) / 2);
			startCoodinate[1] = (int)Math.max(0, (maxHeight - outputHeight) / 2);
		}
	}
	
	public static boolean checkDirectory(String dir) {
		File inputDir = new File(dir);
		if(inputDir.isDirectory()) {
			for (File f: inputDir.listFiles()) {
				if (readble.contains(f.getName().substring(f.getName().lastIndexOf(".") + 1).toLowerCase())) {
					dirFiles.add(f);
				} else {
					System.out.println(f.getName() + " was skipped. Only JPG, GIF, PNG, BMP are supported.");
				}
			}
		}
		return inputDir.isDirectory();
	}
	
	public static void scaleImage(BufferedImage org, File out, int width, int height){
        try {
            ImageFilter filter = new AreaAveragingScaleFilter(width, height);
            ImageProducer p = new FilteredImageSource(org.getSource(), filter);
            Image dstImage = Toolkit.getDefaultToolkit().createImage(p);
            
            if (makeSquare) {
            	outputWidth = (int)maxWidth;
            	outputHeight = (int)maxHeight;
            }
            BufferedImage dst = new BufferedImage((int)outputWidth , (int)outputHeight, BufferedImage.TYPE_INT_ARGB);
        	Graphics2D g = dst.createGraphics();
        	g.setColor(background);
        	g.fillRect(0, 0, dst.getWidth(), dst.getHeight());
            g.drawImage(dstImage, Math.max(0, startCoodinate[0]), Math.max(0, startCoodinate[1]), null);
            g.dispose();
			ImageIO.write(dst, extension, out);
			System.out.println(out.getName() + " was successed.");
		} catch (IOException e) {
			System.out.println(out.getName() + " could not be written.");
		}
    }

}
```

## 使いやすくするために
- Windows マシンでは以下のバッチファイルを使うとラク
- もっと言うと、`cmd /c "C:\*\image-resizer.bat"` をタスクバーに入れるとラク

```bash:image-resizer.bat
@echo off
setlocal
set /p dir="対象のファイルまたはフォルダ（複数指定可）: "
set /p makeSqare="余白を追加して正方形にしますか？(y/n): "
if %makeSqare%==y (
    set /p firstAug="余白の色は白または黒です(w/b): "
)
if %makeSqare%==n (
    set firstAug=f
)
echo 処理を開始します
java -jar image-resizer.jar %firstAug% %dir%
echo リサイズされた PNG が元画像と同じフォルダに保存されました
pause
```

# 参考
[画像リサイズ - Qiita](https://qiita.com/tool-taro/items/1f414424b31a86e97446) の [saka1029](https://qiita.com/saka1029) さんのコメント