WebP source subsampling.

This commit is contained in:
Harald Kuhr 2020-11-30 22:10:54 +01:00
parent 4dedf76ebc
commit 3bb312e9e1
6 changed files with 14 additions and 72 deletions

View File

@ -428,7 +428,7 @@ final class WebPImageReader extends ImageReaderBase {
}
private void readVP8Lossless(final WritableRaster raster, final ImageReadParam param) throws IOException {
VP8LDecoder decoder = new VP8LDecoder(imageInput);
VP8LDecoder decoder = new VP8LDecoder(imageInput, DEBUG);
decoder.readVP8Lossless(raster, true);
}
@ -443,7 +443,6 @@ final class WebPImageReader extends ImageReaderBase {
});
if (!frame.decode(raster, param)) {
// TODO: Does this make any sense? Only happens if frame type is not still (0)
processWarningOccurred("Nothing to decode");
}
}

View File

@ -41,7 +41,7 @@ import java.util.ArrayList;
import java.util.List;
import static com.twelvemonkeys.imageio.plugins.webp.RasterUtils.asByteRaster;
import static java.lang.Math.abs;
import static java.lang.Math.*;
/**
* VP8LDecoder.
@ -49,14 +49,13 @@ import static java.lang.Math.abs;
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
*/
public final class VP8LDecoder {
private final ImageInputStream imageInput;
private final LSBBitReader lsbBitReader;
private final List<Transform> transforms = new ArrayList<>();
private ColorCache colorCache;
public VP8LDecoder(final ImageInputStream imageInput) {
public VP8LDecoder(final ImageInputStream imageInput, final boolean debug) {
this.imageInput = imageInput;
lsbBitReader = new LSBBitReader(imageInput);
}
@ -275,7 +274,7 @@ public final class VP8LDecoder {
// Clamp the input value between 0 and 255.
private static int clamp(final int a) {
return a < 0 ? 0 : a > 255 ? 255 : a;
return max(0, min(a, 255));
}
private static int clampAddSubtractFull(final int a, final int b, final int c) {

View File

@ -499,8 +499,7 @@ final class Globals {
for (int i = 0; i < vp8DefaultCoefProbs.length; i++)
for (int j = 0; j < vp8DefaultCoefProbs[0].length; j++)
for (int k = 0; k < vp8DefaultCoefProbs[0][0].length; k++)
for (int l = 0; l < vp8DefaultCoefProbs[0][0][0].length; l++)
r[i][j][k][l] = vp8DefaultCoefProbs[i][j][k][l];
r[i][j][k] = vp8DefaultCoefProbs[i][j][k].clone();
return r;
}

View File

@ -145,16 +145,6 @@ final class LoopFilter {
return abs(p1 - p0) > threshold || abs(q1 - q0) > threshold;
}
// public static void loopFilter(VP8Frame frame) {
// if (frame.getFilterType() == 2) {
// loopFilterUV(frame);
// loopFilterY(frame);
// }
// else if (frame.getFilterType() == 1) {
// loopFilterSimple(frame);
// }
// }
static void loopFilterBlock(final MacroBlock cmb, final MacroBlock lmb, final MacroBlock tmb, int frameType, boolean simpleFilter, int sharpness) {
if (simpleFilter) {
loopFilterSimpleBlock(cmb, lmb, tmb, sharpness);
@ -165,23 +155,7 @@ final class LoopFilter {
}
}
private static void loopFilterSimple(VP8Frame frame) {
for (int y = 0; y < frame.getMacroBlockRows(); y++) {
// frame.fireLFProgressUpdate((100.0f * ((float) (y + 1) / (float) (frame
// .getMacroBlockRows()))));
for (int x = 0; x < frame.getMacroBlockCols(); x++) {
loopFilterSimpleBlock(frame.getMacroBlock(x, y),
x > 0 ? frame.getMacroBlock(x - 1, y) : null,
y > 0 ? frame.getMacroBlock(x, y - 1) : null,
frame.getSharpnessLevel());
}
}
}
static void loopFilterSimpleBlock(final MacroBlock cmb, final MacroBlock lmb, final MacroBlock tmb, final int sharpnessLevel) {
// System.out.println("x: "+x+" y: "+y);
// MacroBlock bmb = frame.getMacroBlock(x, y); // TODO: Same..?
int loop_filter_level = cmb.getFilterLevel();
if (loop_filter_level != 0) {
int interior_limit = cmb.getFilterLevel();
@ -277,18 +251,6 @@ final class LoopFilter {
}
}
private static void loopFilterUV(VP8Frame frame) {
for (int y = 0; y < frame.getMacroBlockRows(); y++) {
for (int x = 0; x < frame.getMacroBlockCols(); x++) {
loopFilterUVBlock(frame.getMacroBlock(x, y),
x > 0 ? frame.getMacroBlock(x - 1, y) : null,
y > 0 ? frame.getMacroBlock(x, y - 1) : null,
frame.getSharpnessLevel(), frame.getFrameType()
);
}
}
}
static void loopFilterUVBlock(final MacroBlock cmb, final MacroBlock lmb, final MacroBlock tmb, final int sharpnessLevel, final int frameType) {
int loop_filter_level = cmb.getFilterLevel();
if (loop_filter_level != 0) {
@ -407,17 +369,6 @@ final class LoopFilter {
}
}
private static void loopFilterY(VP8Frame frame) {
for (int y = 0; y < frame.getMacroBlockRows(); y++) {
for (int x = 0; x < frame.getMacroBlockCols(); x++) {
loopFilterYBlock(frame.getMacroBlock(x, y),
x > 0 ? frame.getMacroBlock(x - 1, y) : null,
y > 0 ? frame.getMacroBlock(x, y - 1) : null,
frame.getSharpnessLevel(), frame.getFrameType());
}
}
}
static void loopFilterYBlock(final MacroBlock cmb, final MacroBlock lmb, final MacroBlock tmb, final int sharpnessLevel, final int frameType) {
int loop_filter_level = cmb.getFilterLevel();
@ -587,9 +538,6 @@ final class LoopFilter {
if ((abs(seg.P0 - seg.Q0) * 2 + abs(seg.P1 - seg.Q1) / 2) <= edge_limit) {
common_adjust(true, seg); // use outer taps
}
else {
// TODO?
}
}
private static void subblock_filter(int hev_threshold, // detect high edge variance
@ -605,9 +553,6 @@ final class LoopFilter {
seg.P1 = s2u(p1 + a);
}
}
else {
// TODO?
}
}
/* Convert pixel value (0 <= v <= 255) to an 8-bit signed number. */

View File

@ -236,7 +236,6 @@ final class SubBlock {
}
public SubBlock getAbove() {
return above;
}
@ -247,6 +246,7 @@ final class SubBlock {
&& plane == Plane.Y1) {
r = r + "\n " + Globals.getSubBlockModeAsString(mode);
}
return r;
}
@ -260,12 +260,10 @@ final class SubBlock {
}
public int[][] getDiff() {
return diff;
}
public SubBlock getLeft() {
return left;
}

View File

@ -1060,22 +1060,24 @@ public final class VP8Frame {
private final byte[] rgb = new byte[4]; // Allow decoding into RGBA, leaving the alpha out.
private void copyBlock(final MacroBlock macroBlock, final WritableRaster byteRGBRaster, final ImageReadParam param) {
// TODO: Consider doing YCbCr -> RGB in reader instead, or pass a flag to allow readRaster reading direct YUV/YCbCr values
int sourceYSubsampling = param != null ? param.getSourceYSubsampling() : 1;
int sourceXSubsampling = param != null ? param.getSourceXSubsampling() : 1;
// We might be copying into a smaller raster
int yStart = macroBlock.getY() * 16;
int yEnd = Math.min(16, byteRGBRaster.getHeight() - yStart);
int yEnd = Math.min(16, byteRGBRaster.getHeight() * sourceYSubsampling - yStart);
int xStart = macroBlock.getX() * 16;
int xEnd = Math.min(16, byteRGBRaster.getWidth() - xStart);
int xEnd = Math.min(16, byteRGBRaster.getWidth() * sourceXSubsampling - xStart);
for (int y = 0; y < yEnd; y++) {
for (int x = 0; x < xEnd; x++) {
for (int y = 0; y < yEnd; y += sourceYSubsampling) {
for (int x = 0; x < xEnd; x += sourceXSubsampling) {
yuv[0] = (byte) macroBlock.getSubBlock(SubBlock.Plane.Y1, x / 4, y / 4).getDest()[x % 4][y % 4];
yuv[1] = (byte) macroBlock.getSubBlock(SubBlock.Plane.U, (x / 2) / 4, (y / 2) / 4).getDest()[(x / 2) % 4][(y / 2) % 4];
yuv[2] = (byte) macroBlock.getSubBlock(SubBlock.Plane.V, (x / 2) / 4, (y / 2) / 4).getDest()[(x / 2) % 4][(y / 2) % 4];
// TODO: Consider doing YCbCr -> RGB in reader instead, or pass a flag to allow readRaster reading direct YUV/YCbCr values
convertYCbCr2RGB(yuv, rgb, 0);
byteRGBRaster.setDataElements(xStart + x, yStart + y, rgb);
byteRGBRaster.setDataElements((xStart + x) / sourceXSubsampling, (yStart + y) / sourceYSubsampling, rgb);
}
}
}