Logo Search packages:      
Sourcecode: jhdf version File versions  Download package

DefaultImageView.java

/*****************************************************************************
 * Copyright by The HDF Group.                                               *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of the HDF Java Products distribution.                  *
 * The full copyright notice, including terms governing use, modification,   *
 * and redistribution, is contained in the files COPYING and Copyright.html. *
 * COPYING can be found at the root of the source code distribution tree.    *
 * Or, see http://hdfgroup.org/products/hdf-java/doc/Copyright.html.         *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 ****************************************************************************/

package ncsa.hdf.view;

import ncsa.hdf.object.*;

import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.border.*;
import javax.swing.event.*;
import java.awt.image.*;
import java.io.*;
import java.lang.reflect.Array;
import java.util.*;
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.Rectangle;
import java.awt.Point;
import java.awt.Color;
import java.awt.Insets;
import java.awt.GridLayout;
import java.awt.Font;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.awt.event.*;

import javax.swing.text.NumberFormatter;

/**
 * ImageView displays an HDF dataset as an image.
 * <p>
 * A scalar dataset in HDF can be displayed in image or table. By default, an
 * HDF4 GR image and HDF5 image is displayed as an image. Other scalar datasets
 * are display in a two-dimensional table.
 * <p>
 * Users can also choose to display a scalar dataset as image. Currently verion
 * of the ImageView only supports 8-bit raster image with indexed RGB color
 * model of 256 colors or 24-bit true color raster image. Data of other type
 * will be converted to 8-bit integer. The simple linear conversion is used for
 * this purpose:
  <pre>
      y = f * (x - min),
      where y   = the value of 8-bit integer,
            x   = the value of original data,
            f   = 255/(max-min), conversion factor,
            max = the maximum of the original data,
            min = the minimum of the original data.
  </pre>
 * <p>
 * A default color table is provided for images without palette attached to it.
 * Current choice of default palettes include Gray, Rainbow, Nature and Wave.
 * For more infomation on palette, read
 * <a href="http://hdfgroup.org/HDF5/doc/ADGuide/ImageSpec.html">
 * HDF5 Image and Palette Specification </a>
 *    
 * @author Peter X. Cao
 * @version 2.4 9/6/2007
 */
00075 public class DefaultImageView extends JInternalFrame
implements ImageView, ActionListener
{
      public static final long serialVersionUID = HObject.serialVersionUID;

    /** Horizontal direction to flip an image. */
00081     public static final int FLIP_HORIZONTAL = 0;

    /** Vertical direction to flip an image. */
00084     public static final int FLIP_VERTICAL   = 1;

    /** ROTATE IMAGE 90 DEGREE CLOCKWISE. */
00087     public static final int ROTATE_CW_90 = 10;

    /** ROTATE IMAGE COUNTER CLOCKWISE 90 DEGREE. */
00090     public static final int ROTATE_CCW_90   = 11;

    /**
     * The main HDFView.
     */
00095     private final ViewManager viewer;

    /**
     * The Scalar Dataset.
     */
00100     private ScalarDS dataset;

    /**
     * The JComponent containing the image.
     */
00105     private ImageComponent imageComponent;

    /**
     * The image contained in the ImageView.
     */
00110     private Image image;

    /**
     * The zooming factor of this image.
     */
00115     private float zoomFactor;

    /**
     * The byte data array of the image.
     */
00120     private byte[] imageByteData;

    /**
     * The color table of the image.
     */
00125     private byte[][] imagePalette;

    /**
     * The title of this imageview.
     */
00130     private String frameTitle;

    /** TextField to show the image value. */
00133     private JTextField valueField;

    /** Flag to indicate if the image is a true color image */
00136     private boolean isTrueColor;

    /** Flag to indicate if the image is a 3D */
00139     private boolean is3D;

    /** Flag to indicate if the image is plane interleaved */
00142     private boolean isPlaneInterlace;

    private boolean isHorizontalFlipped = false;

    private boolean isVerticalFlipped = false;

    private int rotateCount = 0;

    /** the number type of the image data */
00151     private char NT;

    /** the raw data of the image */
00154     private Object data;

    /** flag to indicate if the original data type is unsigned integer */
00157     private boolean isUnsigned;

    private final Toolkit toolkit;

    private double[] dataRange;
    private double[] originalRange;

    private PaletteComponent paletteComponent;

    private int animationSpeed = 2;

    private List rotateRelatedItems;

    private JScrollPane imageScroller;

    private JTextField frameField;

    private long curFrame=0, maxFrame=1;
    
    private MemoryImageSource memoryImageSource;
    
    private AutoContrastSlider autoContrastSlider;
 
    private GeneralContrastSlider generalContrastSlider;

    /** gainBias[0] = gain, gainBias[1] = bias.
     *  gain equates to contrast and bias equates to brightness
     */
00185     private double[] gainBias;
    private double[] minMaxGain;
    private double[] minMaxBias;
    
    /** int array to hold unsigned short or signed int data from applying the autogain */
00190     private Object autoGainData;

    /**
     * Constructs an ImageView.
     * <p>
     * @param theView the main HDFView.
     */
00197     public DefaultImageView(ViewManager theView)
    {
        super();

        setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);
        setFrameIcon(ViewProperties.getImageIcon());

        viewer = theView;
        zoomFactor = 1.0f;
        imageByteData = null;
        imagePalette = null;
        paletteComponent = null;
        isTrueColor = false;
        is3D = false;
        isPlaneInterlace = false;
        isUnsigned = false;
        data = null;
        NT = 0;
        toolkit = Toolkit.getDefaultToolkit();
        rotateRelatedItems = new Vector(10);
        imageScroller = null;
        memoryImageSource = null;
        autoContrastSlider = null;
        gainBias = null;
        minMaxGain = null;
        minMaxBias = null;
        autoGainData = null;
        generalContrastSlider = null;

        HObject hobject = (HObject)viewer.getTreeView().getCurrentObject();
        if ((hobject == null) || !(hobject instanceof ScalarDS)) {
            viewer.showStatus("Display data in image failed for - "+hobject);
            return;
        }

        dataset = (ScalarDS)hobject;
        dataRange = dataset.getImageDataRange();
        if (dataRange == null)
        {
            dataRange = new double[2];
            dataRange[0] = dataRange[1] = 0;
            if (dataset.getDatatype().getDatatypeSize() == 1 ) {
                dataRange[1] = 255; // byte image data rang = [0, 255]           
            }
        }

        JPanel contentPane = (JPanel)getContentPane();
        contentPane.setLayout(new BorderLayout());

        // add the text field to display pixel data
        contentPane.add(valueField=new JTextField(), BorderLayout.SOUTH);
        valueField.setEditable(false);
        valueField.setVisible(false);

        if (image == null) {
           getImage();
        }
 
        if (image == null) {
            viewer.showStatus("Loading image failed - "+dataset.getName());
            dataset = null;
            return;
        }
        
        originalRange = new double[2];
        originalRange[0] = dataRange[0];
        originalRange[1] = dataRange[1];

        imageComponent = new ImageComponent(image);
        JScrollPane scroller = new JScrollPane(imageComponent);
        scroller.getVerticalScrollBar().setUnitIncrement(50);
        scroller.getHorizontalScrollBar().setUnitIncrement(50);
        imageScroller = scroller;
        contentPane.add (scroller, BorderLayout.CENTER);

        // add palette convas to show the palette
        if (imagePalette != null) {
            paletteComponent = new PaletteComponent(imagePalette, dataRange);
            contentPane.add (paletteComponent, BorderLayout.EAST);
        }

        // set title
        StringBuffer sb = new StringBuffer("ImageView  -  ");
        sb.append(hobject.getName());
        sb.append("  -  ");
        sb.append(hobject.getPath());
        sb.append("  -  ");
        sb.append(dataset.getFile());
        setTitle(sb.toString());

        frameTitle = sb.toString();
        setTitle(frameTitle);
        this.setName(frameTitle);

        // setup subset information
        int rank = dataset.getRank();
        int[] selectedIndex = dataset.getSelectedIndex();
        long[] count = dataset.getSelectedDims();
        long[] stride = dataset.getStride();
        long[] dims = dataset.getDims();
        long[] start = dataset.getStartDims();
        int n = Math.min(3, rank);

        if (rank>2) {
            curFrame = start[selectedIndex[2]];
            maxFrame = dims[selectedIndex[2]];
        }

        sb.append(" [ dims");
        sb.append(selectedIndex[0]);
        for (int i=1; i<n; i++)
        {
            sb.append("x");
            sb.append(selectedIndex[i]);
        }
        sb.append(", start");
        sb.append(start[selectedIndex[0]]);
        for (int i=1; i<n; i++)
        {
            sb.append("x");
            sb.append(start[selectedIndex[i]]);
        }
        sb.append(", count");
        sb.append(count[selectedIndex[0]]);
        for (int i=1; i<n; i++)
        {
            sb.append("x");
            sb.append(count[selectedIndex[i]]);
        }
        sb.append(", stride");
        sb.append(stride[selectedIndex[0]]);
        for (int i=1; i<n; i++)
        {
            sb.append("x");
            sb.append(stride[selectedIndex[i]]);
        }
        sb.append(" ] ");

        setJMenuBar(createMenuBar());
        viewer.showStatus(sb.toString());
    }

    private JMenuBar createMenuBar() {
        JMenuBar bar = new JMenuBar();
        JButton button;
        boolean isEditable = !dataset.getFileFormat().isReadOnly();

        JMenu menu = new JMenu("Image", false);
        menu.setMnemonic('I');
        bar.add(menu);

        JMenu convertImageMenu = new JMenu("Save Image As");
        menu.add(convertImageMenu);

        JMenuItem item = new JMenuItem(Tools.FILE_TYPE_JPEG);
        item.addActionListener(this);
        item.setActionCommand("Save image as jpeg");
        convertImageMenu.add(item);

        /* ImageIO does not support tiff by default
        item = new JMenuItem(Tools.FILE_TYPE_TIFF);
        item.addActionListener(this);
        item.setActionCommand("Save image as tiff");
        convertImageMenu.add(item);
        */

        item = new JMenuItem(Tools.FILE_TYPE_PNG);
        item.addActionListener(this);
        item.setActionCommand("Save image as png");
        convertImageMenu.add(item);

        item = new JMenuItem(Tools.FILE_TYPE_GIF);
        item.addActionListener(this);
        item.setActionCommand("Save image as gif");
        convertImageMenu.add(item);

        item = new JMenuItem(Tools.FILE_TYPE_BMP);
        item.addActionListener(this);
        item.setActionCommand("Save image as bmp");
        convertImageMenu.add(item);

        menu.addSeparator();

        item = new JMenuItem( "Write Selection to Image");
        item.addActionListener(this);
        item.setActionCommand("Write selection to image");
        item.setEnabled(isEditable);
        rotateRelatedItems.add(item);
        menu.add(item);

        menu.addSeparator();

        item = new JMenuItem( "Change Palette");
        item.addActionListener(this);
        item.setActionCommand("Edit palette");
        item.setEnabled(!isTrueColor);
        menu.add(item);
        
        item = new JMenuItem( "Import Palette from File");
        item.addActionListener(this);
        item.setActionCommand("Import palette");
        item.setEnabled(!isTrueColor);
        menu.add(item);

        menu.addSeparator();

        item = new JMenuItem( "Set Value Range");
        item.setEnabled(!isTrueColor);
        item.addActionListener(this);
        item.setActionCommand("Set data range");
        menu.add(item);
        // no need for byte data
        try {
            String cname = data.getClass().getName();
            char dname = cname.charAt(cname.lastIndexOf("[")+1);
            if (dname == 'B') {
                item.setEnabled(false);
            }
        } catch (Exception ex) {}

       menu.addSeparator();

        item = new JMenuItem( "Show Histogram");
        item.addActionListener(this);
        item.setActionCommand("Show chart");
        item.setEnabled(!isTrueColor);
        rotateRelatedItems.add(item);
        menu.add(item);

        menu.addSeparator();

        item = new JMenuItem( "Zoom In");
        item.addActionListener(this);
        item.setActionCommand("Zoom in");
        menu.add(item);

        item = new JMenuItem( "Zoom Out");
        item.addActionListener(this);
        item.setActionCommand("Zoom out");
        menu.add(item);

        menu.addSeparator();

        JMenu imageMenu = new JMenu("Flip");
        menu.add(imageMenu);

        item = new JMenuItem( "Horizontal");
        item.addActionListener(this);
        item.setActionCommand("Flip horizontal");
        imageMenu.add(item);

        item = new JMenuItem( "Vertical");
        item.addActionListener(this);
        item.setActionCommand("Flip vertical");
        imageMenu.add(item);

        imageMenu = new JMenu("Rotate Image");
        menu.add(imageMenu);

        char t= 186;
        item = new JMenuItem( "90"+t+" CW");
        item.addActionListener(this);
        item.setActionCommand("Rotate clockwise");
        imageMenu.add(item);

        item = new JMenuItem( "90"+t+" CCW");
        item.addActionListener(this);
        item.setActionCommand("Rotate counter clockwise");
        imageMenu.add(item);

        menu.addSeparator();

        item = new JMenuItem( "Brightness/Contrast");
        item.addActionListener(this);
        item.setActionCommand("Brightness");
        menu.add(item);

        JMenu contourMenu = new JMenu("Contour");
        for (int i=3; i<10; i++)
        {
            item = new JMenuItem( String.valueOf(i));
            item.addActionListener(this);
            item.setActionCommand("Contour "+i);
            contourMenu.add(item);
        }
        menu.add(contourMenu);

        menu.addSeparator();

        item = new JMenuItem( "Show Animation");
        item.addActionListener(this);
        item.setActionCommand("Show animation");
        item.setEnabled(is3D);
        menu.add(item);

        JMenu animationMenu = new JMenu("Animation (frames/second)");
        for (int i=2; i<12; i=i+2) {
            item = new JMenuItem( String.valueOf(i));
            item.addActionListener(this);
            item.setActionCommand("Animation speed "+i);
            animationMenu.add(item);
        }
        animationMenu.setEnabled(is3D);
        menu.add(animationMenu);
        menu.addSeparator();

        JCheckBoxMenuItem imageValueCheckBox = new JCheckBoxMenuItem( "Show Value", false);
        imageValueCheckBox.addActionListener(this);
        imageValueCheckBox.setActionCommand("Show image value");
        rotateRelatedItems.add(imageValueCheckBox);
        menu.add(imageValueCheckBox);
        
        item = new JMenuItem( "Show Statistics");
        item.addActionListener(this);
        item.setActionCommand("Show statistics");
        menu.add(item);

        menu.addSeparator();

        item = new JMenuItem( "Select All");
        item.addActionListener(this);
        item.setActionCommand("Select all data");
        menu.add(item);

        menu.addSeparator();

        item = new JMenuItem( "Close");
        item.addActionListener(this);
        item.setActionCommand("Close");
        menu.add(item);

        bar.add( new JLabel("       ") );

        // add icons to the menubar

        Insets margin = new Insets( 0, 2, 0, 2 );

        // chart button
        button = new JButton( ViewProperties.getChartIcon() );
        bar.add( button );
        button.setToolTipText( "Histogram" );
        button.setMargin( margin );
        button.addActionListener( this );
        button.setActionCommand( "Show chart" );
        button.setEnabled(!isTrueColor);

        // palette button
        button = new JButton( ViewProperties.getPaletteIcon() );
        bar.add( button );
        button.setToolTipText( "Palette" );
        button.setMargin( margin );
        button.addActionListener( this );
        button.setActionCommand( "Edit palette" );
        button.setEnabled(!isTrueColor);
        
        // brightness button
        button = new JButton( ViewProperties.getBrightIcon() );
        bar.add( button );
        button.setToolTipText( "Brightness" );
        button.setMargin( margin );
        button.addActionListener( this );
        button.setActionCommand( "Brightness" );
        
        // brightness button
        button = new JButton( ViewProperties.getAutocontrastIcon() );
        bar.add( button );
        button.setToolTipText( "Calculate AutoGain" );
        button.setMargin( margin );
        button.addActionListener( this );
        button.setActionCommand( "Calculate AutoGain" );
        button.setEnabled(ViewProperties.isAutoContrast());

        button = new JButton(ViewProperties.getZoominIcon());
        bar.add(button);
        button.addActionListener(this);
        button.setMargin( margin );
        button.setActionCommand("Zoom in");
        button.setToolTipText("Zoom In");

        // zoom out button
        button = new JButton( ViewProperties.getZoomoutIcon() );
        bar.add( button );
        button.setToolTipText( "Zoom Out" );
        button.setMargin( margin );
        button.addActionListener( this );
        button.setActionCommand( "Zoom out" );

        if (is3D) {
            bar.add( new JLabel("     ") );

            // first button
            button = new JButton( ViewProperties.getFirstIcon() );
            bar.add( button );
            button.setToolTipText( "First" );
            button.setMargin( margin );
            button.addActionListener( this );
            button.setActionCommand( "First page" );

            // previous button
            button = new JButton( ViewProperties.getPreviousIcon() );
            bar.add( button );
            button.setToolTipText( "Previous" );
            button.setMargin( margin );
            button.addActionListener( this );
            button.setActionCommand( "Previous page" );

            frameField = new JTextField(String.valueOf(curFrame));
            frameField.setMaximumSize(new Dimension(50,30));
            bar.add( frameField );
            frameField.setMargin( margin );
            frameField.addActionListener( this );
            frameField.setActionCommand( "Go to frame" );

            JLabel tmpField = new JLabel(String.valueOf(maxFrame),SwingConstants.CENTER);
            tmpField.setMaximumSize(new Dimension(50,30));
            bar.add( tmpField );
            
            // next button
            button = new JButton( ViewProperties.getNextIcon() );
            bar.add( button );
            button.setToolTipText( "Next" );
            button.setMargin( margin );
            button.addActionListener( this );
            button.setActionCommand( "Next page" );

            // last button
            button = new JButton( ViewProperties.getLastIcon() );
            bar.add( button );
            button.setToolTipText( "Last" );
            button.setMargin( margin );
            button.addActionListener( this );
            button.setActionCommand( "Last page" );

            button = new JButton( ViewProperties.getAnimationIcon() );
            bar.add( button );
            button.setToolTipText( "Animation" );
            button.setMargin( margin );
            button.addActionListener( this );
            button.setActionCommand( "Show animation" );
            
       }

        return bar;
    }

    // Implementing DataObserver.
    private void previousPage()
    {
        int rank = dataset.getRank();

        if (rank < 3) {
            return;
        }

        int[] selectedIndex = dataset.getSelectedIndex();
        long[] selectedDims = dataset.getSelectedDims();

        if (selectedDims[selectedIndex[2]] >1 ) {
            return; // it is a true color image with three color components
        }

        long[] start = dataset.getStartDims();
        long[] dims = dataset.getDims();
        long idx = start[selectedIndex[2]];
        if (idx == 0) {
            return; // current page is the first page
        }

        gotoPage(start[selectedIndex[2]]-1);
    }

    // Implementing DataObserver.
    private void nextPage()
    {
        int rank = dataset.getRank();

        if (rank < 3) {
            return;
        }

        int[] selectedIndex = dataset.getSelectedIndex();
        long[] selectedDims = dataset.getSelectedDims();

        if (selectedDims[selectedIndex[2]] >1 ) {
            return; // it is a true color image with three color components
        }

        long[] start = dataset.getStartDims();
        long[] dims = dataset.getDims();
        long idx = start[selectedIndex[2]];
        if (idx == dims[selectedIndex[2]]-1) {
            return; // current page is the last page
        }

        gotoPage(start[selectedIndex[2]]+1);
    }

    // Implementing DataObserver.
    private void firstPage()
    {
        int rank = dataset.getRank();

        if (rank < 3) {
            return;
        }

        int[] selectedIndex = dataset.getSelectedIndex();
        long[] selectedDims = dataset.getSelectedDims();

        if (selectedDims[selectedIndex[2]] >1 ) {
            return; // it is a true color image with three color components
        }

        long[] start = dataset.getStartDims();
        long[] dims = dataset.getDims();
        long idx = start[selectedIndex[2]];
        if (idx == 0) {
            return; // current page is the first page
        }

        gotoPage(0);
    }

    // Implementing DataObserver.
    private void lastPage()
    {
        int rank = dataset.getRank();

        if (rank < 3) {
            return;
        }

        int[] selectedIndex = dataset.getSelectedIndex();
        long[] selectedDims = dataset.getSelectedDims();

        if (selectedDims[selectedIndex[2]] >1 ) {
            return; // it is a true color image with three color components
        }

        long[] start = dataset.getStartDims();
        long[] dims = dataset.getDims();
        long idx = start[selectedIndex[2]];
        if (idx == dims[selectedIndex[2]]-1) {
            return; // current page is the last page
        }

        gotoPage(dims[selectedIndex[2]]-1);
    }

