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