001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.image; 031 032import java.awt.Color; 033import java.awt.Component; 034import java.awt.Container; 035import java.awt.Dimension; 036import java.awt.FlowLayout; 037import java.awt.FontMetrics; 038import java.awt.Graphics; 039import java.awt.Graphics2D; 040import java.awt.GraphicsEnvironment; 041import java.awt.GridLayout; 042import java.awt.event.ComponentAdapter; 043import java.awt.event.ComponentEvent; 044import java.awt.event.MouseEvent; 045import java.awt.event.MouseListener; 046import java.awt.event.MouseMotionListener; 047import java.awt.event.WindowAdapter; 048import java.awt.event.WindowEvent; 049import java.awt.image.BufferedImage; 050import java.util.ArrayList; 051import java.util.Arrays; 052import java.util.Collection; 053import java.util.HashMap; 054import java.util.Map; 055 056import javax.swing.JComponent; 057import javax.swing.JFrame; 058import javax.swing.JScrollPane; 059import javax.swing.SwingUtilities; 060 061import org.openimaj.image.DisplayUtilities.ImageComponent.ImageComponentListener; 062import org.openimaj.image.pixel.ConnectedComponent; 063import org.openimaj.image.processor.connectedcomponent.render.BlobRenderer; 064import org.openimaj.math.geometry.point.Point2d; 065import org.openimaj.math.geometry.point.Point2dImpl; 066import org.openimaj.math.geometry.shape.Polygon; 067import org.openimaj.math.geometry.shape.Rectangle; 068 069/** 070 * Static methods for displaying images using Swing. 071 * 072 * In addition to normal windows, the class also supports "named windows" which 073 * can be referred to by name. 074 * 075 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 076 */ 077public class DisplayUtilities 078{ 079 private static int windowCount = 0; 080 081 private static int windowOpenCount = 0; 082 083 private static Map<String, JFrame> namedWindows = new HashMap<String, JFrame>(); 084 085 /** 086 * Get the number of open windows 087 * 088 * @return number of open windows 089 */ 090 public static int openWindowCount() 091 { 092 return DisplayUtilities.windowOpenCount; 093 } 094 095 /** 096 * Display an image with the default name 097 * 098 * @param image 099 * the image 100 * @return frame containing the image 101 */ 102 public static JFrame display(final Image<?, ?> image) 103 { 104 return DisplayUtilities.display(image, "Image: " 105 + DisplayUtilities.windowCount); 106 } 107 108 /** 109 * Display an image with the default name 110 * 111 * @param image 112 * the image 113 * @return frame containing the image 114 */ 115 public static JFrame display(final BufferedImage image) 116 { 117 return DisplayUtilities.display(image, "Image: " 118 + DisplayUtilities.windowCount); 119 } 120 121 /** 122 * Display an image with the given title 123 * 124 * @param image 125 * the image 126 * @param title 127 * the title 128 * @return frame containing the image 129 */ 130 public static JFrame display(final Image<?, ?> image, final String title) 131 { 132 return DisplayUtilities.display( 133 ImageUtilities.createBufferedImageForDisplay(image), title, 134 image); 135 } 136 137 /** 138 * Display an image with the default name No additional functionality, such 139 * as zooming, is enabled. 140 * 141 * @param image 142 * the image 143 * @return frame containing the image 144 */ 145 public static JFrame displaySimple(final Image<?, ?> image) 146 { 147 return DisplayUtilities.displaySimple(image, "Image: " 148 + DisplayUtilities.windowCount); 149 } 150 151 /** 152 * Display an image with the default name. No additional functionality, such 153 * as zooming, is enabled. 154 * 155 * @param image 156 * the image 157 * @return frame containing the image 158 */ 159 public static JFrame displaySimple(final BufferedImage image) 160 { 161 return DisplayUtilities.displaySimple(image, "Image: " 162 + DisplayUtilities.windowCount); 163 } 164 165 /** 166 * Display an image with the given title. No additional functionality, such 167 * as zooming, is enabled. 168 * 169 * @param image 170 * the image 171 * @param title 172 * the title 173 * @return frame containing the image 174 */ 175 public static JFrame displaySimple(final Image<?, ?> image, 176 final String title) 177 { 178 return DisplayUtilities.displaySimple( 179 ImageUtilities.createBufferedImageForDisplay(image), title, 180 image); 181 } 182 183 private static BufferedImage getImage(final JFrame frame) 184 { 185 if (frame == null) 186 return null; 187 188 if (frame.getContentPane().getComponentCount() > 0 189 && frame.getContentPane().getComponent(0) instanceof ImageComponent) 190 { 191 return ((ImageComponent) frame.getContentPane().getComponent(0)).image; 192 } 193 194 return null; 195 } 196 197 /** 198 * Display an image in the given frame 199 * 200 * @param image 201 * the image 202 * @param frame 203 * the frame 204 * @return the frame 205 */ 206 public static JFrame display(final Image<?, ?> image, final JFrame frame) 207 { 208 final BufferedImage bimg = DisplayUtilities.getImage(frame); 209 return DisplayUtilities.display( 210 ImageUtilities.createBufferedImageForDisplay(image, bimg), 211 frame); 212 } 213 214 /** 215 * Set the position of a named window. 216 * 217 * @param name 218 * The window name 219 * @param x 220 * the x position 221 * @param y 222 * the y position 223 */ 224 public static void positionNamed(final String name, final int x, 225 final int y) 226 { 227 final JFrame w = DisplayUtilities.createNamedWindow(name); 228 w.setBounds(x, y, w.getWidth(), w.getHeight()); 229 } 230 231 /** 232 * Update the image that is being displayed in the given named window. 233 * 234 * @param name 235 * The named window 236 * @param newImage 237 * The new image to display 238 * @param title 239 * The window title 240 */ 241 public static void updateNamed(final String name, 242 final Image<?, ?> newImage, final String title) 243 { 244 final JFrame w = DisplayUtilities.createNamedWindow(name, title, true); 245 final BufferedImage bimg = DisplayUtilities.getImage(w); 246 247 ((ImageComponent) w.getContentPane().getComponent(0)) 248 .setImage(ImageUtilities.createBufferedImageForDisplay( 249 newImage, bimg)); 250 } 251 252 /** 253 * Create a named window with a title that is also the name 254 * 255 * @param name 256 * @return the window 257 */ 258 public static JFrame createNamedWindow(final String name) 259 { 260 return DisplayUtilities.createNamedWindow(name, name, false); 261 } 262 263 /** 264 * Create a named window with a title 265 * 266 * @param name 267 * @param title 268 * @return the window 269 */ 270 public static JFrame createNamedWindow(final String name, 271 final String title) 272 { 273 return DisplayUtilities.createNamedWindow(name, title, false); 274 } 275 276 /** 277 * Create a named window that auto resizes 278 * 279 * @param name 280 * @param title 281 * @param autoResize 282 * @return the window 283 */ 284 public static JFrame createNamedWindow(final String name, 285 final String title, final boolean autoResize) 286 { 287 if (DisplayUtilities.namedWindows.containsKey(name)) 288 return DisplayUtilities.namedWindows.get(name); 289 final JFrame frame = DisplayUtilities.makeDisplayFrame(title, 0, 0, 290 null); 291 ((ImageComponent) frame.getContentPane().getComponent(0)).autoResize = autoResize; 292 ((ImageComponent) frame.getContentPane().getComponent(0)).autoPack = autoResize; 293 DisplayUtilities.namedWindows.put(name, frame); 294 return frame; 295 } 296 297 /** 298 * Display an image in the given frame by name (will be created if not 299 * already done so using {@link #createNamedWindow(String)} 300 * 301 * @param image 302 * the image 303 * @param name 304 * the name of the frame 305 * @return the frame 306 */ 307 public static JFrame displayName(final Image<?, ?> image, final String name) 308 { 309 final JFrame frame = DisplayUtilities.createNamedWindow(name); 310 final BufferedImage bimg = DisplayUtilities.getImage(frame); 311 return DisplayUtilities.display( 312 ImageUtilities.createBufferedImageForDisplay(image, bimg), 313 frame, image); 314 } 315 316 /** 317 * Display an image in the given frame by name (will be created if not 318 * already done so using {@link #createNamedWindow(String)} 319 * 320 * @param image 321 * the image 322 * @param name 323 * the name of the frame 324 * @param autoResize 325 * should the frame resize to fit its contents 326 * @return the frame 327 */ 328 public static JFrame displayName(final Image<?, ?> image, 329 final String name, final boolean autoResize) 330 { 331 final JFrame frame = DisplayUtilities.createNamedWindow(name, name, 332 autoResize); 333 final BufferedImage bimg = DisplayUtilities.getImage(frame); 334 return DisplayUtilities.display( 335 ImageUtilities.createBufferedImageForDisplay(image, bimg), 336 frame, image); 337 } 338 339 /** 340 * An image viewer that displays and image and allows zooming and panning of 341 * images. 342 * <p> 343 * When allowZooming is TRUE, clicking in the image will zoom in. CTRL-click 344 * in the image to zoom out. 345 * 346 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 347 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 348 */ 349 public static class ImageComponent extends JComponent implements 350 MouseListener, MouseMotionListener 351 { 352 /** 353 * Listener for zoom and pan events 354 * 355 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 356 * @created 25 Jul 2012 357 * @version $Author$, $Revision$, $Date$ 358 */ 359 public static interface ImageComponentListener 360 { 361 /** 362 * Called when the image has been zoomed to the new zoom factor. 363 * 364 * @param newScaleFactor 365 * The new zoom factor 366 */ 367 public void imageZoomed(double newScaleFactor); 368 369 /** 370 * Called when the image has been panned to a new position. 371 * 372 * @param newX 373 * The new X position 374 * @param newY 375 * The new Y position 376 */ 377 public void imagePanned(double newX, double newY); 378 } 379 380 /** */ 381 private static final long serialVersionUID = 1L; 382 383 /** The image being displayed */ 384 protected BufferedImage image; 385 386 /** The original image being displayed. Used for pixel interrogation */ 387 protected Image<?, ?> originalImage; 388 389 /** Whether to auto resize the component to the content size */ 390 private boolean autoResize = false; 391 392 /** Whether to pack the component on resize */ 393 private boolean autoPack = false; 394 395 /** Whether to size the image to fit within the component's given size */ 396 private boolean autoFit = false; 397 398 /** When using autoFit, whether to keep the aspect ratio constant */ 399 private boolean keepAspect = true; 400 401 /** Draw a grid where there is no image */ 402 private boolean drawTransparencyGrid = false; 403 404 /** Whether to draw the mouse over pixel colour on the next paint */ 405 private boolean drawPixelColour = false; 406 407 /** Whether to show pixel colours on mouse over */ 408 private boolean showPixelColours = true; 409 410 /** Whether to show the XY coordinate of the mouse */ 411 private boolean showXY = true; 412 413 /** Whether to allow zooming */ 414 private boolean allowZooming = true; 415 416 /** Whether to allow dragging */ 417 private boolean allowDragging = true; 418 419 /** Gives the image-coord point in the centre of the image */ 420 private double drawX = 0; 421 422 /** Gives the image-coord point in the centre of the image */ 423 private double drawY = 0; 424 425 /** Gives the image scale */ 426 private double scaleFactorX = 1; 427 428 /** Gives the image scale */ 429 private double scaleFactorY = 1; 430 431 /** The last location of the drag - x-coordinate */ 432 private int dragStartX = 0; 433 434 /** The last location of the drag - y-coordinate */ 435 private int dragStartY = 0; 436 437 /** The x-coordinate of the pixel being displayed */ 438 private int pixelX = 0; 439 440 /** The y-coordinate of the pixel being displayed */ 441 private int pixelY = 0; 442 443 /** The current mouse coordinate */ 444 private int mouseX = 0; 445 446 /** The current mouse coordinate */ 447 private int mouseY = 0; 448 449 /** The current pixel colour */ 450 private Float[] currentPixelColour = null; 451 452 /** List of listeners */ 453 private final ArrayList<ImageComponentListener> listeners = 454 new ArrayList<ImageComponentListener>(); 455 456 /** The last displayed image */ 457 private BufferedImage displayedImage = null; 458 459 /** 460 * Default constructor 461 */ 462 public ImageComponent() 463 { 464 this(false, false); 465 } 466 467 /** 468 * Default constructor. Allows setting of the autoResize parameter which 469 * if true changes the size of the component to fit the contents. 470 * 471 * @param autoResize 472 * automatically resize the component to the content size 473 */ 474 public ImageComponent(final boolean autoResize) 475 { 476 this(autoResize, true); 477 } 478 479 /** 480 * Construct with given image 481 * 482 * @param image 483 * the image 484 */ 485 public ImageComponent(final BufferedImage image) 486 { 487 this(true, true); 488 this.setImage(image); 489 } 490 491 /** 492 * Default constructor. Allows setting of the autoResize parameter which 493 * if true changes the size of the component to fit the contents, and 494 * the autoPack parameter which automatically packs the containers root 495 * (if its a JFrame) whenever it is resized. 496 * 497 * @param autoResize 498 * automatically resize the component to the content size 499 * @param autoPack 500 * automatically pack the root component on resize 501 */ 502 public ImageComponent(final boolean autoResize, final boolean autoPack) 503 { 504 this(1f, autoResize, autoPack); 505 } 506 507 /** 508 * Default constructor. Allows setting of the autoResize parameter which 509 * if true changes the size of the component to fit the contents, and 510 * the autoPack parameter which automatically packs the containers root 511 * (if its a JFrame) whenever it is resized. 512 * 513 * @param initialScale 514 * initial scale of the image 515 * @param autoResize 516 * automatically resize the component to the content size 517 * @param autoPack 518 * automatically pack the root component on resize 519 */ 520 public ImageComponent(final float initialScale, 521 final boolean autoResize, final boolean autoPack) 522 { 523 this.autoPack = autoPack; 524 this.autoResize = autoResize; 525 this.scaleFactorX = initialScale; 526 this.scaleFactorY = initialScale; 527 528 this.addMouseListener(this); 529 this.addMouseMotionListener(this); 530 531 // Add a component listener so that we can detect when the 532 // component has been resized so that we can update 533 this.addComponentListener(new ComponentAdapter() 534 { 535 @Override 536 public void componentResized(final ComponentEvent e) 537 { 538 ImageComponent.this.calculateScaleFactorsToFit( 539 ImageComponent.this.image, ImageComponent.this.getBounds()); 540 }; 541 }); 542 } 543 544 /** 545 * Add the given listener to this image component. 546 * 547 * @param l 548 * The listener to add 549 */ 550 public void addImageComponentListener(final ImageComponentListener l) 551 { 552 this.listeners.add(l); 553 } 554 555 /** 556 * Remove the given listener from this image component. 557 * 558 * @param l 559 * The listener to remove. 560 */ 561 public void removeImageComponentListener(final ImageComponentListener l) 562 { 563 this.listeners.remove(l); 564 } 565 566 /** 567 * Set whether to allow zooming. 568 * 569 * @param allowZoom 570 * TRUE to allow zooming 571 */ 572 public void setAllowZoom(final boolean allowZoom) 573 { 574 this.allowZooming = allowZoom; 575 if (allowZoom) 576 this.autoFit = false; 577 } 578 579 /** 580 * Set whether to allow panning. 581 * 582 * @param allowPan 583 * TRUE to allow panning 584 */ 585 public void setAllowPanning(final boolean allowPan) 586 { 587 this.allowDragging = allowPan; 588 if (allowPan) 589 this.autoFit = false; 590 } 591 592 /** 593 * Set whether to allow drawing of the transparency grid. 594 * 595 * @param drawGrid 596 * TRUE draws the grid 597 */ 598 public void setTransparencyGrid(final boolean drawGrid) 599 { 600 this.drawTransparencyGrid = drawGrid; 601 this.repaint(); 602 } 603 604 /** 605 * Set whether to show pixel colours or not. 606 * 607 * @param showPixelColours 608 * TRUE to show pixel colours 609 */ 610 public void setShowPixelColours(final boolean showPixelColours) 611 { 612 this.showPixelColours = showPixelColours; 613 this.repaint(); 614 } 615 616 /** 617 * Set whether to show the XY position of the mouse curson or not 618 * 619 * @param showXYPosition 620 * TRUE to show XY position 621 */ 622 public void setShowXYPosition(final boolean showXYPosition) 623 { 624 this.showXY = showXYPosition; 625 this.repaint(); 626 } 627 628 /** 629 * Set the image to draw 630 * 631 * @param image 632 * the image 633 */ 634 public void setImage(final BufferedImage image) 635 { 636 this.image = image; 637 638 if (this.autoFit) 639 { 640 this.calculateScaleFactorsToFit(image, this.getBounds()); 641 } 642 else if (this.autoResize) 643 { 644 // If the component isn't the right shape, we'll resize the 645 // component. 646 if (image.getWidth() != this.getWidth() || 647 image.getHeight() != this.getHeight()) 648 { 649 this.setPreferredSize(new Dimension( 650 (int) (image.getWidth() * this.scaleFactorX), 651 (int) (image.getHeight() * this.scaleFactorY))); 652 this.setSize(new Dimension( 653 (int) (image.getWidth() * this.scaleFactorX), 654 (int) (image.getHeight() * this.scaleFactorY))); 655 } 656 657 final Component c = SwingUtilities.getRoot(this); 658 if (c == null) 659 return; 660 c.validate(); 661 662 if (c instanceof JFrame && this.autoPack) 663 { 664 final JFrame f = (JFrame) c; 665 f.pack(); 666 } 667 } 668 669 if (this.showPixelColours) 670 // This forces a repaint if showPixelColours is true 671 this.updatePixelColours(); 672 else 673 this.repaint(); 674 } 675 676 /** 677 * Given an image, will calculate two scale factors for the X and Y 678 * dimensions of the image, such that the image will fit within the 679 * bounds. 680 * 681 * @param image 682 * The image to fit 683 * @param bounds 684 * The bounds to fit within 685 */ 686 private void calculateScaleFactorsToFit(final BufferedImage image, 687 final java.awt.Rectangle bounds) 688 { 689 if (image == null || bounds == null) 690 return; 691 692 if (this.autoFit) 693 { 694 // If we can stretch the image it's pretty simple. 695 if (!this.keepAspect) 696 { 697 this.scaleFactorX = bounds.width / (double) image.getWidth(); 698 this.scaleFactorY = bounds.height / (double) image.getHeight(); 699 } 700 // Otherwise we need to find the ratios to fit while keeping 701 // aspect 702 else 703 { 704 this.scaleFactorX = this.scaleFactorY = Math.min( 705 bounds.width / (double) image.getWidth(), 706 bounds.height / (double) image.getHeight()); 707 } 708 } 709 } 710 711 /** 712 * Move the image to the given position (image coordinates) 713 * 714 * @param x 715 * The x image coordinate 716 * @param y 717 * The y image coordinate 718 */ 719 public void moveTo(final double x, final double y) 720 { 721 if (this.drawX != x || this.drawY != y) 722 { 723 this.drawX = x; 724 this.drawY = y; 725 this.repaint(); 726 727 for (final ImageComponentListener l : this.listeners) 728 l.imagePanned(x, y); 729 } 730 } 731 732 /** 733 * Set the scale factor to zoom to 734 * 735 * @param sf 736 * The scale factor 737 */ 738 public void zoom(final double sf) 739 { 740 this.scaleFactorX = this.scaleFactorY = sf; 741 this.repaint(); 742 743 for (final ImageComponentListener l : this.listeners) 744 l.imageZoomed(sf); 745 } 746 747 /** 748 * Set the scale factor to draw the image in the x-direction. Allows the 749 * image to be stretched or shrunk horizontally. 750 * 751 * @param sf 752 * The new scale factor 753 */ 754 public void setScaleFactorX(final double sf) 755 { 756 this.scaleFactorX = sf; 757 } 758 759 /** 760 * Set the scale factor to draw the image in the y-direction. Allows the 761 * image to be stretched or shrunk vertically. 762 * 763 * @param sf 764 * The new scale factor 765 */ 766 public void setScaleFactorY(final double sf) 767 { 768 this.scaleFactorY = sf; 769 } 770 771 /** 772 * Set the scale factor to draw the image. Allows the image to be 773 * stretched or shrunk both horizontall or vertically. 774 * 775 * @param sfx 776 * The new x scale factor 777 * @param sfy 778 * The new y scale factor 779 */ 780 public void setScaleFactor(final double sfx, final double sfy) 781 { 782 this.setScaleFactorX(sfx); 783 this.setScaleFactorY(sfy); 784 } 785 786 /** 787 * If you want to be able to inspect the original image's pixel values 788 * (rather than the generated BufferedImage) set the original image 789 * here. Use null to enforce showing the BufferedImage pixel values. 790 * This does not set the BufferedImage that is being used for the 791 * display. 792 * 793 * @param image 794 * The original image. 795 */ 796 public void setOriginalImage(final Image<?, ?> image) 797 { 798 this.originalImage = image; 799 } 800 801 /** 802 * Make sure the x and y position we're drawing the image in is not 803 * going mad. 804 */ 805 private void sanitiseVars() 806 { 807 // Make sure we're not going out of the space 808 // this.moveTo( 809 // Math.max( 810 // this.image.getWidth() / this.scaleFactorX / 2, 811 // Math.min( 812 // this.drawX, 813 // this.image.getWidth() 814 // - (this.getWidth() / 2 / this.scaleFactorX) ) ), 815 // Math.max( this.image.getHeight() / this.scaleFactorY / 2, 816 // Math.min( 817 // this.drawY, 818 // this.image.getHeight() 819 // - (this.getHeight() / 2 / this.scaleFactorY) ) ) ); 820 } 821 822 /** 823 * {@inheritDoc} 824 * 825 * @see javax.swing.JComponent#paint(java.awt.Graphics) 826 */ 827 @Override 828 public void paint(final Graphics gfx) 829 { 830 // Create a double buffer into which we'll draw first. 831 final BufferedImage img = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_3BYTE_BGR); 832 final Graphics2D g = (Graphics2D) img.getGraphics(); 833 834 if (this.drawTransparencyGrid) 835 { 836 final BufferedImage transparencyGrid = new BufferedImage( 837 this.getWidth(), this.getHeight(), 838 BufferedImage.TYPE_3BYTE_BGR); 839 final Graphics tg = transparencyGrid.getGraphics(); 840 841 final int gridSizeX = (int) (20 * this.scaleFactorX); 842 final int gridSizeY = (int) (20 * this.scaleFactorY); 843 for (int y = 0; y < this.getHeight(); y += gridSizeY) 844 { 845 for (int x = 0; x < this.getWidth(); x += gridSizeX) 846 { 847 final int c = (x / gridSizeX + y / gridSizeY) % 2; 848 if (c == 0) 849 tg.setColor(new Color(220, 220, 220)); 850 else 851 tg.setColor(Color.white); 852 853 tg.fillRect(x, y, gridSizeX, gridSizeY); 854 } 855 } 856 857 g.drawImage(transparencyGrid, 0, 0, null); 858 } 859 860 // Draw the image 861 if (this.image != null) 862 { 863 // Scale and translate to the image drawing coordinates 864 g.scale(this.scaleFactorX, this.scaleFactorY); 865 g.translate(-this.drawX, -this.drawY); 866 867 // Blat the image to the screen 868 g.drawImage(this.image, 0, 0, this.image.getWidth(), 869 this.image.getHeight(), null); 870 871 // Reset the graphics back to the original pixel-based coords 872 g.translate(this.drawX, this.drawY); 873 g.scale(1 / this.scaleFactorX, 1 / this.scaleFactorY); 874 875 // If we're to show pixel colours and we're supposed to do it 876 // on this time around... 877 if ((this.showPixelColours || this.showXY) 878 && this.drawPixelColour) 879 { 880 final StringBuffer pixelColourStrB = new StringBuffer(); 881 882 if (this.showXY) 883 pixelColourStrB.append("[" + this.pixelX + "," 884 + this.pixelY + "] "); 885 886 if (this.showPixelColours) 887 pixelColourStrB.append(Arrays 888 .toString(this.currentPixelColour)); 889 890 // Calculate the size to draw 891 final FontMetrics fm = g.getFontMetrics(); 892 final int fw = fm.stringWidth(pixelColourStrB.toString()); 893 final int fh = fm.getHeight() + fm.getDescent(); 894 final int p = 4; // padding 895 final int dx = 0; 896 int dy = this.getHeight() - (fh + p); 897 898 // If the mouse is over where we want to put the box, 899 // we'll move the box to another corner 900 if (this.mouseX <= dx + fw + p && this.mouseX >= dx && 901 this.mouseY >= dy && this.mouseY <= dy + fh + p) 902 dy = 0; 903 904 // Draw a box 905 g.setColor(new Color(0, 0, 0, 0.5f)); 906 g.fillRect(dx, dy, fw + p, fh + p); 907 908 // Draw the text 909 g.setColor(Color.white); 910 g.drawString(pixelColourStrB.toString(), dx + p / 2, dy 911 + fm.getHeight() + p / 2); 912 } 913 } 914 915 // Blat our offscreen image to the screen 916 gfx.drawImage(img, 0, 0, null); 917 918 // Store this displayed image 919 this.displayedImage = img; 920 } 921 922 /** 923 * {@inheritDoc} 924 * 925 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent) 926 */ 927 @Override 928 public void mouseClicked(final MouseEvent e) 929 { 930 if (e.getButton() == MouseEvent.BUTTON1 && this.allowZooming) 931 { 932 if (e.isControlDown()) 933 { 934 // Scale the scalars down 935 this.scaleFactorX /= 2; 936 this.scaleFactorY /= 2; 937 938 final double moveX = this.drawX - e.getX() / this.scaleFactorX / 2; 939 final double moveY = this.drawY - e.getY() / this.scaleFactorY / 2; 940 if (this.allowDragging) 941 this.moveTo(moveX, moveY); 942 else 943 this.moveTo(0, 0); 944 } 945 else 946 { 947 // Scale the scalars up 948 this.scaleFactorX *= 2; 949 this.scaleFactorY *= 2; 950 951 // Make sure we zoom in on the bit the user clicked on 952 if (this.allowDragging) 953 this.moveTo( 954 this.drawX + e.getX() / this.scaleFactorX, 955 this.drawY + e.getY() / this.scaleFactorY); 956 else 957 this.moveTo(0, 0); 958 } 959 960 // Make sure we're not going to draw out of bounds. 961 this.sanitiseVars(); 962 963 this.repaint(); 964 } 965 } 966 967 @Override 968 public void mousePressed(final MouseEvent e) 969 { 970 if (this.allowDragging) 971 { 972 this.dragStartX = e.getX(); 973 this.dragStartY = e.getY(); 974 } 975 } 976 977 @Override 978 public void mouseReleased(final MouseEvent e) 979 { 980 } 981 982 @Override 983 public void mouseEntered(final MouseEvent e) 984 { 985 } 986 987 @Override 988 public void mouseExited(final MouseEvent e) 989 { 990 this.drawPixelColour = false; 991 this.repaint(); 992 } 993 994 @Override 995 public void mouseDragged(final MouseEvent e) 996 { 997 if (!this.allowDragging) 998 return; 999 1000 final int diffx = e.getX() - this.dragStartX; 1001 final int diffy = e.getY() - this.dragStartY; 1002 1003 if (diffx == 0 && diffy == 0) 1004 return; 1005 1006 // Update the draw position 1007 this.moveTo(this.drawX - diffx / this.scaleFactorX, 1008 this.drawY - diffy / this.scaleFactorY); 1009 1010 // Reset the draggers 1011 this.dragStartX = e.getX(); 1012 this.dragStartY = e.getY(); 1013 1014 // Make sure the drag stays within the bounds 1015 this.sanitiseVars(); 1016 1017 // Redraw the component 1018 this.repaint(); 1019 } 1020 1021 @Override 1022 public void mouseMoved(final MouseEvent e) 1023 { 1024 if (this.image == null) 1025 return; 1026 1027 // Convert the screen coords into image coords 1028 final double x = e.getX() / this.scaleFactorX + this.drawX; 1029 final double y = e.getY() / this.scaleFactorY + this.drawY; 1030 1031 // If we're outside the image we don't print anything 1032 if (x >= this.image.getWidth() || y >= this.image.getHeight() || 1033 x < 0 || y < 0) 1034 { 1035 this.drawPixelColour = false; 1036 this.repaint(); 1037 return; 1038 } 1039 1040 // Pixel coordinates in the image 1041 this.pixelX = (int) x; 1042 this.pixelY = (int) y; 1043 1044 this.mouseX = e.getX(); 1045 this.mouseY = e.getY(); 1046 1047 this.updatePixelColours(); 1048 } 1049 1050 /** 1051 * Update the display of pixel colours 1052 */ 1053 protected void updatePixelColours() 1054 { 1055 if (this.showPixelColours && this.image != null) 1056 { 1057 // If we don't have the original image, we'll just use the 1058 // colours from the BufferedImage 1059 if (this.originalImage == null) 1060 { 1061 final int colour = this.image.getRGB(this.pixelX, this.pixelY); 1062 this.currentPixelColour = new Float[3]; 1063 this.currentPixelColour[0] = (float) ((colour & 0x00ff0000) >> 16); 1064 this.currentPixelColour[1] = (float) ((colour & 0x0000ff00) >> 8); 1065 this.currentPixelColour[2] = (float) ((colour & 0x000000ff)); 1066 } 1067 else 1068 { 1069 // If we're outside of the original image's coordinates, 1070 // we don't need to do anything else.. 1071 if (this.pixelX >= this.originalImage.getWidth() || this.pixelX < 0 || 1072 this.pixelY >= this.originalImage.getHeight() || this.pixelY < 0) 1073 return; 1074 1075 // If we have the original image we get each of the bands 1076 // from it and update the current pixel colour member 1077 if (this.originalImage instanceof FImage) 1078 { 1079 final Object o = this.originalImage.getPixel(this.pixelX, this.pixelY); 1080 this.currentPixelColour = new Float[1]; 1081 this.currentPixelColour[0] = (Float) o; 1082 } 1083 else if (this.originalImage instanceof MBFImage) 1084 { 1085 final MBFImage i = (MBFImage) this.originalImage; 1086 this.currentPixelColour = new Float[i.numBands()]; 1087 for (int b = 0; b < i.numBands(); b++) 1088 this.currentPixelColour[b] = i.getBand(b) 1089 .getPixel(this.pixelX, this.pixelY); 1090 } 1091 } 1092 1093 this.drawPixelColour = true; 1094 this.repaint(); 1095 } 1096 1097 if (this.showXY) 1098 { 1099 this.drawPixelColour = true; 1100 this.repaint(); 1101 } 1102 } 1103 1104 /** 1105 * Sets whether to automatically size the image to fit within the bounds 1106 * of the image component which is being sized externally. This 1107 * shouldn't be used in combination with autoResize. When this method is 1108 * called with TRUE, zooming and dragging are disabled. 1109 * 1110 * @param tf 1111 * TRUE to auto fit the image. 1112 */ 1113 public void setAutoFit(final boolean tf) 1114 { 1115 this.autoFit = tf; 1116 if (this.autoFit) 1117 { 1118 this.allowZooming = false; 1119 this.allowDragging = false; 1120 } 1121 } 1122 1123 /** 1124 * Sets whether to keep the aspect ratio of the image constant when the 1125 * image is being autoFit into the component. 1126 * 1127 * @param tf 1128 * TRUE to keep the aspect ratio constant 1129 */ 1130 public void setKeepAspect(final boolean tf) 1131 { 1132 this.keepAspect = tf; 1133 } 1134 1135 /** 1136 * Sets whether to automatically resize the component to fit image (at 1137 * it's given scale factor) within it. Note that in certain 1138 * circumstances, where the image component is being sized by external 1139 * forces (such as a layout manager), setting this to true can cause 1140 * weird results where the image is pulled out and in constantly. This 1141 * shouldn't be used in combination with autoFit. 1142 * 1143 * @param tf 1144 * TRUE to resize the component. 1145 */ 1146 public void setAutoResize(final boolean tf) 1147 { 1148 this.autoResize = tf; 1149 } 1150 1151 /** 1152 * Sets whether the component is to attempt to pack a frame into which 1153 * it is added. If it is not in a frame this will have no effect. This 1154 * allows the frame to resize with the component. 1155 * 1156 * @param tf 1157 * TRUE to auto pack the parent frame. 1158 */ 1159 public void setAutoPack(final boolean tf) 1160 { 1161 this.autoPack = tf; 1162 } 1163 1164 /** 1165 * Returns the current mouse position in pixels within the viewport. 1166 * Will return the last known position if the mouse is no longer within 1167 * the viewport. 1168 * 1169 * @return The position in pixels 1170 */ 1171 public Point2d getCurrentMousePosition() 1172 { 1173 return new Point2dImpl(this.mouseX, this.mouseY); 1174 } 1175 1176 /** 1177 * Returns the current mouse position in the coordinates of the image 1178 * and is determined by the scaling factors and the position of the 1179 * image within the viewport. If the mouse is no longer in the viewport, 1180 * the last known mouse position will be returned. 1181 * 1182 * @return The position in image coordinates. 1183 */ 1184 public Point2d getCurrentMouseImagePosition() 1185 { 1186 return new Point2dImpl(this.pixelX, this.pixelY); 1187 } 1188 1189 /** 1190 * Returns the current pixel colour at the point of the mouse. The 1191 * number of elements in the array will equal be 3, if no original has 1192 * been supplied to the image component. The values will be between 0 1193 * and 255 and ordered red, green and blue. If the original has been 1194 * supplied, then the number of elements will be equal to the number of 1195 * bands in the original image and the values will be the original pixel 1196 * values in the original image. 1197 * 1198 * @return The current pixel colour. 1199 */ 1200 public Float[] getCurrentPixelColour() 1201 { 1202 return this.currentPixelColour; 1203 } 1204 1205 /** 1206 * Returns the current displayed pixel colour (as an RGB encoded int) 1207 * from the currently displayed image. 1208 * 1209 * @return The current displayed pixel colour. 1210 */ 1211 public int getCurrentDisplayedPixelColour() 1212 { 1213 return this.displayedImage.getRGB(this.mouseX, this.mouseY); 1214 } 1215 1216 /** 1217 * Returns the currently displaying image. 1218 * 1219 * @return The displayed image. 1220 */ 1221 public BufferedImage getDisplayedImage() 1222 { 1223 return this.displayedImage; 1224 } 1225 } 1226 1227 /** 1228 * An extension of {@link ImageComponent} that scales the displayed image. 1229 * 1230 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 1231 * 1232 */ 1233 public static class ScalingImageComponent extends ImageComponent 1234 { 1235 /** 1236 * 1237 */ 1238 private static final long serialVersionUID = 1L; 1239 1240 @Override 1241 public void paint(final Graphics g) 1242 { 1243 final Component f = SwingUtilities.getRoot(this); 1244 if (this.image != null) 1245 g.drawImage(this.image, 0, 0, this.getWidth(), 1246 this.getHeight(), f); 1247 } 1248 } 1249 1250 /** 1251 * Display an image in the given frame 1252 * 1253 * @param image 1254 * the image 1255 * @param frame 1256 * the frame 1257 * @return the frame 1258 */ 1259 public static JFrame display(final BufferedImage image, final JFrame frame) 1260 { 1261 return DisplayUtilities.display(image, frame, null); 1262 } 1263 1264 /** 1265 * Displays an image in the given named window 1266 * 1267 * @param image 1268 * The image 1269 * @param name 1270 * The name of the window 1271 * @return The frame that was created. 1272 */ 1273 public static JFrame displayName(final BufferedImage image, final String name) 1274 { 1275 final JFrame f = DisplayUtilities.createNamedWindow(name); 1276 return DisplayUtilities.display(image, f); 1277 1278 } 1279 1280 /** 1281 * Display an image in the given frame 1282 * 1283 * @param image 1284 * the image 1285 * @param frame 1286 * the frame 1287 * @param originalImage 1288 * the original image 1289 * @return the frame 1290 */ 1291 public static JFrame display(final BufferedImage image, 1292 final JFrame frame, final Image<?, ?> originalImage) 1293 { 1294 if (frame == null) 1295 return DisplayUtilities.makeDisplayFrame("Image: " 1296 + DisplayUtilities.windowCount, image.getWidth(), 1297 image.getHeight(), image); 1298 1299 if (frame.getContentPane().getComponentCount() > 0 1300 && frame.getContentPane().getComponent(0) instanceof ImageComponent) 1301 { 1302 final ImageComponent cmp = ((ImageComponent) frame.getContentPane() 1303 .getComponent(0)); 1304 if (!frame.isVisible()) 1305 { 1306 final boolean ar = cmp.autoResize; 1307 final boolean ap = cmp.autoPack; 1308 cmp.autoResize = true; 1309 cmp.autoPack = true; 1310 cmp.setImage(image); 1311 cmp.setOriginalImage(originalImage); 1312 cmp.autoResize = ar; 1313 cmp.autoPack = ap; 1314 frame.setVisible(true); 1315 } 1316 else 1317 { 1318 cmp.setImage(image); 1319 cmp.setOriginalImage(originalImage); 1320 } 1321 } 1322 else 1323 { 1324 frame.getContentPane().removeAll(); 1325 1326 final ImageComponent c = new ImageComponent(image); 1327 c.setOriginalImage(originalImage); 1328 1329 frame.add(c); 1330 frame.pack(); 1331 frame.setVisible(true); 1332 } 1333 return frame; 1334 } 1335 1336 /** 1337 * Make a frame with the given title. 1338 * 1339 * @param title 1340 * the title 1341 * @return the frame 1342 */ 1343 public static JFrame makeFrame(final String title) 1344 { 1345 final JFrame f = new JFrame(title); 1346 f.setResizable(false); 1347 f.setUndecorated(false); 1348 1349 f.addWindowListener(new WindowAdapter() 1350 { 1351 @Override 1352 public void windowClosing(final WindowEvent evt) 1353 { 1354 DisplayUtilities.windowOpenCount = DisplayUtilities.windowCount - 1; 1355 f.dispose(); 1356 } 1357 }); 1358 return f; 1359 } 1360 1361 /** 1362 * Display an image with the given title. No additional functionality, such 1363 * as zooming, is enabled. 1364 * 1365 * @param image 1366 * the image 1367 * @param title 1368 * the title 1369 * @return frame containing the image 1370 */ 1371 public static JFrame displaySimple(final BufferedImage image, 1372 final String title) 1373 { 1374 return DisplayUtilities.displaySimple(image, title, null); 1375 } 1376 1377 /** 1378 * Display an image with the given title 1379 * 1380 * @param image 1381 * the image 1382 * @param title 1383 * the title 1384 * @return frame containing the image 1385 */ 1386 public static JFrame display(final BufferedImage image, final String title) 1387 { 1388 return DisplayUtilities.display(image, title, null); 1389 } 1390 1391 /** 1392 * Display an image with the given title. No additional functionality, such 1393 * as zooming, is enabled. 1394 * 1395 * @param image 1396 * the image 1397 * @param title 1398 * the title 1399 * @param originalImage 1400 * original image 1401 * @return frame containing the image 1402 */ 1403 public static JFrame displaySimple(final BufferedImage image, 1404 final String title, final Image<?, ?> originalImage) 1405 { 1406 if (GraphicsEnvironment.isHeadless()) 1407 return null; 1408 1409 return DisplayUtilities.makeDisplayFrameSimple(title, 1410 image.getWidth(), image.getHeight(), image, originalImage); 1411 } 1412 1413 /** 1414 * Get a frame that will display an image. No additional functionality, such 1415 * as zooming, is enabled. 1416 * 1417 * @param title 1418 * the frame title 1419 * @param width 1420 * the frame width 1421 * @param height 1422 * the frame height 1423 * @param img 1424 * the image to display 1425 * @param originalImage 1426 * the original image 1427 * @return A {@link JFrame} that allows images to be displayed. 1428 */ 1429 public static JFrame makeDisplayFrameSimple(final String title, 1430 final int width, final int height, final BufferedImage img, 1431 final Image<?, ?> originalImage) 1432 { 1433 final JFrame f = DisplayUtilities.makeFrame(title); 1434 1435 final ImageComponent c = new ImageComponent(); 1436 if (img != null) 1437 c.setImage(img); 1438 c.setOriginalImage(originalImage); 1439 c.setSize(width, height); 1440 c.setPreferredSize(new Dimension(c.getWidth(), c.getHeight())); 1441 1442 c.removeMouseListener(c); 1443 c.removeMouseMotionListener(c); 1444 c.setShowPixelColours(false); 1445 c.setShowXYPosition(false); 1446 c.setAllowZoom(false); 1447 c.setAutoscrolls(false); 1448 c.setAllowPanning(false); 1449 1450 f.add(c); 1451 f.pack(); 1452 f.setVisible(img != null); 1453 1454 DisplayUtilities.windowCount++; 1455 1456 return f; 1457 } 1458 1459 /** 1460 * Display an image with the given title 1461 * 1462 * @param image 1463 * the image 1464 * @param title 1465 * the title 1466 * @param originalImage 1467 * original image 1468 * @return frame containing the image 1469 */ 1470 public static JFrame display(final BufferedImage image, 1471 final String title, final Image<?, ?> originalImage) 1472 { 1473 if (GraphicsEnvironment.isHeadless()) 1474 return null; 1475 1476 return DisplayUtilities.makeDisplayFrame(title, image.getWidth(), 1477 image.getHeight(), image, originalImage); 1478 } 1479 1480 /** 1481 * Get a frame that will display an image. 1482 * 1483 * @param title 1484 * the frame title 1485 * @param width 1486 * the frame width 1487 * @param height 1488 * the frame height 1489 * @return A {@link JFrame} that allows images to be displayed. 1490 */ 1491 public static JFrame makeDisplayFrame(final String title, final int width, 1492 final int height) 1493 { 1494 return DisplayUtilities.makeDisplayFrame(title, width, height, null); 1495 } 1496 1497 /** 1498 * Get a frame that will display an image. 1499 * 1500 * @param title 1501 * the frame title 1502 * @param width 1503 * the frame width 1504 * @param height 1505 * the frame height 1506 * @param img 1507 * the image to display 1508 * @return A {@link JFrame} that allows images to be displayed. 1509 */ 1510 public static JFrame makeDisplayFrame(final String title, final int width, 1511 final int height, final BufferedImage img) 1512 { 1513 return DisplayUtilities.makeDisplayFrame(title, width, height, img, 1514 null); 1515 } 1516 1517 /** 1518 * Get a frame that will display an image. 1519 * 1520 * @param title 1521 * the frame title 1522 * @param width 1523 * the frame width 1524 * @param height 1525 * the frame height 1526 * @param img 1527 * the image to display 1528 * @param originalImage 1529 * the original image 1530 * @return A {@link JFrame} that allows images to be displayed. 1531 */ 1532 public static JFrame makeDisplayFrame(final String title, final int width, 1533 final int height, final BufferedImage img, 1534 final Image<?, ?> originalImage) 1535 { 1536 final JFrame f = DisplayUtilities.makeFrame(title); 1537 1538 final ImageComponent c = new ImageComponent(); 1539 if (img != null) 1540 c.setImage(img); 1541 c.setOriginalImage(originalImage); 1542 c.setSize(width, height); 1543 c.setPreferredSize(new Dimension(c.getWidth(), c.getHeight())); 1544 1545 f.add(c); 1546 f.pack(); 1547 f.setVisible(img != null); 1548 1549 DisplayUtilities.windowCount++; 1550 1551 return f; 1552 } 1553 1554 /** 1555 * Render a connected component and display it 1556 * 1557 * @param input 1558 * the connected component 1559 * @return frame containing the rendered image 1560 */ 1561 public static JFrame display(final ConnectedComponent input) 1562 { 1563 return DisplayUtilities.display(input, 1.0f); 1564 } 1565 1566 /** 1567 * Render a connected component with a given grey level and display it 1568 * 1569 * @param input 1570 * the connected component 1571 * @param col 1572 * the grey level 1573 * @return frame containing the rendered image 1574 */ 1575 public static JFrame display(final ConnectedComponent input, 1576 final float col) 1577 { 1578 final ConnectedComponent cc = input.clone(); 1579 1580 final Rectangle bb = cc.calculateRegularBoundingBox(); 1581 1582 // Render the mask, leaving a 10 px border 1583 cc.translate(10 - (int) bb.x, 10 - (int) bb.y); 1584 final FImage mask = new FImage((int) Math.max(bb.width + 20, 100), 1585 (int) Math.max(bb.height + 20, 100)); 1586 final BlobRenderer<Float> br = new BlobRenderer<Float>(mask, 1.0F); 1587 cc.process(br); 1588 1589 return DisplayUtilities.display(mask); 1590 } 1591 1592 /** 1593 * Render a polygon to an image and display it. 1594 * 1595 * @param input 1596 * the polygon 1597 * @return the frame 1598 */ 1599 public static JFrame display(final Polygon input) 1600 { 1601 return DisplayUtilities.display(input, 1.0f); 1602 } 1603 1604 /** 1605 * Render a polygon with a given grey level and display it 1606 * 1607 * @param input 1608 * the polygon 1609 * @param col 1610 * the grey level 1611 * @return frame containing the rendered image 1612 */ 1613 public static JFrame display(final Polygon input, final float col) 1614 { 1615 final Polygon p = input.clone(); 1616 1617 final Rectangle bb = p.calculateRegularBoundingBox(); 1618 1619 // Render the mask, leaving a 1 px border 1620 p.translate(10 - bb.x, 10 - bb.y); 1621 final FImage mask = new FImage((int) (bb.width + 20), 1622 (int) (bb.height + 20)); 1623 mask.createRenderer().drawPolygon(p, col); 1624 1625 return DisplayUtilities.display(mask); 1626 } 1627 1628 /** 1629 * Display multiple images in an array 1630 * 1631 * @param title 1632 * the frame title 1633 * @param images 1634 * the images 1635 * @return the frame 1636 */ 1637 public static JFrame display(final String title, 1638 final Image<?, ?>... images) 1639 { 1640 final BufferedImage[] bimages = new BufferedImage[images.length]; 1641 1642 for (int i = 0; i < images.length; i++) 1643 bimages[i] = ImageUtilities 1644 .createBufferedImageForDisplay(images[i]); 1645 1646 return DisplayUtilities.display(title, bimages); 1647 } 1648 1649 /** 1650 * Display multiple images in a collection 1651 * 1652 * @param title 1653 * the frame title 1654 * @param images 1655 * the images 1656 * @return the frame 1657 */ 1658 public static JFrame display(final String title, 1659 final Collection<? extends Image<?, ?>> images) 1660 { 1661 final BufferedImage[] bimages = new BufferedImage[images.size()]; 1662 1663 int i = 0; 1664 for (final Image<?, ?> img : images) 1665 bimages[i++] = ImageUtilities 1666 .createBufferedImageForDisplay(img); 1667 1668 return DisplayUtilities.display(title, bimages); 1669 } 1670 1671 /** 1672 * Display multiple images in an array 1673 * 1674 * @param title 1675 * the frame title 1676 * @param cols 1677 * number of columns 1678 * @param images 1679 * the images 1680 * @return the frame 1681 */ 1682 public static JFrame display(final String title, final int cols, 1683 final Image<?, ?>... images) 1684 { 1685 final JFrame f = new JFrame(title); 1686 1687 f.getContentPane().setLayout(new GridLayout(0, cols)); 1688 1689 for (final Image<?, ?> image : images) 1690 { 1691 if (image != null) 1692 { 1693 final ImageComponent ic = new ImageComponent( 1694 ImageUtilities.createBufferedImageForDisplay(image)); 1695 ic.setOriginalImage(image); 1696 f.getContentPane().add(ic); 1697 } 1698 } 1699 1700 f.pack(); 1701 f.setVisible(true); 1702 1703 return f; 1704 } 1705 1706 /** 1707 * Display multiple images in an array 1708 * 1709 * @param title 1710 * the frame title 1711 * @param cols 1712 * number of columns 1713 * @param images 1714 * the images 1715 * @return the frame 1716 */ 1717 public static JFrame displayLinked(final String title, final int cols, 1718 final Image<?, ?>... images) 1719 { 1720 final JFrame f = new JFrame(title); 1721 1722 f.getContentPane().setLayout(new GridLayout(0, cols)); 1723 1724 ImageComponent ic = null; 1725 for (final Image<?, ?> image : images) 1726 { 1727 if (image != null) 1728 { 1729 final ImageComponent ic2 = new ImageComponent( 1730 ImageUtilities.createBufferedImageForDisplay(image)); 1731 1732 if (ic != null) 1733 { 1734 ic.addImageComponentListener(new ImageComponentListener() 1735 { 1736 @Override 1737 public void imageZoomed(final double newScaleFactor) 1738 { 1739 ic2.zoom(newScaleFactor); 1740 } 1741 1742 @Override 1743 public void imagePanned(final double newX, 1744 final double newY) 1745 { 1746 ic2.moveTo(newX, newY); 1747 } 1748 }); 1749 } 1750 1751 ic2.setOriginalImage(image); 1752 f.getContentPane().add(ic2); 1753 1754 ic = ic2; 1755 } 1756 } 1757 1758 f.pack(); 1759 f.setVisible(true); 1760 1761 return f; 1762 } 1763 1764 /** 1765 * Display multiple images in an array of frames 1766 * 1767 * @param title 1768 * the frame title 1769 * @param images 1770 * the images 1771 * @return the frame 1772 */ 1773 public static JFrame display(final String title, 1774 final BufferedImage... images) 1775 { 1776 if (GraphicsEnvironment.isHeadless()) 1777 return null; 1778 1779 final JFrame f = new JFrame(title); 1780 1781 final int box_size = 200; 1782 final int n_images = images.length; 1783 final int n_boxes_x = 4; 1784 final int width = n_boxes_x * box_size; 1785 final int height = box_size * n_images / n_boxes_x; 1786 1787 f.addWindowListener(new WindowAdapter() 1788 { 1789 @Override 1790 public void windowClosing(final WindowEvent evt) 1791 { 1792 DisplayUtilities.windowOpenCount = DisplayUtilities.windowCount - 1; 1793 f.dispose(); 1794 } 1795 }); 1796 1797 final Container scrollContainer = new Container(); 1798 scrollContainer.setLayout(new FlowLayout()); 1799 1800 final Container container = new Container(); 1801 container.setSize(new Dimension(width, height)); 1802 container.setPreferredSize(new Dimension(width, height)); 1803 container.setLayout(new GridLayout(0, n_boxes_x)); 1804 scrollContainer.add(container); 1805 1806 for (final BufferedImage img : images) 1807 { 1808 final JComponent c = new JComponent() 1809 { 1810 private static final long serialVersionUID = 1L; 1811 1812 @Override 1813 public void paint(final Graphics g) 1814 { 1815 final int cw = this.getWidth(); 1816 final int ch = this.getHeight(); 1817 if (img.getWidth() < cw && img.getHeight() < ch) 1818 { 1819 final int x = (cw - img.getWidth()) / 2; 1820 final int y = (ch - img.getHeight()) / 2; 1821 g.drawImage(img, x, y, img.getWidth(), 1822 img.getHeight(), f); 1823 } 1824 else if (img.getWidth() > img.getHeight()) 1825 { 1826 final float sf = (float) cw / (float) img.getWidth(); 1827 final int h = Math.round(sf * img.getHeight()); 1828 g.drawImage(img, 0, (ch - h) / 2, cw, h, f); 1829 } 1830 else 1831 { 1832 final float sf = (float) ch / (float) img.getHeight(); 1833 final int w = Math.round(sf * img.getWidth()); 1834 g.drawImage(img, (cw - w) / 2, 0, w, ch, f); 1835 } 1836 // TODO: scale image proportionally and draw centered 1837 1838 } 1839 }; 1840 c.setSize(200, 200); 1841 c.setPreferredSize(new Dimension(c.getWidth(), c.getHeight())); 1842 container.add(c); 1843 } 1844 f.setSize(new Dimension(840, 600)); 1845 f.setPreferredSize(new Dimension(840, 600)); 1846 1847 f.getContentPane().add(new JScrollPane(scrollContainer)); 1848 1849 f.pack(); 1850 f.setVisible(true); 1851 1852 DisplayUtilities.windowCount++; 1853 1854 return f; 1855 } 1856 1857}