00746     public Image getImage()
    {
        if (image != null) {
            return image;
        }

        int rank = dataset.getRank();
        if (rank <=0) {
            dataset.init();
        }
        isTrueColor = dataset.isTrueColor();
        is3D = (dataset.getRank() > 2) && !((ScalarDS)dataset).isTrueColor();

        String strValue = null;
        try
        {
            if (isTrueColor)
            {
                getTrueColorImage();
            } else {
                
                getIndexedImage();
            }
        }
        catch (Throwable ex) {
            toolkit.beep();
            JOptionPane.showMessageDialog(this,
                ex,
                getTitle(),
                JOptionPane.ERROR_MESSAGE);
            return null;
        }

        // set number type, ...
        if (data != null)
        {
            isUnsigned = dataset.isUnsigned();
            String cname = data.getClass().getName();
            NT = cname.charAt(cname.lastIndexOf("[")+1);
        }

        return image;
    }

    /**
     * @throws Exception
     * @throws OutOfMemoryError
     */
00794     private void getIndexedImage() throws Exception, OutOfMemoryError {
        imagePalette = dataset.getPalette();
        boolean noPalette = false;
        boolean doAutoContrast = false;
        boolean isLocalFile = dataset.getFileFormat().exists();
 
        if (imagePalette == null) {
            noPalette = true;
            imagePalette = Tools.createGrayPalette();
            viewer.showStatus("\nNo attached palette found, default grey palette is used to display image");
        }

        data = dataset.getData();
        if (dataset.getDatatype().getDatatypeClass() == Datatype.CLASS_INTEGER) {
            data =  dataset.convertFromUnsignedC();
            doAutoContrast = (ViewProperties.isAutoContrast() && noPalette && isLocalFile);
        }
        else
            doAutoContrast = false;

        boolean isAutoContrastFailed = true;
        if (doAutoContrast) 
            isAutoContrastFailed = (!computeAutoGainImageData());
        
        int w = dataset.getWidth();
        int h = dataset.getHeight();
 
        if (isAutoContrastFailed) {
            // converts raw data to image data
            if (dataset.isDefaultImageOrder()) {
               imageByteData = Tools.getBytes(data, dataRange, dataset.getFillValue(), imageByteData);
            } else {
                imageByteData = Tools.getBytes(data, dataRange, w, h, true, dataset.getFillValue(), imageByteData);
            }
        }

        image = createIndexedImage(imageByteData, imagePalette, w, h);
    }

    /**
     * @throws Exception
     * @throws OutOfMemoryError
     */
00837     private void getTrueColorImage() throws Exception, OutOfMemoryError {
        isPlaneInterlace = (dataset.getInterlace() ==ScalarDS.INTERLACE_PLANE);

        long[] selected = dataset.getSelectedDims();
        long[] start = dataset.getStartDims();
        int[] selectedIndex = dataset.getSelectedIndex();
        long[] stride = dataset.getStride();

        if (start.length > 2)
        {
            start[selectedIndex[2]] = 0;
            selected[selectedIndex[2]] = 3;
            stride[selectedIndex[2]] = 1;
        }

        // reload data
        dataset.clearData();
        data = dataset.getData();

        // converts raw data to image data
        imageByteData = Tools.getBytes(data, dataRange, dataset.getFillValue(), imageByteData);

        int w = dataset.getWidth();
        int h = dataset.getHeight();

        image = createTrueColorImage(imageByteData, isPlaneInterlace, w, h);
    }

    /**
     * Compute image data from autogain
     * @return
     */
00869     private boolean computeAutoGainImageData() 
    {
        boolean retValue = true;
        
        // data is unsigned short. Convert image byte data using auto-contrast image algorithm 
        boolean isUnsigned = dataset.isUnsigned();

        if (gainBias == null) { // calculate auto_gain only once
            gainBias = new double[2];
            minMaxGain = new double[2];
            minMaxBias = new double[2];
            Tools.autoContrastCompute(data, gainBias, isUnsigned);
            Tools.autoContrastComputeSliderRange( gainBias, minMaxGain, minMaxBias);
         }

        autoGainData=Tools.autoContrastApply(data, autoGainData, gainBias, isUnsigned);
 
        if (autoGainData != null) {
            if ((imageByteData == null) || (imageByteData.length != Array.getLength(data))) {
                imageByteData = new byte[Array.getLength(data)];
            }
            retValue = (Tools.autoContrastConvertImageBuffer(autoGainData, imageByteData, true)>=0);
        }
        else
            retValue = false;
        
        return retValue;
    }
    
    // implementing ImageObserver
    private void zoomIn()
    {
        if (zoomFactor >= 1) {
            zoomTo(zoomFactor+1.0f);
        } else {
            zoomTo(zoomFactor +0.125f);
        }
    }

    // implementing ImageObserver
    private void zoomOut()
    {
        if (zoomFactor > 1) {
            zoomTo(zoomFactor-1.0f);
        } else {
            zoomTo(zoomFactor-0.125f);
        }
    }

    // implementing ImageObserver
    private void zoomTo(float zf)
    {
        if (zf > 8) {
            zoomFactor = 8;
        } else if (zf < 0.125) {
            zoomFactor = 0.125f;
        } else {
            zoomFactor = zf;
        }

        Dimension imageSize = new Dimension(
                (int)(imageComponent.originalSize.width*zoomFactor),
                (int)(imageComponent.originalSize.height*zoomFactor));

        this.invalidate();
        imageComponent.invalidate();
        imageComponent.setImageSize(imageSize);
        this.validate();
        //updateUI();

         if ((zoomFactor>0.99) && (zoomFactor<1.01)) {
            setTitle(frameTitle);
        } else {
            setTitle(frameTitle+ " - "+100*zoomFactor+"%");
        }
    }

    // implementing ImageObserver
    private void showColorTable()
    {
        if (imagePalette == null) {
            return;
        }

        String viewName = (String)HDFView.getListOfPaletteView().get(0);

        try {
            Class theClass =  Class.forName(viewName);
            if ("ncsa.hdf.view.DefaultPaletteView".equals(viewName))
            {
                Object[] initargs = {viewer, this};
                PaletteView theView = (PaletteView)Tools.newInstance(theClass, initargs);
            }
            else
            {
                Object[] initargs = {this};
                PaletteView theView = (PaletteView)Tools.newInstance(theClass, initargs);
            }
        } catch (Exception ex) {viewer.showStatus(ex.toString());}
     }

    // implementing ImageObserver
    private void showHistogram()
    {
        Rectangle rec = imageComponent.selectedArea;

        if (isTrueColor) {
            toolkit.beep();
            JOptionPane.showMessageDialog(this,
            "Unsupported operation: unable to draw histogram for true color image.",
            getTitle(),
            JOptionPane.ERROR_MESSAGE);
            return;
        }


        if( (rec == null) ||
            (rec.getWidth()<=0) ||
            (rec.getHeight()<= 0))
        {
            toolkit.beep();
            JOptionPane.showMessageDialog(this,
            "No data for histogram.\nUse Shift+Mouse_drag to select an image area.",
            getTitle(),
            JOptionPane.ERROR_MESSAGE);
            return;
        }

        double chartData[][] = new double[1][256];
        for (int i=0; i<256; i++) {
            chartData[0][i] = 0.0;
        }

        int w = dataset.getWidth();
        int x0 = (int)(rec.x/zoomFactor);
        int y0 = (int)(rec.y/zoomFactor);
        int x = x0 + (int)(rec.width/zoomFactor);
        int y = y0 + (int)(rec.height/zoomFactor);
        int arrayIndex = 0;
        for (int i=y0; i<y; i++)
        {
            for (int j=x0; j<x; j++)
            {
                arrayIndex = (int)imageByteData[i*w+j];
                if (arrayIndex < 0) {
                    arrayIndex += 256;
                }
                chartData[0][arrayIndex] += 1.0;
            }
        }

        /*
        double[] xRange = dataset.getImageDataRange();
        if (xRange == null || xRange[0] == xRange[1]) {
            xRange = new double[2];
            Tools.findMinMax(data, xRange, null);
        }
        */

        double[] xRange = {0, 255};
        Chart cv = new Chart(
            (JFrame)viewer,
            "Histogram - " + dataset.getPath()+dataset.getName() + " - by pixel index",
            Chart.HISTOGRAM,
            chartData,
            xRange,
            null);
        cv.setVisible(true);
    }

    /**
     * Selects all whole image.
     * @throws Exception
     */
