/* * 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(); }