/*
* org.openmicroscopy.shoola.agents.imviewer.util.saver.ImgSaver
*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2015 University of Dundee. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.agents.imviewer.util.saver;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import javax.swing.JDialog;
import javax.swing.JFrame;
import org.apache.commons.io.FileUtils;
import org.openmicroscopy.shoola.agents.imviewer.ImViewerAgent;
import org.openmicroscopy.shoola.agents.imviewer.util.ImagePaintingFactory;
import org.openmicroscopy.shoola.agents.imviewer.view.ImViewer;
import org.openmicroscopy.shoola.env.ui.UserNotifier;
import org.openmicroscopy.shoola.util.filter.file.TIFFFilter;
import org.openmicroscopy.shoola.util.image.geom.Factory;
import org.openmicroscopy.shoola.util.image.io.Encoder;
import org.openmicroscopy.shoola.util.image.io.EncoderException;
import org.openmicroscopy.shoola.util.image.io.TIFFEncoder;
import org.openmicroscopy.shoola.util.image.io.WriterImage;
import org.openmicroscopy.shoola.util.ui.MessageBox;
import org.openmicroscopy.shoola.util.ui.NotificationDialog;
import org.openmicroscopy.shoola.util.ui.RegExFactory;
import org.openmicroscopy.shoola.util.ui.UIUtilities;
/**
* A modal dialog to save the currently rendered image.
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author Andrea Falconi
* <a href="mailto:a.falconi@dundee.ac.uk">a.falconi@dundee.ac.uk</a>
* @author Donald MacDonald
* <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a>
* @version 3.0
* @since OME2.2
*/
public class ImgSaver
extends JDialog
{
/** Bound property indicating to save the image. */
public static final String SAVE_IMAGE_PROPERTY = "saveImage";
/** Save the main image. */
public static final int IMAGE = ImgSaverUI.IMAGE;
/** Save the grid image. */
public static final int GRID_IMAGE = ImgSaverUI.GRID_IMAGE;
/** Save the projected image. */
public static final int PROJECTED_IMAGE =
ImgSaverUI.PROJECTED_IMAGE;
/**
* Save the images and an image of each channel composing the rendered
* image.
*/
public static final int IMAGE_AND_COMPONENTS =
ImgSaverUI.IMAGE_AND_COMPONENTS;
/**
* Save the images and an image of each channel composing the rendered
* image. Each channel rendered in grey scale mode.
*/
public static final int IMAGE_AND_COMPONENTS_GREY =
ImgSaverUI.IMAGE_AND_COMPONENTS_GREY;
/** Save the lens image. */
static final int LENS_IMAGE = 4;
/** Save the lens image and the split channels. */
static final int LENS_IMAGE_AND_COMPONENTS = 5;
/** Save the lens image. */
static final int LENS_IMAGE_AND_COMPONENTS_GREY = 6;
/** Indicates to display all possible save options. */
public static final int FULL = 0;
/** Indicates to display save options without lens saving options. */
public static final int PARTIAL = 1;
/** Indicates to display save options without lens saving options. */
public static final int BASIC = 2;
/** The window's title. */
static final String TITLE = "Save Image";
/** The title of the preview window. */
static final String PREVIEW_TITLE = "Preview image to save.";
/**
* Indicates that the question dialog is for the <code>Preview</code>
* dialog.
*/
static final int PREVIEW = 0;
/** Indicates that the question dialog will save the image directly. */
static final int DIRECT = 1;
/** The message when an image with the name and extension already exists. */
private static final String MESSAGE = "A file with the same name and \n" +
"extension already exists in this " +
"directory.\n" +
"Do you really want to save the image?";
/** Reference to the model. */
private ImViewer model;
/** The UI delegate. */
private ImgSaverUI uiDelegate;
/** The file's format. */
private String format;
/** The main image i.e. the one displayed in the viewer. */
private BufferedImage mainImage;
/**
* The list of images composing the main image. <code>null</code> if no
* components.
*/
private List imageComponents;
/** The type of the image to save. */
private int imageType;
/** One of the following constants: {@link #FULL}, {@link #PARTIAL}. */
private int savingType;
/**
* Notifies that the image has been saved locally.
*
* @param name The path to the local file.
*/
private void notifySave(String name)
{
NotificationDialog dialog = new NotificationDialog(this, "Save Image",
getSaveMessage(name), true);
dialog.addPropertyChangeListener(new PropertyChangeListener() {
/**
* Opens the image with default viewer.
*/
public void propertyChange(PropertyChangeEvent evt) {
if (NotificationDialog.HYPERLINK_OPEN_PROPERTY.equals(
evt.getPropertyName())) {
UserNotifier un =
ImViewerAgent.getRegistry().getUserNotifier();
un.openApplication(null, (String) evt.getNewValue());
}
}
});
dialog.pack();
UIUtilities.centerAndShow(dialog);
}
/**
* Creates and returns the save message.
*
* @return See above.
*/
private String getSaveMessage(String name)
{
StringBuffer buffer = new StringBuffer();
buffer.append("<html><body>");
buffer.append("<p>The image has been saved successfully.<br>");
buffer.append("<a href=\"");
buffer.append(name);
buffer.append("\">View Image</a>.");
buffer.append("</body><html>");
return buffer.toString();
}
/**
* Creates a single image.
*
* @param image The image to create.
* @param constraint The constraint indicating to add the scale bar.
* @param name The name of the image.
* @throws EncoderException
* @throws IOException
*/
private void writeSingleImage(BufferedImage image, boolean constraint,
String name)
throws EncoderException, IOException
{
int width = image.getWidth();
int h = image.getHeight();
String v = getUnitBarValue();
int s = (int) model.getUnitBarSize();
BufferedImage newImage = new BufferedImage(width, h,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = (Graphics2D) newImage.getGraphics();
ImagePaintingFactory.setGraphicRenderingSettings(g2);
//Paint the original image.
g2.drawImage(image, null, 0, 0);
if (constraint)
ImagePaintingFactory.paintScaleBar(g2, width-s-10, h-10, s, v);
writeImage(newImage, name);
}
/**
* Displays the preview dialog with images depending on the
* saving type.
*
* @param savingType The type of saving.
*/
private void createImages(int savingType)
{
boolean b = uiDelegate.includeROI();
switch (savingType) {
default:
case ImgSaverUI.IMAGE:
imageType = ImgSaverUI.IMAGE;
mainImage = model.getDisplayedImage(b);
imageComponents = null;
break;
case ImgSaverUI.GRID_IMAGE:
imageType = ImgSaverUI.GRID_IMAGE;
mainImage = model.getGridImage();
imageComponents = null;
break;
case ImgSaverUI.PROJECTED_IMAGE:
imageType = ImgSaverUI.PROJECTED_IMAGE;
mainImage = model.getDisplayedProjectedImage();
imageComponents = null;
break;
case ImgSaverUI.IMAGE_AND_COMPONENTS:
imageType = ImgSaverUI.IMAGE_AND_COMPONENTS;
mainImage = model.getDisplayedImage(b);
imageComponents = model.getImageComponents(
ImViewer.RGB_MODEL, b);
break;
case ImgSaverUI.IMAGE_AND_COMPONENTS_GREY:
imageType = ImgSaverUI.IMAGE_AND_COMPONENTS;
mainImage = model.getDisplayedImage(b);
imageComponents = model.getImageComponents(
ImViewer.GREY_SCALE_MODEL, b);
break;
case ImgSaverUI.LENS_IMAGE:
imageType = ImgSaverUI.LENS_IMAGE;
mainImage = model.getZoomedLensImage();
imageComponents = null;
break;
case ImgSaverUI.LENS_IMAGE_AND_COMPONENTS:
imageType = ImgSaverUI.LENS_IMAGE_AND_COMPONENTS;
mainImage = model.getZoomedLensImage();
imageComponents = model.getLensImageComponents(
ImViewer.RGB_MODEL);
break;
case ImgSaverUI.LENS_IMAGE_AND_COMPONENTS_GREY:
imageType = ImgSaverUI.LENS_IMAGE_AND_COMPONENTS;
mainImage = model.getZoomedLensImage();
imageComponents = model.getLensImageComponents(
ImViewer.GREY_SCALE_MODEL);
}
}
/**
* Writes the image.
*
* @param image The image to write to the file.
* @param n The name of the image.
* @throws EncoderException
* @throws IOException
*/
private void writeImage(BufferedImage image, String n) throws EncoderException, IOException
{
//n += "."+format;
String extendedName = getExtendedName(n, format);
File f = new File(extendedName);
if (TIFFFilter.TIF.equals(format)) {
Encoder encoder = new TIFFEncoder(Factory.createImage(image),
new DataOutputStream(new FileOutputStream(f)));
WriterImage.saveImage(encoder);
} else
WriterImage.saveImage(f, image, format);
close();
}
/** Sets the properties of the dialog. */
private void setProperties()
{
setTitle(TITLE);
setModal(true);
}
/**
* Checks and sets the saving type.
*
* @param t The value to control and set.
*/
private void checkSavingType(int t)
{
switch (t) {
case PARTIAL:
savingType = t;
break;
case FULL:
default:
savingType = t;
break;
}
}
/**
* Creates a new instance.
*
* @param owner The owner of this dialog.
* @param model Reference to the Model.
* Mustn't be <code>null</code>.
* @param savingType One of the constants defined by this class.
* @param defaultType Either, <code>Image</code>, <code>GridImage</code>
* or <code>Projected Image</code> depending on the
* selected tab.
* @param withROI Passed <code>true</code> to turn on the option to
* include ROI, <code>false</code> otherwise.
*/
public ImgSaver(JFrame owner, ImViewer model, int savingType,
int defaultType, boolean withROI)
{
super(owner);
//if (model == null) throw new IllegalArgumentException("No model.");
checkSavingType(savingType);
this.model = model;
setProperties();
uiDelegate = new ImgSaverUI(this, defaultType, withROI);
pack();
}
/**
* Adds the extension to the passed name if necessary.
*
* @param name The name to handle.
* @param format The selected file format.
* @return See above.
*/
String getExtendedName(String name, String format)
{
String extension = "."+format;
Pattern pattern = RegExFactory.createPattern(extension);
String n;
if (RegExFactory.find(pattern, name)) {
n = name;
} else {
pattern = RegExFactory.createCaseInsensitivePattern(extension);
if (RegExFactory.find(pattern, name)) n = name;
else n = name + "." + format;
}
return n;
}
/**
* Returns the name of the image.
*
* @return See above.
*/
String getPartialImageName()
{
return UIUtilities.removeFileExtension(model.getImageName());
}
/**
* Returns the type of options available.
*
* @return See above.
*/
int getSavingType() { return savingType; }
/**
* Returns the type of image to save.
*
* @return See above.
*/
int getImageType() { return imageType; }
/**
* Sets the format of the file.
*
* @param format The file's format.
*/
void setFileFormat(String format) { this.format = format; }
/** Brings up a preview of the image or images to save. */
void previewImage()
{
ImgSaverPreviewer preview = new ImgSaverPreviewer(this);
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
createImages(uiDelegate.getSavingType());
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
preview.initialize();
UIUtilities.centerAndShow(preview);
}
/**
* Brings up on screen the dialog asking a <code>Yes/No</code>.
*
* @param index One of the constants defined by this class.
*/
void setSelection(int index)
{
MessageBox dialog = new MessageBox(this, "Save Image", MESSAGE);
dialog.pack();
Dimension d = dialog.getPreferredSize();
dialog.setSize(d.width, d.height+30);
if (dialog.centerMsgBox() == MessageBox.YES_OPTION) {
dialog.setVisible(false);
switch (index) {
case DIRECT:
saveImage(true);
break;
case PREVIEW:
previewImage();
}
dialog.dispose();
}
}
/** Closes the window and disposes. */
void close()
{
setVisible(false);
dispose();
}
/**
* Saves the displayed images.
*
* @param init Pass <code>true</code> to initialize the images to save,
* <code>false</code> otherwise.
*/
void saveImage(boolean init)
{
UserNotifier un = ImViewerAgent.getRegistry().getUserNotifier();
if (init) createImages(uiDelegate.getSavingType());
//Builds the image to display.
boolean unitBar = model.isUnitBar();
String v = getUnitBarValue();
int s = (int) getUnitBarSize();
boolean constrain;
try {
String name = uiDelegate.getSelectedFilePath();
// make sure the parent directory paths all exist
FileUtils.forceMkdir(new File(name).getParentFile());
if (imageComponents == null) {
constrain = unitBar && v != null && s < mainImage.getWidth()
&& imageType == ImgSaverUI.IMAGE;
writeSingleImage(mainImage, constrain, name);
} else {
if (mainImage == null) return;
Iterator i;
int h, w;
BufferedImage newImage;
Graphics2D g2;
if (uiDelegate.isSaveImagesInSeparatedFiles()) {
constrain = unitBar && v != null && s < mainImage.getWidth()
&& imageType == ImgSaverUI.IMAGE;
writeSingleImage(mainImage, constrain, name);
i = imageComponents.iterator();
int j = 0;
while (i.hasNext()) {
constrain = unitBar && v != null &&
imageType != ImgSaverUI.LENS_IMAGE_AND_COMPONENTS;
writeSingleImage((BufferedImage) i.next(), constrain,
name+"_"+j);
j++;
}
} else {
int width = mainImage.getWidth();
h = mainImage.getHeight();
int n = imageComponents.size();
w = width*(n+1)+ImgSaverPreviewer.SPACE*(n-1);
newImage = new BufferedImage(w, h,
BufferedImage.TYPE_INT_RGB);
g2 = (Graphics2D) newImage.getGraphics();
g2.setColor(Color.WHITE);
ImagePaintingFactory.setGraphicRenderingSettings(g2);
//Paint the original image.
i = imageComponents.iterator();
int x = 0;
while (i.hasNext()) {
g2.drawImage((BufferedImage) i.next(), null, x, 0);
if (unitBar && v != null &&
imageType != ImgSaverUI.LENS_IMAGE_AND_COMPONENTS)
ImagePaintingFactory.paintScaleBar(g2, x+width-s-10,
h-10, s, v);
x += width;
g2.fillRect(x, 0, ImgSaverPreviewer.SPACE, h);
x += ImgSaverPreviewer.SPACE;
}
g2.drawImage(mainImage, null, x, 0);
if (unitBar && v != null &&
!(imageType == ImgSaverUI.LENS_IMAGE_AND_COMPONENTS ||
imageType == ImgSaverUI.LENS_IMAGE))
ImagePaintingFactory.paintScaleBar(g2, x+width-s-10,
h-10, s, v);
writeImage(newImage, name);
}
}
} catch (Exception e) {
if (e instanceof IOException || e.getCause() instanceof IOException)
un.notifyInfo(
"Save Image failure",
"Could not access file "
+ uiDelegate.getSelectedFilePath()
+ "\nMake sure you have the necessary permissions to perform this action.");
else
un.notifyError("Save Image failure",
"An error occurred while saving the image.", e);
return;
}
notifySave(getExtendedName(uiDelegate.getSelectedFilePath(), format));
if (uiDelegate.isSetDefaultFolder())
UIUtilities.setDefaultFolder(uiDelegate.getCurrentDirectory());
}
/**
* Returns the main image.
*
* @return See above.
*/
BufferedImage getImage() { return mainImage; }
/**
* Returns the images composing the main image i.e. one per channel
* if two or more channels compose the main image.
* Returns <code>null</code> otherwise.
*
* @return See above.
*/
List getImageComponents() { return imageComponents; }
/**
* Returns <code>true</code> if the unit bar is painted on top of
* the displayed image, <code>false</code> otherwise.
*
* @return See above.
*/
boolean isUnitBar() { return model.isUnitBar(); }
/**
* Returns the value (with two decimals) of the unit bar or
* <code>null</code> if the actual value is <i>negative</i>.
*
* @return See above.
*/
String getUnitBarValue() { return model.getUnitBarValue(); }
/**
* Returns the size of the unit bar.
*
* @return See above.
*/
double getUnitBarSize() { return model.getUnitBarSize(); }
/**
* Returns the color of the unit bar.
*
* @return See above.
*/
Color getUnitBarColor() { return model.getUnitBarColor(); }
}