mirror of
https://github.com/AppleCommander/ShrinkItArchive.git
synced 2024-12-22 02:31:00 +00:00
Added RleOutputStream for RLE compression.
This commit is contained in:
parent
c188bf6c2b
commit
006eadf410
82
src/com/webcodepro/shrinkit/io/RleOutputStream.java
Normal file
82
src/com/webcodepro/shrinkit/io/RleOutputStream.java
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package com.webcodepro.shrinkit.io;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The RleOutputStream handles the NuFX RLE data stream.
|
||||||
|
* This data stream is byte oriented. If a repeat occurs,
|
||||||
|
* the data stream will contain the marker byte, byte to
|
||||||
|
* repeat, and the number of repeats (zero based; ie, $00=1,
|
||||||
|
* $01=2, ... $ff=256). The default marker is $DB.
|
||||||
|
*
|
||||||
|
* @author robgreene@users.sourceforge.net
|
||||||
|
*/
|
||||||
|
public class RleOutputStream extends OutputStream {
|
||||||
|
private OutputStream os;
|
||||||
|
private int escapeChar;
|
||||||
|
private int repeatedByte;
|
||||||
|
private int numBytes = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an RLE output stream with the default marker byte.
|
||||||
|
*/
|
||||||
|
public RleOutputStream(OutputStream bs) {
|
||||||
|
this(bs, 0xdb);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create an RLE output stream with the specified marker byte.
|
||||||
|
*/
|
||||||
|
public RleOutputStream(OutputStream os, int escapeChar) {
|
||||||
|
this.os = os;
|
||||||
|
this.escapeChar = escapeChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the next byte to the output stream.
|
||||||
|
*/
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
if (numBytes == -1) {
|
||||||
|
repeatedByte = b;
|
||||||
|
numBytes++;
|
||||||
|
} else if (repeatedByte == b) {
|
||||||
|
numBytes++;
|
||||||
|
if (numBytes > 255) {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
flush();
|
||||||
|
repeatedByte = b;
|
||||||
|
numBytes++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush out any remaining data.
|
||||||
|
* If we only have 1 byte and it is <em>not</em> the repeated
|
||||||
|
* byte, we can just dump that byte. Otherwise, we need to
|
||||||
|
* write out the escape character, the repeated byte, and
|
||||||
|
* the number of bytes.
|
||||||
|
*/
|
||||||
|
public void flush() throws IOException {
|
||||||
|
if (numBytes != -1) {
|
||||||
|
if (numBytes == 0 && escapeChar != repeatedByte) {
|
||||||
|
os.write(repeatedByte);
|
||||||
|
} else {
|
||||||
|
os.write(escapeChar);
|
||||||
|
os.write(repeatedByte);
|
||||||
|
os.write(numBytes);
|
||||||
|
}
|
||||||
|
numBytes = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close out the data stream. Makes sure the repeate buffer
|
||||||
|
* is flushed.
|
||||||
|
*/
|
||||||
|
public void close() throws IOException {
|
||||||
|
flush();
|
||||||
|
os.close();
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package com.webcodepro.shrinkit.io;
|
package com.webcodepro.shrinkit.io;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -11,23 +12,59 @@ import java.io.OutputStream;
|
|||||||
* @author robgreene@users.sourceforge.net
|
* @author robgreene@users.sourceforge.net
|
||||||
*/
|
*/
|
||||||
public class RleTest extends TestCaseHelper {
|
public class RleTest extends TestCaseHelper {
|
||||||
|
/**
|
||||||
|
* Test the RleInputStream to verify decoding.
|
||||||
|
*/
|
||||||
public void testInputStream() throws IOException {
|
public void testInputStream() throws IOException {
|
||||||
InputStream is = new RleInputStream(new LittleEndianByteInputStream(getPatternFileRle()));
|
InputStream is = new RleInputStream(new ByteArrayInputStream(getPatternFileRle()));
|
||||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
copy(is,os);
|
copy(is,os);
|
||||||
byte[] expected = getPatternFileUncompressed();
|
byte[] expected = getPatternFileUncompressed();
|
||||||
byte[] actual = os.toByteArray();
|
byte[] actual = os.toByteArray();
|
||||||
assertEquals(expected, actual);
|
assertEquals(expected, actual);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Test the RleOutputStream to verify compression.
|
||||||
|
*/
|
||||||
|
public void testOutputStream() throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
RleOutputStream os = new RleOutputStream(baos);
|
||||||
|
ByteArrayInputStream is = new ByteArrayInputStream(getPatternFileUncompressed());
|
||||||
|
copy(is,os);
|
||||||
|
byte[] expected = getPatternFileRle();
|
||||||
|
byte[] actual = baos.toByteArray();
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Test the RleOutputStream with the escape character to ensure it <em>always</em> is encoded.
|
||||||
|
* Note that a file with lots of 0xdb codes will grow.
|
||||||
|
*/
|
||||||
|
public void testOutputStreamEscapeCharacter() throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
RleOutputStream os = new RleOutputStream(baos);
|
||||||
|
ByteArrayInputStream is = new ByteArrayInputStream(new byte[] { (byte)0xdb, (byte)0xdb, 0x00, (byte)0xdb });
|
||||||
|
copy(is,os);
|
||||||
|
byte[] expected = new byte[] { (byte)0xdb, (byte)0xdb, 0x01, 0x00, (byte)0xdb, (byte)0xdb, 0x00 };
|
||||||
|
byte[] actual = baos.toByteArray();
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy the input stream to the output stream.
|
||||||
|
*/
|
||||||
private void copy(InputStream is, OutputStream os) throws IOException {
|
private void copy(InputStream is, OutputStream os) throws IOException {
|
||||||
int b = is.read();
|
int b = is.read();
|
||||||
while (b != -1) {
|
while (b != -1) {
|
||||||
os.write(b);
|
os.write(b);
|
||||||
b = is.read();
|
b = is.read();
|
||||||
}
|
}
|
||||||
|
is.close();
|
||||||
|
os.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This the RLE compressed pattern file that happens to not compress via LZW.
|
||||||
|
*/
|
||||||
private byte[] getPatternFileRle() {
|
private byte[] getPatternFileRle() {
|
||||||
return new byte[] {
|
return new byte[] {
|
||||||
(byte)0xdb, 0x01, (byte)0xfd,
|
(byte)0xdb, 0x01, (byte)0xfd,
|
||||||
@ -49,6 +86,9 @@ public class RleTest extends TestCaseHelper {
|
|||||||
(byte)0xdb, 0x11, (byte)0x97
|
(byte)0xdb, 0x11, (byte)0x97
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This is the uncompressed pattern file that happens to not compress via LZW.
|
||||||
|
*/
|
||||||
private byte[] getPatternFileUncompressed() {
|
private byte[] getPatternFileUncompressed() {
|
||||||
byte[] data = new byte[4096];
|
byte[] data = new byte[4096];
|
||||||
int value = 0x01;
|
int value = 0x01;
|
||||||
|
Loading…
Reference in New Issue
Block a user