package edu.colostate.vchill.gui;
import edu.colostate.vchill.Config;
import edu.colostate.vchill.DialogUtil;
import edu.colostate.vchill.ScaleManager;
import net.jmge.gif.Gif89Encoder;
import javax.imageio.ImageIO;
import javax.print.*;
import javax.swing.*;
import javax.swing.Timer;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.List;
/**
* This class is the cycling image buffer described in the
* C-Requirements. It will hold up a user specified number of
* images and display them as the user clicks next and previous.
* <p/>
* Methods are synchronized on this.map to prevent conflict with
* synchronized JPanel methods
*
* @author Justin Carlson
* @author Jochen Deyke
* @author jpont
* @version 2010-08-30
* @created August 15, 2003
*/
public class ViewImageDisplay extends JPanel {
/**
*
*/
private static final long serialVersionUID = -2860693420417672170L;
private static final ScaleManager sm = ScaleManager.getInstance();
private static final Config config = Config.getInstance();
private static final String GIF_COMMENT = "Created by Java VCHILL";
private final File[] lastDir = new File[1];
/**
* Selects from the different plotted images
*/
private final JTextField imageNum;
private final JTextField imageMax;
private final JTextField imageLim;
/**
* Selects from the different plotted images
*/
private final JComboBox typeSelector;
/**
* Contains the pane, display to the user
*/
private JFrame imageFrame = null;
/**
* Holds the lists of images
*/
private final LinkedHashMap<String, ArrayList<SavedImage<BufferedImage>>> map = new LinkedHashMap<String, ArrayList<SavedImage<BufferedImage>>>(); //map of ArrayLists, one per type
/**
* Holds the images
*/
private ArrayList<SavedImage<BufferedImage>> images; //images of current type
/**
* Maximum number of images to store for each data type
*/
private int maxImages;
/**
* Index of the current image to display
*/
private int currIndex;
/**
* Used to create a slideshow
*/
private Timer loop;
/**
* Creates the panel etc to be used to display the images.
*/
public ViewImageDisplay() {
setMaxImages(config.getDefaultImageMax());
imageNum = new JTextField("0", 3);
imageNum.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent ae) {
try {
setCurrIndex(Integer.parseInt(imageNum.getText()) - 1);
repaint();
} catch (NumberFormatException nfe) {
}
}
});
imageMax = new JTextField("0", 3);
imageMax.setEnabled(false);
imageLim = new JTextField(String.valueOf(this.maxImages), 3);
imageLim.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent ae) {
try {
setMaxImages(Integer.parseInt(imageLim.getText()));
} catch (NumberFormatException nfe) {
}
}
});
Object[] types = sm.getTypes().toArray();
this.typeSelector = new JComboBox(types);
currIndex = 0;
this.loop = new Timer(200, new ActionListener() {
public void actionPerformed(final ActionEvent ae) {
next();
}
});
this.loop.setRepeats(true);
this.loop.setInitialDelay(0);
if (types.length > 0) this.selectType((String) types[0]);
sm.addObserver(new Observer() {
public void update(final Observable o, final Object arg) {
if (arg == null) updateTypes();
else typeSelector.addItem(arg);
}
});
}
public void updateTypes() {
synchronized (this.map) {
this.typeSelector.removeAllItems();
for (String type : sm.getTypes()) this.typeSelector.addItem(type);
}
}
private void setCurrIndex(final int newIndex) {
synchronized (this.map) {
if (images == null) {
this.currIndex = 0;
this.imageNum.setText("0");
} else
this.currIndex = newIndex % images.size();
}
}
private void setMaxImages(final int maxImages) {
synchronized (this.map) {
this.maxImages = maxImages > 1 ? maxImages : 8;
if (this.maxImages == 8)
this.imageLim.setText("8");
}
}
private int getMaxImages() {
synchronized (this.map) {
return this.maxImages;
}
}
public void addImage(final Set<String> type, final SavedImage<BufferedImage> image) {
synchronized (this.map) {
this.addImage(type.iterator().next(), image);
}
}
/**
* Adds an image to the queue. Currently it expects a buffered
* image, this might be best changed to use any sort of image
* file.
*
* @param type the data type to select
* @param image The Image object that is going to be drawn.
*/
public void addImage(String type, final SavedImage<BufferedImage> image) {
synchronized (this.map) {
if (type == null || image == null) return;
if (config.isSaveToDiskEnabled()) {
File file = new File(config.getSaveToDiskPath(), type + "." + image.getDescription() + ".png");
System.out.println("Saving " + file.getName());
saveImageToFile(image.getImage(), file);
} else {
this.selectType(type);
while (images.size() >= this.maxImages) images.remove(0);
images.add(image);
imageNum.setText(String.valueOf(images.size() > 0 ? currIndex + 1 : 0));
imageMax.setText(String.valueOf(images.size()));
repaint();
}
}
}
/**
* Move the image to the next in the buffer (or cycle) and repaint.
*/
public void next() {
synchronized (this.map) {
if (images == null || images.size() < 1) return;
currIndex = (currIndex + 1) % images.size();
imageNum.setText(String.valueOf(images.size() > 0 ? currIndex + 1 : 0));
repaint();
}
}
/**
* This will return the actual image to draw using the current
* image index to get it out of the list.
*
* @return the image referenced by the current index.
*/
public BufferedImage getCurrentImage() {
synchronized (this.map) {
if (images == null || images.size() < 1) return null;
if (currIndex >= images.size()) currIndex = 0;
return images.get(currIndex).getImage();
}
}
/**
* This will display the image "previous" to the one currently viewed in
* the image buffer.
*/
public void prev() {
synchronized (this.map) {
if (images == null || images.size() < 1) return;
if (currIndex > 0) --currIndex;
else currIndex = images.size() - 1;
imageNum.setText(String.valueOf(images.size() > 0 ? currIndex + 1 : 0));
repaint();
}
}
/**
* This will bring up a popup of the image display, the user can then
* use the next and previous buttons to navigate.
*
* @param sizeX requested width of the popup.
* @param sizeY requested height of the popup.
*/
public void displayImage(final int sizeX, final int sizeY) {
if (imageFrame == null) {
final JPanel encapsulate = new JPanel();
encapsulate.setLayout(new BorderLayout());
this.setPreferredSize(new Dimension(sizeX, sizeY - 26));
encapsulate.add(this, BorderLayout.CENTER);
final JPanel control = getControlPanel();
control.setPreferredSize(new Dimension(480, 56));
encapsulate.add(control, BorderLayout.SOUTH);
if (images != null) {
imageNum.setText(String.valueOf(images.size() > 0 ? currIndex + 1 : 0));
imageMax.setText(String.valueOf(images.size()));
}
imageLim.setText(String.valueOf(getMaxImages()));
imageFrame = new JFrame("Saved Images");
imageFrame.setIconImage(GUIUtil.ICON);
imageFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
imageFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(final WindowEvent e) {
closeWindow();
}
});
imageFrame.setContentPane(encapsulate);
imageFrame.pack();
imageFrame.setLocationRelativeTo(WindowManager.getInstance().getMainWindow()); //centered
imageFrame.setVisible(true);
} else {
imageFrame.toFront();
//imageFrame.requestFocus();
//repaint();
}
}
@Override
public void paintComponent(final Graphics g) {
if (g == null) return;
super.paintComponent(g);
g.drawImage(getCurrentImage(), 0, 0, null);
}
/**
* This will return a JPanel with all of the control objects in it.
* For example, the next and previous, the listing methods etc.
*
* @return A JPanel with control options.
*/
private JPanel getControlPanel() {
JButton button = null;
JPanel controlPanel = new JPanel();
controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS));
controlPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JPanel topRow = new JPanel();
topRow.setLayout(new BoxLayout(topRow, BoxLayout.X_AXIS));
JPanel bottomRow = new JPanel();
bottomRow.setLayout(new BoxLayout(bottomRow, BoxLayout.X_AXIS));
JLabel imageLabel = new JLabel("Image ");
imageLabel.setLabelFor(imageNum);
imageLabel.setDisplayedMnemonic('i');
JLabel limitLabel = new JLabel(" max) ");
limitLabel.setLabelFor(imageLim);
limitLabel.setDisplayedMnemonic('m');
topRow.add(imageLabel);
topRow.add(imageNum);
topRow.add(new JLabel(" of "));
topRow.add(imageMax);
topRow.add(new JLabel(" ("));
topRow.add(imageLim);
topRow.add(limitLabel);
button = new JButton("Next");
button.setMnemonic(KeyEvent.VK_N);
button.addActionListener(new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = 4584521262936562019L;
public void actionPerformed(final ActionEvent e) {
loop.stop();
next();
}
});
button.setToolTipText("Advance one image");
topRow.add(button);
button = new JButton("Prev");
button.setMnemonic(KeyEvent.VK_P);
button.addActionListener(new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = -2059500850910679648L;
public void actionPerformed(final ActionEvent e) {
loop.stop();
prev();
}
});
button.setToolTipText("Go back one image");
topRow.add(button);
button = new JButton("Loop");
button.setMnemonic(KeyEvent.VK_L);
button.addActionListener(new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = -7151401905075308439L;
public void actionPerformed(final ActionEvent e) {
if (loop.isRunning()) loop.stop();
else loop.start();
}
});
button.setToolTipText("Start/stop automatically advancing through images");
topRow.add(button);
button = new JButton("Speed");
button.setMnemonic(KeyEvent.VK_S);
button.addActionListener(new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = -1001377916422030622L;
public void actionPerformed(final ActionEvent e) {
loop.stop();
while (true) {
String str = DialogUtil.showInputDialog(imageFrame, "Input speed", "Please enter desired delay between images in milliseconds");
if (str == null) return;
try {
loop.setDelay(Integer.parseInt(str));
loop.setInitialDelay(0);
break;
} catch (NumberFormatException nfe) {
DialogUtil.showErrorDialog(imageFrame, "Error: Invalid input", "Please enter only integer values");
}
}
loop.start();
}
});
button.setToolTipText("Input new speed and start advancing thorugh images");
topRow.add(button);
button = new JButton("Remove");
button.setMnemonic(KeyEvent.VK_R);
button.addActionListener(new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = 4415890045581374769L;
public void actionPerformed(final ActionEvent e) {
loop.stop();
if (images == null || images.size() < 1) return; //no image to remove
images.remove(currIndex);
currIndex = images.size() > 0 ? currIndex % images.size() : 0;
imageNum.setText(String.valueOf(images.size() > 0 ? currIndex + 1 : 0));
imageMax.setText(String.valueOf(images.size()));
repaint();
}
});
button.setToolTipText("Remove the currently displayed image");
topRow.add(button);
button = new JButton("Clear");
button.setMnemonic(KeyEvent.VK_C);
button.addActionListener(new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = -7134180243004889251L;
public void actionPerformed(final ActionEvent e) {
loop.stop();
currIndex = 0;
if (images == null) return;
images.clear();
imageNum.setText(String.valueOf(images.size() > 0 ? currIndex + 1 : 0));
imageMax.setText(String.valueOf(images.size()));
repaint();
}
});
button.setToolTipText("Remove all images of the current type");
topRow.add(button);
JLabel typeLabel = new JLabel("Type:");
typeLabel.setDisplayedMnemonic('t');
typeLabel.setLabelFor(this.typeSelector);
bottomRow.add(typeLabel);
this.typeSelector.addActionListener(new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = 8741340180851587765L;
public void actionPerformed(final ActionEvent ae) {
selectType((String) typeSelector.getSelectedItem());
repaint();
}
});
this.typeSelector.setToolTipText("Show images of this data type");
bottomRow.add(this.typeSelector);
button = new JButton("Image to File");
button.setMnemonic(KeyEvent.VK_F);
button.addActionListener(new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = 8160242673173152713L;
public void actionPerformed(final ActionEvent e) {
loop.stop();
if (images == null || images.size() < 1) return;
CheckBoxFileChooser chooser = new CheckBoxFileChooser("Printer friendly");
chooser.setFileFilter(new ImageFileFilter());
int returnVal = chooser.showSaveDialog(imageFrame);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile();
lastDir[0] = file;
saveImageToFile(chooser.isSelected() ? switchBlackAndWhite(getCurrentImage()) : getCurrentImage(), file);
}
}
});
button.setToolTipText("Save the currently displayed image to a file");
bottomRow.add(button);
button = new JButton("Loop to Dir");
button.setMnemonic(KeyEvent.VK_D);
button.addActionListener(new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = -5399632952029836390L;
public void actionPerformed(final ActionEvent ae) {
loop.stop();
if (images == null || images.size() < 1) return;
new Thread(new Runnable() {
public void run() {
CheckBoxFileChooser chooser = new CheckBoxFileChooser("Printer friendly");
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int returnVal = chooser.showSaveDialog(imageFrame);
if (returnVal == JFileChooser.APPROVE_OPTION) {
int progress = 0;
lastDir[0] = chooser.getSelectedFile();
String path = lastDir[0].getAbsolutePath() + File.separator;
String prefix = path + typeSelector.getSelectedItem() + ".";
final ProgressMonitor progressMonitor = new ProgressMonitor(imageFrame, "Saving image to:", "", 0, images.size());
progressMonitor.setProgress(progress++);
progressMonitor.setMillisToDecideToPopup(500);
for (final SavedImage<BufferedImage> image : images) {
File file;
int i = 0;
do {
file = new File(prefix + image.getDescription() + " " + i++ + ".png");
} while (file.exists());
String filename = file.getName();
progressMonitor.setNote(filename);
System.out.println("Saving " + filename);
Thread.yield();
if (progressMonitor.isCanceled()) return;
saveImageToFile(chooser.isSelected() ? switchBlackAndWhite(image.getImage()) : image.getImage(), file);
progressMonitor.setProgress(progress++);
Thread.yield();
}
}
}
}, "LoopToDirThread").start();
}
});
button.setToolTipText("Save all images of this type to separate files");
bottomRow.add(button);
button = new JButton("Loop to GIF");
button.setMnemonic(KeyEvent.VK_G);
button.addActionListener(new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = -5357969871557245856L;
public void actionPerformed(final ActionEvent ae) {
loop.stop();
if (images == null || images.size() < 1) return;
new Thread(new Runnable() {
public void run() {
CheckBoxFileChooser chooser = new CheckBoxFileChooser("Printer friendly");
chooser.setFileFilter(new GifFileFilter());
int returnVal = chooser.showSaveDialog(imageFrame);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile();
lastDir[0] = file;
if (!file.getName().toLowerCase().endsWith(".gif"))
file = new File(file.getAbsolutePath() + ".gif");
try {
List<BufferedImage> i = new ArrayList<BufferedImage>(images.size());
for (final SavedImage<BufferedImage> image : images) {
i.add(chooser.isSelected() ? switchBlackAndWhite(image.getImage()) : image.getImage());
}
saveGif(i, file);
} catch (IOException ioe) {
try {
List<BufferedImage> i = new ArrayList<BufferedImage>(images.size());
for (final SavedImage<BufferedImage> image : images) {
i.add(reduceColors(chooser.isSelected() ? switchBlackAndWhite(image.getImage()) : image.getImage()));
}
saveGif(i, file);
DialogUtil.showWarningDialog(imageFrame, "Automatically reduced colors",
"GIF supports a maximum of 256 colors per file. Because the image to be saved had more,\n" +
"the colors have been automatically remapped. This can produce undesireable dithering.\n" +
"To avoid this effect, try to reduce the number of colors used when capturing images for\n" +
"use in GIFs by disabling color interpolation or changing colors.");
} catch (IOException ioe2) {
ioe2.printStackTrace();
DialogUtil.showErrorDialog(imageFrame, "Failed to create animated GIF", ioe2.toString());
}
}
}
}
}, "LoopToGIFThread").start();
}
});
button.setToolTipText("Save all images of this type to an animated GIF file");
bottomRow.add(button);
button = new JButton("Composite to GIF");
button.setMnemonic(KeyEvent.VK_C);
button.addActionListener(new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = 3997373801386745039L;
public void actionPerformed(final ActionEvent ae) {
loop.stop();
if (images == null || images.size() < 1) return;
new Thread(new Runnable() {
public void run() {
String otherType = (String) DialogUtil.showOptionDialog(imageFrame, "Composite animated GIF", "What data type do you want to composite the current type with?", sm.getTypes().toArray());
CheckBoxFileChooser chooser = new CheckBoxFileChooser("Printer friendly");
chooser.setFileFilter(new GifFileFilter());
int returnVal = chooser.showSaveDialog(imageFrame);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile();
lastDir[0] = file;
if (!file.getName().toLowerCase().endsWith(".gif"))
file = new File(file.getAbsolutePath() + ".gif");
List<BufferedImage> comps = new ArrayList<BufferedImage>(images.size());
ArrayList<SavedImage<BufferedImage>> bottomImages = map.get(otherType);
if (bottomImages == null) {
DialogUtil.showErrorDialog(imageFrame, "Failed to create animated GIF",
"There are no saved images for type " + otherType);
return;
}
for (int i = 0; i < images.size() && i < bottomImages.size(); ++i) {
BufferedImage top = images.get(i).getImage();
BufferedImage bottom = bottomImages.get(i).getImage();
BufferedImage comp = new BufferedImage(top.getWidth(), top.getHeight() * 2, top.getType());
Graphics g = comp.createGraphics();
g.drawImage(top, 0, 0, null);
g.drawImage(bottom, 0, top.getHeight(), null);
comps.add(chooser.isSelected() ? switchBlackAndWhite(comp) : comp);
}
try {
saveGif(comps, file);
} catch (IOException ioe) {
try {
List<BufferedImage> i = new ArrayList<BufferedImage>(comps.size());
for (final BufferedImage image : comps) {
i.add(reduceColors(image));
}
saveGif(i, file);
DialogUtil.showWarningDialog(imageFrame, "Automatically reduced colors",
"GIF supports a maximum of 256 colors per file. Because the image to be saved had more,\n" +
"the colors have been automatically remapped. This can produce undesireable dithering.\n" +
"To avoid this effect, try to reduce the number of colors used when capturing images for\n" +
"use in GIFs by using a quality setting of \"High\" or lower and/or disabling color\n" +
"interpolation.");
} catch (IOException ioe2) {
ioe2.printStackTrace();
DialogUtil.showErrorDialog(imageFrame, "Failed to create animated GIF", ioe2.toString());
}
}
}
}
}, "CompositeToGIFThread").start();
}
});
button.setToolTipText("Save all images of this type and another to a single animated GIF file");
bottomRow.add(button);
button = new JButton("Load");
button.setMnemonic(KeyEvent.VK_O);
button.addActionListener(new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = -3441419339433777977L;
public void actionPerformed(final ActionEvent e) {
loop.stop();
JFileChooser chooser = new JFileChooser(lastDir[0]);
chooser.setFileFilter(new ImageFileFilter());
chooser.setMultiSelectionEnabled(true);
int returnVal = chooser.showOpenDialog(imageFrame);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File[] selected = chooser.getSelectedFiles();
if (selected.length > 0) lastDir[0] = selected[0];
for (int i = 0; i < selected.length; ++i) {
String[] filename = selected[i].getName().split("\\."); //type.description.filetype
String type = lookupType(filename[0]);
try {
addImage(type, new SavedImage<BufferedImage>(ImageIO.read(selected[i]),
filename.length > 1 ? filename[1] : filename[0]));
} catch (IOException ioe) {
System.err.println(ioe);
}
}
currIndex = 0;
if (images == null || images.size() < 1) return;
imageNum.setText(String.valueOf(images.size() > 0 ? currIndex + 1 : 0));
imageMax.setText(String.valueOf(images.size()));
repaint();
}
}
});
button.setToolTipText("Load one ore more previously saved images");
bottomRow.add(button);
button = new JButton("Exit");
button.setMnemonic(KeyEvent.VK_X);
button.addActionListener(new AbstractAction() {
/**
*
*/
private static final long serialVersionUID = 6476551648402562464L;
public void actionPerformed(final ActionEvent ae) {
loop.stop();
closeWindow();
}
});
button.setToolTipText("Close this window");
bottomRow.add(button);
controlPanel.add(topRow);
controlPanel.add(bottomRow);
return controlPanel;
}
/**
* @param type the String to check
* @return the entry matching the input; if none match, the current selection
*/
private String lookupType(final String type) {
for (int i = 0; i < typeSelector.getItemCount(); ++i) {
if (typeSelector.getItemAt(i).toString().equals(type)) return (String) typeSelector.getItemAt(i);
}
return (String) typeSelector.getSelectedItem();
}
/**
* @param type the data type to select
* @return this.images, now set to the desired type
*/
private ArrayList<SavedImage<BufferedImage>> selectType(final String type) {
synchronized (this.map) {
ArrayList<SavedImage<BufferedImage>> currType = this.map.get(type);
if (currType == null) {
currType = new ArrayList<SavedImage<BufferedImage>>();
this.map.put(type, currType);
}
this.typeSelector.setSelectedItem(type);
this.images = currType;
imageNum.setText(String.valueOf(images.size() > 0 ? currIndex + 1 : 0));
imageMax.setText(String.valueOf(images.size()));
return this.images;
}
}
private void closeWindow() {
imageFrame.dispose();
imageFrame = null;
}
/**
* Saves an image (such as those stored in the cyclic image buffer)
* to a file on the disk
*
* @param image the image to save
* @param file the file to save to
*/
public void saveImageToFile(final BufferedImage image, final File file) {
String filename = file.getName();
try {
String extension = filename.substring(filename.lastIndexOf('.') + 1, filename.length()).toLowerCase();
if (extension.equals("png") || extension.equals("gif") || extension.equals("jpg") || extension.equals("jpeg")) {
ImageIO.write(image, extension, file);
} else if (extension.equals("ps") || extension.equals("eps")) {
PrintableImage printableImage = new PrintableImage(image,
file,
DocFlavor.SERVICE_FORMATTED.PRINTABLE,
DocFlavor.BYTE_ARRAY.POSTSCRIPT.getMimeType());
// } else if (extension.equals("gif")) {
// try {
// saveGif(image, file);
// } catch (IOException ioe) {
// try {
// saveGif(reduceColors(image), file);
// DialogUtil.showWarningDialog(imageFrame, "Automatically reduced colors",
// "GIF supports a maximum of 256 colors per file. Because the image to be saved had more,\n" +
// "the colors have been automatically remapped. This can produce undesireable dithering.\n" +
// "To avoid this effect, try to reduce the number of colors used when capturing images for\n" +
// "use in GIFs by using a quality setting of \"High\" or lower and/or disabling color\n" +
// "interpolation.");
// } catch (IOException ioe2) {
// ioe2.printStackTrace();
// DialogUtil.showErrorDialog(imageFrame, "Failed to create GIF", ioe2.toString());
// }
// }
} else {
ImageIO.write(image, "png", new File(file.getParent(), (filename = (filename + ".png"))));
}
} catch (Exception e) {
System.err.println("Exception while saving file " + filename + ": " + e);
}
}
public static void saveGif(final BufferedImage image, final File file) throws IOException {
Gif89Encoder gifenc = new Gif89Encoder(image);
gifenc.setComments(GIF_COMMENT);
gifenc.setTransparentIndex(-1);
gifenc.getFrameAt(0).setInterlaced(false);
gifenc.encode(new FileOutputStream(file));
}
public void saveGif(final List<BufferedImage> images, final File file) throws IOException {
int progress = 0;
final ProgressMonitor progressMonitor = new ProgressMonitor(imageFrame, "Saving image to: " + file, "", 0, images.size());
progressMonitor.setMillisToDecideToPopup(500);
Gif89Encoder gifenc = new Gif89Encoder();
for (final BufferedImage image : images) {
if (progressMonitor.isCanceled()) return;
gifenc.addFrame(image);
progressMonitor.setProgress(progress++);
Thread.yield();
}
gifenc.setComments(GIF_COMMENT);
gifenc.setLoopCount(0); //infinite
gifenc.setUniformDelay(loop.getDelay() / 10); //centiseconds
gifenc.encode(new FileOutputStream(file));
progressMonitor.setProgress(progress++);
Thread.yield();
System.out.println("Animated GIF saved");
}
public static BufferedImage switchBlackAndWhite(final BufferedImage image) {
BufferedImage result = new BufferedImage(image.getWidth(), image.getHeight(), image.getType());
for (int w = 0; w < image.getWidth(); ++w)
for (int h = 0; h < image.getHeight(); ++h) {
int rgb = image.getRGB(w, h);
if (rgb == Color.WHITE.getRGB()) rgb = Color.BLACK.getRGB();
else if (rgb == Color.BLACK.getRGB()) rgb = Color.WHITE.getRGB();
result.setRGB(w, h, rgb);
}
return result;
}
public static BufferedImage reduceColors(final BufferedImage image) {
BufferedImage indexedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
indexedImage.createGraphics().drawImage(image, 0, 0, null);
return indexedImage;
}
/**
* A file filter for accepting GIF files.
*/
protected static class GifFileFilter extends FileFilter {
public boolean accept(final File file) {
if (file.isDirectory()) return true;
String filename = file.getName().toLowerCase();
return filename.endsWith(".gif");
}
public String getDescription() {
return "GIF images";
}
}
/**
* A file filter for accepting a file in any of the accepted formats.
*/
protected static class ImageFileFilter extends FileFilter {
public boolean accept(final File file) {
if (file.isDirectory()) return true;
String filename = file.getName().toLowerCase();
if (filename.endsWith(".gif") ||
filename.endsWith(".jpg") ||
filename.endsWith(".jpeg") ||
filename.endsWith(".png") ||
filename.endsWith(".ps") ||
filename.endsWith(".eps")) {
return true;
}
return false;
}
public String getDescription() {
return "GIF, Jpeg, PNG, PS and EPS images";
}
}
protected class CheckBoxFileChooser extends JFileChooser {
/**
*
*/
private static final long serialVersionUID = -1001729783408270200L;
protected final JCheckBox checkBox;
public CheckBoxFileChooser(final String label) {
super(lastDir[0]);
checkBox = new JCheckBox(label);
checkBox.setToolTipText("Flip black and white to conserve ink when printed");
super.setAccessory(checkBox);
}
public boolean isSelected() {
return this.checkBox.isSelected();
}
}
private class PrintableImage implements Printable {
private BufferedImage image;
public PrintableImage(BufferedImage image, File file, DocFlavor flavor, String mimeType) throws Exception {
this.image = image;
//find a suitable print service to use
StreamPrintServiceFactory[] factories =
StreamPrintServiceFactory.lookupStreamPrintServiceFactories(
flavor, mimeType);
if (factories.length == 0) {
DialogUtil.showErrorDialog(imageFrame,
"Failed to create image",
"could not find suitable print service");
return;
}
FileOutputStream fileOut = new FileOutputStream(file);
StreamPrintService sps = factories[0].getPrintService(fileOut);
DocPrintJob printJob = sps.createPrintJob();
//create the attributes
//PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet();
//create the printable image and print it
Doc imageDoc = new SimpleDoc(this, flavor, null);
printJob.print(imageDoc, null);
fileOut.close();
}
public int print(Graphics g, PageFormat pf, int pageIndex) {
if (pageIndex == 0) {
Graphics2D g2d = (Graphics2D) g;
g2d.translate(pf.getImageableX(), pf.getImageableY());
g2d.drawImage(image, null, 0, 0);
return Printable.PAGE_EXISTS;
} else {
return Printable.NO_SUCH_PAGE;
}
}
}
}