01043     private void selectAll() throws Exception {
        imageComponent.selectAll();
    }

    // implementing ImageObserver
    private void flip(int direction)
    {
        ImageFilter filter = new FlipFilter(direction);

        if (filter == null) {
            return;
        }

        if (changeImageFilter(filter))
        {
            // taggle flip flag
            if (direction == FLIP_HORIZONTAL) {
                isHorizontalFlipped = !isHorizontalFlipped;
            } else {
                isVerticalFlipped = !isVerticalFlipped;
            }
        }
    }

    // implementing ImageObserver
    private void rotate(int direction)
    {
        Rotate90Filter filter = new Rotate90Filter(direction);
        changeImageFilter(filter);
    }


    // implementing ImageObserver
    private void contour(int level)
    {
        ImageFilter filter = new ContourFilter(level);

        if (filter == null) {
            return;
        }

        changeImageFilter(filter);
    }

    // implementing ImageObserver
    private void brightness(int blevel, int clevel)
    {
        ImageFilter filter = new BrightnessFilter(blevel, clevel);

        if (filter == null) {
            return;
        }

        changeImageFilter(filter);
    }

    /** Apply contrast/brightness to unsigned short integer */
01100     private void applyAutoGain() {
        
        if (computeAutoGainImageData()) {
            int w = dataset.getWidth();
            int h = dataset.getHeight();
            image = createIndexedImage(imageByteData, imagePalette, w, h); 
            imageComponent.setImage(image);
            zoomTo(zoomFactor);
        }
    }

    // implementing ImageObserver
    private void setValueVisible(boolean b)
    {
        valueField.setVisible(b);
        validate();
        //updateUI(); //bug !!! on Windows. gives NullPointerException at
        //javax.swing.plaf.basic.BasicInternalFrameUI$BorderListener.mousePressed(BasicInternalFrameUI.java:693)
    }


    /**
     * This method returns true if the specified image has transparent pixels.
     * @param image the image to be check if has alpha.
     * @return true if the image has alpha setting.
     */
01126     private boolean hasAlpha(Image image)
    {
        if (image == null) {
            return false;
        }

        // If buffered image, the color model is readily available
        if (image instanceof BufferedImage)
        {
            BufferedImage bimage = (BufferedImage)image;
            return bimage.getColorModel().hasAlpha();
        }

        // Use a pixel grabber to retrieve the image's color model;
        // grabbing a single pixel is usually sufficient
        PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);
        try { pg.grabPixels(); } catch (InterruptedException e) {}
        ColorModel cm = pg.getColorModel();

        return cm.hasAlpha();
    }

    /**
     * This method returns a buffered image with the contents of an image.
     * @param image the plain image object.
     * @return buffered image for the given image.
     */
01153     private BufferedImage toBufferedImage(Image image)
    {
        if (image == null) {
            return null;
        }

        if (image instanceof BufferedImage) {
            return (BufferedImage)image;
        }

        // !!!!!!!!!!!!!!!!!! NOTICE !!!!!!!!!!!!!!!!!!!!!
        // the following way of creating a buffered image is using
        // Component.createImage(). This method can be used only if the
        // component is visible on the screen. Also, this method returns
        // buffered images that do not support transparent pixels.
        // The buffered image created by this way works for package
        // com.sun.image.codec.jpeg.*
        // It does not work well with JavaTM Advanced Imaging
        // com.sun.media.jai.codec.*;
        // if the screen setting is less than 32-bit color
        int w = image.getWidth(null);
        int h = image.getHeight(null);
        BufferedImage bimage = (BufferedImage)createImage(w, h);
        Graphics g = bimage.createGraphics();
        g.drawImage(image, 0, 0, null);

        g.dispose();
        return bimage;
    }

    /** Save the image to an image file.
     *  @param type the image type.
     *  @throws Exception
     */
01187     private void saveImageAs(String type) throws Exception
    {
        if (image == null) {
            return;
        }

        final JFileChooser fchooser = new JFileChooser(dataset.getFile());
        if (type.equals(Tools.FILE_TYPE_JPEG)) {
            fchooser.setFileFilter(DefaultFileFilter.getFileFilterJPEG());
        //} else if (type.equals(Tools.FILE_TYPE_TIFF)) {
        //    fchooser.setFileFilter(DefaultFileFilter.getFileFilterTIFF());
        } else if (type.equals(Tools.FILE_TYPE_PNG)) {
            fchooser.setFileFilter(DefaultFileFilter.getFileFilterPNG());
        } else if (type.equals(Tools.FILE_TYPE_GIF)) {
            fchooser.setFileFilter(DefaultFileFilter.getFileFilterGIF());
        } else if (type.equals(Tools.FILE_TYPE_BMP)) {
            fchooser.setFileFilter(DefaultFileFilter.getFileFilterBMP());
        }

        //fchooser.changeToParentDirectory();
        fchooser.setDialogTitle("Save Current Image To "+type+" File --- "+dataset.getName());

        File choosedFile = new File(dataset.getName()+"."+type.toLowerCase());
        fchooser.setSelectedFile(choosedFile);

        int returnVal = fchooser.showSaveDialog(this);
        if(returnVal != JFileChooser.APPROVE_OPTION) {
            return;
        }

        choosedFile = fchooser.getSelectedFile();
        if (choosedFile == null) {
            return;
        }
        String fname = choosedFile.getAbsolutePath();

        if (choosedFile.exists())
        {
            int newFileFlag = JOptionPane.showConfirmDialog(this,
                "File exists. Do you want to replace it ?",
                this.getTitle(),
                JOptionPane.YES_NO_OPTION);
            if (newFileFlag == JOptionPane.NO_OPTION) {
                return;
            }
        }

        BufferedImage bi = null;
        try {
            bi = toBufferedImage(image);
        } catch (OutOfMemoryError err)
        {
            toolkit.beep();
            JOptionPane.showMessageDialog(this,
                err.getMessage(),
                getTitle(),
                JOptionPane.ERROR_MESSAGE);
            return;
        }

        Tools.saveImageAs(bi, choosedFile, type);

        bi = null;

        viewer.showStatus("Current image saved to: "+fname);

        try {
            RandomAccessFile rf = new RandomAccessFile(choosedFile, "r");
            long size = rf.length();
            rf.close();
            viewer.showStatus("File size (bytes): "+size);
        } catch (Exception ex) {}
    }

    public void actionPerformed(ActionEvent e)
    {
      try { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
      
        Object source = e.getSource();
        String cmd = e.getActionCommand();

        if (cmd.equals("Close")) {
            dispose();  // terminate the application
            ((Vector)rotateRelatedItems).setSize(0);
        }
        else if (cmd.startsWith("Save image as "))
        {
            String filetype = null;
            if (cmd.equals("Save image as jpeg")) {
                filetype = Tools.FILE_TYPE_JPEG;
            } else if (cmd.equals("Save image as tiff")) {
                filetype = Tools.FILE_TYPE_TIFF;
            } else if (cmd.equals("Save image as png")) {
                filetype = Tools.FILE_TYPE_PNG;
            } else if (cmd.equals("Save image as gif")) {
                filetype = Tools.FILE_TYPE_GIF;
            } else if (cmd.equals("Save image as bmp")) {
                filetype = Tools.FILE_TYPE_BMP;
            }

            try { saveImageAs(filetype); }
            catch (Exception ex) {
                    toolkit.beep();
                    JOptionPane.showMessageDialog(this, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
            }
        }
        else if (cmd.equals("Write selection to image"))
        {
            if ((getSelectedArea().width <=0) ||
                (getSelectedArea().height <= 0))
            {
                JOptionPane.showMessageDialog(
                        this,
                        "No data to write.\nUse Shift+Mouse_drag to select an image area.",
                        "HDFView",
                        JOptionPane.INFORMATION_MESSAGE);
                return;
            }

            TreeView treeView = viewer.getTreeView();
            TreeNode node = treeView.findTreeNode(dataset);
            Group pGroup = (Group)((DefaultMutableTreeNode)node.getParent()).getUserObject();
            TreeNode root = dataset.getFileFormat().getRootNode();

            if (root == null) {
                return;
            }

            Vector list = new Vector(dataset.getFileFormat().getNumberOfMembers()+5);
            DefaultMutableTreeNode theNode = null;
            Enumeration local_enum = ((DefaultMutableTreeNode)root).depthFirstEnumeration();

            while(local_enum.hasMoreElements()) {
                theNode = (DefaultMutableTreeNode)local_enum.nextElement();
                list.add(theNode.getUserObject());
            }

            NewDatasetDialog dialog = new NewDatasetDialog(
                (JFrame)viewer,
                pGroup,
                list,
                this);
            dialog.setVisible(true);

            HObject obj = (HObject)dialog.getObject();
            if (obj != null) {
                Group pgroup = dialog.getParentGroup();
                try { treeView.addObject(obj, pgroup); }
                catch (Exception ex) {}
            }
            
            list.setSize(0);
        }
        else if (cmd.equals("Zoom in")) {
            zoomIn();
        }
        else if (cmd.equals("Zoom out")) {
            zoomOut();
        }
        else if (cmd.equals("Edit palette")) {
            showColorTable();
        }
        else if (cmd.equals("Import palette")) {
            JFileChooser fchooser = new JFileChooser(ViewProperties.getWorkDir());
            int returnVal = fchooser.showOpenDialog(this);
            
            if(returnVal != JFileChooser.APPROVE_OPTION) {
               return;
           }

           File choosedFile = fchooser.getSelectedFile();
           if (choosedFile == null || choosedFile.isDirectory()) {
               return;
           }
           
           (ViewProperties.getPaletteList()).addElement(choosedFile.getAbsolutePath());
        }        
        else if (cmd.equals("Set data range"))
        {
            DataRangeDialog drd = new DataRangeDialog ((JFrame)viewer, dataRange, 
                    (int)originalRange[0], (int)originalRange[1]);
            double[] drange = drd.getRange();

            if ((drange == null) || 
                (drange[0] == drange[1]) ||
                ((drange[0] == dataRange[0]) && (drange[1] == dataRange[1]))) {
                return;
            }

            changeDataRange(drange);
        }
        else if (cmd.equals("Flip horizontal")) {
            flip(FLIP_HORIZONTAL);
        }
        else if (cmd.equals("Flip vertical")) {
            flip(FLIP_VERTICAL);
        }
        else if (cmd.startsWith("Rotate")) {
            if (cmd.equals("Rotate clockwise")) {
                rotate(ROTATE_CW_90);
                rotateCount++;
                if (rotateCount == 4) {
                    rotateCount = 0;
                }
            }
            else {
                rotate(ROTATE_CCW_90);
                rotateCount--;
                if (rotateCount == -4) {
                    rotateCount = 0;
                }
            }

            int n = rotateRelatedItems.size();
            for (int i=0; i< n; i++) {
                boolean itemState =  (rotateCount == 0);
                ((javax.swing.JComponent)rotateRelatedItems.get(i)).setEnabled(itemState);
            }
        }
        else if (cmd.equals("Show image value")) {
            boolean b = ((JCheckBoxMenuItem)source).getState();
            setValueVisible(b);
        }
        else if (cmd.startsWith("Go to frame"))
        {
            int page = 0;
            try { page = Integer.parseInt(frameField.getText().trim()); }
            catch (Exception ex) { page = -1; }

            gotoPage(page);
        }
        else if (cmd.startsWith("Show animation"))
        {
            setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            Animation animation = new Animation((JFrame)viewer, dataset);
            setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        }
        else if (cmd.startsWith("Animation speed"))
        {
            animationSpeed = Integer.parseInt((cmd.substring(cmd.length()-2)).trim());
        }

        else if (cmd.startsWith("Contour"))
        {
            int level = Integer.parseInt(cmd.substring(cmd.length()-1));
            contour(level);
        }
        else if (cmd.startsWith("Brightness"))
        {
            // auto contrast is not needed for byte data
            boolean doAutoContrast = (ViewProperties.isAutoContrast() && 
                                     (minMaxGain != null) && (minMaxBias != null));
            if (doAutoContrast) {
                if (autoContrastSlider == null) {
                    autoContrastSlider = new AutoContrastSlider((JFrame)viewer, dataRange);
                }
                
                autoContrastSlider.setVisible(true);
                
                if (autoContrastSlider.isValueChanged) {
                    applyAutoGain();
                }
             } else {
                 if (generalContrastSlider == null) {
                     generalContrastSlider = new GeneralContrastSlider((JFrame)viewer, image.getSource());
                 }
                 generalContrastSlider.setVisible(true);
             }
        }
        else if (cmd.equals("Calculate AutoGain")) {
            boolean doAutoContrast = (ViewProperties.isAutoContrast() && 
                    (minMaxGain != null) && (minMaxBias != null));
            if (doAutoContrast) {
                gainBias = null;
                applyAutoGain();
            }
        }
        
        else if (cmd.equals("Show chart")) {
            showHistogram();
        }
        else if (cmd.equals("First page")) {
            firstPage();
        }
        else if (cmd.equals("Previous page")) {
            previousPage();
        }
        else if (cmd.equals("Next page")) {
            nextPage();
        }
        else if (cmd.equals("Last page")) {
            lastPage();
        }
        else if (cmd.equals("Show statistics")) {
            try {
                double[] minmax = new double[2];
                double[] stat = new double[2];
                Tools.findMinMax(data, minmax, dataset.getFillValue());
                if (Tools.computeStatistics(data, stat, dataset.getFillValue()) > 0) {
                    String statistics = "Min                      = "+minmax[0] +
                                      "\nMax                      = "+minmax[1] +
                                      "\nMean                     = "+stat[0] +
                                      "\nStandard deviation = "+stat[1];
                    JOptionPane.showMessageDialog(this, statistics, "Statistics", JOptionPane.INFORMATION_MESSAGE);
                }
            } catch (Exception ex) {
                toolkit.beep();
                JOptionPane.showMessageDialog((JFrame)viewer,
                        ex,
                        getTitle(),
                        JOptionPane.ERROR_MESSAGE);
            }
        }
        else if (cmd.equals("Select all data")) {
            try { selectAll(); }
            catch (Exception ex) {
                toolkit.beep();
                JOptionPane.showMessageDialog((JFrame)viewer,
                        ex,
                        getTitle(),
                        JOptionPane.ERROR_MESSAGE);
            }
        }
            }finally { setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); }
        
    }

