mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 04:25:29 -04:00
CITT Group 3/4 Support - some cleanup, group 3 /T.4 should work now
This commit is contained in:
parent
a2042e75bf
commit
2b22b02523
@ -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;
|
||||
|
@ -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];
|
||||
|
Loading…
x
Reference in New Issue
Block a user