All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.algart.matrices.tiff.codecs.CCITTFaxEncoderStreamAdapted Maven / Gradle / Ivy

There is a newer version: 1.3.7
Show newest version
package net.algart.matrices.tiff.codecs;

import java.io.IOException;
import java.io.OutputStream;

/**
 * This is an adapted copy of com.twelvemonkeys.imageio.plugins.tiff.CCITTFaxEncoderStream class
 * from TwelveMonkeys 3.11.
 * Below (after the class declaration) is the copyright notice, copied from the source code of this class.
 * - Daniel Alievsky.
 *
 * 

CCITT Modified Huffman RLE, Group 3 (T4) and Group 4 (T6) fax compression. * * @author Oliver Schmidtmer * @author last modified by $Author$ * @version $Id$ */ final class CCITTFaxEncoderStreamAdapted extends OutputStream { // (It is placed here to avoid autocorrection by IntelliJ IDEA) /* * 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 of the copyright holder 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 HOLDER 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. */ private int currentBufferLength = 0; private final byte[] inputBuffer; private final int inputBufferLength; private int columns; private int rows; private int[] changesCurrentRow; private int[] changesReferenceRow; private int currentRow = 0; private int changesCurrentRowLength = 0; private int changesReferenceRowLength = 0; private byte outputBuffer = 0; private byte outputBufferBitLength = 0; private int type; private int fillOrder; private boolean optionG32D; private boolean optionG3Fill; private boolean optionUncompressed; private OutputStream stream; public CCITTFaxEncoderStreamAdapted(final OutputStream stream, final int columns, final int rows, final int type, final int fillOrder, final long options) { this.stream = stream; this.type = type; this.columns = columns; this.rows = rows; this.fillOrder = fillOrder; this.changesReferenceRow = new int[columns]; this.changesCurrentRow = new int[columns]; switch (type) { case TinyTwelveMonkey.COMPRESSION_CCITT_T4: optionG32D = (options & TinyTwelveMonkey.GROUP3OPT_2DENCODING) != 0; optionG3Fill = (options & TinyTwelveMonkey.GROUP3OPT_FILLBITS) != 0; optionUncompressed = (options & TinyTwelveMonkey.GROUP3OPT_UNCOMPRESSED) != 0; break; case TinyTwelveMonkey.COMPRESSION_CCITT_T6: optionUncompressed = (options & TinyTwelveMonkey.GROUP4OPT_UNCOMPRESSED) != 0; break; } inputBufferLength = (columns + 7) / 8; inputBuffer = new byte[inputBufferLength]; TinyTwelveMonkey.isTrue(!optionUncompressed, optionUncompressed, "CCITT GROUP 3/4 OPTION UNCOMPRESSED is not supported"); } @Override public void write(int b) throws IOException { inputBuffer[currentBufferLength] = (byte) b; currentBufferLength++; if (currentBufferLength == inputBufferLength) { encodeRow(); currentBufferLength = 0; } } @Override public void flush() throws IOException { stream.flush(); } @Override public void close() throws IOException { stream.close(); } private void encodeRow() throws IOException { currentRow++; int[] tmp = changesReferenceRow; changesReferenceRow = changesCurrentRow; changesCurrentRow = tmp; changesReferenceRowLength = changesCurrentRowLength; changesCurrentRowLength = 0; int index = 0; boolean white = true; while (index < columns) { int byteIndex = index / 8; int bit = index % 8; if ((((inputBuffer[byteIndex] >> (7 - bit)) & 1) == 1) == (white)) { changesCurrentRow[changesCurrentRowLength] = index; changesCurrentRowLength++; white = !white; } index++; } switch (type) { case TinyTwelveMonkey.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE: encodeRowType2(); break; case TinyTwelveMonkey.COMPRESSION_CCITT_T4: encodeRowType4(); break; case TinyTwelveMonkey.COMPRESSION_CCITT_T6: encodeRowType6(); break; } if (currentRow == rows) { if (type == TinyTwelveMonkey.COMPRESSION_CCITT_T6) { writeEOL(); writeEOL(); } fill(); } } private void encodeRowType2() throws IOException { encode1D(); fill(); } private void encodeRowType4() throws IOException { writeEOL(); if (optionG32D) { // do k=1 only on first line. Detect first line by missing reference // line. if (changesReferenceRowLength == 0) { write(1, 1); encode1D(); } else { write(0, 1); encode2D(); } } else { encode1D(); } if (optionG3Fill) { fill(); } } private void encodeRowType6() throws IOException { encode2D(); } private void encode1D() throws IOException { int index = 0; boolean white = true; while (index < columns) { int[] nextChanges = getNextChanges(index, white); int runLength = nextChanges[0] - index; writeRun(runLength, white); index += runLength; white = !white; } } private int[] getNextChanges(int pos, boolean white) { int[] result = new int[] {columns, columns}; for (int i = 0; i < changesCurrentRowLength; i++) { if (pos < changesCurrentRow[i] || (pos == 0 && white)) { result[0] = changesCurrentRow[i]; if ((i + 1) < changesCurrentRowLength) { result[1] = changesCurrentRow[i + 1]; } break; } } return result; } private void writeRun(int runLength, boolean white) throws IOException { int nonterm = runLength / 64; 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); nonterm -= codes.length; } else { write(codes[nonterm - 1].code, codes[nonterm - 1].length); nonterm = 0; } } Code c = white ? WHITE_TERMINATING_CODES[runLength % 64] : BLACK_TERMINATING_CODES[runLength % 64]; write(c.code, c.length); } private void encode2D() throws IOException { boolean white = true; int index = 0; // a0 while (index < columns) { int[] nextChanges = getNextChanges(index, white); // a1, a2 int[] nextRefs = getNextRefChanges(index, white); // b1, b2 int difference = nextChanges[0] - nextRefs[0]; if (nextChanges[0] > nextRefs[1]) { // PMODE write(1, 4); index = nextRefs[1]; } else if (difference > 3 || difference < -3) { // HMODE write(1, 3); writeRun(nextChanges[0] - index, white); writeRun(nextChanges[1] - nextChanges[0], !white); index = nextChanges[1]; } 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; index = nextRefs[0] + difference; } } } private int[] getNextRefChanges(int a0, boolean white) { int[] result = new int[] {columns, columns}; for (int i = (white ? 0 : 1); i < changesReferenceRowLength; i += 2) { if (changesReferenceRow[i] > a0 || (a0 == 0 && i == 0)) { result[0] = changesReferenceRow[i]; if ((i + 1) < changesReferenceRowLength) { result[1] = changesReferenceRow[i + 1]; } break; } } return result; } private void write(int code, int codeLength) throws IOException { for (int i = 0; i < codeLength; i++) { boolean codeBit = ((code >> (codeLength - i - 1)) & 1) == 1; if (fillOrder == TinyTwelveMonkey.FILL_LEFT_TO_RIGHT) { outputBuffer |= (codeBit ? 1 << (7 - ((outputBufferBitLength) % 8)) : 0); } else { outputBuffer |= (codeBit ? 1 << (((outputBufferBitLength) % 8)) : 0); } outputBufferBitLength++; if (outputBufferBitLength == 8) { stream.write(outputBuffer); clearOutputBuffer(); } } } private void writeEOL() throws IOException { if (optionG3Fill) { // Fill up so EOL ends on a byte-boundary while (outputBufferBitLength != 4) { write(0, 1); } } write(1, 12); } private void fill() throws IOException { if (outputBufferBitLength != 0) { stream.write(outputBuffer); } clearOutputBuffer(); } private void clearOutputBuffer() { outputBuffer = 0; outputBufferBitLength = 0; } public static class Code { private Code(int code, int length) { this.code = code; this.length = length; } final int code; final int length; } public static final Code[] WHITE_TERMINATING_CODES; public static final Code[] WHITE_NONTERMINATING_CODES; public static final Code[] BLACK_TERMINATING_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 < CCITTFaxDecoderStreamAdapted.WHITE_CODES.length; i++) { int bitLength = i + 4; for (int j = 0; j < CCITTFaxDecoderStreamAdapted.WHITE_CODES[i].length; j++) { int value = CCITTFaxDecoderStreamAdapted.WHITE_RUN_LENGTHS[i][j]; int code = CCITTFaxDecoderStreamAdapted.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 < CCITTFaxDecoderStreamAdapted.BLACK_CODES.length; i++) { int bitLength = i + 2; for (int j = 0; j < CCITTFaxDecoderStreamAdapted.BLACK_CODES[i].length; j++) { int value = CCITTFaxDecoderStreamAdapted.BLACK_RUN_LENGTHS[i][j]; int code = CCITTFaxDecoderStreamAdapted.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); } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy