mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 12:05:29 -04:00
Implement actual decoding including resolving backward refs and cache
This commit is contained in:
parent
0ed0246762
commit
fafa58b718
@ -57,6 +57,25 @@ import static java.lang.Math.max;
|
|||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
*/
|
*/
|
||||||
public final class VP8LDecoder {
|
public final class VP8LDecoder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for decoding backward references
|
||||||
|
* Upper 4Bits are y distance, lower 4 Bits are 8 minus x distance
|
||||||
|
*/
|
||||||
|
private final static byte[] DISTANCES = {
|
||||||
|
0x18, 0x07, 0x17, 0x19, 0x28, 0x06, 0x27, 0x29, 0x16, 0x1a,
|
||||||
|
0x26, 0x2a, 0x38, 0x05, 0x37, 0x39, 0x15, 0x1b, 0x36, 0x3a,
|
||||||
|
0x25, 0x2b, 0x48, 0x04, 0x47, 0x49, 0x14, 0x1c, 0x35, 0x3b,
|
||||||
|
0x46, 0x4a, 0x24, 0x2c, 0x58, 0x45, 0x4b, 0x34, 0x3c, 0x03,
|
||||||
|
0x57, 0x59, 0x13, 0x1d, 0x56, 0x5a, 0x23, 0x2d, 0x44, 0x4c,
|
||||||
|
0x55, 0x5b, 0x33, 0x3d, 0x68, 0x02, 0x67, 0x69, 0x12, 0x1e,
|
||||||
|
0x66, 0x6a, 0x22, 0x2e, 0x54, 0x5c, 0x43, 0x4d, 0x65, 0x6b,
|
||||||
|
0x32, 0x3e, 0x78, 0x01, 0x77, 0x79, 0x53, 0x5d, 0x11, 0x1f,
|
||||||
|
0x64, 0x6c, 0x42, 0x4e, 0x76, 0x7a, 0x21, 0x2f, 0x75, 0x7b,
|
||||||
|
0x31, 0x3f, 0x63, 0x6d, 0x52, 0x5e, 0x00, 0x74, 0x7c, 0x41,
|
||||||
|
0x4f, 0x10, 0x20, 0x62, 0x6e, 0x30, 0x73, 0x7d, 0x51, 0x5f,
|
||||||
|
0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70
|
||||||
|
};
|
||||||
private final ImageInputStream imageInput;
|
private final ImageInputStream imageInput;
|
||||||
private final LSBBitReader lsbBitReader;
|
private final LSBBitReader lsbBitReader;
|
||||||
|
|
||||||
@ -95,8 +114,155 @@ public final class VP8LDecoder {
|
|||||||
colorCache = new ColorCache(colorCacheBits);
|
colorCache = new ColorCache(colorCacheBits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//If multiple indices packed into one pixel xSize is different from raster width
|
||||||
|
WritableRaster writableChild = raster.createWritableChild(0, 0, xSize, ySize, 0, 0, null);
|
||||||
|
|
||||||
// Use the Huffman trees to decode the LZ77 encoded data.
|
// Use the Huffman trees to decode the LZ77 encoded data.
|
||||||
// decodeImageData(raster, )
|
decodeImage(writableChild, huffmanInfo, colorCache);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void decodeImage(WritableRaster raster, HuffmanInfo huffmanInfo, ColorCache colorCache) throws IOException {
|
||||||
|
int width = raster.getWidth();
|
||||||
|
int height = raster.getHeight();
|
||||||
|
|
||||||
|
int huffmanMask = huffmanInfo.metaCodeBits == 0 ? -1 : ((1 << huffmanInfo.metaCodeBits) - 1);
|
||||||
|
HuffmanCodeGroup curCodeGroup = huffmanInfo.huffmanGroups[0];
|
||||||
|
|
||||||
|
byte[] rgba = new byte[4];
|
||||||
|
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
|
||||||
|
if ((x & huffmanMask) == 0 && huffmanInfo.huffmanMetaCodes != null) {
|
||||||
|
//Crossed border into new metaGroup
|
||||||
|
int index = huffmanInfo.huffmanMetaCodes.getSample(x >> huffmanInfo.metaCodeBits, y >> huffmanInfo.metaCodeBits, 0);
|
||||||
|
curCodeGroup = huffmanInfo.huffmanGroups[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
short code = curCodeGroup.mainCode.readSymbol(lsbBitReader);
|
||||||
|
|
||||||
|
if (code < 256) { //Literal
|
||||||
|
decodeLiteral(raster, colorCache, curCodeGroup, rgba, y, x, code);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (code < 256 + 24) { //backward reference
|
||||||
|
|
||||||
|
int length = decodeBwRef(raster, colorCache, width, curCodeGroup, rgba, code, x, y);
|
||||||
|
|
||||||
|
//Decrement one because for loop already increments by one
|
||||||
|
x--;
|
||||||
|
y = y + ((x + length) / width);
|
||||||
|
x = (x + length) % width;
|
||||||
|
|
||||||
|
|
||||||
|
//Reset Huffman meta group
|
||||||
|
if (y < height && x < width && huffmanInfo.huffmanMetaCodes != null) {
|
||||||
|
int index = huffmanInfo.huffmanMetaCodes.getSample(x >> huffmanInfo.metaCodeBits, y >> huffmanInfo.metaCodeBits, 0);
|
||||||
|
curCodeGroup = huffmanInfo.huffmanGroups[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else { //colorCache
|
||||||
|
//Color cache should never be null here
|
||||||
|
assert colorCache != null;
|
||||||
|
decodeCached(raster, colorCache, rgba, y, x, code);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void decodeCached(WritableRaster raster, ColorCache colorCache, byte[] rgba, int y, int x, short code) {
|
||||||
|
|
||||||
|
int argb = colorCache.lookup(code - 256 - 24);
|
||||||
|
|
||||||
|
rgba[0] = (byte) ((argb >> 16) & 0xff);
|
||||||
|
rgba[1] = (byte) ((argb >> 8) & 0xff);
|
||||||
|
rgba[2] = (byte) (argb & 0xff);
|
||||||
|
rgba[3] = (byte) (argb >>> 24);
|
||||||
|
|
||||||
|
raster.setDataElements(x, y, rgba);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void decodeLiteral(WritableRaster raster, ColorCache colorCache, HuffmanCodeGroup curCodeGroup, byte[] rgba, int y, int x, short code) throws IOException {
|
||||||
|
byte red = (byte) curCodeGroup.redCode.readSymbol(lsbBitReader);
|
||||||
|
byte blue = (byte) curCodeGroup.blueCode.readSymbol(lsbBitReader);
|
||||||
|
byte alpha = (byte) curCodeGroup.alphaCode.readSymbol(lsbBitReader);
|
||||||
|
rgba[0] = red;
|
||||||
|
rgba[1] = (byte) code;
|
||||||
|
rgba[2] = blue;
|
||||||
|
rgba[3] = alpha;
|
||||||
|
raster.setDataElements(x, y, rgba);
|
||||||
|
if (colorCache != null) {
|
||||||
|
colorCache.insert((alpha & 0xff) << 24 | (red & 0xff) << 16 | (code & 0xff) << 8 | (blue & 0xff));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int decodeBwRef(WritableRaster raster, ColorCache colorCache, int width, HuffmanCodeGroup curCodeGroup, byte[] rgba, short code, int x, int y) throws IOException {
|
||||||
|
int length = lz77decode(code - 256);
|
||||||
|
|
||||||
|
short distancePrefix = curCodeGroup.distanceCode.readSymbol(lsbBitReader);
|
||||||
|
int distanceCode = lz77decode(distancePrefix);
|
||||||
|
|
||||||
|
int xSrc, ySrc;
|
||||||
|
|
||||||
|
if (distanceCode > 120) {
|
||||||
|
//Linear distance
|
||||||
|
int distance = distanceCode - 120;
|
||||||
|
ySrc = y - (distance / width);
|
||||||
|
xSrc = x - (distance % width);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//See comment of distances array
|
||||||
|
xSrc = x - (8 - (DISTANCES[distanceCode - 1] & 0xf));
|
||||||
|
ySrc = y - (DISTANCES[distanceCode - 1] >> 4);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xSrc < 0) {
|
||||||
|
ySrc--;
|
||||||
|
xSrc += width;
|
||||||
|
}
|
||||||
|
else if (xSrc >= width) {
|
||||||
|
xSrc -= width;
|
||||||
|
ySrc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int l = length; l > 0; x++, l--) {
|
||||||
|
//Check length and xSrc, ySrc not falling outside raster? (Should not occur if image is correct)
|
||||||
|
|
||||||
|
if (x == width) {
|
||||||
|
x = 0;
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
raster.getDataElements(xSrc++, ySrc, rgba);
|
||||||
|
raster.setDataElements(x, y, rgba);
|
||||||
|
if (xSrc == width) {
|
||||||
|
xSrc = 0;
|
||||||
|
ySrc++;
|
||||||
|
}
|
||||||
|
if (colorCache != null) {
|
||||||
|
colorCache.insert((rgba[3] & 0xff) << 24 | (rgba[0] & 0xff) << 16 | (rgba[1] & 0xff) << 8 | (rgba[2] & 0xff));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int lz77decode(int prefixCode) throws IOException {
|
||||||
|
//According to specification
|
||||||
|
|
||||||
|
if (prefixCode < 4) {
|
||||||
|
return prefixCode + 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int extraBits = (prefixCode - 2) >> 1;
|
||||||
|
int offset = (2 + (prefixCode & 1)) << extraBits;
|
||||||
|
return offset + (int) lsbBitReader.readBits(extraBits) + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int readTransform(int xSize, int ySize, List<Transform> transforms) throws IOException {
|
private int readTransform(int xSize, int ySize, List<Transform> transforms) throws IOException {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user