//
// BaseTiffReader.java
//
/*
LOCI Bio-Formats package for reading and converting biological file formats.
Copyright (C) 2005-@year@ Melissa Linkert, Curtis Rueden, Chris Allan,
Eric Kjellman and Brian Loranger.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Library 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 Library General Public License for more details.
You should have received a copy of the GNU Library 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.in;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
import java.text.*;
import java.util.*;
import loci.formats.*;
/**
* BaseTiffReader is the superclass for file format readers compatible with
* or derived from the TIFF 6.0 file format.
*
* <dl><dt><b>Source code:</b></dt>
* <dd><a href="https://skyking.microscopy.wisc.edu/trac/java/browser/trunk/loci/formats/in/BaseTiffReader.java">Trac</a>,
* <a href="https://skyking.microscopy.wisc.edu/svn/java/trunk/loci/formats/in/BaseTiffReader.java">SVN</a></dd></dl>
*
* @author Curtis Rueden ctrueden at wisc.edu
* @author Melissa Linkert linkert at wisc.edu
*/
public abstract class BaseTiffReader extends FormatReader {
// -- Fields --
/** List of IFDs for the current TIFF. */
protected Hashtable[] ifds;
// -- Constructors --
/** Constructs a new BaseTiffReader. */
public BaseTiffReader(String name, String suffix) { super(name, suffix); }
/** Constructs a new BaseTiffReader. */
public BaseTiffReader(String name, String[] suffixes) {
super(name, suffixes);
}
// -- BaseTiffReader API methods --
/** Gets the dimensions of the given (possibly multi-page) TIFF file. */
public int[] getTiffDimensions() throws FormatException, IOException {
if (ifds == null || ifds.length == 0) return null;
return new int[] {
TiffTools.getIFDIntValue(ifds[0], TiffTools.IMAGE_WIDTH, false, -1),
TiffTools.getIFDIntValue(ifds[0], TiffTools.IMAGE_LENGTH, false, -1),
core.imageCount[0]
};
}
// -- IFormatReader API methods --
/* @see loci.formats.IFormatReader#isThisType(byte[]) */
public boolean isThisType(byte[] block) {
return TiffTools.isValidHeader(block);
}
/* @see loci.formats.IFormatReader#get8BitLookupTable() */
public byte[][] get8BitLookupTable() throws FormatException, IOException {
FormatTools.assertId(currentId, true, 1);
int[] bits = TiffTools.getBitsPerSample(ifds[0]);
if (bits[0] <= 8) {
int[] colorMap =
(int[]) TiffTools.getIFDValue(ifds[0], TiffTools.COLOR_MAP);
if (colorMap == null) return null;
byte[][] table = new byte[3][colorMap.length / 3];
int next = 0;
for (int j=0; j<table.length; j++) {
for (int i=0; i<table[0].length; i++) {
if (isLittleEndian()) {
int n = colorMap[next++];
if ((n & 0xffff) > 255) table[j][i] = (byte) ((n & 0xff00) >> 8);
else table[j][i] = (byte) (n & 0xff);
}
else table[j][i] = (byte) ((colorMap[next++] & 0xff00) >> 8);
}
}
return table;
}
return null;
}
/* @see loci.formats.IFormatReader#get16BitLookupTable() */
public short[][] get16BitLookupTable() throws FormatException, IOException {
FormatTools.assertId(currentId, true, 1);
int[] bits = TiffTools.getBitsPerSample(ifds[0]);
if (bits[0] <= 16 && bits[0] > 8) {
int[] colorMap =
(int[]) TiffTools.getIFDValue(ifds[0], TiffTools.COLOR_MAP);
if (colorMap == null) return null;
short[][] table = new short[3][colorMap.length / 3];
int next = 0;
for (int i=0; i<table.length; i++) {
for (int j=0; j<table[0].length; j++) {
if (core.littleEndian[0]) {
table[i][j] = (short) (colorMap[next++] & 0xffff);
}
else {
int n = colorMap[next++];
table[i][j] =
(short) (((n & 0xff0000) >> 8) | ((n & 0xff000000) >> 24));
}
}
}
return table;
}
return null;
}
/* @see loci.formats.IFormatReader#getMetadataValue(String) */
public Object getMetadataValue(String field) {
FormatTools.assertId(currentId, true, 1);
return getMeta(field);
}
/* @see loci.formats.FormatReader#openBytes(int, byte[]) */
public byte[] openBytes(int no, byte[] buf)
throws FormatException, IOException
{
FormatTools.assertId(currentId, true, 1);
FormatTools.checkPlaneNumber(this, no);
FormatTools.checkBufferSize(this, buf.length);
TiffTools.getSamples(ifds[no], in, buf);
return swapIfRequired(buf);
}
// -- Internal BaseTiffReader API methods --
/** Populates the metadata hashtable and metadata store. */
protected void initMetadata() throws FormatException, IOException {
initStandardMetadata();
initMetadataStore();
}
/**
* Parses standard metadata.
*
* NOTE: Absolutely <b>no</b> calls to the metadata store should be made in
* this method or methods that override this method. Data <b>will</b> be
* overwritten if you do so.
*/
protected void initStandardMetadata() throws FormatException, IOException {
Hashtable ifd = ifds[0];
put("ImageWidth", ifd, TiffTools.IMAGE_WIDTH);
put("ImageLength", ifd, TiffTools.IMAGE_LENGTH);
put("BitsPerSample", ifd, TiffTools.BITS_PER_SAMPLE);
int comp = TiffTools.getIFDIntValue(ifd, TiffTools.COMPRESSION);
String compression = null;
switch (comp) {
case TiffTools.UNCOMPRESSED:
compression = "None";
break;
case TiffTools.CCITT_1D:
compression = "CCITT Group 3 1-Dimensional Modified Huffman";
break;
case TiffTools.GROUP_3_FAX:
compression = "CCITT T.4 bilevel encoding";
break;
case TiffTools.GROUP_4_FAX:
compression = "CCITT T.6 bilevel encoding";
break;
case TiffTools.LZW:
compression = "LZW";
break;
case TiffTools.JPEG:
compression = "JPEG";
break;
case TiffTools.PACK_BITS:
compression = "PackBits";
break;
}
put("Compression", compression);
int photo = TiffTools.getIFDIntValue(ifd,
TiffTools.PHOTOMETRIC_INTERPRETATION);
String photoInterp = null;
String metaDataPhotoInterp = null;
switch (photo) {
case TiffTools.WHITE_IS_ZERO:
photoInterp = "WhiteIsZero";
metaDataPhotoInterp = "Monochrome";
break;
case TiffTools.BLACK_IS_ZERO:
photoInterp = "BlackIsZero";
metaDataPhotoInterp = "Monochrome";
break;
case TiffTools.RGB:
photoInterp = "RGB";
metaDataPhotoInterp = "RGB";
break;
case TiffTools.RGB_PALETTE:
photoInterp = "Palette";
metaDataPhotoInterp = "Monochrome";
break;
case TiffTools.TRANSPARENCY_MASK:
photoInterp = "Transparency Mask";
metaDataPhotoInterp = "RGB";
break;
case TiffTools.CMYK:
photoInterp = "CMYK";
metaDataPhotoInterp = "CMYK";
break;
case TiffTools.Y_CB_CR:
photoInterp = "YCbCr";
metaDataPhotoInterp = "RGB";
break;
case TiffTools.CIE_LAB:
photoInterp = "CIELAB";
metaDataPhotoInterp = "RGB";
break;
case TiffTools.CFA_ARRAY:
photoInterp = "Color Filter Array";
metaDataPhotoInterp = "RGB";
break;
}
put("PhotometricInterpretation", photoInterp);
put("MetaDataPhotometricInterpretation", metaDataPhotoInterp);
putInt("CellWidth", ifd, TiffTools.CELL_WIDTH);
putInt("CellLength", ifd, TiffTools.CELL_LENGTH);
int or = TiffTools.getIFDIntValue(ifd, TiffTools.ORIENTATION);
// adjust the width and height if necessary
if (or == 8) {
put("ImageWidth", ifd, TiffTools.IMAGE_LENGTH);
put("ImageLength", ifd, TiffTools.IMAGE_WIDTH);
}
String orientation = null;
// there is no case 0
switch (or) {
case 1:
orientation = "1st row -> top; 1st column -> left";
break;
case 2:
orientation = "1st row -> top; 1st column -> right";
break;
case 3:
orientation = "1st row -> bottom; 1st column -> right";
break;
case 4:
orientation = "1st row -> bottom; 1st column -> left";
break;
case 5:
orientation = "1st row -> left; 1st column -> top";
break;
case 6:
orientation = "1st row -> right; 1st column -> top";
break;
case 7:
orientation = "1st row -> right; 1st column -> bottom";
break;
case 8:
orientation = "1st row -> left; 1st column -> bottom";
break;
}
put("Orientation", orientation);
putInt("SamplesPerPixel", ifd, TiffTools.SAMPLES_PER_PIXEL);
put("Software", ifd, TiffTools.SOFTWARE);
put("Instrument Make", ifd, TiffTools.MAKE);
put("Instrument Model", ifd, TiffTools.MODEL);
put("Document Name", ifd, TiffTools.DOCUMENT_NAME);
put("DateTime", ifd, TiffTools.DATE_TIME);
put("Artist", ifd, TiffTools.ARTIST);
put("HostComputer", ifd, TiffTools.HOST_COMPUTER);
put("Copyright", ifd, TiffTools.COPYRIGHT);
put("NewSubfileType", ifd, TiffTools.NEW_SUBFILE_TYPE);
int thresh = TiffTools.getIFDIntValue(ifd, TiffTools.THRESHHOLDING);
String threshholding = null;
switch (thresh) {
case 1:
threshholding = "No dithering or halftoning";
break;
case 2:
threshholding = "Ordered dithering or halftoning";
break;
case 3:
threshholding = "Randomized error diffusion";
break;
}
put("Threshholding", threshholding);
int fill = TiffTools.getIFDIntValue(ifd, TiffTools.FILL_ORDER);
String fillOrder = null;
switch (fill) {
case 1:
fillOrder = "Pixels with lower column values are stored " +
"in the higher order bits of a byte";
break;
case 2:
fillOrder = "Pixels with lower column values are stored " +
"in the lower order bits of a byte";
break;
}
put("FillOrder", fillOrder);
putInt("Make", ifd, TiffTools.MAKE);
putInt("Model", ifd, TiffTools.MODEL);
putInt("MinSampleValue", ifd, TiffTools.MIN_SAMPLE_VALUE);
putInt("MaxSampleValue", ifd, TiffTools.MAX_SAMPLE_VALUE);
putInt("XResolution", ifd, TiffTools.X_RESOLUTION);
putInt("YResolution", ifd, TiffTools.Y_RESOLUTION);
int planar = TiffTools.getIFDIntValue(ifd,
TiffTools.PLANAR_CONFIGURATION);
String planarConfig = null;
switch (planar) {
case 1:
planarConfig = "Chunky";
break;
case 2:
planarConfig = "Planar";
break;
}
put("PlanarConfiguration", planarConfig);
putInt("XPosition", ifd, TiffTools.X_POSITION);
putInt("YPosition", ifd, TiffTools.Y_POSITION);
putInt("FreeOffsets", ifd, TiffTools.FREE_OFFSETS);
putInt("FreeByteCounts", ifd, TiffTools.FREE_BYTE_COUNTS);
putInt("GrayResponseUnit", ifd, TiffTools.GRAY_RESPONSE_UNIT);
putInt("GrayResponseCurve", ifd, TiffTools.GRAY_RESPONSE_CURVE);
putInt("T4Options", ifd, TiffTools.T4_OPTIONS);
putInt("T6Options", ifd, TiffTools.T6_OPTIONS);
int res = TiffTools.getIFDIntValue(ifd, TiffTools.RESOLUTION_UNIT);
String resUnit = null;
switch (res) {
case 1:
resUnit = "None";
break;
case 2:
resUnit = "Inch";
break;
case 3:
resUnit = "Centimeter";
break;
}
put("ResolutionUnit", resUnit);
putInt("PageNumber", ifd, TiffTools.PAGE_NUMBER);
putInt("TransferFunction", ifd, TiffTools.TRANSFER_FUNCTION);
int predict = TiffTools.getIFDIntValue(ifd, TiffTools.PREDICTOR);
String predictor = null;
switch (predict) {
case 1:
predictor = "No prediction scheme";
break;
case 2:
predictor = "Horizontal differencing";
break;
}
put("Predictor", predictor);
putInt("WhitePoint", ifd, TiffTools.WHITE_POINT);
putInt("PrimaryChromacities", ifd, TiffTools.PRIMARY_CHROMATICITIES);
putInt("HalftoneHints", ifd, TiffTools.HALFTONE_HINTS);
putInt("TileWidth", ifd, TiffTools.TILE_WIDTH);
putInt("TileLength", ifd, TiffTools.TILE_LENGTH);
putInt("TileOffsets", ifd, TiffTools.TILE_OFFSETS);
putInt("TileByteCounts", ifd, TiffTools.TILE_BYTE_COUNTS);
int ink = TiffTools.getIFDIntValue(ifd, TiffTools.INK_SET);
String inkSet = null;
switch (ink) {
case 1:
inkSet = "CMYK";
break;
case 2:
inkSet = "Other";
break;
}
put("InkSet", inkSet);
putInt("InkNames", ifd, TiffTools.INK_NAMES);
putInt("NumberOfInks", ifd, TiffTools.NUMBER_OF_INKS);
putInt("DotRange", ifd, TiffTools.DOT_RANGE);
put("TargetPrinter", ifd, TiffTools.TARGET_PRINTER);
putInt("ExtraSamples", ifd, TiffTools.EXTRA_SAMPLES);
int fmt = TiffTools.getIFDIntValue(ifd, TiffTools.SAMPLE_FORMAT);
String sampleFormat = null;
switch (fmt) {
case 1:
sampleFormat = "unsigned integer";
break;
case 2:
sampleFormat = "two's complement signed integer";
break;
case 3:
sampleFormat = "IEEE floating point";
break;
case 4:
sampleFormat = "undefined";
break;
}
put("SampleFormat", sampleFormat);
putInt("SMinSampleValue", ifd, TiffTools.S_MIN_SAMPLE_VALUE);
putInt("SMaxSampleValue", ifd, TiffTools.S_MAX_SAMPLE_VALUE);
putInt("TransferRange", ifd, TiffTools.TRANSFER_RANGE);
int jpeg = TiffTools.getIFDIntValue(ifd, TiffTools.JPEG_PROC);
String jpegProc = null;
switch (jpeg) {
case 1:
jpegProc = "baseline sequential process";
break;
case 14:
jpegProc = "lossless process with Huffman coding";
break;
}
put("JPEGProc", jpegProc);
putInt("JPEGInterchangeFormat", ifd, TiffTools.JPEG_INTERCHANGE_FORMAT);
putInt("JPEGRestartInterval", ifd, TiffTools.JPEG_RESTART_INTERVAL);
putInt("JPEGLosslessPredictors",
ifd, TiffTools.JPEG_LOSSLESS_PREDICTORS);
putInt("JPEGPointTransforms", ifd, TiffTools.JPEG_POINT_TRANSFORMS);
putInt("JPEGQTables", ifd, TiffTools.JPEG_Q_TABLES);
putInt("JPEGDCTables", ifd, TiffTools.JPEG_DC_TABLES);
putInt("JPEGACTables", ifd, TiffTools.JPEG_AC_TABLES);
putInt("YCbCrCoefficients", ifd, TiffTools.Y_CB_CR_COEFFICIENTS);
int ycbcr = TiffTools.getIFDIntValue(ifd,
TiffTools.Y_CB_CR_SUB_SAMPLING);
String subSampling = null;
switch (ycbcr) {
case 1:
subSampling = "chroma image dimensions = luma image dimensions";
break;
case 2:
subSampling = "chroma image dimensions are " +
"half the luma image dimensions";
break;
case 4:
subSampling = "chroma image dimensions are " +
"1/4 the luma image dimensions";
break;
}
put("YCbCrSubSampling", subSampling);
putInt("YCbCrPositioning", ifd, TiffTools.Y_CB_CR_POSITIONING);
putInt("ReferenceBlackWhite", ifd, TiffTools.REFERENCE_BLACK_WHITE);
// bits per sample and number of channels
Object bpsObj = TiffTools.getIFDValue(ifd, TiffTools.BITS_PER_SAMPLE);
int bps = -1, numC = 3;
if (bpsObj instanceof int[]) {
int[] q = (int[]) bpsObj;
bps = q[0];
numC = q.length;
}
else if (bpsObj instanceof Number) {
bps = ((Number) bpsObj).intValue();
numC = 1;
}
// numC isn't set properly if we have an indexed color image, so we need
// to reset it here
int p = TiffTools.getIFDIntValue(ifd, TiffTools.PHOTOMETRIC_INTERPRETATION);
if (p == TiffTools.RGB_PALETTE || p == TiffTools.CFA_ARRAY) {
numC = 3;
bps *= 3;
}
put("BitsPerSample", bps);
put("NumberOfChannels", numC);
// TIFF comment
String comment = null;
Object o = TiffTools.getIFDValue(ifd, TiffTools.IMAGE_DESCRIPTION);
if (o instanceof String) comment = (String) o;
else if (o instanceof String[]) {
String[] s = (String[]) o;
if (s.length > 0) comment = s[0];
}
else if (o != null) comment = o.toString();
if (comment != null) {
// sanitize comment
comment = comment.replaceAll("\r\n", "\n"); // CR-LF to LF
comment = comment.replaceAll("\r", "\n"); // CR to LF
put("Comment", comment);
}
int samples = TiffTools.getIFDIntValue(ifds[0],
TiffTools.SAMPLES_PER_PIXEL, false, 1);
core.rgb[0] = samples > 1 || p == TiffTools.RGB_PALETTE ||
p == TiffTools.CFA_ARRAY || p == TiffTools.RGB;
core.interleaved[0] = false;
core.littleEndian[0] = TiffTools.isLittleEndian(ifds[0]);
core.sizeX[0] =
TiffTools.getIFDIntValue(ifds[0], TiffTools.IMAGE_WIDTH, false, 0);
core.sizeY[0] =
TiffTools.getIFDIntValue(ifds[0], TiffTools.IMAGE_LENGTH, false, 0);
core.sizeZ[0] = 1;
core.sizeC[0] = core.rgb[0] ? samples : 1;
core.sizeT[0] = ifds.length;
core.metadataComplete[0] = true;
core.indexed[0] = TiffTools.getIFDIntValue(ifds[0],
TiffTools.PHOTOMETRIC_INTERPRETATION) == TiffTools.RGB_PALETTE;
core.falseColor[0] = false;
int bitFormat = TiffTools.getIFDIntValue(ifds[0],
TiffTools.SAMPLE_FORMAT);
while (bps % 8 != 0) bps++;
if (bps == 24 || bps == 48) bps /= 3;
if (bitFormat == 3) core.pixelType[0] = FormatTools.FLOAT;
else if (bitFormat == 2) {
switch (bps) {
case 16:
core.pixelType[0] = FormatTools.INT16;
break;
case 32:
core.pixelType[0] = FormatTools.INT32;
break;
default:
core.pixelType[0] = FormatTools.UINT8;
}
}
else {
switch (bps) {
case 16:
core.pixelType[0] = FormatTools.UINT16;
break;
case 32:
core.pixelType[0] = FormatTools.UINT32;
break;
default:
core.pixelType[0] = FormatTools.UINT8;
}
}
core.currentOrder[0] = "XYCZT";
}
/**
* Populates the metadata store using the data parsed in
* {@link #initStandardMetadata()} along with some further parsing done in
* the method itself.
*
* All calls to the active <code>MetadataStore</code> should be made in this
* method and <b>only</b> in this method. This is especially important for
* sub-classes that override the getters for pixel set array size, etc.
*/
protected void initMetadataStore() {
Hashtable ifd = ifds[0];
try {
// the metadata store we're working with
MetadataStore store = getMetadataStore();
// set the pixel values in the metadata store
FormatTools.populatePixels(store, this);
// populate Experimenter element
String artist = (String) TiffTools.getIFDValue(ifd, TiffTools.ARTIST);
if (artist != null) {
String firstName = null, lastName = null;
int ndx = artist.indexOf(" ");
if (ndx < 0) lastName = artist;
else {
firstName = artist.substring(0, ndx);
lastName = artist.substring(ndx + 1);
}
String email = (String)
TiffTools.getIFDValue(ifd, TiffTools.HOST_COMPUTER);
store.setExperimenter(firstName, lastName, email,
null, null, null, null);
}
// format the creation date to ISO 8061
String creationDate = getImageCreationDate();
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
SimpleDateFormat parse = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
Date date = parse.parse(creationDate, new ParsePosition(0));
creationDate = sdf.format(date);
}
catch (NullPointerException e) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
SimpleDateFormat parse =
new SimpleDateFormat("dd/MM/yyyy HH:mm:ss.SS");
Date date = parse.parse(creationDate, new ParsePosition(0));
creationDate = sdf.format(date);
}
catch (NullPointerException exc) {
if (debug) trace(exc);
creationDate = null;
}
}
// populate Image element
store.setImage(getImageName(),
creationDate, getImageDescription(), null);
// populate Logical Channel elements
for (int i=0; i<getSizeC(); i++) {
try {
setLogicalChannel(i);
}
catch (FormatException exc) {
if (debug) trace(exc);
}
catch (IOException exc) {
if (debug) trace(exc);
}
}
// set the X and Y pixel dimensions
int resolutionUnit = TiffTools.getIFDIntValue(ifd,
TiffTools.RESOLUTION_UNIT);
TiffRational xResolution = TiffTools.getIFDRationalValue(ifd,
TiffTools.X_RESOLUTION, false);
TiffRational yResolution = TiffTools.getIFDRationalValue(ifd,
TiffTools.Y_RESOLUTION, false);
float pixX = xResolution == null ? 0f : xResolution.floatValue();
float pixY = yResolution == null ? 0f : yResolution.floatValue();
switch (resolutionUnit) {
case 2:
// resolution is expressed in pixels per inch
pixX *= 0.0254;
pixY *= 0.0254;
break;
case 3:
// resolution is expressed in pixels per centimeter
pixX /= 100;
pixY /= 100;
break;
}
store.setDimensions(new Float(pixX), new Float(pixY), null,
null, null, null);
// populate StageLabel element
Object x = TiffTools.getIFDValue(ifd, TiffTools.X_POSITION);
Object y = TiffTools.getIFDValue(ifd, TiffTools.Y_POSITION);
Float stageX;
Float stageY;
if (x instanceof TiffRational) {
stageX = x == null ? null : new Float(((TiffRational) x).floatValue());
stageY = y == null ? null : new Float(((TiffRational) y).floatValue());
}
else {
stageX = x == null ? null : new Float((String) x);
stageY = y == null ? null : new Float((String) y);
}
if (stageX != null || stageY != null) {
store.setStageLabel(null, stageX, stageY, null, null);
}
// populate Instrument element
String model = (String) TiffTools.getIFDValue(ifd, TiffTools.MODEL);
String serialNumber = (String)
TiffTools.getIFDValue(ifd, TiffTools.MAKE);
store.setInstrument(null, model, serialNumber, null, null);
}
catch (FormatException exc) { trace(exc); }
}
/**
* Retrieves the image name from the TIFF.
* @return the image name.
*/
protected String getImageName() { return currentId; }
/**
* Retrieves the image creation date.
* @return the image creation date.
*/
protected String getImageCreationDate() {
Object o = TiffTools.getIFDValue(ifds[0], TiffTools.DATE_TIME);
if (o instanceof String) return (String) o;
if (o instanceof String[]) return ((String[]) o)[0];
return null;
}
/**
* Retrieves the image description.
* @return the image description.
*/
protected String getImageDescription() {
return (String) getMeta("Comment");
}
/**
* Examines a byte array to see if it needs to be byte swapped and modifies
* the byte array directly.
* @param byteArray The byte array to check and modify if required.
* @return the <i>byteArray</i> either swapped or not for convenience.
* @throws IOException if there is an error read from the file.
* @throws FormatException if there is an error during metadata parsing.
*/
protected byte[] swapIfRequired(byte[] byteArray)
throws FormatException, IOException
{
int bitsPerSample = TiffTools.getBitsPerSample(ifds[0])[0];
// We've got nothing to do if the samples are only 8-bits wide or if they
// are floating point.
if (bitsPerSample == 8 || bitsPerSample == 32) return byteArray;
if (isLittleEndian()) {
if (bitsPerSample == 16) { // short
ShortBuffer buf = ByteBuffer.wrap(byteArray).asShortBuffer();
for (int i = 0; i < (byteArray.length / 2); i++) {
buf.put(i, DataTools.swap(buf.get(i)));
}
}
else {
throw new FormatException(
"Unsupported sample bit width: '" + bitsPerSample + "'");
}
}
// We've got a big-endian file with a big-endian byte array.
return byteArray;
}
// -- Internal FormatReader API methods - metadata convenience --
protected void put(String key, Object value) {
if (value == null) return;
if (value instanceof String) value = ((String) value).trim();
addMeta(key, value);
}
protected void put(String key, int value) {
if (value == -1) return; // indicates missing value
addMeta(key, new Integer(value));
}
protected void put(String key, boolean value) {
put(key, new Boolean(value));
}
protected void put(String key, byte value) { put(key, new Byte(value)); }
protected void put(String key, char value) { put(key, new Character(value)); }
protected void put(String key, double value) { put(key, new Double(value)); }
protected void put(String key, float value) { put(key, new Float(value)); }
protected void put(String key, long value) { put(key, new Long(value)); }
protected void put(String key, short value) { put(key, new Short(value)); }
protected void put(String key, Hashtable ifd, int tag) {
put(key, TiffTools.getIFDValue(ifd, tag));
}
protected void putInt(String key, Hashtable ifd, int tag) {
put(key, TiffTools.getIFDIntValue(ifd, tag));
}
// -- Internal FormatReader API methods --
/* @see loci.formats.FormatReader#initFile(String) */
protected void initFile(String id) throws FormatException, IOException {
if (debug) debug("BaseTiffReader.initFile(" + id + ")");
super.initFile(id);
in = new RandomAccessStream(id);
in.order(in.readShort() == 0x4949);
status("Reading IFDs");
ifds = TiffTools.getIFDs(in);
if (ifds == null) throw new FormatException("No IFDs found");
status("Populating metadata");
core.imageCount[0] = ifds.length;
initMetadata();
}
// -- Helper methods --
/**
* Sets the logical channel in the metadata store.
* @param i the logical channel number.
* @throws FormatException if there is an error parsing metadata.
* @throws IOException if there is an error reading the file.
*/
private void setLogicalChannel(int i) throws FormatException, IOException {
getMetadataStore().setLogicalChannel(i, getChannelName(i), null, null, null,
null, null, null, null, null, null, null, null,
getPhotometricInterpretation(i), getMode(i), null, null, null, null, null,
getEmWave(i), getExWave(i), null, getNdFilter(i), null);
}
private String getChannelName(int i) { return null; }
private Float getNdFilter(int i) { return null; }
private Integer getEmWave(int i) { return null; }
private Integer getExWave(int i) { return null; }
private String getPhotometricInterpretation(int i)
throws FormatException, IOException
{
return (String) getMetadataValue("metaDataPhotometricInterpretation");
}
private String getMode(int i) { return null; }
}