mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-02 19:15:29 -04:00
#702 Fix NPE while reading an WebP animation without alpha
+ bonus cleanup
This commit is contained in:
parent
29dca0f124
commit
0160fb70f8
@ -31,29 +31,6 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.color.ICC_ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorConvertOp;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.color.ColorProfiles;
|
||||
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
||||
@ -68,6 +45,25 @@ import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||
import com.twelvemonkeys.imageio.util.RasterUtils;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
import java.awt.image.*;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
/**
|
||||
* WebPImageReader
|
||||
*/
|
||||
@ -216,7 +212,7 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
|
||||
switch (chunk) {
|
||||
case WebP.CHUNK_VP8_:
|
||||
//https://tools.ietf.org/html/rfc6386#section-9.1
|
||||
// https://tools.ietf.org/html/rfc6386#section-9.1
|
||||
int frameType = lsbBitReader.readBit(); // 0 = key frame, 1 = inter frame (not used in WebP)
|
||||
|
||||
if (frameType != 0) {
|
||||
@ -436,7 +432,6 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
if (header.containsANIM) {
|
||||
AnimationFrame frame = frames.get(imageIndex);
|
||||
imageInput.seek(frame.offset + 16);
|
||||
opaqueAlpha(destination.getAlphaRaster());
|
||||
readVP8Extended(destination, param, frame.offset + frame.length, frame.bounds.width, frame.bounds.height);
|
||||
}
|
||||
else {
|
||||
@ -466,8 +461,7 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
readVP8Extended(destination, param, streamEnd, header.width, header.height);
|
||||
}
|
||||
|
||||
private void readVP8Extended(BufferedImage destination, ImageReadParam param, long streamEnd, final int width,
|
||||
final int height) throws IOException {
|
||||
private void readVP8Extended(BufferedImage destination, ImageReadParam param, long streamEnd, final int width, final int height) throws IOException {
|
||||
while (imageInput.getStreamPosition() < streamEnd) {
|
||||
int nextChunk = imageInput.readInt();
|
||||
long chunkLength = imageInput.readUnsignedInt();
|
||||
@ -482,12 +476,11 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
switch (nextChunk) {
|
||||
case WebP.CHUNK_ALPH:
|
||||
readAlpha(destination, param, width, height);
|
||||
|
||||
break;
|
||||
|
||||
case WebP.CHUNK_VP8_:
|
||||
readVP8(RasterUtils.asByteRaster(destination.getRaster())
|
||||
.createWritableChild(0, 0, destination.getWidth(), destination.getHeight(), 0, 0, new int[]{ 0, 1, 2}), param);
|
||||
.createWritableChild(0, 0, destination.getWidth(), destination.getHeight(), 0, 0, new int[] {0, 1, 2}), param);
|
||||
break;
|
||||
|
||||
case WebP.CHUNK_VP8L:
|
||||
@ -519,8 +512,7 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
int reserved = (int) imageInput.readBits(2);
|
||||
if (reserved != 0) {
|
||||
// Spec says SHOULD be 0
|
||||
processWarningOccurred(
|
||||
String.format("Unexpected 'ALPH' chunk reserved value, expected 0: %d", reserved));
|
||||
processWarningOccurred(String.format("Unexpected 'ALPH' chunk reserved value, expected 0: %d", reserved));
|
||||
}
|
||||
|
||||
int preProcessing = (int) imageInput.readBits(2);
|
||||
@ -539,15 +531,14 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
readUncompressedAlpha(alphaRaster);
|
||||
break;
|
||||
case 1:
|
||||
WritableRaster tempRaster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
|
||||
destination.getWidth(), destination.getHeight(), 4,
|
||||
destination.getRaster().getBounds().getLocation());
|
||||
//Simulate header
|
||||
// Simulate header
|
||||
imageInput.seek(imageInput.getStreamPosition() - 5);
|
||||
|
||||
WritableRaster tempRaster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, destination.getWidth(), destination.getHeight(), 4, null);
|
||||
readVP8Lossless(tempRaster, param, width, height);
|
||||
//Copy from green (band 1) in temp to alpha in destination
|
||||
alphaRaster.setRect(tempRaster.createChild(0, 0, tempRaster.getWidth(),
|
||||
tempRaster.getHeight(), 0, 0, new int[] {1}));
|
||||
|
||||
// Copy from green (band 1) in temp to alpha in destination
|
||||
alphaRaster.setRect(tempRaster.createChild(0, 0, tempRaster.getWidth(), tempRaster.getHeight(), 0, 0, new int[] {1}));
|
||||
break;
|
||||
default:
|
||||
processWarningOccurred("Unknown WebP alpha compression: " + compression);
|
||||
@ -571,34 +562,21 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
return 0;
|
||||
case AlphaFiltering.HORIZONTAL:
|
||||
if (x == 0) {
|
||||
if (y == 0) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return alphaRaster.getSample(0, y - 1, 0);
|
||||
}
|
||||
return y == 0 ? 0 : alphaRaster.getSample(0, y - 1, 0);
|
||||
}
|
||||
else {
|
||||
return alphaRaster.getSample(x - 1, y, 0);
|
||||
}
|
||||
case AlphaFiltering.VERTICAL:
|
||||
if (y == 0) {
|
||||
if (x == 0) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return alphaRaster.getSample(x - 1, 0, 0);
|
||||
}
|
||||
return x == 0 ? 0 : alphaRaster.getSample(x - 1, 0, 0);
|
||||
}
|
||||
else {
|
||||
return alphaRaster.getSample(x, y - 1, 0);
|
||||
}
|
||||
case AlphaFiltering.GRADIENT:
|
||||
if (x == 0 && y == 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x == 0) {
|
||||
return alphaRaster.getSample(0, y - 1, 0);
|
||||
if (x == 0) {
|
||||
return y == 0 ? 0 : alphaRaster.getSample(0, y - 1, 0);
|
||||
}
|
||||
else if (y == 0) {
|
||||
return alphaRaster.getSample(x - 1, 0, 0);
|
||||
@ -608,7 +586,7 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
int top = alphaRaster.getSample(x, y - 1, 0);
|
||||
int topLeft = alphaRaster.getSample(x - 1, y - 1, 0);
|
||||
|
||||
return Math.max(0, Math.min(left + top - topLeft, 255));
|
||||
return max(0, min(left + top - topLeft, 255));
|
||||
}
|
||||
default:
|
||||
processWarningOccurred("Unknown WebP alpha filtering: " + filtering);
|
||||
@ -647,6 +625,7 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
private void readUncompressedAlpha(final WritableRaster alphaRaster) throws IOException {
|
||||
// Hardly used in practice, need to find a sample file
|
||||
processWarningOccurred("Uncompressed WebP alpha not implemented");
|
||||
|
@ -74,16 +74,13 @@ public final class VP8LDecoder {
|
||||
private final ImageInputStream imageInput;
|
||||
private final LSBBitReader lsbBitReader;
|
||||
|
||||
public VP8LDecoder(final ImageInputStream imageInput, final boolean debug) {
|
||||
public VP8LDecoder(final ImageInputStream imageInput, @SuppressWarnings("unused") final boolean debug) {
|
||||
this.imageInput = imageInput;
|
||||
lsbBitReader = new LSBBitReader(imageInput);
|
||||
this.lsbBitReader = new LSBBitReader(imageInput);
|
||||
}
|
||||
|
||||
public void readVP8Lossless(final WritableRaster raster, final boolean topLevel, ImageReadParam param, int width,
|
||||
int height) throws IOException {
|
||||
//https://github.com/webmproject/libwebp/blob/666bd6c65483a512fe4c2eb63fbc198b6fb4fae4/src/dec/vp8l_dec.c#L1114
|
||||
|
||||
//Skip past already read parts of header (signature, width, height, alpha, version) 5 Bytes in total
|
||||
public void readVP8Lossless(final WritableRaster raster, final boolean topLevel, ImageReadParam param, int width, int height) throws IOException {
|
||||
// Skip past already read parts of header (signature, width, height, alpha, version) 5 Bytes in total
|
||||
if (topLevel) {
|
||||
imageInput.seek(imageInput.getStreamPosition() + 5);
|
||||
}
|
||||
@ -100,6 +97,7 @@ public final class VP8LDecoder {
|
||||
int colorCacheBits = 0;
|
||||
if (lsbBitReader.readBit() == 1) {
|
||||
colorCacheBits = (int) lsbBitReader.readBits(4);
|
||||
|
||||
if (colorCacheBits < 1 || colorCacheBits > 11) {
|
||||
throw new IIOException("Corrupt WebP stream, colorCacheBits < 1 || > 11: " + colorCacheBits);
|
||||
}
|
||||
@ -116,16 +114,16 @@ public final class VP8LDecoder {
|
||||
|
||||
WritableRaster fullSizeRaster;
|
||||
WritableRaster decodeRaster;
|
||||
if (topLevel) {
|
||||
|
||||
if (topLevel) {
|
||||
Rectangle bounds = new Rectangle(width, height);
|
||||
fullSizeRaster = getRasterForDecoding(raster, param, bounds);
|
||||
|
||||
//If multiple indices packed into one pixel xSize is different from raster width
|
||||
// If multiple indices packed into one pixel xSize is different from raster width
|
||||
decodeRaster = fullSizeRaster.createWritableChild(0, 0, xSize, height, 0, 0, null);
|
||||
}
|
||||
else {
|
||||
//All recursive calls have Rasters of the correct sizes with origin (0, 0)
|
||||
// All recursive calls have Rasters of the correct sizes with origin (0, 0)
|
||||
decodeRaster = fullSizeRaster = raster;
|
||||
}
|
||||
|
||||
@ -137,7 +135,7 @@ public final class VP8LDecoder {
|
||||
}
|
||||
|
||||
if (fullSizeRaster != raster && param != null) {
|
||||
//Copy into destination raster with settings applied
|
||||
// Copy into destination raster with settings applied
|
||||
Rectangle sourceRegion = param.getSourceRegion();
|
||||
int sourceXSubsampling = param.getSourceXSubsampling();
|
||||
int sourceYSubsampling = param.getSourceYSubsampling();
|
||||
@ -150,18 +148,17 @@ public final class VP8LDecoder {
|
||||
}
|
||||
|
||||
if (sourceXSubsampling == 1 && sourceYSubsampling == 1) {
|
||||
//Only apply offset (and limit to requested region)
|
||||
// Only apply offset (and limit to requested region)
|
||||
raster.setRect(destinationOffset.x, destinationOffset.y, fullSizeRaster);
|
||||
}
|
||||
else {
|
||||
//Manual copy, more efficient way might exist
|
||||
// Manual copy, more efficient way might exist
|
||||
byte[] rgba = new byte[4];
|
||||
int xEnd = raster.getWidth() + raster.getMinX();
|
||||
int yEnd = raster.getHeight() + raster.getMinY();
|
||||
for (int xDst = destinationOffset.x, xSrc = sourceRegion.x + subsamplingXOffset;
|
||||
xDst < xEnd; xDst++, xSrc += sourceXSubsampling) {
|
||||
for (int yDst = destinationOffset.y, ySrc = sourceRegion.y + subsamplingYOffset;
|
||||
yDst < yEnd; yDst++, ySrc += sourceYSubsampling) {
|
||||
|
||||
for (int xDst = destinationOffset.x, xSrc = sourceRegion.x + subsamplingXOffset; xDst < xEnd; xDst++, xSrc += sourceXSubsampling) {
|
||||
for (int yDst = destinationOffset.y, ySrc = sourceRegion.y + subsamplingYOffset; yDst < yEnd; yDst++, ySrc += sourceYSubsampling) {
|
||||
fullSizeRaster.getDataElements(xSrc, ySrc, rgba);
|
||||
raster.setDataElements(xDst, yDst, rgba);
|
||||
}
|
||||
@ -171,14 +168,14 @@ public final class VP8LDecoder {
|
||||
}
|
||||
|
||||
private WritableRaster getRasterForDecoding(WritableRaster raster, ImageReadParam param, Rectangle bounds) {
|
||||
//If the ImageReadParam requires only a subregion of the image, and if the whole image does not fit into the
|
||||
// If the ImageReadParam requires only a subregion of the image, and if the whole image does not fit into the
|
||||
// Raster or subsampling is requested, we need a temporary Raster as we can only decode the whole image at once
|
||||
|
||||
boolean originSet = false;
|
||||
|
||||
if (param != null) {
|
||||
if (param.getSourceRegion() != null && !param.getSourceRegion().contains(bounds) ||
|
||||
param.getSourceXSubsampling() != 1 || param.getSourceYSubsampling() != 1) {
|
||||
//Can't reuse existing
|
||||
// Can't reuse existing
|
||||
return Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, bounds.width, bounds.height,
|
||||
4 * bounds.width, 4, new int[] {0, 1, 2, 3}, null);
|
||||
}
|
||||
@ -189,12 +186,13 @@ public final class VP8LDecoder {
|
||||
}
|
||||
}
|
||||
if (!raster.getBounds().contains(bounds)) {
|
||||
//Can't reuse existing
|
||||
// Can't reuse existing
|
||||
return Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, bounds.width, bounds.height, 4 * bounds.width,
|
||||
4, new int[] {0, 1, 2, 3}, null);
|
||||
}
|
||||
|
||||
return originSet ?
|
||||
//Recenter to (0, 0)
|
||||
// Recenter to (0, 0)
|
||||
raster.createWritableChild(bounds.x, bounds.y, bounds.width, bounds.height, 0, 0, null) :
|
||||
raster;
|
||||
}
|
||||
@ -210,47 +208,39 @@ public final class VP8LDecoder {
|
||||
|
||||
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
|
||||
// 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
|
||||
if (code < 256) { // Literal
|
||||
decodeLiteral(raster, colorCache, curCodeGroup, rgba, y, x, code);
|
||||
|
||||
}
|
||||
else if (code < 256 + 24) { //backward reference
|
||||
|
||||
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
|
||||
// Decrement one because for loop already increments by one
|
||||
x--;
|
||||
y = y + ((x + length) / width);
|
||||
x = (x + length) % width;
|
||||
|
||||
|
||||
//Reset Huffman meta group
|
||||
// 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
|
||||
else { // colorCache
|
||||
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);
|
||||
@ -265,11 +255,13 @@ public final class VP8LDecoder {
|
||||
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));
|
||||
}
|
||||
@ -284,16 +276,15 @@ public final class VP8LDecoder {
|
||||
int xSrc, ySrc;
|
||||
|
||||
if (distanceCode > 120) {
|
||||
//Linear distance
|
||||
// Linear distance
|
||||
int distance = distanceCode - 120;
|
||||
ySrc = y - (distance / width);
|
||||
xSrc = x - (distance % width);
|
||||
}
|
||||
else {
|
||||
//See comment of distances array
|
||||
// See comment of distances array
|
||||
xSrc = x - (8 - (DISTANCES[distanceCode - 1] & 0xf));
|
||||
ySrc = y - (DISTANCES[distanceCode - 1] >> 4);
|
||||
|
||||
}
|
||||
|
||||
if (xSrc < 0) {
|
||||
@ -306,14 +297,15 @@ public final class VP8LDecoder {
|
||||
}
|
||||
|
||||
for (int l = length; l > 0; x++, l--) {
|
||||
//Check length and xSrc, ySrc not falling outside raster? (Should not occur if image is correct)
|
||||
|
||||
// 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++;
|
||||
@ -322,22 +314,21 @@ public final class VP8LDecoder {
|
||||
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
|
||||
|
||||
// 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 {
|
||||
@ -347,7 +338,7 @@ public final class VP8LDecoder {
|
||||
|
||||
switch (transformType) {
|
||||
case TransformType.PREDICTOR_TRANSFORM:
|
||||
//Intentional Fallthrough
|
||||
// Intentional Fallthrough
|
||||
case TransformType.COLOR_TRANSFORM: {
|
||||
// The two first transforms contains the exact same data, can be combined
|
||||
|
||||
@ -360,7 +351,7 @@ public final class VP8LDecoder {
|
||||
new int[] {0, 1, 2, 3}, null);
|
||||
readVP8Lossless(raster, false, null, blockWidth, blockHeight);
|
||||
|
||||
//Keep data as raster for convenient (x,y) indexing
|
||||
// Keep data as raster for convenient (x,y) indexing
|
||||
if (transformType == TransformType.PREDICTOR_TRANSFORM) {
|
||||
transforms.add(0, new PredictorTransform(raster, sizeBits));
|
||||
}
|
||||
@ -376,7 +367,6 @@ public final class VP8LDecoder {
|
||||
break;
|
||||
}
|
||||
case TransformType.COLOR_INDEXING_TRANSFORM: {
|
||||
|
||||
// 8 bit value for color table size
|
||||
int colorTableSize = ((int) lsbBitReader.readBits(8)) + 1; // 1-256
|
||||
|
||||
@ -387,7 +377,6 @@ public final class VP8LDecoder {
|
||||
colorTableSize > 4 ? 16 :
|
||||
colorTableSize > 2 ? 4 : 2;
|
||||
|
||||
|
||||
byte[] colorTable = new byte[safeColorTableSize * 4];
|
||||
|
||||
// The color table can be obtained by reading an image,
|
||||
@ -398,12 +387,10 @@ public final class VP8LDecoder {
|
||||
readVP8Lossless(
|
||||
Raster.createInterleavedRaster(
|
||||
new DataBufferByte(colorTable, colorTableSize * 4),
|
||||
colorTableSize, 1, colorTableSize * 4,
|
||||
4, new int[] {0, 1, 2, 3}, null)
|
||||
, false, null, colorTableSize, 1);
|
||||
colorTableSize, 1, colorTableSize * 4, 4, new int[] {0, 1, 2, 3}, null),
|
||||
false, null, colorTableSize, 1);
|
||||
|
||||
|
||||
//resolve subtraction code
|
||||
// resolve subtraction code
|
||||
for (int i = 4; i < colorTable.length; i++) {
|
||||
colorTable[i] += colorTable[i - 4];
|
||||
}
|
||||
@ -428,8 +415,7 @@ public final class VP8LDecoder {
|
||||
return xSize;
|
||||
}
|
||||
|
||||
private HuffmanInfo readHuffmanCodes(int xSize, int ySize, int colorCacheBits, boolean readMetaCodes)
|
||||
throws IOException {
|
||||
private HuffmanInfo readHuffmanCodes(int xSize, int ySize, int colorCacheBits, boolean readMetaCodes) throws IOException {
|
||||
int huffmanGroupNum = 1;
|
||||
int huffmanXSize;
|
||||
int huffmanYSize;
|
||||
@ -437,32 +423,30 @@ public final class VP8LDecoder {
|
||||
int metaCodeBits = 0;
|
||||
|
||||
WritableRaster huffmanMetaCodes = null;
|
||||
|
||||
if (readMetaCodes && lsbBitReader.readBit() == 1) {
|
||||
//read in meta codes
|
||||
// read in meta codes
|
||||
metaCodeBits = (int) lsbBitReader.readBits(3) + 2;
|
||||
huffmanXSize = subSampleSize(xSize, metaCodeBits);
|
||||
huffmanYSize = subSampleSize(ySize, metaCodeBits);
|
||||
|
||||
//Raster with elements as BARG (only the RG components encode the meta group)
|
||||
// Raster with elements as BARG (only the RG components encode the meta group)
|
||||
WritableRaster packedRaster = Raster.createPackedRaster(DataBuffer.TYPE_INT, huffmanXSize, huffmanYSize,
|
||||
new int[] {0x0000ff00, 0x000000ff, 0xff000000, 0x00ff0000}, null);
|
||||
readVP8Lossless(asByteRaster(packedRaster), false, null, huffmanXSize, huffmanYSize);
|
||||
|
||||
int[] data = ((DataBufferInt) packedRaster.getDataBuffer()).getData();
|
||||
//Max metaGroup is number of meta groups
|
||||
// Max metaGroup is number of meta groups
|
||||
int maxCode = Integer.MIN_VALUE;
|
||||
for (int code : data) {
|
||||
maxCode = max(maxCode, code & 0xffff);
|
||||
}
|
||||
huffmanGroupNum = maxCode + 1;
|
||||
|
||||
/*
|
||||
New Raster with just RG components exposed as single band allowing simple access of metaGroupIndex with
|
||||
x,y lookup
|
||||
*/
|
||||
// New Raster with just RG components exposed as single band
|
||||
// allowing simple access of metaGroupIndex with x,y lookup
|
||||
huffmanMetaCodes = Raster.createPackedRaster(packedRaster.getDataBuffer(), huffmanXSize, huffmanYSize,
|
||||
huffmanXSize, new int[] {0xffff}, null);
|
||||
|
||||
}
|
||||
|
||||
HuffmanCodeGroup[] huffmanGroups = new HuffmanCodeGroup[huffmanGroupNum];
|
||||
|
Loading…
x
Reference in New Issue
Block a user