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