01514     public void dispose()
    {
        // reload the data when it is displayed next time
        // because the display type (table or image) may be
        // different.
        if (!dataset.isImage()) {
            dataset.clearData();
        }

        data = null;
        image = null;
        imageByteData = null;
        imageComponent = null;
        autoGainData = null;
        ((Vector)rotateRelatedItems).setSize(0);
        System.runFinalization();
        System.gc();

        viewer.removeDataView(this);

        super.dispose();
    }

    // Implementing DataView.
01538     public HObject getDataObject() {
        return dataset;
    }

01542     public byte[] getImageByteData() {
        return imageByteData;
    }

    /**
     * Returns the selected data values.
     * @return the selected data object.
     */
01550     public Object getSelectedData()
    {
        Object selectedData = null;

        int cols = imageComponent.originalSelectedArea.width;
        int rows = imageComponent.originalSelectedArea.height;

        if ((cols <=0) || (rows <= 0)) {
            return null; // no data is selected
        }

        int size = cols*rows;
        if (isTrueColor) {
            size *= 3;
        }

        if (NT == 'B') {
            selectedData = new byte[size];
        } else if (NT == 'S') {
            selectedData = new short[size];
        } else if (NT == 'I') {
            selectedData = new int[size];
        } else if (NT == 'J') {
            selectedData = new long[size];
        } else if (NT == 'F') {
            selectedData = new float[size];
        } else if (NT == 'D') {
            selectedData = new double[size];
        } else {
            return null;
        }

        int r0 = imageComponent.originalSelectedArea.y;
        int c0 = imageComponent.originalSelectedArea.x;
        int w = imageComponent.originalSize.width;
        int h = imageComponent.originalSize.height;

        // transfer location to the original coordinator
        if (isHorizontalFlipped) {
            c0 = w - 1 - c0 - cols;
        }

        if (isVerticalFlipped) {
            r0 = h - 1 - r0 - rows;
        }

        int idx_src=0, idx_dst=0;
        if (isTrueColor)
        {
            int imageSize = w*h;
            if (isPlaneInterlace)
            {
                for (int j=0; j<3; j++)
                {
                    int plane = imageSize*j;
                    for (int i=0; i<rows; i++)
                    {
                        idx_src = plane+(r0+i)*w+c0;
                        System.arraycopy(data, idx_src, selectedData, idx_dst, cols);
                        idx_dst += cols;
                    }
                }
            }
            else
            {
                int numberOfDataPoints = cols*3;
                for (int i=0; i<rows; i++)
                {
                    idx_src = (r0+i)*w+c0;
                    System.arraycopy(data, idx_src*3, selectedData, idx_dst, numberOfDataPoints);
                    idx_dst += numberOfDataPoints;
                }
            }
        }
        else // indexed image
        {
            for (int i=0; i<rows; i++)
            {
                idx_src = (r0+i)*w+c0;
                System.arraycopy(data, idx_src, selectedData, idx_dst, cols);
                idx_dst += cols;
            }
        }
        
        return selectedData;
    }

    /** returns the selected area of the image
     * @return the rectangle of the selected image area.
     */
01640     public Rectangle getSelectedArea() {
        return imageComponent.originalSelectedArea;
    }

    /** @return true if the image is a truecolor image. */
01645     public boolean isTrueColor() { return isTrueColor; }

    /** @return true if the image interlace is plance interlace. */
01648     public boolean isPlaneInterlace() { return isPlaneInterlace; }

01650     public void setImage(Image img) {
        image = img;
        imageComponent.setImage(img);
    }

01655     public byte[][] getPalette() { return imagePalette; }

01657     public void setPalette(byte[][] pal) {
        imagePalette = pal;
        paletteComponent.updatePalette(pal);
    }

    private void gotoPage(long idx)
    {
        if (dataset.getRank() < 3) {
            return;
        }

        long[] start = dataset.getStartDims();
        int[] selectedIndex = dataset.getSelectedIndex();
        long[] dims = dataset.getDims();

        if ((idx <0) || (idx >= dims[selectedIndex[2]])) {
            toolkit.beep();
            JOptionPane.showMessageDialog(this,
                "Frame number must be between 0 and "+(dims[selectedIndex[2]]-1),
                getTitle(),
                JOptionPane.ERROR_MESSAGE);
            return;
        }

      setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        
        start[selectedIndex[2]] = idx;
        curFrame = idx;
        dataset.clearData();
        image = null;
        imageComponent.setImage(getImage());
        frameField.setText(String.valueOf(curFrame));

      setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        
        updateUI();
    }

    /** Creates a RGB indexed image of 256 colors.
     *  @param imageData the byte array of the image data.
     *  @param palette the color lookup table.
     *  @param w the width of the image.
     *  @param h the height of the image.
     *  @return the image.
     */
01702     public Image createIndexedImage(byte[] imageData, byte[][] palette, int w, int h)
    {
        Image theImage = null;

        IndexColorModel colorModel = new IndexColorModel (
            8,           // bits - the number of bits each pixel occupies
            256,         // size - the size of the color component arrays
            palette[0],  // r - the array of red color components
            palette[1],  // g - the array of green color components
            palette[2]); // b - the array of blue color components

        if (memoryImageSource == null) {
            memoryImageSource = new MemoryImageSource(w, h, colorModel, imageData, 0, w);
        } else {
            memoryImageSource.newPixels(imageData, colorModel, 0, w);
        }
        
        theImage = Toolkit.getDefaultToolkit().createImage (memoryImageSource);

        return theImage;
    }
    

    /**
     *  Creates a true color image.
     *  <p>
     *  DirectColorModel is used to construct the image from raw data. The
     *  DirectColorModel model is similar to an X11 TrueColor visual, which has
     *  the following parameters: <br>
        <pre>
            Number of bits:        32
            Red mask:              0x00ff0000
            Green mask:            0x0000ff00
            Blue mask:             0x000000ff
            Alpha mask:            0xff000000
            Color space:           sRGB
            isAlphaPremultiplied:  False
            Transparency:          Transparency.TRANSLUCENT
            transferType:          DataBuffer.TYPE_INT
        </pre>
     * <p>
     * The data may be arranged in one of two ways: by pixel or by plane. In both
     * cases, the dataset will have a dataspace with three dimensions, height,
     * width, and components.
     * <p>
     * For HDF4, the interlace modes specify orders for the dimensions as:
       <pre>
           INTERLACE_PIXEL = [width][height][pixel components]
           INTERLACE_PLANE = [pixel components][width][height]
       </pre>
     * <p>
     * For HDF5, the interlace modes specify orders for the dimensions as:
       <pre>
           INTERLACE_PIXEL = [height][width][pixel components]
           INTERLACE_PLANE = [pixel components][height][width]
       </pre>
     * <p>
     *  @param imageData the byte array of the image data.
     *  @param planeInterlace flag if the image is plane intelace.
     *  @param w the width of the image.
     *  @param h the height of the image.
     *  @return the image.
     */
01765     public Image createTrueColorImage(byte[] imageData, boolean planeInterlace, int w, int h)
    {
        Image theImage = null;
        int imgSize = w*h;
        int packedImageData[] = new int[imgSize];
        int pixel=0, idx=0, r=0, g=0, b=0;
        for (int i=0; i<h; i++)
        {
            for (int j=0; j<w; j++)
            {
                pixel = r = g = b = 0;
                if (planeInterlace)
                {
                    r = (int) imageData[idx];
                    g = (int) imageData[imgSize+idx];
                    b = (int) imageData[imgSize*2+idx];
                }
                else
                {
                    r = (int) imageData[idx*3];
                    g = (int) imageData[idx*3+1];
                    b = (int) imageData[idx*3+2];
                }

                r = (r << 16) & 0x00ff0000;
                g = (g <<  8) & 0x0000ff00;
                b =  b        & 0x000000ff;

                // bits packed into alpha (1), red (r), green (g) and blue (b)
                // as 11111111rrrrrrrrggggggggbbbbbbbb
                pixel = 0xff000000 | r | g | b;
                packedImageData[idx++] = pixel;
            } //for (int j=0; j<w; j++)
        } // for (int i=0; i<h; i++)

        DirectColorModel dcm = (DirectColorModel)ColorModel.getRGBdefault();
        
        if (memoryImageSource == null) {
            memoryImageSource = new MemoryImageSource(w, h, dcm, packedImageData, 0, w);
        } else {
            memoryImageSource.newPixels(packedImageData, dcm, 0, w);
        }
            
        theImage = Toolkit.getDefaultToolkit().createImage (memoryImageSource);

        packedImageData = null;

        return theImage;
    }

    private boolean changeImageFilter(ImageFilter filter)
    {
        boolean status = true;
        ImageProducer imageProducer = image.getSource();

        try {
            image = createImage(new FilteredImageSource(imageProducer,filter));
            imageComponent.setImage(image);
            zoomTo(zoomFactor);
        } catch (Throwable err)
        {
            toolkit.beep();
            JOptionPane.showMessageDialog(this,
                err.getMessage(),
                getTitle(),
                JOptionPane.ERROR_MESSAGE);
            status = false;
        }

        return status;
    }

    private void changeDataRange(double[] newRange)
    {
       try {
            imageByteData = Tools.getBytes(data, newRange, dataset.getFillValue(), imageByteData);
            int w = dataset.getWidth();
            int h = dataset.getHeight();
            image = createIndexedImage(imageByteData, imagePalette, w, h);
            imageComponent.setImage(image);
            zoomTo(zoomFactor);
            paletteComponent.updateRange(newRange);
        } catch (Throwable err)
        {
            toolkit.beep();
            JOptionPane.showMessageDialog(this,
                err.getMessage(),
                getTitle(),
                JOptionPane.ERROR_MESSAGE);
        }
        dataRange[0] = newRange[0];
        dataRange[1] = newRange[1];
    }
    
    /** PaletteComponent draws the palette on the side of the image. */
01860     private class PaletteComponent extends JComponent
    {
      public static final long serialVersionUID = HObject.serialVersionUID;

        private Color[] colors = null;
        private double[] pixelData = null;
        private Dimension paintSize = null;
        java.text.DecimalFormat format;
        double[] dRange = null;

        private PaletteComponent (byte[][] palette, double[] range)
        {
            paintSize = new Dimension(25, 2);
            format = new java.text.DecimalFormat("0.00E0");
            dRange = range;
            double unsigned_celling=0;

            if ((palette != null) && (range !=null))
            {
                double ratio = (dRange[1] - dRange[0])/255;

                pixelData = new double[256];
                for (int i=0; i<256; i++)
                {
                    pixelData[i] = (dRange[0] + ratio*i);
                }
            }

            updatePalette (palette);

            setPreferredSize(new Dimension(paintSize.width+60, paintSize.height*256));
            setVisible(true);
        }

        private void updatePalette (byte[][] palette)
        {
            if ((palette != null) && (dRange !=null))
            {
                colors = new Color[256];

                int r, g, b;
                for (int i=0; i<256; i++)
                {
                    r = (int)palette[0][i];
                    if (r < 0) {
                        r += 256;
                    }
                    g = (int)palette[1][i];
                    if (g < 0) {
                        g += 256;
                    }
                    b = (int)palette[2][i];
                    if (b < 0) {
                        b += 256;
                    }

                    colors[i] = new Color(r, g, b);
                }
            }

            repaint();
        }

        private void updateRange(double[] newRange)
        {
            if (newRange == null) {
                return;
            }

            dRange = newRange;
            double ratio = (dRange[1] - dRange[0])/255;
            for (int i=0; i<256; i++)
            {
                pixelData[i] = (dRange[0] + ratio*i);
            }

            repaint();
        }

        public void paint(Graphics g)
        {
            if ((colors == null) && (pixelData == null)) {
                return;
            }

            Font font = g.getFont();
            g.setFont(new Font(font.getName(), font.getStyle(), 12));
            for (int i=0; i<256; i++)
            {
                g.setColor(colors[i]);
                g.fillRect(0, paintSize.height*i, paintSize.width, paintSize.height);
            }

            g.setColor(Color.black);
            for (int i=0; i<25; i++)
            {
                g.drawString(format.format(pixelData[i*10]), paintSize.width+5, 10+paintSize.height*i*10);
            }
            g.drawString(format.format(pixelData[255]), paintSize.width+5, paintSize.height*255);
        }
    }

    /** ImageComponent draws the image. */
