package com.colloquial.arithcode;
import java.io.IOException;
import java.io.OutputStream;
/** <P>Performs arithmetic encoding, converting cumulative probability
* interval input into bit output. Cumulative probability intervals
* are given as integer counts <code>low</code>, <code>high</code> and
* <code>total</code>, with the range being
* <code>[low/total,high/total)</code>.
*
* <P>For more details, see <a href="../../../tutorial.html">The Arithemtic Coding Tutorial</a>.
*
* @author <a href="http://www.colloquial.com/carp/">Bob Carpenter</a>
* @version 1.1
* @see ArithDecoder
* @see BitOutput
* @since 1.0
*/
public final class ArithEncoder extends ArithCoder {
/** Construct an arithmetic coder from a bit output.
* @param out Underlying bit output.
* @since 1.1
*/
public ArithEncoder(BitOutput out) {
_out = out;
}
/** Construct an arithmetic coder from an output stream.
* @param out Underlying output stream.
*/
public ArithEncoder(OutputStream out) {
this(new BitOutput(out));
}
/** Close the arithmetic encoder, writing all bits that are
* buffered and closing the underlying output streams.
* @throws IOException If there is an exception writing to or closing the underlying output stream.
*/
public void close() throws IOException {
++_bitsToFollow; // need a final bit (not sure why)
if (_low < FIRST_QUARTER) bitPlusFollowFalse();
else bitPlusFollowTrue();
_out.close();
}
/** Flushes bit output.
* @throws IOException If there is an exception flushing the underlying output stream.
*/
public void flush() throws IOException { _out.flush(); }
/** Encodes an interval expressed as a low count, high count and
* total count in an array <code>{low,high,total}</code>.
* @param counts Low, high and total counts of symbols.
* @see #encode(int,int,int)
* @throws IOException If there is an exception writing to the underlying stream.
*/
public void encode(int[] counts) throws IOException {
encode(counts[0],counts[1],counts[2]);
}
/** Encodes an interval expressed as a low count, high count and total count.
* The high count is taken to be exclusive, and the resulting range is
* <code>highCount - lowCount + 1</code>.
* @param lowCount Cumulative count of symbols below current one.
* @param highCount Cumulative count of symbols below current one plus currnet one.
* @param totalCount Cumulative count of all symbols.
* @throws IOException If there is an exception writing to the underlying stream.
* @see #encode(int[])
*/
public void encode(int lowCount, int highCount, int totalCount) throws IOException {
long range = _high - _low + 1;
_high = _low + (range * highCount) / totalCount - 1;
_low = _low + (range * lowCount) / totalCount;
while (true) {
if (_high < HALF) {
bitPlusFollowFalse();
} else if (_low >= HALF) {
bitPlusFollowTrue();
_low -= HALF;
_high -= HALF;
} else if (_low >= FIRST_QUARTER && _high < THIRD_QUARTER) {
++_bitsToFollow;
_low -= FIRST_QUARTER;
_high -= FIRST_QUARTER;
} else {
return;
}
_low <<= 1;
_high = (_high << 1) + 1;
}
}
/** Bit output stream for writing encoding bits.
*/
private final BitOutput _out;
/** Number of bits beyond first bit that were normalized.
*/
private int _bitsToFollow; // implied = 0;
/** Write a <code>true</code> bit, and then a number of <code>false</code> bits
* equal to the number of bits to follow.
* @throws IOException If there is an exception writing a bit.
* @since 1.1
*/
private void bitPlusFollowTrue() throws IOException {
for (_out.writeBitTrue(); _bitsToFollow > 0; --_bitsToFollow) _out.writeBitFalse();
}
/** Write a <code>false</code> bit, and then a number of <code>true</code> bits
* equal to the number of bits to follow.
* @throws IOException If there is an exception writing a bit.
* @since 1.1
*/
private void bitPlusFollowFalse() throws IOException {
for (_out.writeBitFalse(); _bitsToFollow > 0; --_bitsToFollow) _out.writeBitTrue();
}
}