mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 04:25:29 -04:00
Merge pull request #737 from tc-wleite/WebP_FixAlphaSubsampling
WebP: Fix alpha decoding when source subsampling is used
This commit is contained in:
commit
614a07e040
@ -61,6 +61,8 @@ import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.webp.lossless.VP8LDecoder.copyIntoRasterWithParams;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
@ -536,21 +538,26 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
// 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);
|
||||
// Temp alpha raster must have same dimensions as the source, because of filtering.
|
||||
WritableRaster tempRaster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height, 4, null);
|
||||
readVP8Lossless(tempRaster, null, 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}));
|
||||
WritableRaster alphaChannel = tempRaster.createWritableChild(0, 0, tempRaster.getWidth(), tempRaster.getHeight(), 0, 0, new int[]{1});
|
||||
alphaFilter(alphaChannel, filtering);
|
||||
copyIntoRasterWithParams(alphaChannel, alphaRaster, param);
|
||||
break;
|
||||
default:
|
||||
processWarningOccurred("Unknown WebP alpha compression: " + compression);
|
||||
opaqueAlpha(alphaRaster);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void alphaFilter(WritableRaster alphaRaster, int filtering) {
|
||||
if (filtering != AlphaFiltering.NONE) {
|
||||
for (int y = 0; y < destination.getHeight(); y++) {
|
||||
for (int x = 0; x < destination.getWidth(); x++) {
|
||||
for (int y = 0; y < alphaRaster.getHeight(); y++) {
|
||||
for (int x = 0; x < alphaRaster.getWidth(); x++) {
|
||||
int predictorAlpha = getPredictorAlpha(alphaRaster, filtering, y, x);
|
||||
alphaRaster.setSample(x, y, 0, alphaRaster.getSample(x, y, 0) + predictorAlpha % 256);
|
||||
}
|
||||
|
@ -135,33 +135,36 @@ public final class VP8LDecoder {
|
||||
}
|
||||
|
||||
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();
|
||||
copyIntoRasterWithParams(fullSizeRaster, raster, param);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a source raster into a destination raster with settings applied.
|
||||
*/
|
||||
public static void copyIntoRasterWithParams(final Raster srcRaster, final WritableRaster dstRaster,
|
||||
final ImageReadParam param) {
|
||||
Rectangle sourceRegion = param != null && param.getSourceRegion() != null ? param.getSourceRegion() : dstRaster.getBounds();
|
||||
int sourceXSubsampling = param != null ? param.getSourceXSubsampling() : 1;
|
||||
int sourceYSubsampling = param != null ? param.getSourceYSubsampling() : 1;
|
||||
int subsamplingXOffset = param != null ? param.getSubsamplingXOffset() : 0;
|
||||
int subsamplingYOffset = param != null ? param.getSubsamplingYOffset() : 0;
|
||||
Point destinationOffset = param != null ? param.getDestinationOffset() : new Point(0, 0) ;
|
||||
|
||||
if (sourceRegion == null) {
|
||||
sourceRegion = raster.getBounds();
|
||||
}
|
||||
if (sourceXSubsampling == 1 && sourceYSubsampling == 1) {
|
||||
// Only apply offset (and limit to requested region)
|
||||
dstRaster.setRect(destinationOffset.x, destinationOffset.y, srcRaster);
|
||||
}
|
||||
else {
|
||||
// Manual copy, more efficient way might exist
|
||||
byte[] rgba = new byte[4];
|
||||
int xEnd = dstRaster.getWidth() + dstRaster.getMinX();
|
||||
int yEnd = dstRaster.getHeight() + dstRaster.getMinY();
|
||||
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
srcRaster.getDataElements(xSrc, ySrc, rgba);
|
||||
dstRaster.setDataElements(xDst, yDst, rgba);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,9 @@ public class WebPImageReaderTest extends ImageReaderAbstractTest<WebPImageReader
|
||||
new Dimension(400, 400), new Dimension(400, 400), new Dimension(400, 394),
|
||||
new Dimension(371, 394), new Dimension(394, 382), new Dimension(400, 388),
|
||||
new Dimension(394, 383), new Dimension(394, 394), new Dimension(372, 394),
|
||||
new Dimension(400, 400), new Dimension(320, 382))
|
||||
new Dimension(400, 400), new Dimension(320, 382)),
|
||||
// Alpha transparency and Alpha filtering
|
||||
new TestData(getClassLoaderResource("/webp/alpha_filter.webp"), new Dimension(1600, 1600))
|
||||
);
|
||||
}
|
||||
|
||||
@ -162,4 +164,27 @@ public class WebPImageReaderTest extends ImageReaderAbstractTest<WebPImageReader
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlphaSubsampling() throws IOException {
|
||||
WebPImageReader reader = createReader();
|
||||
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/webp/alpha_filter.webp"))) {
|
||||
reader.setInput(stream);
|
||||
|
||||
// Read the image using a subsampling factor of 2
|
||||
ImageReadParam param = new ImageReadParam();
|
||||
param.setSourceSubsampling(2, 2, 0, 0);
|
||||
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertRGBEquals("Expected transparent at (100, 265)", 0x00000000, image.getRGB(100, 265) & 0xFF000000, 8);
|
||||
assertRGBEquals("Expected transparent at (512, 320)", 0x00000000, image.getRGB(512, 320) & 0xFF000000, 8);
|
||||
assertRGBEquals("Expected opaque at (666, 444)", 0xFF000000, image.getRGB(666, 444) & 0xFF000000, 8);
|
||||
assertRGBEquals("Expected opaque corner (799, 799)", 0xFF000000, image.getRGB(699, 699) & 0xFF000000, 8);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
imageio/imageio-webp/src/test/resources/webp/alpha_filter.webp
Normal file
BIN
imageio/imageio-webp/src/test/resources/webp/alpha_filter.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
Loading…
x
Reference in New Issue
Block a user