001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the file COPYING.                     *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * If you do not have access to this file, you may request a copy from       *
011 * help@hdfgroup.org.                                                        *
012 ****************************************************************************/
013
014package hdf.view;
015
016import java.awt.BorderLayout;
017import java.awt.Color;
018import java.awt.Dimension;
019import java.awt.Graphics;
020import java.awt.GridLayout;
021import java.awt.Image;
022import java.awt.Point;
023import java.awt.Toolkit;
024import java.awt.Window;
025import java.awt.event.ActionEvent;
026import java.awt.event.ActionListener;
027import java.awt.event.ItemEvent;
028import java.awt.event.ItemListener;
029import java.awt.event.KeyEvent;
030import java.awt.event.MouseEvent;
031import java.awt.event.MouseListener;
032import java.awt.event.MouseMotionListener;
033import java.awt.image.IndexColorModel;
034import java.awt.image.MemoryImageSource;
035import java.util.Vector;
036
037import javax.swing.BorderFactory;
038import javax.swing.ButtonGroup;
039import javax.swing.CellEditor;
040import javax.swing.JButton;
041import javax.swing.JComboBox;
042import javax.swing.JComponent;
043import javax.swing.JDialog;
044import javax.swing.JFrame;
045import javax.swing.JOptionPane;
046import javax.swing.JPanel;
047import javax.swing.JRadioButton;
048import javax.swing.JScrollPane;
049import javax.swing.JTable;
050import javax.swing.ListSelectionModel;
051import javax.swing.WindowConstants;
052import javax.swing.border.LineBorder;
053import javax.swing.event.ChangeEvent;
054import javax.swing.table.DefaultTableCellRenderer;
055import javax.swing.table.DefaultTableModel;
056
057import hdf.object.FileFormat;
058import hdf.object.HObject;
059import hdf.object.ScalarDS;
060
061/**
062 * Displays a dialog for viewing and change palettes.
063 *
064 * @author Peter X. Cao
065 * @version 2.4 9/6/2007
066 */
067public class DefaultPaletteView extends JDialog implements PaletteView,
068        MouseListener, MouseMotionListener, ActionListener, ItemListener {
069    private static final long serialVersionUID = -5092012421988388661L;
070
071    private ScalarDS dataset;
072
073    /** Panel that draws plot of data values. */
074    private ChartPanel chartP;
075    private ImageView imageView;
076    private PaletteValueTable paletteValueTable;
077
078    private JRadioButton checkRed, checkGreen, checkBlue;
079
080    private JComboBox choicePalette;
081
082    private Image originalImage, currentImage;
083
084    private final Color[] lineColors = { Color.red, Color.green, Color.blue };
085    private final String lineLabels[] = { "Red", "Green", "Blue" };
086
087    private static String PALETTE_GRAY = "Gray";
088    private static String PALETTE_DEFAULT = "Default";
089    private static String PALETTE_REVERSE_GRAY = "Reverse Gray";
090    private static String PALETTE_GRAY_WAVE = "GrayWave";
091    private static String PALETTE_RAINBOW = "Rainbow";
092    private static String PALETTE_NATURE = "Nature";
093    private static String PALETTE_WAVE = "Wave";
094
095    byte[][] palette;
096    private int  numberOfPalettes;
097    private int[][] paletteData;
098
099    private int x0, y0; // starting point of mouse drag
100    boolean isPaletteChanged = false;
101
102    @SuppressWarnings("rawtypes")
103    private boolean startEditing = false;
104
105    public DefaultPaletteView(ImageView theImageView) {
106        this(null, theImageView);
107    }
108
109    @SuppressWarnings({ "rawtypes", "unchecked" })
110    public DefaultPaletteView(ViewManager theViewer, ImageView theImageView) {
111        super((JFrame) theViewer, true);
112
113        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
114
115        imageView = theImageView;
116        dataset = (ScalarDS) imageView.getDataObject();
117
118        numberOfPalettes = 1;
119
120        choicePalette = new JComboBox();
121        choicePalette.addItemListener(this);
122
123        boolean isH5 = dataset.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5));
124
125        choicePalette.addItem("Select palette");
126        String paletteName = ((ScalarDS) dataset).getPaletteName(0);
127
128        if (paletteName!= null)
129            paletteName = paletteName.trim();
130
131        if (paletteName!= null && paletteName.length()>0)
132            choicePalette.addItem(paletteName);
133
134        if (isH5 && (dataset instanceof ScalarDS)) {
135            byte[] palRefs = ((ScalarDS) dataset).getPaletteRefs();
136            if ((palRefs != null) && (palRefs.length > 8)) {
137              numberOfPalettes = palRefs.length / 8;
138            }
139        }
140        for (int i = 1; i < numberOfPalettes; i++) {
141            paletteName = ((ScalarDS) dataset).getPaletteName(i);
142            choicePalette.addItem(paletteName);
143        }
144        choicePalette.addItem(PALETTE_GRAY);
145        choicePalette.addItem(PALETTE_GRAY_WAVE);
146        choicePalette.addItem(PALETTE_RAINBOW);
147        choicePalette.addItem(PALETTE_NATURE);
148        choicePalette.addItem(PALETTE_WAVE);
149        Vector<?> plist = ViewProperties.getPaletteList();
150        int n = plist.size();
151        for (int i = 0; i < n; i++)
152            choicePalette.addItem((String) plist.get(i));
153
154        chartP = new ChartPanel();
155        chartP.setBackground(Color.white);
156
157        paletteData = new int[3][256];
158        byte[][] imagePalette = imageView.getPalette();
159        this.setTitle("Image Palette for - " + dataset.getPath()
160                + dataset.getName());
161
162        int d = 0;
163        for (int i = 0; i < 3; i++) {
164            for (int j = 0; j < 256; j++) {
165                d = imagePalette[i][j];
166                if (d < 0) {
167                    d += 256;
168                }
169                paletteData[i][j] = d;
170            }
171        }
172
173        imageView = theImageView;
174        chartP.addMouseListener(this);
175        chartP.addMouseMotionListener(this);
176
177        x0 = y0 = 0;
178        originalImage = currentImage = imageView.getImage();
179        palette = new byte[3][256];
180
181        createUI();
182        setVisible(true);
183    }
184
185    /** returns the data object displayed in this data viewer */
186    public HObject getDataObject() {
187        return dataset;
188    }
189
190    /**
191     * Creates and layouts GUI componentes.
192     */
193    private void createUI() {
194        Window owner = getOwner();
195
196        JPanel contentPane = (JPanel) getContentPane();
197        contentPane.setLayout(new BorderLayout(5, 5));
198        contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
199        int w = 700 + (ViewProperties.getFontSize() - 12) * 15;
200        int h = 500 + (ViewProperties.getFontSize() - 12) * 10;
201        contentPane.setPreferredSize(new Dimension(w, h));
202
203        contentPane.add(chartP, BorderLayout.CENTER);
204
205        JButton button = new JButton("  Ok  ");
206        button.addActionListener(this);
207        button.setActionCommand("Ok");
208        JPanel buttonP = new JPanel();
209        buttonP.setBorder(new LineBorder(Color.GRAY));
210        buttonP.add(button);
211        button = new JButton("Cancel");
212        button.addActionListener(this);
213        button.setActionCommand("Cancel");
214        buttonP.add(button);
215        button = new JButton("Preview");
216        button.addActionListener(this);
217        button.setActionCommand("Preview");
218        buttonP.add(button);
219
220        JPanel bottomP = new JPanel();
221        bottomP.setLayout(new BorderLayout(20, 2));
222        bottomP.add(buttonP, BorderLayout.EAST);
223
224        checkRed = new JRadioButton("Red");
225        checkRed.setForeground(Color.red);
226        checkGreen = new JRadioButton("Green");
227        checkGreen.setForeground(Color.green);
228        checkBlue = new JRadioButton("Blue");
229        checkBlue.setForeground(Color.blue);
230        checkRed.setSelected(true);
231        ButtonGroup bgroup = new ButtonGroup();
232        bgroup.add(checkRed);
233        bgroup.add(checkGreen);
234        bgroup.add(checkBlue);
235        JPanel checkP = new JPanel();
236        checkP.setBorder(new LineBorder(Color.GRAY));
237        checkP.add(checkRed);
238        checkP.add(checkGreen);
239        checkP.add(checkBlue);
240        bottomP.add(checkP, BorderLayout.WEST);
241
242        JPanel valueP = new JPanel();
243        valueP.setLayout(new GridLayout(1, 2));
244        valueP.setBorder(new LineBorder(Color.GRAY));
245        JButton valueButton = new JButton("Show Values");
246        valueButton.setActionCommand("Show palette values");
247        valueButton.addActionListener(this);
248        valueP.add(choicePalette);
249        valueP.add(valueButton);
250        bottomP.add(valueP, BorderLayout.CENTER);
251
252        contentPane.add(bottomP, BorderLayout.SOUTH);
253
254        Point l = owner.getLocation();
255        l.x += 350;
256        l.y += 200;
257        setLocation(l);
258        pack();
259    }
260
261    public void actionPerformed(ActionEvent e) {
262        String cmd = e.getActionCommand();
263
264        if (cmd.equals("Ok")) {
265            if (isPaletteChanged) {
266                this.updatePalette();
267                isPaletteChanged = false;
268                imageView.setPalette(palette);
269                imageView.setImage(currentImage);
270            }
271            super.dispose();
272        }
273        else if (cmd.equals("Cancel")) {
274            imageView.setImage(originalImage);
275            super.dispose();
276        }
277        else if (cmd.equals("Preview")) {
278            this.updatePalette();
279            imageView.setImage(currentImage);
280        }
281        else if (cmd.equals("Show palette values")) {
282            if (paletteValueTable == null) {
283                paletteValueTable = new PaletteValueTable(this);
284            }
285            paletteValueTable.refresh();
286            paletteValueTable.setVisible(true);
287        }
288        else if (cmd.equals("Hide palette values")) {
289            if (paletteValueTable != null) {
290                paletteValueTable.setVisible(false);
291            }
292        }
293    }
294
295    @Override
296    public void dispose() {
297        imageView.setImage(originalImage);
298        super.dispose();
299    }
300
301    public void itemStateChanged(ItemEvent e) {
302        Object src = e.getSource();
303
304        if (!src.equals(choicePalette)) {
305            return;
306        }
307
308        int idx = choicePalette.getSelectedIndex();
309        if (idx <= 0) {
310            return;
311        }
312
313        byte[][] imagePalette = null;
314        Object item = choicePalette.getSelectedItem();
315
316        if (item.equals(PALETTE_DEFAULT)) {
317            imagePalette = dataset.getPalette();
318        }
319        else if (item.equals(PALETTE_GRAY)) {
320            imagePalette = Tools.createGrayPalette();
321        }
322        else if (item.equals(PALETTE_REVERSE_GRAY)) {
323            imagePalette = Tools.createReverseGrayPalette();
324        }
325        else if (item.equals(PALETTE_GRAY_WAVE)) {
326            imagePalette = Tools.createGrayWavePalette();
327        }
328        else if (item.equals(PALETTE_RAINBOW)) {
329            imagePalette = Tools.createRainbowPalette();
330        }
331        else if (item.equals(PALETTE_NATURE)) {
332            imagePalette = Tools.createNaturePalette();
333        }
334        else if (item.equals(PALETTE_WAVE)) {
335            imagePalette = Tools.createWavePalette();
336        }
337        else if (idx > 0 && idx <= numberOfPalettes) {
338            imagePalette = ((ScalarDS) dataset).readPalette(idx - 1);
339        }
340        else {
341            imagePalette = Tools.readPalette((String)item);
342        }
343
344        if (imagePalette == null) {
345            return;
346        }
347
348        int d = 0;
349        for (int i = 0; i < 3; i++) {
350            for (int j = 0; j < 256; j++) {
351                d = imagePalette[i][j];
352                if (d < 0) {
353                    d += 256;
354                }
355                paletteData[i][j] = d;
356            }
357        }
358
359        chartP.repaint();
360        isPaletteChanged = true;
361
362    }
363
364    private void updatePalette() {
365        for (int i = 0; i < 256; i++) {
366            palette[0][i] = (byte) paletteData[0][i];
367            palette[1][i] = (byte) paletteData[1][i];
368            palette[2][i] = (byte) paletteData[2][i];
369        }
370
371        IndexColorModel colorModel = new IndexColorModel(8, // bits - the number
372                                                            // of bits each
373                                                            // pixel occupies
374                256, // size - the size of the color component arrays
375                palette[0], // r - the array of red color components
376                palette[1], // g - the array of green color components
377                palette[2]); // b - the array of blue color components
378
379        int w = dataset.getWidth();
380        int h = dataset.getHeight();
381        MemoryImageSource memoryImageSource = null;
382
383        try {
384            memoryImageSource = (MemoryImageSource) originalImage.getSource();
385        }
386        catch (Throwable err) {
387            memoryImageSource = null;
388        }
389
390        if (memoryImageSource == null) {
391            memoryImageSource = new MemoryImageSource(w, h, colorModel, imageView.getImageByteData(), 0, w);
392        }
393        else {
394            memoryImageSource.newPixels(imageView.getImageByteData(), colorModel, 0, w);
395        }
396
397        currentImage = Toolkit.getDefaultToolkit().createImage(memoryImageSource);
398    }
399
400    public void mouseClicked(MouseEvent e) {
401    } // MouseListener
402
403    public void mouseReleased(MouseEvent e) {
404        if ((paletteValueTable != null) && paletteValueTable.isVisible()) {
405            paletteValueTable.refresh();
406        }
407    } // MouseListener
408
409    public void mouseEntered(MouseEvent e) {
410    } // MouseListener
411
412    public void mouseExited(MouseEvent e) {
413    } // MouseListener
414
415    public void mouseMoved(MouseEvent e) {
416    } // MouseMotionListener
417
418    // implementing MouseListener
419    public void mousePressed(MouseEvent e) {
420        // x0 = e.getX()-40; // takes the horizontal gap
421        // if (x0 < 0) x0 = 0;
422        // y0 = e.getY()+20;
423    }
424
425    // implementing MouseMotionListener
426    public void mouseDragged(MouseEvent e) {
427        int x1 = e.getX() - 40;// takes the vertical gap
428        if (x1 < 0) {
429            x1 = 0;
430        }
431        int y1 = e.getY() + 20;
432
433        Dimension d = chartP.getSize();
434        double ry = 255 / (double) d.height;
435        double rx = 255 / (double) d.width;
436
437        int lineIdx = 0;
438        if (checkGreen.isSelected()) {
439            lineIdx = 1;
440        }
441        else if (checkBlue.isSelected()) {
442            lineIdx = 2;
443        }
444
445        int idx = 0;
446        double b = (double) (y1 - y0) / (double) (x1 - x0);
447        double a = y0 - b * x0;
448        double value = y0 * ry;
449        int i0 = Math.min(x0, x1);
450        int i1 = Math.max(x0, x1);
451        for (int i = i0; i < i1; i++) {
452            idx = (int) (rx * i);
453            if (idx > 255) {
454                continue;
455            }
456            value = 255 - (a + b * i) * ry;
457            if (value < 0) {
458                value = 0;
459            }
460            else if (value > 255) {
461                value = 255;
462            }
463            paletteData[lineIdx][idx] = (int) value;
464        }
465
466        chartP.repaint();
467        isPaletteChanged = true;
468    }
469
470    /** The dialog to show the palette values in spreadsheet. */
471    private final class PaletteValueTable extends JDialog {
472        private static final long serialVersionUID = 6105012612969555535L;
473        private JTable valueTable;
474        private DefaultTableModel valueTableModel;
475        String rgbName = "Color";
476        String idxName = "Index";
477        int editingRow =-1, editingCol=-1;
478
479        public PaletteValueTable(DefaultPaletteView owner) {
480            super(owner);
481            String[] columnNames = { idxName, "Red", "Green", "Blue", rgbName };
482            valueTableModel = new DefaultTableModel(columnNames, 256);
483
484            valueTable = new JTable(valueTableModel) {
485                private static final long serialVersionUID = -2823793138915014637L;
486
487                @Override
488                public boolean isCellEditable(int row, int col) {
489                    return (col > 0 && col < 4);
490                }
491
492                @Override
493                public Object getValueAt(int row, int col) {
494                    if (startEditing && row==editingRow && col==editingCol)
495                        return "";
496
497                    if (col == 0)
498                        return String.valueOf(row);
499                    else if (col < 4) {
500                        return String.valueOf(paletteData[col - 1][row]);
501                    }
502                    else {
503                        return "";
504                    }
505                }
506
507                @Override
508                public boolean editCellAt(int row, int column, java.util.EventObject e)
509                {
510                    if (!isCellEditable(row, column)) {
511                        return super.editCellAt(row, column, e);
512                    }
513
514                    if (e instanceof KeyEvent) {
515                        KeyEvent ke = (KeyEvent) e;
516                        if (ke.getID() == KeyEvent.KEY_PRESSED) {
517                            startEditing = true;
518                            editingRow = row;
519                            editingCol = column;
520                        }
521                    }
522
523                    return super.editCellAt(row, column, e);
524                }
525
526                @Override
527                public void editingStopped(ChangeEvent e) {
528                    int row = getEditingRow();
529                    int col = getEditingColumn();
530
531                    if (!isCellEditable(row, col)) {
532                        return;
533                    }
534
535                    String oldValue = (String) getValueAt(row, col);
536                    super.editingStopped(e);
537                    startEditing = false;
538                    editingRow = -1;
539                    editingCol = -1;
540
541                    Object source = e.getSource();
542
543                    if (source instanceof CellEditor) {
544                        CellEditor editor = (CellEditor) source;
545                        String newValue = (String) editor.getCellEditorValue();
546                        setValueAt(oldValue, row, col); // set back to what it
547                                                        // is
548                        updatePaletteValue(newValue, row, col - 1);
549                    }
550                }
551            };
552
553            valueTable.setName("PaletteValue");
554            valueTable.getColumn(rgbName).setCellRenderer(
555                    new DefaultTableCellRenderer() {
556                        private static final long serialVersionUID = 8390954944015521331L;
557                        Color color = Color.white;
558
559                        @Override
560                        public java.awt.Component getTableCellRendererComponent(
561                                JTable table, Object value, boolean isSelected,
562                                boolean hasFocus, int row, int col) {
563                            java.awt.Component comp = super
564                                    .getTableCellRendererComponent(table,
565                                            value, isSelected, hasFocus, row,
566                                            col);
567                            color = new Color(paletteData[0][row],
568                                    paletteData[1][row], paletteData[2][row]);
569                            comp.setBackground(color);
570                            return comp;
571                        }
572                    });
573
574            valueTable.getColumn(idxName).setCellRenderer(
575                    new DefaultTableCellRenderer() {
576                        private static final long serialVersionUID = 2786027382023940417L;
577
578                        @Override
579                        public java.awt.Component getTableCellRendererComponent(
580                                JTable table, Object value, boolean isSelected,
581                                boolean hasFocus, int row, int col) {
582                            java.awt.Component comp = super
583                                    .getTableCellRendererComponent(table,
584                                            value, isSelected, hasFocus, row,
585                                            col);
586                            comp.setBackground(Color.LIGHT_GRAY);
587                            return comp;
588                        }
589                    });
590
591            valueTable.setRowSelectionAllowed(false);
592            valueTable.setCellSelectionEnabled(true);
593            valueTable.getTableHeader().setReorderingAllowed(false);
594            valueTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
595
596            // set cell height for large fonts
597            int cellRowHeight = Math.max(16, valueTable.getFontMetrics(
598                    valueTable.getFont()).getHeight());
599            valueTable.setRowHeight(cellRowHeight);
600
601            JScrollPane scroller = new JScrollPane(valueTable);
602
603            JPanel contentPane = (JPanel) getContentPane();
604            int w = 300 + (ViewProperties.getFontSize() - 12) * 10;
605            int h = 600 + (ViewProperties.getFontSize() - 12) * 15;
606            contentPane.setPreferredSize(new Dimension(w, h));
607            contentPane.setLayout(new BorderLayout(5, 5));
608            contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
609            contentPane.add(scroller, BorderLayout.CENTER);
610
611            JButton button = new JButton("  Ok  ");
612            button.addActionListener(owner);
613            button.setActionCommand("Hide palette values");
614
615            JPanel tmpP = new JPanel();
616            tmpP.add(button);
617            contentPane.add(tmpP, BorderLayout.SOUTH);
618
619            Point l = owner.getLocation();
620            l.x += 100;
621            l.y += 100;
622            setLocation(l);
623            pack();
624        }
625
626        private void updatePaletteValue(String strValue, int row, int col) {
627            if (strValue == null) {
628                return;
629            }
630
631            int value = 0;
632
633            try {
634                value = Integer.parseInt(strValue);
635            }
636            catch (Exception ex) {
637                return;
638            }
639
640            if (value < 0 || value > 255) {
641                JOptionPane.showMessageDialog(this,
642                        "Value is out of range [0, 255]\n", getTitle(),
643                        JOptionPane.ERROR_MESSAGE);
644                return;
645            }
646
647            paletteData[col][row] = value;
648            chartP.repaint();
649            isPaletteChanged = true;
650        }
651
652        public void refresh() {
653            valueTable.editingStopped(new ChangeEvent(valueTable));
654            valueTable.updateUI();
655        }
656    }
657
658    /** The canvas that paints the data lines. */
659    private final class ChartPanel extends JComponent {
660        private static final long serialVersionUID = -6861041412971944L;
661
662        /**
663         * Paints the plot components.
664         */
665        @Override
666        public void paint(Graphics g) {
667            Dimension d = getSize();
668            int gap = 20;
669            int legendSpace = 60;
670            int h = d.height - gap;
671            int w = d.width - 3 * gap - legendSpace;
672
673            // draw the X axis
674            g.drawLine(2 * gap, h, w + 2 * gap, h);
675
676            // draw the Y axis
677            g.drawLine(2 * gap, h, 2 * gap, 0);
678
679            // draw X and Y labels: 10 labels for x and y
680            int dh = h / 10;
681            int dw = w / 10;
682            int dx = 25;
683            double dy = 25;
684            int xp = 2 * gap, yp = 0, x = 0, x0, y0, x1, y1;
685            double y = 0;
686
687            // draw X and Y grid labels
688            g.drawString(String.valueOf((int) y), 0, h + 8);
689            g.drawString(String.valueOf(x), xp - 5, h + gap);
690            for (int i = 0; i < 10; i++) {
691                xp += dw;
692                yp += dh;
693                x += dx;
694                y += dy;
695                g.drawLine(xp, h, xp, h - 5);
696                g.drawLine(2 * gap, h - yp, 2 * gap + 5, h - yp);
697                g.drawString(String.valueOf((int) y), 0, h - yp + 8);
698                g.drawString(String.valueOf(x), xp - 5, h + gap);
699            }
700
701            Color c = g.getColor();
702            for (int i = 0; i < 3; i++) {
703                g.setColor(lineColors[i]);
704
705                // set up the line data for drawing one line a time
706                for (int j = 0; j < 255; j++) {
707                    x0 = (w * j / 255) + 2 * gap;
708                    y0 = (h - h * paletteData[i][j] / 255);
709                    x1 = (w * (j + 1) / 255) + 2 * gap;
710                    y1 = (h - h * (paletteData[i][j + 1]) / 255);
711                    g.drawLine(x0, y0, x1, y1);
712                }
713
714                x0 = w + legendSpace;
715                y0 = gap + gap * i;
716                g.drawLine(x0, y0, x0 + 7, y0);
717                g.drawString(lineLabels[i], x0 + 10, y0 + 3);
718            }
719
720            g.setColor(c); // set the color back to its default
721
722            // draw a box on the legend
723            g.drawRect(w + legendSpace - 10, 10, legendSpace, 10 * gap);
724        } // public void paint(Graphics g)
725
726    } // private class ChartPanel extends Canvas
727
728} // private class PaletteView extends ChartView
729