//
// ChannelFiller.java
//
/*
OME Bio-Formats package for reading and converting biological file formats.
Copyright (C) 2005-@year@ UW-Madison LOCI and Glencoe Software, Inc.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package loci.formats;
import java.io.IOException;
import loci.formats.meta.MetadataStore;
/**
* For indexed color data representing true color, factors out
* the indices, replacing them with the color table values directly.
*
* For all other data (either non-indexed, or indexed with
* "false color" tables), does nothing.
*
* <dl><dt><b>Source code:</b></dt>
* <dd><a href="http://trac.openmicroscopy.org.uk/ome/browser/bioformats.git/components/bio-formats/src/loci/formats/ChannelFiller.java">Trac</a>,
* <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/bio-formats/src/loci/formats/ChannelFiller.java;hb=HEAD">Gitweb</a></dd></dl>
*/
public class ChannelFiller extends ReaderWrapper {
// -- Utility methods --
/** Converts the given reader into a ChannelFiller, wrapping if needed. */
public static ChannelFiller makeChannelFiller(IFormatReader r) {
if (r instanceof ChannelFiller) return (ChannelFiller) r;
return new ChannelFiller(r);
}
// -- Fields --
/**
* Whether to fill in the indices.
* By default, indices are filled iff data not false color.
*/
protected Boolean filled = null;
/** Number of LUT components. */
protected int lutLength;
// -- Constructors --
/** Constructs a ChannelFiller around a new image reader. */
public ChannelFiller() { super(); }
/** Constructs a ChannelFiller with a given reader. */
public ChannelFiller(IFormatReader r) { super(r); }
// -- ChannelFiller methods --
/** Returns true if the indices are being factored out. */
public boolean isFilled() {
if (!reader.isIndexed()) return false; // cannot fill non-indexed color
if (lutLength < 1) return false; // cannot fill when LUTs are missing
return filled == null ? !reader.isFalseColor() : filled;
}
/** Toggles whether the indices should be factored out. */
public void setFilled(boolean filled) {
this.filled = filled;
}
// -- IFormatReader API methods --
/* @see IFormatReader#getSizeC() */
@Override
public int getSizeC() {
if (!isFilled()) return reader.getSizeC();
return reader.getSizeC() * lutLength;
}
/* @see IFormatReader#isRGB() */
@Override
public boolean isRGB() {
if (!isFilled()) return reader.isRGB();
return getRGBChannelCount() > 1;
}
/* @see IFormatReader#isIndexed() */
@Override
public boolean isIndexed() {
if (!isFilled()) return reader.isIndexed();
return false;
}
/* @see IFormatReader#get8BitLookupTable() */
@Override
public byte[][] get8BitLookupTable() throws FormatException, IOException {
if (!isFilled()) return reader.get8BitLookupTable();
return null;
}
/* @see IFormatReader#get16BitLookupTable() */
@Override
public short[][] get16BitLookupTable() throws FormatException, IOException {
if (!isFilled()) return reader.get16BitLookupTable();
return null;
}
/* @see IFormatReader#getChannelDimLengths() */
@Override
public int[] getChannelDimLengths() {
int[] cLengths = reader.getChannelDimLengths();
if (!isFilled()) return cLengths;
// in the case of a single channel, replace rather than append
if (cLengths.length == 1 && cLengths[0] == 1) cLengths = new int[0];
// append filled dimension to channel dim lengths
int[] newLengths = new int[1 + cLengths.length];
newLengths[0] = lutLength;
System.arraycopy(cLengths, 0, newLengths, 1, cLengths.length);
return newLengths;
}
/* @see IFormatReader#getChannelDimTypes() */
@Override
public String[] getChannelDimTypes() {
String[] cTypes = reader.getChannelDimTypes();
if (!isFilled()) return cTypes;
// in the case of a single channel, leave type unchanged
int[] cLengths = reader.getChannelDimLengths();
if (cLengths.length == 1 && cLengths[0] == 1) return cTypes;
// append filled dimension to channel dim types
String[] newTypes = new String[1 + cTypes.length];
newTypes[0] = FormatTools.CHANNEL;
System.arraycopy(cTypes, 0, newTypes, 1, cTypes.length);
return newTypes;
}
/* @see IFormatReader#openBytes(int) */
@Override
public byte[] openBytes(int no) throws FormatException, IOException {
return openBytes(no, 0, 0, getSizeX(), getSizeY());
}
/* @see IFormatReader#openBytes(int, byte[]) */
@Override
public byte[] openBytes(int no, byte[] buf)
throws FormatException, IOException
{
return openBytes(no, buf, 0, 0, getSizeX(), getSizeY());
}
/* @see IFormatReader#openBytes(int, int, int, int, int) */
@Override
public byte[] openBytes(int no, int x, int y, int w, int h)
throws FormatException, IOException
{
byte[] buf = new byte[w * h * getRGBChannelCount() *
FormatTools.getBytesPerPixel(getPixelType())];
return openBytes(no, buf, x, y, w, h);
}
/* @see IFormatReader#openBytes(int, byte[], int, int, int, int) */
@Override
public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h)
throws FormatException, IOException
{
if (!isFilled()) return reader.openBytes(no, buf, x, y, w, h);
// TODO: The pixel type should change to match the available color table.
// That is, even if the indices are uint8, if the color table is 16-bit,
// The pixel type should change to uint16. Similarly, if the indices are
// uint16 but we are filling with an 8-bit color table, the pixel type
// should change to uint8.
// TODO: This logic below is opaque and could use some comments.
byte[] pix = reader.openBytes(no, x, y, w, h);
if (getPixelType() == FormatTools.UINT8) {
byte[][] b = ImageTools.indexedToRGB(reader.get8BitLookupTable(), pix);
if (isInterleaved()) {
int pt = 0;
for (int i=0; i<b[0].length; i++) {
for (int j=0; j<b.length; j++) {
buf[pt++] = b[j][i];
}
}
}
else {
for (int i=0; i<b.length; i++) {
System.arraycopy(b[i], 0, buf, i*b[i].length, b[i].length);
}
}
return buf;
}
short[][] s = ImageTools.indexedToRGB(reader.get16BitLookupTable(),
pix, isLittleEndian());
if (isInterleaved()) {
int pt = 0;
for (int i=0; i<s[0].length; i++) {
for (int j=0; j<s.length; j++) {
buf[pt++] = (byte) (isLittleEndian() ?
(s[j][i] & 0xff) : (s[j][i] >> 8));
buf[pt++] = (byte) (isLittleEndian() ?
(s[j][i] >> 8) : (s[j][i] & 0xff));
}
}
}
else {
int pt = 0;
for (int i=0; i<s.length; i++) {
for (int j=0; j<s[i].length; j++) {
buf[pt++] = (byte) (isLittleEndian() ?
(s[i][j] & 0xff) : (s[i][j] >> 8));
buf[pt++] = (byte) (isLittleEndian() ?
(s[i][j] >> 8) : (s[i][j] & 0xff));
}
}
}
return buf;
}
// -- IFormatHandler API methods --
/* @see IFormatHandler#getNativeDataType() */
public Class<?> getNativeDataType() {
return byte[].class;
}
/* @see IFormatHandler#setId(String) */
@Override
public void setId(String id) throws FormatException, IOException {
super.setId(id);
lutLength = getLookupTableComponentCount();
MetadataStore store = getMetadataStore();
MetadataTools.populatePixels(store, this, false, false);
}
// -- Helper methods --
/** Gets the number of color components in the lookup table. */
private int getLookupTableComponentCount()
throws FormatException, IOException
{
byte[][] lut8 = reader.get8BitLookupTable();
if (lut8 != null) return lut8.length;
short[][] lut16 = reader.get16BitLookupTable();
if (lut16 != null) return lut16.length;
lut8 = reader.get8BitLookupTable();
if (lut8 != null) return lut8.length;
lut16 = reader.get16BitLookupTable();
if (lut16 != null) return lut16.length;
return 0; // LUTs are missing
}
}