01963     private class ImageComponent extends JComponent
        implements MouseListener, MouseMotionListener, MouseWheelListener
    {
      public static final long serialVersionUID = HObject.serialVersionUID;

        private Dimension originalSize, imageSize;
        private Image image;
        private Point startPosition, currentPosition; // mouse clicked position
        private Rectangle selectedArea, originalSelectedArea;
        private StringBuffer strBuff; // to hold display value
        private int yMousePosition=0; /* the vertical position of the current mouse */
        private Dimension scrollDim = null;
        private JScrollBar hbar = null;
        private JScrollBar vbar = null;

        private ImageComponent (Image img)
        {
            image = img;
            imageSize = new Dimension(image.getWidth(this), image.getHeight(this));
            originalSize = imageSize;
            selectedArea = new Rectangle();
            originalSelectedArea = new Rectangle();
            setPreferredSize(imageSize);
            strBuff = new StringBuffer();

            addMouseListener(this);
            addMouseMotionListener(this);
            addMouseWheelListener(this);
        }

        public void paint(Graphics g)
        {
            g.drawImage(image, 0, 0, imageSize.width, imageSize.height, this);
            if ((selectedArea.width>0) && (selectedArea.height >0))
            {
                g.setColor(Color.red);
                g.drawRect(selectedArea.x, selectedArea.y, selectedArea.width, selectedArea.height);
            }
        }

        public void mousePressed(MouseEvent e)
        {
            startPosition = e.getPoint();
            selectedArea.setBounds(startPosition.x, startPosition.y, 0, 0);
                  scrollDim = imageScroller.getSize();
            hbar = imageScroller.getHorizontalScrollBar();
            vbar = imageScroller.getVerticalScrollBar();
            
            if ( (e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) == InputEvent.SHIFT_DOWN_MASK ) {
                setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
            } else {
                setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
            }           
        }

        public void mouseClicked(MouseEvent e)
        {
                  startPosition = e.getPoint();
            selectedArea.setBounds(startPosition.x, startPosition.y, 0, 0);
            
            if (hbar.isVisible()) {
                  hbar.setValue(startPosition.x-scrollDim.width/2);
            }

            if (vbar.isVisible()) {
                  vbar.setValue(startPosition.y-scrollDim.height/2);
            }
            
            repaint();
        }

        public void mouseDragged(MouseEvent e)
        {
            // don't update too often. 
            try { Thread.sleep(20); } catch (Exception ex) {}
            
            currentPosition = e.getPoint();

                  if ( (e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) == InputEvent.SHIFT_DOWN_MASK ) 
                  {
                  int x0 = Math.max(0, Math.min(startPosition.x, currentPosition.x));
                  int y0 = Math.max(0, Math.min(startPosition.y, currentPosition.y));
                  int x1 = Math.min(imageSize.width, Math.max(startPosition.x, currentPosition.x));
                  int y1 = Math.min(imageSize.height, Math.max(startPosition.y, currentPosition.y));
                        
                  int w = x1 - x0;
                  int h = y1 - y0;
                  
                  selectedArea.setBounds(x0, y0, w, h);
                  double ratio = 1.0/zoomFactor;

                  originalSelectedArea.setBounds(
                      (int)(x0*ratio),
                      (int)(y0*ratio),
                      (int)(w*ratio),
                      (int)(h*ratio));

                  repaint();
                  } else 
                  {
                  if (hbar.isVisible()) {
                              int dx = startPosition.x-currentPosition.x;
                        hbar.setValue(hbar.getValue()+dx);
                  }

                  if (vbar.isVisible()) {
                        int dy = startPosition.y-currentPosition.y;
                        vbar.setValue(vbar.getValue()+dy);
                  }
                  }
        }

        public void mouseReleased(MouseEvent e) {
            setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));           
        }
        
        public void mouseEntered(MouseEvent e) {
        }
        
        public void mouseExited(MouseEvent e)  {
        }

        public void mouseMoved(MouseEvent e)
        {
            yMousePosition = e.getY();
            showPixelValue(e.getX(), yMousePosition);
        }

        public void mouseWheelMoved(MouseWheelEvent e)
        {
            JScrollBar jb = imageScroller.getVerticalScrollBar();
            int us = e.getUnitsToScroll();
            int wr = e.getWheelRotation();
            int n = us*jb.getUnitIncrement();
            int y = jb.getValue();

            if (((y<=0) && (wr<0)) || (y+jb.getVisibleAmount()*wr >= zoomFactor*originalSize.height)) {
                return;
            }

            yMousePosition += n;
            jb.setValue(jb.getValue()+n);

            showPixelValue(e.getX(), yMousePosition);
        }

        private void showPixelValue(int x, int y)
        {
            if (!valueField.isVisible() || rotateCount!=0) {
                return;
            }

            if (data == null) {
                return;
            }

            x = (int) (x/zoomFactor);
            int w = originalSize.width;
            if ((x < 0) || (x >= w)) {
                return; // out of image bound
            }

            y = (int) (y/zoomFactor);
            int h = originalSize.height;
            if ((y < 0) || (y >= h)) {
                return; // out of image bound
            }
            
            // transfer location to the original coordinator
            if (isHorizontalFlipped) {
                x = w - 1 - x;
            }

            if (isVerticalFlipped) {
                y = h - 1 - y;
            }

            strBuff.setLength(0); // reset the string buffer
            strBuff.append("x=");
            strBuff.append(x);
            strBuff.append(",   y=");
            strBuff.append(y);
            strBuff.append(",   value=");

            if (isTrueColor)
            {
                strBuff.append("(");
                if (isPlaneInterlace)
                {
                    if (isUnsigned)
                    {
                        strBuff.append(convertUnsignedPoint(y*w+x));
                        strBuff.append(", ");
                        strBuff.append(convertUnsignedPoint(w*h+y*w+x));
                        strBuff.append(", ");
                        strBuff.append(convertUnsignedPoint(2*w*h+y*w+x));
                    }
                    else
                    {
                        strBuff.append(Array.get(data, y*w+x));
                        strBuff.append(", ");
                        strBuff.append(Array.get(data, w*h+y*w+x));
                        strBuff.append(", ");
                        strBuff.append(Array.get(data, 2*w*h+y*w+x));
                    }
                }
                else
                {
                    if (isUnsigned)
                    {
                        strBuff.append(convertUnsignedPoint(3*(y*w+x)));
                        strBuff.append(", ");
                        strBuff.append(convertUnsignedPoint(3*(y*w+x)+1));
                        strBuff.append(", ");
                        strBuff.append(convertUnsignedPoint(3*(y*w+x)+2));
                    }
                    else
                    {
                        strBuff.append(Array.get(data, 3*(y*w+x)));
                        strBuff.append(", ");
                        strBuff.append(Array.get(data, 3*(y*w+x)+1));
                        strBuff.append(", ");
                        strBuff.append(Array.get(data, 3*(y*w+x)+2));
                    }
                }
                strBuff.append(")");
            }
            else
            {
                if (isUnsigned) {
                    strBuff.append(convertUnsignedPoint(y*w+x));
                } else {
                    strBuff.append(Array.get(data, y*w+x));
                }
            }

            valueField.setText(strBuff.toString());
        }

        private void selectAll()
        {
            selectedArea.setBounds(0, 0, imageSize.width, imageSize.height);
            originalSelectedArea.setBounds(0, 0, originalSize.width, originalSize.height);
            repaint();
        }

        private long convertUnsignedPoint(int idx)
        {
            long l = 0;

            if (NT == 'B')
            {
                byte b = Array.getByte(data, idx);
                if (b<0) {
                    l = b+256;
                } else {
                    l = b;
                }
            }
            else if (NT == 'S')
            {
                short s = Array.getShort(data, idx);
                if (s<0) {
                    l = s+65536;
                } else {
                    l = s;
                }
            }
            else if (NT == 'I')
            {
                int i = Array.getInt(data, idx);
                if (i<0) {
                    l = i+4294967296L;
                } else {
                    l = i;
                }
            }

            return l;
        }

        private void setImageSize(Dimension size)
        {
            imageSize = size;
            setPreferredSize(imageSize);

            int w = selectedArea.width;
            int h = selectedArea.height;
            if ((w>0) && (h >0))
            {
                // use fixed aelected area to reduce the rounding error
                selectedArea.setBounds(
                    (int)(originalSelectedArea.x*zoomFactor),
                    (int)(originalSelectedArea.y*zoomFactor),
                    (int)(originalSelectedArea.width*zoomFactor),
                    (int)(originalSelectedArea.height*zoomFactor)
                );
            }

            repaint();
        }

        private void setImage(Image img)
        {
            image = img;
            imageSize = new Dimension(image.getWidth(this), image.getHeight(this));
            originalSize = imageSize;
            selectedArea.setSize(0, 0);
            setPreferredSize(imageSize);

            setImageSize(new Dimension(
                (int)(originalSize.width*zoomFactor),
                (int)(originalSize.height*zoomFactor)));

            repaint();
        }
    } // private class ImageComponent extends JComponent

    /**
     * FlipFileter creates image filter to flip image horizontally or
     * vertically.
     */
02285     private class FlipFilter extends ImageFilter
    {
        /** flip direction */
02288         private int direction;

        /** pixel value */
02291         private int raster[] = null;

        /** width & height */
02294         private int imageWidth, imageHeight;

        /**
         * Constructs an image filter to flip horizontally or vertically.
         * <p>
         * @param d the flip direction.
         */
02301         private FlipFilter(int d)
        {
            if (d < FLIP_HORIZONTAL) {
                d = FLIP_HORIZONTAL;
            } else if (d > FLIP_VERTICAL) {
                d = FLIP_VERTICAL;
            }

            direction = d;
        }

        public void setDimensions(int w, int h)
        {
            imageWidth = w;
            imageHeight = h;

            // specify the raster
            if (raster == null) {
                raster = new int[imageWidth*imageHeight];
            }

            consumer.setDimensions(imageWidth, imageHeight);
        }

        public void setPixels(int x, int y, int w, int h, ColorModel model,
            byte pixels[], int off, int scansize)
        {
            int srcoff = off;
            int dstoff = y * imageWidth + x;
            for (int yc = 0; yc < h; yc++)
            {
                for (int xc = 0; xc < w; xc++)
                {
                    raster[dstoff++] = model.getRGB(pixels[srcoff++] & 0xff);
                }

                srcoff += (scansize - w);
                dstoff += (imageWidth - w);
            }
        }

        public void setPixels(int x, int y, int w, int h, ColorModel model,
            int pixels[], int off, int scansize)
        {
            int srcoff = off;
            int dstoff = y * imageWidth + x;

            for (int yc = 0; yc < h; yc++)
            {
                for (int xc = 0; xc < w; xc++)
                {
                    raster[dstoff++] = model.getRGB(pixels[srcoff++]);
                }
                srcoff += (scansize - w);
                dstoff += (imageWidth - w);
            }
        }

        public void imageComplete(int status)
        {
            if ((status == IMAGEERROR) || (status == IMAGEABORTED))
            {
                consumer.imageComplete(status);
                return;
            }

            int pixels[] = new int[imageWidth];
            for (int y = 0; y < imageHeight; y++)
            {
                if (direction == FLIP_VERTICAL )
                {
                    // grab pixel values of the target line ...
                    int pos = (imageHeight-1-y)*imageWidth;
                    for (int kk=0; kk<imageWidth; kk++) {
                        pixels[kk] = raster[pos+kk];
                    }
                }
                else
                {
                    int pos = y*imageWidth;
                    for (int kk=0; kk<imageWidth; kk++) {
                        pixels[kk] = raster[pos+kk];
                    }

                    // swap the pixel values of the target line
                    int hw = imageWidth/2;
                    for (int  kk=0; kk<hw; kk++)
                    {
                        int tmp = pixels[kk];
                        pixels[kk]  = pixels[imageWidth-kk-1];
                        pixels[imageWidth-kk-1] = tmp;
                    }
                }

                // consumer it ....
                consumer.setPixels(0, y, imageWidth, 1,
                    ColorModel.getRGBdefault(), pixels, 0, imageWidth);
            } // for (int y = 0; y < imageHeight; y++)

            // complete ?
            consumer.imageComplete(status);
        }
    } // private class FlipFilter extends ImageFilter


    /**
     * Apply general brightness/contrast algorithm. 
     * For details, visit http://www.developerfusion.co.uk/
     * 
     * The general algorithm is represented by: 
     *     If Brighten = True   
     *         New_Value = Old_Value + Adjustment_Amount
     *     Else   New_Value = Old_Value - Adjustment_Amount
     *     If New_Value < Value_Minimum   
     *         New_Value = Value_Minimum
     *     If New_Value > Value_Maximum   
     *         New_Value = Value_Maximum
     *         
     * Contrast is a complicated operation. It is hard to formulate a "general algorithm". 
     * Here is the closest representation (Contrast_Value=[0, 2]): 
     * 
     *     //Converts to a percent
     *     //[0, 1]
     *     New_Value = Old_Value / 255
     *
     *     //Centers on 0 instead of .5
     *     //[-.5, .5] 
     *     New_Value -= 0.5
     *
     *     //Adjusts by Contrast_Value
     *     //[-127.5, 127.5], usually [-1, 1]
     *     New_Value *= Contrast_Value
     * 
     *     //Re-add .5 (un-center over 0)
     *     //[-127, 128]
     *     New_Value += 0.5
     * 
     *     //Re-multiply by 255 (un-convert to percent)
     *     //[-32385, 32640], usually [0, 255]
     *     New_Value *= 255
     *     //Clamp
     *     [0, 255]
     *     If(New_Value > 255)
     *         New_Value = 255
     *     If(New_Value < 0)
     *         New_Value = 0
     */
