/*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2010 University of Dundee. All rights reserved.
*
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.examples.viewer;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;
import java.io.ByteArrayInputStream;
import javax.imageio.ImageIO;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.openmicroscopy.shoola.util.image.geom.Factory;
import omero.api.RenderingEnginePrx;
import omero.romio.PlaneDef;
import omero.romio.RegionDef;
import omero.gateway.model.ImageData;
import omero.gateway.model.PixelsData;
import sun.awt.image.IntegerInterleavedRaster;
/**
* Displays the image and controls.
* Thanks to Galli Vanni vanni.galli@supsi.ch to add controls for turning
* channels on and off.
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author Donald MacDonald
* <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a>
* @version 3.0
* @since 3.0-Beta4
*/
class ViewerPane
extends JPanel
implements ChangeListener
{
/** The compression level. */
private static final float COMPRESSION = 0.5f;
/** The red mask. */
private static final int RED_MASK = 0x00ff0000;
/** The green mask. */
private static final int GREEN_MASK = 0x0000ff00;
/** The blue mask. */
private static final int BLUE_MASK = 0x000000ff;
/** The RGB masks. */
private static final int[] RGB = {RED_MASK, GREEN_MASK, BLUE_MASK};
/** Reference to the rendering engine. */
private RenderingEnginePrx engine;
/** The slider to select the z-section. */
private JSlider zSlider;
/** The slider to select the z-section. */
private JSlider tSlider;
/** Box indicating to render the image as compressed or not. */
private JCheckBox compressed;
/** The image canvas. */
private ImageCanvasInterface canvas;
/** The image currently viewed. */
private ImageData image;
/** JPanel displaying all the available channels **/
private JPanel channelsPane;
/** Number of channels of the image **/
private int channelsNumber;
/** Indicates that the channels are selected or not. */
private JCheckBox[] channels;
private double factor = 0.25;
/**
* Creates a buffer image from the specified <code>array</code> of
* integers.
*
* @param buf The array to handle.
* @param bits The number of bits in the pixel values.
* @param sizeX The width (in pixels) of the region of image data described.
* @param sizeY The height (in pixels) of the region of image data
* described.
* @return See above.
*/
private BufferedImage createImage(int[] buf, int bits, int sizeX,
int sizeY)
{
if (buf == null) return null;
DataBuffer j2DBuf = new DataBufferInt(buf, sizeX*sizeY);
SinglePixelPackedSampleModel sampleModel =
new SinglePixelPackedSampleModel(
DataBuffer.TYPE_INT, sizeX, sizeY, sizeX, RGB);
WritableRaster raster =
new IntegerInterleavedRaster(sampleModel, j2DBuf,
new Point(0, 0));
ColorModel colorModel = new DirectColorModel(bits, RGB[0],
RGB[1], RGB[2]);
BufferedImage image =
new BufferedImage(colorModel, raster, false, null);
image.setAccelerationPriority(1f);
return image;
}
/** Renders a plane. */
private void render()
{
try {
int sizeX = image.getDefaultPixels().getSizeX();
int sizeY = image.getDefaultPixels().getSizeY();
PlaneDef pDef = new PlaneDef();
pDef.t = engine.getDefaultT();
pDef.z = engine.getDefaultZ();
pDef.slice = omero.romio.XY.value;
//now render the image. possible to render it compressed or not
//not compressed
BufferedImage img = null;
if (!compressed.isSelected()) {
int[] buf = engine.renderAsPackedInt(pDef);
img = createImage(buf, 32, sizeX, sizeY);
} else {
byte[] values = engine.renderCompressed(pDef);
ByteArrayInputStream stream = new ByteArrayInputStream(values);
img = ImageIO.read(stream);
img.setAccelerationPriority(1f);
}
canvas.setImage(img);
} catch (Exception e) {
}
}
/**
* Renders a region.
*
* @param region The region to render
*/
private void renderRegion(Rectangle region)
{
try {
PlaneDef pDef = new PlaneDef();
pDef.t = engine.getDefaultT();
pDef.z = engine.getDefaultZ();
pDef.slice = omero.romio.XY.value;
int factor = 4;
pDef.region = new RegionDef(region.x*factor, region.y*factor,
region.width*factor, region.height*factor);
//now render the image. possible to render it compressed or not
//not compressed
BufferedImage img = null;
if (!compressed.isSelected()) {
int[] buf = engine.renderAsPackedInt(pDef);
img = createImage(buf, 32, pDef.region.width, pDef.region.height);
} else {
byte[] values = engine.renderCompressed(pDef);
ByteArrayInputStream stream = new ByteArrayInputStream(values);
img = ImageIO.read(stream);
img.setAccelerationPriority(1f);
}
canvas.setImage(Factory.magnifyImage(5, img)); //for testing purpose only
} catch (Exception e) {
e.printStackTrace();
}
}
private void setSelection(Point p)
{
}
/**
* Initializes the components.
* @param viewType The type of viewer to display the image, JAVA, or
* Processing.
*/
private void initComponents(ViewType viewType)
{
compressed = new JCheckBox("Compressed Image");
compressed.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
render();
}
});
switch(viewType)
{
case JAVA:
canvas = new ImageCanvas();
}
zSlider = new JSlider();
zSlider.setMinimum(0);
zSlider.setEnabled(false);
zSlider.addChangeListener(this);
tSlider = new JSlider();
tSlider.setMinimum(0);
tSlider.setEnabled(false);
tSlider.addChangeListener(this);
}
/**
* Builds the channel component.
*
* @param n The number of channels.
*/
private void buildChannelsPane(int n)
{
try
{
channelsPane.removeAll();
remove(channelsPane);
channels = new JCheckBox[n];
for (int i = 0; i < n; i ++)
{
channels[i] = new JCheckBox("Channel " + i);
channels[i].setSelected(true);
channels[i].addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
setActiveChannels(e);
}
});
channelsPane.add(channels[i]);
}
add(channelsPane);
}
catch (Exception e)
{
}
}
/**
* Sets the channel active or not.
*
* @param evt The event to handle.
*/
private void setActiveChannels(ActionEvent evt)
{
try
{
for (int i = 0; i < channelsNumber; i ++)
{
engine.setActive(i, channels[i].isSelected());
}
render();
}
catch(Exception e)
{
// TODO: handle exception
}
}
/** Builds and lays out the component. */
private void buildGUI()
{
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
JPanel row = new JPanel();
row.setLayout(new FlowLayout(FlowLayout.LEFT));
row.add(new JLabel("Z"));
row.add(zSlider);
row.add(Box.createHorizontalStrut(5));
row.add(new JLabel("T"));
row.add(tSlider);
JPanel p = new JPanel();
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
p.add(row);
JPanel content = new JPanel();
content.setLayout(new FlowLayout(FlowLayout.LEFT));
content.add(compressed);
p.add(content);
channelsPane = new JPanel();
channelsPane.setLayout(new FlowLayout(FlowLayout.LEFT));
p.add(channelsPane);
JPanel pp = new JPanel();
pp.setLayout(new FlowLayout(FlowLayout.LEFT));
pp.add(p);
add(new JScrollPane(canvas.getCanvas()));
add(pp);
}
/** Creates a new instance.
* @param viewType The type of viewer used to display the image, Java or
* processing.
*/
ViewerPane(ViewType viewType)
{
initComponents(viewType);
buildGUI();
}
/**
* Sets the rendering engine.
*
* @param image The image.
* @param engine The engine.
*/
void setRenderingControl(ImageData image, RenderingEnginePrx engine)
{
this.engine = engine;
this.image = image;
try {
this.engine.setCompressionLevel(COMPRESSION);
} catch (Exception e) {
// TODO: handle exception
}
PixelsData pixels = image.getDefaultPixels();
int sizeX = pixels.getSizeX();
int sizeY = pixels.getSizeY();
Dimension d = new Dimension(sizeX, sizeY);
canvas.setCanvasSize(d);
zSlider.removeChangeListener(this);
tSlider.removeChangeListener(this);
zSlider.setMaximum(pixels.getSizeZ());
zSlider.setEnabled(pixels.getSizeZ() > 1);
tSlider.setMaximum(pixels.getSizeT());
tSlider.setEnabled(pixels.getSizeT() > 1);
try {
zSlider.setValue(engine.getDefaultZ()+1);
tSlider.setValue(engine.getDefaultT()+1);
} catch (Exception e) {
// TODO: handle exception
}
zSlider.addChangeListener(this);
tSlider.addChangeListener(this);
// number of channels in the image (RGB)
channelsNumber = image.getDefaultPixels().getSizeC();
buildChannelsPane(channelsNumber);
render();
}
/** Sets the z-section or time-point. */
public void stateChanged(ChangeEvent e)
{
Object src = e.getSource();
try {
if (src == zSlider) {
engine.setDefaultZ(zSlider.getValue()-1);
render();
} else if (src == tSlider) {
engine.setDefaultT(tSlider.getValue()-1);
render();
}
} catch (Exception e2) {
}
}
}