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 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;
|
||||||
|
@ -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];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user