02448     private class BrightnessFilter extends RGBImageFilter
    {
        // brightness level = [-200, 200]
        int brightLevel = 0;
        
        // contrast level [0, 4]
        float contrastLevel = 0;

        public BrightnessFilter(int blevel, int clevel) {
            if (blevel < -100) {
                brightLevel = -100;
            } else if (blevel > 100) {
                brightLevel = 100;
            } else {
                brightLevel = blevel;
            }
            brightLevel *= 2;
            
            if (clevel < -100) {
                clevel = -100;
            } else if (clevel > 100) {
                clevel = 100;
            }
            
            if (clevel>0) {
                contrastLevel = (clevel/100f+1)*2;
            } else if (clevel < 0) {
                contrastLevel = (clevel/100f+1)/2;
            } else {
                contrastLevel = 0;
            }
 
            canFilterIndexColorModel = true;
        }

        public int filterRGB(int x, int y, int rgb)
        {
            // adjust brightness first, then adjust contrast
            // it gives more color depth
            
            if (brightLevel != 0) {
                int r = (rgb & 0x00ff0000) >> 16;
                int g = (rgb & 0x0000ff00) >> 8;
                int b = (rgb & 0x000000ff);
                
                r += brightLevel;
                g += brightLevel;
                b += brightLevel;
                
                if (r < 0) {
                    r = 0;
                }
                if (r > 255) {
                    r = 255;
                }
                if (g < 0) {
                    g = 0;
                }
                if (g > 255) {
                    g = 255;
                }
                if (b < 0) {
                    b = 0;
                }
                if (b > 255) {
                    b = 255;
                }
                
                r = (r << 16) & 0x00ff0000;
                g = (g <<  8) & 0x0000ff00;
                b =  b        & 0x000000ff;
                
                rgb = ((rgb & 0xff000000) | r | g | b);                
            }
            
            if (contrastLevel > 0.000001) { // do not compare float using !=0 or ==0
                int r = (rgb & 0x00ff0000) >> 16;
                int g = (rgb & 0x0000ff00) >> 8;
                int b = (rgb & 0x000000ff);
                
                float f = (float)r/255f;
                f -= 0.5;
                f *= contrastLevel;
                f += 0.5;
                f *= 255f;
                if (f < 0) {
                    f = 0;
                }
                if (f > 255) {
                    f = 255;
                }
                r = (int)f;

                f = (float)g/255f;
                f -= 0.5;
                f *= contrastLevel;
                f += 0.5;
                f *= 255f;
                if (f < 0) {
                    f = 0;
                }
                if (f > 255) {
                    f = 255;
                }
                g = (int)f;

                f = (float)b/255f;
                f -= 0.5;
                f *= contrastLevel;
                f += 0.5;
                f *= 255f;
                if (f < 0) {
                    f = 0;
                }
                if (f > 255) {
                    f = 255;
                }
                b = (int)f;

                r = (r << 16) & 0x00ff0000;
                g = (g <<  8) & 0x0000ff00;
                b =  b        & 0x000000ff;
                
                rgb = ((rgb & 0xff000000) | r | g | b);                
            }

            return rgb;
        }
    }

    /**
     * Makes an image filter for contour.
     */
02581     private class ContourFilter extends ImageFilter
    {
        // default color model
        private ColorModel defaultRGB;

        // contour level
        int level;

        // the table of the contour levels
        int levels[];

        // colors for drawable contour line
        int[] levelColors;

        // default RGB

        // pixel value
        private int raster[] = null;

        // width & height
        private int imageWidth, imageHeight;

        /**
         * Create an contour filter for a given level contouring.
         * @param theLevel the contour level.
         */
02607         private ContourFilter(int theLevel)
        {
            defaultRGB = ColorModel.getRGBdefault();

            levelColors = new int[9];

            if (theLevel < 1) {
                theLevel = 1;
            } else if (theLevel > 9) {
                theLevel = 9;
            }

            level = theLevel;
            levels = new int[level];

            levelColors[0] = Color.white.getRGB();
            levelColors[1] = Color.red.getRGB();
            levelColors[2] = Color.yellow.getRGB();
            levelColors[3] = Color.blue.getRGB();
            levelColors[4] = Color.orange.getRGB();
            levelColors[5] = Color.green.getRGB();
            levelColors[6] = Color.cyan.getRGB();
            levelColors[7] = Color.pink.getRGB();
            levelColors[8] = Color.gray.getRGB();

            int dx  = 255/level;
            for (int i=0; i<level; i++) {
                levels[i] = (i+1)*dx;
            }
        }

        public void setDimensions(int width, int height)
        {
            this.imageWidth = width;
            this.imageHeight= height;

            // specify the raster
            if (raster == null) {
                raster = new int[imageWidth*imageHeight];
            }

            consumer.setDimensions(width, height);
        }

        public void setPixels(int x, int y, int w, int h,
            ColorModel model, byte pixels[], int off, int scansize)
        {
            int rgb = 0;
            int srcoff = off;
            int dstoff = y * imageWidth + x;

            for (int yc = 0; yc < h; yc++)
            {
                for (int xc = 0; xc < w; xc++)
                {
                    rgb = model.getRGB(pixels[srcoff++] & 0xff);
                    raster[dstoff++] = (((rgb >> 16) & 0xff) +
                        ((rgb >> 8) & 0xff) +
                        (rgb & 0xff))/3;
                }
                srcoff += (scansize - w);
                dstoff += (imageWidth - w);
            }

        }

        public void setPixels(int x, int y, int w, int h,
            ColorModel model, int pixels[], int off, int scansize)
        {
            int rgb = 0;
            int srcoff = off;
            int dstoff = y * imageWidth + x;

            for (int yc = 0; yc < h; yc++)
            {
                for (int xc = 0; xc < w; xc++)
                {
                    rgb = model.getRGB(pixels[srcoff++] & 0xff);
                    raster[dstoff++] = (((rgb >> 16) & 0xff) +
                        ((rgb >> 8) & 0xff) +
                        (rgb & 0xff))/3;
                }

                srcoff += (scansize - w);
                dstoff += (imageWidth - w);
            }
        }

        public void imageComplete(int status)
        {
            if ((status == IMAGEERROR) ||
                (status == IMAGEABORTED))
            {
                consumer.imageComplete(status);
                return;
            }

            int pixels[] = new int[imageWidth*imageHeight];
            for (int z=0; z<levels.length; z++)
            {
                int currentLevel = levels[z];
                int color = levelColors[z];

                setContourLine(raster, pixels, currentLevel, color, imageWidth, imageHeight);
            }

            int line[] = new int[imageWidth];
            for (int y = 0; y < imageHeight; y++)
            {
                for (int x=0; x < imageWidth; x++) {
                    line[x] = pixels[y*imageWidth+x];
                }

                consumer.setPixels(0, y, imageWidth, 1, defaultRGB, line, 0, imageWidth);
            }  // for (int y = 0; y < imageHeight; y++) {

            // complete ?
            consumer.imageComplete(status);
        }

        /** draw a contour line based on the current parameter---level, color
         * @param raster the data of the raster image.
         * @param pixels the pixel value of the image.
         * @param level the contour level.
         * @param color the color of the contour line.
         * @param w the width of the image.
         * @param h the height of the image.
         */
02735         private void setContourLine(int[] raster, int[] pixels,
                int level, int color, int w, int h)
        {
            int p = 0;   // entrance point
            int q = p + (w*h-1);   // bottom right point
            int u = 0 + (w-1);     // top right point

            // first round
            while(true)
            {
                while ( p < u )
                {
                    int rgb = raster[p];
                    if (rgb < level)
                    {
                        while ((raster[p] < level)&&(p < u)) {
                            p++;
                        }
                        if (raster[p] >= level) {
                            pixels[p] = color;
                        }
                    }
                    else if (rgb == level)
                    {
                        while ((raster[p] == level)&&(p < u)) {
                            p++;
                        }
                        if ((raster[p] < level)  || (raster[p] > level)) {
                            pixels[p] = color;
                        }
                    }
                    else
                    {
                        while ((raster[p] > level)&&(p < u)) {
                            p++;
                        }
                        if ((raster[p] <= level)) {
                            pixels[p] = color;
                        }
                    }
                }

                if (u == q) {
                    break;
                } else
                {
                    u += w;
                    p++;
                }
            }
        }

    } // private class ContourFilter extends ImageFilter

    private class Rotate90Filter extends ImageFilter {
        private ColorModel defaultRGB = ColorModel.getRGBdefault();

        private double coord[] = new double[2];

        private int raster[];
        private int xoffset, yoffset;
        private int srcW, srcH;
        private int dstW, dstH;
        private int direction;

        public Rotate90Filter(int dir) {
            direction = dir;
        }

        public void transform(double x, double y, double[] retcoord) {
            if (direction == ROTATE_CW_90) {
                retcoord[0] =  -y;
                retcoord[1] = x;
            } else {
                retcoord[0] = y;
                retcoord[1] = -x;
            }
        }

        public void itransform(double x, double y, double[] retcoord) {
            if (direction == ROTATE_CCW_90) {
                retcoord[0] =  -y;
                retcoord[1] = x;
            } else {
                retcoord[0] = y;
                retcoord[1] = -x;
            }
        }

        public void transformBBox(Rectangle rect) {
            double minx = Double.POSITIVE_INFINITY;
            double miny = Double.POSITIVE_INFINITY;
            double maxx = Double.NEGATIVE_INFINITY;
            double maxy = Double.NEGATIVE_INFINITY;
            for (int y = 0; y <= 1; y++) {
                for (int x = 0; x <= 1; x++) {
                    transform(rect.x + x * rect.width, rect.y + y * rect.height, coord);
                    minx = Math.min(minx, coord[0]);
                    miny = Math.min(miny, coord[1]);
                    maxx = Math.max(maxx, coord[0]);
                    maxy = Math.max(maxy, coord[1]);
                }
            }
            rect.x = (int) Math.floor(minx);
            rect.y = (int) Math.floor(miny);
            rect.width = (int) Math.ceil(maxx) - rect.x;
            rect.height = (int) Math.ceil(maxy) - rect.y;
        }

        public void setDimensions(int width, int height) {
            Rectangle rect = new Rectangle(0, 0, width, height);
            transformBBox(rect);
            xoffset = -rect.x;
            yoffset = -rect.y;
            srcW = width;
            srcH = height;
            dstW = rect.width;
            dstH = rect.height;
            raster = new int[srcW * srcH];
            consumer.setDimensions(dstW, dstH);
        }

        public void setProperties(Hashtable props) {
            props = (Hashtable) props.clone();
            Object o = props.get("filters");
            if (o == null) {
                props.put("filters", toString());
            } else if (o instanceof String) {
                props.put("filters", ((String) o)+toString());
            }
            consumer.setProperties(props);
        }

        public void setColorModel(ColorModel model) {
            consumer.setColorModel(defaultRGB);
        }

        public void setHints(int hintflags) {
            consumer.setHints(TOPDOWNLEFTRIGHT
                | COMPLETESCANLINES
                | SINGLEPASS
                | (hintflags & SINGLEFRAME));
        }

        public void setPixels(int x, int y, int w, int h, ColorModel model, byte pixels[], int off, int scansize) {
            int srcoff = off;
            int dstoff = y * srcW + x;
            for (int yc = 0; yc < h; yc++) {
                for (int xc = 0; xc < w; xc++) {
                    raster[dstoff++] = model.getRGB(pixels[srcoff++] & 0xff);
                }
                srcoff += (scansize - w);
                dstoff += (srcW - w);
            }
        }

        public void setPixels(int x, int y, int w, int h, ColorModel model, int pixels[], int off, int scansize) {
            int srcoff = off;
            int dstoff = y * srcW + x;
            if (model == defaultRGB) {
                for (int yc = 0; yc < h; yc++) {
                    System.arraycopy(pixels, srcoff, raster, dstoff, w);
                    srcoff += scansize;
                    dstoff += srcW;
                }
            } else {
                for (int yc = 0; yc < h; yc++) {
                    for (int xc = 0; xc < w; xc++) {
                        raster[dstoff++] = model.getRGB(pixels[srcoff++]);
                    }
                    srcoff += (scansize - w);
                    dstoff += (srcW - w);
                }
            }
        }

        public void imageComplete(int status) {
            if ((status == IMAGEERROR) || (status == IMAGEABORTED)) {
                consumer.imageComplete(status);
                return;
            }
            int pixels[] = new int[dstW];
            for (int dy = 0; dy < dstH; dy++) {
                itransform(0 - xoffset, dy - yoffset, coord);
                double x1 = coord[0];
                double y1 = coord[1];
                itransform(dstW - xoffset, dy - yoffset, coord);
                double x2 = coord[0];
                double y2 = coord[1];
                double xinc = (x2 - x1) / dstW;
                double yinc = (y2 - y1) / dstW;
                for (int dx = 0; dx < dstW; dx++) {
                    int sx = (int) Math.round(x1);
                    int sy = (int) Math.round(y1);
                    if ((sx < 0) || (sy < 0) || (sx >= srcW) || (sy >= srcH)) {
                        pixels[dx] = 0;
                    } else {
                        pixels[dx] = raster[sy * srcW + sx];
                    }
                    x1 += xinc;
                    y1 += yinc;
                }
                consumer.setPixels(0, dy, dstW, 1, defaultRGB, pixels, 0, dstW);
            }
            consumer.imageComplete(status);
        }
    } // private class RotateFilter

    /**
     * Makes animaion for 3D images.
     */
