CITT Group 3/4 Support - some cleanup, group 3 /T.4 should work now

This commit is contained in:
Schmidor 2015-07-02 23:35:19 +02:00
parent a2042e75bf
commit 2b22b02523
3 changed files with 745 additions and 735 deletions

View File

@ -56,8 +56,8 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
private final int fillOrder;
private final int type;
private final int[] changesReferenceRow;
private final int[] changesCurrentRow;
private int[] changesReferenceRow;
private int[] changesCurrentRow;
private int changesReferenceRowCount;
private int changesCurrentRowCount;
@ -141,41 +141,48 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
}
private void decode2D() throws IOException {
changesReferenceRowCount = changesCurrentRowCount;
int[] tmp = changesCurrentRow;
changesCurrentRow = changesReferenceRow;
changesReferenceRow = tmp;
if (changesReferenceRowCount == 0) {
changesReferenceRowCount = 3;
changesReferenceRow[0] = columns;
changesReferenceRow[1] = columns;
changesReferenceRow[2] = columns;
}
boolean white = true;
int index = 0;
int index = -1;
changesCurrentRowCount = 0;
int ref = 0;
mode: while (index < columns) {
// read mode
N n = codeTree.root;
Node n = codeTree.root;
while (true) {
n = n.walk(readBit());
if (n == null) {
continue mode;
} else if (n.isLeaf) {
switch (n.value) {
case VALUE_HMODE:
System.out.print("|H=");
int runLength = 0;
runLength = decodeRun(white ? whiteRunTree : blackRunTree);
changesCurrentRow[changesCurrentRowCount++] = index;
index += runLength;
System.out.print(runLength + (white? "W" : "B"));
changesCurrentRow[changesCurrentRowCount++] = index;
runLength = decodeRun(white ? blackRunTree : whiteRunTree);
changesCurrentRow[changesCurrentRowCount++] = index;
index += runLength;
System.out.print(runLength + (!white? "W" : "B"));
changesCurrentRow[changesCurrentRowCount++] = index;
break;
case VALUE_PASSMODE:
System.out.print("|P");
ref++;
// TODO
index = changesReferenceRow[getNextChangingElement(index, white) + 1];
break;
default:
System.out.print("|V" + n.value);
index = changesReferenceRow[ref] + n.value;
changesCurrentRow[ref] = index;
if(changesCurrentRow[ref] <= index) ref++; //TODO
// Vertical mode (-3 to 3)
index = changesReferenceRow[getNextChangingElement(index, white)] + n.value;
changesCurrentRow[changesCurrentRowCount] = index;
changesCurrentRowCount++;
white = !white;
break;
@ -186,6 +193,17 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
}
}
private int getNextChangingElement(int a0, boolean white) {
int start = white ? 0 : 1;
for (int i = start; i < changesReferenceRowCount; i += 2) {
if (a0 < changesReferenceRow[i]) {
return i;
}
}
return 0;
}
private void decodeRowType2() throws IOException {
resetBuffer();
decode1D();
@ -194,33 +212,26 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
private void decodeRowType4() throws IOException {
eof: while (true) {
// read till next EOL code
N n = eolOnlyTree.root;
Node n = eolOnlyTree.root;
while (true) {
N tmp = n;
Node tmp = n;
n = n.walk(readBit());
if (n == null)
continue eof;
if (n.isLeaf) {
System.out.print("|EOL");
break eof;
}
if(tmp == n) System.out.print("F");
}
}
boolean k = optionG32D ? readBit() : true;
System.out.print("|k=" + k);
if (k) {
decode1D();
changesReferenceRowCount = changesCurrentRowCount;
System.arraycopy(changesCurrentRow, 0, changesReferenceRow, 0, changesCurrentRowCount);
} else {
decode2D();
}
}
private void decodeRowType6() throws IOException {
changesReferenceRowCount = 1;
changesReferenceRow[0] = columns;
decode2D();
}
@ -243,24 +254,34 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
if (i != changesCurrentRowCount) {
nextChange = changesCurrentRow[i];
}
if (nextChange > columns) {
nextChange = columns;
}
int byteIndex = index / 8;
while (index % 8 != 0 && (nextChange - index) > 0) {
decodedRow[index++ / 8] |= (white ? 1 << 8 - (index % 8) : 0);
decodedRow[byteIndex] |= (white ? 1 << (7 - ((index) % 8)) : 0);
index++;
}
if (index % 8 == 0) {
byteIndex = index / 8;
final byte value = (byte) (white ? 0xff : 0x00);
while ((nextChange - index) > 7) {
decodedRow[index / 8] = value;
decodedRow[byteIndex] = value;
index += 8;
++byteIndex;
}
}
while ((nextChange - index) > 0) {
if (index % 8 == 0)
decodedRow[(index + 1) / 8] = 0;
decodedRow[index++ / 8] |= (white ? 1 << 8 - (index % 8) : 0);
while ((nextChange - index) > 0) {
if (index % 8 == 0) {
decodedRow[byteIndex] = 0;
}
decodedRow[byteIndex] |= (white ? 1 << (7 - ((index) % 8)) : 0);
index++;
}
white = !white;
@ -276,7 +297,7 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
private int decodeRun(Tree tree) throws IOException {
int total = 0;
N n = tree.root;
Node n = tree.root;
while (true) {
boolean bit = readBit();
n = n.walk(bit);
@ -400,15 +421,15 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
throw new IOException("mark/reset not supported");
}
static class N {
N left;
N right;
static class Node {
Node left;
Node right;
int value; // > 63 non term.
boolean canBeFill = false;
boolean isLeaf = false;
void set(boolean next, N node) {
void set(boolean next, Node node) {
if (!next) {
left = node;
} else {
@ -416,7 +437,7 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
}
}
N walk(boolean next) {
Node walk(boolean next) {
return next ? right : left;
}
@ -427,16 +448,16 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
}
static class Tree {
N root = new N();
Node root = new Node();
void fill(int depth, int path, int value) throws IOException {
N current = root;
Node current = root;
for (int i = 0; i < depth; i++) {
int bitPos = depth - 1 - i;
boolean isSet = ((path >> bitPos) & 1) == 1;
N next = current.walk(isSet);
Node next = current.walk(isSet);
if (next == null) {
next = new N();
next = new Node();
if (i == depth - 1) {
next.value = value;
next.isLeaf = true;
@ -452,17 +473,17 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
}
}
void fill(int depth, int path, N node) throws IOException {
N current = root;
void fill(int depth, int path, Node node) throws IOException {
Node current = root;
for (int i = 0; i < depth; i++) {
int bitPos = depth - 1 - i;
boolean isSet = ((path >> bitPos) & 1) == 1;
N next = current.walk(isSet);
Node next = current.walk(isSet);
if (next == null) {
if (i == depth - 1) {
next = node;
} else {
next = new N();
next = new Node();
}
if (path == 0)
next.canBeFill = true;
@ -578,8 +599,8 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
{ // 12 bits
1984, 2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496, 2560, } };
final static N EOL;
final static N FILL;
final static Node EOL;
final static Node FILL;
final static Tree blackRunTree;
final static Tree whiteRunTree;
final static Tree eolOnlyTree;
@ -591,10 +612,10 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
final static int VALUE_HMODE = -4000;
static {
EOL = new N();
EOL = new Node();
EOL.isLeaf = true;
EOL.value = VALUE_EOL;
FILL = new N();
FILL = new Node();
FILL.value = VALUE_FILL;
FILL.left = FILL;
FILL.right = EOL;

View File

@ -50,40 +50,30 @@ import static org.junit.Assert.*;
*/
public class CCITTFaxDecoderStreamTest {
static final byte[] DATA_G3_1D = { 0x00, 0x18, // 000000000001|1000| EOL|3W|
0x4E, 0x00, // 010|0111|000000000 1B|2W|EOL
0x30, (byte) 0x9C, // 001|1000|010|0111|00 |3W|1B|2W|EOL
0x00, 0x61, // 0000000001|1000|01 |3W|1B
0x38, 0x00, // 0|0111|00000000000 |2W|EOL
(byte) 0xBE, (byte) 0xE0 }; // 1|0111|11|0111|00000 |2W|2B|2W|5F
// group3_1d.tif: EOL|3W|1B|2W|EOL|3W|1B|2W|EOL|3W|1B|2W|EOL|2W|2B|2W|5*F
static final byte[] DATA_G3_1D = { 0x00, 0x18, 0x4E, 0x00, 0x30, (byte) 0x9C, 0x00, 0x61, 0x38, 0x00, (byte) 0xBE,
(byte) 0xE0 };
// group3_1d_fill.tif
static final byte[] DATA_G3_1D_FILL = { 0x00, 0x01, (byte) 0x84, (byte) 0xE0, 0x01, (byte) 0x84, (byte) 0xE0, 0x01,
(byte) 0x84, (byte) 0xE0, 0x1, 0x7D, (byte) 0xC0 };
static final byte[] DATA_G3_2D = {
0x00, 0x1C, // 000000000001|1|100 EOL|1|3W
0x27, 0x00, // 0|010|0111|00000000 |1B|2W|EOL
0x17, 0x00, // 0001|0|1|1|1|00000000 |0|V|V|V|EOL
0x1C, 0x27, // 0001|1|1000|010|0111| |1|3W|1B|2W|
0x00, 0x12, // 000000000001|0|010| EOL|0|V-1|
(byte) 0xC0 }; // 1|1|000000 V|V|6F
// group3_2d.tif: EOL|k=1|3W|1B|2W|EOL|k=0|V|V|V|EOL|k=1|3W|1B|2W|EOL|k=0|V-1|V|V|6*F
static final byte[] DATA_G3_2D = { 0x00, 0x1C, 0x27, 0x00, 0x17, 0x00, 0x1C, 0x27, 0x00, 0x12, (byte) 0xC0 };
// group3_2d_fill.tif
static final byte[] DATA_G3_2D_FILL = { 0x00, 0x01, (byte) 0xC2, 0x70, 0x01, 0x70, 0x01, (byte) 0xC2, 0x70, 0x01,
0x2C };
// EOF exception, not sure
static final byte[] DATA_G3_2D_lsb2msb = { 0x00, 0x38, (byte) 0xE4, 0x00, (byte) 0xE8, 0x00, 0x38, (byte) 0xE4,
0x00, 0x48, 0x03 };
static final byte[] DATA_G4 = {
0x04, 0x17, // 0000 0100 0001 01|11
(byte) 0xF5, (byte) 0x80, // 1|111| 0101 1000 0000
0x08, 0x00, // 0000 1000 0000 0000
(byte) 0x80 }; // 1000 0000
// group4.tif:
// Line 1: V-3, V-2, V0
// Line 2: V0 V0 V0
// Line 3: V0 V0 V0
// Line 4: V-1, V0, V0 EOL EOL
static final byte[] DATA_G4 = { 0x04, 0x17, (byte) 0xF5, (byte) 0x80, 0x08, 0x00, (byte) 0x80 };
// TODO: Better tests (full A4 width scan lines?)
@ -192,8 +182,7 @@ public class CCITTFaxDecoderStreamTest {
@Test
public void testDecodeType4() throws IOException {
InputStream stream = new CCITTFaxDecoderStream(new ByteArrayInputStream(DATA_G4), 6,
TIFFExtension.COMPRESSION_CCITT_T6, 1,
0L);
TIFFExtension.COMPRESSION_CCITT_T6, 1, 0L);
byte[] imageData = ((DataBufferByte) image.getData().getDataBuffer()).getData();
byte[] bytes = new byte[imageData.length];