Reading the bits
10-Sep-2009 05:59 - Filed under: SPIWith this post we start talking about how parts of jrawio are implemented. This information will be useful for people wanting to understand the internals and eventually contribute new code for supporting new formats.
Part of the posts will be related to the Java Image I/O API, so they could be of interest for people wanting to develop codecs for other formats.
One of the first things that comes into our mind when we think of reading a camera raw file is getting the bits of the raster. In Java we know that whenever we want to read from a stream, InputStream and its implementations are the classes that we need. The Image I/O API provides an augmented interface, ImageInputStream:
public interface javax.imageio.stream.ImageInputStream extends java.io.DataInput
{
public void setByteOrder(java.nio.ByteOrder);
public java.nio.ByteOrder getByteOrder();
public int read()
throws java.io.IOException;
public int read (byte[] buffer)
throws java.io.IOException;
public int read (byte[] buffer, int offset, int count)
throws java.io.IOException;
public void readBytes (javax.imageio.stream.IIOByteBuffer buffer, int count)
throws java.io.IOException;
public boolean readBoolean()
throws java.io.IOException;
public byte readByte()
throws java.io.IOException;
public int readUnsignedByte()
throws java.io.IOException;
public short readShort()
throws java.io.IOException;
public int readUnsignedShort()
throws java.io.IOException;
public char readChar()
throws java.io.IOException;
public int readInt()
throws java.io.IOException;
public long readUnsignedInt()
throws java.io.IOException;
public long readLong()
throws java.io.IOException;
public float readFloat()
throws java.io.IOException;
public double readDouble()
throws java.io.IOException;
public String readLine()
throws java.io.IOException;
public String readUTF()
throws java.io.IOException;
public void readFully (byte[] buffer, int, int count)
throws java.io.IOException;
public void readFully (byte[] buffer)
throws java.io.IOException;
public void readFully (short[] buffer, int, int count)
throws java.io.IOException;
public void readFully (char[] buffer, int, int count)
throws java.io.IOException;
public void readFully (int[] buffer, int, int count)
throws java.io.IOException;
public void readFully (long[] buffer, int, int count)
throws java.io.IOException;
public void readFully (float[] buffer, int, int count)
throws java.io.IOException;
public void readFully (double[] buffer, int, int count)
throws java.io.IOException;
public long getStreamPosition()
throws java.io.IOException;
public int getBitOffset()
throws java.io.IOException;
public void setBitOffset (int offset)
throws java.io.IOException;
public int readBit()
throws java.io.IOException;
public long readBits (int count)
throws java.io.IOException;
public long length()
throws java.io.IOException;
public int skipBytes (int count)
throws java.io.IOException;
public long skipBytes (long count)
throws java.io.IOException;
public void seek (long position)
throws java.io.IOException;
public void mark();
public void reset()
throws java.io.IOException;
public void flushBefore (long position)
throws java.io.IOException;
public void flush()
throws java.io.IOException;
public long getFlushedPosition();
public boolean isCached();
public boolean isCachedMemory();
public boolean isCachedFile();
public void close()
throws java.io.IOException;
}
The additional capabilities are:
- it's possible to declare the byte order (big endian, little endian) that affects all multi-byte operations (such as
readInt()) - there are bitwise operations, such as
readBit(),readBits(),get/setByteOffset(). - it's possible to cache portions of files, for better performance.
For jrawio, this is not enough, as experience showed that some more operations are commonly needed when decoding the raster of a camera raw format; so a further interface is defined with extra features:
public interface RAWImageInputStream extends ImageInputStream
{
public void setBaseOffset (@Nonnegative long baseOffset);
@Nonnegative
public long getBaseOffset();
public void setSkipZeroAfterFF (boolean skipZeroAfterFF);
public boolean isSkipZeroAfterFF();
public void selectBitReader (@Nonnegative int bitCount,
@Nonnegative int bufferSize);
public void skipBits (@Nonnegative int bitCount)
throws IOException;
}
The new operations are:
- The property 'baseOffset' is needed to deal with a typical pattern that is found in many camera raw files: embedding metadata items (or eventually whole JPG or TIFF files) into another metadata item. The TIFF specifications make use of absolute offsets to link together various components (such as metadata directories or rasters); for instance, a metadata item could say "the raster starts at 0x204050". When we deal with embedding, those offsets are no more absolute, but relative to the position of the embedding item: if the previous example was embedded into a metadata item starting at 0x604020, we would find the raster at the sum of the two values: 0x204050 + 0x604020. But JPG and TIFF decoders only refer to absolute values, as per specifications. By setting a base offset (e.g.
setBaseOffset(0x604020), we can trick the JPG or TIFF decoder to work with the needed relative addressing. - The property 'skipZeroAfterFF', when enabled, automatically skips a byte after reading a byte whose value is 0xFF. This is a feature typical of many camera raw formats (the skipped byte is usually set to zero).
- It might happen (even though it's not frequent) that rasters in camera raw files are encoded in an uncompressed way, with a fixed number of bits (10, 12, 14, 16) sequentially packed (e.g. three bytes containing two packs of 12 bits each). In such cases, knowing in advance that we're going to perform many millions of calls to read bit strings all with the same length allow to implement some relevant optimizations: that's why
RAWImageInputStreammakes it possible to specify these properties: the proper implementation will be selected accordingly. - At last, a convenience
skipBits()method is added.
This blog doesn't allow to directly comment posts. Please provide us with your feedback by means of either the “dev” or the “users” mailing list, as described in the contact page. Thanks!