All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hu.kazocsaba.imageviewer.ImageViewer Maven / Gradle / Ivy

package hu.kazocsaba.imageviewer;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JViewport;

/**
 * A general purpose image viewer component. It contains a scroll pane and manages the size of the image in accordance
 * with the {@link #setResizeStrategy(ResizeStrategy) resize strategy} property. It can also provide a popup menu
 * containing generally useful viewing controls and functions; this default popup can be disabled by passing
 * {@code defaultPopupMenu = null} to the constructor.
 * 

* The Swing component that can be added to the GUI is obtained by calling * {@link #getComponent()}. *

* ImageViewer supports {@link #setStatusBar(StatusBar) status bars}: arbitrary * components that can be added to the viewer and are displayed below the image. *

* {@link #addOverlay Overlays} can also be added to the viewer; for details, see * the the documentation of the {@link Overlay} class. * @see StatusBar * @see Overlay * @author Kazó Csaba */ public final class ImageViewer { private final LayeredImageView view; private ImageComponent theImage; private final JScrollPane scroller; private JPanel panel; private StatusBar statusBar; private boolean statusBarVisible=false; private PropertyChangeSupport propertyChangeSupport; private Synchronizer synchronizer; /** * Creates a new image viewer. Initially it will be empty, and it will have a default popup menu. */ public ImageViewer() { this(null); } /** * Creates a new image viewer displaying the specified image. TThe viewer will have a default popup menu. * @param image the image to display; if {@code null} then no image is displayed */ public ImageViewer(BufferedImage image) { this(image, true); } /** * Creates a new image viewer displaying the specified image. * @param image the image to display; if null then no image is displayed * @param defaultPopupMenu if {@code true}, then a default popup menu will be created and registered for the viewer * @see #setImage(BufferedImage) */ public ImageViewer(BufferedImage image, boolean defaultPopupMenu) { propertyChangeSupport=new PropertyChangeSupport(this); panel=new JPanel(new BorderLayout()); scroller=new JScrollPane() { @Override protected JViewport createViewport() { return new JViewport() { @Override protected LayoutManager createLayoutManager() { return new CustomViewportLayout(ImageViewer.this); } @Override public Dimension getMaximumSize() { return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } }; } @Override public boolean isValidateRoot() { return false; } }; synchronizer=new Synchronizer(this); theImage=new ImageComponent(this, propertyChangeSupport); view=new LayeredImageView(theImage); scroller.setViewportView(view.getComponent()); theImage.setImage(image); panel.add(scroller, BorderLayout.CENTER); setStatusBar(new DefaultStatusBar()); if (defaultPopupMenu) { theImage.addMouseListener(new MouseAdapter() { JPopupMenu popup; private void showPopup(MouseEvent e) { e.consume(); if (popup==null) popup=new DefaultViewerPopup(ImageViewer.this); Point p = panel.getPopupLocation(e); if (p == null) { p = e.getPoint(); } popup.show(e.getComponent(), p.x, p.y); } @Override public void mousePressed(MouseEvent e) { if (e.isPopupTrigger()) { showPopup(e); } } @Override public void mouseReleased(MouseEvent e) { if (e.isPopupTrigger()) { showPopup(e); } } }); } } /** * Sets the status bar component for this image viewer. The new status bar is made * visible only if the statusBarVisible property is true. If statusBar is * null this method removes any existing status bar. * @param statusBar the new status bar component to set * @throws IllegalArgumentException if the status bar has already been added to a * different image viewer */ public void setStatusBar(StatusBar statusBar) { if (statusBar==this.statusBar) return; if (statusBar.getImageViewer()!=null) throw new IllegalArgumentException("Status bar already added to an image viewer"); StatusBar oldStatusBar=this.statusBar; if (oldStatusBar!=null) { panel.remove(oldStatusBar.getComponent()); oldStatusBar.setImageViewer(null); } this.statusBar=statusBar; if (this.statusBar!=null) { this.statusBar.setImageViewer(this); if (statusBarVisible) { panel.add(this.statusBar.getComponent(), BorderLayout.SOUTH); panel.revalidate(); panel.repaint(); } } propertyChangeSupport.firePropertyChange("statusBar", oldStatusBar, statusBar); } /** * Returns the status bar currently associated with this viewer. * @return the current status bar, or {@code null} if the viewer has no status bar */ public StatusBar getStatusBar() { return statusBar; } /** * Sets whether the status bar is visible. The status bar is hidden by default. * @param statusBarVisible true, if the status bar should be visible; false otherwise */ public void setStatusBarVisible(boolean statusBarVisible) { if (this.statusBarVisible == statusBarVisible) return; if (statusBar!=null) { if (statusBarVisible) panel.add(statusBar.getComponent(), BorderLayout.SOUTH); else panel.remove(statusBar.getComponent()); panel.revalidate(); panel.repaint(); } boolean prev = this.statusBarVisible; this.statusBarVisible = statusBarVisible; synchronizer.statusBarVisibilityChanged(this); propertyChangeSupport.firePropertyChange("statusBarVisible", prev, statusBarVisible); } /** * Returns whether the status bar is set to be visible. The status bar is hidden by default. * @return the statusBarVisible property */ public boolean isStatusBarVisible() { return statusBarVisible; } /** * Returns the image viewer component that can be displayed. * @return the image viewer component */ public JComponent getComponent() { return panel; } /** * Sets the image displayed by the viewer. If the argument is the same object as the image currently being displayed, * then this method will trigger a refresh. If you modify the image shown by the viewer, use this function to notify * the component and cause it to update. * @param image the new image to display; if null then no image is displayed */ public void setImage(BufferedImage image) { theImage.setImage(image); } /** * Returns the currently displayed image. * @return the current image, or null if no image is displayed */ public BufferedImage getImage() { return theImage.getImage(); } /** * Sets the resize strategy this viewer should use. The default is {@link ResizeStrategy#SHRINK_TO_FIT}. * @param resizeStrategy the new resize strategy */ public void setResizeStrategy(ResizeStrategy resizeStrategy) { theImage.setResizeStrategy(resizeStrategy); } /** * Returns the current resize strategy. The default is {@link ResizeStrategy#SHRINK_TO_FIT}. * @return the current resize strategy */ public ResizeStrategy getResizeStrategy() { return theImage.getResizeStrategy(); } /** * Sets whether the image should be resized with nearest neighbor interpolation when it is expanded. * The default is {@code false}. * @param pixelatedZoom the new value of the pixelatedZoom property */ public void setPixelatedZoom(boolean pixelatedZoom) { theImage.setPixelatedZoom(pixelatedZoom); } /** * Returns the current pixelated zoom setting. The default is {@code false}. * @return the current pixelated zoom setting */ public boolean isPixelatedZoom() { return theImage.isPixelatedZoom(); } /** * Returns the current interpolation type. The default is {@link java.awt.RenderingHints#VALUE_INTERPOLATION_BICUBIC}. * @return the interpolation type * @see #setInterpolationType(Object) */ public Object getInterpolationType() { return theImage.getInterpolationType(); } /** * Sets the interpolation type to use when resizing images. See {@link java.awt.RenderingHints#KEY_INTERPOLATION} * for details. The default value is {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC}. *

* The allowed values are: *

    *
  • {@link java.awt.RenderingHints#VALUE_INTERPOLATION_NEAREST_NEIGHBOR} *
  • {@link java.awt.RenderingHints#VALUE_INTERPOLATION_BILINEAR} *
  • {@link java.awt.RenderingHints#VALUE_INTERPOLATION_BICUBIC} (default) *
* Changing the interpolation type to bilinear or nearest neighbor improves painting performance when the image * needs to be resized. *

* Note: when the {@code pixelatedZoom} property is set to true and the image is enlarged, then the nearest * neighbor method is used regardless of this setting. * @param type the interpolation type to use when resizing images * @throws IllegalArgumentException if the parameter is not one of the allowed values */ public void setInterpolationType(Object type) { theImage.setInterpolationType(type); } /** * Returns the zoom factor used when resize strategy is CUSTOM_ZOOM. The default value is 1. * @return the custom zoom factor */ public double getZoomFactor() { return theImage.getZoomFactor(); } /** * Sets the zoom factor to use when the resize strategy is CUSTOM_ZOOM. The default value is 1. *

* Note that calling this function does not change the current resize strategy. * @param newZoomFactor the new zoom factor for the CUSTOM_ZOOM strategy * @throws IllegalArgumentException if {@code newZoomFactor} is not a positive number */ public void setZoomFactor(double newZoomFactor) { theImage.setZoomFactor(newZoomFactor); } /** * Returns the transformation that is applied to the image. Most commonly the * transformation is the concatenation of a uniform scale and a translation. *

* The AffineTransform * instance returned by this method should not be modified. * @return the transformation applied to the image before painting * @throws IllegalStateException if there is no image set or if the size of the viewer is 0 (for example because * it is not in a visible component) */ public AffineTransform getImageTransform() { return theImage.getImageTransform(); } /** * Adds an overlay as the specified layer. * @param overlay the overlay to add * @param layer the layer to add the overlay to; higher layers are on top of lower layers; * the image resides in layer 0 */ public void addOverlay(Overlay overlay, int layer) { view.addOverlay(overlay, layer); } /** * Adds an overlay to layer 1. * @param overlay the overlay to add */ public void addOverlay(Overlay overlay) { addOverlay(overlay, 1); } /** * Removes an overlay from the image viewer. * @param overlay the overlay to remove * @throws IllegalArgumentException if the overlay is not in the image viewer */ public void removeOverlay(Overlay overlay) { view.removeOverlay(overlay); } /** * Adds the specified mouse listener to receive mouse events from * the image component of this image viewer. If listener l * is null, no exception is thrown and no action is performed. * @param l the mouse listener */ public void addMouseListener(MouseListener l) { theImage.addMouseListener(l); } /** * Removes the specified mouse listener so that it no longer receives * mouse motion events from the image component of this image viewer. This method * performs no function, nor does it throw an exception, if the listener specified * by the argument was not previously added to this component. If listener * l is null, no exception is thrown and no action is * performed. * @param l the mouse motion listener */ public void removeMouseListener(MouseListener l) { theImage.removeMouseListener(l); } /** * Adds the specified mouse motion listener to receive mouse events from * the image component of this image viewer. If listener l * is null, no exception is thrown and no action is performed. * @param l the mouse listener */ public void addMouseMotionListener(MouseMotionListener l) { theImage.addMouseMotionListener(l); } /** * Removes the specified mouse motion listener so that it no longer receives * mouse motion events from the image component of this image viewer. This method * performs no function, nor does it throw an exception, if the listener specified * by the argument was not previously added to this component. If listener * l is null, no exception is thrown and no action is * performed. * @param l the mouse motion listener */ public void removeMouseMotionListener(MouseMotionListener l) { theImage.removeMouseMotionListener(l); } /** * Adds the specified image mouse motion listener to this viewer. The listener is notified as the mouse * moves over pixels of the image. If listener l * is {@code null}, no exception is thrown and no action is performed. * @param l the image mouse motion listener */ public void addImageMouseMotionListener(ImageMouseMotionListener l) { theImage.addImageMouseMoveListener(l); } /** * Removes the specified image mouse motion listener so that it no longer receives * mouse motion events from the image component of this image viewer. This method * performs no function, nor does it throw an exception, if the listener specified * by the argument was not previously added to this component. If listener * l is {@code null}, no exception is thrown and no action is * performed. * @param l the mouse motion listener */ public void removeImageMouseMotionListener(ImageMouseMotionListener l) { theImage.removeImageMouseMoveListener(l); } /** * Adds the specified image mouse listener to this viewer. The listener is notified as mouse buttons are clicked * over pixels of the image. If listener l * is {@code null}, no exception is thrown and no action is performed. * @param l the image mouse motion listener */ public void addImageMouseClickListener(ImageMouseClickListener l) { theImage.addImageMouseClickListener(l); } /** * Removes the specified image mouse listener so that it no longer receives * mouse click events from the image component of this image viewer. This method * performs no function, nor does it throw an exception, if the listener specified * by the argument was not previously added to this component. If the listener * l is {@code null}, no exception is thrown and no action is * performed. * @param l the mouse motion listener */ public void removeImageMouseClickListener(ImageMouseClickListener l) { theImage.removeImageMouseClickListener(l); } /** * Adds a {@code PropertyChangeListener} to the listener list. * The same listener object may be added more than once, and will be * called as many times as it is added. If the listener is {@code null}, * no exception is thrown and no action is taken. * @param l the listener to be added */ public void addPropertyChangeListener(PropertyChangeListener l) { propertyChangeSupport.addPropertyChangeListener(l); } /** * Remove a {@code PropertyChangeListener} from the listener list. * This removes a listener that was registered for all properties. * If the listener was added more than once, it will be notified * one less time after being removed. If the listener is {@code null}, * or was never added, no exception is thrown and no action is taken. * @param l the listener to remove */ public void removePropertyChangeListener(PropertyChangeListener l) { propertyChangeSupport.removePropertyChangeListener(l); } /** * Adds a {@code PropertyChangeListener} for a specific property. The listener * will be invoked only when a call on firePropertyChange names that specific property. * The same listener object may be added more than once. For each property, * the listener will be invoked the number of times it was added for that property. * If the property name or the listener is null, no exception is thrown and no action is taken. * @param name the name of the property to listen on * @param l the listener to add */ public void addPropertyChangeListener(String name, PropertyChangeListener l) { propertyChangeSupport.addPropertyChangeListener(name, l); } /** * Remove a {@code PropertyChangeListener} from the listener list. * This removes a PropertyChangeListener that was registered for all properties. * If the listener was added more than once, * it will be notified one less time after being removed. If the listener is {@code null}, * or was never added, no exception is thrown and no action is taken. * @param name the name of the property that was listened on * @param l the listener to remove */ public void removePropertyChangeListener(String name, PropertyChangeListener l) { propertyChangeSupport.removePropertyChangeListener(name, l); } /** * Returns the scroll pane of the image viewer. * @return the scroll pane */ JScrollPane getScrollPane() { return scroller; } Synchronizer getSynchronizer() { return synchronizer; } void setSynchronizer(Synchronizer newSync) { synchronizer=newSync; } /** * Returns the image pixel corresponding to the given point. If the clipToImage * parameter is false, then the function will return an appropriately positioned * pixel on an infinite plane, even if the point is outside the image bounds. If * clipToImage is true then the function will return null * for such positions, and any non-null return value will be a valid image pixel. * @param p a point in component coordinate system * @param clipToImage whether the function should return null for positions outside * the image bounds * @return the corresponding image pixel * @throws IllegalStateException if there is no image set or if the size of the viewer is 0 (for example because * it is not in a visible component) */ public Point pointToPixel(Point p, boolean clipToImage) { return theImage.pointToPixel(p, clipToImage); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy