/*-
* Copyright 2016 Diamond Light Source Ltd.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package uk.ac.diamond.scisoft.analysis.io;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.dawnsci.analysis.api.io.IDataHolder;
import org.eclipse.dawnsci.analysis.api.io.ScanFileHolderException;
import org.eclipse.january.IMonitor;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.DatasetFactory;
import org.eclipse.january.dataset.IntegerDataset;
import org.eclipse.january.metadata.Metadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Loader to open the Elmitec UView .dat files (or sequence of files). The UView format is historically called
* UKSOFT2001 (it was used originally for STM pictures)
*/
public class UViewDatLoader extends AbstractFileLoader {
private static final Logger logger = LoggerFactory.getLogger(UViewDatLoader.class);
private Map<String, Serializable> headers = new HashMap<>();
private static final int HEADER_SIZE = 104; // headerSize is always 104 bytes
private static final int IMAGE_HEADER_SIZE = 288;
private static final int RECIPE_BLOCK_SIZE = 128;
public UViewDatLoader() {
}
public UViewDatLoader(String fileName) {
this();
setFile(fileName);
}
@Override
public IDataHolder loadFile() throws ScanFileHolderException {
return loadFile((IMonitor) null);
}
@Override
public IDataHolder loadFile(final IMonitor mon) throws ScanFileHolderException {
// first instantiate the return object.
final IDataHolder result = new DataHolder();
BufferedInputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(fileName));
//read metadata/headers and return position of image data
int pos = readMetadata(in);
int width = (int) headers.get(BinaryKey.WIDTH.toString());
int height = (int) headers.get(BinaryKey.HEIGHT.toString());
IntegerDataset imagedata = (IntegerDataset) DatasetFactory.zeros(
new int[] { width, height },
Dataset.INT32);
Utils.readLeShort(in, imagedata, pos, false);
result.addDataset(DEF_IMAGE_NAME, imagedata);
if (loadMetadata) {
result.setMetadata(metadata);
result.getDataset(0).setMetadata(metadata);
}
return result;
} catch (IOException e) {
logger.error("UViewDatLoader.loadFile exception loading " + fileName, e);
throw new ScanFileHolderException("UViewDatLoader.loadFile exception loading " + fileName, e);
} finally {
try {
if (in != null)
in.close();
} catch (IOException e) {
logger.error("Cannot close stream from file " + fileName, e);
throw new ScanFileHolderException("Cannot close stream from file " + fileName, e);
}
}
}
private int getShort(BufferedInputStream f) throws IOException {
int b0 = getByte(f);
int b1 = getByte(f);
return ((b1 << 8) + b0);
}
private int getByte(BufferedInputStream f) throws IOException {
int b = f.read();
if (b == -1)
throw new IOException("unexpected EOF");
return b;
}
@SuppressWarnings("unused")
private int getUnsignedShort(BufferedInputStream f) throws IOException {
int high = f.read();
int low = f.read();
return (((high & 0xFF) << 8) | (low & 0xFF));
}
@SuppressWarnings("unused")
private int getInt(BufferedInputStream f) throws IOException {
int b0 = getShort(f);
int b1 = getShort(f);
return ((b1 << 16) + b0);
}
private int readMetadata(BufferedInputStream bi) throws IOException {
int pos = readHeader(bi);
metadata = new Metadata();
metadata.initialize(headers);
return pos;
}
private int readHeader(BufferedInputStream bis) throws IOException {
headers.clear();
int[] sizeAndStart = new int[3];
boolean hasRecipe = false, hasAttachedMarkup = false;
int line = 0, idx = 0, ukVersion = 0, attachedRecipeSize = 0, filePointer = 0, attachedMarkupSize = 0,
imageWidth = 0, imageHeight = 0, leemDataVersion = 0;
int totalHeaderSize = 0;
while ((line = getShort(bis)) != -1) {
if (idx == 11) { // UK version
ukVersion = line;
}
if (idx == 20) { // image width
imageWidth = line;
}
if (idx == 21) { // image height
imageHeight = line;
}
if (idx == 23) {
if (ukVersion >= 7) { // attached RecipeSize
attachedRecipeSize = line;
hasRecipe = attachedRecipeSize > 0;
} else {
attachedRecipeSize = 0;
hasRecipe = false;
}
// define file pointer length
if (hasRecipe) {
filePointer = HEADER_SIZE + RECIPE_BLOCK_SIZE;
} else {
filePointer = HEADER_SIZE;
}
}
if (idx > 23 && idx == ((filePointer + 22) / 2)) {
attachedMarkupSize = line;
hasAttachedMarkup = attachedMarkupSize != 0;
if (hasAttachedMarkup) {
attachedMarkupSize = 128 * ((attachedMarkupSize / 128) + 1);
} else {
attachedMarkupSize = 0;
}
}
if (idx > 25 && idx == ((filePointer + 26) / 2)) {
leemDataVersion = line;
totalHeaderSize = HEADER_SIZE + attachedRecipeSize + IMAGE_HEADER_SIZE + attachedMarkupSize
+ leemDataVersion;
sizeAndStart[2] = totalHeaderSize;
break;
}
idx++;
}
// Fill headers
for (BinaryKey k : BinaryKey.values()) {
Serializable s = null;
switch (k) {
case WIDTH:
s = imageWidth;
break;
case HEIGHT:
s = imageHeight;
break;
case VERSION:
s = ukVersion;
break;
case HASRECIPE:
s = hasRecipe;
break;
case HASMARKUP:
s = hasAttachedMarkup;
break;
case RECIPESIZE:
s = attachedRecipeSize;
break;
case MARKUPSIZE:
s = attachedMarkupSize;
break;
case LEEMDATAVERSION:
s = leemDataVersion;
break;
case TOTALHEADERSIZE:
s = totalHeaderSize;
break;
}
if (s != null)
headers.put(k.toString(), s);
}
return totalHeaderSize - (idx * 2);
}
@Override
protected void clearMetadata() {
metadata = null;
headers.clear();
}
enum BinaryKey {
WIDTH, //
HEIGHT, //
VERSION, //
HASRECIPE, //
HASMARKUP, //
RECIPESIZE, //
MARKUPSIZE, //
LEEMDATAVERSION, //
TOTALHEADERSIZE, //
;
}
}