View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.imaging.common.mylzw;
18  
19  import java.io.IOException;
20  import java.io.OutputStream;
21  import java.nio.ByteOrder;
22  
23  public class MyBitOutputStream extends OutputStream {
24      private final OutputStream os;
25      private final ByteOrder byteOrder;
26      private int bitsInCache;
27      private int bitCache;
28      private int bytesWritten;
29  
30      public MyBitOutputStream(final OutputStream os, final ByteOrder byteOrder) {
31          this.byteOrder = byteOrder;
32          this.os = os;
33      }
34  
35      @Override
36      public void write(final int value) throws IOException {
37          writeBits(value, 8);
38      }
39  
40      // TODO: in and out streams CANNOT accurately read/write 32bits at a time,
41      // as int will overflow. should have used a long
42      public void writeBits(int value, final int sampleBits) throws IOException {
43          final int sampleMask = (1 << sampleBits) - 1;
44          value &= sampleMask;
45  
46          if (byteOrder == ByteOrder.BIG_ENDIAN) {
47              // MSB, so add to right
48              bitCache = (bitCache << sampleBits) | value;
49          } else {
50              // LSB, so add to left
51              bitCache = bitCache | (value << bitsInCache);
52          }
53          bitsInCache += sampleBits;
54  
55          while (bitsInCache >= 8) {
56              if (byteOrder == ByteOrder.BIG_ENDIAN) {
57                  // MSB, so write from left
58                  final int b = 0xff & (bitCache >> (bitsInCache - 8));
59                  actualWrite(b);
60  
61                  bitsInCache -= 8;
62              } else {
63                  // LSB, so write from right
64                  final int b = 0xff & bitCache;
65                  actualWrite(b);
66  
67                  bitCache >>= 8;
68                  bitsInCache -= 8;
69              }
70              final int remainderMask = (1 << bitsInCache) - 1; // unnecessary
71              bitCache &= remainderMask; // unnecessary
72          }
73  
74      }
75  
76      private void actualWrite(final int value) throws IOException {
77          os.write(value);
78          bytesWritten++;
79      }
80  
81      public void flushCache() throws IOException {
82          if (bitsInCache > 0) {
83              final int bitMask = (1 << bitsInCache) - 1;
84              int b = bitMask & bitCache;
85  
86              if (byteOrder == ByteOrder.BIG_ENDIAN) {
87                  // MSB, so write from left
88                  b <<= 8 - bitsInCache; // left align fragment.
89                  os.write(b);
90              } else {
91                  // LSB, so write from right
92                  os.write(b);
93              }
94          }
95  
96          bitsInCache = 0;
97          bitCache = 0;
98      }
99  
100     public int getBytesWritten() {
101         return bytesWritten + ((bitsInCache > 0) ? 1 : 0);
102     }
103 
104 }