1 package charactermanaj.graphics.io;
\r
3 import java.awt.BorderLayout;
\r
4 import java.awt.Color;
\r
5 import java.awt.Component;
\r
6 import java.awt.Font;
\r
7 import java.awt.Graphics2D;
\r
8 import java.awt.GridBagConstraints;
\r
9 import java.awt.GridBagLayout;
\r
10 import java.awt.Insets;
\r
11 import java.awt.RenderingHints;
\r
12 import java.awt.image.BufferedImage;
\r
13 import java.io.File;
\r
14 import java.io.IOException;
\r
15 import java.io.OutputStream;
\r
16 import java.util.Arrays;
\r
17 import java.util.Iterator;
\r
18 import java.util.List;
\r
19 import java.util.Properties;
\r
20 import java.util.logging.Level;
\r
21 import java.util.logging.Logger;
\r
23 import javax.imageio.IIOImage;
\r
24 import javax.imageio.ImageIO;
\r
25 import javax.imageio.ImageWriteParam;
\r
26 import javax.imageio.ImageWriter;
\r
27 import javax.imageio.event.IIOWriteWarningListener;
\r
28 import javax.imageio.stream.ImageOutputStream;
\r
29 import javax.swing.BorderFactory;
\r
30 import javax.swing.Box;
\r
31 import javax.swing.JCheckBox;
\r
32 import javax.swing.JComboBox;
\r
33 import javax.swing.JFileChooser;
\r
34 import javax.swing.JLabel;
\r
35 import javax.swing.JOptionPane;
\r
36 import javax.swing.JPanel;
\r
37 import javax.swing.JSpinner;
\r
38 import javax.swing.SpinnerNumberModel;
\r
39 import javax.swing.filechooser.FileFilter;
\r
41 import charactermanaj.graphics.io.OutputOption.PictureMode;
\r
42 import charactermanaj.graphics.io.OutputOption.ZoomRenderingType;
\r
43 import charactermanaj.util.LocalizedMessageComboBoxRender;
\r
44 import charactermanaj.util.LocalizedResourcePropertyLoader;
\r
48 * イメージを保存するためのヘルパークラス.<br>
\r
50 public class ImageSaveHelper {
\r
55 private static final Logger logger = Logger.getLogger(ImageSaveHelper.class.getName());
\r
60 protected static final String STRINGS_RESOURCE = "languages/imageSaveHelper";
\r
63 * このヘルパクラス用のファイルフィルタの抽象実装
\r
66 protected static abstract class ImageSaveHelperFilter extends FileFilter {
\r
69 public boolean accept(File f) {
\r
70 if (f.isDirectory()) {
\r
75 return isSupported(f);
\r
78 protected boolean isSupported(File f) {
\r
79 // サポートしている拡張子のいずれかにマッチするか?
\r
81 String lcName = f.getName().toLowerCase();
\r
82 for (String ext : getSupprotedExtension()) {
\r
83 if (lcName.endsWith("." + ext.toLowerCase())) {
\r
91 * 現在の選択されたファイル名を取得し、そのファイル名がデフォルトの拡張子で終端していなければ
\r
92 * デフォルトの拡張子を設定してファイルチューザに設定し直す.<Br>
\r
93 * @param fileChooser ファイルチューザ
\r
94 * @return デフォルトの拡張子で終端されたファイル
\r
96 public File supplyDefaultExtension(JFileChooser fileChooser) {
\r
97 File outFile = fileChooser.getSelectedFile();
\r
98 if (outFile == null) {
\r
102 if ( !isSupported(outFile)) {
\r
103 String extName = "." + getSupprotedExtension()[0];
\r
104 outFile = new File(outFile.getPath() + extName);
\r
105 fileChooser.setSelectedFile(outFile);
\r
111 * サポートするファイルの拡張子を取得する.<br>
\r
112 * 最初のものがデフォルトの拡張子として用いられる.<br>
\r
115 protected abstract String[] getSupprotedExtension();
\r
121 protected static final FileFilter pngFilter = new ImageSaveHelperFilter() {
\r
123 public String getDescription() {
\r
124 return "PNG(*.png)";
\r
127 protected String[] getSupprotedExtension() {
\r
128 return new String[] {"png"};
\r
135 protected static final FileFilter jpegFilter = new ImageSaveHelperFilter() {
\r
137 public String getDescription() {
\r
138 return "JPEG(*.jpg;*.jpeg)";
\r
141 protected String[] getSupprotedExtension() {
\r
142 return new String[] {"jpeg", "jpg"};
\r
149 protected static final FileFilter bmpFilter = new ImageSaveHelperFilter() {
\r
151 public String getDescription() {
\r
152 return "Bitmap(*.bmp)";
\r
155 protected String[] getSupprotedExtension() {
\r
156 return new String[] {"bmp"};
\r
161 * このヘルパクラスで定義されているファイルフィルタのリスト
\r
163 protected static final List<FileFilter> fileFilters = Arrays.asList(
\r
164 pngFilter, jpegFilter, bmpFilter);
\r
169 protected OutputImageBuilderFactory imageBuilderFactory;
\r
172 * 最後に使用した出力オプション.<br>
\r
175 protected OutputOption outputOption;
\r
180 protected File lastUseSaveDir;
\r
185 protected FileFilter lastUseFilter = pngFilter;
\r
188 * 最後に使用したディレクトリを設定する
\r
189 * @param lastUseSaveDir 最後に使用したディレクトリ、設定しない場合はnull
\r
191 public void setLastUseSaveDir(File lastUseSaveDir) {
\r
192 this.lastUseSaveDir = lastUseSaveDir;
\r
196 * 最後に使用したディレクトリを取得する
\r
197 * @return 最後に使用したディレクトリ、なければnull
\r
199 public File getLastUsedSaveDir() {
\r
200 return lastUseSaveDir;
\r
207 public ImageSaveHelper() {
\r
208 imageBuilderFactory = new OutputImageBuilderFactory();
\r
209 outputOption = imageBuilderFactory.createDefaultOutputOption();
\r
213 * 画像ファイルの保存用ダイアログを表示する.
\r
214 * @param parent 親ウィンドウ
\r
217 public File showSaveFileDialog(Component parent) {
\r
219 // 最後に使用したディレクトリを指定してファイルダイアログを構築する.
\r
220 JFileChooser fileChooser = new JFileChooser(lastUseSaveDir) {
\r
221 private static final long serialVersionUID = -9091369410030011886L;
\r
227 public void approveSelection() {
\r
228 File outFile = getSelectedFile();
\r
229 if (outFile == null) {
\r
233 // 選択したファイルフィルタに従ってデフォルトの拡張子を付与する.
\r
234 FileFilter selfilter = getFileFilter();
\r
235 if (selfilter instanceof ImageSaveHelperFilter) {
\r
236 outFile = ((ImageSaveHelperFilter) selfilter).supplyDefaultExtension(this);
\r
239 // ファイルが存在すれば上書き確認する.
\r
240 if (outFile.exists()) {
\r
241 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
\r
242 .getLocalizedProperties(STRINGS_RESOURCE);
\r
244 if (JOptionPane.showConfirmDialog(this,
\r
245 strings.getProperty("confirmOverwrite"),
\r
246 strings.getProperty("confirm"),
\r
247 JOptionPane.YES_NO_OPTION,
\r
248 JOptionPane.WARNING_MESSAGE) != JOptionPane.YES_OPTION) {
\r
253 super.approveSelection();
\r
258 // final OutputOptionPanel accessoryPanel = new OutputOptionPanel(this.outputOption);
\r
259 // fileChooser.setAccessory(accessoryPanel);
\r
262 fileChooser.setAcceptAllFileFilterUsed(false);
\r
263 for (FileFilter fileFilter : fileFilters) {
\r
264 fileChooser.addChoosableFileFilter(fileFilter);
\r
267 // 最後に使用したファイルフィルタが既定のフィルタでないか未設定であればPNGにする.
\r
268 if (lastUseFilter == null || !fileFilters.contains(lastUseFilter)) {
\r
269 lastUseFilter = pngFilter;
\r
272 // 最後に使用したフィルタをデフォルトのフィルタに設定する.
\r
273 fileChooser.setFileFilter(lastUseFilter);
\r
276 int ret = fileChooser.showSaveDialog(parent);
\r
277 if (ret != JFileChooser.APPROVE_OPTION) {
\r
282 // OutputOption outputOption = accessoryPanel.getOutputOption();
\r
283 // this.outputOption = outputOption;
\r
285 // 最後に使用したフィルタ、および選択したディレクトリを記憶する.
\r
286 File outFile = fileChooser.getSelectedFile();
\r
287 lastUseSaveDir = outFile.getParentFile();
\r
288 lastUseFilter = fileChooser.getFileFilter();
\r
294 public OutputOption getOutputOption() {
\r
295 return outputOption.clone();
\r
298 public void setOutputOption(OutputOption outputOption) {
\r
299 if (outputOption == null) {
\r
300 throw new IllegalArgumentException();
\r
302 this.outputOption = outputOption.clone();
\r
306 * ファイル名を指定してイメージをファイルに出力します.<br>
\r
307 * 出力形式は拡張子より判定します.<br>
\r
308 * サポートされていない拡張子の場合はIOException例外が発生します.<br>
\r
310 * @param imgBgColor JPEGの場合の背景色
\r
311 * @param outFile 出力先ファイル(拡張子が必須)
\r
312 * @param warnings 警告を記録するバッファ、必要なければnull
\r
313 * @throws IOException 失敗
\r
315 public void savePicture(BufferedImage img, Color imgBgColor,
\r
316 File outFile, final StringBuilder warnings) throws IOException {
\r
317 if (img == null || outFile == null) {
\r
318 throw new IllegalArgumentException();
\r
321 // ファイル名から拡張子を取り出します.
\r
322 String fname = outFile.getName();
\r
323 int extpos = fname.lastIndexOf(".");
\r
325 throw new IOException("missing file extension.");
\r
327 String ext = fname.substring(extpos + 1).toLowerCase();
\r
329 // 拡張子に対するImageIOのライタを取得します.
\r
330 Iterator<ImageWriter> ite = ImageIO.getImageWritersBySuffix(ext);
\r
331 if (!ite.hasNext()) {
\r
332 throw new IOException("unsupported file extension: " + ext);
\r
335 ImageWriter iw = ite.next();
\r
337 // ライタを使いイメージを書き込みます.
\r
338 savePicture(img, imgBgColor, iw, outFile, warnings);
\r
342 * イメージをMIMEで指定された形式で出力します.
\r
344 * @param imgBgColor JPEGの場合の背景色
\r
345 * @param outstm 出力先
\r
347 * @param warnings 警告を書き込むバッファ、必要なければnull
\r
348 * @throws IOException 例外
\r
350 public void savePicture(BufferedImage img, Color imgBgColor,
\r
351 OutputStream outstm, String mime, final StringBuilder warnings)
\r
352 throws IOException {
\r
353 if (img == null || outstm == null || mime == null) {
\r
354 throw new IllegalArgumentException();
\r
357 // mimeがパラメータ付きの場合は、パラメータを除去する.
\r
358 int pt = mime.indexOf(';');
\r
360 mime = mime.substring(0, pt).trim();
\r
363 // サポートしているmimeタイプを検出.
\r
364 Iterator<ImageWriter> ite = ImageIO.getImageWritersByMIMEType(mime);
\r
365 if (!ite.hasNext()) {
\r
366 throw new IOException("unsupported mime: " + mime);
\r
369 ImageWriter iw = ite.next();
\r
370 savePicture(img, imgBgColor, iw, outstm, warnings);
\r
374 protected void savePicture(BufferedImage img, Color imgBgColor, ImageWriter iw,
\r
375 Object output, final StringBuilder warnings)
\r
376 throws IOException {
\r
378 iw.addIIOWriteWarningListener(new IIOWriteWarningListener() {
\r
379 public void warningOccurred(ImageWriter source, int imageIndex,
\r
381 if (warnings.length() > 0) {
\r
382 warnings.append(System.getProperty("line.separator"));
\r
384 if (warnings != null) {
\r
385 warnings.append(warning);
\r
387 logger.log(Level.WARNING, warning);
\r
391 boolean jpeg = false;
\r
392 boolean bmp = false;
\r
393 for (String mime : iw.getOriginatingProvider().getMIMETypes()) {
\r
394 if (mime.contains("image/jpeg") || mime.contains("image/jpg")) {
\r
398 if (mime.contains("image/bmp") || mime.contains("image/x-bmp")
\r
399 || mime.contains("image/x-windows-bmp")) {
\r
405 ImageWriteParam iwp = iw.getDefaultWriteParam();
\r
409 iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
\r
410 iwp.setCompressionQuality((float) outputOption.getJpegQuality());
\r
412 // JPEGは透過色をサポートしていないので背景色を設定する.
\r
413 ioimg = new IIOImage(createJpegFormatPicture(img, imgBgColor), null, null);
\r
416 // BMPは透過色をサポートしていないので背景色を設定する.
\r
417 ioimg = new IIOImage(createBMPFormatPicture(img, imgBgColor), null, null);
\r
419 } else if (outputOption.isForceBgColor()) {
\r
421 // JPEG, BMP以外(PNGを想定)
\r
422 ioimg = new IIOImage(createOpaquePNGFormatPicture(img, imgBgColor), null, null);
\r
426 // JPEG, BMP以外(PNGを想定)
\r
427 ioimg = new IIOImage(img, null, null);
\r
430 ImageOutputStream imgstm = ImageIO.createImageOutputStream(output);
\r
432 iw.setOutput(imgstm);
\r
433 iw.write(null, ioimg, iwp);
\r
445 * ARGB形式から、アルファチャンネルを削除し、かわりに背景色を設定したBGR形式画像を返します.<br>
\r
446 * JPEG画像として用いることを想定しています.<br>
\r
447 * @param img 変換するイメージ
\r
448 * @param imgBgColor 背景色
\r
449 * @return 変換されたイメージ
\r
451 public BufferedImage createJpegFormatPicture(BufferedImage img, Color imgBgColor) {
\r
452 if (imgBgColor == null) {
\r
453 imgBgColor = Color.WHITE;
\r
455 return createFormatPicture(img, imgBgColor, BufferedImage.TYPE_INT_BGR);
\r
459 * ARGB形式から、アルファチャンネルを削除し、かわりに背景色を設定したBGR形式画像を返します.<br>
\r
460 * BMP画像として用いることを想定しています.<br>
\r
461 * @param img 変換するイメージ
\r
462 * @param imgBgColor 背景色
\r
463 * @return 変換されたイメージ
\r
465 public BufferedImage createBMPFormatPicture(BufferedImage img, Color imgBgColor) {
\r
466 if (imgBgColor == null) {
\r
467 imgBgColor = Color.WHITE;
\r
469 return createFormatPicture(img, imgBgColor, BufferedImage.TYPE_3BYTE_BGR);
\r
473 * ARGB形式から、アルファチャンネルを削除し、かわりに背景色を設定したRGB形式画像を返します.<br>
\r
474 * 背景付きPNG画像として用いることを想定しています.<br>
\r
475 * @param img 変換するイメージ
\r
476 * @param imgBgColor 背景色
\r
477 * @return 変換されたイメージ
\r
479 public BufferedImage createOpaquePNGFormatPicture(BufferedImage img, Color imgBgColor) {
\r
480 if (imgBgColor == null) {
\r
481 imgBgColor = Color.WHITE;
\r
483 return createFormatPicture(img, imgBgColor, BufferedImage.TYPE_INT_ARGB);
\r
487 * ARGB形式から、アルファチャンネルを削除し、かわりに背景色を設定したBGR形式画像を返します.<br>
\r
488 * JPEG画像として用いることを想定しています.<br>
\r
489 * @param img 変換するイメージ
\r
490 * @param imgBgColor 背景色
\r
491 * @return 変換されたイメージ
\r
493 protected BufferedImage createFormatPicture(BufferedImage img, Color imgBgColor, int type) {
\r
495 throw new IllegalArgumentException();
\r
497 int w = img.getWidth();
\r
498 int h = img.getHeight();
\r
499 BufferedImage tmpImg = new BufferedImage(w, h, type);
\r
500 Graphics2D g = tmpImg.createGraphics();
\r
502 g.setRenderingHint(
\r
503 RenderingHints.KEY_ALPHA_INTERPOLATION,
\r
504 RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
\r
506 if (imgBgColor != null) {
\r
507 g.setColor(imgBgColor);
\r
508 g.fillRect(0, 0, w, h);
\r
510 g.drawImage(img, 0, 0, w, h, 0, 0, w, h, null);
\r
523 class OutputOptionPanel extends JPanel {
\r
524 private static final long serialVersionUID = 1L;
\r
526 private JSpinner jpegQualitySpinner;
\r
528 private JCheckBox lblZoom;
\r
530 private JSpinner zoomSpinner;
\r
532 private JComboBox zoomAlgoCombo;
\r
534 private JComboBox pictureModeCombo;
\r
536 private JCheckBox checkForceBgColor;
\r
539 public OutputOptionPanel() {
\r
540 this(new OutputOption());
\r
543 public OutputOptionPanel(OutputOption outputOption) {
\r
545 Properties strings = LocalizedResourcePropertyLoader.getCachedInstance()
\r
546 .getLocalizedProperties(ImageSaveHelper.STRINGS_RESOURCE);
\r
548 setBorder(BorderFactory.createCompoundBorder(
\r
549 BorderFactory.createEmptyBorder(3, 3, 3, 3),
\r
550 BorderFactory.createTitledBorder(
\r
551 strings.getProperty("ouputOption.caption"))));
\r
552 setLayout(new GridBagLayout());
\r
553 GridBagConstraints gbc = new GridBagConstraints();
\r
555 gbc.anchor = GridBagConstraints.EAST;
\r
556 gbc.fill = GridBagConstraints.BOTH;
\r
557 gbc.insets = new Insets(3, 3, 3, 3);
\r
558 gbc.gridheight = 1;
\r
568 add(Box.createHorizontalStrut(6), gbc);
\r
575 JLabel lblJpeg = new JLabel(
\r
576 strings.getProperty("outputOption.jpeg.caption"));
\r
577 lblJpeg.setFont(lblJpeg.getFont().deriveFont(Font.BOLD));
\r
585 strings.getProperty("outputOption.jpeg.quality"),
\r
586 JLabel.RIGHT), gbc);
\r
588 SpinnerNumberModel spmodel = new SpinnerNumberModel(100, 10, 100, 1);
\r
589 this.jpegQualitySpinner = new JSpinner(spmodel);
\r
594 add(jpegQualitySpinner, gbc);
\r
599 add(new JLabel("%"), gbc);
\r
606 gbc.insets = new Insets(10, 3, 3, 3);
\r
607 lblZoom = new JCheckBox(strings.getProperty("outputOption.zoom.caption"));
\r
608 lblZoom.setFont(lblJpeg.getFont().deriveFont(Font.BOLD));
\r
615 gbc.insets = new Insets(3, 3, 3, 3);
\r
617 strings.getProperty("outputOption.zoom.factor"), JLabel.RIGHT), gbc);
\r
619 SpinnerNumberModel zoomSpModel = new SpinnerNumberModel(100, 20, 800, 1);
\r
620 this.zoomSpinner = new JSpinner(zoomSpModel);
\r
625 add(zoomSpinner, gbc);
\r
630 add(new JLabel("%"), gbc);
\r
637 strings.getProperty("outputOption.zoom.renderingMode"),
\r
638 JLabel.RIGHT), gbc);
\r
640 this.zoomAlgoCombo = new JComboBox(ZoomRenderingType.values());
\r
641 this.zoomAlgoCombo.setRenderer(new LocalizedMessageComboBoxRender(strings));
\r
646 add(zoomAlgoCombo, gbc);
\r
652 gbc.insets = new Insets(10, 3, 3, 3);
\r
653 JLabel lblPictureMode = new JLabel(
\r
654 strings.getProperty("outputOption.picture"));
\r
655 lblPictureMode.setFont(lblJpeg.getFont().deriveFont(Font.BOLD));
\r
656 add(lblPictureMode, gbc);
\r
661 gbc.insets = new Insets(3, 3, 3, 3);
\r
663 strings.getProperty("outputOption.picture.type"),
\r
664 JLabel.RIGHT), gbc);
\r
666 this.pictureModeCombo = new JComboBox(PictureMode.values());
\r
667 this.pictureModeCombo.setRenderer(
\r
668 new LocalizedMessageComboBoxRender(strings));
\r
673 add(pictureModeCombo, gbc);
\r
679 checkForceBgColor = new JCheckBox(
\r
680 strings.getProperty("outputOption.picture.forceBgColor"));
\r
681 add(checkForceBgColor, gbc);
\r
683 JPanel pnlBgAlpha = new JPanel(new BorderLayout(3, 3));
\r
684 pnlBgAlpha.add(new JLabel("背景アルファ"), BorderLayout.WEST);
\r
686 SpinnerNumberModel bgAlphaModel = new SpinnerNumberModel(255, 0, 255, 1);
\r
687 JSpinner bgAlphaSpinner = new JSpinner(bgAlphaModel);
\r
689 pnlBgAlpha.add(bgAlphaSpinner, BorderLayout.CENTER);
\r
695 add(pnlBgAlpha, gbc);
\r
703 add(Box.createGlue(), gbc);
\r
706 setOutputOption(outputOption);
\r
709 public void setOutputOption(OutputOption outputOption) {
\r
710 if (outputOption == null) {
\r
711 outputOption = new OutputOption();
\r
714 jpegQualitySpinner.setValue((int) (outputOption.getJpegQuality() * 100));
\r
715 lblZoom.setSelected(outputOption.isEnableZoom());
\r
716 zoomSpinner.setValue((int) (outputOption.getZoomFactor() * 100));
\r
717 zoomAlgoCombo.setSelectedItem(outputOption.getZoomRenderingType());
\r
718 pictureModeCombo.setSelectedItem(outputOption.getPictureMode());
\r
719 checkForceBgColor.setSelected(outputOption.isForceBgColor());
\r
722 public OutputOption getOutputOption() {
\r
723 OutputOption outputOption = new OutputOption();
\r
725 outputOption.setJpegQuality(((Integer) jpegQualitySpinner.getValue() / 100.));
\r
726 outputOption.setEnableZoom(lblZoom.isSelected());
\r
727 outputOption.setZoomFactor(((Integer) zoomSpinner.getValue() / 100.));
\r
728 outputOption.setZoomRenderingType((ZoomRenderingType) zoomAlgoCombo.getSelectedItem());
\r
729 outputOption.setPictureMode((PictureMode) pictureModeCombo.getSelectedItem());
\r
730 outputOption.setForceBgColor(checkForceBgColor.isSelected());
\r
732 return outputOption;
\r