02946     private class Animation extends JDialog implements ActionListener, Runnable
    {
      public static final long serialVersionUID = HObject.serialVersionUID;

        private final int MAX_ANIMATION_IMAGE_SIZE = 300;

        private Image[] frames = null; // a list of images for animation
        private JComponent canvas = null; // canvas to draw the image
        private Thread engine = null; // Thread animating the images
        private int numberOfImages = 0;
        private int currentFrame = 0;
        private int sleepTime = 200;
        private Image offScrImage;  // Offscreen image
        private Graphics offScrGC; // Offscreen graphics context
        private JFrame owner;
        private int x0=0, y0=0; // offset of the image drawing

        public Animation(JFrame theOwner, ScalarDS dataset) {
            super(theOwner, "Animation", true);
            owner = theOwner;
            setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);
 
            long[] dims = dataset.getDims();
            long[] stride = dataset.getStride();
            long[] start = dataset.getStartDims();
            long[] selected = dataset.getSelectedDims();
            int[] selectedIndex = dataset.getSelectedIndex();
            int rank = dataset.getRank();
            if (animationSpeed != 0) {
                sleepTime = 1000/animationSpeed;
            }

            // back up the sart and selected size
            long[] tstart = new long[rank];
            long[] tselected = new long[rank];
            long[] tstride = new long[rank];
            System.arraycopy(start, 0, tstart, 0, rank);
            System.arraycopy(selected, 0, tselected, 0, rank);
            System.arraycopy(stride, 0, tstride, 0, rank);

            int stride_n = 1;
            int max_size = (int)Math.max(selected[selectedIndex[0]], selected[selectedIndex[1]]);
            if (max_size > MAX_ANIMATION_IMAGE_SIZE) {
                stride_n = max_size/MAX_ANIMATION_IMAGE_SIZE;
            }

            start[selectedIndex[0]] = 0;
            start[selectedIndex[1]] = 0;
            start[selectedIndex[2]] = 0;
            selected[selectedIndex[0]] = dims[selectedIndex[0]]/stride_n;
            selected[selectedIndex[1]] = dims[selectedIndex[1]]/stride_n;
            selected[selectedIndex[2]] = 1;
            stride[selectedIndex[0]] = stride_n;
            stride[selectedIndex[1]] = stride_n;
            stride[selectedIndex[2]] = 1;

            Object data3d = null;
            byte[] byteData = null;
            int h = (int)selected[selectedIndex[0]];
            int w = (int)selected[selectedIndex[1]];
            int size = w*h;
            
            numberOfImages = (int)dims[selectedIndex[2]];
            frames = new Image[numberOfImages];
            MemoryImageSource mir = memoryImageSource;
            
            try {
                for (int i=0; i<numberOfImages; i++) {
                  memoryImageSource = null; // each amimation image has its own image resource
                    start[selectedIndex[2]] = i;

                    dataset.clearData();
                    try { data3d = dataset.read(); }
                    catch (Throwable err) {continue;}

                    byteData = new byte[size];
                    Tools.getBytes(data3d, dataRange, byteData);
                    frames[i] = createIndexedImage(byteData, imagePalette, w, h);
                }
            } finally {
                // set back to original state
                  memoryImageSource = mir;
                System.arraycopy(tstart, 0, start, 0, rank);
                System.arraycopy(tselected, 0, selected, 0, rank);
                System.arraycopy(tstride, 0, stride, 0, rank);
            }

            offScrImage = owner.createImage(w, h);
            offScrGC = offScrImage.getGraphics();
            x0 = Math.max((MAX_ANIMATION_IMAGE_SIZE-w)/2, 0);
            y0 = Math.max((MAX_ANIMATION_IMAGE_SIZE-h)/2, 0);

            canvas = new JComponent() {
                  public static final long serialVersionUID = HObject.serialVersionUID;

                public void paint(Graphics g) {
                    g.clearRect(0,0,MAX_ANIMATION_IMAGE_SIZE,MAX_ANIMATION_IMAGE_SIZE);

                    if ((offScrGC == null) || (frames==null)) {
                        return;
                    }
                    
                    offScrGC.drawImage(frames[currentFrame],0,0,owner);
                    g.drawImage(offScrImage,x0,y0,owner);
                }
            };

            JPanel contentPane = (JPanel)getContentPane();
            contentPane.setPreferredSize(new Dimension(MAX_ANIMATION_IMAGE_SIZE, MAX_ANIMATION_IMAGE_SIZE));
            contentPane.setLayout(new BorderLayout());
            JButton b = new JButton("Close");
            b.setActionCommand("Close animation");
            b.addActionListener(this);
            contentPane.add(b, BorderLayout.SOUTH);

            contentPane.add(canvas, BorderLayout.CENTER);
             
            start();

            Point l = getParent().getLocation();
            l.x += 300;
            l.y += 200;
            setLocation(l);
            
            pack();
            setVisible(true);
        }

        public void actionPerformed(ActionEvent e)
        {
            Object source = e.getSource();
            String cmd = e.getActionCommand();

            if (cmd.equals("Close animation")) {
                dispose();  // terminate the animation
            }
        }

        public void dispose() {
            engine = null;
            frames = null;
            super.dispose();
        }

        /**
         * No need to clear anything; just paint.
         */
03093         public void update(Graphics g) {
             paint(g);
        }

        /**
         * Paint the current frame
         */
03100         public void paint(Graphics g) {
            canvas.paint(g);
        }

        /**
         * Start the applet by forking an animation thread.
         */
03107         private void start() {
            engine = new Thread(this);
            engine.start();
        }

        /**
         * Run the animation. This method is called by class Thread.
         * @see java.lang.Thread
         */
