001/***************************************************************************** 002 * Copyright by The HDF Group. * 003 * Copyright by the Board of Trustees of the University of Illinois. * 004 * All rights reserved. * 005 * * 006 * This file is part of the HDF Java Products distribution. * 007 * The full copyright notice, including terms governing use, modification, * 008 * and redistribution, is contained in the file COPYING. * 009 * COPYING can be found at the root of the source code distribution tree. * 010 * If you do not have access to this file, you may request a copy from * 011 * help@hdfgroup.org. * 012 ****************************************************************************/ 013 014package hdf.view; 015 016import java.awt.BorderLayout; 017import java.awt.Color; 018import java.awt.Cursor; 019import java.awt.Dimension; 020import java.awt.Font; 021import java.awt.Graphics; 022import java.awt.Graphics2D; 023import java.awt.GridLayout; 024import java.awt.Image; 025import java.awt.Insets; 026import java.awt.Point; 027import java.awt.Rectangle; 028import java.awt.RenderingHints; 029import java.awt.Toolkit; 030import java.awt.event.ActionEvent; 031import java.awt.event.ActionListener; 032import java.awt.event.InputEvent; 033import java.awt.event.KeyEvent; 034import java.awt.event.MouseEvent; 035import java.awt.event.MouseListener; 036import java.awt.event.MouseMotionListener; 037import java.awt.event.MouseWheelEvent; 038import java.awt.event.MouseWheelListener; 039import java.awt.image.BufferedImage; 040import java.awt.image.ColorModel; 041import java.awt.image.DataBufferInt; 042import java.awt.image.FilteredImageSource; 043import java.awt.image.ImageFilter; 044import java.awt.image.ImageProducer; 045import java.awt.image.PixelGrabber; 046import java.awt.image.RGBImageFilter; 047import java.beans.PropertyChangeEvent; 048import java.beans.PropertyChangeListener; 049import java.io.BufferedWriter; 050import java.io.File; 051import java.io.FileWriter; 052import java.io.PrintWriter; 053import java.io.RandomAccessFile; 054import java.lang.reflect.Array; 055import java.text.DecimalFormat; 056import java.util.ArrayList; 057import java.util.BitSet; 058import java.util.Enumeration; 059import java.util.HashMap; 060import java.util.Hashtable; 061import java.util.List; 062import java.util.Vector; 063 064import javax.swing.BorderFactory; 065import javax.swing.JButton; 066import javax.swing.JCheckBoxMenuItem; 067import javax.swing.JComponent; 068import javax.swing.JDialog; 069import javax.swing.JFileChooser; 070import javax.swing.JFormattedTextField; 071import javax.swing.JFrame; 072import javax.swing.JInternalFrame; 073import javax.swing.JLabel; 074import javax.swing.JMenu; 075import javax.swing.JMenuBar; 076import javax.swing.JMenuItem; 077import javax.swing.JOptionPane; 078import javax.swing.JPanel; 079import javax.swing.JScrollBar; 080import javax.swing.JScrollPane; 081import javax.swing.JSlider; 082import javax.swing.JTextField; 083import javax.swing.SwingConstants; 084import javax.swing.border.Border; 085import javax.swing.border.TitledBorder; 086import javax.swing.event.ChangeEvent; 087import javax.swing.event.ChangeListener; 088import javax.swing.filechooser.FileNameExtensionFilter; 089import javax.swing.text.NumberFormatter; 090import javax.swing.tree.DefaultMutableTreeNode; 091import javax.swing.tree.TreeNode; 092 093import hdf.object.Datatype; 094import hdf.object.Group; 095import hdf.object.HObject; 096import hdf.object.ScalarDS; 097import hdf.view.ViewProperties.BITMASK_OP; 098 099/** 100 * ImageView displays an HDF dataset as an image. 101 * <p> 102 * A scalar dataset in HDF can be displayed in image or table. By default, an 103 * HDF4 GR image and HDF5 image is displayed as an image. Other scalar datasets 104 * are display in a two-dimensional table. 105 * <p> 106 * Users can also choose to display a scalar dataset as image. Currently verion 107 * of the ImageView only supports 8-bit raster image with indexed RGB color 108 * model of 256 colors or 24-bit true color raster image. Data of other type 109 * will be converted to 8-bit integer. The simple linear conversion is used for 110 * this purpose: 111 * 112 * <pre> 113 * y = f * (x - min), 114 * where y = the value of 8-bit integer, 115 * x = the value of original data, 116 * f = 255/(max-min), conversion factor, 117 * max = the maximum of the original data, 118 * min = the minimum of the original data. 119 * </pre> 120 * <p> 121 * A default color table is provided for images without palette attached to it. 122 * Current choice of default palettes include Gray, Rainbow, Nature and Wave. 123 * For more infomation on palette, 124 * read <a href="http://hdfgroup.org/HDF5/doc/ADGuide/ImageSpec.html"> HDF5 Image and 125 * Palette Specification </a> 126 * 127 * @author Peter X. Cao 128 * @version 2.4 9/6/2007 129 */ 130public class DefaultImageView extends JInternalFrame implements ImageView, ActionListener { 131 private static final long serialVersionUID = -6534336542813587242L; 132 133 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultImageView.class); 134 135 /** Horizontal direction to flip an image. */ 136 public static final int FLIP_HORIZONTAL = 0; 137 138 /** Vertical direction to flip an image. */ 139 public static final int FLIP_VERTICAL = 1; 140 141 /** ROTATE IMAGE 90 DEGREE CLOCKWISE. */ 142 public static final int ROTATE_CW_90 = 10; 143 144 /** ROTATE IMAGE COUNTER CLOCKWISE 90 DEGREE. */ 145 public static final int ROTATE_CCW_90 = 11; 146 147 /** 148 * The main HDFView. 149 */ 150 private final ViewManager viewer; 151 152 private Toolkit toolkit; 153 154 /** 155 * The Scalar Dataset. 156 */ 157 private ScalarDS dataset; 158 159 /** 160 * The JComponent containing the image. 161 */ 162 private ImageComponent imageComponent; 163 164 /** 165 * The image contained in the ImageView. 166 */ 167 private Image image; 168 169 /** 170 * The zooming factor of this image. 171 */ 172 private float zoomFactor; 173 174 /** 175 * The byte data array of the image. 176 */ 177 private byte[] imageByteData; 178 179 /** 180 * The color table of the image. 181 */ 182 private byte[][] imagePalette; 183 184 /** 185 * The title of this imageview. 186 */ 187 private String frameTitle; 188 189 /** TextField to show the image value. */ 190 private JTextField valueField; 191 192 /** Flag to indicate if the image is a true color image */ 193 private boolean isTrueColor; 194 195 /** Flag to indicate if the image is a 3D */ 196 private boolean is3D; 197 198 /** Flag to indicate if the image is plane interleaved */ 199 private boolean isPlaneInterlace; 200 201 private boolean isHorizontalFlipped = false; 202 203 private boolean isVerticalFlipped = false; 204 205 private int rotateCount = 0; 206 207 /** the number type of the image data */ 208 private char NT; 209 210 /** the raw data of the image */ 211 private Object data; 212 213 /** flag to indicate if the original data type is unsigned integer */ 214 private boolean isUnsigned; 215 216 private boolean isUnsignedConverted = false; 217 218 private double[] dataRange; 219 private final double[] originalRange = {0,0}; 220 221 private PaletteComponent paletteComponent; 222 223 private int animationSpeed = 2; 224 225 private List rotateRelatedItems; 226 227 private JScrollPane imageScroller; 228 229 private JTextField frameField; 230 231 private long curFrame = 0, maxFrame = 1; 232 233 private BufferedImage bufferedImage; 234 235 private ContrastSlider contrastSlider; 236 237 private int indexBase = 0; 238 private int[] dataDist = null; 239 240 /** 241 * equates to brightness 242 */ 243 private boolean doAutoGainContrast = false; 244 private double[] gainBias, gainBias_current; 245 246 /** 247 * int array to hold unsigned short or signed int data from applying the 248 * autogain 249 */ 250 private Object autoGainData; 251 252 private BitSet bitmask; 253 private boolean convertByteData = false; 254 private BITMASK_OP bitmaskOP = BITMASK_OP.EXTRACT; 255 256 private enum Origin { 257 UPPER_LEFT, LOWER_LEFT, UPPER_RIGHT, LOWER_RIGHT 258 } 259 260 private Origin imageOrigin = null; 261 262 private List<Integer> invalidValueIndex; 263 264 /** 265 * Constructs an ImageView. 266 * 267 * @param theView 268 * the main HDFView. 269 */ 270 public DefaultImageView(ViewManager theView) { 271 this(theView, null); 272 } 273 274 /** 275 * Constructs an ImageView. 276 * 277 * @param theView 278 * the main HDFView. 279 * @param map 280 * the properties on how to show the data. The map is used to 281 * allow applications to pass properties on how to display the 282 * data, such as, transposing data, showing data as character, 283 * applying bitmask, and etc. Predefined keys are listed at 284 * ViewProperties.DATA_VIEW_KEY. 285 */ 286 public DefaultImageView(ViewManager theView, HashMap map) { 287 super(); 288 289 setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE); 290 setFrameIcon(ViewProperties.getImageIcon()); 291 292 viewer = theView; 293 zoomFactor = 1.0f; 294 imageByteData = null; 295 imagePalette = null; 296 paletteComponent = null; 297 isTrueColor = false; 298 is3D = false; 299 isPlaneInterlace = false; 300 isUnsigned = false; 301 data = null; 302 NT = 0; 303 rotateRelatedItems = new Vector(10); 304 imageScroller = null; 305 gainBias = null; 306 gainBias_current = null; 307 autoGainData = null; 308 contrastSlider = null; 309 bitmask = null; 310 invalidValueIndex = new ArrayList<Integer>(); 311 312 toolkit = Toolkit.getDefaultToolkit(); 313 314 String origStr = ViewProperties.getImageOrigin(); 315 if (ViewProperties.ORIGIN_LL.equalsIgnoreCase(origStr)) 316 imageOrigin = Origin.LOWER_LEFT; 317 else if (ViewProperties.ORIGIN_UR.equalsIgnoreCase(origStr)) 318 imageOrigin = Origin.UPPER_RIGHT; 319 else if (ViewProperties.ORIGIN_LR.equalsIgnoreCase(origStr)) 320 imageOrigin = Origin.LOWER_RIGHT; 321 322 if (ViewProperties.isIndexBase1()) 323 indexBase = 1; 324 325 HObject hobject = null; 326 327 if (map != null) { 328 hobject = (HObject) map.get(ViewProperties.DATA_VIEW_KEY.OBJECT); 329 bitmask = (BitSet) map.get(ViewProperties.DATA_VIEW_KEY.BITMASK); 330 bitmaskOP = (BITMASK_OP)map.get(ViewProperties.DATA_VIEW_KEY.BITMASKOP); 331 332 Boolean b = (Boolean) map.get(ViewProperties.DATA_VIEW_KEY.CONVERTBYTE); 333 if (b != null) 334 convertByteData = b.booleanValue(); 335 336 b = (Boolean) map.get(ViewProperties.DATA_VIEW_KEY.INDEXBASE1); 337 if (b != null) { 338 if (b.booleanValue()) 339 indexBase = 1; 340 else 341 indexBase = 0; 342 } 343 } 344 345 if (hobject == null) 346 hobject = (HObject) theView.getTreeView().getCurrentObject(); 347 348 if ((hobject == null) || !(hobject instanceof ScalarDS)) { 349 viewer.showStatus("Display data in image failed for - " + hobject); 350 return; 351 } 352 353 dataset = (ScalarDS) hobject; 354 dataRange = dataset.getImageDataRange(); 355 if (dataRange == null) { 356 dataRange = new double[2]; 357 dataRange[0] = dataRange[1] = 0; 358 if (dataset.getDatatype().getDatatypeSize() == 1 359 && !convertByteData) { 360 dataRange[1] = 255; // byte image data rang = [0, 255] 361 } 362 } 363 else { 364 if (dataRange[0] < dataRange[1]) 365 convertByteData = true; 366 } 367 368 JPanel contentPane = (JPanel) getContentPane(); 369 contentPane.setName("imagecontentpane"); 370 contentPane.setLayout(new BorderLayout()); 371 372 // add the text field to display pixel data 373 contentPane.add(valueField = new JTextField(), BorderLayout.SOUTH); 374 valueField.setName("valuefield"); 375 valueField.setEditable(false); 376 valueField.setVisible(ViewProperties.showImageValues()); 377 378 if (image == null) { 379 getImage(); 380 } 381 382 if (image == null) { 383 viewer.showStatus("Loading image failed - " + dataset.getName()); 384 dataset = null; 385 return; 386 } 387 388 originalRange[0] = dataRange[0]; 389 originalRange[1] = dataRange[1]; 390 391 imageComponent = new ImageComponent(image); 392 JScrollPane scroller = new JScrollPane(imageComponent); 393 scroller.getVerticalScrollBar().setUnitIncrement(50); 394 scroller.getHorizontalScrollBar().setUnitIncrement(50); 395 scroller.setName("imagecontent"); 396 imageScroller = scroller; 397 contentPane.add(scroller, BorderLayout.CENTER); 398 399 // add palette convas to show the palette 400 if (imagePalette != null) { 401 paletteComponent = new PaletteComponent(imagePalette, dataRange); 402 contentPane.add(paletteComponent, BorderLayout.EAST); 403 } 404 405 if (imageOrigin == Origin.LOWER_LEFT) 406 flip(FLIP_VERTICAL); 407 else if (imageOrigin == Origin.UPPER_RIGHT) 408 flip(FLIP_HORIZONTAL); 409 if (imageOrigin == Origin.LOWER_RIGHT) { 410 rotate(ROTATE_CW_90); 411 rotate(ROTATE_CW_90); 412 } 413 414 // set title 415 StringBuffer sb = new StringBuffer(hobject.getName()); 416 sb.append(" at "); 417 sb.append(hobject.getPath()); 418 sb.append(" ["); 419 sb.append(dataset.getFileFormat().getName()); 420 sb.append(" in "); 421 sb.append(dataset.getFileFormat().getParent()); 422 sb.append("]"); 423 424 frameTitle = sb.toString(); 425 setTitle(frameTitle); 426 this.setName(frameTitle); 427 428 // setup subset information 429 int rank = dataset.getRank(); 430 int[] selectedIndex = dataset.getSelectedIndex(); 431 long[] count = dataset.getSelectedDims(); 432 long[] stride = dataset.getStride(); 433 long[] dims = dataset.getDims(); 434 long[] start = dataset.getStartDims(); 435 int n = Math.min(3, rank); 436 437 if (rank > 2) { 438 curFrame = start[selectedIndex[2]]+indexBase; 439 maxFrame = (indexBase == 1) ? dims[selectedIndex[2]] : dims[selectedIndex[2]] - 1; 440 } 441 442 sb.append(" [ dims"); 443 sb.append(selectedIndex[0]); 444 for (int i = 1; i < n; i++) { 445 sb.append("x"); 446 sb.append(selectedIndex[i]); 447 } 448 sb.append(", start"); 449 sb.append(start[selectedIndex[0]]); 450 for (int i = 1; i < n; i++) { 451 sb.append("x"); 452 sb.append(start[selectedIndex[i]]); 453 } 454 sb.append(", count"); 455 sb.append(count[selectedIndex[0]]); 456 for (int i = 1; i < n; i++) { 457 sb.append("x"); 458 sb.append(count[selectedIndex[i]]); 459 } 460 sb.append(", stride"); 461 sb.append(stride[selectedIndex[0]]); 462 for (int i = 1; i < n; i++) { 463 sb.append("x"); 464 sb.append(stride[selectedIndex[i]]); 465 } 466 sb.append(" ] "); 467 468 setJMenuBar(createMenuBar()); 469 viewer.showStatus(sb.toString()); 470 471 int titleJustification = TitledBorder.LEFT; 472 int titlePosition = TitledBorder.TOP; 473 String orgin = ViewProperties.getImageOrigin(); 474 if (orgin.equalsIgnoreCase(ViewProperties.ORIGIN_UR)) 475 titleJustification = TitledBorder.RIGHT; 476 else if (orgin.equalsIgnoreCase(ViewProperties.ORIGIN_LL)) 477 titlePosition = TitledBorder.BOTTOM; 478 else if (orgin.equalsIgnoreCase(ViewProperties.ORIGIN_LR)) { 479 titleJustification = TitledBorder.RIGHT; 480 titlePosition = TitledBorder.BOTTOM; 481 } 482 483 String originTag = "(0,0)"; 484 if (ViewProperties.isIndexBase1()) 485 originTag = "(1,1)"; 486 487 Border border = BorderFactory.createCompoundBorder( 488 BorderFactory.createRaisedBevelBorder(), BorderFactory 489 .createTitledBorder(BorderFactory 490 .createLineBorder(Color.lightGray, 1), 491 originTag, 492 titleJustification, titlePosition, 493 this.getFont(), Color.black)); 494 contentPane.setBorder(border); 495 } 496 497 private JMenuBar createMenuBar() { 498 JMenuBar bar = new JMenuBar(); 499 JButton button; 500 boolean isEditable = !dataset.getFileFormat().isReadOnly(); 501 502 JMenu menu = new JMenu("Image", false); 503 menu.setMnemonic('I'); 504 bar.add(menu); 505 506 JMenu convertImageMenu = new JMenu("Save Image As"); 507 menu.add(convertImageMenu); 508 509 JMenuItem item = new JMenuItem(Tools.FILE_TYPE_JPEG); 510 item.addActionListener(this); 511 item.setActionCommand("Save image as jpeg"); 512 convertImageMenu.add(item); 513 514 /* 515 * ImageIO does not support tiff by default 516 */ 517 518 item = new JMenuItem(Tools.FILE_TYPE_PNG); 519 item.addActionListener(this); 520 item.setActionCommand("Save image as png"); 521 convertImageMenu.add(item); 522 523 item = new JMenuItem(Tools.FILE_TYPE_GIF); 524 item.addActionListener(this); 525 item.setActionCommand("Save image as gif"); 526 convertImageMenu.add(item); 527 528 item = new JMenuItem(Tools.FILE_TYPE_BMP); 529 item.addActionListener(this); 530 item.setActionCommand("Save image as bmp"); 531 convertImageMenu.add(item); 532 533 menu.addSeparator(); 534 535 item = new JMenuItem("Write Selection to Image"); 536 item.addActionListener(this); 537 item.setActionCommand("Write selection to image"); 538 item.setEnabled(isEditable); 539 rotateRelatedItems.add(item); 540 menu.add(item); 541 542 menu.addSeparator(); 543 544 item = new JMenuItem("Change Palette"); 545 item.addActionListener(this); 546 item.setActionCommand("Edit palette"); 547 item.setEnabled(!isTrueColor); 548 menu.add(item); 549 550 item = new JMenuItem("Import Palette"); 551 item.addActionListener(this); 552 item.setActionCommand("Import palette"); 553 item.setEnabled(!isTrueColor); 554 menu.add(item); 555 556 item = new JMenuItem("Export Palette"); 557 item.addActionListener(this); 558 item.setActionCommand("Export palette"); 559 item.setEnabled(!isTrueColor); 560 menu.add(item); 561 562 menu.addSeparator(); 563 564 item = new JMenuItem("Set Value Range"); 565 item.setEnabled(!isTrueColor); 566 item.addActionListener(this); 567 item.setActionCommand("Set data range"); 568 menu.add(item); 569 570 menu.addSeparator(); 571 572 item = new JMenuItem("Show Histogram"); 573 item.addActionListener(this); 574 item.setActionCommand("Show chart"); 575 item.setEnabled(!isTrueColor); 576 rotateRelatedItems.add(item); 577 menu.add(item); 578 579 menu.addSeparator(); 580 581 item = new JMenuItem("Zoom In"); 582 item.addActionListener(this); 583 item.setActionCommand("Zoom in"); 584 menu.add(item); 585 586 item = new JMenuItem("Zoom Out"); 587 item.addActionListener(this); 588 item.setActionCommand("Zoom out"); 589 menu.add(item); 590 591 menu.addSeparator(); 592 593 JMenu imageMenu = new JMenu("Flip"); 594 menu.add(imageMenu); 595 596 item = new JMenuItem("Horizontal"); 597 item.addActionListener(this); 598 item.setActionCommand("Flip horizontal"); 599 imageMenu.add(item); 600 601 item = new JMenuItem("Vertical"); 602 item.addActionListener(this); 603 item.setActionCommand("Flip vertical"); 604 imageMenu.add(item); 605 606 imageMenu = new JMenu("Rotate Image"); 607 menu.add(imageMenu); 608 609 char t = 186; 610 item = new JMenuItem("90" + t + " CW"); 611 item.addActionListener(this); 612 item.setActionCommand("Rotate clockwise"); 613 imageMenu.add(item); 614 615 item = new JMenuItem("90" + t + " CCW"); 616 item.addActionListener(this); 617 item.setActionCommand("Rotate counter clockwise"); 618 imageMenu.add(item); 619 620 menu.addSeparator(); 621 622 item = new JMenuItem("Brightness/Contrast"); 623 item.addActionListener(this); 624 item.setActionCommand("Brightness"); 625 menu.add(item); 626 627 JMenu contourMenu = new JMenu("Contour"); 628 for (int i = 3; i < 10; i=i+2) { 629 item = new JMenuItem(String.valueOf(i)); 630 item.addActionListener(this); 631 item.setActionCommand("Contour " + i); 632 contourMenu.add(item); 633 } 634 menu.add(contourMenu); 635 636 menu.addSeparator(); 637 638 item = new JMenuItem("Show Animation"); 639 item.addActionListener(this); 640 item.setActionCommand("Show animation"); 641 item.setEnabled(is3D); 642 menu.add(item); 643 644 JMenu animationMenu = new JMenu("Animation (frames/second)"); 645 for (int i = 2; i < 12; i = i + 2) { 646 item = new JMenuItem(String.valueOf(i)); 647 item.addActionListener(this); 648 item.setActionCommand("Animation speed " + i); 649 animationMenu.add(item); 650 } 651 animationMenu.setEnabled(is3D); 652 menu.add(animationMenu); 653 menu.addSeparator(); 654 655 JCheckBoxMenuItem imageValueCheckBox = new JCheckBoxMenuItem( 656 "Show Value", false); 657 imageValueCheckBox.addActionListener(this); 658 imageValueCheckBox.setActionCommand("Show image value"); 659 imageValueCheckBox.setSelected(ViewProperties.showImageValues()); 660 rotateRelatedItems.add(imageValueCheckBox); 661 imageValueCheckBox.setName("showvaluebutton"); 662 menu.add(imageValueCheckBox); 663 664 item = new JMenuItem("Show Statistics"); 665 item.addActionListener(this); 666 item.setActionCommand("Show statistics"); 667 menu.add(item); 668 669 menu.addSeparator(); 670 671 item = new JMenuItem("Select All"); 672 item.addActionListener(this); 673 item.setActionCommand("Select all data"); 674 menu.add(item); 675 676 menu.addSeparator(); 677 678 item = new JMenuItem("Close"); 679 item.addActionListener(this); 680 item.setActionCommand("Close"); 681 menu.add(item); 682 683 bar.add(new JLabel(" ")); 684 685 // add icons to the menubar 686 687 Insets margin = new Insets(0, 2, 0, 2); 688 689 // chart button 690 button = new JButton(ViewProperties.getChartIcon()); 691 bar.add(button); 692 button.setToolTipText("Histogram"); 693 button.setMargin(margin); 694 button.addActionListener(this); 695 button.setActionCommand("Show chart"); 696 button.setEnabled(!isTrueColor); 697 698 // palette button 699 button = new JButton(ViewProperties.getPaletteIcon()); 700 bar.add(button); 701 button.setToolTipText("Palette"); 702 button.setMargin(margin); 703 button.addActionListener(this); 704 button.setActionCommand("Edit palette"); 705 button.setEnabled(!isTrueColor); 706 707 // brightness button 708 button = new JButton(ViewProperties.getBrightIcon()); 709 bar.add(button); 710 button.setToolTipText("Brightness"); 711 button.setMargin(margin); 712 button.addActionListener(this); 713 button.setActionCommand("Brightness"); 714 715 // brightness button 716 // button = new JButton(ViewProperties.getAutocontrastIcon()); 717 // bar.add(button); 718 // button.setToolTipText("Calculate AutoGain"); 719 // button.setMargin(margin); 720 // button.addActionListener(this); 721 // button.setActionCommand("Calculate AutoGain"); 722 // button.setEnabled(ViewProperties.isAutoContrast()); 723 724 button = new JButton(ViewProperties.getZoominIcon()); 725 bar.add(button); 726 button.addActionListener(this); 727 button.setMargin(margin); 728 button.setActionCommand("Zoom in"); 729 button.setToolTipText("Zoom In"); 730 button.setName("zoomin"); 731 732 // zoom out button 733 button = new JButton(ViewProperties.getZoomoutIcon()); 734 bar.add(button); 735 button.setToolTipText("Zoom Out"); 736 button.setMargin(margin); 737 button.addActionListener(this); 738 button.setActionCommand("Zoom out"); 739 button.setName("zoomout"); 740 741 if (is3D) { 742 bar.add(new JLabel(" ")); 743 744 // first button 745 button = new JButton(ViewProperties.getFirstIcon()); 746 bar.add(button); 747 button.setToolTipText("First"); 748 button.setMargin(margin); 749 button.addActionListener(this); 750 button.setActionCommand("First page"); 751 button.setName("firstframebutton"); 752 753 // previous button 754 button = new JButton(ViewProperties.getPreviousIcon()); 755 bar.add(button); 756 button.setToolTipText("Previous"); 757 button.setMargin(margin); 758 button.addActionListener(this); 759 button.setActionCommand("Previous page"); 760 button.setName("prevframebutton"); 761 762 frameField = new JTextField(String.valueOf(curFrame)); 763 frameField.setMaximumSize(new Dimension(50, 30)); 764 bar.add(frameField); 765 frameField.setMargin(margin); 766 frameField.addActionListener(this); 767 frameField.setActionCommand("Go to frame"); 768 frameField.setName("enterFrameField"); 769 770 JLabel tmpField = new JLabel(String.valueOf(maxFrame), 771 SwingConstants.CENTER); 772 tmpField.setMaximumSize(new Dimension(50, 30)); 773 bar.add(tmpField); 774 775 // next button 776 button = new JButton(ViewProperties.getNextIcon()); 777 bar.add(button); 778 button.setToolTipText("Next"); 779 button.setMargin(margin); 780 button.addActionListener(this); 781 button.setActionCommand("Next page"); 782 button.setName("nextframebutton"); 783 784 // last button 785 button = new JButton(ViewProperties.getLastIcon()); 786 bar.add(button); 787 button.setToolTipText("Last"); 788 button.setMargin(margin); 789 button.addActionListener(this); 790 button.setActionCommand("Last page"); 791 button.setName("lastframebutton"); 792 793 button = new JButton(ViewProperties.getAnimationIcon()); 794 bar.add(button); 795 button.setToolTipText("Animation"); 796 button.setMargin(margin); 797 button.addActionListener(this); 798 button.setActionCommand("Show animation"); 799 800 } 801 802 return bar; 803 } 804 805 // Implementing DataObserver. 806 private void previousPage() { 807 int rank = dataset.getRank(); 808 809 if (rank < 3) { 810 return; 811 } 812 813 int[] selectedIndex = dataset.getSelectedIndex(); 814 long[] selectedDims = dataset.getSelectedDims(); 815 816 if (selectedDims[selectedIndex[2]] > 1) { 817 return; // it is a true color image with three color components 818 } 819 820 long[] start = dataset.getStartDims(); 821 long idx = start[selectedIndex[2]]; 822 if (idx == 0) { 823 return; // current page is the first page 824 } 825 826 gotoPage(start[selectedIndex[2]] - 1); 827 } 828 829 // Implementing DataObserver. 830 private void nextPage() { 831 int rank = dataset.getRank(); 832 833 if (rank < 3) { 834 return; 835 } 836 837 int[] selectedIndex = dataset.getSelectedIndex(); 838 long[] selectedDims = dataset.getSelectedDims(); 839 840 if (selectedDims[selectedIndex[2]] > 1) { 841 return; // it is a true color image with three color components 842 } 843 844 long[] start = dataset.getStartDims(); 845 long[] dims = dataset.getDims(); 846 long idx = start[selectedIndex[2]]; 847 if (idx == dims[selectedIndex[2]] - 1) { 848 return; // current page is the last page 849 } 850 851 gotoPage(start[selectedIndex[2]] + 1); 852 } 853 854 // Implementing DataObserver. 855 private void firstPage() { 856 int rank = dataset.getRank(); 857 858 if (rank < 3) { 859 return; 860 } 861 862 int[] selectedIndex = dataset.getSelectedIndex(); 863 long[] selectedDims = dataset.getSelectedDims(); 864 865 if (selectedDims[selectedIndex[2]] > 1) { 866 return; // it is a true color image with three color components 867 } 868 869 long[] start = dataset.getStartDims(); 870 long idx = start[selectedIndex[2]]; 871 if (idx == 0) { 872 return; // current page is the first page 873 } 874 875 gotoPage(0); 876 } 877 878 // Implementing DataObserver. 879 private void lastPage() { 880 int rank = dataset.getRank(); 881 882 if (rank < 3) { 883 return; 884 } 885 886 int[] selectedIndex = dataset.getSelectedIndex(); 887 long[] selectedDims = dataset.getSelectedDims(); 888 889 if (selectedDims[selectedIndex[2]] > 1) { 890 return; // it is a true color image with three color components 891 } 892 893 long[] start = dataset.getStartDims(); 894 long[] dims = dataset.getDims(); 895 long idx = start[selectedIndex[2]]; 896 if (idx == dims[selectedIndex[2]] - 1) { 897 return; // current page is the last page 898 } 899 900 gotoPage(dims[selectedIndex[2]] - 1); 901 } 902 903 @Override 904 public Image getImage() { 905 if (image != null) { 906 return image; 907 } 908 909 int rank = dataset.getRank(); 910 if (rank <= 0) { 911 dataset.init(); 912 } 913 isTrueColor = dataset.isTrueColor(); 914 is3D = (dataset.getRank() > 2) && !((ScalarDS) dataset).isTrueColor(); 915 916 try { 917 if (isTrueColor) { 918 getTrueColorImage(); 919 } 920 else { 921 getIndexedImage(); 922 } 923 } 924 catch (Throwable ex) { 925 toolkit.beep(); 926 JOptionPane.showMessageDialog(this, ex, "ImageView:"+getTitle(), 927 JOptionPane.ERROR_MESSAGE); 928 return null; 929 } 930 931 // set number type, ... 932 if (data != null) { 933 isUnsigned = dataset.isUnsigned(); 934 String cname = data.getClass().getName(); 935 NT = cname.charAt(cname.lastIndexOf("[") + 1); 936 } 937 938 return image; 939 } 940 941 /** 942 * @throws Exception if a failure occurred 943 * @throws OutOfMemoryError if memory is exhausted 944 */ 945 private void getIndexedImage() throws Exception, OutOfMemoryError { 946 947 if (imagePalette==null) 948 imagePalette = dataset.getPalette(); 949 950 boolean noPalette = false; 951 boolean isLocalFile = dataset.getFileFormat().exists(); 952 953 if (imagePalette == null) { 954 noPalette = true; 955 imagePalette = Tools.createGrayPalette(); 956 viewer.showStatus("\nNo attached palette found, default grey palette is used to display image"); 957 } 958 959 // Make sure entire dataset is not loaded when looking at 3D 960 // datasets using the default display mode (double clicking the 961 // data object) 962 if (dataset.getRank() > 2) { 963 dataset.getSelectedDims()[dataset.getSelectedIndex()[2]] = 1; 964 } 965 966 data = dataset.getData(); 967 if (bitmask != null) { 968 if (Tools.applyBitmask(data, bitmask, bitmaskOP)) { 969 doAutoGainContrast = false; 970 } 971 } 972 973 int typeClass = dataset.getDatatype().getDatatypeClass(); 974 if (typeClass == Datatype.CLASS_INTEGER || typeClass == Datatype.CLASS_CHAR) { 975 data = dataset.convertFromUnsignedC(); 976 isUnsignedConverted = true; 977 doAutoGainContrast = doAutoGainContrast || 978 (ViewProperties.isAutoContrast() && noPalette && isLocalFile); 979 } 980 else 981 doAutoGainContrast = false; 982 983 boolean isAutoContrastFailed = true; 984 if (doAutoGainContrast) { 985 isAutoContrastFailed = (!computeAutoGainImageData(gainBias,null)); 986 } 987 988 int w = dataset.getWidth(); 989 int h = dataset.getHeight(); 990 991 if (isAutoContrastFailed) { 992 doAutoGainContrast = false; 993 imageByteData = Tools.getBytes(data, dataRange, w, h, !dataset 994 .isDefaultImageOrder(), dataset.getFilteredImageValues(), 995 convertByteData, imageByteData, invalidValueIndex); 996 } 997 else if (dataRange!= null && dataRange[0]==dataRange[1]) { 998 Tools.findMinMax(data, dataRange, null); 999 } 1000 1001 image = createIndexedImage(imageByteData, imagePalette, w, h); 1002 } 1003 1004 /** 1005 * @throws Exception 1006 * @throws OutOfMemoryError 1007 */ 1008 private void getTrueColorImage() throws Exception, OutOfMemoryError { 1009 isPlaneInterlace = (dataset.getInterlace() == ScalarDS.INTERLACE_PLANE); 1010 1011 long[] selected = dataset.getSelectedDims(); 1012 long[] start = dataset.getStartDims(); 1013 int[] selectedIndex = dataset.getSelectedIndex(); 1014 long[] stride = dataset.getStride(); 1015 1016 if (start.length > 2) { 1017 start[selectedIndex[2]] = 0; 1018 selected[selectedIndex[2]] = 3; 1019 stride[selectedIndex[2]] = 1; 1020 } 1021 1022 // reload data 1023 dataset.clearData(); 1024 data = dataset.getData(); 1025 1026 1027 int w = dataset.getWidth(); 1028 int h = dataset.getHeight(); 1029 1030 // converts raw data to image data 1031 imageByteData = Tools.getBytes(data, dataRange, w, h, false, dataset.getFilteredImageValues(), 1032 imageByteData); 1033 1034 image = createTrueColorImage(imageByteData, isPlaneInterlace, w, h); 1035 } 1036 1037 /** 1038 * Compute image data from autogain 1039 * 1040 * @param gb the gain bias 1041 * @param range the contrast range to apply 1042 * 1043 * @return true if the image buffer is converted 1044 */ 1045 private boolean computeAutoGainImageData(double[] gb, double[] range) { 1046 boolean retValue = true; 1047 1048 // data is unsigned short. Convert image byte data using auto-contrast 1049 // image algorithm 1050 boolean isUnsigned = dataset.isUnsigned(); 1051 1052 if (gainBias == null) { // calculate auto_gain only once 1053 gainBias = new double[2]; 1054 Tools.autoContrastCompute(data, gainBias, isUnsigned); 1055 } 1056 1057 if (gb == null) 1058 gb = gainBias; 1059 1060 autoGainData = Tools.autoContrastApply(data, autoGainData, gb, range, isUnsigned); 1061 1062 if (autoGainData != null) { 1063 if ((imageByteData == null) 1064 || (imageByteData.length != Array.getLength(data))) { 1065 imageByteData = new byte[Array.getLength(data)]; 1066 } 1067 retValue = (Tools.autoContrastConvertImageBuffer(autoGainData, imageByteData, true) >= 0); 1068 } 1069 else 1070 retValue = false; 1071 1072 if (gainBias_current == null) 1073 gainBias_current = new double[2]; 1074 1075 gainBias_current[0] = gb[0]; 1076 gainBias_current[1] = gb[1]; 1077 1078 return retValue; 1079 } 1080 1081 // implementing ImageObserver 1082 private void zoomIn() { 1083 if (zoomFactor >= 1) { 1084 zoomTo(zoomFactor + 1.0f); 1085 } 1086 else { 1087 zoomTo(zoomFactor + 0.125f); 1088 } 1089 } 1090 1091 // implementing ImageObserver 1092 private void zoomOut() { 1093 if (zoomFactor > 1) { 1094 zoomTo(zoomFactor - 1.0f); 1095 } 1096 else { 1097 zoomTo(zoomFactor - 0.125f); 1098 } 1099 } 1100 1101 // implementing ImageObserver 1102 private void zoomTo(float zf) { 1103 if (zf > 8) 1104 zf = 8; 1105 else if (zf < 0.125) 1106 zf = 0.125f; 1107 1108 if (zoomFactor == zf) 1109 return; // no change in zooming 1110 1111 zoomFactor = zf; 1112 1113 Dimension imageSize = new Dimension( 1114 (int) (imageComponent.originalSize.width * zoomFactor), 1115 (int) (imageComponent.originalSize.height * zoomFactor)); 1116 1117 this.invalidate(); 1118 imageComponent.invalidate(); 1119 imageComponent.setImageSize(imageSize); 1120 this.validate(); 1121 1122 if ((zoomFactor > 0.99) && (zoomFactor < 1.01)) { 1123 setTitle(frameTitle); 1124 } 1125 else { 1126 setTitle(frameTitle + " - " + 100 * zoomFactor + "%"); 1127 } 1128 } 1129 1130 // implementing ImageObserver 1131 private void showColorTable() { 1132 if (imagePalette == null) { 1133 return; 1134 } 1135 1136 String viewName = (String) HDFView.getListOfPaletteView().get(0); 1137 1138 try { 1139 Class theClass = Class.forName(viewName); 1140 if ("hdf.view.DefaultPaletteView".equals(viewName)) { 1141 Object[] initargs = { viewer, this }; 1142 Tools.newInstance(theClass, initargs); 1143 } 1144 else { 1145 Object[] initargs = { this }; 1146 Tools.newInstance(theClass, initargs); 1147 } 1148 } 1149 catch (Exception ex) { 1150 viewer.showStatus(ex.toString()); 1151 } 1152 } 1153 1154 private void showHistogram() { 1155 Rectangle rec = imageComponent.selectedArea; 1156 1157 if (isTrueColor) { 1158 toolkit.beep(); 1159 JOptionPane.showMessageDialog( 1160 this, 1161 "Unsupported operation: unable to draw histogram for true color image.", 1162 getTitle(), JOptionPane.ERROR_MESSAGE); 1163 return; 1164 } 1165 1166 if ((rec == null) || (rec.getWidth() <= 0) || (rec.getHeight() <= 0)) { 1167 toolkit.beep(); 1168 JOptionPane.showMessageDialog( 1169 this, 1170 "No data for histogram.\nUse Shift+Mouse_drag to select an image area.", 1171 getTitle(), JOptionPane.ERROR_MESSAGE); 1172 return; 1173 } 1174 1175 double chartData[][] = new double[1][256]; 1176 for (int i = 0; i < 256; i++) { 1177 chartData[0][i] = 0.0; 1178 } 1179 1180 int w = dataset.getWidth(); 1181 int x0 = (int) (rec.x / zoomFactor); 1182 int y0 = (int) (rec.y / zoomFactor); 1183 int x = x0 + (int) (rec.width / zoomFactor); 1184 int y = y0 + (int) (rec.height / zoomFactor); 1185 int arrayIndex = 0; 1186 for (int i = y0; i < y; i++) { 1187 for (int j = x0; j < x; j++) { 1188 arrayIndex = (int) imageByteData[i * w + j]; 1189 if (arrayIndex < 0) { 1190 arrayIndex += 256; 1191 } 1192 chartData[0][arrayIndex] += 1.0; 1193 } 1194 } 1195 1196 // Use original data range 1197 double[] xRange = originalRange; 1198 if (xRange == null || xRange[0] == xRange[1]) { 1199 xRange = new double[2]; 1200 Tools.findMinMax(data, xRange, null); 1201 } 1202 1203 // double[] xRange = {0, 255}; 1204 1205 Chart cv = new Chart((JFrame) viewer, "Histogram - " + dataset.getPath() 1206 + dataset.getName() + " - by pixel index", Chart.HISTOGRAM, 1207 chartData, xRange, null); 1208 cv.setVisible(true); 1209 } 1210 1211 /** 1212 * Selects the whole image. 1213 * 1214 * @throws Exception if a failure occurred 1215 */ 1216 private void selectAll() throws Exception { 1217 imageComponent.selectAll(); 1218 } 1219 1220 // implementing ImageObserver 1221 private void flip(int direction) { 1222 ImageFilter filter = new FlipFilter(direction); 1223 1224 if (applyImageFilter(filter)) { 1225 // toggle flip flag 1226 if (direction == FLIP_HORIZONTAL) { 1227 isHorizontalFlipped = !isHorizontalFlipped; 1228 } 1229 else { 1230 isVerticalFlipped = !isVerticalFlipped; 1231 } 1232 } 1233 } 1234 1235 // implementing ImageObserver 1236 private void rotate(int direction) { 1237 if ( !(direction == ROTATE_CW_90 || direction == ROTATE_CCW_90)) 1238 return; 1239 1240 Rotate90Filter filter = new Rotate90Filter(direction); 1241 applyImageFilter(filter); 1242 1243 if (direction == ROTATE_CW_90) { 1244 rotateCount++; 1245 if (rotateCount == 4) { 1246 rotateCount = 0; 1247 } 1248 } 1249 else { 1250 rotateCount--; 1251 if (rotateCount == -4) { 1252 rotateCount = 0; 1253 } 1254 } 1255 } 1256 1257 // implementing ImageObserver 1258 private void contour(int level) { 1259 applyImageFilter(new ContourFilter(level)); 1260 } 1261 1262 /** Apply contrast/brightness to unsigned short integer 1263 * 1264 * @param gb the gain bias 1265 * @param range the contrast range to apply 1266 */ 1267 private void applyAutoGain(double[] gb, double[] range) { 1268 1269 if (computeAutoGainImageData(gb, range)) { 1270 int w = dataset.getWidth(); 1271 int h = dataset.getHeight(); 1272 image = createIndexedImage(imageByteData, imagePalette, w, h); 1273 imageComponent.setImage(image); 1274 zoomTo(zoomFactor); 1275 } 1276 } 1277 1278 private void setValueVisible(boolean b) { 1279 valueField.setVisible(b); 1280 validate(); 1281 // updateUI(); //bug !!! on Windows. gives NullPointerException at 1282 // javax.swing.plaf.basic.BasicInternalFrameUI$BorderListener.mousePressed(BasicInternalFrameUI.java:693) 1283 } 1284 1285 /** change alpha value for a given list of pixel locations 1286 * 1287 * @param img the image to adjust 1288 * @param alpha the alpha value 1289 * @param idx the list of indices to adjust 1290 * 1291 */ 1292 private void adjustAlpha(BufferedImage img, int alpha, List<Integer> idx) 1293 { 1294 if (img==null || idx.size()<=0) 1295 return; 1296 1297 final int[] pixels = ( (DataBufferInt) img.getRaster().getDataBuffer() ).getData(); 1298 int len = pixels.length; 1299 1300 alpha = alpha << 24; 1301 for (Integer i : idx) { 1302 if (i<len) 1303 pixels[i] = alpha | (pixels[i] & 0x00ffffff); 1304 } 1305 1306 // for test only 1307 // final int[] pixels = ( (DataBufferInt) img.getRaster().getDataBuffer() ).getData(); 1308 // for (int i=0; i<pixels.length/2; i++) pixels[i] = (pixels[i] & 0x60ffffff); 1309 } 1310 1311 1312 /** 1313 * This method returns a buffered image with the contents of an image. 1314 * 1315 * @param image 1316 * the plain image object. 1317 * @return buffered image for the given image. 1318 */ 1319 private BufferedImage toBufferedImage(Image image) { 1320 if (image == null) { 1321 return null; 1322 } 1323 1324 if (image instanceof BufferedImage) { 1325 return (BufferedImage) image; 1326 } 1327 1328 // !!!!!!!!!!!!!!!!!! NOTICE !!!!!!!!!!!!!!!!!!!!! 1329 // the following way of creating a buffered image is using 1330 // Component.createImage(). This method can be used only if the 1331 // component is visible on the screen. Also, this method returns 1332 // buffered images that do not support transparent pixels. 1333 // The buffered image created by this way works for package 1334 // com.sun.image.codec.jpeg.* 1335 // It does not work well with JavaTM Advanced Imaging 1336 // com.sun.media.jai.codec.*; 1337 // if the screen setting is less than 32-bit color 1338 int w = image.getWidth(null); 1339 int h = image.getHeight(null); 1340 BufferedImage bimage = (BufferedImage) createImage(w, h); 1341 Graphics g = bimage.createGraphics(); 1342 g.drawImage(image, 0, 0, null); 1343 1344 g.dispose(); 1345 return bimage; 1346 } 1347 1348 /** 1349 * Save the image to an image file. 1350 * 1351 * @param type 1352 * the image type. 1353 * 1354 * @throws Exception if a failure occured 1355 */ 1356 private void saveImageAs(String type) throws Exception { 1357 if (image == null) { 1358 return; 1359 } 1360 1361 final JFileChooser fchooser = new JFileChooser(dataset.getFile()); 1362 if (type.equals(Tools.FILE_TYPE_JPEG)) { 1363 fchooser.setFileFilter(DefaultFileFilter.getFileFilterJPEG()); 1364 // } else if (type.equals(Tools.FILE_TYPE_TIFF)) { 1365 // fchooser.setFileFilter(DefaultFileFilter.getFileFilterTIFF()); 1366 } 1367 else if (type.equals(Tools.FILE_TYPE_PNG)) { 1368 fchooser.setFileFilter(DefaultFileFilter.getFileFilterPNG()); 1369 } 1370 else if (type.equals(Tools.FILE_TYPE_GIF)) { 1371 fchooser.setFileFilter(DefaultFileFilter.getFileFilterGIF()); 1372 } 1373 else if (type.equals(Tools.FILE_TYPE_BMP)) { 1374 fchooser.setFileFilter(DefaultFileFilter.getFileFilterBMP()); 1375 } 1376 1377 // fchooser.changeToParentDirectory(); 1378 fchooser.setDialogTitle("Save Current Image To " + type + " File --- " + dataset.getName()); 1379 1380 File chosenFile = new File(dataset.getName() + "." + type.toLowerCase()); 1381 fchooser.setSelectedFile(chosenFile); 1382 1383 int returnVal = fchooser.showSaveDialog(this); 1384 if (returnVal != JFileChooser.APPROVE_OPTION) { 1385 return; 1386 } 1387 1388 chosenFile = fchooser.getSelectedFile(); 1389 if (chosenFile == null) { 1390 return; 1391 } 1392 String fname = chosenFile.getAbsolutePath(); 1393 1394 if (chosenFile.exists()) { 1395 int newFileFlag = JOptionPane.showConfirmDialog(this, 1396 "File exists. Do you want to replace it ?", 1397 this.getTitle(), JOptionPane.YES_NO_OPTION); 1398 if (newFileFlag == JOptionPane.NO_OPTION) { 1399 return; 1400 } 1401 } 1402 1403 BufferedImage bi = null; 1404 try { 1405 bi = toBufferedImage(image); 1406 1407 // Convert JPG and BMP images to TYPE_INT_RGB so ImageIO.write succeeds 1408 if (bi.getType() == BufferedImage.TYPE_INT_ARGB && 1409 (type.equals("JPEG") || type.equals("BMP"))) { 1410 BufferedImage newImage = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_INT_RGB); 1411 Graphics g = newImage.createGraphics(); 1412 g.drawImage(bi, 0, 0, Color.BLACK, null); 1413 g.dispose(); 1414 bi = newImage; 1415 } 1416 } 1417 catch (OutOfMemoryError err) { 1418 toolkit.beep(); 1419 JOptionPane.showMessageDialog(this, err.getMessage(), getTitle(), JOptionPane.ERROR_MESSAGE); 1420 return; 1421 } 1422 1423 Tools.saveImageAs(bi, chosenFile, type); 1424 1425 bi = null; 1426 1427 viewer.showStatus("Current image saved to: " + chosenFile.getAbsolutePath()); 1428 1429 try { 1430 RandomAccessFile rf = new RandomAccessFile(chosenFile, "r"); 1431 long size = rf.length(); 1432 rf.close(); 1433 viewer.showStatus("File size (bytes): " + size); 1434 } 1435 catch (Exception ex) { 1436 log.debug("File {} size:", chosenFile.getName(), ex); 1437 } 1438 } 1439 1440 public void actionPerformed(ActionEvent e) { 1441 try { 1442 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 1443 1444 Object source = e.getSource(); 1445 String cmd = e.getActionCommand(); 1446 1447 if (cmd.equals("Close")) { 1448 dispose(); // terminate the application 1449 ((Vector) rotateRelatedItems).setSize(0); 1450 } 1451 else if (cmd.startsWith("Save image as ")) { 1452 String filetype = null; 1453 if (cmd.equals("Save image as jpeg")) { 1454 filetype = Tools.FILE_TYPE_JPEG; 1455 } 1456 else if (cmd.equals("Save image as tiff")) { 1457 filetype = Tools.FILE_TYPE_TIFF; 1458 } 1459 else if (cmd.equals("Save image as png")) { 1460 filetype = Tools.FILE_TYPE_PNG; 1461 } 1462 else if (cmd.equals("Save image as gif")) { 1463 filetype = Tools.FILE_TYPE_GIF; 1464 } 1465 else if (cmd.equals("Save image as bmp")) { 1466 filetype = Tools.FILE_TYPE_BMP; 1467 } 1468 1469 try { 1470 saveImageAs(filetype); 1471 } 1472 catch (Exception ex) { 1473 toolkit.beep(); 1474 JOptionPane.showMessageDialog(this, ex, getTitle(), 1475 JOptionPane.ERROR_MESSAGE); 1476 } 1477 } 1478 else if (cmd.equals("Write selection to image")) { 1479 if ((getSelectedArea().width <= 0) 1480 || (getSelectedArea().height <= 0)) { 1481 JOptionPane 1482 .showMessageDialog( 1483 this, 1484 "No data to write.\nUse Shift+Mouse_drag to select an image area.", 1485 "HDFView", JOptionPane.INFORMATION_MESSAGE); 1486 return; 1487 } 1488 1489 TreeView treeView = viewer.getTreeView(); 1490 TreeNode node = treeView.findTreeNode(dataset); 1491 Group pGroup = (Group) ((DefaultMutableTreeNode) node 1492 .getParent()).getUserObject(); 1493 TreeNode root = dataset.getFileFormat().getRootNode(); 1494 1495 if (root == null) { 1496 return; 1497 } 1498 1499 Vector list = new Vector(dataset.getFileFormat() 1500 .getNumberOfMembers() + 5); 1501 DefaultMutableTreeNode theNode = null; 1502 Enumeration local_enum = ((DefaultMutableTreeNode) root) 1503 .depthFirstEnumeration(); 1504 1505 while (local_enum.hasMoreElements()) { 1506 theNode = (DefaultMutableTreeNode) local_enum.nextElement(); 1507 list.add(theNode.getUserObject()); 1508 } 1509 1510 NewDatasetDialog dialog = new NewDatasetDialog((JFrame) viewer, 1511 pGroup, list, this); 1512 dialog.setVisible(true); 1513 1514 HObject obj = (HObject) dialog.getObject(); 1515 if (obj != null) { 1516 Group pgroup = dialog.getParentGroup(); 1517 try { 1518 treeView.addObject(obj, pgroup); 1519 } 1520 catch (Exception ex) { 1521 log.debug("Write selection to image:", ex); 1522 } 1523 } 1524 1525 list.setSize(0); 1526 } 1527 else if (cmd.equals("Zoom in")) { 1528 zoomIn(); 1529 } 1530 else if (cmd.equals("Zoom out")) { 1531 zoomOut(); 1532 } 1533 else if (cmd.equals("Edit palette")) { 1534 showColorTable(); 1535 } 1536 else if (cmd.equals("Import palette")) { 1537 JFileChooser fchooser = new JFileChooser(ViewProperties 1538 .getWorkDir()); 1539 int returnVal = fchooser.showOpenDialog(this); 1540 1541 if (returnVal != JFileChooser.APPROVE_OPTION) { 1542 return; 1543 } 1544 1545 File choosedFile = fchooser.getSelectedFile(); 1546 if (choosedFile == null || choosedFile.isDirectory()) { 1547 return; 1548 } 1549 1550 Vector<String> palList = ViewProperties.getPaletteList(); 1551 String palPath = choosedFile.getAbsolutePath(); 1552 if(!palList.contains(palList)) 1553 palList.addElement(palPath); 1554 } 1555 else if (cmd.equals("Export palette")) { 1556 if (imagePalette == null) 1557 return; 1558 1559 String wd =ViewProperties.getWorkDir()+File.separator; 1560 JFileChooser fchooser = new JFileChooser(wd); 1561 FileNameExtensionFilter filter = new FileNameExtensionFilter("Color lookup table", "lut"); 1562 File pfile = Tools.checkNewFile(wd, ".lut"); 1563 fchooser.setSelectedFile(pfile); 1564 fchooser.setFileFilter(filter); 1565 int returnVal = fchooser.showOpenDialog(this); 1566 1567 if (returnVal != JFileChooser.APPROVE_OPTION) { 1568 return; 1569 } 1570 1571 File choosedFile = fchooser.getSelectedFile(); 1572 if (choosedFile == null || choosedFile.isDirectory()) { 1573 return; 1574 } 1575 1576 if (choosedFile.exists()) 1577 { 1578 int newFileFlag = JOptionPane.showConfirmDialog(this, 1579 "File exists. Do you want to replace it ?", 1580 this.getTitle(), 1581 JOptionPane.YES_NO_OPTION); 1582 if (newFileFlag == JOptionPane.NO_OPTION) { 1583 return; 1584 } 1585 } 1586 1587 PrintWriter out = null; 1588 1589 try { 1590 out = new PrintWriter(new BufferedWriter(new FileWriter(choosedFile))); 1591 } 1592 catch (Exception ex) { 1593 out = null; 1594 } 1595 1596 if (out == null) 1597 return; 1598 1599 int cols = 3; 1600 int rows = 256; 1601 int rgb = 0; 1602 for (int i=0; i<rows; i++) { 1603 out.print(i); 1604 for (int j=0; j<cols; j++) { 1605 out.print(' '); 1606 rgb = imagePalette[j][i]; 1607 if (rgb<0) rgb += 256; 1608 out.print(rgb); 1609 } 1610 out.println(); 1611 } 1612 1613 out.flush(); 1614 out.close(); 1615 } 1616 else if (cmd.equals("Set data range")) { 1617 1618 if (originalRange==null || originalRange[0]== originalRange[1]) 1619 return; 1620 1621 // call only once 1622 if (dataDist == null) { 1623 dataDist = new int[256]; 1624 Tools.findDataDist(data, dataDist, originalRange); 1625 } 1626 1627 DataRangeDialog drd = new DataRangeDialog((JFrame) viewer, dataRange, originalRange,dataDist); 1628 double[] drange = drd.getRange(); 1629 1630 if ((drange == null) 1631 || (drange[0] == drange[1]) 1632 || ((drange[0] == dataRange[0]) && (drange[1] == dataRange[1]))) { 1633 return; 1634 } 1635 1636 applyDataRange(drange); 1637 } 1638 else if (cmd.equals("Flip horizontal")) { 1639 flip(FLIP_HORIZONTAL); 1640 } 1641 else if (cmd.equals("Flip vertical")) { 1642 flip(FLIP_VERTICAL); 1643 } 1644 else if (cmd.startsWith("Rotate")) { 1645 if (cmd.equals("Rotate clockwise")) 1646 rotate(ROTATE_CW_90); 1647 else 1648 rotate(ROTATE_CCW_90); 1649 1650 int n = rotateRelatedItems.size(); 1651 for (int i = 0; i < n; i++) { 1652 boolean itemState = (rotateCount == 0); 1653 ((javax.swing.JComponent) rotateRelatedItems.get(i)) 1654 .setEnabled(itemState); 1655 } 1656 } 1657 else if (cmd.equals("Show image value")) { 1658 boolean b = ((JCheckBoxMenuItem) source).getState(); 1659 setValueVisible(b); 1660 } 1661 else if (cmd.startsWith("Go to frame")) { 1662 int page = 0; 1663 try { 1664 page = Integer.parseInt(frameField.getText().trim())-indexBase; 1665 } 1666 catch (Exception ex) { 1667 page = -1; 1668 } 1669 1670 gotoPage (page); 1671 } 1672 else if (cmd.startsWith("Show animation")) { 1673 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 1674 new Animation((JFrame) viewer, dataset); 1675 setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 1676 } 1677 else if (cmd.startsWith("Animation speed")) { 1678 animationSpeed = Integer.parseInt((cmd 1679 .substring(cmd.length() - 2)).trim()); 1680 } 1681 1682 else if (cmd.startsWith("Contour")) { 1683 int level = Integer.parseInt(cmd.substring(cmd.length() - 1)); 1684 contour(level); 1685 } 1686 else if (cmd.startsWith("Brightness")) { 1687 if (contrastSlider == null) { 1688 contrastSlider = new ContrastSlider((JFrame) viewer, image.getSource()); 1689 } 1690 contrastSlider.setVisible(true); 1691 } 1692 else if (cmd.equals("Show chart")) { 1693 showHistogram(); 1694 } 1695 else if (cmd.equals("First page")) { 1696 firstPage(); 1697 } 1698 else if (cmd.equals("Previous page")) { 1699 previousPage(); 1700 } 1701 else if (cmd.equals("Next page")) { 1702 nextPage(); 1703 } 1704 else if (cmd.equals("Last page")) { 1705 lastPage(); 1706 } 1707 else if (cmd.equals("Show statistics")) { 1708 try { 1709 double[] minmax = new double[2]; 1710 double[] stat = new double[2]; 1711 1712 Object theData = null; 1713 theData = getSelectedData(); 1714 1715 if (theData == null) { 1716 theData = data; 1717 } 1718 1719 Tools.findMinMax(theData, minmax, dataset.getFillValue()); 1720 if (Tools.computeStatistics(theData, stat, dataset.getFillValue()) > 0) { 1721 String statistics = "Min = " 1722 + minmax[0] + "\nMax = " 1723 + minmax[1] + "\nMean = " 1724 + stat[0] + "\nStandard deviation = " + stat[1]; 1725 JOptionPane.showMessageDialog(this, statistics, 1726 "Statistics", JOptionPane.INFORMATION_MESSAGE); 1727 } 1728 } 1729 catch (Exception ex) { 1730 toolkit.beep(); 1731 JOptionPane.showMessageDialog((JFrame) viewer, ex, 1732 getTitle(), JOptionPane.ERROR_MESSAGE); 1733 } 1734 } 1735 else if (cmd.equals("Select all data")) { 1736 try { 1737 selectAll(); 1738 } 1739 catch (Exception ex) { 1740 toolkit.beep(); 1741 JOptionPane.showMessageDialog((JFrame) viewer, ex, 1742 getTitle(), JOptionPane.ERROR_MESSAGE); 1743 } 1744 } 1745 } 1746 finally { 1747 setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 1748 } 1749 1750 } 1751 1752 public void dispose() { 1753 // reload the data when it is displayed next time 1754 // because the display type (table or image) may be 1755 // different. 1756 if (!dataset.isImage()) { 1757 dataset.clearData(); 1758 } 1759 1760 data = null; 1761 image = null; 1762 imageByteData = null; 1763 imageComponent = null; 1764 autoGainData = null; 1765 ((Vector) rotateRelatedItems).setSize(0); 1766 System.runFinalization(); 1767 System.gc(); 1768 1769 viewer.removeDataView(this); 1770 1771 super.dispose(); 1772 } 1773 1774 // Implementing DataView. 1775 @Override 1776 public HObject getDataObject() { 1777 return dataset; 1778 } 1779 1780 @Override 1781 public byte[] getImageByteData() { 1782 return imageByteData; 1783 } 1784 1785 /** 1786 * Returns the selected data values. 1787 * 1788 * @return the selected data object. 1789 */ 1790 @Override 1791 public Object getSelectedData() { 1792 Object selectedData = null; 1793 1794 int cols = imageComponent.originalSelectedArea.width; 1795 int rows = imageComponent.originalSelectedArea.height; 1796 1797 if ((cols <= 0) || (rows <= 0)) { 1798 return null; // no data is selected 1799 } 1800 1801 int size = cols * rows; 1802 if (isTrueColor) { 1803 size *= 3; 1804 } 1805 1806 if (NT == 'B') { 1807 selectedData = new byte[size]; 1808 } 1809 else if (NT == 'S') { 1810 selectedData = new short[size]; 1811 } 1812 else if (NT == 'I') { 1813 selectedData = new int[size]; 1814 } 1815 else if (NT == 'J') { 1816 selectedData = new long[size]; 1817 } 1818 else if (NT == 'F') { 1819 selectedData = new float[size]; 1820 } 1821 else if (NT == 'D') { 1822 selectedData = new double[size]; 1823 } 1824 else { 1825 return null; 1826 } 1827 1828 int r0 = imageComponent.originalSelectedArea.y; 1829 int c0 = imageComponent.originalSelectedArea.x; 1830 int w = imageComponent.originalSize.width; 1831 int h = imageComponent.originalSize.height; 1832 1833 // transfer location to the original coordinator 1834 if (isHorizontalFlipped) { 1835 c0 = w - 1 - c0 - cols; 1836 } 1837 1838 if (isVerticalFlipped) { 1839 r0 = h - 1 - r0 - rows; 1840 } 1841 1842 int idx_src = 0, idx_dst = 0; 1843 if (isTrueColor) { 1844 int imageSize = w * h; 1845 if (isPlaneInterlace) { 1846 for (int j = 0; j < 3; j++) { 1847 int plane = imageSize * j; 1848 for (int i = 0; i < rows; i++) { 1849 idx_src = plane + (r0 + i) * w + c0; 1850 System.arraycopy(data, idx_src, selectedData, idx_dst, 1851 cols); 1852 idx_dst += cols; 1853 } 1854 } 1855 } 1856 else { 1857 int numberOfDataPoints = cols * 3; 1858 for (int i = 0; i < rows; i++) { 1859 idx_src = (r0 + i) * w + c0; 1860 System.arraycopy(data, idx_src * 3, selectedData, idx_dst, 1861 numberOfDataPoints); 1862 idx_dst += numberOfDataPoints; 1863 } 1864 } 1865 } 1866 else { // indexed image 1867 for (int i = 0; i < rows; i++) { 1868 idx_src = (r0 + i) * w + c0; 1869 System.arraycopy(data, idx_src, selectedData, idx_dst, cols); 1870 idx_dst += cols; 1871 } 1872 } 1873 1874 return selectedData; 1875 } 1876 1877 /** 1878 * returns the selected area of the image 1879 * 1880 * @return the rectangle of the selected image area. 1881 */ 1882 @Override 1883 public Rectangle getSelectedArea() { 1884 return imageComponent.originalSelectedArea; 1885 } 1886 1887 /** @return true if the image is a truecolor image. */ 1888 @Override 1889 public boolean isTrueColor() { 1890 return isTrueColor; 1891 } 1892 1893 /** @return true if the image interlace is plance interlace. */ 1894 @Override 1895 public boolean isPlaneInterlace() { 1896 return isPlaneInterlace; 1897 } 1898 1899 @Override 1900 public void setImage(Image img) { 1901 image = img; 1902 imageComponent.setImage(img); 1903 1904 setImageDirection(); 1905 } 1906 1907 private void setImageDirection() { 1908 boolean isHF = isHorizontalFlipped; 1909 boolean isVF = isVerticalFlipped; 1910 int rc = rotateCount; 1911 1912 if (isHF || isVF || rc!=0) { 1913 isHorizontalFlipped = false; 1914 isVerticalFlipped = false; 1915 rotateCount = 0; 1916 1917 if (isHF) 1918 flip(FLIP_HORIZONTAL); 1919 1920 if (isVF) 1921 flip(FLIP_VERTICAL); 1922 1923 while (rc > 0) { 1924 rotate(ROTATE_CW_90); 1925 rc--; 1926 } 1927 1928 while (rc < 0) { 1929 rotate(ROTATE_CCW_90); 1930 rc++; 1931 } 1932 1933 } 1934 else { 1935 if (imageOrigin == Origin.LOWER_LEFT) 1936 flip(FLIP_VERTICAL); 1937 else if (imageOrigin == Origin.UPPER_RIGHT) 1938 flip(FLIP_HORIZONTAL); 1939 if (imageOrigin == Origin.LOWER_RIGHT) { 1940 rotate(ROTATE_CW_90); 1941 rotate(ROTATE_CW_90); 1942 } 1943 } 1944 1945 zoomTo(zoomFactor); 1946 } 1947 1948 @Override 1949 public byte[][] getPalette() { 1950 return imagePalette; 1951 } 1952 1953 @Override 1954 public void setPalette(byte[][] pal) { 1955 imagePalette = pal; 1956 paletteComponent.updatePalette(pal); 1957 } 1958 1959 private void gotoPage(long idx) { 1960 if (dataset.getRank() < 3 || idx == (curFrame - indexBase)) { 1961 return; 1962 } 1963 1964 long[] start = dataset.getStartDims(); 1965 int[] selectedIndex = dataset.getSelectedIndex(); 1966 long[] dims = dataset.getDims(); 1967 1968 if ((idx < 0) || (idx >= dims[selectedIndex[2]])) { 1969 toolkit.beep(); 1970 JOptionPane.showMessageDialog(this, 1971 "Frame number must be between " + indexBase + " and " 1972 + (dims[selectedIndex[2]] - 1 + indexBase), getTitle(), 1973 JOptionPane.ERROR_MESSAGE); 1974 return; 1975 } 1976 1977 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 1978 1979 start[selectedIndex[2]] = idx; 1980 curFrame = idx+indexBase; 1981 dataset.clearData(); 1982 image = null; 1983 gainBias = null; 1984 imageComponent.setImage(getImage()); 1985 frameField.setText(String.valueOf(curFrame)); 1986 1987 isHorizontalFlipped = false; 1988 isVerticalFlipped = false; 1989 rotateCount = 0; 1990 1991 if (imageOrigin == Origin.LOWER_LEFT) 1992 flip(FLIP_VERTICAL); 1993 else if (imageOrigin == Origin.UPPER_RIGHT) 1994 flip(FLIP_HORIZONTAL); 1995 if (imageOrigin == Origin.LOWER_RIGHT) { 1996 rotate(ROTATE_CW_90); 1997 rotate(ROTATE_CW_90); 1998 } 1999 2000 setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 2001 2002 updateUI(); 2003 } 2004 2005 /** 2006 * Creates a RGB indexed image of 256 colors. 2007 * 2008 * @param imageData 2009 * the byte array of the image data. 2010 * @param palette 2011 * the color lookup table. 2012 * @param w 2013 * the width of the image. 2014 * @param h 2015 * the height of the image. 2016 * 2017 * @return the image. 2018 */ 2019 private Image createIndexedImage(byte[] imageData, byte[][] palette, int w, int h) 2020 { 2021 bufferedImage = (BufferedImage)Tools.createIndexedImage(bufferedImage, imageData, palette, w, h); 2022 adjustAlpha(bufferedImage, 0, invalidValueIndex); 2023 2024 return bufferedImage; 2025 } 2026 2027 /** 2028 * Creates a true color image. 2029 * <p> 2030 * The data may be arranged in one of two ways: by pixel or by plane. In 2031 * both cases, the dataset will have a dataspace with three dimensions, 2032 * height, width, and components. 2033 * <p> 2034 * For HDF4, the interlace modes specify orders for the dimensions as: 2035 * 2036 * <pre> 2037 * INTERLACE_PIXEL = [width][height][pixel components] 2038 * INTERLACE_PLANE = [pixel components][width][height] 2039 * </pre> 2040 * <p> 2041 * For HDF5, the interlace modes specify orders for the dimensions as: 2042 * 2043 * <pre> 2044 * INTERLACE_PIXEL = [height][width][pixel components] 2045 * INTERLACE_PLANE = [pixel components][height][width] 2046 * </pre> 2047 * 2048 * @param imageData 2049 * the byte array of the image data. 2050 * @param planeInterlace 2051 * flag if the image is plane intelace. 2052 * @param w 2053 * the width of the image. 2054 * @param h 2055 * the height of the image. 2056 * 2057 * @return the image. 2058 */ 2059 private Image createTrueColorImage(byte[] imageData, boolean planeInterlace, 2060 int w, int h) 2061 { 2062 2063 if (bufferedImage == null) 2064 bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); 2065 2066 final int[] pixels = ( (DataBufferInt) bufferedImage.getRaster().getDataBuffer() ).getData(); 2067 int len = pixels.length; 2068 2069 int idx = 0, r = 0, g = 0, b = 0; 2070 for (int i = 0; i < h; i++) { 2071 for (int j = 0; j < w; j++) { 2072 if (planeInterlace) { 2073 r = ((int)imageData[idx] & 0xff)<<16; 2074 g = ((int)imageData[len + idx] & 0xff)<<8; 2075 b = ((int)imageData[len * 2 + idx] & 0xff); 2076 } 2077 else { 2078 r = ((int)imageData[idx * 3] & 0xff)<<16; 2079 g = ((int)imageData[idx * 3 + 1] & 0xff)<<8; 2080 b = ((int)imageData[idx * 3 + 2] & 0xff); 2081 } 2082 pixels[idx++] = 0xff000000 | r | g | b; 2083 } 2084 } 2085 2086 adjustAlpha(bufferedImage, 0, invalidValueIndex); 2087 return bufferedImage; 2088 } 2089 2090 private boolean applyImageFilter(ImageFilter filter) { 2091 boolean status = true; 2092 ImageProducer imageProducer = image.getSource(); 2093 2094 try { 2095 image = createImage(new FilteredImageSource(imageProducer, filter)); 2096 imageComponent.setImage(image); 2097 zoomTo(zoomFactor); 2098 } 2099 catch (Throwable err) { 2100 toolkit.beep(); 2101 JOptionPane.showMessageDialog(this, err.getMessage(), getTitle(), 2102 JOptionPane.ERROR_MESSAGE); 2103 status = false; 2104 } 2105 2106 return status; 2107 } 2108 2109 private void applyDataRange(double[] newRange) { 2110 if (doAutoGainContrast && gainBias!= null) { 2111 applyAutoGain(gainBias_current, newRange); 2112 } 2113 else { 2114 int w = dataset.getWidth(); 2115 int h = dataset.getHeight(); 2116 2117 invalidValueIndex.clear(); // data range changed. need to reset invalid values 2118 2119 // invalid values 2120 imageByteData = Tools.getBytes(data, newRange, w, h, 2121 !dataset.isDefaultImageOrder(), 2122 dataset.getFilteredImageValues(), true, null, 2123 invalidValueIndex); 2124 2125 image = createIndexedImage(imageByteData, imagePalette, w, h); 2126 setImage(image); 2127 zoomTo(zoomFactor); 2128 paletteComponent.updateRange(newRange); 2129 } 2130 2131 dataRange[0] = newRange[0]; 2132 dataRange[1] = newRange[1]; 2133 } 2134 2135 /** PaletteComponent draws the palette on the side of the image. */ 2136 private class PaletteComponent extends JComponent { 2137 private static final long serialVersionUID = -5194383032992628565L; 2138 private Color[] colors = null; 2139 private double[] pixelData = null; 2140 private Dimension paintSize = null; 2141 java.text.DecimalFormat format; 2142 double[] dRange = null; 2143 2144 private PaletteComponent(byte[][] palette, double[] range) { 2145 paintSize = new Dimension(25, 2); 2146 format = new java.text.DecimalFormat("0.00E0"); 2147 dRange = range; 2148 double unsigned_celling = 0; 2149 2150 if ((palette != null) && (range != null)) { 2151 double ratio = (dRange[1] - dRange[0]) / 255; 2152 2153 pixelData = new double[256]; 2154 for (int i = 0; i < 256; i++) { 2155 pixelData[i] = (dRange[0] + ratio * i); 2156 } 2157 } 2158 2159 updatePalette(palette); 2160 2161 setPreferredSize(new Dimension(paintSize.width + 60, 2162 paintSize.height * 256)); 2163 setVisible(true); 2164 } 2165 2166 private void updatePalette(byte[][] palette) { 2167 if ((palette != null) && (dRange != null)) { 2168 colors = new Color[256]; 2169 2170 int r, g, b; 2171 for (int i = 0; i < 256; i++) { 2172 r = (int) palette[0][i]; 2173 if (r < 0) { 2174 r += 256; 2175 } 2176 g = (int) palette[1][i]; 2177 if (g < 0) { 2178 g += 256; 2179 } 2180 b = (int) palette[2][i]; 2181 if (b < 0) { 2182 b += 256; 2183 } 2184 2185 colors[i] = new Color(r, g, b); 2186 } 2187 } 2188 2189 repaint(); 2190 } 2191 2192 private void updateRange(double[] newRange) { 2193 if (newRange == null) { 2194 return; 2195 } 2196 2197 dRange = newRange; 2198 double ratio = (dRange[1] - dRange[0]) / 255; 2199 for (int i = 0; i < 256; i++) { 2200 pixelData[i] = (dRange[0] + ratio * i); 2201 } 2202 2203 repaint(); 2204 } 2205 2206 public void paint(Graphics g) { 2207 if ((colors == null) && (pixelData == null)) { 2208 return; 2209 } 2210 2211 Font font = g.getFont(); 2212 g.setFont(new Font(font.getName(), font.getStyle(), 12)); 2213 for (int i = 0; i < 256; i++) { 2214 g.setColor(colors[i]); 2215 g.fillRect(0, paintSize.height * i, paintSize.width, 2216 paintSize.height); 2217 } 2218 2219 g.setColor(Color.black); 2220 for (int i = 0; i < 25; i++) { 2221 g.drawString(format.format(pixelData[i * 10]), 2222 paintSize.width + 5, 10 + paintSize.height * i * 10); 2223 } 2224 g.drawString(format.format(pixelData[255]), paintSize.width + 5, 2225 paintSize.height * 255); 2226 } 2227 } 2228 2229 /** ImageComponent draws the image. */ 2230 private class ImageComponent extends JComponent implements MouseListener, 2231 MouseMotionListener, MouseWheelListener { 2232 private static final long serialVersionUID = -2690648149547151532L; 2233 2234 private Image image; 2235 private Dimension originalSize, imageSize; 2236 private Dimension scrollDim = null; 2237 private Point startPosition, currentPosition; // mouse clicked position 2238 private Rectangle selectedArea, originalSelectedArea; 2239 private StringBuffer strBuff; // to hold display value 2240 private int yMousePosition = 0; // the vertical position of the current mouse 2241 private JScrollBar hbar = null; 2242 private JScrollBar vbar = null; 2243 2244 private ImageComponent(Image img) { 2245 2246 image = img; 2247 imageSize = new Dimension(image.getWidth(this), image.getHeight(this)); 2248 2249 originalSize = imageSize; 2250 selectedArea = new Rectangle(); 2251 originalSelectedArea = new Rectangle(); 2252 setPreferredSize(imageSize); 2253 strBuff = new StringBuffer(); 2254 2255 addMouseListener(this); 2256 addMouseMotionListener(this); 2257 addMouseWheelListener(this); 2258 } 2259 2260 public void paint(Graphics g) { 2261 if (g instanceof Graphics2D && (zoomFactor<0.99)) { 2262 Graphics2D g2 = (Graphics2D) g; 2263 2264 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 2265 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 2266 Image scaledImg = multiBiliner(image, imageSize.width, imageSize.height, true); 2267 g2.drawImage(scaledImg, 0, 0, imageSize.width, imageSize.height, this); 2268 2269 } 2270 else 2271 g.drawImage(image, 0, 0, imageSize.width, imageSize.height, this); 2272 2273 if ((selectedArea.width > 0) && (selectedArea.height > 0)) { 2274 g.setColor(Color.red); 2275 g.drawRect(selectedArea.x, selectedArea.y, selectedArea.width, 2276 selectedArea.height); 2277 } 2278 } 2279 2280 /** 2281 * Create an image using multiple step bilinear, see details at 2282 * http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html 2283 * 2284 * @param img the original image to be scaled 2285 * @param targetWidth the desired width of the scaled instance 2286 * @param targetHeight the desired height of the scaled instance, 2287 * @return a scaled version of the original 2288 */ 2289 private Image multiBiliner(Image img, int targetWidth, int targetHeight, boolean highquality) 2290 { 2291 Image ret = img; 2292 int w = img.getWidth(null)/2; 2293 int h = img.getHeight(null)/2; 2294 2295 // only do multiple step bilinear for down scale more than two times 2296 if (!highquality || w <=targetWidth || h <=targetHeight) 2297 return ret; 2298 2299 int type = BufferedImage.TYPE_INT_RGB; 2300 if (image instanceof BufferedImage) { 2301 BufferedImage tmp = (BufferedImage)image; 2302 if (tmp.getColorModel().hasAlpha()) 2303 type = BufferedImage.TYPE_INT_ARGB; 2304 } 2305 else { 2306 PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false); 2307 ColorModel cm = pg.getColorModel(); 2308 if (cm!=null && cm.hasAlpha()) 2309 type = BufferedImage.TYPE_INT_ARGB; 2310 } 2311 2312 do { 2313 BufferedImage tmp = new BufferedImage(w, h, type); 2314 Graphics2D g2 = tmp.createGraphics(); 2315 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 2316 g2.drawImage(ret, 0, 0, w, h, null); 2317 g2.dispose(); 2318 ret = tmp; 2319 2320 w /= 2; 2321 if (w < targetWidth) { 2322 w = targetWidth; 2323 } 2324 2325 h /= 2; 2326 if (h < targetHeight) { 2327 h = targetHeight; 2328 } 2329 2330 } while (w != targetWidth || h != targetHeight); 2331 2332 return ret; 2333 } 2334 public void mousePressed(MouseEvent e) { 2335 startPosition = e.getPoint(); 2336 selectedArea.setBounds(startPosition.x, startPosition.y, 0, 0); 2337 scrollDim = imageScroller.getSize(); 2338 hbar = imageScroller.getHorizontalScrollBar(); 2339 vbar = imageScroller.getVerticalScrollBar(); 2340 2341 if ((e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) == InputEvent.SHIFT_DOWN_MASK) { 2342 setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); 2343 } 2344 else { 2345 setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 2346 } 2347 } 2348 2349 public void mouseClicked(MouseEvent e) { 2350 startPosition = e.getPoint(); 2351 selectedArea.setBounds(startPosition.x, startPosition.y, 0, 0); 2352 2353 if (hbar.isVisible()) { 2354 hbar.setValue(startPosition.x - scrollDim.width / 2); 2355 } 2356 2357 if (vbar.isVisible()) { 2358 vbar.setValue(startPosition.y - scrollDim.height / 2); 2359 } 2360 2361 repaint(); 2362 } 2363 2364 public void mouseDragged(MouseEvent e) { 2365 // don't update too often. 2366 try { 2367 Thread.sleep(20); 2368 } 2369 catch (Exception ex) { 2370 log.debug("thread sleep:", ex); 2371 } 2372 currentPosition = e.getPoint(); 2373 2374 if ((e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) == InputEvent.SHIFT_DOWN_MASK) { 2375 int x0 = Math.max(0, Math.min(startPosition.x, 2376 currentPosition.x)); 2377 int y0 = Math.max(0, Math.min(startPosition.y, 2378 currentPosition.y)); 2379 int x1 = Math.min(imageSize.width, Math.max(startPosition.x, 2380 currentPosition.x)); 2381 int y1 = Math.min(imageSize.height, Math.max(startPosition.y, 2382 currentPosition.y)); 2383 2384 int w = x1 - x0; 2385 int h = y1 - y0; 2386 2387 selectedArea.setBounds(x0, y0, w, h); 2388 double ratio = 1.0 / zoomFactor; 2389 2390 originalSelectedArea.setBounds((int) (x0 * ratio), 2391 (int) (y0 * ratio), (int) (w * ratio), 2392 (int) (h * ratio)); 2393 2394 repaint(); 2395 } 2396 else { 2397 if (hbar.isVisible()) { 2398 int dx = startPosition.x - currentPosition.x; 2399 hbar.setValue(hbar.getValue() + dx); 2400 } 2401 2402 if (vbar.isVisible()) { 2403 int dy = startPosition.y - currentPosition.y; 2404 vbar.setValue(vbar.getValue() + dy); 2405 } 2406 } 2407 } 2408 2409 public void mouseReleased(MouseEvent e) { 2410 setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 2411 } 2412 2413 public void mouseEntered(MouseEvent e) { 2414 } 2415 2416 public void mouseExited(MouseEvent e) { 2417 } 2418 2419 public void mouseMoved(MouseEvent e) { 2420 yMousePosition = e.getY(); 2421 showPixelValue(e.getX(), yMousePosition); 2422 } 2423 2424 public void mouseWheelMoved(MouseWheelEvent e) { 2425 JScrollBar jb = imageScroller.getVerticalScrollBar(); 2426 int us = e.getUnitsToScroll(); 2427 int wr = e.getWheelRotation(); 2428 int n = us * jb.getUnitIncrement(); 2429 int y = jb.getValue(); 2430 2431 if (((y <= 0) && (wr < 0)) 2432 || (y + jb.getVisibleAmount() * wr >= zoomFactor 2433 * originalSize.height)) { 2434 return; 2435 } 2436 2437 yMousePosition += n; 2438 jb.setValue(jb.getValue() + n); 2439 2440 showPixelValue(e.getX(), yMousePosition); 2441 } 2442 2443 private void showPixelValue(int x, int y) { 2444 if (!valueField.isVisible() || rotateCount != 0) { 2445 return; 2446 } 2447 2448 if (data == null) { 2449 return; 2450 } 2451 2452 x = (int) (x / zoomFactor); 2453 int w = originalSize.width; 2454 2455 if ((x < 0) || (x >= w)) { 2456 return; // out of image bound 2457 } 2458 2459 y = (int) (y / zoomFactor); 2460 int h = originalSize.height; 2461 if ((y < 0) || (y >= h)) { 2462 return; // out of image bound 2463 } 2464 2465 // transfer location to the original coordinator 2466 if (isHorizontalFlipped) { 2467 x = w - 1 - x; 2468 } 2469 2470 if (isVerticalFlipped) { 2471 y = h - 1 - y; 2472 } 2473 2474 strBuff.setLength(0); // reset the string buffer 2475 strBuff.append("x="); 2476 strBuff.append(x+indexBase); 2477 strBuff.append(", y="); 2478 strBuff.append(y+indexBase); 2479 strBuff.append(", value="); 2480 2481 if (isTrueColor) { 2482 strBuff.append("("); 2483 int i0, i1, i2; 2484 String r, g, b; 2485 2486 if (isPlaneInterlace) { 2487 i0 = y * w + x; // index for the first plane 2488 i1 = i0 + w * h; // index for the second plane 2489 i2 = i0 + 2 * w * h; // index for the third plane 2490 } 2491 else { 2492 i0 = 3 * (y * w + x); // index for the first pixel 2493 i1 = i0 + 1; // index for the second pixel 2494 i2 = i0 + 2; // index for the third pixel 2495 } 2496 2497 if (isUnsigned && !isUnsignedConverted) { 2498 r = String.valueOf(convertUnsignedPoint(i0)); 2499 g = String.valueOf(convertUnsignedPoint(i1)); 2500 b = String.valueOf(convertUnsignedPoint(i2)); 2501 } 2502 else { 2503 r = String.valueOf(Array.get(data, i0)); 2504 g = String.valueOf(Array.get(data, i1)); 2505 b = String.valueOf(Array.get(data, i2)); 2506 } 2507 2508 strBuff.append(r + ", " + g + ", " + b); 2509 strBuff.append(")"); 2510 } // if (isTrueColor) 2511 else { 2512 2513 int idx = y * w + x; 2514 if (!dataset.isDefaultImageOrder()) 2515 idx = x*h+y; 2516 2517 if (isUnsigned && !isUnsignedConverted) { 2518 strBuff.append(convertUnsignedPoint(idx)); 2519 } 2520 else { 2521 strBuff.append(Array.get(data, idx)); 2522 } 2523 } 2524 2525 valueField.setText(strBuff.toString()); 2526 } // private void showPixelValue 2527 2528 private long convertUnsignedPoint(int idx) { 2529 long l = 0; 2530 2531 if (NT == 'B') { 2532 byte b = Array.getByte(data, idx); 2533 2534 if (b < 0) { 2535 l = b + 256; 2536 } 2537 else { 2538 l = b; 2539 } 2540 } 2541 else if (NT == 'S') { 2542 short s = Array.getShort(data, idx); 2543 if (s < 0) { 2544 l = s + 65536; 2545 } 2546 else { 2547 l = s; 2548 } 2549 } 2550 else if (NT == 'I') { 2551 int i = Array.getInt(data, idx); 2552 if (i < 0) { 2553 l = i + 4294967296L; 2554 } 2555 else { 2556 l = i; 2557 } 2558 } 2559 2560 return l; 2561 } 2562 2563 private void selectAll() { 2564 selectedArea.setBounds(0, 0, imageSize.width, imageSize.height); 2565 originalSelectedArea.setBounds(0, 0, originalSize.width, originalSize.height); 2566 2567 repaint(); 2568 } 2569 2570 private void setImageSize(Dimension size) { 2571 imageSize = size; 2572 setPreferredSize(imageSize); 2573 2574 int w = selectedArea.width; 2575 int h = selectedArea.height; 2576 if ((w > 0) && (h > 0)) { 2577 // use fixed selected area to reduce the rounding error 2578 selectedArea.setBounds( 2579 (int) (originalSelectedArea.x * zoomFactor), 2580 (int) (originalSelectedArea.y * zoomFactor), 2581 (int) (originalSelectedArea.width * zoomFactor), 2582 (int) (originalSelectedArea.height * zoomFactor)); 2583 } 2584 2585 repaint(); 2586 } 2587 2588 private void setImage(Image img) { 2589 image = img; 2590 imageSize = new Dimension(image.getWidth(this), image.getHeight(this)); 2591 originalSize = imageSize; 2592 selectedArea.setSize(0, 0); 2593 setPreferredSize(imageSize); 2594 2595 setImageSize(new Dimension((int) (originalSize.width * zoomFactor), 2596 (int) (originalSize.height * zoomFactor))); 2597 2598 repaint(); 2599 } 2600 } // private class ImageComponent extends JComponent 2601 2602 /** 2603 * FlipFilter creates image filter to flip image horizontally or vertically. 2604 */ 2605 private class FlipFilter extends ImageFilter { 2606 /** flip direction */ 2607 private int direction; 2608 2609 /** pixel value */ 2610 private int raster[] = null; 2611 2612 /** width & height */ 2613 private int imageWidth, imageHeight; 2614 2615 /** 2616 * Constructs an image filter to flip horizontally or vertically. 2617 * <p> 2618 * 2619 * @param d 2620 * the flip direction. 2621 */ 2622 private FlipFilter(int d) { 2623 if (d < FLIP_HORIZONTAL) { 2624 d = FLIP_HORIZONTAL; 2625 } 2626 else if (d > FLIP_VERTICAL) { 2627 d = FLIP_VERTICAL; 2628 } 2629 2630 direction = d; 2631 } 2632 2633 @Override 2634 public void setDimensions(int w, int h) { 2635 imageWidth = w; 2636 imageHeight = h; 2637 2638 // specify the raster 2639 if (raster == null) { 2640 raster = new int[imageWidth * imageHeight]; 2641 } 2642 2643 consumer.setDimensions(imageWidth, imageHeight); 2644 } 2645 2646 @Override 2647 public void setPixels(int x, int y, int w, int h, ColorModel model, 2648 byte pixels[], int off, int scansize) { 2649 int srcoff = off; 2650 int dstoff = y * imageWidth + x; 2651 for (int yc = 0; yc < h; yc++) { 2652 for (int xc = 0; xc < w; xc++) { 2653 raster[dstoff++] = model.getRGB(pixels[srcoff++] & 0xff); 2654 } 2655 2656 srcoff += (scansize - w); 2657 dstoff += (imageWidth - w); 2658 } 2659 } 2660 2661 @Override 2662 public void setPixels(int x, int y, int w, int h, ColorModel model, 2663 int pixels[], int off, int scansize) { 2664 int srcoff = off; 2665 int dstoff = y * imageWidth + x; 2666 2667 for (int yc = 0; yc < h; yc++) { 2668 for (int xc = 0; xc < w; xc++) { 2669 raster[dstoff++] = model.getRGB(pixels[srcoff++]); 2670 } 2671 srcoff += (scansize - w); 2672 dstoff += (imageWidth - w); 2673 } 2674 } 2675 2676 @Override 2677 public void imageComplete(int status) { 2678 if ((status == IMAGEERROR) || (status == IMAGEABORTED)) { 2679 consumer.imageComplete(status); 2680 return; 2681 } 2682 2683 int pixels[] = new int[imageWidth]; 2684 for (int y = 0; y < imageHeight; y++) { 2685 if (direction == FLIP_VERTICAL) { 2686 // grab pixel values of the target line ... 2687 int pos = (imageHeight - 1 - y) * imageWidth; 2688 for (int kk = 0; kk < imageWidth; kk++) { 2689 pixels[kk] = raster[pos + kk]; 2690 } 2691 } 2692 else { 2693 int pos = y * imageWidth; 2694 for (int kk = 0; kk < imageWidth; kk++) { 2695 pixels[kk] = raster[pos + kk]; 2696 } 2697 2698 // swap the pixel values of the target line 2699 int hw = imageWidth / 2; 2700 for (int kk = 0; kk < hw; kk++) { 2701 int tmp = pixels[kk]; 2702 pixels[kk] = pixels[imageWidth - kk - 1]; 2703 pixels[imageWidth - kk - 1] = tmp; 2704 } 2705 } 2706 2707 // consumer it .... 2708 consumer.setPixels(0, y, imageWidth, 1, 2709 ColorModel.getRGBdefault(), pixels, 0, imageWidth); 2710 } // for (int y = 0; y < imageHeight; y++) 2711 2712 // complete ? 2713 consumer.imageComplete(status); 2714 } 2715 } // private class FlipFilter extends ImageFilter 2716 2717 /** 2718 * Apply general brightness/contrast algorithm. For details, visit 2719 * http://www.developerfusion.co.uk/ 2720 * 2721 * The general algorithm is represented by: If Brighten = True New_Value = 2722 * Old_Value + Adjustment_Amount Else New_Value = Old_Value - 2723 * Adjustment_Amount If New_Value < Value_Minimum New_Value = Value_Minimum 2724 * If New_Value > Value_Maximum New_Value = Value_Maximum 2725 * 2726 * Contrast is a complicated operation. It is hard to formulate a 2727 * "general algorithm". Here is the closest representation 2728 * (Contrast_Value=[0, 2]): 2729 * 2730 * //Converts to a percent //[0, 1] New_Value = Old_Value / 255 2731 * 2732 * //Centers on 0 instead of .5 //[-.5, .5] New_Value -= 0.5 2733 * 2734 * //Adjusts by Contrast_Value //[-127.5, 127.5], usually [-1, 1] New_Value 2735 * *= Contrast_Value 2736 * 2737 * //Re-add .5 (un-center over 0) //[-127, 128] New_Value += 0.5 2738 * 2739 * //Re-multiply by 255 (un-convert to percent) //[-32385, 32640], usually 2740 * [0, 255] New_Value *= 255 //Clamp [0, 255] If(New_Value > 255) New_Value 2741 * = 255 If(New_Value < 0) New_Value = 0 2742 */ 2743 private class BrightnessFilter extends RGBImageFilter { 2744 // brightness level = [-200, 200] 2745 int brightLevel = 0; 2746 2747 // contrast level [0, 4] 2748 float contrastLevel = 0; 2749 2750 public BrightnessFilter(int blevel, int clevel) { 2751 if (blevel < -100) { 2752 brightLevel = -100; 2753 } 2754 else if (blevel > 100) { 2755 brightLevel = 100; 2756 } 2757 else { 2758 brightLevel = blevel; 2759 } 2760 brightLevel *= 2; 2761 2762 if (clevel < -100) { 2763 clevel = -100; 2764 } 2765 else if (clevel > 100) { 2766 clevel = 100; 2767 } 2768 2769 if (clevel > 0) { 2770 contrastLevel = (clevel / 100f + 1) * 2; 2771 } 2772 else if (clevel < 0) { 2773 contrastLevel = (clevel / 100f + 1) / 2; 2774 } 2775 else { 2776 contrastLevel = 0; 2777 } 2778 2779 canFilterIndexColorModel = true; 2780 } 2781 2782 @Override 2783 public int filterRGB(int x, int y, int rgb) { 2784 // adjust brightness first, then adjust contrast 2785 // it gives more color depth 2786 2787 if (brightLevel != 0) { 2788 int r = (rgb & 0x00ff0000) >> 16; 2789 int g = (rgb & 0x0000ff00) >> 8; 2790 int b = (rgb & 0x000000ff); 2791 2792 r += brightLevel; 2793 g += brightLevel; 2794 b += brightLevel; 2795 2796 if (r < 0) { 2797 r = 0; 2798 } 2799 if (r > 255) { 2800 r = 255; 2801 } 2802 if (g < 0) { 2803 g = 0; 2804 } 2805 if (g > 255) { 2806 g = 255; 2807 } 2808 if (b < 0) { 2809 b = 0; 2810 } 2811 if (b > 255) { 2812 b = 255; 2813 } 2814 2815 r = (r << 16) & 0x00ff0000; 2816 g = (g << 8) & 0x0000ff00; 2817 b = b & 0x000000ff; 2818 2819 rgb = ((rgb & 0xff000000) | r | g | b); 2820 } 2821 2822 if (contrastLevel > 0.000001) { // do not compare float using !=0 or 2823 // ==0 2824 int r = (rgb & 0x00ff0000) >> 16; 2825 int g = (rgb & 0x0000ff00) >> 8; 2826 int b = (rgb & 0x000000ff); 2827 2828 float f = (float) r / 255f; 2829 f -= 0.5; 2830 f *= contrastLevel; 2831 f += 0.5; 2832 f *= 255f; 2833 if (f < 0) { 2834 f = 0; 2835 } 2836 if (f > 255) { 2837 f = 255; 2838 } 2839 r = (int) f; 2840 2841 f = (float) g / 255f; 2842 f -= 0.5; 2843 f *= contrastLevel; 2844 f += 0.5; 2845 f *= 255f; 2846 if (f < 0) { 2847 f = 0; 2848 } 2849 if (f > 255) { 2850 f = 255; 2851 } 2852 g = (int) f; 2853 2854 f = (float) b / 255f; 2855 f -= 0.5; 2856 f *= contrastLevel; 2857 f += 0.5; 2858 f *= 255f; 2859 if (f < 0) { 2860 f = 0; 2861 } 2862 if (f > 255) { 2863 f = 255; 2864 } 2865 b = (int) f; 2866 2867 r = (r << 16) & 0x00ff0000; 2868 g = (g << 8) & 0x0000ff00; 2869 b = b & 0x000000ff; 2870 2871 rgb = ((rgb & 0xff000000) | r | g | b); 2872 } 2873 2874 return rgb; 2875 } 2876 } 2877 2878 /** 2879 * Makes an image filter for contour. 2880 */ 2881 private class ContourFilter extends ImageFilter { 2882 // default color model 2883 private ColorModel defaultRGB; 2884 2885 // contour level 2886 int level; 2887 2888 // the table of the contour levels 2889 int levels[]; 2890 2891 // colors for drawable contour line 2892 int[] levelColors; 2893 2894 // default RGB 2895 2896 // pixel value 2897 private int raster[] = null; 2898 2899 // width & height 2900 private int imageWidth, imageHeight; 2901 2902 /** 2903 * Create an contour filter for a given level contouring. 2904 * 2905 * @param theLevel 2906 * the contour level. 2907 */ 2908 private ContourFilter(int theLevel) { 2909 defaultRGB = ColorModel.getRGBdefault(); 2910 2911 levelColors = new int[9]; 2912 levelColors[0] = Color.red.getRGB(); 2913 levelColors[1] = Color.green.getRGB(); 2914 levelColors[2] = Color.blue.getRGB(); 2915 levelColors[3] = Color.magenta.getRGB(); 2916 levelColors[4] = Color.orange.getRGB(); 2917 levelColors[5] = Color.cyan.getRGB(); 2918 levelColors[6] = Color.black.getRGB(); 2919 levelColors[7] = Color.pink.getRGB(); 2920 levelColors[8] = Color.yellow.getRGB(); 2921 2922 2923 if (theLevel < 1) { 2924 theLevel = 1; 2925 } 2926 else if (theLevel > 9) { 2927 theLevel = 9; 2928 } 2929 2930 level = theLevel; 2931 levels = new int[level]; 2932 2933 int dx = 128 / level; 2934 for (int i = 0; i < level; i++) { 2935 levels[i] = (i + 1) * dx; 2936 } 2937 } 2938 2939 @Override 2940 public void setDimensions(int width, int height) { 2941 this.imageWidth = width; 2942 this.imageHeight = height; 2943 2944 // specify the raster 2945 if (raster == null) { 2946 raster = new int[imageWidth * imageHeight]; 2947 } 2948 2949 consumer.setDimensions(width, height); 2950 } 2951 2952 @Override 2953 public void setPixels(int x, int y, int w, int h, ColorModel model, 2954 byte pixels[], int off, int scansize) { 2955 int rgb = 0; 2956 int srcoff = off; 2957 int dstoff = y * imageWidth + x; 2958 2959 for (int yc = 0; yc < h; yc++) { 2960 for (int xc = 0; xc < w; xc++) { 2961 rgb = model.getRGB(pixels[srcoff++] & 0xff); 2962 raster[dstoff++] = (((rgb >> 16) & 0xff) 2963 + ((rgb >> 8) & 0xff) + (rgb & 0xff)) / 3; 2964 } 2965 srcoff += (scansize - w); 2966 dstoff += (imageWidth - w); 2967 } 2968 2969 } 2970 2971 @Override 2972 public void setPixels(int x, int y, int w, int h, ColorModel model, 2973 int pixels[], int off, int scansize) { 2974 int rgb = 0; 2975 int srcoff = off; 2976 int dstoff = y * imageWidth + x; 2977 2978 for (int yc = 0; yc < h; yc++) { 2979 for (int xc = 0; xc < w; xc++) { 2980 rgb = model.getRGB(pixels[srcoff++] & 0xff); 2981 raster[dstoff++] = (((rgb >> 16) & 0xff) 2982 + ((rgb >> 8) & 0xff) + (rgb & 0xff)) / 3; 2983 } 2984 2985 srcoff += (scansize - w); 2986 dstoff += (imageWidth - w); 2987 } 2988 } 2989 2990 @Override 2991 public void imageComplete(int status) { 2992 if ((status == IMAGEERROR) || (status == IMAGEABORTED)) { 2993 consumer.imageComplete(status); 2994 return; 2995 } 2996 2997 int pixels[] = new int[imageWidth * imageHeight]; 2998 for (int z = 0; z < levels.length; z++) { 2999 int currentLevel = levels[z]; 3000 int color = levelColors[z]; 3001 3002 setContourLine(raster, pixels, currentLevel, color, imageWidth, 3003 imageHeight); 3004 } 3005 3006 int line[] = new int[imageWidth]; 3007 for (int y = 0; y < imageHeight; y++) { 3008 for (int x = 0; x < imageWidth; x++) { 3009 line[x] = pixels[y * imageWidth + x]; 3010 } 3011 3012 consumer.setPixels(0, y, imageWidth, 1, defaultRGB, line, 0, 3013 imageWidth); 3014 } // for (int y = 0; y < imageHeight; y++) { 3015 3016 // complete ? 3017 consumer.imageComplete(status); 3018 } 3019 3020 /** 3021 * draw a contour line based on the current parameter---level, color 3022 * 3023 * @param raster 3024 * the data of the raster image. 3025 * @param pixels 3026 * the pixel value of the image. 3027 * @param level 3028 * the contour level. 3029 * @param color 3030 * the color of the contour line. 3031 * @param w 3032 * the width of the image. 3033 * @param h 3034 * the height of the image. 3035 */ 3036 private void setContourLine(int[] raster, int[] pixels, int level, 3037 int color, int w, int h) { 3038 int p = 0; // entrance point 3039 int q = p + (w * h - 1); // bottom right point 3040 int u = 0 + (w - 1); // top right point 3041 3042 // first round 3043 while (true) { 3044 while (p < u) { 3045 int rgb = raster[p]; 3046 if (rgb < level) { 3047 while ((raster[p] < level) && (p < u)) { 3048 p++; 3049 } 3050 if (raster[p] >= level) { 3051 pixels[p] = color; 3052 } 3053 } 3054 else if (rgb == level) { 3055 while ((raster[p] == level) && (p < u)) { 3056 p++; 3057 } 3058 if ((raster[p] < level) || (raster[p] > level)) { 3059 pixels[p] = color; 3060 } 3061 } 3062 else { 3063 while ((raster[p] > level) && (p < u)) { 3064 p++; 3065 } 3066 if ((raster[p] <= level)) { 3067 pixels[p] = color; 3068 } 3069 } 3070 } 3071 3072 if (u == q) { 3073 break; 3074 } 3075 else { 3076 u += w; 3077 p++; 3078 } 3079 } 3080 } 3081 3082 } // private class ContourFilter extends ImageFilter 3083 3084 private class Rotate90Filter extends ImageFilter { 3085 private ColorModel defaultRGB = ColorModel.getRGBdefault(); 3086 3087 private double coord[] = new double[2]; 3088 3089 private int raster[]; 3090 private int xoffset, yoffset; 3091 private int srcW, srcH; 3092 private int dstW, dstH; 3093 private int direction; 3094 3095 public Rotate90Filter(int dir) { 3096 direction = dir; 3097 } 3098 3099 public void transform(double x, double y, double[] retcoord) { 3100 if (direction == ROTATE_CW_90) { 3101 retcoord[0] = -y; 3102 retcoord[1] = x; 3103 } 3104 else { 3105 retcoord[0] = y; 3106 retcoord[1] = -x; 3107 } 3108 } 3109 3110 public void itransform(double x, double y, double[] retcoord) { 3111 if (direction == ROTATE_CCW_90) { 3112 retcoord[0] = -y; 3113 retcoord[1] = x; 3114 } 3115 else { 3116 retcoord[0] = y; 3117 retcoord[1] = -x; 3118 } 3119 } 3120 3121 public void transformBBox(Rectangle rect) { 3122 double minx = Double.POSITIVE_INFINITY; 3123 double miny = Double.POSITIVE_INFINITY; 3124 double maxx = Double.NEGATIVE_INFINITY; 3125 double maxy = Double.NEGATIVE_INFINITY; 3126 for (int y = 0; y <= 1; y++) { 3127 for (int x = 0; x <= 1; x++) { 3128 transform(rect.x + x * rect.width, 3129 rect.y + y * rect.height, coord); 3130 minx = Math.min(minx, coord[0]); 3131 miny = Math.min(miny, coord[1]); 3132 maxx = Math.max(maxx, coord[0]); 3133 maxy = Math.max(maxy, coord[1]); 3134 } 3135 } 3136 rect.x = (int) Math.floor(minx); 3137 rect.y = (int) Math.floor(miny); 3138 rect.width = (int) Math.ceil(maxx) - rect.x; 3139 rect.height = (int) Math.ceil(maxy) - rect.y; 3140 } 3141 3142 @Override 3143 public void setDimensions(int width, int height) { 3144 Rectangle rect = new Rectangle(0, 0, width, height); 3145 transformBBox(rect); 3146 xoffset = -rect.x; 3147 yoffset = -rect.y; 3148 srcW = width; 3149 srcH = height; 3150 dstW = rect.width; 3151 dstH = rect.height; 3152 raster = new int[srcW * srcH]; 3153 consumer.setDimensions(dstW, dstH); 3154 } 3155 3156 @Override 3157 public void setProperties(Hashtable props) { 3158 props = (Hashtable) props.clone(); 3159 Object o = props.get("filters"); 3160 if (o == null) { 3161 props.put("filters", toString()); 3162 } 3163 else if (o instanceof String) { 3164 props.put("filters", ((String) o) + toString()); 3165 } 3166 consumer.setProperties(props); 3167 } 3168 3169 @Override 3170 public void setColorModel(ColorModel model) { 3171 consumer.setColorModel(defaultRGB); 3172 } 3173 3174 @Override 3175 public void setHints(int hintflags) { 3176 consumer.setHints(TOPDOWNLEFTRIGHT | COMPLETESCANLINES | SINGLEPASS 3177 | (hintflags & SINGLEFRAME)); 3178 } 3179 3180 @Override 3181 public void setPixels(int x, int y, int w, int h, ColorModel model, 3182 byte pixels[], int off, int scansize) { 3183 int srcoff = off; 3184 int dstoff = y * srcW + x; 3185 for (int yc = 0; yc < h; yc++) { 3186 for (int xc = 0; xc < w; xc++) { 3187 raster[dstoff++] = model.getRGB(pixels[srcoff++] & 0xff); 3188 } 3189 srcoff += (scansize - w); 3190 dstoff += (srcW - w); 3191 } 3192 } 3193 3194 @Override 3195 public void setPixels(int x, int y, int w, int h, ColorModel model, 3196 int pixels[], int off, int scansize) { 3197 int srcoff = off; 3198 int dstoff = y * srcW + x; 3199 if (model == defaultRGB) { 3200 for (int yc = 0; yc < h; yc++) { 3201 System.arraycopy(pixels, srcoff, raster, dstoff, w); 3202 srcoff += scansize; 3203 dstoff += srcW; 3204 } 3205 } 3206 else { 3207 for (int yc = 0; yc < h; yc++) { 3208 for (int xc = 0; xc < w; xc++) { 3209 raster[dstoff++] = model.getRGB(pixels[srcoff++]); 3210 } 3211 srcoff += (scansize - w); 3212 dstoff += (srcW - w); 3213 } 3214 } 3215 } 3216 3217 @Override 3218 public void imageComplete(int status) { 3219 if ((status == IMAGEERROR) || (status == IMAGEABORTED)) { 3220 consumer.imageComplete(status); 3221 return; 3222 } 3223 int pixels[] = new int[dstW]; 3224 for (int dy = 0; dy < dstH; dy++) { 3225 itransform(0 - xoffset, dy - yoffset, coord); 3226 double x1 = coord[0]; 3227 double y1 = coord[1]; 3228 itransform(dstW - xoffset, dy - yoffset, coord); 3229 double x2 = coord[0]; 3230 double y2 = coord[1]; 3231 double xinc = (x2 - x1) / dstW; 3232 double yinc = (y2 - y1) / dstW; 3233 for (int dx = 0; dx < dstW; dx++) { 3234 int sx = (int) Math.round(x1); 3235 int sy = (int) Math.round(y1); 3236 if ((sx < 0) || (sy < 0) || (sx >= srcW) || (sy >= srcH)) { 3237 pixels[dx] = 0; 3238 } 3239 else { 3240 pixels[dx] = raster[sy * srcW + sx]; 3241 } 3242 x1 += xinc; 3243 y1 += yinc; 3244 } 3245 consumer.setPixels(0, dy, dstW, 1, defaultRGB, pixels, 0, dstW); 3246 } 3247 consumer.imageComplete(status); 3248 } 3249 } // private class RotateFilter 3250 3251 /** 3252 * Makes animation for 3D images. 3253 */ 3254 private class Animation extends JDialog implements ActionListener, Runnable { 3255 private static final long serialVersionUID = 6717628496771098250L; 3256 3257 private final int MAX_ANIMATION_IMAGE_SIZE = 300; 3258 3259 private Image[] frames = null; // a list of images for animation 3260 private JComponent canvas = null; // canvas to draw the image 3261 private Thread engine = null; // Thread animating the images 3262 private int numberOfImages = 0; 3263 private int currentFrame = 0; 3264 private int sleepTime = 200; 3265 private Image offScrImage; // Offscreen image 3266 private Graphics offScrGC; // Offscreen graphics context 3267 private JFrame owner; 3268 private int x0 = 0, y0 = 0; // offset of the image drawing 3269 3270 public Animation(JFrame theOwner, ScalarDS dataset) { 3271 super(theOwner, "Animation", true); 3272 owner = theOwner; 3273 setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE); 3274 3275 long[] dims = dataset.getDims(); 3276 long[] stride = dataset.getStride(); 3277 long[] start = dataset.getStartDims(); 3278 long[] selected = dataset.getSelectedDims(); 3279 int[] selectedIndex = dataset.getSelectedIndex(); 3280 int rank = dataset.getRank(); 3281 if (animationSpeed != 0) { 3282 sleepTime = 1000 / animationSpeed; 3283 } 3284 3285 // back up the start and selected size 3286 long[] tstart = new long[rank]; 3287 long[] tselected = new long[rank]; 3288 long[] tstride = new long[rank]; 3289 System.arraycopy(start, 0, tstart, 0, rank); 3290 System.arraycopy(selected, 0, tselected, 0, rank); 3291 System.arraycopy(stride, 0, tstride, 0, rank); 3292 3293 int stride_n = 1; 3294 int max_size = (int) Math.max(selected[selectedIndex[0]], 3295 selected[selectedIndex[1]]); 3296 if (max_size > MAX_ANIMATION_IMAGE_SIZE) { 3297 stride_n = (int)( (double)max_size / (double)MAX_ANIMATION_IMAGE_SIZE +0.5); 3298 } 3299 3300 start[selectedIndex[0]] = 0; 3301 start[selectedIndex[1]] = 0; 3302 start[selectedIndex[2]] = 0; 3303 selected[selectedIndex[0]] = dims[selectedIndex[0]] / stride_n; 3304 selected[selectedIndex[1]] = dims[selectedIndex[1]] / stride_n; 3305 selected[selectedIndex[2]] = 1; 3306 stride[selectedIndex[0]] = stride_n; 3307 stride[selectedIndex[1]] = stride_n; 3308 stride[selectedIndex[2]] = 1; 3309 3310 Object data3d = null; 3311 byte[] byteData = null; 3312 int h = (int) selected[selectedIndex[0]]; 3313 int w = (int) selected[selectedIndex[1]]; 3314 int size = w * h; 3315 3316 numberOfImages = (int) dims[selectedIndex[2]]; 3317 frames = new Image[numberOfImages]; 3318 BufferedImage mir = bufferedImage; 3319 try { 3320 for (int i = 0; i < numberOfImages; i++) { 3321 bufferedImage = null; // each animation image has its 3322 // own image resource 3323 start[selectedIndex[2]] = i; 3324 3325 dataset.clearData(); 3326 try { 3327 data3d = dataset.read(); 3328 } 3329 catch (Throwable err) { 3330 continue; 3331 } 3332 3333 byteData = new byte[size]; 3334 3335 byteData=Tools.getBytes(data3d, dataRange, w, h, false, dataset.getFilteredImageValues(), 3336 true, byteData); 3337 3338 frames[i] = createIndexedImage(byteData, imagePalette, w, h); 3339 } 3340 } 3341 finally { 3342 // set back to original state 3343 bufferedImage = mir; 3344 System.arraycopy(tstart, 0, start, 0, rank); 3345 System.arraycopy(tselected, 0, selected, 0, rank); 3346 System.arraycopy(tstride, 0, stride, 0, rank); 3347 } 3348 3349 offScrImage = owner.createImage(w, h); 3350 offScrGC = offScrImage.getGraphics(); 3351 x0 = Math.max((MAX_ANIMATION_IMAGE_SIZE - w) / 2, 0); 3352 y0 = Math.max((MAX_ANIMATION_IMAGE_SIZE - h) / 2, 0); 3353 3354 canvas = new JComponent() { 3355 private static final long serialVersionUID = -6828735330511795835L; 3356 3357 public void paint(Graphics g) { 3358 g.clearRect(0, 0, MAX_ANIMATION_IMAGE_SIZE, 3359 MAX_ANIMATION_IMAGE_SIZE); 3360 3361 if ((offScrGC == null) || (frames == null)) { 3362 return; 3363 } 3364 3365 offScrGC.drawImage(frames[currentFrame], 0, 0, owner); 3366 g.drawImage(offScrImage, x0, y0, owner); 3367 } 3368 }; 3369 3370 JPanel contentPane = (JPanel) getContentPane(); 3371 contentPane.setPreferredSize(new Dimension( 3372 MAX_ANIMATION_IMAGE_SIZE, MAX_ANIMATION_IMAGE_SIZE)); 3373 contentPane.setLayout(new BorderLayout()); 3374 JButton b = new JButton("Close"); 3375 b.setActionCommand("Close animation"); 3376 b.addActionListener(this); 3377 contentPane.add(b, BorderLayout.SOUTH); 3378 3379 contentPane.add(canvas, BorderLayout.CENTER); 3380 3381 start(); 3382 3383 Point l = getParent().getLocation(); 3384 l.x += 300; 3385 l.y += 200; 3386 setLocation(l); 3387 3388 pack(); 3389 setVisible(true); 3390 } 3391 3392 public void actionPerformed(ActionEvent e) { 3393 Object source = e.getSource(); 3394 String cmd = e.getActionCommand(); 3395 3396 if (cmd.equals("Close animation")) { 3397 dispose(); // terminate the animation 3398 } 3399 } 3400 3401 public void dispose() { 3402 engine = null; 3403 frames = null; 3404 super.dispose(); 3405 } 3406 3407 /** 3408 * No need to clear anything; just paint. 3409 */ 3410 public void update(Graphics g) { 3411 paint(g); 3412 } 3413 3414 /** 3415 * Paint the current frame 3416 */ 3417 public void paint(Graphics g) { 3418 canvas.paint(g); 3419 } 3420 3421 /** 3422 * Start the applet by forking an animation thread. 3423 */ 3424 private void start() { 3425 engine = new Thread(this); 3426 engine.start(); 3427 } 3428 3429 /** 3430 * Run the animation. This method is called by class Thread. 3431 * 3432 * @see java.lang.Thread 3433 */ 3434 public void run() { 3435 Thread me = Thread.currentThread(); 3436 3437 if ((frames == null) || (canvas == null)) { 3438 return; 3439 } 3440 3441 while (me == engine) { 3442 if (++currentFrame >= numberOfImages) 3443 currentFrame = 0; 3444 repaint(); 3445 this.getToolkit().sync(); // Force it to be drawn *now*. 3446 try { 3447 Thread.sleep(sleepTime); 3448 } 3449 catch (InterruptedException e) { 3450 log.debug("Thread.sleep({}):", sleepTime, e); 3451 } 3452 } 3453 } // public void run() { 3454 } // private class Animation extends JDialog 3455 3456 private class DataRangeDialog extends JDialog implements ActionListener, 3457 ChangeListener, PropertyChangeListener { 3458 final int NTICKS = 10; 3459 double tickRatio = 1; 3460 final int W = 500, H = 400; 3461 double[] minmax_current = {0, 0}; 3462 double min, max, min_org, max_org; 3463 final double[] minmax_previous = {0, 0}; 3464 final double[] minmax_dist = {0,0}; 3465 JSlider minSlider, maxSlider; 3466 JFormattedTextField minField, maxField; 3467 3468 public DataRangeDialog(JFrame theOwner, double[] minmaxCurrent, 3469 double[] minmaxOriginal, final int[] dataDist) 3470 { 3471 super(theOwner, "Image Value Range", true); 3472 3473 Tools.findMinMax(dataDist, minmax_dist, null); 3474 3475 if ((minmaxCurrent == null) || (minmaxCurrent.length <= 1)) { 3476 minmax_current[0] = 0; 3477 minmax_current[1] = 255; 3478 } 3479 else { 3480 if (minmaxCurrent[0] == minmaxCurrent[1]) { 3481 Tools.findMinMax(data, minmaxCurrent, dataset.getFillValue()); 3482 } 3483 3484 minmax_current[0] = minmaxCurrent[0]; 3485 minmax_current[1] = minmaxCurrent[1]; 3486 } 3487 3488 minmax_previous[0] = min = minmax_current[0]; 3489 minmax_previous[1] = max = minmax_current[1]; 3490 min_org = originalRange[0]; 3491 max_org = originalRange[1]; 3492 3493 tickRatio = (max_org-min_org)/(double)NTICKS; 3494 3495 final DecimalFormat numberFormat = new DecimalFormat("#.##E0"); 3496 NumberFormatter formatter = new NumberFormatter(numberFormat); 3497 formatter.setMinimum(new Double(min)); 3498 formatter.setMaximum(new Double(max)); 3499 3500 minField = new JFormattedTextField(formatter); 3501 minField.addPropertyChangeListener(this); 3502 minField.setValue(new Double(min)); 3503 maxField = new JFormattedTextField(formatter); 3504 maxField.addPropertyChangeListener(this); 3505 maxField.setValue(new Double(max)); 3506 3507 minSlider = new JSlider(JSlider.HORIZONTAL, 0, NTICKS, 0); 3508 minSlider.setMajorTickSpacing(1); 3509 minSlider.setPaintTicks(true); 3510 minSlider.setPaintLabels(false); 3511 minSlider.addChangeListener(this); 3512 minSlider.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); 3513 3514 maxSlider = new JSlider(JSlider.HORIZONTAL, 0, NTICKS, NTICKS); 3515 maxSlider.setMajorTickSpacing(1); 3516 maxSlider.setPaintTicks(true); 3517 maxSlider.setPaintLabels(false); 3518 maxSlider.addChangeListener(this); 3519 maxSlider.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); 3520 3521 JPanel contentPane = (JPanel) getContentPane(); 3522 contentPane.setLayout(new BorderLayout(5, 5)); 3523 contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 3524 contentPane.setPreferredSize(new Dimension(W, H)); 3525 3526 JPanel minPane = new JPanel(); 3527 minPane.setBorder(new TitledBorder("Lower Bound")); 3528 minPane.setLayout(new BorderLayout()); 3529 minPane.add(minField, BorderLayout.CENTER); 3530 minPane.add(minSlider, BorderLayout.SOUTH); 3531 3532 JPanel maxPane = new JPanel(); 3533 maxPane.setBorder(new TitledBorder("Upper Bound")); 3534 maxPane.setLayout(new BorderLayout()); 3535 maxPane.add(maxField, BorderLayout.CENTER); 3536 maxPane.add(maxSlider, BorderLayout.SOUTH); 3537 3538 JPanel chartPane = new JPanel() { 3539 int numberOfPoints = dataDist.length; 3540 int gap = 5; 3541 int xgap = 2 * gap; 3542 double xmin = originalRange[0]; 3543 double xmax = originalRange[1]; 3544 3545 public void paint(Graphics g) { 3546 int h = H/3 -50; 3547 int w = W; 3548 int xnpoints = Math.min(10, numberOfPoints - 1); 3549 3550 // draw the X axis 3551 g.drawLine(xgap, h, w + xgap, h); 3552 3553 // draw x labels 3554 double xp = 0, x = xmin; 3555 double dw = (double) w / (double) xnpoints; 3556 double dx = (xmax - xmin) / xnpoints; 3557 for (int i = 0; i <= xnpoints; i++) { 3558 x = xmin + i * dx; 3559 xp = xgap + i * dw; 3560 g.drawLine((int) xp, h, (int) xp, h - 5); 3561 g.drawString(numberFormat.format(x), (int) xp - 5, h + 20); 3562 } 3563 3564 Color c = g.getColor(); 3565 double yp, ymin=minmax_dist[0], dy=minmax_dist[1]-minmax_dist[0]; 3566 if (dy<=0) 3567 dy =1; 3568 3569 xp = xgap; 3570 yp = 0; 3571 g.setColor(Color.blue); 3572 int barWidth = w / numberOfPoints; 3573 if (barWidth <= 0) { 3574 barWidth = 1; 3575 } 3576 dw = (double) w / (double) numberOfPoints; 3577 3578 for (int j = 0; j < numberOfPoints; j++) { 3579 xp = xgap + j * dw; 3580 yp = (int) (h * (dataDist[j] - ymin) / dy); 3581 g.fillRect((int) xp, (int) (h - yp), barWidth, (int) yp); 3582 } 3583 3584 g.setColor(c); // set the color back to its default 3585 } // public void paint(Graphics g) 3586 } ; 3587 3588 JPanel mainPane = new JPanel(); 3589 mainPane.setLayout(new GridLayout(3, 1, 5, 5)); 3590 mainPane.add(chartPane); 3591 mainPane.add(minPane); 3592 mainPane.add(maxPane); 3593 contentPane.add(mainPane, BorderLayout.CENTER); 3594 3595 // add OK and CANCEL buttons 3596 JPanel confirmP = new JPanel(); 3597 JButton button = new JButton(" Ok "); 3598 button.setMnemonic(KeyEvent.VK_O); 3599 button.setActionCommand("Ok"); 3600 button.addActionListener(this); 3601 confirmP.add(button); 3602 button = new JButton("Cancel"); 3603 button.setMnemonic(KeyEvent.VK_C); 3604 button.setActionCommand("Cancel"); 3605 button.addActionListener(this); 3606 confirmP.add(button); 3607 button = new JButton("Apply"); 3608 button.setMnemonic(KeyEvent.VK_A); 3609 button.setActionCommand("Apply"); 3610 button.addActionListener(this); 3611 confirmP.add(button); 3612 contentPane.add(confirmP, BorderLayout.SOUTH); 3613 contentPane.add(new JLabel(" "), BorderLayout.NORTH); 3614 3615 if (min==max) { 3616 minSlider.setEnabled(false); 3617 maxSlider.setEnabled(false); 3618 } 3619 3620 Point l = getParent().getLocation(); 3621 Dimension d = getParent().getPreferredSize(); 3622 l.x += 300; 3623 l.y += 200; 3624 setLocation(l); 3625 pack(); 3626 setVisible(true); 3627 } 3628 3629 public void actionPerformed(ActionEvent e) { 3630 String cmd = e.getActionCommand(); 3631 3632 if (cmd.equals("Ok")) { 3633 minmax_current[0] = ((Number) minField.getValue()).doubleValue(); 3634 minmax_current[1] = ((Number) maxField.getValue()).doubleValue(); 3635 3636 this.dispose(); 3637 } 3638 if (cmd.equals("Apply")) { 3639 minmax_previous[0] = minmax_current[0]; 3640 minmax_previous[1] = minmax_current[1]; 3641 3642 minmax_current[0] = ((Number) minField.getValue()).doubleValue(); 3643 minmax_current[1] = ((Number) maxField.getValue()).doubleValue(); 3644 3645 applyDataRange(minmax_current); 3646 minmax_current[0] = minmax_current[1] = 0; 3647 } 3648 else if (cmd.equals("Cancel")) { 3649 3650 minmax_current[0] = minmax_previous[0]; 3651 minmax_current[1] = minmax_previous[1]; 3652 3653 applyDataRange(minmax_previous); 3654 3655 this.dispose(); 3656 } 3657 } 3658 3659 /** Listen to the slider. */ 3660 public void stateChanged(ChangeEvent e) { 3661 Object source = e.getSource(); 3662 3663 if (!(source instanceof JSlider)) { 3664 return; 3665 } 3666 3667 JSlider slider = (JSlider) source; 3668 if (!slider.isEnabled()) 3669 return; 3670 3671 double value = slider.getValue(); 3672 if (slider.equals(minSlider)) { 3673 double maxValue = maxSlider.getValue(); 3674 if (value > maxValue) { 3675 value = maxValue; 3676 slider.setValue((int)value); 3677 } 3678 3679 minField.setValue(new Double(value*tickRatio+min_org)); 3680 } 3681 else if (slider.equals(maxSlider)) { 3682 double minValue = minSlider.getValue(); 3683 if (value < minValue) { 3684 value = minValue; 3685 slider.setValue((int)value); 3686 } 3687 maxField.setValue(new Double(value*tickRatio+min_org)); 3688 } 3689 } 3690 3691 /** 3692 * Listen to the text field. This method detects when the value of the 3693 * text field changes. 3694 */ 3695 public void propertyChange(PropertyChangeEvent e) { 3696 Object source = e.getSource(); 3697 if ("value".equals(e.getPropertyName())) { 3698 Number num = (Number) e.getNewValue(); 3699 if (num == null) { 3700 return; 3701 } 3702 double value = num.doubleValue(); 3703 3704 if (source.equals(minField) && (minSlider != null) && minSlider.isEnabled()) { 3705 if (value > max_org) { 3706 value = max_org; 3707 minField.setText(String.valueOf(value)); 3708 } 3709 3710 minSlider.setValue((int) ((value-min_org)/tickRatio)); 3711 } 3712 else if (source.equals(maxField) && (maxSlider != null) && minSlider.isEnabled()) { 3713 if (value < min_org) { 3714 value = min_org; 3715 maxField.setText(String.valueOf(value)); 3716 } 3717 //minmax[1] = value; 3718 maxSlider.setValue((int) ((value-min_org)/tickRatio)); 3719 } 3720 } 3721 } 3722 3723 public double[] getRange() { 3724 return minmax_current; 3725 } 3726 } // private class DataRangeDialog extends JDialog implements ActionListener 3727 3728 private class ContrastSlider extends JDialog implements 3729 ActionListener, ChangeListener, PropertyChangeListener { 3730 private static final long serialVersionUID = -3002524363351111565L; 3731 JSlider brightSlider, contrastSlider; 3732 JFormattedTextField brightField, contrastField; 3733 ImageProducer imageProducer; 3734 double[] autoGainBias = {0, 0}; 3735 int bLevel=0, cLevel=0; 3736 3737 public ContrastSlider(JFrame theOwner, ImageProducer producer) 3738 { 3739 super(theOwner, "Brightness/Contrast", true); 3740 String bLabel = "Brightness", cLabel="Contrast"; 3741 3742 imageProducer = producer; 3743 3744 if (doAutoGainContrast && gainBias!= null) { 3745 bLabel = "Bias"; 3746 cLabel="Gain"; 3747 this.setTitle(bLabel+"/"+cLabel); 3748 } 3749 3750 java.text.NumberFormat numberFormat = java.text.NumberFormat 3751 .getNumberInstance(); 3752 NumberFormatter formatter = new NumberFormatter(numberFormat); 3753 3754 formatter.setMinimum(new Integer(-100)); 3755 formatter.setMaximum(new Integer(100)); 3756 brightField = new JFormattedTextField(formatter); 3757 brightField.addPropertyChangeListener(this); 3758 brightField.setValue(new Integer(0)); 3759 3760 brightSlider = new JSlider(JSlider.HORIZONTAL, -100, 100, 0); 3761 brightSlider.setMajorTickSpacing(20); 3762 brightSlider.setPaintTicks(true); 3763 brightSlider.setPaintLabels(true); 3764 brightSlider.addChangeListener(this); 3765 brightSlider 3766 .setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); 3767 3768 formatter = new NumberFormatter(numberFormat); 3769 formatter.setMinimum(new Integer(-100)); 3770 formatter.setMaximum(new Integer(100)); 3771 contrastField = new JFormattedTextField(formatter); 3772 contrastField.addPropertyChangeListener(this); 3773 contrastField.setValue(new Integer(0)); 3774 3775 contrastSlider = new JSlider(JSlider.HORIZONTAL, -100, 100, 0); 3776 contrastSlider.setMajorTickSpacing(20); 3777 contrastSlider.setPaintTicks(true); 3778 contrastSlider.setPaintLabels(true); 3779 contrastSlider.addChangeListener(this); 3780 contrastSlider.setBorder(BorderFactory.createEmptyBorder(0, 0, 10,0)); 3781 3782 JPanel contentPane = (JPanel) getContentPane(); 3783 contentPane.setLayout(new BorderLayout(5, 5)); 3784 contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 3785 contentPane.setPreferredSize(new Dimension(500, 300)); 3786 3787 JPanel brightPane = new JPanel(); 3788 brightPane.setBorder(new TitledBorder(bLabel+"%")); 3789 brightPane.setLayout(new BorderLayout()); 3790 brightPane.add(brightField, BorderLayout.NORTH); 3791 brightPane.add(brightSlider, BorderLayout.CENTER); 3792 3793 JPanel contrastPane = new JPanel(); 3794 contrastPane.setBorder(new TitledBorder(cLabel+"%")); 3795 contrastPane.setLayout(new BorderLayout()); 3796 contrastPane.add(contrastField, BorderLayout.NORTH); 3797 contrastPane.add(contrastSlider, BorderLayout.CENTER); 3798 3799 JPanel mainPane = new JPanel(); 3800 mainPane.setLayout(new GridLayout(2, 1, 5, 5)); 3801 mainPane.add(brightPane); 3802 mainPane.add(contrastPane); 3803 contentPane.add(mainPane, BorderLayout.CENTER); 3804 3805 // add OK and CANCEL buttons 3806 JPanel confirmP = new JPanel(); 3807 JButton button = new JButton(" Ok "); 3808 button.setMnemonic(KeyEvent.VK_O); 3809 button.setActionCommand("Ok_brightness_change"); 3810 button.addActionListener(this); 3811 confirmP.add(button); 3812 button = new JButton("Cancel"); 3813 button.setMnemonic(KeyEvent.VK_C); 3814 button.setActionCommand("Cancel_brightness_change"); 3815 button.addActionListener(this); 3816 confirmP.add(button); 3817 3818 button = new JButton("Apply"); 3819 button.setMnemonic(KeyEvent.VK_A); 3820 button.setActionCommand("Apply_brightness_change"); 3821 button.addActionListener(this); 3822 confirmP.add(button); 3823 3824 contentPane.add(confirmP, BorderLayout.SOUTH); 3825 contentPane.add(new JLabel(" "), BorderLayout.NORTH); 3826 3827 Point l = getParent().getLocation(); 3828 Dimension d = getParent().getPreferredSize(); 3829 l.x += 300; 3830 l.y += 200; 3831 setLocation(l); 3832 pack(); 3833 } 3834 3835 public void actionPerformed(ActionEvent e) { 3836 String cmd = e.getActionCommand(); 3837 3838 if (cmd.equals("Ok_brightness_change") 3839 || cmd.equals("Apply_brightness_change")) { 3840 int b = ((Number) brightField.getValue()).intValue(); 3841 int c = ((Number) contrastField.getValue()).intValue(); 3842 3843 applyBrightContrast(b, c); 3844 3845 if (cmd.startsWith("Ok")) { 3846 bLevel = b; 3847 cLevel = c; 3848 setVisible(false); 3849 } 3850 } 3851 else if (cmd.equals("Cancel_brightness_change")) { 3852 applyBrightContrast(bLevel, cLevel); 3853 setVisible(false); 3854 } 3855 } 3856 3857 /** Listen to the slider. */ 3858 public void stateChanged(ChangeEvent e) { 3859 Object source = e.getSource(); 3860 3861 if (!(source instanceof JSlider)) { 3862 return; 3863 } 3864 3865 JSlider slider = (JSlider) source; 3866 int value = slider.getValue(); 3867 if (slider.equals(brightSlider)) { 3868 brightField.setValue(new Integer(value)); 3869 } 3870 else if (slider.equals(contrastSlider)) { 3871 contrastField.setValue(new Integer(value)); 3872 } 3873 } 3874 3875 /** 3876 * Listen to the text field. This method detects when the value of the 3877 * text field changes. 3878 */ 3879 public void propertyChange(PropertyChangeEvent e) { 3880 Object source = e.getSource(); 3881 if ("value".equals(e.getPropertyName())) { 3882 Number num = (Number) e.getNewValue(); 3883 if (num == null) { 3884 return; 3885 } 3886 3887 double value = num.doubleValue(); 3888 if (value > 100) { 3889 value = 100; 3890 } 3891 else if (value < -100) { 3892 value = -100; 3893 } 3894 3895 if (source.equals(brightField) && (brightSlider != null)) { 3896 brightSlider.setValue((int) value); 3897 } 3898 else if (source.equals(contrastField) 3899 && (contrastSlider != null)) { 3900 contrastSlider.setValue((int) value); 3901 } 3902 } 3903 } 3904 3905 private void applyBrightContrast(int blevel, int clevel) { 3906 // do not separate autogain and simple contrast process 3907 // ImageFilter filter = new BrightnessFilter(blevel, clevel); 3908 // image = createImage(new FilteredImageSource(imageProducer, filter)); 3909 // imageComponent.setImage(image); 3910 // zoomTo(zoomFactor); 3911 3912 // separate autogain and simple contrast process 3913 if (doAutoGainContrast && gainBias!= null) { 3914 autoGainBias[0] = gainBias[0]*(1+((double)clevel)/100.0); 3915 autoGainBias[1] = gainBias[1]*(1+((double)blevel)/100.0); 3916 applyAutoGain(autoGainBias, null); 3917 } 3918 else { 3919 ImageFilter filter = new BrightnessFilter(blevel, clevel); 3920 image = createImage(new FilteredImageSource(imageProducer, filter)); 3921 imageComponent.setImage(image); 3922 zoomTo(zoomFactor); 3923 } 3924 } 3925 } // private class ContrastSlider extends JDialog implements ActionListener 3926}