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