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  
18  package org.apache.commons.imaging.formats.tiff;
19  
20  import static org.junit.Assert.assertEquals;
21  
22  import java.io.ByteArrayInputStream;
23  import java.io.File;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.nio.ByteOrder;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.List;
30  
31  import org.apache.commons.imaging.ImageReadException;
32  import org.apache.commons.imaging.ImageWriteException;
33  import org.apache.commons.imaging.common.bytesource.ByteSource;
34  import org.apache.commons.imaging.common.bytesource.ByteSourceFile;
35  import org.apache.commons.imaging.common.mylzw.MyLzwCompressor;
36  import org.apache.commons.imaging.common.mylzw.MyLzwDecompressor;
37  import org.apache.commons.imaging.util.Debug;
38  import org.junit.Ignore;
39  import org.junit.Test;
40  
41  public class TiffLzwTest extends TiffBaseTest {
42  
43      @Test
44      public void testTrivial() throws Exception {
45          final byte[] bytes = { 0, };
46          compressRoundtripAndValidate(bytes);
47      }
48  
49      @Test
50      public void testMedium() throws Exception {
51          final int LENGTH = 1024 * 32;
52          final byte[] bytes = new byte[LENGTH];
53          for (int modulator = 1; modulator < 255; modulator += 3) {
54              for (int i = 0; i < LENGTH; i++) {
55                  bytes[i] = (byte) (0xff & (i % modulator));
56              }
57  
58              compressRoundtripAndValidate(bytes);
59          }
60      }
61  
62      @Ignore // FIXME fails with java.io.IOException: Bad Code: -1 codes: 258 code_size: 9, table: 4096
63      @Test
64      public void testTiffImageData() throws IOException, ImageReadException,
65              ImageWriteException {
66          final List<File> images = getTiffImages();
67          for (final File image : images) {
68  
69              Debug.debug("imageFile", image);
70  
71              final ByteSource byteSource = new ByteSourceFile(image);
72              final List<byte[]> data = new TiffImageParser().collectRawImageData(byteSource,
73                      Collections.<String, Object>emptyMap());
74  
75              for (final byte[] bytes : data) {
76                  decompressRoundtripAndValidate(bytes);
77              }
78          }
79      }
80  
81      private void compressRoundtripAndValidate(final byte src[]) throws IOException {
82          final boolean DEBUG = false;
83  
84          if (DEBUG) {
85              Debug.debug();
86              Debug.debug("roundtripAndValidate: " + src.length);
87              Debug.debug();
88          }
89  
90          final int LZW_MINIMUM_CODE_SIZE = 8;
91          final List<Integer> codes = new ArrayList<>();
92          final MyLzwCompressor.Listener compressionListener = new MyLzwCompressor.Listener() {
93              @Override
94              public void dataCode(final int code) {
95                  codes.add(code);
96              }
97  
98              @Override
99              public void eoiCode(final int code) {
100                 codes.add(code);
101             }
102 
103             @Override
104             public void clearCode(final int code) {
105                 codes.add(code);
106             }
107 
108             @Override
109             public void init(final int clearCode, final int eoiCode) {
110             }
111         };
112 
113         final MyLzwCompressor compressor = new MyLzwCompressor(LZW_MINIMUM_CODE_SIZE,
114                 ByteOrder.BIG_ENDIAN, true, compressionListener);
115         final byte compressed[] = compressor.compress(src);
116 
117         final MyLzwDecompressor.Listener decompressionListener = new MyLzwDecompressor.Listener() {
118 
119             int index = 0;
120             int clearCode, eoiCode;
121 
122             @Override
123             public void code(final int code) {
124                 if (DEBUG) {
125                     if (code == clearCode) {
126                         Debug.debug("clearCode: " + index + "/" + codes.size());
127                         Debug.debug();
128                     }
129                     if (code == eoiCode) {
130                         Debug.debug("eoiCode: " + index + "/" + codes.size());
131                         Debug.debug();
132                     }
133                 }
134                 final Integer expectedCode = codes.get(index++);
135                 if (code != expectedCode) {
136                     Debug.debug("bad code: " + index + "/" + codes.size());
137                     Debug.debug("code: " + code + " (0x"
138                             + Integer.toHexString(code) + ") "
139                             + Integer.toBinaryString(code));
140                     Debug.debug("expected: " + expectedCode + " (0x"
141                             + Integer.toHexString(expectedCode)
142                             + ") "
143                             + Integer.toBinaryString(expectedCode));
144                     Debug.debug("clearCode: " + clearCode + " (0x"
145                             + Integer.toHexString(clearCode) + ") "
146                             + Integer.toBinaryString(clearCode));
147                     Debug.debug("eoiCode: " + eoiCode + " (0x"
148                             + Integer.toHexString(eoiCode) + ") "
149                             + Integer.toBinaryString(eoiCode));
150                     Debug.debug();
151                 }
152             }
153 
154             @Override
155             public void init(final int clearCode, final int eoiCode) {
156                 this.clearCode = clearCode;
157                 this.eoiCode = eoiCode;
158             }
159 
160         };
161         final InputStream is = new ByteArrayInputStream(compressed);
162         final MyLzwDecompressor decompressor = new MyLzwDecompressor(
163                 LZW_MINIMUM_CODE_SIZE, ByteOrder.BIG_ENDIAN,
164                 decompressionListener);
165         decompressor.setTiffLZWMode();
166         final byte decompressed[] = decompressor.decompress(is, src.length);
167 
168         assertEquals(src.length, decompressed.length);
169         for (int i = 0; i < src.length; i++) {
170             assertEquals(src[i], decompressed[i]);
171         }
172     }
173 
174     private void decompressRoundtripAndValidate(final byte src[]) throws IOException {
175         Debug.debug();
176         Debug.debug("roundtripAndValidate: " + src.length);
177         Debug.debug();
178 
179         final int LZW_MINIMUM_CODE_SIZE = 8;
180         final List<Integer> codes = new ArrayList<>();
181 
182         final MyLzwDecompressor.Listener decompressionListener = new MyLzwDecompressor.Listener() {
183 
184             @Override
185             public void code(final int code) {
186                 Debug.debug("listener code: " + code + " (0x"
187                         + Integer.toHexString(code) + ") "
188                         + Integer.toBinaryString(code) + ", index: "
189                         + codes.size());
190                 codes.add(code);
191             }
192 
193             @Override
194             public void init(final int clearCode, final int eoiCode) {
195             }
196 
197         };
198         final InputStream is = new ByteArrayInputStream(src);
199         final MyLzwDecompressor decompressor = new MyLzwDecompressor(
200                 LZW_MINIMUM_CODE_SIZE, ByteOrder.BIG_ENDIAN,
201                 decompressionListener);
202         decompressor.setTiffLZWMode();
203         final byte decompressed[] = decompressor.decompress(is, src.length);
204 
205         final MyLzwCompressor.Listener compressionListener = new MyLzwCompressor.Listener() {
206 
207             int clearCode, eoiCode;
208 
209             @Override
210             public void init(final int clearCode, final int eoiCode) {
211                 this.clearCode = clearCode;
212                 this.eoiCode = eoiCode;
213             }
214 
215             int index = 0;
216 
217             private void code(final int code) {
218 
219                 if (code == clearCode) {
220                     Debug.debug("clearCode: " + index + "/" + codes.size());
221                     Debug.debug();
222                 }
223                 if (code == eoiCode) {
224                     Debug.debug("eoiCode: " + index + "/" + codes.size());
225                     Debug.debug();
226                 }
227                 final Integer expectedCode = codes.get(index++);
228                 if (code != expectedCode) {
229                     Debug.debug("bad code: " + index + "/" + codes.size());
230                     Debug.debug("code: " + code + " (0x"
231                             + Integer.toHexString(code) + ") "
232                             + Integer.toBinaryString(code));
233                     Debug.debug("expected: " + expectedCode + " (0x"
234                             + Integer.toHexString(expectedCode)
235                             + ") "
236                             + Integer.toBinaryString(expectedCode));
237                     Debug.debug("clearCode: " + clearCode + " (0x"
238                             + Integer.toHexString(clearCode) + ") "
239                             + Integer.toBinaryString(clearCode));
240                     Debug.debug("eoiCode: " + eoiCode + " (0x"
241                             + Integer.toHexString(eoiCode) + ") "
242                             + Integer.toBinaryString(eoiCode));
243                     Debug.debug();
244                 }
245             }
246 
247             @Override
248             public void dataCode(final int code) {
249                 code(code);
250             }
251 
252             @Override
253             public void eoiCode(final int code) {
254                 code(code);
255             }
256 
257             @Override
258             public void clearCode(final int code) {
259                 code(code);
260             }
261 
262         };
263 
264         final MyLzwCompressor compressor = new MyLzwCompressor(LZW_MINIMUM_CODE_SIZE,
265                 ByteOrder.BIG_ENDIAN, true, compressionListener);
266         final byte compressed[] = compressor.compress(decompressed);
267 
268         assertEquals(src.length, compressed.length);
269         for (int i = 0; i < src.length; i++) {
270             assertEquals(src[i], compressed[i]);
271         }
272     }
273 
274 }