/*
* Copyright 2010-2015 Institut Pasteur.
*
* This file is part of Icy.
*
* Icy 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 3 of the License, or
* (at your option) any later version.
*
* Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>.
*/
package icy.gui.sequence.tools;
import icy.gui.component.IcyTextField;
import icy.gui.component.sequence.SequencePreviewPanel;
import icy.image.IcyBufferedImage;
import icy.image.IcyBufferedImageUtil;
import icy.image.IcyBufferedImageUtil.FilterType;
import icy.math.UnitUtil;
import icy.resource.ResourceUtil;
import icy.sequence.AbstractSequenceModel;
import icy.sequence.Sequence;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JSpinner.DefaultEditor;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* @author Stephane
*/
public abstract class SequenceBaseResizePanel extends JPanel
{
/**
*
*/
private static final long serialVersionUID = -9220345511598410844L;
protected enum SizeUnit
{
PIXEL, PERCENT, MICRON
}
// pixel / micron
protected class OriginalModel extends AbstractSequenceModel
{
public OriginalModel()
{
super();
}
@Override
public int getSizeX()
{
return getMaxSizeX();
}
@Override
public int getSizeY()
{
return getMaxSizeY();
}
@Override
public int getSizeZ()
{
return sequence.getSizeZ();
}
@Override
public int getSizeT()
{
return sequence.getSizeT();
}
@Override
public int getSizeC()
{
return sequence.getSizeC();
}
@Override
public BufferedImage getImage(int t, int z)
{
return sequence.getImage(t, z);
}
@Override
public BufferedImage getImage(int t, int z, int c)
{
return sequence.getImage(t, z, c);
}
}
protected class ResultModel extends AbstractSequenceModel
{
public ResultModel()
{
super();
}
@Override
public int getSizeX()
{
return getMaxSizeX();
}
@Override
public int getSizeY()
{
return getMaxSizeY();
}
@Override
public int getSizeZ()
{
return sequence.getSizeZ();
}
@Override
public int getSizeT()
{
return sequence.getSizeT();
}
@Override
public int getSizeC()
{
return sequence.getSizeC();
}
@Override
public BufferedImage getImage(int t, int z)
{
try
{
return IcyBufferedImageUtil.scale(sequence.getImage(t, z), getNewWidth(), getNewHeight(),
getResizeContent(), getXAlign(), getYAlign(), getFilterType());
}
catch (OutOfMemoryError e)
{
return null;
}
}
@Override
public BufferedImage getImage(int t, int z, int c)
{
return ((IcyBufferedImage) getImage(t, z)).getImage(c);
}
}
final Sequence sequence;
// GUI
protected JCheckBox keepRatioCheckBox;
protected JSpinner heightSpinner;
protected JSpinner widthSpinner;
protected SequencePreviewPanel originalPreview;
protected SequencePreviewPanel resultPreview;
protected JPanel infoPanel;
protected IcyTextField widthField;
protected IcyTextField heightField;
protected IcyTextField sizeField;
protected JComboBox sizeUnitComboBox;
protected JLabel accolLeftLabel;
protected JPanel panel;
protected Component horizontalGlue;
protected Component horizontalGlue_1;
protected JPanel settingPanel;
/**
* Create the panel.
*/
public SequenceBaseResizePanel(Sequence sequence)
{
super();
this.sequence = sequence;
initialize();
setNewWidth(sequence.getSizeX());
setNewHeight(sequence.getSizeY());
accolLeftLabel.setIcon(ResourceUtil.getImageIcon(ResourceUtil.IMAGE_ACCOLADE_LEFT));
accolLeftLabel.setText(null);
originalPreview.setFitToView(false);
resultPreview.setFitToView(false);
originalPreview.setModel(new OriginalModel());
resultPreview.setModel(new ResultModel());
updatePreview();
final ChangeListener spinnerChangeListener = new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent e)
{
// maintain ratio
if (keepRatioCheckBox.isSelected())
{
final Sequence seq = SequenceBaseResizePanel.this.sequence;
if (e.getSource() == widthSpinner)
{
// adjust height
final double ratio = (double) getNewWidth() / (double) seq.getWidth();
setNewHeight((int) Math.round(seq.getHeight() * ratio));
}
else
{
// adjust width
final double ratio = (double) getNewHeight() / (double) seq.getHeight();
setNewWidth((int) Math.round(seq.getWidth() * ratio));
}
}
updatePreview();
}
};
heightSpinner.addChangeListener(spinnerChangeListener);
widthSpinner.addChangeListener(spinnerChangeListener);
sizeUnitComboBox.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
// convert width and height in new unit
final int w = Integer.parseInt(widthField.getText());
final int h = Integer.parseInt(heightField.getText());
setNewWidth(w);
setNewHeight(h);
}
});
}
protected void initialize()
{
setLayout(new BorderLayout(0, 0));
panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
add(panel, BorderLayout.NORTH);
infoPanel = new JPanel();
panel.add(infoPanel);
infoPanel.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Size in pixel",
TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
GridBagLayout gbl_infoPanel = new GridBagLayout();
gbl_infoPanel.columnWidths = new int[] {20, 100, 20, 100, 20, 100, 20, 0};
gbl_infoPanel.rowHeights = new int[] {0, 0, 0};
gbl_infoPanel.columnWeights = new double[] {1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, Double.MIN_VALUE};
gbl_infoPanel.rowWeights = new double[] {0.0, 0.0, Double.MIN_VALUE};
infoPanel.setLayout(gbl_infoPanel);
final JLabel lblOriginalWidth = new JLabel("Width");
GridBagConstraints gbc_lblOriginalWidth = new GridBagConstraints();
gbc_lblOriginalWidth.fill = GridBagConstraints.BOTH;
gbc_lblOriginalWidth.insets = new Insets(0, 0, 5, 5);
gbc_lblOriginalWidth.gridx = 1;
gbc_lblOriginalWidth.gridy = 0;
infoPanel.add(lblOriginalWidth, gbc_lblOriginalWidth);
lblOriginalWidth.setToolTipText("");
final JLabel lblNewLabel_3 = new JLabel("Height");
GridBagConstraints gbc_lblNewLabel_3 = new GridBagConstraints();
gbc_lblNewLabel_3.fill = GridBagConstraints.BOTH;
gbc_lblNewLabel_3.insets = new Insets(0, 0, 5, 5);
gbc_lblNewLabel_3.gridx = 3;
gbc_lblNewLabel_3.gridy = 0;
infoPanel.add(lblNewLabel_3, gbc_lblNewLabel_3);
lblNewLabel_3.setToolTipText("");
final JLabel lblNewLabel_2 = new JLabel("Memory size");
GridBagConstraints gbc_lblNewLabel_2 = new GridBagConstraints();
gbc_lblNewLabel_2.fill = GridBagConstraints.BOTH;
gbc_lblNewLabel_2.insets = new Insets(0, 0, 5, 5);
gbc_lblNewLabel_2.gridx = 5;
gbc_lblNewLabel_2.gridy = 0;
infoPanel.add(lblNewLabel_2, gbc_lblNewLabel_2);
lblNewLabel_2.setToolTipText("");
widthField = new IcyTextField();
widthField.setToolTipText("Width in pixel");
widthField.setText("0000");
widthField.setEditable(false);
GridBagConstraints gbc_widthField = new GridBagConstraints();
gbc_widthField.fill = GridBagConstraints.BOTH;
gbc_widthField.insets = new Insets(0, 0, 0, 5);
gbc_widthField.gridx = 1;
gbc_widthField.gridy = 1;
infoPanel.add(widthField, gbc_widthField);
widthField.setColumns(5);
heightField = new IcyTextField();
heightField.setToolTipText("Height in pixel");
heightField.setText("0");
heightField.setEditable(false);
GridBagConstraints gbc_heightField = new GridBagConstraints();
gbc_heightField.fill = GridBagConstraints.BOTH;
gbc_heightField.insets = new Insets(0, 0, 0, 5);
gbc_heightField.gridx = 3;
gbc_heightField.gridy = 1;
infoPanel.add(heightField, gbc_heightField);
heightField.setColumns(5);
sizeField = new IcyTextField();
sizeField.setToolTipText("Memory size");
sizeField.setText("0.0B");
sizeField.setEditable(false);
GridBagConstraints gbc_sizeField = new GridBagConstraints();
gbc_sizeField.insets = new Insets(0, 0, 0, 5);
gbc_sizeField.fill = GridBagConstraints.BOTH;
gbc_sizeField.gridx = 5;
gbc_sizeField.gridy = 1;
infoPanel.add(sizeField, gbc_sizeField);
sizeField.setColumns(5);
settingPanel = new JPanel();
panel.add(settingPanel);
settingPanel.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Setting",
TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
GridBagLayout gbl_settingPanel = new GridBagLayout();
gbl_settingPanel.columnWidths = new int[] {20, 100, 20, 100, 20, 100, 20, 0};
gbl_settingPanel.rowHeights = new int[] {0, 0, 0, 0, 10, 0, 0, 0};
gbl_settingPanel.columnWeights = new double[] {1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, Double.MIN_VALUE};
gbl_settingPanel.rowWeights = new double[] {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
settingPanel.setLayout(gbl_settingPanel);
final JLabel lblWidth = new JLabel("Width");
GridBagConstraints gbc_lblWidth = new GridBagConstraints();
gbc_lblWidth.fill = GridBagConstraints.BOTH;
gbc_lblWidth.insets = new Insets(0, 0, 5, 5);
gbc_lblWidth.gridx = 1;
gbc_lblWidth.gridy = 0;
settingPanel.add(lblWidth, gbc_lblWidth);
widthSpinner = new JSpinner();
widthSpinner.setModel(new SpinnerNumberModel(new Integer(1), new Integer(1), null, new Integer(1)));
widthSpinner.setToolTipText("New width to set");
GridBagConstraints gbc_widthSpinner = new GridBagConstraints();
gbc_widthSpinner.fill = GridBagConstraints.BOTH;
gbc_widthSpinner.insets = new Insets(0, 0, 5, 5);
gbc_widthSpinner.gridx = 1;
gbc_widthSpinner.gridy = 1;
settingPanel.add(widthSpinner, gbc_widthSpinner);
final JLabel lblNewLabel = new JLabel("Height");
GridBagConstraints gbc_lblNewLabel = new GridBagConstraints();
gbc_lblNewLabel.fill = GridBagConstraints.BOTH;
gbc_lblNewLabel.insets = new Insets(0, 0, 5, 5);
gbc_lblNewLabel.gridx = 1;
gbc_lblNewLabel.gridy = 2;
settingPanel.add(lblNewLabel, gbc_lblNewLabel);
accolLeftLabel = new JLabel("");
accolLeftLabel.setHorizontalAlignment(SwingConstants.CENTER);
GridBagConstraints gbc_accolLeftLabel = new GridBagConstraints();
gbc_accolLeftLabel.fill = GridBagConstraints.BOTH;
gbc_accolLeftLabel.gridheight = 3;
gbc_accolLeftLabel.insets = new Insets(0, 0, 5, 5);
gbc_accolLeftLabel.gridx = 2;
gbc_accolLeftLabel.gridy = 1;
settingPanel.add(accolLeftLabel, gbc_accolLeftLabel);
lblNewLabel.setLabelFor(heightSpinner);
sizeUnitComboBox = new JComboBox();
sizeUnitComboBox.setMaximumRowCount(3);
sizeUnitComboBox.setToolTipText("Width / Height unit");
sizeUnitComboBox.setModel(new DefaultComboBoxModel(new String[] {"pixel", "%", "\u00B5m"}));
sizeUnitComboBox.setSelectedIndex(0);
GridBagConstraints gbc_sizeUnitComboBox = new GridBagConstraints();
gbc_sizeUnitComboBox.fill = GridBagConstraints.HORIZONTAL;
gbc_sizeUnitComboBox.gridheight = 3;
gbc_sizeUnitComboBox.insets = new Insets(0, 0, 5, 5);
gbc_sizeUnitComboBox.gridx = 3;
gbc_sizeUnitComboBox.gridy = 1;
settingPanel.add(sizeUnitComboBox, gbc_sizeUnitComboBox);
heightSpinner = new JSpinner();
heightSpinner.setModel(new SpinnerNumberModel(new Integer(1), new Integer(1), null, new Integer(1)));
heightSpinner.setToolTipText("New height to set");
GridBagConstraints gbc_heightSpinner = new GridBagConstraints();
gbc_heightSpinner.fill = GridBagConstraints.BOTH;
gbc_heightSpinner.insets = new Insets(0, 0, 5, 5);
gbc_heightSpinner.gridx = 1;
gbc_heightSpinner.gridy = 3;
settingPanel.add(heightSpinner, gbc_heightSpinner);
keepRatioCheckBox = new JCheckBox("Keep ratio");
keepRatioCheckBox.setVerticalAlignment(SwingConstants.TOP);
keepRatioCheckBox.setToolTipText("Keep original aspect ratio");
keepRatioCheckBox.setSelected(true);
GridBagConstraints gbc_keepRatioCheckBox = new GridBagConstraints();
gbc_keepRatioCheckBox.gridwidth = 3;
gbc_keepRatioCheckBox.fill = GridBagConstraints.BOTH;
gbc_keepRatioCheckBox.insets = new Insets(0, 0, 5, 5);
gbc_keepRatioCheckBox.gridx = 1;
gbc_keepRatioCheckBox.gridy = 5;
settingPanel.add(keepRatioCheckBox, gbc_keepRatioCheckBox);
horizontalGlue = Box.createHorizontalGlue();
GridBagConstraints gbc_horizontalGlue = new GridBagConstraints();
gbc_horizontalGlue.fill = GridBagConstraints.HORIZONTAL;
gbc_horizontalGlue.insets = new Insets(0, 0, 0, 5);
gbc_horizontalGlue.gridx = 5;
gbc_horizontalGlue.gridy = 6;
settingPanel.add(horizontalGlue, gbc_horizontalGlue);
horizontalGlue_1 = Box.createHorizontalGlue();
GridBagConstraints gbc_horizontalGlue_1 = new GridBagConstraints();
gbc_horizontalGlue_1.fill = GridBagConstraints.HORIZONTAL;
gbc_horizontalGlue_1.gridx = 6;
gbc_horizontalGlue_1.gridy = 6;
settingPanel.add(horizontalGlue_1, gbc_horizontalGlue_1);
final JPanel previewPanel = new JPanel();
previewPanel.setBorder(new TitledBorder(null, "Preview", TitledBorder.LEADING, TitledBorder.TOP, null, null));
add(previewPanel, BorderLayout.CENTER);
previewPanel.setLayout(new BoxLayout(previewPanel, BoxLayout.LINE_AXIS));
originalPreview = new SequencePreviewPanel("Original");
previewPanel.add(originalPreview);
resultPreview = new SequencePreviewPanel("Result");
previewPanel.add(resultPreview);
}
void updatePreview()
{
final int w = getNewWidth();
final int h = getNewHeight();
widthField.setText(Integer.toString(w));
heightField.setText(Integer.toString(h));
sizeField.setText(UnitUtil.getBytesString(w * h * sequence.getSizeC() * sequence.getSizeZ()
* sequence.getSizeT() * sequence.getDataType_().getSize()));
originalPreview.imageChanged();
resultPreview.imageChanged();
}
public Sequence getSequence()
{
return sequence;
}
/**
* pixel resolution X (micron / pixel)
*/
public double getPixelSizeX()
{
if (sequence != null)
return sequence.getPixelSizeX();
return 1d;
}
/**
* pixel resolution X (micron / pixel)
*/
public double getPixelSizeY()
{
if (sequence != null)
return sequence.getPixelSizeY();
return 1d;
}
public SizeUnit getSizeUnit()
{
switch (sizeUnitComboBox.getSelectedIndex())
{
default:
case 0:
return SizeUnit.PIXEL;
case 1:
return SizeUnit.PERCENT;
case 2:
return SizeUnit.MICRON;
}
}
public int unitToPixel(double value, int originPixel, SizeUnit unit, double micronPerPixel)
{
switch (unit)
{
default:
case PIXEL:
return (int) Math.round(value);
case PERCENT:
return (int) Math.round((originPixel * value) / 100d);
case MICRON:
return (int) Math.round(value / micronPerPixel);
}
}
public int unitToPixelX(double value, int originPixel, SizeUnit unit)
{
return unitToPixel(value, originPixel, unit, getPixelSizeX());
}
public int unitToPixelY(double value, int originPixel, SizeUnit unit)
{
return unitToPixel(value, originPixel, unit, getPixelSizeY());
}
public double pixelToUnit(int value, int originPixel, SizeUnit unit, double micronPerPixel)
{
switch (unit)
{
default:
case PIXEL:
return value;
case PERCENT:
return (int) Math.round((value * 100d) / originPixel);
case MICRON:
return (int) (value * micronPerPixel);
}
}
public double pixelXToUnit(int value, int originPixel, SizeUnit unit)
{
return pixelToUnit(value, originPixel, unit, getPixelSizeX());
}
public double pixelYToUnit(int value, int originPixel, SizeUnit unit)
{
return pixelToUnit(value, originPixel, unit, getPixelSizeY());
}
public double getSpinnerSizeValue(JSpinner spinner)
{
switch (getSizeUnit())
{
default:
case PIXEL:
return ((Integer) spinner.getValue()).intValue();
case PERCENT:
case MICRON:
return ((Double) spinner.getValue()).doubleValue();
}
}
public int getNewWidth()
{
final int result = unitToPixelX(getSpinnerSizeValue(widthSpinner), sequence.getSizeX(), getSizeUnit());
return Math.min(65535, Math.max(1, result));
}
public int getNewHeight()
{
final int result = unitToPixelY(getSpinnerSizeValue(heightSpinner), sequence.getSizeY(), getSizeUnit());
return Math.min(Math.max(1, result), 65535);
}
void setSpinnerSizeValue(JSpinner spinner, double value)
{
switch (getSizeUnit())
{
default:
case PIXEL:
spinner.setModel(new SpinnerNumberModel((int) value, 0, 65535, 1));
// we don't want the model to affect
((DefaultEditor) spinner.getEditor()).getTextField().setColumns(1);
break;
case PERCENT:
spinner.setModel(new SpinnerNumberModel(value, 0d, Double.MAX_VALUE, 1d));
// we don't want the model to affect
((DefaultEditor) spinner.getEditor()).getTextField().setColumns(1);
break;
case MICRON:
spinner.setModel(new SpinnerNumberModel(value, 0d, Double.MAX_VALUE, 0.01d));
// we don't want the model to affect
((DefaultEditor) spinner.getEditor()).getTextField().setColumns(1);
break;
}
}
void setNewWidth(int value)
{
setSpinnerSizeValue(widthSpinner, pixelXToUnit(value, sequence.getSizeX(), getSizeUnit()));
}
void setNewHeight(int value)
{
setSpinnerSizeValue(heightSpinner, pixelYToUnit(value, sequence.getSizeY(), getSizeUnit()));
}
public int getMaxSizeX()
{
return Math.max(getNewWidth(), sequence.getSizeX());
}
public int getMaxSizeY()
{
return Math.max(getNewHeight(), sequence.getSizeY());
}
public abstract FilterType getFilterType();
public abstract boolean getResizeContent();
public abstract int getXAlign();
public abstract int getYAlign();
}