Skip to content

Instantly share code, notes, and snippets.

@taka2
Created November 12, 2009 08:34
Show Gist options
  • Select an option

  • Save taka2/232736 to your computer and use it in GitHub Desktop.

Select an option

Save taka2/232736 to your computer and use it in GitHub Desktop.
import java.awt.*;
import java.awt.dnd.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.text.*;
import javax.imageio.*;
import javax.swing.*;
// metadata extraction in java: http://www.drewnoakes.com/code/exif/
import com.drew.imaging.jpeg.*;
import com.drew.metadata.*;
import com.drew.metadata.exif.*;
/**
* JPEGファイルに対して以下の処理を行います。
* ・指定した倍率で縮小(拡大)する
* ・撮影日を入れる
* ・指定した複数の画像を指定した行列数で並べる(つなげる)
*/
public class JpegTransformer
{
// 画像を並べる方向
public static final int DIRECTION_HORIZONTAL = 1;
public static final int DIRECTION_VERTICAL = 2;
// 日時フォーマッタ
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy:MM:dd hh:mm:ss");
private static final SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy/MM/dd");
// 撮影日挿入用フォントカラー
private static final Color originalDateColor = new Color(255, 69, 0);
// 撮影日挿入割合(幅の1/6に収まるように撮影日を挿入する)
private static final int ORIGINAL_DATE_INSERTION_RATE = 6;
public static void main(String[] args) throws Exception
{
// パラメーターの処理
if(args.length == 0)
{
// 引数なしの場合は画面を表示
new JpegTransformerFrame().setVisible(true);
}
else if(args.length < 5)
{
// 引数あり、かつ、5つ未満の場合はエラー
System.err.println("JpegTransformer outWidth(pixel) scaleRate(%) direction(1 or 2) outFileName inFileNames...");
System.exit(1);
}
else
{
// バッチ処理
int outWidth = Integer.parseInt(args[0]);
int scaleRate = Integer.parseInt(args[1]);
int direction = Integer.parseInt(args[2]);
String outFileName = args[3];
String inFileNames[] = new String[args.length - 4];
for(int i=0; i<inFileNames.length; i++)
{
inFileNames[i] = args[i + 4];
}
BufferedImage bi = new JpegTransformer().transform(outWidth, scaleRate, direction, inFileNames);
ImageIO.write(bi, "jpg", new File(outFileName));
}
}
public BufferedImage transform(int outWidth, int scaleRate, int direction, String[] inFileNames) throws Exception
{
java.util.List<BufferedImage> listBi = new ArrayList<BufferedImage>();
for(int i=0; i<inFileNames.length; i++)
{
// 撮影日を取得
Date dtOriginal = getOriginalDateTime(inFileNames[i]);
// 画像を読み込み
File f = new File(inFileNames[i]);
BufferedImage bi = ImageIO.read(f);
// 画像をスケール
BufferedImage biScaled = scaleImage(bi, scaleRate);
// 撮影日を画像に埋め込み
addOriginalDateTime(biScaled, dtOriginal);
listBi.add(biScaled);
}
// List → Array
BufferedImage[] arrImage = (BufferedImage[])listBi.toArray(new BufferedImage[0]);
// 画像をつなげる
return concatImage(arrImage, outWidth, direction);
}
public BufferedImage transform(int outWidth, int scaleRate, int direction, java.util.List<File> inFiles) throws Exception
{
String[] arrInFiles = new String[inFiles.size()];
for(int i=0; i<inFiles.size(); i++)
{
arrInFiles[i] = inFiles.get(i).getAbsolutePath();
}
return transform(outWidth, scaleRate, direction, arrInFiles);
}
/**
* 指定した倍率で縮小(拡大)する
*
* @param source 元画像
* @param scaleRate 倍率(%指定)
*/
private BufferedImage scaleImage(BufferedImage image, int scaleRate)
{
// スケール先のバッファを用意
BufferedImage scaledImage = new BufferedImage((int)(image.getWidth() * scaleRate / 100f),
(int)(image.getHeight() * scaleRate / 100f),
image.getType());
// スケール処理を行う
AffineTransform at = AffineTransform.getScaleInstance(scaleRate / 100f, scaleRate / 100f);
AffineTransformOp atOp = new AffineTransformOp(at, null);
atOp.filter(image, scaledImage);
return scaledImage;
}
/**
* 撮影日を取得する
*
* @param source 元画像
*/
private Date getOriginalDateTime(String imageFileName) throws Exception
{
// Fileオブジェクトの構築
File f = new File(imageFileName);
// JPEGメタデータの取得
Metadata metadata = JpegMetadataReader.readMetadata(f);
if(metadata == null)
{
// JPEGメタデータがない場合は、本日日付を返却する
return new Date();
}
// 撮影日の取得
Directory directory = metadata.getDirectory(ExifDirectory.class);
if(directory == null)
{
// Exif情報がない場合は、本日日付を返却する
return new Date();
}
String strOriginalDateTime = directory.getString(ExifDirectory.TAG_DATETIME_ORIGINAL);
if(strOriginalDateTime == null || strOriginalDateTime.length() == 0)
{
// OriginalDateTimeがない場合は、本日日付を返却する
return new Date();
}
return sdf.parse(strOriginalDateTime);
}
/**
* 撮影日を入れる
*
* @param source 元画像
*/
private void addOriginalDateTime(BufferedImage source, Date originalDateTime) throws IOException
{
String strOriginalDate = sdf2.format(originalDateTime);
int[] fontSize = getOptimizedFontSize(source, strOriginalDate, ORIGINAL_DATE_INSERTION_RATE);
Graphics2D g2d = source.createGraphics();
g2d.setColor(originalDateColor);
g2d.setFont(new Font(g2d.getFont().getFamily(), Font.PLAIN, fontSize[0]));
g2d.drawString(sdf2.format(originalDateTime), source.getWidth() - fontSize[1], source.getHeight() - fontSize[2]);
}
/**
* 指定した複数の画像を指定した行列数で並べる(つなげる)
*
* @param source 元画像リスト
* @param outWidth 列数
* @param direction 方向
*/
private BufferedImage concatImage(BufferedImage[] source, int outWidth, int direction)
{
// 行数を求める
int outHeight = (int)Math.ceil((double)source.length / outWidth);
BufferedImage biNew = new BufferedImage(source[0].getWidth() * outWidth, source[0].getHeight() * outHeight, source[0].getType());
Graphics2D g2dNew = biNew.createGraphics();
switch(direction)
{
case DIRECTION_HORIZONTAL:
out_of_loop:
for(int i=0; i<outHeight; i++)
{
for(int j=0; j<outWidth; j++)
{
int pos = i * outWidth + j;
if(pos >= source.length)
{
break out_of_loop;
}
BufferedImage targetImage = source[pos];
g2dNew.drawImage(targetImage, targetImage.getWidth() * j, targetImage.getHeight() * i, null);
}
}
break;
case DIRECTION_VERTICAL:
out_of_loop2:
for(int i=0; i<outWidth; i++)
{
for(int j=0; j<outHeight; j++)
{
int pos = i * outWidth + j;
if(pos >= source.length)
{
break out_of_loop2;
}
BufferedImage targetImage = source[pos];
g2dNew.drawImage(targetImage, targetImage.getWidth() * i, targetImage.getHeight() * j, null);
}
}
break;
}
return biNew;
}
/**
* 撮影日挿入に最適なフォントサイズとポジションを取得する
*/
private int[] getOptimizedFontSize(BufferedImage source, String str, int rate)
{
Graphics2D g2d = source.createGraphics();
for(int i=1; i<=200; i++)
{
g2d.setFont(new Font("Dialog", Font.PLAIN, i));
FontMetrics fm = g2d.getFontMetrics();
int width = 0;
for(int j=0; j<str.length(); j++)
{
width += fm.charWidth(str.charAt(j));
}
if(width > source.getWidth() / rate)
{
int fontSize = i;
int fixWidth = width + source.getWidth() / 20;
int fixHeight= fm.getHeight();
int[] result = new int[3];
result[0] = fontSize;
result[1] = fixWidth;
result[2] = fixHeight;
return result;
}
}
throw new RuntimeException();
}
private static final class JpegTransformerFrame extends JFrame
{
public JpegTransformerFrame()
{
setLayout(null);
// 出力カラム数
JLabel lblOutWidth = new JLabel("出力カラム数:1~10");
lblOutWidth.setBounds(10, 10, 120, 20);
add(lblOutWidth);
JTextField txtOutWidth = new JTextField();
txtOutWidth.setBounds(130, 10, 50, 20);
add(txtOutWidth);
// 出力レート(縮小率)
JLabel lblScaleRate = new JLabel("縮小率(%):1~200");
lblScaleRate.setBounds(10, 40, 120, 20);
add(lblScaleRate);
JTextField txtScaleRate = new JTextField();
txtScaleRate.setBounds(130, 40, 50, 20);
add(txtScaleRate);
// 出力方向
JLabel lblDirection = new JLabel("出力方向");
lblDirection.setBounds(10, 70, 120, 20);
add(lblDirection);
String[] cmbItems = {"→", "↓"};
JComboBox cmbDirection = new JComboBox(cmbItems);
cmbDirection.setBounds(130, 70, 50, 20);
add(cmbDirection);
// 出力ファイル名
JLabel lblOutFileName = new JLabel("出力ファイル名");
lblOutFileName.setBounds(10, 100, 120, 20);
add(lblOutFileName);
JTextField txtOutFileName = new JTextField(1);
txtOutFileName.setBounds(130, 100, 120, 20);
add(txtOutFileName);
// ドロップターゲット
JLabel lblDropTarget = new JLabel("ここにファイルをドロップしてください", SwingConstants.CENTER);
lblDropTarget.setBounds(0, 150, 350, 100);
lblDropTarget.setVerticalAlignment(SwingConstants.CENTER);
// ドロップターゲットの定義
MyDropTargetListener mydtl = new MyDropTargetListener(txtOutWidth, txtScaleRate, cmbDirection, txtOutFileName);
DropTarget target = new DropTarget(lblDropTarget, DnDConstants.ACTION_REFERENCE, mydtl);
add(lblDropTarget);
// Frameの設定
setTitle("Jpeg Transformer");
setSize(350, 250);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
private static final class MyDropTargetListener implements DropTargetListener
{
// 出力カラム数
private JTextField txtOutWidth;
// 出力レート(縮小率)
private JTextField txtScaleRate;
// 出力方向
private JComboBox cmbDirection;
// 出力ファイル名
private JTextField txtOutFileName;
public MyDropTargetListener(JTextField txtOutWidth, JTextField txtScaleRate, JComboBox cmbDirection, JTextField txtOutFileName)
{
this.txtOutWidth = txtOutWidth;
this.txtScaleRate = txtScaleRate;
this.cmbDirection = cmbDirection;
this.txtOutFileName = txtOutFileName;
}
public void dragEnter(DropTargetDragEvent enter)
{
}
public void dragOver(DropTargetDragEvent over)
{
}
public void dropActionChanged(DropTargetDragEvent action)
{
}
public void dragExit(DropTargetEvent exit)
{
}
// ドロップされた時にコールされる
public void drop(DropTargetDropEvent drop)
{
try
{
// フォームデータ → パラメータ
int outWidth;
try
{
outWidth = Integer.parseInt(txtOutWidth.getText());
if(outWidth < 0 || outWidth > 10)
{
showMessageDialog(txtOutWidth, "出力カラム数は1~10の範囲で指定してください。");
return;
}
}
catch(NumberFormatException e)
{
showMessageDialog(txtOutWidth, "出力カラム数は数値で指定してください。");
return;
}
int scaleRate;
try
{
scaleRate = Integer.parseInt(txtScaleRate.getText());
if(scaleRate < 0 || scaleRate > 200)
{
showMessageDialog(txtScaleRate, "縮小率(%)は1~200の範囲で指定してください。");
return;
}
}
catch(NumberFormatException e)
{
showMessageDialog(txtScaleRate, "縮小率(%)は数値で指定してください。");
return;
}
int direction = 0;
switch(cmbDirection.getSelectedIndex())
{
case 0:
direction = DIRECTION_HORIZONTAL;
break;
case 1:
direction = DIRECTION_VERTICAL;
break;
}
String outFileName = txtOutFileName.getText();
Transferable transfer = drop.getTransferable();
if(transfer.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
{
// ドロップ処理開始
drop.acceptDrop(DnDConstants.ACTION_REFERENCE);
@SuppressWarnings("unchecked")
java.util.List<File> fileList = (java.util.List<File>)transfer.getTransferData(DataFlavor.javaFileListFlavor);
// 画像変換処理を呼び出し
BufferedImage bi = new JpegTransformer().transform(outWidth, scaleRate, direction, fileList);
ImageIO.write(bi, "jpg", new File(outFileName));
// ドロップ処理終了
drop.getDropTargetContext().dropComplete(true);
}
}
catch(Exception e)
{
showMessageDialog(drop.getDropTargetContext().getComponent(), "予期せぬエラーが発生しました。" + e.getMessage());
System.err.println(e);
}
}
private void showMessageDialog(Component c, String message)
{
JOptionPane.showMessageDialog(c, message);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment