/** * Copyright (C) 2008-2011 Daniel Senff * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package de.danielsenff.radds.view.canvas; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import java.awt.image.Raster; import java.util.HashSet; import java.util.Set; import javax.swing.BorderFactory; import javax.swing.JPanel; import javax.swing.Scrollable; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import ddsutil.BIUtil; import ddsutil.ImageOperations; /** * JComponent for drawing {@link BufferedImage}s. * @author danielsenff * */ public class BICanvas extends JPanel implements Scrollable, MouseMotionListener { /** * */ private static final long serialVersionUID = 1L; /** * BufferedImage that is actually rendered. It is derived from the biSource. */ private BufferedImage biRendered; /** * BufferedImage source from which displayed BufferedImage are derived. */ private BufferedImage biSource; /** * Mode to display the image by channel. */ private ImageOperations.ChannelMode channelMode; /** * Zoom factor for display. */ private float zoomFactor = 1.0f; /** * Color displayed when showing transparency. */ private Color transparencyColor; /** * Displays a {@link BufferedImage} in RGB-Mode * without multiplied Alpha-channel. * @param controller * @param image * @param biRendered BufferedImage to display */ public BICanvas(BufferedImage image) { this(image, ImageOperations.ChannelMode.RGB); } /** * Displays a {@link BufferedImage} in the channel-mode specified. * @param controller * @param image * @param biRendered BufferedImage to display * @param channel Channel of the BufferedImage to display */ public BICanvas(final BufferedImage image, final ImageOperations.ChannelMode channel) { this.channelMode = channel; this.biRendered = image; this.biSource = image; changeChannelBi(channel, biSource); this.addMouseMotionListener(this); this.transparencyColor = Color.CYAN; this.setBorder(BorderFactory.createLineBorder(Color.BLACK)); this.setPreferredSize(new Dimension(biRendered.getWidth(), biRendered.getHeight())); } private void changeChannelBi(ImageOperations.ChannelMode channel, BufferedImage currentImage) { switch(channel){ default: case RGBA: this.biRendered = currentImage; break; case RGB: this.biRendered = BIUtil.getChannel(currentImage, ImageOperations.ChannelMode.RGB); break; case ALPHA: this.biRendered = BIUtil.getChannel(currentImage, ImageOperations.ChannelMode.ALPHA); break; case RED: this.biRendered = BIUtil.getChannel(currentImage, ImageOperations.ChannelMode.RED); break; case GREEN: this.biRendered = BIUtil.getChannel(currentImage, ImageOperations.ChannelMode.GREEN); break; case BLUE: this.biRendered = BIUtil.getChannel(currentImage, ImageOperations.ChannelMode.BLUE); break; } super.repaint(); revalidate(); } /** * Changes the color-channel of the displayed BufferedImage * @param channel new channel to display */ public void setChannelMode(final ImageOperations.ChannelMode channel) { changeChannelBi(channel, biSource); this.channelMode = channel; } /** * Current channel displayed on the canvas * @return */ public ImageOperations.ChannelMode getChannelMode() { return this.channelMode; } /** * Returns the {@link BufferedImage} currently displayed on the canvas. * @return */ public BufferedImage getCanvas() { return this.biRendered; } /** * @return */ public BufferedImage getSource() { return this.biSource; } /** * Overwrite the current source-BufferedImage. * This will also update the displayed canvas and the window-size. * @param bi */ public void setSourceBI(final BufferedImage bi) { this.biRendered = bi; this.biSource = bi; int width = (int) (zoomFactor*bi.getWidth()); int height = (int) (zoomFactor*bi.getHeight()); this.setPreferredSize(new Dimension(width, height)); this.getParent().setPreferredSize(new Dimension(bi.getWidth(), bi.getHeight())); changeChannelBi(channelMode, biSource); invalidate(); notifyAllRenderedChangeListeners(); } /** * Sets the factor the original-image dimensions are multiplied with * @param zoom */ public void setZoomFactor(final float zoom) { float oldValue = this.zoomFactor; this.zoomFactor = zoom; int newW = (int) (biRendered.getWidth() * zoom); int newH = (int) (biRendered.getHeight() * zoom); this.setPreferredSize(new Dimension(newW, newH)); this.revalidate(); firePropertyChange("zoomFactor", oldValue, zoomFactor); } /** * Returns the factor the original-image dimensions are multiplied with * @return */ public float getZoomFactor() { return this.zoomFactor; } /** * @return the transparencyColor */ public final Color getTransparencyColor() { return this.transparencyColor; } /** * @param transparencyColor the transparencyColor to set */ public final void setTransparencyColor(Color transparencyColor) { this.transparencyColor = transparencyColor; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); drawBackground(g); int width = biRendered.getWidth(), height = biRendered.getHeight(); int newW = (int) (zoomFactor * width); int newH = (int) (zoomFactor * height); int offsetX = (int) ((0.5*g.getClipBounds().getWidth()) - (0.5*newW)); // offset im viewport int offsetY = (int) ((0.5*g.getClipBounds().getHeight())- (0.5*newH)); // offset im viewport int moveX = 0; //offset on bi int moveY = 0; //offset on bi offsetX=0; offsetY=0; g.setColor(Color.WHITE); // g.fillRect(0, 0, width, height); //g.drawImage(this.displayBi, 0, 0, this); ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); g.drawImage(biRendered, offsetX, offsetY, newW+offsetX, newH+offsetY, moveX, moveY, biRendered.getWidth(), biRendered.getHeight(), null); } private void drawBackground(Graphics g) { g.setColor(this.transparencyColor); g.fillRect(0, 0, (int) (zoomFactor * biRendered.getWidth()), (int) (zoomFactor * biRendered.getHeight())); } /* (non-Javadoc) * @see javax.swing.Scrollable#getPreferredScrollableViewportSize() */ @Override public Dimension getPreferredScrollableViewportSize() { return null; } /* (non-Javadoc) * @see javax.swing.Scrollable#getScrollableBlockIncrement(java.awt.Rectangle, int, int) */ @Override public int getScrollableBlockIncrement(Rectangle arg0, int arg1, int arg2) { return 50; } /* (non-Javadoc) * @see javax.swing.Scrollable#getScrollableTracksViewportHeight() */ @Override public boolean getScrollableTracksViewportHeight() { return false; } /* (non-Javadoc) * @see javax.swing.Scrollable#getScrollableTracksViewportWidth() */ @Override public boolean getScrollableTracksViewportWidth() { return false; } /** * The dimensions of the stored {@link BufferedImage} multiplied by the zoom-factor. * @return dimension Dimension of the stored {@link BufferedImage} */ public Dimension getViewDimension() { return new Dimension((int) (zoomFactor * biRendered.getWidth()), (int) (zoomFactor * biRendered.getWidth())); } /* (non-Javadoc) * @see javax.swing.Scrollable#getScrollableUnitIncrement(java.awt.Rectangle, int, int) */ @Override public int getScrollableUnitIncrement(Rectangle arg0, int arg1, int arg2) { return 15; // pixel } @Override public void mouseDragged(MouseEvent e) {} @Override public void mouseMoved(MouseEvent e) { int x = (int) (e.getPoint().x/zoomFactor); int y = (int) (e.getPoint().y/zoomFactor); Raster data = biSource.getData(); String tooltip = "Coordinate (" + x + ", "+ y + "), "; int index = y*biSource.getWidth() + x; if(biSource.getColorModel() instanceof IndexColorModel) { tooltip += "Index Colors (" + biSource.getColorModel().getAlpha(index) + ", " + biSource.getColorModel().getRed(index) + ", " + biSource.getColorModel().getGreen(index) + ", " + biSource.getColorModel().getBlue(index) + ")"; } else if(biSource.getColorModel().getNumComponents() > 3) { tooltip += "ARGB (" + data.getSample((int)x, (int)y, 3) + ", " + data.getSample((int)x, (int)y, 0) + ", " + data.getSample((int)x, (int)y, 1) + ", " + data.getSample((int)x, (int)y, 2) + ")"; } else { tooltip += "RGB (" + data.getSample((int)x, (int)y, 0) + ", " + data.getSample((int)x, (int)y, 1) + ", " + data.getSample((int)x, (int)y, 2) + ")"; } this.setToolTipText(tooltip); } /** * Listeners that get notified if the displayed image changes. */ Set<ChangeListener> renderChangeListeners = new HashSet<ChangeListener>(); public void addRenderedChangeListener(ChangeListener changeListener) { renderChangeListeners.add(changeListener); } public void notifyAllRenderedChangeListeners() { for(ChangeListener listener : renderChangeListeners) { listener.stateChanged(new ChangeEvent(this)); } } }