Want more features on Pastebin? Sign Up, it's FREE!
Guest

NavigableImpagePanel

By: a guest on Sep 22nd, 2011  |  syntax: Java  |  size: 26.83 KB  |  views: 55  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. import java.awt.AWTEvent;
  2. import java.awt.BorderLayout;
  3. import java.awt.Color;
  4. import java.awt.Dimension;
  5. import java.awt.Graphics;
  6. import java.awt.Graphics2D;
  7. import java.awt.GraphicsEnvironment;
  8. import java.awt.Image;
  9. import java.awt.Point;
  10. import java.awt.Rectangle;
  11. import java.awt.RenderingHints;
  12. import java.awt.Toolkit;
  13. import java.awt.event.ComponentAdapter;
  14. import java.awt.event.ComponentEvent;
  15. import java.awt.event.MouseAdapter;
  16. import java.awt.event.MouseEvent;
  17. import java.awt.event.MouseMotionListener;
  18. import java.awt.event.MouseWheelEvent;
  19. import java.awt.event.MouseWheelListener;
  20. import java.awt.image.BufferedImage;
  21. import java.io.File;
  22. import java.io.IOException;
  23. import java.util.Arrays;
  24. import javax.imageio.ImageIO;
  25. import javax.swing.JFrame;
  26. import javax.swing.JOptionPane;
  27. import javax.swing.JPanel;
  28. import javax.swing.SwingUtilities;
  29.  
  30. /**
  31.  * <p>
  32.  * <code>NavigableImagePanel</code> is a lightweight container displaying
  33.  * an image that can be zoomed in and out and panned with ease and simplicity,
  34.  * using an adaptive rendering for high quality display and satisfactory performance.
  35.  * </p>
  36.  * <p>
  37.  * <h3>Image</h3>
  38.  * <p>An image is loaded either via a constructor:</p>
  39.  * <pre>
  40.  * NavigableImagePanel panel = new NavigableImagePanel(image);
  41.  * </pre>
  42.  * or using a setter:
  43.  * <pre>
  44.  * NavigableImagePanel panel = new NavigableImagePanel();
  45.  * panel.setImage(image);
  46.  * </pre>
  47.  * When an image is set, it is initially painted centered in the component,
  48.  * at the largest possible size, fully visible, with its aspect ratio is preserved.
  49.  * This is defined as 100% of the image size and its corresponding zoom level is 1.0.
  50.  * </p>
  51.  * <h3>Zooming</h3>
  52.  * <p>
  53.  * Zooming can be controlled interactively, using either the mouse scroll wheel (default)
  54.  * or the mouse two buttons, or programmatically, allowing the programmer to
  55.  * implement other custom zooming methods. If the mouse does not have a scroll wheel,
  56.  * set the zooming device to mouse buttons:
  57.  * <pre>
  58.  * panel.setZoomDevice(ZoomDevice.MOUSE_BUTTON);
  59.  * </pre>
  60.  * The left mouse button works as a toggle switch between zooming in and zooming out
  61.  * modes, and the right button zooms an image by one increment (default is 20%).
  62.  * You can change the zoom increment value by:
  63.  * <pre>
  64.  * panel.setZoomIncrement(newZoomIncrement);
  65.  * </pre>
  66.  * If you intend to provide programmatic zoom control, set the zoom device to none
  67.  * to disable both the mouse wheel and buttons for zooming purposes:
  68.  * <pre>
  69.  * panel.setZoomDevice(ZoomDevice.NONE);
  70.  * </pre>
  71.  * and use <code>setZoom()</code> to change the zoom level.
  72.  * </p>
  73.  * <p>
  74.  * Zooming is always around the point the mouse pointer is currently at, so that
  75.  * this point (called a zooming center) remains stationary ensuring that the area
  76.  * of an image we are zooming into does not disappear off the screen. The zooming center
  77.  * stays at the same location on the screen and all other points move radially away from
  78.  * it (when zooming in), or towards it (when zooming out). For programmatically
  79.  * controlled zooming the zooming center is either specified when <code>setZoom()</code>
  80.  * is called:
  81.  * <pre>
  82.  * panel.setZoom(newZoomLevel, newZoomingCenter);
  83.  * </pre>
  84.  * or assumed to be the point of an image which is
  85.  * the closest to the center of the panel, if no zooming center is specified:
  86.  * <pre>
  87.  * panel.setZoom(newZoomLevel);
  88.  * </pre>
  89.  * </p>
  90.  * <p>
  91.  * There are no lower or upper zoom level limits.
  92.  * </p>
  93.  * <h3>Navigation</h3>
  94.  * <p><code>NavigableImagePanel</code> does not use scroll bars for navigation,
  95.  * but relies on a navigation image located in the upper left corner of the panel.
  96.  * The navigation image is a small replica of the image displayed in the panel.
  97.  * When you click on any point of the navigation image that part of the image
  98.  * is displayed in the panel, centered. The navigation image can also be
  99.  * zoomed in the same way as the main image.</p>
  100.  * <p>In order to adjust the position of an image in the panel, it can be dragged
  101.  * with the mouse, using the left button.
  102.  * </p>
  103.  * <p>For programmatic image navigation, disable the navigation image:
  104.  * <pre>
  105.  * panel.setNavigationImageEnabled(false)
  106.  * </pre>
  107.  * and use <code>getImageOrigin()</code> and
  108.  * <code>setImageOrigin()</code> to move the image around the panel.
  109.  * </p>
  110.  * <h3>Rendering</h3>
  111.  * <p><code>NavigableImagePanel</code> uses the Nearest Neighbor interpolation
  112.  * for image rendering (default in Java).
  113.  * When the scaled image becomes larger than the original image,
  114.  * the Bilinear interpolation is applied, but only to the part
  115.  * of the image which is displayed in the panel. This interpolation change threshold
  116.  * can be controlled by adjusting the value of
  117.  * <code>HIGH_QUALITY_RENDERING_SCALE_THRESHOLD</code>.</p>
  118.  */
  119. public class NavigableImagePanel extends JPanel {
  120.  
  121.         /**
  122.          * <p>Identifies a change to the zoom level.</p>
  123.          */
  124.         public static final String ZOOM_LEVEL_CHANGED_PROPERTY = "zoomLevel";
  125.        
  126.         /**
  127.          * <p>Identifies a change to the zoom increment.</p>
  128.          */
  129.         public static final String ZOOM_INCREMENT_CHANGED_PROPERTY = "zoomIncrement";
  130.        
  131.         /**
  132.          * <p>Identifies that the image in the panel has changed.</p>
  133.          */
  134.         public static final String IMAGE_CHANGED_PROPERTY = "image";
  135.  
  136.         private static final double SCREEN_NAV_IMAGE_FACTOR = 0.15; // 15% of panel's width
  137.         private static final double NAV_IMAGE_FACTOR = 0.3; // 30% of panel's width
  138.         private static final double HIGH_QUALITY_RENDERING_SCALE_THRESHOLD = 1.0;
  139.         private static final Object INTERPOLATION_TYPE =
  140.                 RenderingHints.VALUE_INTERPOLATION_BILINEAR;
  141.        
  142.         private double zoomIncrement = 0.2;
  143.         private double zoomFactor = 1.0 + zoomIncrement;
  144.         private double navZoomFactor = 1.0 + zoomIncrement;
  145.         private BufferedImage image;
  146.         private BufferedImage navigationImage;
  147.         private int navImageWidth;
  148.         private int navImageHeight;
  149.         private double initialScale = 0.0;
  150.         private double scale = 0.0;
  151.         private double navScale = 0.0;
  152.         private int originX = 0;
  153.         private int originY = 0;
  154.         private Point mousePosition;
  155.         private Dimension previousPanelSize;
  156.         private boolean navigationImageEnabled = true;
  157.         private boolean highQualityRenderingEnabled = true;
  158.  
  159.         private WheelZoomDevice wheelZoomDevice = null;
  160.         private ButtonZoomDevice buttonZoomDevice = null;
  161.        
  162.         /**
  163.          * <p>Defines zoom devices.</p>
  164.          */
  165.         public static class ZoomDevice {
  166.                 /**
  167.                  * <p>Identifies that the panel does not implement zooming,
  168.                  * but the component using the panel does (programmatic zooming method).</p>
  169.                  */
  170.                 public static final ZoomDevice NONE = new ZoomDevice("none");
  171.                
  172.                 /**
  173.                  * <p>Identifies the left and right mouse buttons as the zooming device.</p>
  174.                  */
  175.                 public static final ZoomDevice MOUSE_BUTTON = new ZoomDevice("mouseButton");
  176.                
  177.                 /**
  178.                  * <p>Identifies the mouse scroll wheel as the zooming device.</p>
  179.                  */
  180.                 public static final ZoomDevice MOUSE_WHEEL = new ZoomDevice("mouseWheel");
  181.                
  182.                 private String zoomDevice;
  183.                 private ZoomDevice(String zoomDevice) {
  184.                         this.zoomDevice = zoomDevice;
  185.                 }
  186.                 public String toString() {
  187.                         return zoomDevice;
  188.                 }
  189.         }
  190.        
  191.         //This class is required for high precision image coordinates translation.
  192.         private class Coords {
  193.                 public double x;
  194.                 public double y;
  195.                 public Coords(double x, double y) {
  196.                         this.x = x;
  197.                         this.y = y;
  198.                         }
  199.                 public int getIntX() {
  200.                         return (int)Math.round(x);
  201.                 }
  202.                 public int getIntY() {
  203.                         return (int)Math.round(y);
  204.                 }
  205.                 public String toString() {
  206.                         return "[Coords: x=" + x + ",y=" + y + "]";
  207.                 }
  208.         }
  209.        
  210.         private class WheelZoomDevice implements MouseWheelListener {
  211.                 public void mouseWheelMoved(MouseWheelEvent e) {
  212.                         Point p = e.getPoint();
  213.                         boolean zoomIn = (e.getWheelRotation() < 0);
  214.                         if (isInNavigationImage(p)) {
  215.                                 if (zoomIn) {
  216.                                         navZoomFactor = 1.0 + zoomIncrement;
  217.                                 } else {
  218.                                         navZoomFactor = 1.0 - zoomIncrement;
  219.                                 }
  220.                                 zoomNavigationImage();
  221.                         } else if (isInImage(p)) {
  222.                                 if (zoomIn) {
  223.                                         zoomFactor = 1.0 + zoomIncrement;
  224.                                 } else {
  225.                                         zoomFactor = 1.0 - zoomIncrement;
  226.                                 }
  227.                                 zoomImage();
  228.                         }
  229.                 }
  230.         }
  231.        
  232.         private class ButtonZoomDevice extends MouseAdapter {
  233.                 public void mouseClicked(MouseEvent e) {
  234.                         Point p = e.getPoint();
  235.                         if (SwingUtilities.isRightMouseButton(e)) {
  236.                                 if (isInNavigationImage(p)) {
  237.                                         navZoomFactor = 1.0 - zoomIncrement;
  238.                                         zoomNavigationImage();
  239.                                 } else if (isInImage(p)) {
  240.                                         zoomFactor = 1.0 - zoomIncrement;
  241.                                         zoomImage();
  242.                                 }
  243.                         } else {
  244.                                 if (isInNavigationImage(p)) {
  245.                                         navZoomFactor = 1.0 + zoomIncrement;
  246.                                         zoomNavigationImage();
  247.                                 } else if (isInImage(p)) {
  248.                                         zoomFactor = 1.0 + zoomIncrement;
  249.                                         zoomImage();
  250.                                 }
  251.                         }
  252.                 }              
  253.         }
  254.        
  255.         /**
  256.          * <p>Creates a new navigable image panel with no default image and
  257.          * the mouse scroll wheel as the zooming device.</p>
  258.          */
  259.         public NavigableImagePanel() {
  260.                 setOpaque(false);
  261.                 addComponentListener(new ComponentAdapter() {
  262.                         public void componentResized(ComponentEvent e) {
  263.                                 if (scale > 0.0) {
  264.                                         if (isFullImageInPanel()) {
  265.                                                 centerImage();
  266.                                         } else if (isImageEdgeInPanel()) {
  267.                                                 scaleOrigin();
  268.                                         }
  269.                                         if (isNavigationImageEnabled()) {
  270.                                                 createNavigationImage();
  271.                                         }
  272.                                         repaint();
  273.                                 }
  274.                                 previousPanelSize = getSize();
  275.                         }
  276.                 });
  277.                
  278.                 addMouseListener(new MouseAdapter() {
  279.                         public void mousePressed(MouseEvent e) {
  280.                                 if (SwingUtilities.isLeftMouseButton(e)) {
  281.                                         if (isInNavigationImage(e.getPoint())) {
  282.                                                 Point p = e.getPoint();
  283.                                                 displayImageAt(p);
  284.                                         }
  285.                                 }
  286.                         }
  287.                 });
  288.                
  289.                 addMouseMotionListener(new MouseMotionListener() {
  290.                         public void mouseDragged(MouseEvent e) {
  291.                                 if (SwingUtilities.isLeftMouseButton(e)
  292.                                         && !isInNavigationImage(e.getPoint())) {
  293.                                         Point p = e.getPoint();
  294.                                         moveImage(p);
  295.                                 }
  296.                         }
  297.                         public void mouseMoved(MouseEvent e) {
  298.                                 //we need the mouse position so that after zooming
  299.                                 //that position of the image is maintained
  300.                                 mousePosition = e.getPoint();
  301.                         }
  302.                 });
  303.  
  304.                 setZoomDevice(ZoomDevice.MOUSE_WHEEL);
  305.         }
  306.        
  307.         /**
  308.          * <p>Creates a new navigable image panel with the specified image
  309.          * and the mouse scroll wheel as the zooming device.</p>
  310.          */
  311.         public NavigableImagePanel(BufferedImage image) throws IOException {
  312.                 this();
  313.                 setImage(image);
  314.         }
  315.        
  316.         private void addWheelZoomDevice() {
  317.                 if (wheelZoomDevice == null) {
  318.                         wheelZoomDevice = new WheelZoomDevice();
  319.                         addMouseWheelListener(wheelZoomDevice);
  320.                 }
  321.         }
  322.  
  323.         private void addButtonZoomDevice() {
  324.                 if (buttonZoomDevice == null) {
  325.                         buttonZoomDevice = new ButtonZoomDevice();
  326.                         addMouseListener(buttonZoomDevice);
  327.                 }
  328.         }
  329.        
  330.         private void removeWheelZoomDevice() {
  331.                 if (wheelZoomDevice != null) {
  332.                         removeMouseWheelListener(wheelZoomDevice);
  333.                         wheelZoomDevice = null;
  334.                 }      
  335.         }
  336.        
  337.         private void removeButtonZoomDevice() {
  338.                 if (buttonZoomDevice != null) {
  339.                         removeMouseListener(buttonZoomDevice);
  340.                         buttonZoomDevice = null;
  341.                 }      
  342.         }
  343.        
  344.         /**
  345.          * <p>Sets a new zoom device.</p>
  346.          *
  347.          * @param newZoomDevice specifies the type of a new zoom device.
  348.          */
  349.         public void setZoomDevice(ZoomDevice newZoomDevice) {
  350.                 if (newZoomDevice == ZoomDevice.NONE) {
  351.                         removeWheelZoomDevice();
  352.                         removeButtonZoomDevice();
  353.                 } else if (newZoomDevice == ZoomDevice.MOUSE_BUTTON) {
  354.                         removeWheelZoomDevice();
  355.                         addButtonZoomDevice();
  356.                 } else if (newZoomDevice == ZoomDevice.MOUSE_WHEEL) {
  357.                         removeButtonZoomDevice();
  358.                         addWheelZoomDevice();
  359.                 }
  360.         }
  361.        
  362.         /**
  363.          * <p>Gets the current zoom device.</p>
  364.          */
  365.         public ZoomDevice getZoomDevice() {
  366.                 if (buttonZoomDevice != null) {
  367.                         return ZoomDevice.MOUSE_BUTTON;
  368.                 } else if (wheelZoomDevice != null) {
  369.                         return ZoomDevice.MOUSE_WHEEL;
  370.                 } else {
  371.                         return ZoomDevice.NONE;
  372.                 }
  373.         }      
  374.        
  375.         //Called from paintComponent() when a new image is set.
  376.         private void initializeParams() {
  377.                 double xScale = (double)getWidth() / image.getWidth();
  378.                 double yScale = (double)getHeight() / image.getHeight();
  379.                 initialScale = Math.min(xScale, yScale);
  380.                 scale = initialScale;
  381.                
  382.                 //An image is initially centered
  383.                 centerImage();
  384.                 if (isNavigationImageEnabled()) {
  385.                         createNavigationImage();
  386.                 }
  387.         }
  388.        
  389.         //Centers the current image in the panel.
  390.         private void centerImage() {
  391.                 originX = (int)(getWidth() - getScreenImageWidth()) / 2;
  392.                 originY = (int)(getHeight() - getScreenImageHeight()) / 2;
  393.         }
  394.        
  395.         //Creates and renders the navigation image in the upper let corner of the panel.
  396.         private void createNavigationImage() {
  397.                 //We keep the original navigation image larger than initially
  398.                 //displayed to allow for zooming into it without pixellation effect.
  399.                 navImageWidth = (int)(getWidth() * NAV_IMAGE_FACTOR);
  400.                 navImageHeight = navImageWidth * image.getHeight() / image.getWidth();                                 
  401.                 int scrNavImageWidth = (int)(getWidth() * SCREEN_NAV_IMAGE_FACTOR);
  402.                 int scrNavImageHeight = scrNavImageWidth * image.getHeight() / image.getWidth();
  403.                 navScale = (double)scrNavImageWidth / navImageWidth;
  404.                 navigationImage = new BufferedImage(navImageWidth, navImageHeight,
  405.                         image.getType());
  406.                 Graphics g = navigationImage.getGraphics();
  407.                 g.drawImage(image, 0, 0, navImageWidth, navImageHeight, null);
  408.         }
  409.        
  410.         /**
  411.          * <p>Sets an image for display in the panel.</p>
  412.          *
  413.          * @param image an image to be set in the panel
  414.          */
  415.         public void setImage(BufferedImage image) {
  416.                 BufferedImage oldImage = this.image;
  417.                 this.image = image;
  418.                 //Reset scale so that initializeParameters() is called in paintComponent()
  419.                 //for the new image.
  420.                 scale = 0.0;
  421.                 firePropertyChange(IMAGE_CHANGED_PROPERTY, (Image)oldImage, (Image)image);
  422.                 repaint();
  423.         }
  424.        
  425.         /**
  426.          * <p>Tests whether an image uses the standard RGB color space.</p>
  427.          */
  428.         public static boolean isStandardRGBImage(BufferedImage bImage) {
  429.                 return bImage.getColorModel().getColorSpace().isCS_sRGB();
  430.         }
  431.        
  432.         //Converts this panel's coordinates into the original image coordinates
  433.         private Coords panelToImageCoords(Point p) {
  434.                 return new Coords((p.x - originX) / scale, (p.y - originY) / scale);
  435.         }
  436.  
  437.         //Converts the original image coordinates into this panel's coordinates
  438.         private Coords imageToPanelCoords(Coords p) {
  439.                 return new Coords((p.x * scale) + originX, (p.y * scale) + originY);
  440.         }
  441.  
  442.         //Converts the navigation image coordinates into the zoomed image coordinates  
  443.         private Point navToZoomedImageCoords(Point p) {
  444.                 int x = p.x * getScreenImageWidth() / getScreenNavImageWidth();
  445.                 int y = p.y * getScreenImageHeight() / getScreenNavImageHeight();
  446.                 return new Point(x, y);
  447.         }
  448.        
  449.         //The user clicked within the navigation image and this part of the image
  450.         //is displayed in the panel.
  451.         //The clicked point of the image is centered in the panel.
  452.         private void displayImageAt(Point p) {         
  453.                 Point scrImagePoint = navToZoomedImageCoords(p);
  454.                 originX = -(scrImagePoint.x - getWidth() / 2);
  455.                 originY = -(scrImagePoint.y - getHeight() / 2);
  456.                 repaint();
  457.         }
  458.  
  459.         //Tests whether a given point in the panel falls within the image boundaries.  
  460.         private boolean isInImage(Point p) {
  461.                 Coords coords = panelToImageCoords(p);
  462.                 int x = coords.getIntX();
  463.                 int y = coords.getIntY();
  464.                 return (x >= 0 && x < image.getWidth() && y >= 0 && y < image.getHeight());
  465.         }
  466.        
  467.         //Tests whether a given point in the panel falls within the navigation image
  468.         //boundaries.  
  469.         private boolean isInNavigationImage(Point p) {
  470.                 return (isNavigationImageEnabled() && p.x < getScreenNavImageWidth()
  471.                         && p.y < getScreenNavImageHeight());
  472.         }
  473.        
  474.         //Used when the image is resized.
  475.         private boolean isImageEdgeInPanel() {
  476.                 if (previousPanelSize == null) {
  477.                         return false;
  478.                 }
  479.                
  480.                 return (originX > 0 && originX < previousPanelSize.width
  481.                         || originY > 0 && originY < previousPanelSize.height);
  482.         }
  483.        
  484.         //Tests whether the image is displayed in its entirety in the panel.
  485.         private boolean isFullImageInPanel() {
  486.                 return (originX >= 0 && (originX + getScreenImageWidth()) < getWidth()
  487.                                 && originY >= 0 && (originY + getScreenImageHeight()) < getHeight());
  488.         }
  489.        
  490.         /**
  491.          * <p>Indicates whether the high quality rendering feature is enabled.</p>
  492.          *
  493.          * @return true if high quality rendering is enabled, false otherwise.
  494.          */
  495.         public boolean isHighQualityRenderingEnabled() {
  496.                 return highQualityRenderingEnabled;
  497.         }
  498.        
  499.         /**
  500.          * <p>Enables/disables high quality rendering.</p>
  501.          *
  502.          * @param enabled enables/disables high quality rendering
  503.          */
  504.         public void setHighQualityRenderingEnabled(boolean enabled) {
  505.                 highQualityRenderingEnabled = enabled; 
  506.         }
  507.  
  508.          //High quality rendering kicks in when when a scaled image is larger
  509.          //than the original image. In other words,
  510.          //when image decimation stops and interpolation starts.
  511.         private boolean isHighQualityRendering() {
  512.                 return (highQualityRenderingEnabled
  513.                         && scale > HIGH_QUALITY_RENDERING_SCALE_THRESHOLD);
  514.         }
  515.        
  516.         /**
  517.          * <p>Indicates whether navigation image is enabled.<p>
  518.          *
  519.          * @return true when navigation image is enabled, false otherwise.
  520.          */
  521.         public boolean isNavigationImageEnabled() {
  522.                 return navigationImageEnabled;
  523.         }
  524.        
  525.         /**
  526.          * <p>Enables/disables navigation with the navigation image.</p>
  527.          * <p>Navigation image should be disabled when custom, programmatic navigation
  528.          * is implemented.</p>
  529.          *
  530.          * @param enabled true when navigation image is enabled, false otherwise.
  531.          */
  532.         public void setNavigationImageEnabled(boolean enabled) {
  533.                 navigationImageEnabled = enabled;
  534.                 repaint();
  535.         }
  536.  
  537.         //Used when the panel is resized         
  538.         private void scaleOrigin() {
  539.                 originX = originX * getWidth() / previousPanelSize.width;
  540.                 originY = originY * getHeight() / previousPanelSize.height;
  541.                 repaint();
  542.         }
  543.  
  544.         //Converts the specified zoom level     to scale.
  545.         private double zoomToScale(double zoom) {
  546.                 return initialScale * zoom;
  547.         }
  548.        
  549.         /**
  550.          * <p>Gets the current zoom level.</p>
  551.          *
  552.          * @return the current zoom level
  553.          */
  554.         public double getZoom() {
  555.                 return scale / initialScale;
  556.         }
  557.        
  558.         /**
  559.          * <p>Sets the zoom level used to display the image.</p>
  560.          * <p>This method is used in programmatic zooming. The zooming center is
  561.          * the point of the image closest to the center of the panel.
  562.          * After a new zoom level is set the image is repainted.</p>
  563.          *
  564.          * @param newZoom the zoom level used to display this panel's image.
  565.          */
  566.         public void setZoom(double newZoom) {
  567.                 Point zoomingCenter = new Point(getWidth() / 2, getHeight() / 2);
  568.                 setZoom(newZoom, zoomingCenter);
  569.         }
  570.        
  571.         /**
  572.          * <p>Sets the zoom level used to display the image, and the zooming center,
  573.          * around which zooming is done.</p>
  574.          * <p>This method is used in programmatic zooming.
  575.          * After a new zoom level is set the image is repainted.</p>
  576.          *
  577.          * @param newZoom the zoom level used to display this panel's image.
  578.          */
  579.         public void setZoom(double newZoom, Point zoomingCenter) {
  580.                 Coords imageP = panelToImageCoords(zoomingCenter);
  581.                 if (imageP.x < 0.0) {
  582.                         imageP.x = 0.0;
  583.                 }
  584.                 if (imageP.y < 0.0) {
  585.                         imageP.y = 0.0;
  586.                 }
  587.                 if (imageP.x >= image.getWidth()) {
  588.                         imageP.x = image.getWidth() - 1.0;
  589.                 }
  590.                 if (imageP.y >= image.getHeight()) {
  591.                         imageP.y = image.getHeight() - 1.0;
  592.                 }
  593.                
  594.                 Coords correctedP = imageToPanelCoords(imageP);
  595.                 double oldZoom = getZoom();
  596.                 scale = zoomToScale(newZoom);
  597.                 Coords panelP = imageToPanelCoords(imageP);
  598.                
  599.                 originX += (correctedP.getIntX() - (int)panelP.x);
  600.                 originY += (correctedP.getIntY() - (int)panelP.y);
  601.                
  602.                 firePropertyChange(ZOOM_LEVEL_CHANGED_PROPERTY, new Double(oldZoom),
  603.                         new Double(getZoom()));
  604.                
  605.                 repaint();
  606.         }
  607.        
  608.         /**
  609.          * <p>Gets the current zoom increment.</p>
  610.          *
  611.          * @return the current zoom increment
  612.          */
  613.         public double getZoomIncrement() {
  614.                 return zoomIncrement;
  615.         }
  616.        
  617.         /**
  618.          * <p>Sets a new zoom increment value.</p>
  619.          *
  620.          * @param newZoomIncrement new zoom increment value
  621.          */
  622.         public void setZoomIncrement(double newZoomIncrement) {
  623.                 double oldZoomIncrement = zoomIncrement;
  624.                 zoomIncrement = newZoomIncrement;
  625.                 firePropertyChange(ZOOM_INCREMENT_CHANGED_PROPERTY,
  626.                         new Double(oldZoomIncrement), new Double(zoomIncrement));
  627.         }
  628.        
  629.         //Zooms an image in the panel by repainting it at the new zoom level.
  630.         //The current mouse position is the zooming center.
  631.         private void zoomImage() {
  632.                 Coords imageP = panelToImageCoords(mousePosition);
  633.                 double oldZoom = getZoom();
  634.                 scale *= zoomFactor;
  635.                 Coords panelP = imageToPanelCoords(imageP);
  636.                
  637.                 originX += (mousePosition.x - (int)panelP.x);
  638.                 originY += (mousePosition.y - (int)panelP.y);
  639.                
  640.                 firePropertyChange(ZOOM_LEVEL_CHANGED_PROPERTY, new Double(oldZoom),
  641.                         new Double(getZoom()));
  642.                
  643.                 repaint();
  644.         }
  645.        
  646.         //Zooms the navigation image
  647.         private void zoomNavigationImage() {
  648.                 navScale *= navZoomFactor;
  649.                 repaint();
  650.         }
  651.        
  652.         /**
  653.          * <p>Gets the image origin.</p>
  654.          * <p>Image origin is defined as the upper, left corner of the image in
  655.          * the panel's coordinate system.</p>
  656.          * @return the point of the upper, left corner of the image in the panel's coordinates
  657.          * system.
  658.          */
  659.         public Point getImageOrigin() {
  660.                 return new Point(originX, originY);
  661.         }
  662.        
  663.         /**
  664.          * <p>Sets the image origin.</p>
  665.          * <p>Image origin is defined as the upper, left corner of the image in
  666.          * the panel's coordinate system. After a new origin is set, the image is repainted.
  667.          * This method is used for programmatic image navigation.</p>
  668.          * @param x the x coordinate of the new image origin
  669.          * @param y the y coordinate of the new image origin
  670.          */
  671.         public void setImageOrigin(int x, int y) {
  672.                 setImageOrigin(new Point(x, y));
  673.         }
  674.        
  675.         /**
  676.          * <p>Sets the image origin.</p>
  677.          * <p>Image origin is defined as the upper, left corner of the image in
  678.          * the panel's coordinate system. After a new origin is set, the image is repainted.
  679.          * This method is used for programmatic image navigation.</p>
  680.          * @param newOrigin the value of a new image origin
  681.          */
  682.         public void setImageOrigin(Point newOrigin) {
  683.                 originX = newOrigin.x;
  684.                 originY = newOrigin.y;
  685.                 repaint();
  686.         }
  687.        
  688.         //Moves te image (by dragging with the mouse) to a new mouse position p.
  689.         private void moveImage(Point p) {
  690.                 int xDelta = p.x - mousePosition.x;
  691.                 int yDelta = p.y - mousePosition.y;
  692.                 originX += xDelta;
  693.                 originY += yDelta;
  694.                 mousePosition = p;
  695.                 repaint();
  696.         }
  697.        
  698.         //Gets the bounds of the image area currently displayed in the panel (in image
  699.         //coordinates).
  700.         private Rectangle getImageClipBounds() {
  701.                 Coords startCoords = panelToImageCoords(new Point(0, 0));
  702.                 Coords endCoords = panelToImageCoords(new Point(getWidth() - 1, getHeight() - 1));
  703.                 int panelX1 = startCoords.getIntX();
  704.                 int panelY1 = startCoords.getIntY();
  705.                 int panelX2 = endCoords.getIntX();
  706.                 int panelY2 = endCoords.getIntY();
  707.                 //No intersection?
  708.                 if (panelX1 >= image.getWidth() || panelX2 < 0 || panelY1 >= image.getHeight() || panelY2 < 0) {
  709.                         return null;
  710.                 }
  711.                
  712.                 int x1 = (panelX1 < 0) ? 0 : panelX1;
  713.                 int y1 = (panelY1 < 0) ? 0 : panelY1;
  714.                 int x2 = (panelX2 >= image.getWidth()) ? image.getWidth() - 1 : panelX2;
  715.                 int y2 = (panelY2 >= image.getHeight()) ? image.getHeight() - 1 : panelY2;
  716.                 return new Rectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
  717.         }
  718.  
  719.         /**
  720.          * Paints the panel and its image at the current zoom level, location, and
  721.          * interpolation method dependent on the image scale.</p>
  722.          *
  723.          * @param g the <code>Graphics</code> context for painting
  724.          */
  725.         protected void paintComponent(Graphics g) {
  726.                 super.paintComponent(g); // Paints the background
  727.                
  728.                 if (image == null) {
  729.                         return;
  730.                 }
  731.                
  732.                 if (scale == 0.0) {
  733.                         initializeParams();
  734.                 }
  735.                        
  736.                 if (isHighQualityRendering()) {                
  737.                         Rectangle rect = getImageClipBounds();
  738.                         if (rect == null || rect.width == 0 || rect.height == 0) { // no part of image is displayed in the panel
  739.                                 return;
  740.                         }
  741.  
  742.                         BufferedImage subimage = image.getSubimage(rect.x, rect.y, rect.width,
  743.                                 rect.height);
  744.                         Graphics2D g2 = (Graphics2D)g;
  745.                         g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, INTERPOLATION_TYPE);
  746.                         g2.drawImage(subimage, Math.max(0, originX), Math.max(0, originY),
  747.                                 Math.min((int)(subimage.getWidth() * scale), getWidth()),
  748.                                 Math.min((int)(subimage.getHeight() * scale), getHeight()), null);
  749.                 } else {
  750.                         g.drawImage(image, originX, originY, getScreenImageWidth(),
  751.                                 getScreenImageHeight(), null);
  752.                 }
  753.                
  754.                 //Draw navigation image
  755.                 if (isNavigationImageEnabled()) {
  756.                         g.drawImage(navigationImage, 0, 0, getScreenNavImageWidth(),
  757.                                 getScreenNavImageHeight(), null);
  758.                         drawZoomAreaOutline(g);
  759.                 }
  760.         }
  761.  
  762.         //Paints a white outline over the navigation image indicating
  763.         //the area of the image currently displayed in the panel.
  764.         private void drawZoomAreaOutline(Graphics g) {
  765.                 if (isFullImageInPanel()) {
  766.                         return;
  767.                 }
  768.                
  769.                 int x = -originX * getScreenNavImageWidth() / getScreenImageWidth();
  770.                 int y = -originY * getScreenNavImageHeight() / getScreenImageHeight();
  771.                 int width = getWidth() * getScreenNavImageWidth() / getScreenImageWidth();
  772.                 int height = getHeight() * getScreenNavImageHeight() / getScreenImageHeight();
  773.                 g.setColor(Color.white);
  774.                 g.drawRect(x, y, width, height);
  775.         }
  776.        
  777.         private int getScreenImageWidth() {
  778.                 return (int)(scale * image.getWidth());
  779.         }
  780.        
  781.         private int getScreenImageHeight() {
  782.                 return (int)(scale * image.getHeight());
  783.         }
  784.        
  785.         private int getScreenNavImageWidth() {
  786.                 return (int)(navScale * navImageWidth);
  787.         }
  788.        
  789.         private int getScreenNavImageHeight() {
  790.                 return (int)(navScale * navImageHeight);
  791.         }
  792.        
  793.         private static String[] getImageFormatExtensions() {
  794.                 String[] names = ImageIO.getReaderFormatNames();
  795.                 for(int i = 0; i < names.length; i++) {
  796.                         names[i] = names[i].toLowerCase();
  797.                 }
  798.                 Arrays.sort(names);
  799.                 return names;
  800.         }
  801.        
  802.         private static boolean endsWithImageFormatExtension(String name) {
  803.                 int dotIndex = name.lastIndexOf(".");
  804.                 if (dotIndex == -1) {
  805.                         return false;
  806.                 }
  807.                
  808.                 String extension = name.substring(dotIndex + 1).toLowerCase();
  809.                 return (Arrays.binarySearch(getImageFormatExtensions(), extension) >= 0);
  810.         }
  811.        
  812.         public static void main(String[] args) {       
  813.                 if (args.length == 0) {
  814.                         System.out.println("Usage: java NavigableImagePanel imageFilename");
  815.                         System.exit(1);
  816.                 }
  817.                
  818.                 final String filename = args[0];
  819.                
  820.                 SwingUtilities.invokeLater(new Runnable() {
  821.                         public void run() {            
  822.                                 final JFrame frame = new JFrame("Navigable Image Panel");
  823.                                 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  824.                                 NavigableImagePanel panel = new NavigableImagePanel();
  825.                                 try {
  826.                                         final BufferedImage image = ImageIO.read(new File(filename));
  827.                                         panel.setImage(image);                                                         
  828.                                 } catch (IOException e) {
  829.                                         JOptionPane.showMessageDialog(null, e.getMessage(), "",
  830.                                                 JOptionPane.ERROR_MESSAGE);
  831.                                         System.exit(1);
  832.                                 }
  833.                                
  834.                                 frame.getContentPane().add(panel, BorderLayout.CENTER);
  835.                                
  836.                                 GraphicsEnvironment ge =
  837.                                         GraphicsEnvironment.getLocalGraphicsEnvironment();
  838.                                 Rectangle bounds = ge.getMaximumWindowBounds();
  839.                                 frame.setSize(new Dimension(bounds.width, bounds.height));
  840.                                 frame.setVisible(true);                        
  841.                         }
  842.                 });
  843.         }
  844. }
clone this paste RAW Paste Data