/*** * Image/J Plugins * Copyright (C) 2002-2004 Jarek Sacha * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Latest release available at http://sourceforge.net/projects/ij-plugins/ */ package edu.mbl.jif.imagej; //import com.sun.media.jai.codec.ImageCodec; import ij.ImagePlus; import ij.process.*; import java.awt.*; import java.awt.color.ColorSpace; import java.awt.image.*; //import javax.media.jai.RasterFactory; /** * Creates/converts BufferedImage objects from Image/J's ImageProcessor or ImagePlus. * All Image/J image types are supported. * * @author Jarek Sacha * @version $Revision: 1.8 $ */ public class BufferedImageCreator { private BufferedImageCreator () { } /** * Create BufferedImage from a slice <code>sliceNb</code> in image <code>src</code> . Indexing * starts at 0. New image has a copy of pixels in the source image. * * @param src Source image. * @param sliceNb Slice number, numbering starts at 0. * @return New BufferedImage. */ public static BufferedImage create (ImagePlus src, int sliceNb) { // Get slice image processor int oldSliceNb = src.getCurrentSlice(); src.setSlice(sliceNb + 1); ImageProcessor ip = src.getProcessor().duplicate(); src.setSlice(oldSliceNb); // Convert image processor switch (src.getType()) { case ImagePlus.GRAY8: // Assume gray level 8 bit color model. Do not use color model provided by // ImageProcessor since it can be 16 bit even for 8 bit ByteProcessor. final ColorModel cm = createGray8ColorModel(src.isInvertedLut()); if (cm != null && (cm instanceof IndexColorModel)) { return create((ByteProcessor) ip, (IndexColorModel) cm); } else { return create((ByteProcessor) ip); } case ImagePlus.GRAY16: return create((ShortProcessor) ip); case ImagePlus.GRAY32: // return create((FloatProcessor) ip); case ImagePlus.COLOR_256: return createColor256((ByteProcessor) ip, (IndexColorModel) ip.getColorModel()); case ImagePlus.COLOR_RGB: return create((ColorProcessor) ip); default: throw new IllegalArgumentException("Unrecognized image type: " + src.getType() + "."); } } /** * Create BufferedImages corresponding to each slice in the source image. * @param src Source image. * @return Array of BufferedImages, one per source slice. */ public static BufferedImage[] createArray (ImagePlus src) { BufferedImage[] r = new BufferedImage[src.getStackSize()]; // Get slice image processor int oldSliceNb = src.getCurrentSlice(); for (int i = 0; i < r.length; ++i) { // Set slice here to minimize slice switching by create() src.setSlice(i + 1); r[i] = create(src, i); } src.setSlice(oldSliceNb); return r; } /** * Create BufferedImage from ByteProcessor. * * @param src ByteProcessor source. * @return BufferedImage. */ public static BufferedImage create (ByteProcessor src) { byte[] r = new byte[256]; byte[] g = new byte[256]; byte[] b = new byte[256]; for (int i = 0; i < 256; ++i) { r[i] = g[i] = b[i] = (byte) (i & 0xff); } IndexColorModel icm = new IndexColorModel(8, 256, r, g, b); return create(src, icm); } /** * Create BufferedImage from an 256 indexed color image. If supplied color model mas map size * less than 256 it will be extended. * @param src ByteProcessor source. * @param icm Color model. * @return BufferedImage. * @see #create(ij.process.ByteProcessor, java.awt.image.IndexColorModel) */ public static BufferedImage createColor256 (ByteProcessor src, IndexColorModel icm) { final int mapSize = icm.getMapSize(); final IndexColorModel icm256; if (mapSize == 256) { // Use current color model icm256 = icm; } else if (mapSize < 256) { // Extend color model to 256 final byte[] r = new byte[256]; final byte[] g = new byte[256]; final byte[] b = new byte[256]; icm.getReds(r); icm.getGreens(g); icm.getBlues(b); icm256 = new IndexColorModel(8, 256, r, g, b); } else { throw new UnsupportedOperationException( "Unable to properly decode this image (color map).\n" + "Please report this problem at http://ij-plugins.sf.net\n" + "or by sending email to 'jsacha at users.sourceforge.net'\n" + " Map size = " + mapSize + "."); } return create(src, icm256); } /** * Create BufferedImage from an indexed color image. * * @param src ByteProcessor source. * @param icm Color model. * @return BufferedImage. */ public static BufferedImage create (ByteProcessor src, IndexColorModel icm) { WritableRaster wr = icm.createCompatibleWritableRaster(src.getWidth(), src.getHeight()); final byte[] bitsOn = { (byte) 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; byte[] srcPixels = (byte[]) src.getPixels(); DataBufferByte dataBuffer = (DataBufferByte) wr.getDataBuffer(); byte[] destPixels = dataBuffer.getData(); int mapSize = icm.getMapSize(); if (mapSize == 256) { System.arraycopy(srcPixels, 0, destPixels, 0, destPixels.length); return new BufferedImage(icm, wr, false, null); } else if (mapSize == 2) { // Double check that dest data are large enough int srcWidth = src.getWidth(); int destWidth = (src.getWidth() + 7) / 8; int expectedDestSize = destWidth * src.getHeight(); if (destPixels.length != expectedDestSize) { throw new IllegalStateException("Internal error: wrong size of destPixels."); } // Single bit image, pack bits for (int i = 0; i < destPixels.length; ++i) { byte destByte = 0x00; int offset = (i / destWidth) * srcWidth + (i % destWidth) * 8; for (int j = 0; j < 8 && ((j + offset) < srcPixels.length); ++j) { if (srcPixels[j + offset] != 0) { destByte += bitsOn[j]; } } destPixels[i] = destByte; } return new BufferedImage(icm, wr, false, null); } else { // FIX: deal with all bit packing schemes throw new UnsupportedOperationException( "Unable to properly decode this image (color map).\n" + "Please report this problem at http://ij-plugins.sf.net\n" + "or by sending email to 'jsacha at users.sourceforge.net'\n" + " Map size = " + mapSize + "\n" + " Src pixels = " + srcPixels.length + "\n" + " Dest pixels = " + destPixels.length); } } /** * Create BufferedImage from ShortProcessor. Pixel values are assumed to be unsigned short * integers. * * @param src ShortProcessor source. * @return BufferedImage. */ public static BufferedImage create (ShortProcessor src) { BufferedImage bufferedImage = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_USHORT_GRAY); Raster raster = bufferedImage.getData(); DataBufferUShort dataBuffer = (DataBufferUShort) raster.getDataBuffer(); System.arraycopy(src.getPixels(), 0, dataBuffer.getData(), 0, dataBuffer.getData().length); bufferedImage.setData(raster); return bufferedImage; } /** * Create BufferedImage from FloatProcessor. * * @param src FloatProcessor source. * @return BufferedImage. */ // public static BufferedImage create (FloatProcessor src) { // int w = src.getWidth(); // int h = src.getHeight(); // int nbBands = 1; // int[] rgbOffset = new int[nbBands]; // SampleModel sampleModel = RasterFactory.createPixelInterleavedSampleModel( // DataBuffer.TYPE_FLOAT, w, h, nbBands, nbBands * w, rgbOffset); // // ColorModel colorModel = ImageCodec.createComponentColorModel(sampleModel); // // float[] pixels = (float[]) src.getPixels(); // DataBufferFloat dataBuffer = new DataBufferFloat(pixels, pixels.length); // // WritableRaster raster = RasterFactory.createWritableRaster(sampleModel, // dataBuffer, new Point(0, 0)); // // return new BufferedImage(colorModel, raster, false, null); // } /** * Create BufferedImage from ColorProcessor. * * @param src ColorProcessor source. * @return BufferedImage. */ public static BufferedImage create (ColorProcessor src) { ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); int[] bits = { 8, 8, 8}; ColorModel cm = new ComponentColorModel(cs, bits, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); WritableRaster raster = cm.createCompatibleWritableRaster(src.getWidth(), src.getHeight()); DataBufferByte dataBuffer = (DataBufferByte) raster.getDataBuffer(); byte[] data = dataBuffer.getData(); int n = ((int[]) src.getPixels()).length; byte[] r = new byte[n]; byte[] g = new byte[n]; byte[] b = new byte[n]; src.getRGB(r, g, b); for (int i = 0; i < n; ++i) { int offset = i * 3; data[offset] = r[i]; data[offset + 1] = g[i]; data[offset + 2] = b[i]; } return new BufferedImage(cm, raster, false, null); } static private ColorModel createGray8ColorModel (final boolean invertLut) { final byte[] rLUT = new byte[256]; final byte[] gLUT = new byte[256]; final byte[] bLUT = new byte[256]; if (invertLut) { for (int i = 0; i < 256; i++) { rLUT[255 - i] = (byte) i; gLUT[255 - i] = (byte) i; bLUT[255 - i] = (byte) i; } } else { for (int i = 0; i < 256; i++) { rLUT[i] = (byte) i; gLUT[i] = (byte) i; bLUT[i] = (byte) i; } } return new IndexColorModel(8, 256, rLUT, gLUT, bLUT); } }