/*
* omeis.providers.re.RenderingStrategy
*
* Copyright 2006 University of Dundee. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package omeis.providers.re;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ome.model.core.Pixels;
import ome.model.enums.RenderingModel;
import omeis.providers.re.data.PlaneDef;
import omeis.providers.re.data.RegionDef;
import omeis.providers.re.quantum.QuantizationException;
/**
* Defines how to encapsulate a specific rendering algorithm.
* <p>
* Subclasses realize a given algorithm by implementing the
* {@link #render(Renderer, PlaneDef) render} method. The image is rendered
* according to the current settings in the rendering context which is accessed
* through a {@link Renderer} object representing the rendering environment.
* </p>
* <p>
* The {@link #makeNew(RenderingModel) makeNew} factory method allows to select
* a concrete strategy depending on on how transformed data is to be mapped into
* a color space.
* </p>
*
* @see Renderer
* @author Jean-Marie Burel <a
* href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author <br>
* Andrea Falconi <a
* href="mailto:a.falconi@dundee.ac.uk"> a.falconi@dundee.ac.uk</a>
* @version 2.2 <small> (<b>Internal version:</b> $Revision$ $Date:
* 2005/06/18 14:36:02 $) </small>
* @since OME2.2
*/
abstract class RenderingStrategy {
/** The logger for this particular class */
private static Logger log = LoggerFactory.getLogger(RenderingStrategy.class);
/** The rendering context. */
protected Renderer renderer;
/**
* The number of pixels on the <i>X1</i>-axis. This is the <i>X</i>-axis
* in the case of an <i>XY</i> or <i>XZ</i> plane. Otherwise it is the
* <i>Z</i>-axis <i>ZY</i> plane.
*/
protected int sizeX1;
/**
* The number of pixels on the X2-axis. This is the <i>Y</i>-axis in the
* case of an <i>XY</i> or <i>ZY</i> plane. Otherwise it is the <i>Z</i>-axis
* <i>XZ</i> plane.
*/
protected int sizeX2;
/**
* The maximum number of tasks that we will be using during rendering.
*/
protected int maxTasks;
/**
* Checks if the passed region is valid.
*
* @param region The region to handle.
* @param pixels The pixels set.
*/
private void isRegionValid(RegionDef region, Pixels pixels)
{
if (region == null) return;
int x = region.getX();
if (x < 0)
throw new RuntimeException("Invalid Region, X-coordinate of the " +
"top-left corner cannot be negative:"+x);
int y = region.getY();
if (y < 0)
throw new RuntimeException("Invalid Region, y-coordinate of the " +
"top-left corner cannot be negative:"+y);
int w = region.getWidth();
if (w <= 0)
throw new RuntimeException("Invalid Region, the width must be " +
"positive:"+w);
int h = region.getHeight();
if (h <= 0)
throw new RuntimeException("Invalid Region, the height must be " +
"positive:"+h);
//for now only check of XY plane
int sizeX = pixels.getSizeX().intValue();
int sizeY = pixels.getSizeY().intValue();
if (x+w > sizeX) //reset the width.
region.setWidth(sizeX-x);
if (y+h > sizeY) //reset the height.
region.setHeight(sizeY-y);
}
/**
* Initializes the <code>sizeX1</code> and <code>sizeX2</code> fields
* according to the specified {@link PlaneDef#getSlice() slice}.
*
* @param pd
* Reference to the plane definition defined for the strategy.
* @param pixels
* Dimensions of the pixels set.
*/
protected void initAxesSize(PlaneDef pd, Pixels pixels) {
RegionDef region = pd.getRegion();
isRegionValid(region, pixels);
int stride = pd.getStride();
if (stride < 0) stride = 0;
stride++;
try {
switch (pd.getSlice()) {
case PlaneDef.XY:
if (region != null) {
sizeX1 = region.getWidth();
sizeX2 = region.getHeight();
} else {
sizeX1 = pixels.getSizeX().intValue();
sizeX2 = pixels.getSizeY().intValue();
}
sizeX1 = sizeX1/stride;
sizeX2 = sizeX2/stride;
break;
case PlaneDef.XZ:
sizeX1 = pixels.getSizeX().intValue();
sizeX2 = pixels.getSizeZ().intValue();
break;
case PlaneDef.ZY:
sizeX1 = pixels.getSizeZ().intValue();
sizeX2 = pixels.getSizeY().intValue();
}
} catch (NumberFormatException nfe) {
throw new RuntimeException("Invalid slice ID: " + pd.getSlice()
+ ".", nfe);
}
}
/**
* Constructs a strategy.
*/
protected RenderingStrategy()
{
maxTasks = Runtime.getRuntime().availableProcessors();
}
/**
* Returns an RGB buffer for usage. Note that the buffer is reallocated
* upon each call. Should only be called within the context of a
* "render" operation as it requires a {@link renderer}.
*
* @param x1 The size to allocate along the X1-axis.
* @param x2 The size to allocate along the X2-axis.
* @return See above.
*/
protected RGBBuffer getRgbBuffer()
{
RenderingStats stats = renderer.getStats();
stats.startMalloc();
RGBBuffer buf = new RGBBuffer(sizeX1, sizeX2);
stats.endMalloc();
return buf;
}
/**
* Returns an RGB integer buffer for usage. Note that the buffer is
* reallocated upon each call. Should only be called within the context of
* a "render" operation as it requires a {@link renderer}.
*
* @return See above.
*/
protected RGBIntBuffer getIntBuffer()
{
RenderingStats stats = renderer.getStats();
stats.startMalloc();
RGBIntBuffer buf = new RGBIntBuffer(sizeX1, sizeX2);
stats.endMalloc();
return buf;
}
/**
* Returns an RGBA integer buffer for usage. Note that the buffer is
* reallocated upon each call. Should only be called within the context of
* a "render" operation as it requires a {@link renderer}.
*
* @return See above.
*/
protected RGBAIntBuffer getRGBAIntBuffer()
{
RenderingStats stats = renderer.getStats();
stats.startMalloc();
RGBAIntBuffer buf = new RGBAIntBuffer(sizeX1, sizeX2);
stats.endMalloc();
return buf;
}
/**
* Factory method to retrieve a concrete strategy. The strategy is selected
* according to the model that dictates how transformed raw data is to be
* mapped into a color space. This model is identified by the passed
* argument.
*
* @param model
* Identifies the color space model.
* @return A strategy suitable for the specified model.
*/
static RenderingStrategy makeNew(RenderingModel model) {
String value = model.getValue();
if (value.equals(Renderer.MODEL_GREYSCALE)) {
return new GreyScaleStrategy();
} else if (value.equals(Renderer.MODEL_HSB)) {
return new HSBStrategy();
} else if (value.equals(Renderer.MODEL_RGB)) {
//return new RGBStrategy();
return new HSBStrategy();
}
log.warn("WARNING: Unknown model '" + value + "' using greyscale.");
return new GreyScaleStrategy();
}
/**
* Encapsulates a specific rendering algorithm. The image is rendered
* according to the current settings hold by the <code>ctx</code>
* argument. Typically, active wavelengths are processed by first quantizing
* the wavelength data in the plane selected by <code>pd</code> the
* quantum strategy is retrieved from the {@link QuantumManager} (accessed
* through the <code>ctx</code> object) and the actual data from the
* {@link omeis.providers.re.data.PixelsData PixelsData} service (again,
* retrieved through <code>ctx</code>). Then the codomain transformations
* are applied by calling the transform method of the
* {@link omeis.providers.re.codomain.CodomainChain chain} hold by
* <code>ctx</code>. Transformed wavelength data is finally packed into a
* {@link RGBBuffer} taking into account the color bindings defined by the
* rendering context.
*
* @param ctx
* Represents the rendering environment.
* @param pd
* Selects a plane orthogonal to one of the <i>X</i>, <i>Y</i>,
* or <i>Z</i> axes.
* @return An image rendered according to the current settings hold by
* <code>ctx</code>.
* @throws IOException
* If an error occurred while accessing the pixels raw data.
* @throws QuantizationException
* If an error occurred while quantizing the pixels raw data.
* @see renderAsPackedInt()
*/
abstract RGBBuffer render(Renderer ctx, PlaneDef pd) throws IOException,
QuantizationException;
/**
* Encapsulates a specific rendering algorithm. The image is rendered
* according to the current settings hold by the <code>ctx</code>
* argument. Typically, active wavelengths are processed by first quantizing
* the wavelength data in the plane selected by <code>pd</code> the
* quantum strategy is retrieved from the {@link QuantumManager} (accessed
* through the <code>ctx</code> object) and the actual data from the
* {@link omeis.providers.re.data.PixelsData PixelsData} service (again,
* retrieved through <code>ctx</code>). Then the codomain transformations
* are applied by calling the transform method of the
* {@link omeis.providers.re.codomain.CodomainChain chain} hold by
* <code>ctx</code>. Transformed wavelength data is finally packed into a
* {@link RGBBuffer} taking into account the color bindings defined by the
* rendering context.
*
* @param ctx
* Represents the rendering environment.
* @param pd
* Selects a plane orthogonal to one of the <i>X</i>, <i>Y</i>,
* or <i>Z</i> axes.
* @return An image rendered according to the current settings hold by
* <code>ctx</code>.
* @throws IOException
* If an error occurred while accessing the pixels raw data.
* @throws QuantizationException
* If an error occurred while quantizing the pixels raw data.
* @see render()
*/
abstract RGBIntBuffer renderAsPackedInt(Renderer ctx, PlaneDef pd)
throws IOException, QuantizationException;
/**
* Encapsulates a specific rendering algorithm. The image is rendered
* according to the current settings hold by the <code>ctx</code>
* argument. Typically, active wavelengths are processed by first
* quantizing the wavelength data in the plane selected by <code>pd</code>
* the quantum strategy is retrieved from the {@link QuantumManager}
* (accessed through the <code>ctx</code> object) and the actual data from
* the {@link omeis.providers.re.data.PixelsData PixelsData} service again,
* retrieved through <code>ctx</code>). Then the codomain transformations
* are applied by calling the transform method of the
* {@link omeis.providers.re.codomain.CodomainChain chain} hold by
* <code>ctx</code>. Transformed wavelength data is finally packed into a
* {@link RGBBuffer} taking into account the color bindings defined by the
* rendering context.
*
* @param ctx
* Represents the rendering environment.
* @param pd
* Selects a plane orthogonal to one of the <i>X</i>, <i>Y</i>,
* or <i>Z</i> axes.
* @return An image rendered according to the current settings hold by
* <code>ctx</code>.
* @throws IOException
* If an error occurred while accessing the pixels raw data.
* @throws QuantizationException
* If an error occurred while quantizing the pixels raw data.
* @see render()
*/
abstract RGBAIntBuffer renderAsPackedIntAsRGBA(Renderer ctx, PlaneDef pd)
throws IOException, QuantizationException;
/**
* Returns the size, in bytes, of the {@link RGBBuffer} that would be
* rendered from the plane selected by <code>pd</code> in a pixels set
* having dimensions <code>dims</code>.
*
* @param pd
* Selects a plane orthogonal to one of the <i>X</i>, <i>Y</i>,
* or <i>Z</i> axes.
* @param pixels
* A pixels set.
* @return See above.
*/
abstract int getImageSize(PlaneDef pd, Pixels pixels);
/**
* Returns a string with the dimensions of the specified plane. The returned
* string has the format <code>AxB</code>, where <code>A</code> is the
* number of pixels on the <i>X1</i>-axis and <code>B</code> the the
* number of pixels on the the <i>X2</i>-axis. The <i>X1</i>-axis is the
* <i>X</i>-axis in the case of an <i>XY</i> or <i>XZ</i> plane.
* Otherwise it is the <i>Z</i>-axis <i>ZY</i> plane. The <i>X2</i>-axis
* is the <i>Y</i>-axis in the case of an <i>XY</i> or <i>ZY</i> plane.
* Otherwise it is the <i>Z</i>-axis <i>XZ</i> plane.
*
* @param pd
* Selects a plane orthogonal to one of the <i>X</i>, <i>Y</i>,
* or <i>Z</i> axes.
* @param pixels
* A pixels set.
* @return See above.
*/
abstract String getPlaneDimsAsString(PlaneDef pd, Pixels pixels);
}