From 8e11d95fd6dee1e78a99f4c6ca822bd7cb5e722f Mon Sep 17 00:00:00 2001 From: Schmidor Date: Sun, 4 Oct 2015 00:40:36 +0200 Subject: [PATCH] Work in progress: CCITT Encoder --- .../plugins/tiff/CCITTFaxEncoderStream.java | 167 ++++++++++++++++-- .../tiff/CCITTFaxEncoderStreamTest.java | 66 +++++++ 2 files changed, 217 insertions(+), 16 deletions(-) create mode 100644 imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/CCITTFaxEncoderStreamTest.java diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/CCITTFaxEncoderStream.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/CCITTFaxEncoderStream.java index 0e912e4b..29823188 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/CCITTFaxEncoderStream.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/CCITTFaxEncoderStream.java @@ -1,11 +1,45 @@ +/* + * Copyright (c) 2013, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package com.twelvemonkeys.imageio.plugins.tiff; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import com.twelvemonkeys.lang.Validate; +/** + * CCITT Modified Huffman RLE, Group 3 (T4) and Group 4 (T6) fax compression. + * + * @author Harald Kuhr + * @author last modified by $Author$ + * @version $Id$ + */ public class CCITTFaxEncoderStream extends OutputStream { private int currentBufferLength = 0; @@ -65,6 +99,17 @@ public class CCITTFaxEncoderStream extends OutputStream { } } + @Override + public void flush() throws IOException { + stream.flush(); + } + + @Override + public void close() throws IOException { + fill(); + stream.close(); + } + // TODO: when to write end EOLs, half filled buffer bytes etc. on end? private void encodeRow() throws IOException { @@ -108,11 +153,14 @@ public class CCITTFaxEncoderStream extends OutputStream { if (optionG32D) { // TODO decide whether 1d or 2d row, write k, encode } else { - // TODO encode1d + encode1D(); + } + if (optionG3Fill) { + fill(); } } - private void encodeRowType6() { + private void encodeRowType6() throws IOException { encode2D(); } @@ -132,10 +180,10 @@ public class CCITTFaxEncoderStream extends OutputStream { Code[] codes = white ? WHITE_NONTERMINATING_CODES : BLACK_NONTERMINATING_CODES; while (nonterm > 0) { if (nonterm >= codes.length) { - write(codes[codes.length-1].code,codes[codes.length-1].length); + write(codes[codes.length - 1].code, codes[codes.length - 1].length); nonterm -= codes.length - 1; } else { - write(codes[nonterm - 1].code,codes[nonterm - 1].length); + write(codes[nonterm - 1].code, codes[nonterm - 1].length); nonterm = 0; } } @@ -145,8 +193,65 @@ public class CCITTFaxEncoderStream extends OutputStream { } } - private void encode2D() { + private void encode2D() throws IOException { + boolean white = true; + int lastChange = -1; + for (int i = 0; i < changesCurrentRowLength; i++) { // TODO + // columns-Basiert + // statt references + int nextChange = changesCurrentRow[i]; + int nextRef = getNextRefChange(lastChange); + + int difference = nextRef - nextChange; + if (difference < -3) { + // next change nearer than nextRef, PMODE + write(1, 4); + lastChange = nextRef; + } else if (difference > 3) { + // next change farer than nextRef, hmode + write(1, 3); + // write runLength(white) + // write runLength(!white) + // i++; + // lastChange = ... + } else { + // VMODE + switch (difference) { + case 0: + write(1, 1); + break; + case 1: + write(3, 3); + break; + case 2: + write(3, 6); + break; + case 3: + write(3, 7); + break; + case -1: + write(2, 3); + break; + case -2: + write(2, 6); + break; + case -3: + write(2, 7); + break; + } + white = !white; + } + } + } + + private int getNextRefChange(int currentRowChange) { + for (int i = 0; i < changesReferenceRowLength; i++) { + if (changesReferenceRow[i] > currentRowChange) { + return changesReferenceRow[i]; + } + } + return columns; } private void write(int code, int codeLength) throws IOException { @@ -188,12 +293,7 @@ public class CCITTFaxEncoderStream extends OutputStream { outputBufferBitLength = 0; } - private static class Code { - public static Code create(int code, int length) { - Code c = new Code(code, length); - return c; - } - + public static class Code { private Code(int code, int length) { this.code = code; this.length = length; @@ -203,11 +303,46 @@ public class CCITTFaxEncoderStream extends OutputStream { final int length; } - private Code[] WHITE_TERMINATING_CODES = {}; + public static final Code[] WHITE_TERMINATING_CODES; - private Code[] WHITE_NONTERMINATING_CODES = {}; + public static final Code[] WHITE_NONTERMINATING_CODES; - private Code[] BLACK_TERMINATING_CODES = {}; + public static final Code[] BLACK_TERMINATING_CODES; - private Code[] BLACK_NONTERMINATING_CODES = {}; + public static final Code[] BLACK_NONTERMINATING_CODES; + + static { + // Setup HUFFMAN Codes + WHITE_TERMINATING_CODES = new Code[64]; + WHITE_NONTERMINATING_CODES = new Code[40]; + for (int i = 0; i < CCITTFaxDecoderStream.WHITE_CODES.length; i++) { + int bitLength = i + 4; + for (int j = 0; j < CCITTFaxDecoderStream.WHITE_CODES[i].length; j++) { + int value = CCITTFaxDecoderStream.WHITE_RUN_LENGTHS[i][j]; + int code = CCITTFaxDecoderStream.WHITE_CODES[i][j]; + + if (value < 64) { + WHITE_TERMINATING_CODES[value] = new Code(code, bitLength); + } else { + WHITE_NONTERMINATING_CODES[(value / 64) - 1] = new Code(code, bitLength); + } + } + } + + BLACK_TERMINATING_CODES = new Code[64]; + BLACK_NONTERMINATING_CODES = new Code[40]; + for (int i = 0; i < CCITTFaxDecoderStream.BLACK_CODES.length; i++) { + int bitLength = i + 2; + for (int j = 0; j < CCITTFaxDecoderStream.BLACK_CODES[i].length; j++) { + int value = CCITTFaxDecoderStream.BLACK_RUN_LENGTHS[i][j]; + int code = CCITTFaxDecoderStream.BLACK_CODES[i][j]; + + if (value < 64) { + BLACK_TERMINATING_CODES[value] = new Code(code, bitLength); + } else { + BLACK_NONTERMINATING_CODES[(value / 64) - 1] = new Code(code, bitLength); + } + } + } + } } diff --git a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/CCITTFaxEncoderStreamTest.java b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/CCITTFaxEncoderStreamTest.java new file mode 100644 index 00000000..b609b765 --- /dev/null +++ b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/CCITTFaxEncoderStreamTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.imageio.plugins.tiff; + +import org.junit.Test; + +import com.twelvemonkeys.imageio.plugins.tiff.CCITTFaxEncoderStream.Code; + +import java.io.IOException; +import static org.junit.Assert.*; + +/** + * CCITTFaxEncoderStreamTest + * + * @author Harald Kuhr + * @author last modified by $Author$ + * @version $Id$ + */ +public class CCITTFaxEncoderStreamTest { + + @Test + public void testBuildCodes() throws IOException { + assertTrue(CCITTFaxEncoderStream.WHITE_TERMINATING_CODES.length == 64); + for (Code code : CCITTFaxEncoderStream.WHITE_TERMINATING_CODES) { + assertNotNull(code); + } + assertTrue(CCITTFaxEncoderStream.WHITE_NONTERMINATING_CODES.length == 40); + for (Code code : CCITTFaxEncoderStream.WHITE_NONTERMINATING_CODES) { + assertNotNull(code); + } + assertTrue(CCITTFaxEncoderStream.BLACK_TERMINATING_CODES.length == 64); + for (Code code : CCITTFaxEncoderStream.BLACK_TERMINATING_CODES) { + assertNotNull(code); + } + assertTrue(CCITTFaxEncoderStream.BLACK_NONTERMINATING_CODES.length == 40); + for (Code code : CCITTFaxEncoderStream.BLACK_NONTERMINATING_CODES) { + assertNotNull(code); + } + } +}