Created
November 12, 2009 08:34
-
-
Save taka2/232736 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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