    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.imaging.common.mylzw;
    018
    019import java.io.IOException;
    020import java.io.OutputStream;
    021import java.nio.ByteOrder;
    022
    023public class MyBitOutputStream extends OutputStream {
    024    private final OutputStream os;
    025    private final ByteOrder byteOrder;
    026    private int bitsInCache;
    027    private int bitCache;
    028    private int bytesWritten;
    029
    030    public MyBitOutputStream(final OutputStream os, final ByteOrder byteOrder) {
    031        this.byteOrder = byteOrder;
    032        this.os = os;
    033    }
    034
    035    @Override
    036    public void write(final int value) throws IOException {
    037        writeBits(value, 8);
    038    }
    039
    040    // TODO: in and out streams CANNOT accurately read/write 32bits at a time,
    041    // as int will overflow. should have used a long
    042    public void writeBits(int value, final int sampleBits) throws IOException {
    043        final int sampleMask = (1 << sampleBits) - 1;
    044        value &= sampleMask;
    045
    046        if (byteOrder == ByteOrder.BIG_ENDIAN) {
    047            // MSB, so add to right
    048            bitCache = (bitCache << sampleBits) | value;
    049        } else {
    050            // LSB, so add to left
    051            bitCache = bitCache | (value << bitsInCache);
    052        }
    053        bitsInCache += sampleBits;
    054
    055        while (bitsInCache >= 8) {
    056            if (byteOrder == ByteOrder.BIG_ENDIAN) {
    057                // MSB, so write from left
    058                final int b = 0xff & (bitCache >> (bitsInCache - 8));
    059                actualWrite(b);
    060
    061                bitsInCache -= 8;
    062            } else {
    063                // LSB, so write from right
    064                final int b = 0xff & bitCache;
    065                actualWrite(b);
    066
    067                bitCache >>= 8;
    068                bitsInCache -= 8;
    069            }
    070            final int remainderMask = (1 << bitsInCache) - 1; // unnecessary
    071            bitCache &= remainderMask; // unnecessary
    072        }
    073
    074    }
    075
    076    private void actualWrite(final int value) throws IOException {
    077        os.write(value);
    078        bytesWritten++;
    079    }
    080
    081    public void flushCache() throws IOException {
    082        if (bitsInCache > 0) {
    083            final int bitMask = (1 << bitsInCache) - 1;
    084            int b = bitMask & bitCache;
    085
    086            if (byteOrder == ByteOrder.BIG_ENDIAN) {
    087                // MSB, so write from left
    088                b <<= 8 - bitsInCache; // left align fragment.
    089                os.write(b);
    090            } else {
    091                // LSB, so write from right
    092                os.write(b);
    093            }
    094        }
    095
    096        bitsInCache = 0;
    097        bitCache = 0;
    098    }
    099
    100    public int getBytesWritten() {
    101        return bytesWritten + ((bitsInCache > 0) ? 1 : 0);
    102    }
    103
    104}