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.formats.tiff.datareaders;
18  
19  import java.io.FilterInputStream;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.nio.ByteOrder;
23  
24  import org.apache.commons.imaging.ImagingException;
25  
26  /**
27   * Input stream reading 1-8, 16, 24 or 32 bits, starting from the most significant bit, but incapable of reading non-aligned and < 8 bit fields across byte
28   * boundaries.
29   */
30  final class BitInputStream extends FilterInputStream {
31      private final ByteOrder byteOrder;
32      private int cache;
33      private int cacheBitsRemaining;
34      private long bytesRead;
35  
36      BitInputStream(final InputStream is, final ByteOrder byteOrder) {
37          super(is);
38          this.byteOrder = byteOrder;
39      }
40  
41      public void flushCache() {
42          cacheBitsRemaining = 0;
43      }
44  
45      public long getBytesRead() {
46          return bytesRead;
47      }
48  
49      @Override
50      public int read() throws IOException {
51          if (cacheBitsRemaining > 0) {
52              throw new ImagingException("BitInputStream: incomplete bit read");
53          }
54          return in.read();
55      }
56  
57      public int readBits(final int count) throws IOException {
58          if (count < 8) {
59              if (cacheBitsRemaining == 0) {
60                  // fill cache
61                  cache = in.read();
62                  cacheBitsRemaining = 8;
63                  bytesRead++;
64              }
65              if (count > cacheBitsRemaining) {
66                  throw new ImagingException("BitInputStream: can't read bit fields across bytes");
67              }
68  
69              // int bits_to_shift = cache_bits_remaining - count;
70              cacheBitsRemaining -= count;
71              final int bits = cache >> cacheBitsRemaining;
72  
73              switch (count) {
74              case 1:
75                  return bits & 1;
76              case 2:
77                  return bits & 3;
78              case 3:
79                  return bits & 7;
80              case 4:
81                  return bits & 15;
82              case 5:
83                  return bits & 31;
84              case 6:
85                  return bits & 63;
86              case 7:
87                  return bits & 127;
88              }
89  
90          }
91          if (cacheBitsRemaining > 0) {
92              throw new ImagingException("BitInputStream: incomplete bit read");
93          }
94  
95          if (count == 8) {
96              bytesRead++;
97              return in.read();
98          }
99  
100         /**
101          * Taking default order of the TIFF to be Little Endian and reversing the bytes in the end if its Big Endian.This is done because majority (may be all)
102          * of the files will be of Little Endian.
103          */
104         if (byteOrder == ByteOrder.BIG_ENDIAN) {
105             switch (count) {
106             case 16:
107                 bytesRead += 2;
108                 return in.read() << 8 | in.read() << 0;
109             case 24:
110                 bytesRead += 3;
111                 return in.read() << 16 | in.read() << 8 | in.read() << 0;
112             case 32:
113                 bytesRead += 4;
114                 return in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read() << 0;
115             default:
116                 break;
117             }
118         } else {
119             switch (count) {
120             case 16:
121                 bytesRead += 2;
122                 return in.read() << 0 | in.read() << 8;
123             case 24:
124                 bytesRead += 3;
125                 return in.read() << 0 | in.read() << 8 | in.read() << 16;
126             case 32:
127                 bytesRead += 4;
128                 return in.read() << 0 | in.read() << 8 | in.read() << 16 | in.read() << 24;
129             default:
130                 break;
131             }
132         }
133 
134         throw new ImagingException("BitInputStream: unknown error");
135     }
136 }