mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 03:55:28 -04:00
Support ImageReadParam Settings limiting Raster size
On animation frames dimension has to be passed as it is not guaranteed the same as in the file header.
This commit is contained in:
parent
67b48ce1e3
commit
c858454c5a
@ -427,7 +427,7 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
AnimationFrame frame = frames.get(imageIndex);
|
||||
imageInput.seek(frame.offset + 16);
|
||||
opaqueAlpha(destination.getAlphaRaster());
|
||||
readVP8Extended(destination, param, frame.offset + frame.length);
|
||||
readVP8Extended(destination, param, frame.offset + frame.length, frame.bounds.width, frame.bounds.height);
|
||||
}
|
||||
else {
|
||||
imageInput.seek(header.offset + header.length);
|
||||
@ -453,6 +453,11 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
private void readVP8Extended(BufferedImage destination, ImageReadParam param, long streamEnd) throws IOException {
|
||||
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 {
|
||||
while (imageInput.getStreamPosition() < streamEnd) {
|
||||
int nextChunk = imageInput.readInt();
|
||||
long chunkLength = imageInput.readUnsignedInt();
|
||||
@ -466,7 +471,7 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
|
||||
switch (nextChunk) {
|
||||
case WebP.CHUNK_ALPH:
|
||||
readAlpha(destination, param);
|
||||
readAlpha(destination, param, width, height);
|
||||
|
||||
break;
|
||||
|
||||
@ -476,7 +481,7 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
break;
|
||||
|
||||
case WebP.CHUNK_VP8L:
|
||||
readVP8Lossless(RasterUtils.asByteRaster(destination.getRaster()), param);
|
||||
readVP8Lossless(RasterUtils.asByteRaster(destination.getRaster()), param, width, height);
|
||||
break;
|
||||
|
||||
case WebP.CHUNK_ANIM:
|
||||
@ -500,7 +505,7 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
private void readAlpha(BufferedImage destination, ImageReadParam param) throws IOException {
|
||||
private void readAlpha(BufferedImage destination, ImageReadParam param, final int width, final int height) throws IOException {
|
||||
int reserved = (int) imageInput.readBits(2);
|
||||
if (reserved != 0) {
|
||||
// Spec says SHOULD be 0
|
||||
@ -525,10 +530,11 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
break;
|
||||
case 1:
|
||||
WritableRaster tempRaster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
|
||||
destination.getWidth(), destination.getHeight(), 4, null);
|
||||
destination.getWidth(), destination.getHeight(), 4,
|
||||
destination.getRaster().getBounds().getLocation());
|
||||
//Simulate header
|
||||
imageInput.seek(imageInput.getStreamPosition() - 5);
|
||||
readVP8Lossless(tempRaster, param);
|
||||
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}));
|
||||
@ -638,8 +644,13 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
private void readVP8Lossless(final WritableRaster raster, final ImageReadParam param) throws IOException {
|
||||
readVP8Lossless(raster, param, header.width, header.height);
|
||||
}
|
||||
|
||||
private void readVP8Lossless(final WritableRaster raster, final ImageReadParam param,
|
||||
final int width, final int height) throws IOException {
|
||||
VP8LDecoder decoder = new VP8LDecoder(imageInput, DEBUG);
|
||||
decoder.readVP8Lossless(raster, true);
|
||||
decoder.readVP8Lossless(raster, true, param, width, height);
|
||||
}
|
||||
|
||||
private void readVP8(final WritableRaster raster, final ImageReadParam param) throws IOException {
|
||||
|
@ -42,7 +42,9 @@ import com.twelvemonkeys.imageio.plugins.webp.lossless.transform.Transform;
|
||||
import com.twelvemonkeys.imageio.plugins.webp.lossless.transform.TransformType;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@ -84,7 +86,8 @@ public final class VP8LDecoder {
|
||||
lsbBitReader = new LSBBitReader(imageInput);
|
||||
}
|
||||
|
||||
public void readVP8Lossless(final WritableRaster raster, final boolean topLevel) throws IOException {
|
||||
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
|
||||
@ -92,13 +95,12 @@ public final class VP8LDecoder {
|
||||
imageInput.seek(imageInput.getStreamPosition() + 5);
|
||||
}
|
||||
|
||||
int xSize = raster.getWidth();
|
||||
int ySize = raster.getHeight();
|
||||
int xSize = width;
|
||||
|
||||
// Read transforms
|
||||
ArrayList<Transform> transforms = new ArrayList<>();
|
||||
while (topLevel && lsbBitReader.readBit() == 1) {
|
||||
xSize = readTransform(xSize, ySize, transforms);
|
||||
xSize = readTransform(xSize, height, transforms);
|
||||
}
|
||||
|
||||
// Read color cache size
|
||||
@ -111,7 +113,7 @@ public final class VP8LDecoder {
|
||||
}
|
||||
|
||||
// Read Huffman codes
|
||||
HuffmanInfo huffmanInfo = readHuffmanCodes(xSize, ySize, colorCacheBits, topLevel);
|
||||
HuffmanInfo huffmanInfo = readHuffmanCodes(xSize, height, colorCacheBits, topLevel);
|
||||
|
||||
ColorCache colorCache = null;
|
||||
|
||||
@ -119,16 +121,89 @@ public final class VP8LDecoder {
|
||||
colorCache = new ColorCache(colorCacheBits);
|
||||
}
|
||||
|
||||
WritableRaster fullSizeRaster;
|
||||
WritableRaster decodeRaster;
|
||||
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
|
||||
WritableRaster writableChild = raster.createWritableChild(0, 0, xSize, ySize, 0, 0, null);
|
||||
|
||||
// Use the Huffman trees to decode the LZ77 encoded data.
|
||||
decodeImage(writableChild, huffmanInfo, colorCache);
|
||||
|
||||
for (Transform transform : transforms) {
|
||||
transform.applyInverse(raster);
|
||||
decodeRaster = fullSizeRaster.createWritableChild(0, 0, xSize, height, 0, 0, null);
|
||||
}
|
||||
else {
|
||||
//All recursive calls have Rasters of the correct sizes with origin (0, 0)
|
||||
decodeRaster = fullSizeRaster = raster;
|
||||
}
|
||||
|
||||
// Use the Huffman trees to decode the LZ77 encoded data.
|
||||
decodeImage(decodeRaster, huffmanInfo, colorCache);
|
||||
|
||||
for (Transform transform : transforms) {
|
||||
transform.applyInverse(fullSizeRaster);
|
||||
}
|
||||
|
||||
if (fullSizeRaster != raster && param != null) {
|
||||
//Copy into destination raster with settings applied
|
||||
Rectangle sourceRegion = param.getSourceRegion();
|
||||
int sourceXSubsampling = param.getSourceXSubsampling();
|
||||
int sourceYSubsampling = param.getSourceYSubsampling();
|
||||
int subsamplingXOffset = param.getSubsamplingXOffset();
|
||||
int subsamplingYOffset = param.getSubsamplingYOffset();
|
||||
Point destinationOffset = param.getDestinationOffset();
|
||||
|
||||
if (sourceRegion == null) {
|
||||
sourceRegion = raster.getBounds();
|
||||
}
|
||||
|
||||
if (sourceXSubsampling == 1 && sourceYSubsampling == 1) {
|
||||
//Only apply offset (and limit to requested region)
|
||||
raster.setRect(destinationOffset.x, destinationOffset.y, fullSizeRaster);
|
||||
}
|
||||
else {
|
||||
//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) {
|
||||
fullSizeRaster.getDataElements(xSrc, ySrc, rgba);
|
||||
raster.setDataElements(xDst, yDst, rgba);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// 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
|
||||
return Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, bounds.width, bounds.height,
|
||||
4 * bounds.width, 4, new int[] {0, 1, 2, 3}, null);
|
||||
}
|
||||
else {
|
||||
bounds.setLocation(param.getDestinationOffset());
|
||||
originSet = true;
|
||||
|
||||
}
|
||||
}
|
||||
if (!raster.getBounds().contains(bounds)) {
|
||||
//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)
|
||||
raster.createWritableChild(bounds.x, bounds.y, bounds.width, bounds.height, 0, 0, null) :
|
||||
raster;
|
||||
}
|
||||
|
||||
private void decodeImage(WritableRaster raster, HuffmanInfo huffmanInfo, ColorCache colorCache) throws IOException {
|
||||
@ -296,7 +371,7 @@ public final class VP8LDecoder {
|
||||
WritableRaster raster =
|
||||
Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, blockWidth, blockHeight, 4 * blockWidth, 4,
|
||||
new int[] {0, 1, 2, 3}, null);
|
||||
readVP8Lossless(raster, false);
|
||||
readVP8Lossless(raster, false, null, blockWidth, blockHeight);
|
||||
|
||||
//Keep data as raster for convenient (x,y) indexing
|
||||
if (transformType == TransformType.PREDICTOR_TRANSFORM) {
|
||||
@ -342,7 +417,7 @@ public final class VP8LDecoder {
|
||||
new DataBufferByte(colorTable, colorTableSize * 4),
|
||||
colorTableSize, 1, colorTableSize * 4,
|
||||
4, new int[] {0, 1, 2, 3}, null)
|
||||
, false);
|
||||
, false, null, colorTableSize, 1);
|
||||
|
||||
|
||||
//resolve subtraction code
|
||||
@ -388,7 +463,7 @@ public final class VP8LDecoder {
|
||||
//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);
|
||||
readVP8Lossless(asByteRaster(packedRaster), false, null, huffmanXSize, huffmanYSize);
|
||||
|
||||
int[] data = ((DataBufferInt) packedRaster.getDataBuffer()).getData();
|
||||
//Max metaGroup is number of meta groups
|
||||
|
Loading…
x
Reference in New Issue
Block a user