/* * Copyright 2010-2013 Institut Pasteur. * * This file is part of Icy. * * Icy 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 3 of the License, or * (at your option) any later version. * * Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>. */ package icy.image; import icy.image.lut.LUT; import icy.math.Scaler; import icy.system.SystemUtil; import icy.system.thread.Processor; import icy.system.thread.ThreadUtil; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; /** * @author Stephane */ class ARGBImageBuilder { private static final int BLOC_SIZE = 256 * 256; private static final int PARALLEL_PROCESS = SystemUtil.getAvailableProcessors() * 2; private class BlockBuilder implements Runnable { /** * working buffer */ int[][] componentValues; /** * processor */ private Processor processor; /** * processing flag */ boolean processing; /** * cached variables */ private IcyBufferedImage image; private LUT lut; private int dest[]; private int offset; private int length; private int numChannel; BlockBuilder() { super(); // default componentValues = new int[0][0]; // no queue processor = new Processor(1, 1); processor.setDefaultThreadName("ARGB Image builder"); // don't change priority else our image won't never be build if // normal priority thread take all available time // processor.setPriority(Processor.MIN_PRIORITY + 1); processing = false; } private boolean prepare(IcyBufferedImage image, LUT lut, int[] dest, int offset, int length) { this.image = image; // use internal lut if specified lut is null if (lut == null) this.lut = image.getLUT(); else this.lut = lut; this.dest = dest; this.offset = offset; this.length = length; numChannel = image.getSizeC(); if (lut.getNumChannel() != numChannel) { System.err.println("ARGBImageBuilder.prepare(...): LUT.numChannel != IMAGE.numChannel"); return false; } return true; } private void clean() { // release reference image = null; lut = null; dest = null; } boolean build(IcyBufferedImage image, LUT lut, int dest[], int offset, int length) { synchronized (this) { if (processing) return false; processing = true; } // prepare variables if (prepare(image, lut, dest, offset, length)) { // add task if (processor.submit(this) != null) return true; synchronized (this) { processing = false; } // task not added return false; } synchronized (this) { processing = false; } // error while preparing (synchronization error) --> ignore return true; } @Override public void run() { try { // rebuild buffer if needed if (componentValues.length != numChannel) componentValues = new int[numChannel][BLOC_SIZE]; // update output image buffer final Scaler[] scalers = lut.getScalers(); final boolean signed = image.getIcyColorModel().getDataType_().isSigned(); // scale component values for (int comp = 0; comp < numChannel; comp++) scalers[comp].scale(image.getDataXY(comp), offset, componentValues[comp], 0, length, signed); // build ARGB destination buffer lut.getColorSpace().fillARGBBuffer(componentValues, dest, offset, length); } catch (Exception E) { // we just ignore any exceptions here as we can be in asynch process } finally { // clean up clean(); synchronized (this) { processing = false; } } } } // builders private final BlockBuilder builders[]; /** * */ ARGBImageBuilder() { super(); builders = new BlockBuilder[PARALLEL_PROCESS]; for (int i = 0; i < PARALLEL_PROCESS; i++) builders[i] = new BlockBuilder(); } private BufferedImage getImage(IcyBufferedImage in, BufferedImage out) { if ((out != null) && ImageUtil.sameSize(in, out)) return out; return new BufferedImage(in.getWidth(), in.getHeight(), BufferedImage.TYPE_INT_ARGB); } synchronized BufferedImage buildARGBImage(IcyBufferedImage image, LUT lut, BufferedImage out) { // planar size final int imageSize = image.getSizeX() * image.getSizeY(); final int step = imageSize / BLOC_SIZE; final BufferedImage result = getImage(image, out); // destination buffer final int[] dest = ((DataBufferInt) result.getRaster().getDataBuffer()).getData(); int offset = 0; for (int i = 0; i < step; i++) { // build bloc sendBuild(image, lut, dest, offset, BLOC_SIZE); offset += BLOC_SIZE; } // last bloc if (offset < imageSize) sendBuild(image, lut, dest, offset, imageSize - offset); // wait until image is built waitCompletion(); return result; } private void sendBuild(IcyBufferedImage image, LUT lut, int dest[], int offset, int length) { boolean done = false; while (!done) { final BlockBuilder builder = getAvailableBuilder(); done = builder.build(image, lut, dest, offset, length); } } /** * Get first available builder, wait until we get one */ private BlockBuilder getAvailableBuilder() { while (true) { for (BlockBuilder builder : builders) if (!builder.processing) return builder; // allow other thread to process ThreadUtil.sleep(1); } } boolean isProcessing() { for (BlockBuilder builder : builders) if (builder.processing) return true; return false; } /** * wait until all process ended */ private void waitCompletion() { while (isProcessing()) ThreadUtil.sleep(1); } }