//
// ND2Reader.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.awt.image.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ServiceRegistry;
import javax.imageio.stream.MemoryCacheImageInputStream;
import javax.xml.parsers.*;
import loci.formats.*;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* ND2Reader is the file format reader for Nikon ND2 files.
* The JAI ImageIO library is required to use this reader; it is available from
* http://jai-imageio.dev.java.net. Note that JAI ImageIO is bundled with a
* version of the JJ2000 library, so it is important that either:
* (1) the JJ2000 jar file is *not* in the classpath; or
* (2) the JAI jar file precedes JJ2000 in the classpath.
*
* <dl><dt><b>Source code:</b></dt>
* <dd><a href="https://skyking.microscopy.wisc.edu/trac/java/browser/trunk/loci/formats/in/ND2Reader.java">Trac</a>,
* <a href="https://skyking.microscopy.wisc.edu/svn/java/trunk/loci/formats/in/ND2Reader.java">SVN</a></dd></dl>
*/
public class ND2Reader extends FormatReader {
// -- Constants --
private static final String NO_J2K_MSG =
"The JAI Image I/O Tools are required to read ND2 files. Please " +
"obtain jai_imageio.jar from http://loci.wisc.edu/ome/formats.html";
private static final String J2K_READER =
"com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageReader";
/** Factory for generating SAX parsers. */
public static final SAXParserFactory SAX_FACTORY =
SAXParserFactory.newInstance();
// -- Static fields --
private static boolean noJ2k = false;
private static ReflectedUniverse r = createReflectedUniverse();
private static ReflectedUniverse createReflectedUniverse() {
// NB: ImageJ does not access the jai_imageio classes with the normal
// class loading scheme, and thus the necessary service provider stuff is
// not automatically registered. Instead, we register the J2KImageReader
// with the IIORegistry manually, merely so that we can obtain a
// J2KImageReaderSpi object from the IIORegistry's service provider
// lookup function, then use it to construct a J2KImageReader object
// directly, which we can use to process ND2 files one plane at a time.
ReflectedUniverse ru = null;
try {
// register J2KImageReader with IIORegistry
String j2kReaderSpi = J2K_READER + "Spi";
Class j2kSpiClass = null;
try {
j2kSpiClass = Class.forName(j2kReaderSpi);
}
catch (ClassNotFoundException exc) {
if (debug) LogTools.trace(exc);
noJ2k = true;
}
catch (NoClassDefFoundError err) {
if (debug) LogTools.trace(err);
noJ2k = true;
}
catch (RuntimeException exc) {
// HACK: workaround for bug in Apache Axis2
String msg = exc.getMessage();
if (msg != null && msg.indexOf("ClassNotFound") < 0) throw exc;
if (debug) LogTools.trace(exc);
noJ2k = true;
}
IIORegistry registry = IIORegistry.getDefaultInstance();
if (j2kSpiClass != null) {
Iterator providers = ServiceRegistry.lookupProviders(j2kSpiClass);
registry.registerServiceProviders(providers);
}
// obtain J2KImageReaderSpi instance from IIORegistry
Object j2kSpi = registry.getServiceProviderByClass(j2kSpiClass);
ru = new ReflectedUniverse();
// for computing offsets in initFile
ru.exec("import jj2000.j2k.fileformat.reader.FileFormatReader");
ru.exec("import jj2000.j2k.io.BEBufferedRandomAccessFile");
ru.exec("import jj2000.j2k.util.ISRandomAccessIO");
// for reading pixel data in openImage
ru.exec("import " + J2K_READER);
ru.setVar("j2kSpi", j2kSpi);
ru.exec("j2kReader = new J2KImageReader(j2kSpi)");
}
catch (Throwable t) {
noJ2k = true;
if (debug) LogTools.trace(t);
}
return ru;
}
// -- Fields --
/** Array of image offsets. */
private long[] offsets;
/** Whether or not the pixel data is compressed using JPEG 2000. */
private boolean isJPEG;
/** Whether or not the pixel data is losslessly compressed. */
private boolean isLossless;
private boolean adjustImageCount;
private Vector zs = new Vector();
private Vector ts = new Vector();
// -- Constructor --
/** Constructs a new ND2 reader. */
public ND2Reader() { super("Nikon ND2", new String[] {"nd2", "jp2"}); }
// -- IFormatReader API methods --
/* @see loci.formats.IFormatReader#isThisType(byte[]) */
public boolean isThisType(byte[] block) {
if (block.length < 8) return false;
return block[4] == 0x6a && block[5] == 0x50 && block[6] == 0x20 &&
block[7] == 0x20;
}
/* @see loci.formats.IFormatReader#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);
in.seek(offsets[no]);
if (isJPEG) {
BufferedImage b = openImage(no);
byte[][] pixels = ImageTools.getPixelBytes(b, false);
if (pixels.length == 1 && core.sizeC[0] > 1) {
pixels = ImageTools.splitChannels(pixels[0], core.sizeC[0],
FormatTools.getBytesPerPixel(core.pixelType[0]), false,
!core.interleaved[0]);
}
for (int i=0; i<core.sizeC[0]; i++) {
System.arraycopy(pixels[i], 0, buf, i*pixels[i].length,
pixels[i].length);
}
pixels = null;
}
else if (isLossless) {
byte[] b = new byte[buf.length];
in.read(b);
if ((core.sizeX[0] % 2) != 0) {
buf = new byte[(core.sizeX[0] + 1) * core.sizeY[0] *
getRGBChannelCount() *
FormatTools.getBytesPerPixel(core.pixelType[0])];
}
Inflater decompresser = new Inflater();
decompresser.setInput(b);
try { decompresser.inflate(buf); }
catch (DataFormatException e) { throw new FormatException(e); }
decompresser.end();
if ((core.sizeX[0] % 2) != 0) {
byte[] tmp = buf;
buf = new byte[core.sizeX[0] * core.sizeY[0] * getRGBChannelCount() *
FormatTools.getBytesPerPixel(core.pixelType[0])];
int row = core.sizeX[0] * getRGBChannelCount() *
FormatTools.getBytesPerPixel(core.pixelType[0]);
int padRow = (core.sizeX[0] + 1) * getRGBChannelCount() *
FormatTools.getBytesPerPixel(core.pixelType[0]);
for (int i=0; i<core.sizeY[0]; i++) {
System.arraycopy(tmp, padRow * i, buf, row * i, row);
}
}
}
else in.readFully(buf);
return buf;
}
/* @see loci.formats.IFormatReader#openImage(int) */
public BufferedImage openImage(int no) throws FormatException, IOException {
if (!isJPEG) {
return ImageTools.makeImage(openBytes(no), core.sizeX[0], core.sizeY[0],
core.sizeC[0], core.interleaved[0],
FormatTools.getBytesPerPixel(core.pixelType[0]), core.littleEndian[0]);
}
FormatTools.assertId(currentId, true, 1);
FormatTools.checkPlaneNumber(this, no);
in.seek(offsets[no]);
long len = no < core.imageCount[0] - 1 ? offsets[no + 1] - offsets[no] :
in.length() - offsets[no];
byte[] b = new byte[(int) len];
in.readFully(b);
ByteArrayInputStream bis = new ByteArrayInputStream(b);
// NB: Even after registering J2KImageReader with
// IIORegistry manually, the following still does not work:
//BufferedImage img = ImageIO.read(bis);
MemoryCacheImageInputStream mciis = new MemoryCacheImageInputStream(bis);
BufferedImage img = null;
try {
r.setVar("mciis", mciis);
r.exec("j2kReader.setInput(mciis)");
r.setVar("zero", 0);
r.setVar("param", null);
img = (BufferedImage) r.exec("j2kReader.read(zero, param)");
}
catch (ReflectException exc) {
throw new FormatException(exc);
}
bis.close();
mciis.close();
b = null;
return img;
}
/* @see loci.formats.IFormatReader#close() */
public void close() throws IOException {
super.close();
offsets = null;
zs.clear();
ts.clear();
adjustImageCount = false;
isJPEG = false;
isLossless = false;
}
// -- Internal FormatReader API methods --
/* @see loci.formats.FormatReader#initFile(String) */
protected void initFile(String id) throws FormatException, IOException {
if (debug) debug("ND2Reader.initFile(" + id + ")");
if (noJ2k) throw new FormatException(NO_J2K_MSG);
super.initFile(id);
in = new RandomAccessStream(id);
if (in.read() == -38 && in.read() == -50) {
// newer version of ND2 - doesn't use JPEG2000
isJPEG = false;
in.seek(0);
in.order(true);
byte[] b = new byte[1024 * 1024];
while (in.getFilePointer() < in.length()) {
if (in.read() == -38 && in.read() == -50 && in.read() == -66 &&
in.read() == 10)
{
// found a data chunk
int len = in.readInt() + in.readInt();
if (len > b.length) {
// make sure size at least doubles, for efficiency
int size = b.length + b.length;
if (size < len) size = len;
b = new byte[size];
}
in.skipBytes(4);
if (debug) {
debug("Reading chunk of size " + len +
" at position " + in.getFilePointer());
}
in.readFully(b, 0, len);
if (len >= 12 && b[0] == 'I' && b[1] == 'm' && b[2] == 'a' &&
b[3] == 'g' && b[4] == 'e' && b[5] == 'D' && b[6] == 'a' &&
b[7] == 't' && b[8] == 'a' && b[9] == 'S' && b[10] == 'e' &&
b[11] == 'q') // b.startsWith("ImageDataSeq")
{
// found pixel data
StringBuffer sb = new StringBuffer();
int pt = 13;
while (b[pt] != '!') {
sb.append((char) b[pt]);
pt++;
}
int ndx = Integer.parseInt(sb.toString());
if (core.sizeC[0] == 0) {
core.sizeC[0] = len / (core.sizeX[0] * core.sizeY[0] *
FormatTools.getBytesPerPixel(core.pixelType[0]));
}
offsets[ndx] = in.getFilePointer() - len + sb.length() + 21;
}
else if (len >= 5 && b[0] == 'I' && b[1] == 'm' && b[2] == 'a' &&
b[3] == 'g' && b[4] == 'e') // b.startsWith("Image")
{
// XML metadata
ND2Handler handler = new ND2Handler();
// strip out invalid characters
int off = 0;
for (int i=0; i<len; i++) {
char c = (char) b[i];
if (off == 0 && c == '!') off = i + 1;
if (Character.isISOControl(c) || !Character.isDefined(c)) {
b[i] = (byte) ' ';
}
}
if (len - off >= 5 && b[off] == '<' && b[off + 1] == '?' &&
b[off + 2] == 'x' && b[off + 3] == 'm' &&
b[off + 4] == 'l') // b.substring(off, off + 5).equals("<?xml")
{
ByteArrayInputStream s =
new ByteArrayInputStream(b, off, len - off);
try {
SAXParser parser = SAX_FACTORY.newSAXParser();
parser.parse(s, handler);
}
catch (ParserConfigurationException exc) {
throw new FormatException(exc);
}
catch (SAXException exc) {
throw new FormatException(exc);
}
}
}
if (core.imageCount[0] > 0 && offsets == null) {
offsets = new long[core.imageCount[0]];
}
if (in.getFilePointer() < in.length() - 1) {
if (in.read() != -38) in.skipBytes(15);
else in.seek(in.getFilePointer() - 1);
}
}
}
if (isLossless) {
for (int i=0; i<offsets.length; i++) {
offsets[i]++;
}
}
if (core.sizeC[0] == 0) core.sizeC[0] = 1;
core.currentOrder[0] = "XYCZT";
core.rgb[0] = core.sizeC[0] > 1;
if (core.sizeC[0] > 1 && adjustImageCount) {
core.imageCount[0] /= 3;
core.sizeZ[0] /= 3;
}
core.littleEndian[0] = isLossless;
core.interleaved[0] = true;
core.indexed[0] = false;
core.falseColor[0] = false;
core.metadataComplete[0] = true;
MetadataStore store = getMetadataStore();
store.setImage(currentId, null, null, null);
FormatTools.populatePixels(store, this);
for (int i=0; i<core.sizeC[0]; i++) {
store.setLogicalChannel(i, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null);
}
return;
}
else in.seek(0);
isJPEG = true;
status("Calculating image offsets");
Vector vs = new Vector();
long pos = in.getFilePointer();
boolean lastBoxFound = false;
int length = 0;
int box = 0;
while (!lastBoxFound) {
pos = in.getFilePointer();
length = in.readInt();
if (pos + length >= in.length() || length == 0) lastBoxFound = true;
box = in.readInt();
pos = in.getFilePointer();
length -= 8;
if (box == 0x6a703263) {
vs.add(new Long(in.getFilePointer()));
}
if (!lastBoxFound) in.seek(pos + length);
}
offsets = new long[vs.size()];
for (int i=0; i<offsets.length; i++) {
offsets[i] = ((Long) vs.get(i)).longValue();
}
vs.clear();
vs = null;
status("Finding XML metadata");
core.imageCount[0] = offsets.length;
core.pixelType[0] = FormatTools.UINT8;
core.indexed[0] = false;
core.falseColor[0] = false;
// read XML metadata from the end of the file
in.seek(offsets[offsets.length - 1]);
boolean found = false;
long off = -1;
byte[] buf = new byte[2048];
while (!found && in.getFilePointer() < in.length()) {
int read = 0;
if (in.getFilePointer() == offsets[offsets.length - 1]) {
read = in.read(buf);
}
else {
System.arraycopy(buf, buf.length - 10, buf, 0, 10);
read = in.read(buf, 10, buf.length - 10);
}
if (read == buf.length) read -= 10;
for (int i=0; i<read+9; i++) {
if (buf[i] == (byte) 0xff && buf[i+1] == (byte) 0xd9) {
found = true;
off = in.getFilePointer() - (read + 10) + i;
i = buf.length;
break;
}
}
}
buf = null;
status("Parsing XML");
if (off > 0 && off < in.length() - 5) {
in.seek(off + 5);
byte[] b = new byte[(int) (in.length() - off - 5)];
in.readFully(b);
String xml = new String(b);
// assume that this XML string will be malformed, since that's how both
// sample files are; this means we need to manually parse it :-(
// strip out binary data at the end - this is irrelevant for our purposes
xml = xml.substring(0, xml.lastIndexOf("</MetadataSeq>") + 14);
// strip out all comments
xml = xml.replaceAll("<!--*-->", "");
// each chunk appears on a separate line, so split up the chunks
StringTokenizer st = new StringTokenizer(xml, "\r\n");
while (st.hasMoreTokens()) {
String token = st.nextToken().trim();
if (token.indexOf("<") != -1) {
String prefix = token.substring(1, token.indexOf(">")).trim();
token = token.substring(token.indexOf(">") + 1);
while (token.indexOf("<") != -1) {
int start = token.indexOf("<");
String s = token.substring(start + 1, token.indexOf(">", start));
token = token.substring(token.indexOf(">", start));
// get the prefix for this tag
if (s.indexOf(" ") != -1) {
String pre = s.substring(0, s.indexOf(" ")).trim();
s = s.substring(s.indexOf(" ") + 1);
// get key/value pairs
while (s.indexOf("=") != -1) {
int eq = s.indexOf("=");
String key = s.substring(0, eq).trim();
String value =
s.substring(eq + 2, s.indexOf("\"", eq + 2)).trim();
// strip out the data types
if (key.indexOf("runtype") == -1) {
if (prefix.startsWith("Metadata_V1.2")) {
prefix = "";
}
String effectiveKey = prefix + " " + pre + " " + key;
if (!metadata.containsKey(effectiveKey)) {
addMeta(effectiveKey, value);
if (effectiveKey.equals(
"MetadataSeq _SEQUENCE_INDEX=\"0\" uiCompCount value"))
{
if (value != null) {
core.sizeC[0] = Integer.parseInt(value);
}
}
else if (effectiveKey.endsWith("dTimeAbsolute value")) {
long v = (long) Double.parseDouble(value);
if (!ts.contains(new Long(v))) {
core.sizeT[0]++;
ts.add(new Long(v));
}
}
else if (effectiveKey.endsWith("dZPos value")) {
long v = (long) Double.parseDouble(value);
if (!zs.contains(new Long(v))) {
core.sizeZ[0]++;
zs.add(new Long(v));
}
}
}
else {
String v = (String) getMeta(effectiveKey);
boolean parse = v != null;
if (parse) {
for (int i=0; i<v.length(); i++) {
if (Character.isLetter(v.charAt(i)) ||
Character.isWhitespace(v.charAt(i)))
{
parse = false;
break;
}
}
}
if (parse) {
addMeta(effectiveKey, value);
}
}
}
s = s.substring(s.indexOf("\"", eq + 2) + 1);
}
}
}
}
}
b = null;
xml = null;
st = null;
}
status("Populating metadata");
BufferedImage img = openImage(0);
core.sizeX[0] = img.getWidth();
core.sizeY[0] = img.getHeight();
core.sizeC[0] = img.getRaster().getNumBands();
core.rgb[0] = core.sizeC[0] > 1;
core.pixelType[0] = ImageTools.getPixelType(img);
int numInvalid = 0;
for (int i=1; i<offsets.length; i++) {
if (offsets[i] - offsets[i - 1] < (core.sizeX[0] * core.sizeY[0] / 4)) {
offsets[i - 1] = 0;
numInvalid++;
}
}
long[] tempOffsets = new long[core.imageCount[0] - numInvalid];
int pt = 0;
for (int i=0; i<offsets.length; i++) {
if (offsets[i] != 0) {
tempOffsets[pt] = offsets[i];
pt++;
}
}
offsets = tempOffsets;
core.imageCount[0] = offsets.length;
String sigBits =
(String) getMeta("AdvancedImageAttributes SignificantBits value");
int bits = 0;
if (sigBits != null && sigBits.length() > 0) {
bits = Integer.parseInt(sigBits.trim());
}
// determine the pixel size
String pixX = (String)
getMeta("CalibrationSeq _SEQUENCE_INDEX=\"0\" dCalibration value");
String pixZ = (String)
getMeta("CalibrationSeq _SEQUENCE_INDEX=\"0\" dAspect value");
float pixSizeX = 0f;
float pixSizeZ = 0f;
if (pixX != null && pixX.length() > 0) {
pixSizeX = Float.parseFloat(pixX.trim());
}
if (pixZ != null && pixZ.length() > 0) {
pixSizeZ = Float.parseFloat(pixZ.trim());
}
core.currentOrder[0] = "XY";
long deltaT = ts.size() > 1 ?
((Long) ts.get(1)).longValue() - ((Long) ts.get(0)).longValue() : 1;
long deltaZ = zs.size() > 1 ?
((Long) zs.get(1)).longValue() - ((Long) zs.get(0)).longValue() : 1;
if (deltaT < deltaZ || deltaZ == 0) core.currentOrder[0] += "CTZ";
else core.currentOrder[0] += "CZT";
// we calculate this directly (instead of calling getEffectiveSizeC) because
// sizeZ and sizeT have not been accurately set yet
int effectiveC = ((core.sizeC[0] - 1) / 3) + 1;
if (core.imageCount[0] < core.sizeZ[0] * core.sizeT[0]) {
if (core.sizeT[0] == core.imageCount[0]) {
core.sizeT[0] /= core.sizeZ[0] * effectiveC;
while (core.imageCount[0] > core.sizeZ[0] * core.sizeT[0] * effectiveC)
{
core.sizeT[0]++;
}
}
else if (core.sizeZ[0] == core.imageCount[0]) {
core.sizeZ[0] /= core.sizeT[0] * effectiveC;
while (core.imageCount[0] > core.sizeZ[0] * core.sizeT[0] * effectiveC)
{
core.sizeZ[0]++;
}
}
if (core.imageCount[0] < core.sizeZ[0] * core.sizeT[0] * effectiveC) {
if (core.sizeZ[0] < core.sizeT[0]) {
core.sizeZ[0]--;
while (core.imageCount[0] >
core.sizeZ[0] * core.sizeT[0] * effectiveC)
{
core.sizeT[0]++;
}
while (core.imageCount[0] <
core.sizeZ[0] * core.sizeT[0] * effectiveC)
{
core.sizeT[0]--;
}
}
else {
core.sizeT[0]--;
while (core.imageCount[0] >
core.sizeZ[0] * core.sizeT[0] * effectiveC)
{
core.sizeZ[0]++;
}
if (core.imageCount[0] < core.sizeZ[0] * core.sizeT[0] * effectiveC) {
core.sizeZ[0]--;
}
}
while (core.imageCount[0] > core.sizeZ[0] * core.sizeT[0] * effectiveC)
{
core.imageCount[0]--;
}
}
}
if (bits != 0) {
int bpp = bits;
while (bpp % 8 != 0) bpp++;
switch (bpp) {
case 8:
core.pixelType[0] = FormatTools.UINT8;
break;
case 16:
core.pixelType[0] = FormatTools.UINT16;
break;
case 32:
core.pixelType[0] = FormatTools.UINT8;
core.sizeC[0] = 4;
break;
default:
throw new FormatException("Unsupported bits per pixel: " + bpp);
}
}
if (core.sizeZ[0] == 0) core.sizeZ[0] = 1;
if (core.sizeT[0] == 0) core.sizeT[0] = 1;
if (core.imageCount[0] < core.sizeZ[0] * core.sizeT[0] * core.sizeC[0]) {
core.sizeT[0] = core.imageCount[0];
core.sizeZ[0] = 1;
}
if (core.sizeZ[0] * core.sizeT[0] * core.sizeC[0] < core.imageCount[0]) {
core.sizeT[0] = 1;
core.sizeZ[0] = core.imageCount[0];
}
core.rgb[0] = core.sizeC[0] >= 3;
core.interleaved[0] = false;
core.littleEndian[0] = false;
core.metadataComplete[0] = true;
MetadataStore store = getMetadataStore();
store.setImage(currentId, null, null, null);
FormatTools.populatePixels(store, this);
store.setDimensions(new Float(pixSizeX), new Float(pixSizeX),
new Float(pixSizeZ), null, null, null);
for (int i=0; i<core.sizeC[0]; i++) {
store.setLogicalChannel(i, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null);
}
String prefix = "MetadataSeq _SEQUENCE_INDEX=\"0\" ";
String gain = (String) getMeta(prefix + "dGain value");
String voltage = (String) getMeta(prefix + "dLampVoltage value");
String mag = (String) getMeta(prefix + "dObjectiveMag value");
String na = (String) getMeta(prefix + "dObjectiveNA value");
store.setDetector(null, null, null, null,
gain == null ? null : new Float(gain),
voltage == null ? null : new Float(voltage), null, null, null);
store.setObjective(null, null, null, na == null ? null : new Float(na),
mag == null ? null : new Float(mag), null, null);
}
// -- Helper class --
/** SAX handler for parsing XML. */
class ND2Handler extends DefaultHandler {
public void startElement(String uri, String localName, String qName,
Attributes attributes)
{
if (qName.equals("uiWidth")) {
core.sizeX[0] = Integer.parseInt(attributes.getValue("value"));
}
else if (qName.equals("uiWidthBytes")) {
int bytes =
Integer.parseInt(attributes.getValue("value")) / core.sizeX[0];
switch (bytes) {
case 2:
core.pixelType[0] = FormatTools.UINT16;
break;
case 4:
core.pixelType[0] = FormatTools.UINT32;
break;
default: core.pixelType[0] = FormatTools.UINT8;
}
}
else if (qName.equals("bValid")) {
adjustImageCount = attributes.getValue("value").equals("true");
}
else if (qName.equals("uiComp")) {
core.sizeC[0] = Integer.parseInt(attributes.getValue("value"));
}
else if (qName.equals("uiBpcInMemory")) {
if (attributes.getValue("value") == null) return;
int bits = Integer.parseInt(attributes.getValue("value"));
int bytes = bits / 8;
switch (bytes) {
case 1:
core.pixelType[0] = FormatTools.UINT8;
break;
case 2:
core.pixelType[0] = FormatTools.UINT16;
break;
case 4:
core.pixelType[0] = FormatTools.UINT32;
break;
default: core.pixelType[0] = FormatTools.UINT8;
}
addMeta(qName, attributes.getValue("value"));
}
else if (qName.equals("uiHeight")) {
core.sizeY[0] = Integer.parseInt(attributes.getValue("value"));
}
else if (qName.equals("uiCount")) {
int n = Integer.parseInt(attributes.getValue("value"));
if (core.imageCount[0] == 0) {
core.imageCount[0] = n;
core.sizeZ[0] = n;
}
core.sizeT[0] = 1;
}
else if (qName.equals("dCompressionParam")) {
isLossless = !attributes.getValue("value").equals("0");
addMeta(qName, attributes.getValue("value"));
}
else {
addMeta(qName, attributes.getValue("value"));
}
}
}
}