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