/* * omeis.providers.re.HSBStrategy * * Copyright 2006 University of Dundee. All rights reserved. * Use is subject to license terms supplied in LICENSE.txt */ package omeis.providers.re; import java.awt.Color; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ome.conditions.ResourceError; import ome.io.nio.PixelBuffer; import ome.model.core.Pixels; import ome.model.display.ChannelBinding; import ome.model.display.QuantumDef; import ome.model.enums.PixelsType; import ome.util.PixelData; import omeis.providers.re.codomain.CodomainChain; import omeis.providers.re.data.PlaneFactory; import omeis.providers.re.data.Plane2D; import omeis.providers.re.data.PlaneDef; import omeis.providers.re.quantum.BinaryMaskQuantizer; import omeis.providers.re.quantum.QuantizationException; import omeis.providers.re.quantum.QuantumStrategy; /** * Transforms a plane within a given pixels set into an <i>RGB</i> image. As * many wavelengths (channels) as desired can contribute to the final image and * each wavelength is mapped to a color. All these things are specified by the * rendering context. * <p> * This strategy renders the in "regions", dividing the planar data up based * on {@link #maxTasks} and assigning each task to its own thread. This should * result in parallel rendering on multi-processor machines. * </p> * <p> * Thread-safety relies on the fact that the rendering context is not going to * change during the whole image rendering process and that each task is * working on its own atomic unit of work. * </p> * * @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/22 17:09:48 $) </small> * @since OME2.2 */ class HSBStrategy extends RenderingStrategy { /** The logger for this particular class */ private static Logger log = LoggerFactory.getLogger(HSBStrategy.class); /** * Retrieves the maximum number of reasonable tasks to schedule based on * image size and <i>maxTasks</i>. * * @param size The width along the X2 axis. * @return the number of tasks to schedule. */ private int numTasks(int size) { for (int i = maxTasks; i > 0; i--) { if (size % i == 0) { return i; } } return 1; } /** * Retrieves the wavelength data for all the active channels and overlays. * * @return the wavelength data. */ private List<Plane2D> getWavelengthData(PlaneDef pDef) { ChannelBinding[] channelBindings = renderer.getChannelBindings(); Pixels metadata = renderer.getMetadata(); PixelBuffer pixels = renderer.getPixels(); List<Plane2D> wData = null; try { RenderingStats performanceStats = renderer.getStats(); wData = new ArrayList<Plane2D>(); for (int w = 0; w < channelBindings.length; w++) { if (channelBindings[w].getActive()) { performanceStats.startIO(w); wData.add(PlaneFactory.createPlane(pDef, w, metadata, pixels)); performanceStats.endIO(w); } } Map<byte[], Integer> overlays = renderer.getOverlays(); if (overlays != null) { for (byte[] overlay : overlays.keySet()) { ome.util.PixelData data = new PixelData(PlaneFactory.BIT, ByteBuffer.wrap(overlay)); wData.add(new Plane2D(pDef, metadata, data)); } } } finally { // Make sure that the pixel buffer is cleansed properly. try { pixels.close(); } catch (IOException e) { log.error("Pixels could not be closed successfully.", e); throw new ResourceError( e.getMessage() + " Please check server log."); } } return wData; } /** * Retrieves the color for each active channels. * * @return the active channel color data. */ private List<int[]> getColors() { ChannelBinding[] channelBindings = renderer.getChannelBindings(); List<int[]> colors = new ArrayList<int[]>(); for (int w = 0; w < channelBindings.length; w++) { ChannelBinding cb = channelBindings[w]; if (cb.getActive()) { int[] theNewColor = new int[] { cb.getRed(), cb.getGreen(), cb.getBlue(), cb.getAlpha() }; colors.add(theNewColor); } } Map<byte[], Integer> overlays = renderer.getOverlays(); if (overlays != null) { for (byte[] overlay : overlays.keySet()) { Integer packedColor = overlays.get(overlay); Color color = new Color(packedColor); colors.add(new int[] { color.getRed(), color.getBlue(), color.getGreen(), color.getAlpha() }); } } return colors; } /** * Retrieves the quantum strategy for each active channels * * @return the active channel color data. */ private List<QuantumStrategy> getStrategies() { ChannelBinding[] channelBindings = renderer.getChannelBindings(); QuantumManager qManager = renderer.getQuantumManager(); List<QuantumStrategy> strats = new ArrayList<QuantumStrategy>(); for (int w = 0; w < channelBindings.length; w++) { if (channelBindings[w].getActive()) { strats.add(qManager.getStrategyFor(w)); } } Map<byte[], Integer> overlays = renderer.getOverlays(); if (overlays != null) { QuantumDef def = new QuantumDef(); // Just to fulfill interface Pixels pixels = new Pixels(); PixelsType bitType = new PixelsType(); bitType.setValue(PlaneFactory.BIT); bitType.setBitSize(1); pixels.setPixelsType(bitType); for (int i = 0; i < overlays.size(); i++) { strats.add(new BinaryMaskQuantizer(def, pixels)); } } return strats; } /** * Creates a set of rendering tasks for the image based on the calling * buffer type. * * @param planeDef * The plane to render. * @param buf * The buffer to render into. * @return An array containing the tasks. */ private RenderingTask[] makeRenderingTasks(PlaneDef def, RGBBuffer buf) { List<RenderHSBRegionTask> tasks = new ArrayList<RenderHSBRegionTask>(); // Get all objects we need to create the tasks. CodomainChain cc = renderer.getCodomainChain(); //RenderingStats performanceStats = renderer.getStats(); List<Plane2D> wData = getWavelengthData(def); List<int[]> colors = getColors(); List<QuantumStrategy> strategies = getStrategies(); // Create a number of rendering tasks. int taskCount = numTasks(sizeX2); int delta = sizeX2/taskCount; int x1Start = 0; int x1End = sizeX1; int x2Start, x2End; log.info("taskCount: "+taskCount+" delta: "+delta); for (int i = 0; i < taskCount; i++) { x2Start = i*delta; x2End = (i+1)*delta; tasks.add(new RenderHSBRegionTask(buf, wData, strategies, cc, colors, renderer.getOptimizations(), x1Start, x1End, x2Start, x2End)); } // Turn the list into an array an return it. return tasks.toArray(new RenderingTask[tasks.size()]); } /** * Implemented as specified by the superclass. * * @see RenderingStrategy#render(Renderer ctx, PlaneDef planeDef) */ @Override RGBBuffer render(Renderer ctx, PlaneDef planeDef) throws IOException, QuantizationException { // Set the context and retrieve objects we're gonna use. renderer = ctx; //RenderingStats performanceStats = renderer.getStats(); Pixels metadata = renderer.getMetadata(); // Initialize sizeX1 and sizeX2 according to the plane definition and // create the RGB buffer. initAxesSize(planeDef, metadata); RGBBuffer buf = getRgbBuffer(); render(buf, planeDef); return buf; } /** * Implemented as specified by the superclass. * * @see RenderingStrategy#renderAsPackedInt(Renderer ctx, PlaneDef planeDef) */ @Override RGBIntBuffer renderAsPackedInt(Renderer ctx, PlaneDef planeDef) throws IOException, QuantizationException { // Set the context and retrieve objects we're gonna use. renderer = ctx; Pixels metadata = renderer.getMetadata(); // Initialize sizeX1 and sizeX2 according to the plane definition and // create the RGB buffer. initAxesSize(planeDef, metadata); RGBIntBuffer buf = getIntBuffer(); render(buf, planeDef); return buf; } /** * Implemented as specified by the superclass. * * @see RenderingStrategy#renderAsPackedIntRGBA(Renderer ctx, PlaneDef planeDef) */ @Override RGBAIntBuffer renderAsPackedIntAsRGBA(Renderer ctx, PlaneDef planeDef) throws IOException, QuantizationException { // Set the context and retrieve objects we're gonna use. renderer = ctx; Pixels metadata = renderer.getMetadata(); // Initialize sizeX1 and sizeX2 according to the plane definition and // create the RGB buffer. initAxesSize(planeDef, metadata); RGBAIntBuffer buf = getRGBAIntBuffer(); render(buf, planeDef); return buf; } /** * Implemented as specified by the superclass. * * @see RenderingStrategy#render(Renderer ctx, PlaneDef planeDef) */ private void render(RGBBuffer buf, PlaneDef planeDef) throws IOException, QuantizationException { RenderingStats performanceStats = renderer.getStats(); // Process each active wavelength. If their number N > 1, then // process N-1 async and one in the current thread. If N = 1, // just use the current thread. RenderingTask[] tasks = makeRenderingTasks(planeDef, buf); performanceStats.startRendering(); int n = tasks.length; Future[] rndTskFutures = new Future[n]; // [0] unused. ExecutorService processor = Executors.newCachedThreadPool(); while (0 < --n) { rndTskFutures[n] = processor.submit(tasks[n]); } // Call the task in the current thread. if (n == 0) { tasks[0].call(); } // Wait for all forked tasks (if any) to complete. for (n = 1; n < rndTskFutures.length; ++n) { try { rndTskFutures[n].get(); } catch (Exception e) { if (e instanceof QuantizationException) { throw (QuantizationException) e; } throw new RuntimeException(e); } } // Shutdown the task processor processor.shutdown(); // End the performance metrics for this rendering event. performanceStats.endRendering(); } /** * Implemented as specified by the superclass. * * @see RenderingStrategy#getImageSize(PlaneDef, Pixels) */ @Override int getImageSize(PlaneDef pd, Pixels pixels) { initAxesSize(pd, pixels); return sizeX1 * sizeX2 * 3; } /** * Implemented as specified by the superclass. * * @see RenderingStrategy#getPlaneDimsAsString(PlaneDef, Pixels) */ @Override String getPlaneDimsAsString(PlaneDef pd, Pixels pixels) { initAxesSize(pd, pixels); return sizeX1 + "x" + sizeX2; } }