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}