/*
* #%L
* BSD implementations of Bio-Formats readers and writers
* %%
* Copyright (C) 2005 - 2015 Open Microscopy Environment:
* - Board of Regents of the University of Wisconsin-Madison
* - Glencoe Software, Inc.
* - University of Dundee
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package loci.formats;
import java.io.IOException;
import loci.common.DataTools;
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.
*/
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#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 = DataTools.allocate(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() */
@Override
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.populatePixelsOnly(store, this);
}
// -- 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
}
}