import java.io.File;
import java.awt.Color;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.ArrayList;
import javax.swing.JLabel;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.imageio.ImageIO;
/**
* 把图片集里的所有图片,按照尺寸最小的图片的长宽,进行统一裁剪
*
* 1. 拖动图片目录到命令行,开始处理图片目录
*
* 2. 为了防止图片再次损失,统一生成 PNG,需要进一步缩减大小
*
* 3. 不想操作的图片设置隐藏,记住这个只针对 Windows 系统有效
*
* 4. 图片被窗口调出后,鼠标点击要裁剪的范围,记住黄色方框范围一定要在图片内
*
* @Author: NeedJava@126.com
*
* @Version: 2025.12.14
*/
public final class CropImageToSameSize {
public static final int MIN_SIZE = 10;
public static final int IGNORE_SIZE = 1;
public static final String[] OPTIONS = new String[] {"确定裁剪", "取消裁剪", "退出软件"};
public static void main(final String[] args) throws Exception {
final java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
while (true) {
System.out.println("\r\nPlease input source folder:");
final String source = reader.readLine().trim();
final File sourceFolder = new File(source.startsWith("\"") && source.endsWith("\"") ? source.substring(1, source.length() - 1) : source);
if (sourceFolder.isDirectory() == false) {
System.err.println("Source folder not exists! " + sourceFolder);
continue;
}
askForBounds(getMinSize(sourceFolder, Integer.MAX_VALUE, Integer.MAX_VALUE));
System.out.println("\r\nDONE");
}
}
public static Result getMinSize(final File parent, final int initMinWidth, final int initMinHeight) throws Exception {
final Result result = new Result();
if (parent == null || initMinWidth < MIN_SIZE || initMinHeight < MIN_SIZE) {
System.err.println("parent is null or width: " + initMinWidth + " height: " + initMinHeight);
return result;
}
final String[] children = parent.list();
if (children == null) {
return result;
}
int minWidth = initMinWidth, minHeight = initMinHeight;
if (minWidth > minHeight) { //横向
//Do nothing
} else{
final int temp = minWidth; //对调
minWidth = minHeight;
minHeight = temp;
}
File file = null; BufferedImage image = null; int width = -1, height = -1;
for (int i = 0; i < children.length; i ++) {
file = new File(parent, children[i]);
if (file.isDirectory() || file.isHidden()) {
continue;
}
try {
image = ImageIO.read(file); width = image.getWidth(); height = image.getHeight();
if (width < MIN_SIZE || height < MIN_SIZE) {
continue;
}
if (width > height) { //同为横向
if (minWidth > width) {
minWidth = width;
}
if (minHeight > height) {
minHeight = height;
}
} else { //对调
if (minWidth > height) {
minWidth = height;
}
if (minHeight > width) {
minHeight = width;
}
}
result.mFiles.add(file);
System.out.println("ADDED " + file + " " + minWidth + " " + minHeight);
} catch (Exception e) {
System.err.println("Failed to read image: " + file + " " + e.getMessage());
}
}
result.mSizeLarge = minWidth / IGNORE_SIZE * IGNORE_SIZE; result.mSizeSmall = minHeight / IGNORE_SIZE * IGNORE_SIZE;
return result;
}
public static void askForBounds(final Result result) {
if (result == null || result.mFiles == null) {
System.err.println("result is null");
return;
}
final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
if (screenSize == null) {
System.err.println("screenSize is null");
return;
}
final int previewSize = Math.min(screenSize.width, screenSize.height) / 6 * 5;
File file = null; BufferedImage image = null; int width = -1, height = -1;
double scale = 0.0F; int scaledWidth = -1, scaledHeight = -1; SelectableLabel label = null;
for (int i = 0, count = result.mFiles.size(); i < count; i ++) {
file = result.mFiles.get(i);
if (file.isDirectory() || file.isHidden()) {
continue;
}
try {
image = ImageIO.read(file); width = image.getWidth(); height = image.getHeight();
if (width < MIN_SIZE || height < MIN_SIZE) {
continue;
}
if (width > height) { //获取显示在屏幕上的 Image,给 JLabel 用
scale = ((double) previewSize / width);
scaledWidth = previewSize; scaledHeight = (int) (scale * height);
label = new SelectableLabel(new ImageIcon(image.getScaledInstance(scaledWidth, scaledHeight, Image.SCALE_DEFAULT)), (int) (result.mSizeLarge * scale), (int) (result.mSizeSmall * scale));
//System.out.println(width + "x" + height + " scaled: " + scaledWidth + "x" + scaledHeight + " preview: " + previewSize + " scale: " + scale + " " + result.mSizeLarge + "x" + result.mSizeSmall + " " + (int) (result.mSizeLarge * scale) + "x" + (int) (result.mSizeSmall * scale));
} else {
scale = ((double) previewSize / height);
scaledWidth = (int) (scale * width); scaledHeight = previewSize;
label = new SelectableLabel(new ImageIcon(image.getScaledInstance(scaledWidth, scaledHeight, Image.SCALE_DEFAULT)), (int) (result.mSizeSmall * scale), (int) (result.mSizeLarge * scale));
//System.out.println(width + "x" + height + " scaled: " + scaledWidth + "x" + scaledHeight + " preview: " + previewSize + " scale: " + scale + " " + result.mSizeLarge + "x" + result.mSizeSmall + " " + (int) (result.mSizeSmall * scale) + "x" + (int) (result.mSizeLarge * scale));
}
final int selected = JOptionPane.showOptionDialog(null, label, "选择裁剪位置", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, OPTIONS, OPTIONS[0]);
if (selected == JOptionPane.YES_OPTION) {
System.out.println("Selected: YES_OPTION");
if (width > height) {
cropImage(file, image, (int) (label.mOffsetX / scale), (int) (label.mOffsetY / scale), result.mSizeLarge, result.mSizeSmall);
} else {
cropImage(file, image, (int) (label.mOffsetX / scale), (int) (label.mOffsetY / scale), result.mSizeSmall, result.mSizeLarge);
}
} else if (selected == JOptionPane.NO_OPTION) {
System.out.println("Selected: NO_OPTION");
} else if (selected == JOptionPane.CANCEL_OPTION) {
System.out.println("Selected: CANCEL_OPTION");
break;//System.exit(0);
}
} catch (Exception e) {
System.err.println("Failed to read image: " + file + " " + e.getMessage());
}
}
}
public static void cropImage(final File file, final BufferedImage image, final int x, final int y, final int width, final int height) {
try {
//System.out.println(x + " " + y + " " + width + " " + height);
final File outFolder = new File(file.getParent(), "OUT__PUT"); outFolder.mkdirs();
final File outFile = new File(outFolder, file.getName());
ImageIO.write(image.getSubimage(x, y, width, height), "PNG", outFile);
System.out.println("CROPED " + file + " to " + outFile);
} catch (Exception e) {
System.err.println("Failed to crop image: " + file + " " + e.getMessage());
}
}
}
class Result {
public int mSizeLarge;
public int mSizeSmall;
public final List<File> mFiles = new ArrayList<>();
}
class SelectableLabel extends JLabel implements MouseListener {
private static final long serialVersionUID = 1L;
public int mOffsetX;
public int mOffsetY;
public int mWidth;
public int mHeight;
public SelectableLabel(final ImageIcon icon, final int width, final int height) {
super(icon);
this.mWidth = (icon == null ? -1 : width);
this.mHeight = (icon == null ? -1 : height);
this.addMouseListener(SelectableLabel.this);
}
@Override
protected final void paintComponent(final Graphics g) {
super.paintComponent(g);
if (mWidth < 10 || mHeight < 10) {
return;
}
g.setColor(Color.YELLOW);
g.drawRect(mOffsetX, mOffsetY, mWidth - 1/**/, mHeight - 1/**/);
}
@Override
public final void mouseClicked(final MouseEvent e) {
if (e == null) {
System.err.println("event is null");
return;
}
mOffsetX = e.getX(); mOffsetY = e.getY();
invalidate(); repaint();
//System.out.println("CLICK AT: " + e.getX() + " " + e.getY());
}
@Override
public final void mousePressed(final MouseEvent e) {
}
@Override
public final void mouseReleased(final MouseEvent e) {
}
@Override
public final void mouseEntered(final MouseEvent e) {
}
@Override
public final void mouseExited(final MouseEvent e) {
}
}