03116         public void run() {
            Thread me = Thread.currentThread();

            if ((frames == null) || (canvas == null)) {
                return;
            }

            while (me == engine) {
                if (++currentFrame >= numberOfImages)  currentFrame = 0;
                repaint();
                this.getToolkit().sync();  // Force it to be drawn *now*.
                try { Thread.sleep(sleepTime); } catch (InterruptedException e) {}
            }
        } // public void run() {
    } // private class Animation extends JDialog

    private class DataRangeDialog extends JDialog implements
            ActionListener,
            ChangeListener,
            PropertyChangeListener
    {
      public static final long serialVersionUID = HObject.serialVersionUID;

        double[] minmax = null;
        JSlider minSlider, maxSlider;
        JFormattedTextField minField, maxField;
 
        public DataRangeDialog(JFrame theOwner, double[] dataRange, int iMin, int iMax)
        {
            super(theOwner, "Image Vaule Range", true);
            
            minmax = new double[2];
            if ((dataRange==null) || (dataRange.length<=1))
            {
                minmax[0] =0;
                minmax[1] = 255;
            } else
            {
                if (dataRange[0] == dataRange[1]) {
                    Tools.findMinMax(data, dataRange, dataset.getFillValue());
                }
                
                minmax[0] = dataRange[0];
                minmax[1] = dataRange[1];
            }

            int tickSpace = (iMax-iMin)/10;

            java.text.NumberFormat numberFormat = java.text.NumberFormat.getNumberInstance();
            NumberFormatter formatter = new NumberFormatter(numberFormat);
            formatter.setMinimum(new Double(minmax[0]));
            formatter.setMaximum(new Double(minmax[1]));

            minField = new JFormattedTextField(formatter);
            minField.addPropertyChangeListener(this);
            minField.setValue(new Double(minmax[0]));
            maxField = new JFormattedTextField(formatter);
            maxField.addPropertyChangeListener(this);
            maxField.setValue(new Double(minmax[1]));

            minSlider = new JSlider(JSlider.HORIZONTAL, iMin, iMax, (int)minmax[0]);
            minSlider.setMajorTickSpacing(tickSpace);
            minSlider.setPaintTicks(true);
            minSlider.setPaintLabels(true);
            minSlider.addChangeListener(this);
            minSlider.setBorder( BorderFactory.createEmptyBorder(0,0,10,0));

            maxSlider = new JSlider(JSlider.HORIZONTAL,iMin, iMax, (int)minmax[1]);
            maxSlider.setMajorTickSpacing(tickSpace);
            maxSlider.setPaintTicks(true);
            maxSlider.setPaintLabels(true);
            maxSlider.addChangeListener(this);
            maxSlider.setBorder( BorderFactory.createEmptyBorder(0,0,10,0));

            JPanel contentPane = (JPanel)getContentPane();
            contentPane.setLayout(new BorderLayout(5, 5));
            contentPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
            contentPane.setPreferredSize(new Dimension(500, 300));

            JPanel minPane = new JPanel();
            minPane.setBorder(new TitledBorder("Lower Bound"));
            minPane.setLayout(new BorderLayout());
            minPane.add(minField, BorderLayout.CENTER);
            minPane.add(minSlider, BorderLayout.SOUTH);

            JPanel maxPane = new JPanel();
            maxPane.setBorder(new TitledBorder("Upper Bound"));
            maxPane.setLayout(new BorderLayout());
            maxPane.add(maxField, BorderLayout.CENTER);
            maxPane.add(maxSlider, BorderLayout.SOUTH);

            JPanel mainPane = new JPanel();;
            mainPane.setLayout(new GridLayout(2,1,5,5));
            mainPane.add(minPane);
            mainPane.add(maxPane);
            contentPane.add(mainPane, BorderLayout.CENTER);

            // add OK and CANCEL buttons
            JPanel confirmP = new JPanel();
            JButton button = new JButton("   Ok   ");
            button.setMnemonic(KeyEvent.VK_O);
            button.setActionCommand("Ok");
            button.addActionListener(this);
            confirmP.add(button);
            button = new JButton("Cancel");
            button.setMnemonic(KeyEvent.VK_C);
            button.setActionCommand("Cancel");
            button.addActionListener(this);
            confirmP.add(button);
            button = new JButton("Apply");
            button.setMnemonic(KeyEvent.VK_A);
            button.setActionCommand("Apply");
            button.addActionListener(this);
            confirmP.add(button);
            contentPane.add(confirmP, BorderLayout.SOUTH);
            contentPane.add(new JLabel(" "), BorderLayout.NORTH);

            Point l = getParent().getLocation();
            Dimension d = getParent().getPreferredSize();
            l.x += 300;
            l.y += 200;
            setLocation(l);
            pack();
            setVisible(true);
        }

        public void actionPerformed(ActionEvent e)
        {
            Object source = e.getSource();
            String cmd = e.getActionCommand();

            if (cmd.equals("Ok"))
            {
                minmax[0] = ((Number)minField.getValue()).doubleValue();
                minmax[1] = ((Number)maxField.getValue()).doubleValue();

                this.dispose();
            } if (cmd.equals("Apply")) {
                minmax[0] = ((Number)minField.getValue()).doubleValue();
                minmax[1] = ((Number)maxField.getValue()).doubleValue();
                
                changeDataRange(minmax);
                minmax[0] = minmax[1] = 0;
            }
            else if (cmd.equals("Cancel"))
            {
                minmax = null;
                this.dispose();
            }
        }

        /** Listen to the slider. */
        public void stateChanged(ChangeEvent e)
        {
            Object source = e.getSource();

            if (!(source instanceof JSlider)) {
                return;
            }

            JSlider slider = (JSlider)source;
            int value = slider.getValue();
            if (slider.equals(minSlider))
            {
                int maxValue = maxSlider.getValue();
                if (value > maxValue) {
                    value = maxValue;
                }
                
                if (value != (int)minmax[0]) {
                    minField.setValue(new Integer(value));
                }
            }
            else if (slider.equals(maxSlider))
            {
                int minValue = minSlider.getValue();
                if (value < minValue) {
                    value = minValue;
                }
                if (value != (int)minmax[1]) {
                    maxField.setValue(new Integer(value));
                }
            }
        }

        /**
         * Listen to the text field.  This method detects when the
         * value of the text field changes.
         */
        public void propertyChange(PropertyChangeEvent e)
        {
            Object source = e.getSource();
            if ("value".equals(e.getPropertyName()))
            {
                Number num = (Number)e.getNewValue();
                if (num == null) {
                    return;
                }
                double value = num.doubleValue();

                if (source.equals(minField) && (minSlider!= null))
                {
                    int maxValue = maxSlider.getValue();
                    if (value > maxValue)
                    {
                        value = maxValue;
                        minField.setText(String.valueOf(value));
                    }
                    minmax[0] = value;
                    
                    minSlider.setValue((int)value);
                }
                else if (source.equals(maxField) && (maxSlider!= null))
                {
                    int minValue = minSlider.getValue();
                    if (value < minValue)
                    {
                        value = minValue;
                        maxField.setText(String.valueOf(value));
                    }
                    minmax[1] = value;
                    maxSlider.setValue((int)value);
                }
            }
        }

        public double[] getRange()  { return minmax; }
    } //private class DataRangeDialog extends JDialog implements ActionListener

    private class GeneralContrastSlider extends JDialog implements 
    ActionListener, ChangeListener, PropertyChangeListener
    {
        public static final long serialVersionUID = HObject.serialVersionUID;
        private boolean isValueChanged = false;
        JSlider brightSlider, contrastSlider;
        JFormattedTextField brightField, contrastField;
        int brightLevel=0, contrastLevel=0;
        ImageProducer imageProducer;
        
        public GeneralContrastSlider(JFrame theOwner, ImageProducer producer)
        {
            super(theOwner, "Brightness/Contrast", true);
            
            imageProducer = producer;

           java.text.NumberFormat numberFormat = java.text.NumberFormat.getNumberInstance();
           NumberFormatter formatter = new NumberFormatter(numberFormat);
            
            formatter.setMinimum(new Integer(-100));
            formatter.setMaximum(new Integer(100));
            brightField = new JFormattedTextField(formatter);
            brightField.addPropertyChangeListener(this);
            brightField.setValue(new Integer(0));
            
            brightSlider = new JSlider(JSlider.HORIZONTAL, -100, 100, 0);
            brightSlider.setMajorTickSpacing(20);
            brightSlider.setPaintTicks(true);
            brightSlider.setPaintLabels(true);
            brightSlider.addChangeListener(this);
            brightSlider.setBorder( BorderFactory.createEmptyBorder(0,0,10,0));

            formatter = new NumberFormatter(numberFormat);
            formatter.setMinimum(new Integer(-100));
            formatter.setMaximum(new Integer(100));
            contrastField = new JFormattedTextField(formatter);
            contrastField.addPropertyChangeListener(this);
            contrastField.setValue(new Integer(0));


            contrastSlider = new JSlider(JSlider.HORIZONTAL, -100, 100, 0);
            contrastSlider.setMajorTickSpacing(20);
            contrastSlider.setPaintTicks(true);
            contrastSlider.setPaintLabels(true);
            contrastSlider.addChangeListener(this);
            contrastSlider.setBorder( BorderFactory.createEmptyBorder(0,0,10,0));

            JPanel contentPane = (JPanel)getContentPane();
            contentPane.setLayout(new BorderLayout(5, 5));
            contentPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
            contentPane.setPreferredSize(new Dimension(500, 300));

            JPanel brightPane = new JPanel();
            brightPane.setBorder(new TitledBorder("Brightness"));
            brightPane.setLayout(new BorderLayout());
            brightPane.add(brightField, BorderLayout.NORTH);
            brightPane.add(brightSlider, BorderLayout.CENTER);

            JPanel contrastPane = new JPanel();
            contrastPane.setBorder(new TitledBorder("Contrast"));
            contrastPane.setLayout(new BorderLayout());
            contrastPane.add(contrastField, BorderLayout.NORTH);
            contrastPane.add(contrastSlider, BorderLayout.CENTER);

            JPanel mainPane = new JPanel();;
            mainPane.setLayout(new GridLayout(2,1,5,5));
            mainPane.add(brightPane);
            mainPane.add(contrastPane);
            contentPane.add(mainPane, BorderLayout.CENTER);

            // add OK and CANCEL buttons
            JPanel confirmP = new JPanel();
            JButton button = new JButton("   Ok   ");
            button.setMnemonic(KeyEvent.VK_O);
            button.setActionCommand("Ok_brightness_change");
            button.addActionListener(this);
            confirmP.add(button);
            button = new JButton("Cancel");
            button.setMnemonic(KeyEvent.VK_C);
            button.setActionCommand("Cancel_brightness_change");
            button.addActionListener(this);
            confirmP.add(button);
            
            button = new JButton("Apply");
            button.setMnemonic(KeyEvent.VK_A);
            button.setActionCommand("Apply_brightness_change");
            button.addActionListener(this);
            confirmP.add(button);
           
            contentPane.add(confirmP, BorderLayout.SOUTH);
            contentPane.add(new JLabel(" "), BorderLayout.NORTH);

            Point l = getParent().getLocation();
            Dimension d = getParent().getPreferredSize();
            l.x += 300;
            l.y += 200;
            setLocation(l);
            pack();
        }
        
       public void actionPerformed(ActionEvent e)
        {
            Object source = e.getSource();
            String cmd = e.getActionCommand();

            if (cmd.equals("Ok_brightness_change") || cmd.equals("Apply_brightness_change"))
            {
                int b = ((Number)brightField.getValue()).intValue();
                int c = ((Number)contrastField.getValue()).intValue();
                
                isValueChanged = ( (b!=brightLevel) | (c!=contrastLevel) );
                
                applyBrightContrast(b, c);
                
                brightLevel = b;
                contrastLevel = c;
                
                if (cmd.startsWith("Ok")) {
                    setVisible(false);
                }
            }
            else if (cmd.equals("Cancel_brightness_change"))
            {
                isValueChanged = false;
                setVisible(false);
            }
        }
        
        /** Listen to the slider. */
        public void stateChanged(ChangeEvent e)
        {
            Object source = e.getSource();

            if (!(source instanceof JSlider)) {
                return;
            }

            JSlider slider = (JSlider)source;
            int value = slider.getValue();
            if (slider.equals(brightSlider))
            {
                brightField.setValue(new Integer(value));
            }
            else if (slider.equals(contrastSlider))
            {
                contrastField.setValue(new Integer(value));
            }
        }
        
        /**
         * Listen to the text field.  This method detects when the
         * value of the text field changes.
         */
        public void propertyChange(PropertyChangeEvent e)
        {
            Object source = e.getSource();
            if ("value".equals(e.getPropertyName()))
            {
                Number num = (Number)e.getNewValue();
                if (num == null) {
                    return;
                }
                
                double value = num.doubleValue();
                if (value > 100) {
                    value = 100;
                } else if (value < -100) {
                    value = -100;
                }

                if (source.equals(brightField) && (brightSlider!= null)) {
                    brightSlider.setValue((int)value);
                } else if (source.equals(contrastField) && (contrastSlider!= null)) {
                    contrastSlider.setValue((int)value);
                }
           }
        }
        
        private void applyBrightContrast(int blevel, int clevel) {
            ImageFilter filter = new BrightnessFilter(blevel, clevel);

            if (filter == null) {
                return;
            }
            try {
                image = createImage(new FilteredImageSource(imageProducer, filter));
                imageComponent.setImage(image);
                zoomTo(zoomFactor);
            } catch (Throwable err)  {;}
        }
        
     } //private class GeneralContrastSlider extends JDialog implements ActionListener

    // for unsigned short image data only
    private class AutoContrastSlider extends JDialog implements 
        ActionListener, ChangeListener, PropertyChangeListener
    {
        public static final long serialVersionUID = HObject.serialVersionUID;
        private boolean isValueChanged = false;
        JSlider brightSlider, contrastSlider;
        JFormattedTextField brightField, contrastField;
        
        int[] gain = { (int)minMaxGain[0], (int)minMaxGain[1] };
        int[] bias = { (int)minMaxBias[0],  (int)minMaxBias[1] };
          
        public AutoContrastSlider(JFrame theOwner, double[] dataRange)
        {
            super(theOwner, "Brightness/Contrast", true);

           java.text.NumberFormat numberFormat = java.text.NumberFormat.getNumberInstance();
           NumberFormatter formatter = new NumberFormatter(numberFormat);
            
            formatter.setMinimum(new Integer(bias[0]));
            formatter.setMaximum(new Integer(bias[1]));
            brightField = new JFormattedTextField(formatter);
            brightField.addPropertyChangeListener(this);
            brightField.setValue(new Integer((int)gainBias[1]));
           
            brightSlider = new JSlider(JSlider.HORIZONTAL, bias[0], bias[1], (int)gainBias[1]);
            int tickSpace = (bias[1]-bias[0])/10;
            if (tickSpace < 1) {
                tickSpace = 1;
            }
            brightSlider.setMajorTickSpacing(tickSpace);
            brightSlider.setPaintTicks(true);
            brightSlider.setPaintLabels(true);
            brightSlider.addChangeListener(this);
            brightSlider.setBorder( BorderFactory.createEmptyBorder(0,0,10,0));

            formatter = new NumberFormatter(numberFormat);
            formatter.setMinimum(new Integer(gain[0]));
            formatter.setMaximum(new Integer(gain[1]));
            contrastField = new JFormattedTextField(formatter);
            contrastField.addPropertyChangeListener(this);
            contrastField.setValue(new Integer((int)gainBias[0]));

            contrastSlider = new JSlider(JSlider.HORIZONTAL, gain[0], gain[1], (int)gainBias[0]);
            tickSpace = (gain[1]-gain[0])/10;
            if (tickSpace < 1) {
                tickSpace = 1;
            }
            contrastSlider.setMajorTickSpacing(tickSpace);
            contrastSlider.setPaintTicks(true);
            contrastSlider.setPaintLabels(true);
            contrastSlider.addChangeListener(this);
            contrastSlider.setBorder( BorderFactory.createEmptyBorder(0,0,10,0));

            JPanel contentPane = (JPanel)getContentPane();
            contentPane.setLayout(new BorderLayout(10, 10));
            contentPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
            contentPane.setPreferredSize(new Dimension(500, 350));

            JPanel brightPane = new JPanel();
            brightPane.setBorder(new TitledBorder("Brightness"));
            brightPane.setLayout(new BorderLayout());
            brightPane.add(brightField, BorderLayout.NORTH);
            brightPane.add(brightSlider, BorderLayout.CENTER);

            JPanel contrastPane = new JPanel();
            contrastPane.setBorder(new TitledBorder("Contrast"));
            contrastPane.setLayout(new BorderLayout());
            contrastPane.add(contrastField, BorderLayout.NORTH);
            contrastPane.add(contrastSlider, BorderLayout.CENTER);

            JPanel mainPane = new JPanel();;
            mainPane.setLayout(new GridLayout(2,1,5,5));
            mainPane.add(brightPane);
            mainPane.add(contrastPane);
            contentPane.add(mainPane, BorderLayout.CENTER);

            // add OK and CANCEL buttons
            JPanel confirmP = new JPanel();
            JButton button = new JButton("   Ok   ");
            button.setMnemonic(KeyEvent.VK_O);
            button.setActionCommand("Ok_gain_change");
            button.addActionListener(this);
            confirmP.add(button);
            button = new JButton("Cancel");
            button.setMnemonic(KeyEvent.VK_C);
            button.setActionCommand("Cancel_gain_change");
            button.addActionListener(this);
            confirmP.add(button);
            
            button = new JButton("Apply");
            button.setMnemonic(KeyEvent.VK_A);
            button.setActionCommand("Apply_gain_change");
            button.addActionListener(this);
            confirmP.add(button);
           
            contentPane.add(confirmP, BorderLayout.SOUTH);
            
            button = new JButton("Calculate AutoGain");
            button.setActionCommand("Calculate AutoGain");
            button.addActionListener(this);
            JPanel tmpPane = new JPanel();
            tmpPane.setLayout(new BorderLayout());
            tmpPane.add(button, BorderLayout.EAST);
            
            contentPane.add(tmpPane, BorderLayout.NORTH);

            Point l = getParent().getLocation();
            Dimension d = getParent().getPreferredSize();
            l.x += 300;
            l.y += 200;
            setLocation(l);
            pack();
        }
        
        public void setVisible(boolean aFlag) {
            brightField.setValue(new Integer((int)gainBias[1]));
            contrastField.setValue(new Integer((int)gainBias[0]));
            super.setVisible(aFlag);
        }

        public void actionPerformed(ActionEvent e)
        {
            Object source = e.getSource();
            String cmd = e.getActionCommand();

            if (cmd.equals("Ok_gain_change") || cmd.equals("Apply_gain_change"))
            {
                int b = ((Number)brightField.getValue()).intValue();
                int c = ((Number)contrastField.getValue()).intValue();
                
                if ((b != (int)gainBias[1]) || (c != (int)gainBias[0])) {
                    gainBias[1] = (double) b;
                    gainBias[0] = (double) c;
                    isValueChanged = true;
                } else {
                    isValueChanged = false;
                }
                
                if (cmd.startsWith("Ok")) {
                    setVisible(false);
                } else  if (isValueChanged) { //Apply auto contrast
                    applyAutoGain();
                    isValueChanged = false;
                }
            }
            else if (cmd.equals("Cancel_gain_change"))
            {
                isValueChanged = false;
                setVisible(false);
            }
            else if (cmd.equals("Calculate AutoGain")) {
                gainBias = null;
                applyAutoGain();
                isValueChanged = false;
                brightField.setValue(new Integer((int)gainBias[1]));
                contrastField.setValue(new Integer((int)gainBias[0]));
            }
        }

        /** Listen to the slider. */
        public void stateChanged(ChangeEvent e)
        {
            Object source = e.getSource();

            if (!(source instanceof JSlider)) {
                return;
            }

            JSlider slider = (JSlider)source;
            int value = slider.getValue();
            if (slider.equals(brightSlider))
            {
                brightField.setValue(new Integer(value));
            }
            else if (slider.equals(contrastSlider))
            {
                contrastField.setValue(new Integer(value));
            }
        }
        
        /**
         * Listen to the text field.  This method detects when the
         * value of the text field changes.
         */
        public void propertyChange(PropertyChangeEvent e)
        {
            Object source = e.getSource();
            if ("value".equals(e.getPropertyName()))
            {
                Number num = (Number)e.getNewValue();
                if (num == null) {
                    return;
                }
                double value = num.doubleValue();

                if (source.equals(brightField) && (brightSlider!= null))
                {
                    if (value > bias[1]) {
                        value = (double) bias[1];
                    } else if (value < bias[0]) {
                        value = (double) bias[0];
                    }
                    brightSlider.setValue((int)value);
                }
                else if (source.equals(contrastField) && (contrastSlider!= null))
                {
                    if (value > gain[1]) {
                        value = (double) gain[1];
                    } else if (value < gain[0]) {
                        value = (double) gain[0];
                    }
                    contrastSlider.setValue((int)value);
                }
            }
        }
        
        /**
         * Returns true if the brightness/constrast is changed; otherwise, returns false;
         */
        public boolean isValueChanged() { return isValueChanged; }
        
    } //private class AutoContrastSlider extends JDialog implements ActionListener
}

Generated by  Doxygen 1.6.0   Back to index