001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.gui.sequence.tools; 020 021import icy.gui.component.IcyTextField; 022import icy.gui.component.sequence.SequencePreviewPanel; 023import icy.image.IcyBufferedImage; 024import icy.image.IcyBufferedImageUtil; 025import icy.image.IcyBufferedImageUtil.FilterType; 026import icy.math.UnitUtil; 027import icy.resource.ResourceUtil; 028import icy.sequence.AbstractSequenceModel; 029import icy.sequence.Sequence; 030 031import java.awt.BorderLayout; 032import java.awt.Color; 033import java.awt.Component; 034import java.awt.GridBagConstraints; 035import java.awt.GridBagLayout; 036import java.awt.Insets; 037import java.awt.event.ActionEvent; 038import java.awt.event.ActionListener; 039import java.awt.image.BufferedImage; 040 041import javax.swing.Box; 042import javax.swing.BoxLayout; 043import javax.swing.DefaultComboBoxModel; 044import javax.swing.JCheckBox; 045import javax.swing.JComboBox; 046import javax.swing.JLabel; 047import javax.swing.JPanel; 048import javax.swing.JSpinner; 049import javax.swing.JSpinner.DefaultEditor; 050import javax.swing.SpinnerNumberModel; 051import javax.swing.SwingConstants; 052import javax.swing.UIManager; 053import javax.swing.border.TitledBorder; 054import javax.swing.event.ChangeEvent; 055import javax.swing.event.ChangeListener; 056 057/** 058 * @author Stephane 059 */ 060public abstract class SequenceBaseResizePanel extends JPanel 061{ 062 /** 063 * 064 */ 065 private static final long serialVersionUID = -9220345511598410844L; 066 067 protected enum SizeUnit 068 { 069 PIXEL, PERCENT, MICRON 070 } 071 072 // pixel / micron 073 074 protected class OriginalModel extends AbstractSequenceModel 075 { 076 public OriginalModel() 077 { 078 super(); 079 } 080 081 @Override 082 public int getSizeX() 083 { 084 return getMaxSizeX(); 085 } 086 087 @Override 088 public int getSizeY() 089 { 090 return getMaxSizeY(); 091 } 092 093 @Override 094 public int getSizeZ() 095 { 096 return sequence.getSizeZ(); 097 } 098 099 @Override 100 public int getSizeT() 101 { 102 return sequence.getSizeT(); 103 } 104 105 @Override 106 public int getSizeC() 107 { 108 return sequence.getSizeC(); 109 } 110 111 @Override 112 public BufferedImage getImage(int t, int z) 113 { 114 return sequence.getImage(t, z); 115 } 116 117 @Override 118 public BufferedImage getImage(int t, int z, int c) 119 { 120 return sequence.getImage(t, z, c); 121 } 122 } 123 124 protected class ResultModel extends AbstractSequenceModel 125 { 126 public ResultModel() 127 { 128 super(); 129 } 130 131 @Override 132 public int getSizeX() 133 { 134 return getMaxSizeX(); 135 } 136 137 @Override 138 public int getSizeY() 139 { 140 return getMaxSizeY(); 141 } 142 143 @Override 144 public int getSizeZ() 145 { 146 return sequence.getSizeZ(); 147 } 148 149 @Override 150 public int getSizeT() 151 { 152 return sequence.getSizeT(); 153 } 154 155 @Override 156 public int getSizeC() 157 { 158 return sequence.getSizeC(); 159 } 160 161 @Override 162 public BufferedImage getImage(int t, int z) 163 { 164 try 165 { 166 return IcyBufferedImageUtil.scale(sequence.getImage(t, z), getNewWidth(), getNewHeight(), 167 getResizeContent(), getXAlign(), getYAlign(), getFilterType()); 168 } 169 catch (OutOfMemoryError e) 170 { 171 return null; 172 } 173 } 174 175 @Override 176 public BufferedImage getImage(int t, int z, int c) 177 { 178 return ((IcyBufferedImage) getImage(t, z)).getImage(c); 179 } 180 } 181 182 final Sequence sequence; 183 184 // GUI 185 protected JCheckBox keepRatioCheckBox; 186 protected JSpinner heightSpinner; 187 protected JSpinner widthSpinner; 188 protected SequencePreviewPanel originalPreview; 189 protected SequencePreviewPanel resultPreview; 190 protected JPanel infoPanel; 191 protected IcyTextField widthField; 192 protected IcyTextField heightField; 193 protected IcyTextField sizeField; 194 protected JComboBox sizeUnitComboBox; 195 protected JLabel accolLeftLabel; 196 protected JPanel panel; 197 protected Component horizontalGlue; 198 protected Component horizontalGlue_1; 199 protected JPanel settingPanel; 200 201 /** 202 * Create the panel. 203 */ 204 public SequenceBaseResizePanel(Sequence sequence) 205 { 206 super(); 207 208 this.sequence = sequence; 209 210 initialize(); 211 212 setNewWidth(sequence.getSizeX()); 213 setNewHeight(sequence.getSizeY()); 214 215 accolLeftLabel.setIcon(ResourceUtil.getImageIcon(ResourceUtil.IMAGE_ACCOLADE_LEFT)); 216 accolLeftLabel.setText(null); 217 218 originalPreview.setFitToView(false); 219 resultPreview.setFitToView(false); 220 originalPreview.setModel(new OriginalModel()); 221 resultPreview.setModel(new ResultModel()); 222 223 updatePreview(); 224 225 final ChangeListener spinnerChangeListener = new ChangeListener() 226 { 227 @Override 228 public void stateChanged(ChangeEvent e) 229 { 230 // maintain ratio 231 if (keepRatioCheckBox.isSelected()) 232 { 233 final Sequence seq = SequenceBaseResizePanel.this.sequence; 234 235 if (e.getSource() == widthSpinner) 236 { 237 // adjust height 238 final double ratio = (double) getNewWidth() / (double) seq.getWidth(); 239 setNewHeight((int) Math.round(seq.getHeight() * ratio)); 240 } 241 else 242 { 243 // adjust width 244 final double ratio = (double) getNewHeight() / (double) seq.getHeight(); 245 setNewWidth((int) Math.round(seq.getWidth() * ratio)); 246 } 247 } 248 249 updatePreview(); 250 } 251 }; 252 heightSpinner.addChangeListener(spinnerChangeListener); 253 widthSpinner.addChangeListener(spinnerChangeListener); 254 255 sizeUnitComboBox.addActionListener(new ActionListener() 256 { 257 @Override 258 public void actionPerformed(ActionEvent e) 259 { 260 // convert width and height in new unit 261 final int w = Integer.parseInt(widthField.getText()); 262 final int h = Integer.parseInt(heightField.getText()); 263 264 setNewWidth(w); 265 setNewHeight(h); 266 } 267 }); 268 } 269 270 protected void initialize() 271 { 272 setLayout(new BorderLayout(0, 0)); 273 274 panel = new JPanel(); 275 panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); 276 add(panel, BorderLayout.NORTH); 277 278 infoPanel = new JPanel(); 279 panel.add(infoPanel); 280 infoPanel.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Size in pixel", 281 TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0))); 282 GridBagLayout gbl_infoPanel = new GridBagLayout(); 283 gbl_infoPanel.columnWidths = new int[] {20, 100, 20, 100, 20, 100, 20, 0}; 284 gbl_infoPanel.rowHeights = new int[] {0, 0, 0}; 285 gbl_infoPanel.columnWeights = new double[] {1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, Double.MIN_VALUE}; 286 gbl_infoPanel.rowWeights = new double[] {0.0, 0.0, Double.MIN_VALUE}; 287 infoPanel.setLayout(gbl_infoPanel); 288 289 final JLabel lblOriginalWidth = new JLabel("Width"); 290 GridBagConstraints gbc_lblOriginalWidth = new GridBagConstraints(); 291 gbc_lblOriginalWidth.fill = GridBagConstraints.BOTH; 292 gbc_lblOriginalWidth.insets = new Insets(0, 0, 5, 5); 293 gbc_lblOriginalWidth.gridx = 1; 294 gbc_lblOriginalWidth.gridy = 0; 295 infoPanel.add(lblOriginalWidth, gbc_lblOriginalWidth); 296 lblOriginalWidth.setToolTipText(""); 297 298 final JLabel lblNewLabel_3 = new JLabel("Height"); 299 GridBagConstraints gbc_lblNewLabel_3 = new GridBagConstraints(); 300 gbc_lblNewLabel_3.fill = GridBagConstraints.BOTH; 301 gbc_lblNewLabel_3.insets = new Insets(0, 0, 5, 5); 302 gbc_lblNewLabel_3.gridx = 3; 303 gbc_lblNewLabel_3.gridy = 0; 304 infoPanel.add(lblNewLabel_3, gbc_lblNewLabel_3); 305 lblNewLabel_3.setToolTipText(""); 306 307 final JLabel lblNewLabel_2 = new JLabel("Memory size"); 308 GridBagConstraints gbc_lblNewLabel_2 = new GridBagConstraints(); 309 gbc_lblNewLabel_2.fill = GridBagConstraints.BOTH; 310 gbc_lblNewLabel_2.insets = new Insets(0, 0, 5, 5); 311 gbc_lblNewLabel_2.gridx = 5; 312 gbc_lblNewLabel_2.gridy = 0; 313 infoPanel.add(lblNewLabel_2, gbc_lblNewLabel_2); 314 lblNewLabel_2.setToolTipText(""); 315 316 widthField = new IcyTextField(); 317 widthField.setToolTipText("Width in pixel"); 318 widthField.setText("0000"); 319 widthField.setEditable(false); 320 GridBagConstraints gbc_widthField = new GridBagConstraints(); 321 gbc_widthField.fill = GridBagConstraints.BOTH; 322 gbc_widthField.insets = new Insets(0, 0, 0, 5); 323 gbc_widthField.gridx = 1; 324 gbc_widthField.gridy = 1; 325 infoPanel.add(widthField, gbc_widthField); 326 widthField.setColumns(5); 327 328 heightField = new IcyTextField(); 329 heightField.setToolTipText("Height in pixel"); 330 heightField.setText("0"); 331 heightField.setEditable(false); 332 GridBagConstraints gbc_heightField = new GridBagConstraints(); 333 gbc_heightField.fill = GridBagConstraints.BOTH; 334 gbc_heightField.insets = new Insets(0, 0, 0, 5); 335 gbc_heightField.gridx = 3; 336 gbc_heightField.gridy = 1; 337 infoPanel.add(heightField, gbc_heightField); 338 heightField.setColumns(5); 339 340 sizeField = new IcyTextField(); 341 sizeField.setToolTipText("Memory size"); 342 sizeField.setText("0.0B"); 343 sizeField.setEditable(false); 344 GridBagConstraints gbc_sizeField = new GridBagConstraints(); 345 gbc_sizeField.insets = new Insets(0, 0, 0, 5); 346 gbc_sizeField.fill = GridBagConstraints.BOTH; 347 gbc_sizeField.gridx = 5; 348 gbc_sizeField.gridy = 1; 349 infoPanel.add(sizeField, gbc_sizeField); 350 sizeField.setColumns(5); 351 352 settingPanel = new JPanel(); 353 panel.add(settingPanel); 354 settingPanel.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Setting", 355 TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0))); 356 GridBagLayout gbl_settingPanel = new GridBagLayout(); 357 gbl_settingPanel.columnWidths = new int[] {20, 100, 20, 100, 20, 100, 20, 0}; 358 gbl_settingPanel.rowHeights = new int[] {0, 0, 0, 0, 10, 0, 0, 0}; 359 gbl_settingPanel.columnWeights = new double[] {1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, Double.MIN_VALUE}; 360 gbl_settingPanel.rowWeights = new double[] {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, Double.MIN_VALUE}; 361 settingPanel.setLayout(gbl_settingPanel); 362 363 final JLabel lblWidth = new JLabel("Width"); 364 GridBagConstraints gbc_lblWidth = new GridBagConstraints(); 365 gbc_lblWidth.fill = GridBagConstraints.BOTH; 366 gbc_lblWidth.insets = new Insets(0, 0, 5, 5); 367 gbc_lblWidth.gridx = 1; 368 gbc_lblWidth.gridy = 0; 369 settingPanel.add(lblWidth, gbc_lblWidth); 370 371 widthSpinner = new JSpinner(); 372 widthSpinner.setModel(new SpinnerNumberModel(new Integer(1), new Integer(1), null, new Integer(1))); 373 widthSpinner.setToolTipText("New width to set"); 374 GridBagConstraints gbc_widthSpinner = new GridBagConstraints(); 375 gbc_widthSpinner.fill = GridBagConstraints.BOTH; 376 gbc_widthSpinner.insets = new Insets(0, 0, 5, 5); 377 gbc_widthSpinner.gridx = 1; 378 gbc_widthSpinner.gridy = 1; 379 settingPanel.add(widthSpinner, gbc_widthSpinner); 380 381 final JLabel lblNewLabel = new JLabel("Height"); 382 GridBagConstraints gbc_lblNewLabel = new GridBagConstraints(); 383 gbc_lblNewLabel.fill = GridBagConstraints.BOTH; 384 gbc_lblNewLabel.insets = new Insets(0, 0, 5, 5); 385 gbc_lblNewLabel.gridx = 1; 386 gbc_lblNewLabel.gridy = 2; 387 settingPanel.add(lblNewLabel, gbc_lblNewLabel); 388 389 accolLeftLabel = new JLabel(""); 390 accolLeftLabel.setHorizontalAlignment(SwingConstants.CENTER); 391 GridBagConstraints gbc_accolLeftLabel = new GridBagConstraints(); 392 gbc_accolLeftLabel.fill = GridBagConstraints.BOTH; 393 gbc_accolLeftLabel.gridheight = 3; 394 gbc_accolLeftLabel.insets = new Insets(0, 0, 5, 5); 395 gbc_accolLeftLabel.gridx = 2; 396 gbc_accolLeftLabel.gridy = 1; 397 settingPanel.add(accolLeftLabel, gbc_accolLeftLabel); 398 lblNewLabel.setLabelFor(heightSpinner); 399 400 sizeUnitComboBox = new JComboBox(); 401 sizeUnitComboBox.setMaximumRowCount(3); 402 sizeUnitComboBox.setToolTipText("Width / Height unit"); 403 sizeUnitComboBox.setModel(new DefaultComboBoxModel(new String[] {"pixel", "%", "\u00B5m"})); 404 sizeUnitComboBox.setSelectedIndex(0); 405 GridBagConstraints gbc_sizeUnitComboBox = new GridBagConstraints(); 406 gbc_sizeUnitComboBox.fill = GridBagConstraints.HORIZONTAL; 407 gbc_sizeUnitComboBox.gridheight = 3; 408 gbc_sizeUnitComboBox.insets = new Insets(0, 0, 5, 5); 409 gbc_sizeUnitComboBox.gridx = 3; 410 gbc_sizeUnitComboBox.gridy = 1; 411 settingPanel.add(sizeUnitComboBox, gbc_sizeUnitComboBox); 412 413 heightSpinner = new JSpinner(); 414 heightSpinner.setModel(new SpinnerNumberModel(new Integer(1), new Integer(1), null, new Integer(1))); 415 heightSpinner.setToolTipText("New height to set"); 416 GridBagConstraints gbc_heightSpinner = new GridBagConstraints(); 417 gbc_heightSpinner.fill = GridBagConstraints.BOTH; 418 gbc_heightSpinner.insets = new Insets(0, 0, 5, 5); 419 gbc_heightSpinner.gridx = 1; 420 gbc_heightSpinner.gridy = 3; 421 settingPanel.add(heightSpinner, gbc_heightSpinner); 422 423 keepRatioCheckBox = new JCheckBox("Keep ratio"); 424 keepRatioCheckBox.setVerticalAlignment(SwingConstants.TOP); 425 keepRatioCheckBox.setToolTipText("Keep original aspect ratio"); 426 keepRatioCheckBox.setSelected(true); 427 GridBagConstraints gbc_keepRatioCheckBox = new GridBagConstraints(); 428 gbc_keepRatioCheckBox.gridwidth = 3; 429 gbc_keepRatioCheckBox.fill = GridBagConstraints.BOTH; 430 gbc_keepRatioCheckBox.insets = new Insets(0, 0, 5, 5); 431 gbc_keepRatioCheckBox.gridx = 1; 432 gbc_keepRatioCheckBox.gridy = 5; 433 settingPanel.add(keepRatioCheckBox, gbc_keepRatioCheckBox); 434 435 horizontalGlue = Box.createHorizontalGlue(); 436 GridBagConstraints gbc_horizontalGlue = new GridBagConstraints(); 437 gbc_horizontalGlue.fill = GridBagConstraints.HORIZONTAL; 438 gbc_horizontalGlue.insets = new Insets(0, 0, 0, 5); 439 gbc_horizontalGlue.gridx = 5; 440 gbc_horizontalGlue.gridy = 6; 441 settingPanel.add(horizontalGlue, gbc_horizontalGlue); 442 443 horizontalGlue_1 = Box.createHorizontalGlue(); 444 GridBagConstraints gbc_horizontalGlue_1 = new GridBagConstraints(); 445 gbc_horizontalGlue_1.fill = GridBagConstraints.HORIZONTAL; 446 gbc_horizontalGlue_1.gridx = 6; 447 gbc_horizontalGlue_1.gridy = 6; 448 settingPanel.add(horizontalGlue_1, gbc_horizontalGlue_1); 449 450 final JPanel previewPanel = new JPanel(); 451 previewPanel.setBorder(new TitledBorder(null, "Preview", TitledBorder.LEADING, TitledBorder.TOP, null, null)); 452 add(previewPanel, BorderLayout.CENTER); 453 previewPanel.setLayout(new BoxLayout(previewPanel, BoxLayout.LINE_AXIS)); 454 455 originalPreview = new SequencePreviewPanel("Original"); 456 previewPanel.add(originalPreview); 457 458 resultPreview = new SequencePreviewPanel("Result"); 459 previewPanel.add(resultPreview); 460 } 461 462 void updatePreview() 463 { 464 final int w = getNewWidth(); 465 final int h = getNewHeight(); 466 467 widthField.setText(Integer.toString(w)); 468 heightField.setText(Integer.toString(h)); 469 sizeField.setText(UnitUtil.getBytesString(w * h * sequence.getSizeC() * sequence.getSizeZ() 470 * sequence.getSizeT() * sequence.getDataType_().getSize())); 471 472 originalPreview.imageChanged(); 473 resultPreview.imageChanged(); 474 } 475 476 public Sequence getSequence() 477 { 478 return sequence; 479 } 480 481 /** 482 * pixel resolution X (micron / pixel) 483 */ 484 public double getPixelSizeX() 485 { 486 if (sequence != null) 487 return sequence.getPixelSizeX(); 488 489 return 1d; 490 } 491 492 /** 493 * pixel resolution X (micron / pixel) 494 */ 495 public double getPixelSizeY() 496 { 497 if (sequence != null) 498 return sequence.getPixelSizeY(); 499 500 return 1d; 501 } 502 503 public SizeUnit getSizeUnit() 504 { 505 switch (sizeUnitComboBox.getSelectedIndex()) 506 { 507 default: 508 case 0: 509 return SizeUnit.PIXEL; 510 case 1: 511 return SizeUnit.PERCENT; 512 case 2: 513 return SizeUnit.MICRON; 514 } 515 } 516 517 public int unitToPixel(double value, int originPixel, SizeUnit unit, double micronPerPixel) 518 { 519 switch (unit) 520 { 521 default: 522 case PIXEL: 523 return (int) Math.round(value); 524 case PERCENT: 525 return (int) Math.round((originPixel * value) / 100d); 526 case MICRON: 527 return (int) Math.round(value / micronPerPixel); 528 } 529 } 530 531 public int unitToPixelX(double value, int originPixel, SizeUnit unit) 532 { 533 return unitToPixel(value, originPixel, unit, getPixelSizeX()); 534 } 535 536 public int unitToPixelY(double value, int originPixel, SizeUnit unit) 537 { 538 return unitToPixel(value, originPixel, unit, getPixelSizeY()); 539 } 540 541 public double pixelToUnit(int value, int originPixel, SizeUnit unit, double micronPerPixel) 542 { 543 switch (unit) 544 { 545 default: 546 case PIXEL: 547 return value; 548 case PERCENT: 549 return (int) Math.round((value * 100d) / originPixel); 550 case MICRON: 551 return (int) (value * micronPerPixel); 552 } 553 } 554 555 public double pixelXToUnit(int value, int originPixel, SizeUnit unit) 556 { 557 return pixelToUnit(value, originPixel, unit, getPixelSizeX()); 558 } 559 560 public double pixelYToUnit(int value, int originPixel, SizeUnit unit) 561 { 562 return pixelToUnit(value, originPixel, unit, getPixelSizeY()); 563 } 564 565 public double getSpinnerSizeValue(JSpinner spinner) 566 { 567 switch (getSizeUnit()) 568 { 569 default: 570 case PIXEL: 571 return ((Integer) spinner.getValue()).intValue(); 572 573 case PERCENT: 574 case MICRON: 575 return ((Double) spinner.getValue()).doubleValue(); 576 } 577 } 578 579 public int getNewWidth() 580 { 581 final int result = unitToPixelX(getSpinnerSizeValue(widthSpinner), sequence.getSizeX(), getSizeUnit()); 582 583 return Math.min(65535, Math.max(1, result)); 584 } 585 586 public int getNewHeight() 587 { 588 final int result = unitToPixelY(getSpinnerSizeValue(heightSpinner), sequence.getSizeY(), getSizeUnit()); 589 590 return Math.min(Math.max(1, result), 65535); 591 } 592 593 void setSpinnerSizeValue(JSpinner spinner, double value) 594 { 595 switch (getSizeUnit()) 596 { 597 default: 598 case PIXEL: 599 spinner.setModel(new SpinnerNumberModel((int) value, 0, 65535, 1)); 600 // we don't want the model to affect 601 ((DefaultEditor) spinner.getEditor()).getTextField().setColumns(1); 602 break; 603 604 case PERCENT: 605 spinner.setModel(new SpinnerNumberModel(value, 0d, Double.MAX_VALUE, 1d)); 606 // we don't want the model to affect 607 ((DefaultEditor) spinner.getEditor()).getTextField().setColumns(1); 608 break; 609 610 case MICRON: 611 spinner.setModel(new SpinnerNumberModel(value, 0d, Double.MAX_VALUE, 0.01d)); 612 // we don't want the model to affect 613 ((DefaultEditor) spinner.getEditor()).getTextField().setColumns(1); 614 break; 615 } 616 } 617 618 void setNewWidth(int value) 619 { 620 setSpinnerSizeValue(widthSpinner, pixelXToUnit(value, sequence.getSizeX(), getSizeUnit())); 621 } 622 623 void setNewHeight(int value) 624 { 625 setSpinnerSizeValue(heightSpinner, pixelYToUnit(value, sequence.getSizeY(), getSizeUnit())); 626 } 627 628 public int getMaxSizeX() 629 { 630 return Math.max(getNewWidth(), sequence.getSizeX()); 631 } 632 633 public int getMaxSizeY() 634 { 635 return Math.max(getNewHeight(), sequence.getSizeY()); 636 } 637 638 public abstract FilterType getFilterType(); 639 640 public abstract boolean getResizeContent(); 641 642 public abstract int getXAlign(); 643 644 public abstract int getYAlign(); 645}