package ini.trakem2.io;
import ini.trakem2.imaging.P;
import ini.trakem2.persistence.ImageBytes;
import ini.trakem2.utils.CachingThread;
import ini.trakem2.utils.IJError;
import ini.trakem2.utils.Utils;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public final class RawMipMaps {
/** These are both the 4 supported types and the number of channels that each type has. */
static public final byte GREY = 1,
GREY_ALPHA = 2,
RGB = 3,
RGBA = 4;
/** Two 4-byte ints, for width and height, and one byte for the type. */
static public final int HEADER_SIZE = 9;
static public final boolean save(final String path, final byte[][] b, final int width, final int height) {
if (!ImageSaver.checkPath(path)) return false;
RandomAccessFile ra = null;
try {
ra = new RandomAccessFile(new File(path), "rw");
// Header: must write as an array or integers get saved with less than 4 bytes
final byte[] h = new byte[9];
h[0] = (byte)((width >> 24) & 0xff);
h[1] = (byte)((width >> 16) & 0xff);
h[2] = (byte)((width >> 8) & 0xff);
h[3] = (byte) (width & 0xff);
h[4] = (byte)((height >> 24) & 0xff);
h[5] = (byte)((height >> 16) & 0xff);
h[6] = (byte)((height >> 8) & 0xff);
h[7] = (byte) (height & 0xff);
h[8] = (byte) b.length ; // only possible values: 1,2,3,4; it's the type
ra.write(h);
// Write channels
for (int i=0; i<b.length; ++i) {
ra.write(b[i]);
}
return true;
} catch (Exception e) {
IJError.print(e);
} finally {
if (null != ra) try { ra.close(); } catch (Exception e) { IJError.print(e); }
}
return false;
}
static public final ImageBytes load(final String path) {
return load(path, 0);
}
static public final ImageBytes load(final String path, final int retry) {
RandomAccessFile ra = null;
try {
final File f = new File(path);
ra = new RandomAccessFile(f, "r");
final byte[] h = new byte[9];
read(ra, h);
final int width = ((h[0]&0xff) << 24) | ((h[1]&0xff) << 16) | ((h[2]&0xff) << 8) | (h[3]&0xff);
final int height = ((h[4]&0xff) << 24) | ((h[5]&0xff) << 16) | ((h[6]&0xff) << 8) | (h[7]&0xff);
final int nCh = h[8];
final int chLength = (((int)f.length()) - HEADER_SIZE) / nCh;
final byte[][] ch = CachingThread.getOrCreateByteArray(nCh, chLength); // new byte[nCh][chLength];
for (int i=0; i<nCh; ++i) {
read(ra, ch[i]);
}
return new ImageBytes(ch, width, height);
} catch (FileNotFoundException fnfe) {
Utils.log2("File not found: " + path);
} catch (Exception e) {
// Possible: NegativeArraySizeException
// ... meaning that the file exists but hasn't yet been fully written
// Rather than going fancy with file locks, just wait 100 ms and retry
// Retry
if (retry < 2) {
// Wait for image to be fully written
try { Thread.sleep(100); } catch (InterruptedException ie) {}
return load(path, retry + 1);
}
// Else the error is for real
else IJError.print(e);
} finally {
if (null != ra) try { ra.close(); } catch (Exception e) { IJError.print(e); }
}
return null;
}
static public final BufferedImage read(final String path) {
try {
final ImageBytes ib = load(path);
if (null == ib) return null;
final byte[][] ch = ib.c;
// Channel length also specifies the type
switch (ch.length) {
case GREY:
return ImageSaver.createGrayImage(ch[0], ib.width, ib.height);
}
try {
// Given that the BufferedImage is created with an int[], store the byte[] arrays for reuse
switch (ch.length) {
case GREY_ALPHA:
// TODO: price of PRE shold be paid when saving, not when reading
return ImageSaver.createARGBImagePre(P.blendPre(ch[0], ch[1]), ib.width, ib.height);
case RGB:
return ImageSaver.createRGBImage(P.blend(ch[0], ch[1], ch[2]), ib.width, ib.height);
case RGBA:
// TODO: price of PRE shold be paid when saving, not when reading
return ImageSaver.createARGBImagePre(P.blendPre(ch[0], ch[1], ch[2], ch[3]), ib.width, ib.height);
}
} finally {
CachingThread.storeForReuse(ch);
}
} catch (Exception e) {
IJError.print(e);
}
return null;
}
static private final void read(final RandomAccessFile ra, final byte[] b) throws IOException {
int s = 0;
while (s < b.length) {
int r = ra.read(b, s, b.length - s);
if (-1 == r) return;
s += r;
}
}
}