diff --git a/imageio/imageio-webp/src/main/java/com/twelvemonkeys/imageio/plugins/webp/lossless/VP8LDecoder.java b/imageio/imageio-webp/src/main/java/com/twelvemonkeys/imageio/plugins/webp/lossless/VP8LDecoder.java
index 08415316..3b4621e8 100644
--- a/imageio/imageio-webp/src/main/java/com/twelvemonkeys/imageio/plugins/webp/lossless/VP8LDecoder.java
+++ b/imageio/imageio-webp/src/main/java/com/twelvemonkeys/imageio/plugins/webp/lossless/VP8LDecoder.java
@@ -57,6 +57,25 @@ import static java.lang.Math.max;
* @author Harald Kuhr
*/
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 LSBBitReader lsbBitReader;
@@ -95,8 +114,155 @@ public final class VP8LDecoder {
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.
-// 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 transforms) throws IOException {