package org.rr.commons.swing.image; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.util.logging.Level; import javax.swing.JComponent; import javax.swing.SwingWorker; import org.rr.commons.log.LoggerFactory; import org.rr.commons.mufs.IResourceHandler; import org.rr.pm.image.IImageProvider; import org.rr.pm.image.ImageProviderFactory; import org.rr.pm.image.ImageUtils; public class SimpleImageViewer extends JComponent { /** Creates new form BeanForm */ public SimpleImageViewer() { this.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { transformToView = null; repaint(); } }); } private static final long serialVersionUID = 8055865896136562197L; /** * The Resourcehandler points to the image resource to be displayed. */ private IResourceHandler currentDisplayedResource = null; /** * The image transform to be used for scaling and rotating the image */ private AffineTransform transformToView = null; /** * Stores the image to be displayed. */ private BufferedImage currentDisplayImage = null; /** * Runnable to be invoked after a repaint has been done. */ private Runnable repaintRunnable; /** * Thel SwingWorker which are currently in progress loading a picture. */ private SwingWorker<Void, Void> swingWorker = null; /** * Gets the {@link IResourceHandler} for the image currently displayed * with this {@link SimpleImageViewer} instance. * @return The image {@link IResourceHandler} or <code>null</code> if no one is currently set. */ public IResourceHandler getImageResource() { return this.currentDisplayedResource; } /** * Gets the image which is currently displayed. * @return The diesried image or <code>null</code> if no image is displayed. */ public BufferedImage getImage() { return this.currentDisplayImage; } /** * Needed if this component is only be used for painting but * is not added to a container. The given {@link Runnable} is * always invoked after an image has been loaded and a repaint is neeed. * * @param run The {@link Runnable} to be invoked. */ public void setRepaintInvokable(Runnable run) { this.repaintRunnable = run; } public void setImageViewerResource(IResourceHandler resourceHandler) { //clear the view if(resourceHandler==null) { this.currentDisplayedResource = null; this.currentDisplayImage = null; this.transformToView = null; } //only get a new Image if the file to be displayed has been changed. if(currentDisplayedResource==null || !resourceHandler.getResourceString().equals(currentDisplayedResource.getResourceString())) { try { this.currentDisplayedResource = resourceHandler; this.currentDisplayImage = null; //the old image is not longer used. this.transformToView = null; //the transform for the image is not longer used. } catch (Exception e) { this.currentDisplayImage = null; //the old image is not longer used. this.transformToView = null; //the transform for the image is not longer used. if(e.getClass().getSimpleName().equals("ImageFormatException")) { throw new RuntimeException(e); } LoggerFactory.log(Level.INFO, this, "Could not get an image provider for the file " + resourceHandler.getName(), e); } revalidate(); //need for resize repaint(); } } private IImageProvider getImageProvider() { if(this.currentDisplayedResource != null) { return ImageProviderFactory.getImageProvider(this.currentDisplayedResource); } return null; } /** * Starts a Thread which loads the image previously set with the * {@link #setImageResource(IResourceHandler, GalleryConfig)} method. * The Thread does a repaint after the image has been successfully loaded. */ private synchronized void loadImageThreaded() { //abort all working swing workers if(this.swingWorker!=null) { this.swingWorker.cancel(true); this.swingWorker = null; } this.swingWorker = new SwingWorker<Void, Void>() { @Override protected void done() { //it's not needed to do a repaint if the image could not be loaded. if(currentDisplayImage!=null) { if(repaintRunnable!=null) { repaintRunnable.run(); //a not threaded run } //it's not needed to do a repaint if the viewer is //not attached to a component. if(getParent()!=null) { revalidate(); repaint(); } } swingWorker = null; } @Override protected Void doInBackground() throws Exception { IImageProvider imageProvider = getImageProvider(); if(imageProvider!=null) { //do this only the first time this component is shown. if(currentDisplayImage==null) { currentDisplayImage = imageProvider.getImage(); } } return null; } }; this.swingWorker.execute(); } /** * Gets the {@link AffineTransform} for the image to be displayed in this {@link SimpleImageViewer} * instance. * @return The desired {@link AffineTransform}. */ private AffineTransform getAffineTransform() { if(this.transformToView != null) { return this.transformToView; } //get the image rotation value from the exif data. double imageRotationDegrees = 0d; this.transformToView = null; if(this.transformToView==null && currentDisplayImage!=null) { transformToView = ImageUtils.getTransformToMatchDimension(currentDisplayImage, getSize(), imageRotationDegrees); } return transformToView; } /** * Just paints the image. */ @Override public void paintComponent(Graphics g) { //create a black background g.setColor(Color.BLACK); ((Graphics2D)g).fillRect(0, 0, this.getSize().width, this.getSize().height); if(currentDisplayImage==null) { //load the image and return. The loadImage method will does a repaint if the //image is ready to paint. this.loadImageThreaded(); } else if(currentDisplayImage!=null) { //draw the image with the AffineTransform data. ((Graphics2D)g).drawImage(currentDisplayImage, getAffineTransform(), this); } } public void dispose() { this.currentDisplayImage = null; this.currentDisplayedResource = null; this.transformToView = null; this.repaintRunnable = null; } }