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