package fr.unistra.pelican.gui; import java.awt.BorderLayout; import java.awt.Cursor; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.io.File; import javax.media.jai.RasterFactory; import javax.swing.BoxLayout; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSlider; import javax.swing.JTextField; import javax.swing.JViewport; import javax.swing.JWindow; import javax.swing.ScrollPaneConstants; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.MouseInputAdapter; import com.sun.media.jai.widget.DisplayJAI; import fr.unistra.pelican.ByteImage; import fr.unistra.pelican.Image; import fr.unistra.pelican.algorithms.io.ImageSave; /** * This class creates a JFrame for the visualisation of 2dimensional byte valued * images. Multiple channels, frames and dimension axis are only showed one * image at a time by means of sliders. Only the case of images with exactly 3 * channels and the parameter "color" set, results in color. * * v1.2 added a better management of the memory, * the Pelican Image is not entirely transformed in displayJAI * * TODO : improve zoom management * * @author Aptoula, Lefevre, Jonathan Weber * @version 1.2 */ public class Frame2D extends JFrame { private static final long serialVersionUID = -1619980872944512678L; private Image img; private boolean color; private int width; private int height; private JTextField statusBar; private JSlider channelSld; private JSlider frameSld; private JSlider depthSld; private JLabel channelLbl; private JLabel frameLbl; private JLabel depthLbl; private MouseHandler mouseHandler; private JScrollPane scroll; public JScrollPane getScroll() { return this.scroll; } /** * * @param img * image to show * @param title * window title * @param color * whether it should be shown in color or not */ public Frame2D(fr.unistra.pelican.Image img, String title, boolean color) { this(img, color); // yes, color must be added to the constructor! if (title != null) this.setTitle(title); } /** * * @param img * image to show * @param color * whether it should be shown in color or not */ public Frame2D(fr.unistra.pelican.Image img, boolean color) { final int bdim = img.getBDim(); final int tdim = img.getTDim(); final int zdim = img.getZDim(); final Frame2D ben = this; // yes its ugly this.color = color; this.width = img.getXDim(); this.height = img.getYDim(); this.img = img; if (this.color == true && bdim != 3) { System.err.println("Only " + bdim + " channel" + ((bdim > 1) ? "s " : " ") + "found. Color visualisation cancelled"); this.color = false; } this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); JPanel root = new JPanel(); Toolkit k = Toolkit.getDefaultToolkit(); Dimension tailleEcran = k.getScreenSize(); root.setPreferredSize(new Dimension(Math.min(tailleEcran.width - 3, img .getXDim() + 3), Math.min(tailleEcran.height - 81, img.getYDim() + 81))); root.setLayout(new BorderLayout()); this.setContentPane(root); scroll = new JScrollPane(null, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); if (this.color == false) { makeDisplayedImage(0,0,0); } else { makeColorDisplayedImage(0,0); } mouseHandler = new MouseHandler(this); scroll.addMouseListener(mouseHandler); scroll.addMouseMotionListener(mouseHandler); root.add(scroll, BorderLayout.CENTER); // lower sub panel with labels and sliders...one horizontal box for each JPanel bottomPanel = new JPanel(); // sliders + statusbar bottomPanel.setLayout(new BorderLayout()); statusBar = new JTextField(); statusBar.setEnabled(false); bottomPanel.add(statusBar, BorderLayout.SOUTH); JPanel subPnl = new JPanel(); // only sliders subPnl.setLayout(new GridLayout(3, 1, 0, 5)); bottomPanel.add(subPnl, BorderLayout.CENTER); root.add(bottomPanel, BorderLayout.SOUTH); // channels JPanel channelBox = new JPanel(); channelBox.setLayout(new BoxLayout(channelBox, BoxLayout.X_AXIS)); channelLbl = new JLabel(" Channel : 1/" + bdim + " "); channelBox.add(channelLbl); channelSld = new JSlider(SwingConstants.HORIZONTAL, 1, bdim, 1); channelSld.setSnapToTicks(true); channelBox.add(channelSld); channelSld.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { channelLbl.setText(" Channel : " + Integer.toString(channelSld.getValue()) + "/" + bdim + " "); int b = channelSld.getValue() - 1; int t = frameSld.getValue() - 1; int z = depthSld.getValue() - 1; if (ben.color == false) { makeDisplayedImage(z,t,b); } else { makeColorDisplayedImage(z,t); } } }); subPnl.add(channelBox); if (bdim == 1 || this.color == true) { channelLbl.setEnabled(false); channelSld.setEnabled(false); } // frames JPanel frameBox = new JPanel(); frameBox.setLayout(new BoxLayout(frameBox, BoxLayout.X_AXIS)); frameLbl = new JLabel(" Frame : 1/" + tdim + " "); frameBox.add(frameLbl); frameSld = new JSlider(SwingConstants.HORIZONTAL, 1, tdim, 1); frameSld.setSnapToTicks(true); frameBox.add(frameSld); frameSld.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { frameLbl.setText(" Frame : " + Integer.toString(frameSld.getValue()) + "/" + tdim + " "); int b = channelSld.getValue() - 1; int t = frameSld.getValue() - 1; int z = depthSld.getValue() - 1; if (ben.color == false) { makeDisplayedImage(z,t,b); } else { makeColorDisplayedImage(z,t); } } }); subPnl.add(frameBox); if (tdim == 1) { frameLbl.setEnabled(false); frameSld.setEnabled(false); } // depth JPanel depthBox = new JPanel(); depthBox.setLayout(new BoxLayout(depthBox, BoxLayout.X_AXIS)); depthLbl = new JLabel(" Depth : 1/" + zdim + " "); depthBox.add(depthLbl); depthSld = new JSlider(SwingConstants.HORIZONTAL, 1, zdim, 1); depthSld.setSnapToTicks(true); depthBox.add(depthSld); depthSld.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { depthLbl.setText(" Depth : " + Integer.toString(depthSld.getValue()) + "/" + zdim + " "); int b = channelSld.getValue() - 1; int t = frameSld.getValue() - 1; int z = depthSld.getValue() - 1; if (ben.color == false) { makeDisplayedImage(z,t,b); } else { makeColorDisplayedImage(z,t); } } }); subPnl.add(depthBox); if (zdim == 1) { depthLbl.setEnabled(false); depthSld.setEnabled(false); } if (!channelLbl.isEnabled()) subPnl.remove(channelBox); if (!frameLbl.isEnabled()) subPnl.remove(frameBox); if (!depthLbl.isEnabled()) subPnl.remove(depthBox); pack(); setVisible(true); } private void makeDisplayedImage(int z, int t, int b) { DataBufferByte dbb; SampleModel s; Raster r; BufferedImage bimg = null; int xDim = img.getXDim(); int yDim = img.getYDim(); int zDim = img.getZDim(); int bDim = img.getBDim(); int imageDim = xDim*yDim; byte[] byteVal = new byte[imageDim]; int shift = ((t*zDim+z)*imageDim*bDim)+b; for(int i =0;i<imageDim;i++) { byteVal[i]=(byte)img.getPixelByte(shift); shift+=bDim; } dbb = new DataBufferByte(byteVal, byteVal.length); s = RasterFactory.createBandedSampleModel(DataBuffer.TYPE_BYTE, xDim, yDim, 1); r = RasterFactory.createWritableRaster(s, dbb, new Point(0, 0)); bimg = new BufferedImage(xDim, yDim, BufferedImage.TYPE_BYTE_GRAY); bimg.setData(r); int vValue = scroll.getVerticalScrollBar().getValue(); int hValue = scroll.getHorizontalScrollBar().getValue(); scroll.setViewportView(new DisplayJAI(bimg)); scroll.getVerticalScrollBar().setValue(vValue); scroll.getHorizontalScrollBar().setValue(hValue); } private void makeColorDisplayedImage(int z, int t) { DataBufferByte dbb; SampleModel s; Raster r; BufferedImage bimg = null; int[] bandOffsets = { 0, 1, 2 }; int xDim = img.getXDim(); int yDim = img.getYDim(); int zDim = img.getZDim(); int imageDim = xDim*yDim*3; byte[] byteVal = new byte[imageDim]; int shift = (t*zDim+z)*imageDim; for(int i =0;i<imageDim;i++) { byteVal[i]=(byte)img.getPixelByte(shift++); } dbb = new DataBufferByte(byteVal, byteVal.length); s = RasterFactory.createPixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, xDim, yDim, 3, 3 * xDim, bandOffsets); r = RasterFactory.createWritableRaster(s, dbb, new Point(0, 0)); bimg = new BufferedImage(xDim, yDim, BufferedImage.TYPE_3BYTE_BGR); bimg.setData(r); int vValue = scroll.getVerticalScrollBar().getValue(); int hValue = scroll.getHorizontalScrollBar().getValue(); scroll.setViewportView(new DisplayJAI(bimg)); scroll.getVerticalScrollBar().setValue(vValue); scroll.getHorizontalScrollBar().setValue(hValue); } void statusBarMsg(String s) { SwingUtilities.invokeLater(new MainFrameRunnable(this, s) { public void run() { frame2d.statusBar.setText((String) obj); } }); } class MainFrameRunnable implements Runnable { Frame2D frame2d; Object obj; /** * * @param fr2d * @param obj */ public MainFrameRunnable(Frame2D fr2d, Object obj) { frame2d = fr2d; this.obj = obj; } public void run() { } } /** * heavyweight subclass handling all mouse events fired by the main * jscrollpane. * * main functions : 1) coordinate and value info on statusbar 2) magnifying * lens (x4) * */ private class MouseHandler extends MouseInputAdapter { private JWindow zoomWindow = null; private JPanel root = null; private Frame2D parent; private int width = 100; private int height = 100; private JScrollPane scrollX = null; private int widthX = 25; private int heightX = 25; private Cursor BLANK_CURSOR = Toolkit.getDefaultToolkit() .createCustomCursor(new BufferedImage(1, 1, BufferedImage.TRANSLUCENT), new Point(0, 0), "blank"); MouseHandler(Frame2D parent) { this.parent = parent; zoomWindow = new JWindow(parent); root = new JPanel(); root.setPreferredSize(new Dimension(width, height)); root.setLayout(new BorderLayout()); zoomWindow.setContentPane(root); zoomWindow.pack(); } private void updateImage(int xdim, int ydim) { JViewport view = parent.scroll.getViewport(); Point local = view.getViewPosition(); // get the pixels of the area..25x25. // centered on the current pixel byte[] pixels = null; int[] ipixel = null; int[] bandOffsets = { 0, 1, 2 }; int[] voidPixel = { 0, 0, 0 }; if (parent.color == false) { ipixel = new int[1]; pixels = new byte[width * height]; } else { ipixel = new int[3]; pixels = new byte[width * height * 3]; } int xref = xdim + local.x - widthX / 2; int yref = ydim + local.y - heightX / 2; for (int x = 0; x < widthX; x++) { for (int y = 0; y < heightX; y++) { if (xref + x < 0 || xref + x >= parent.width || yref + y < 0 || yref + y >= parent.height) { if (parent.color == false) setPixel(pixels, x * 4, y * 4, (byte) 0); else setColorPixel(pixels, x * 4, y * 4, voidPixel); } else { int t = frameSld.getValue() - 1; int z = depthSld.getValue() - 1; int finalX=xref + x; int finalY=yref + y; if (parent.color == false) { int b = channelSld.getValue() - 1; setPixel(pixels, x * 4, y * 4, (byte) img.getPixelXYZTBByte(finalX,finalY,z,t,b)); } else { ipixel[0]= img.getPixelXYZTBByte(finalX,finalY,z,t,0); ipixel[1]= img.getPixelXYZTBByte(finalX,finalY,z,t,1); ipixel[2]= img.getPixelXYZTBByte(finalX,finalY,z,t,2); setColorPixel(pixels, x * 4, y * 4, ipixel); } } } } BufferedImage bimg = null; if (parent.color == false) { DataBufferByte dbb = new DataBufferByte(pixels, width * height); SampleModel s = RasterFactory.createBandedSampleModel( DataBuffer.TYPE_BYTE, width, height, 1); Raster r = RasterFactory.createWritableRaster(s, dbb, new Point(0, 0)); bimg = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); bimg.setData(r); } else { DataBufferByte dbb = new DataBufferByte(pixels, width * height * 3); SampleModel s = RasterFactory.createPixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, width, height, 3, 3 * width, bandOffsets); Raster r = RasterFactory.createWritableRaster(s, dbb, new Point(0, 0)); bimg = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); bimg.setData(r); } DisplayJAI disp = new DisplayJAI(bimg); if (scrollX == null) { scrollX = new JScrollPane(disp, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); root.add(scrollX, BorderLayout.CENTER); zoomWindow.pack(); } else { scrollX.setViewportView(disp); } Point p = parent.scroll.getLocationOnScreen(); zoomWindow.setLocation(p.x + xdim - width, p.y + ydim - height); zoomWindow.setVisible(true); } public void mousePressed(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { setCursor(BLANK_CURSOR); updateImage(e.getX(), e.getY()); mouseMoved(e); } else { JFileChooser chooser = new JFileChooser(); chooser.setDialogTitle("Picture save"); int returnVal = chooser.showSaveDialog(parent); if (returnVal == JFileChooser.APPROVE_OPTION) { System.out.println("You save the picture here : " + chooser.getCurrentDirectory() + File.separator + chooser.getSelectedFile().getName()); ImageSave.exec(new ByteImage(img), chooser.getCurrentDirectory() + File.separator + chooser.getSelectedFile().getName()); } } } public void mouseDragged(MouseEvent e) { updateImage(e.getX(), e.getY()); mouseMoved(e); } private void setPixel(byte[] pixels, int x, int y, byte v) { for (int _x = x; _x < x + 4; _x++) { for (int _y = y; _y < y + 4; _y++) { pixels[_x + width * _y] = v; } } } private void setColorPixel(byte[] pixels, int x, int y, int[] v) { for (int b = 0; b < 3; b++) { for (int _x = x; _x < x + 4; _x++) { for (int _y = y; _y < y + 4; _y++) { pixels[b + 3 * _x + 3 * width * _y] = (byte) v[b]; } } } } public void mouseMoved(MouseEvent e) { int xdim = e.getX(); int ydim = e.getY(); JViewport view = parent.scroll.getViewport(); Point local = view.getViewPosition(); xdim += local.x; ydim += local.y; if (xdim >= parent.width || ydim >= parent.height || xdim < 0 || ydim < 0) return; if (color == true) { int t = frameSld.getValue() - 1; int z = depthSld.getValue() - 1; int pixel0 = img.getPixelXYZTBByte(xdim,ydim,z,t,0); int pixel1 = img.getPixelXYZTBByte(xdim,ydim,z,t,1); int pixel2 = img.getPixelXYZTBByte(xdim,ydim,z,t,2); parent.statusBarMsg("(" + xdim + "," + ydim + "):(" + pixel0 + "," + pixel1 + "," + pixel2 + ")"); } else { int b = channelSld.getValue() - 1; int t = frameSld.getValue() - 1; int z = depthSld.getValue() - 1; int pixelValue = img.getPixelXYZTBByte(xdim,ydim,z,t,b); parent.statusBarMsg("(" + xdim + "," + ydim + "):(" + pixelValue + ")"); } } public void mouseReleased(MouseEvent e) { setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); zoomWindow.setVisible(false); } } }