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

View File

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