WebP performance optimizations & clean up.

This commit is contained in:
Harald Kuhr 2020-11-30 17:10:35 +01:00
parent 2376d16ffd
commit 4dedf76ebc
9 changed files with 1368 additions and 1432 deletions

View File

@ -32,6 +32,7 @@
package com.twelvemonkeys.imageio.plugins.webp;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.color.ColorSpaces;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
import com.twelvemonkeys.imageio.metadata.xmp.XMPReader;
@ -48,11 +49,14 @@ import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
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;
@ -83,7 +87,15 @@ final class WebPImageReader extends ImageReaderBase {
@Override
public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) {
// TODO: Figure out why this makes the reader order of magnitudes faster (2-3x?)
// ...or, how to make VP8 decoder make longer reads/make a better FileImageInputStream...
super.setInput(input, seekForwardOnly, ignoreMetadata);
// try {
// super.setInput(new BufferedImageInputStream((ImageInputStream) input), seekForwardOnly, ignoreMetadata);
// }
// catch (IOException e) {
// throw new IOError(e);
// }
lsbBitReader = new LSBBitReader(imageInput);
}
@ -240,7 +252,8 @@ final class WebPImageReader extends ImageReaderBase {
(byte) ((value & 0x0000ff00) >> 8),
(byte) ((value & 0x00ff0000) >> 16),
(byte) ((value & 0xff000000) >>> 24),
}
},
StandardCharsets.US_ASCII
);
}
@ -270,7 +283,12 @@ final class WebPImageReader extends ImageReaderBase {
// "alpha value is codified in bits 31..24, red in bits 23..16, green in bits 15..8 and blue in bits 7..0,
// but implementations of the format are free to use another representation internally."
// TODO: Doc says alpha flag is "hint only" :-P
// TODO: ICC profile?!
if (iccProfile != null && !ColorSpaces.isCS_sRGB(iccProfile)) {
ICC_ColorSpace colorSpace = ColorSpaces.createColorSpace(iccProfile);
int[] bandOffsets = header.containsALPH ? new int[] {0, 1, 2, 3} : new int[] {0, 1, 2};
return ImageTypeSpecifiers.createInterleaved(colorSpace, bandOffsets, DataBuffer.TYPE_BYTE, header.containsALPH, false);
}
return ImageTypeSpecifiers.createFromBufferedImageType(header.containsALPH ? BufferedImage.TYPE_4BYTE_ABGR : BufferedImage.TYPE_3BYTE_BGR);
}
@ -296,13 +314,13 @@ final class WebPImageReader extends ImageReaderBase {
switch (header.fourCC) {
case WebP.CHUNK_VP8_:
imageInput.seek(header.offset);
readVP8(RasterUtils.asByteRaster(destination.getRaster(), destination.getColorModel()));
readVP8(RasterUtils.asByteRaster(destination.getRaster(), destination.getColorModel()), param);
break;
case WebP.CHUNK_VP8L:
imageInput.seek(header.offset);
readVP8Lossless(RasterUtils.asByteRaster(destination.getRaster(), destination.getColorModel()));
readVP8Lossless(RasterUtils.asByteRaster(destination.getRaster(), destination.getColorModel()), param);
break;
@ -344,7 +362,7 @@ final class WebPImageReader extends ImageReaderBase {
break;
case 1:
opaqueAlpha(destination.getAlphaRaster()); // TODO: Remove when correctly implemented!
readVP8Lossless(destination.getAlphaRaster());
readVP8Lossless(destination.getAlphaRaster(), param);
break;
default:
processWarningOccurred("Unknown WebP alpha compression: " + compression);
@ -356,12 +374,12 @@ final class WebPImageReader extends ImageReaderBase {
case WebP.CHUNK_VP8_:
readVP8(RasterUtils.asByteRaster(destination.getRaster(), destination.getColorModel())
.createWritableChild(0, 0, width, height, 0, 0, new int[]{0, 1, 2}));
.createWritableChild(0, 0, width, height, 0, 0, new int[]{0, 1, 2}), param);
break;
case WebP.CHUNK_VP8L:
readVP8Lossless(RasterUtils.asByteRaster(destination.getRaster(), destination.getColorModel()));
readVP8Lossless(RasterUtils.asByteRaster(destination.getRaster(), destination.getColorModel()), param);
break;
@ -409,13 +427,13 @@ final class WebPImageReader extends ImageReaderBase {
opaqueAlpha(alphaRaster);
}
private void readVP8Lossless(final WritableRaster raster) throws IOException {
private void readVP8Lossless(final WritableRaster raster, final ImageReadParam param) throws IOException {
VP8LDecoder decoder = new VP8LDecoder(imageInput);
decoder.readVP8Lossless(raster, true);
}
private void readVP8(final WritableRaster raster) throws IOException {
VP8Frame frame = new VP8Frame(imageInput);
private void readVP8(final WritableRaster raster, final ImageReadParam param) throws IOException {
VP8Frame frame = new VP8Frame(imageInput, DEBUG);
frame.setProgressListener(new ProgressListenerBase() {
@Override
@ -424,9 +442,9 @@ final class WebPImageReader extends ImageReaderBase {
}
});
// TODO: Consider merging these operations...
if (frame.decodeFrame(false)) {
frame.copyTo(raster);
if (!frame.decode(raster, param)) {
// TODO: Does this make any sense? Only happens if frame type is not still (0)
processWarningOccurred("Nothing to decode");
}
}
@ -435,10 +453,8 @@ final class WebPImageReader extends ImageReaderBase {
@Override
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
readHeader(imageIndex);
// TODO: Read possible EXIF and 'XMP ' chunks
readMeta();
return new WebPImageMetadata(header);
}

View File

@ -31,6 +31,9 @@
package com.twelvemonkeys.imageio.plugins.webp.vp8;
import static java.lang.Math.max;
import static java.lang.Math.min;
final class Globals {
/* predict DC using row above and column to the left */
@ -56,8 +59,9 @@ final class Globals {
return "TM_PRED";
case B_PRED:
return "B_PRED";
default:
throw new IllegalArgumentException("Unknown mode: " + mode);
}
return "not found";
}
/* intra_bmode */
@ -109,8 +113,9 @@ final class Globals {
return "B_HD_PRED";
case B_HU_PRED:
return "B_HU_PRED";
default:
throw new IllegalArgumentException("Unknown mode: " + mode);
}
return "not found";
}
static final int MAX_MB_SEGMENTS = 4;
@ -814,9 +819,8 @@ final class Globals {
return String.format("%1$#x ", c);
}
// clamp between 0 and value
static int clamp(final int input, final int value) {
return input < 0 ? 0 : input > value ? value : input;
// clamp between 0 and max
static int clamp(final int input, final int max) {
return max(0, min(input, max));
}
}

View File

@ -37,17 +37,15 @@ final class IDCT {
private static final int sinpi8sqrt2 = 35468;
public static int[][] idct4x4llm(int input[]) {
int i;
public static int[][] idct4x4llm(int[] input) {
int a1, b1, c1, d1;
int offset = 0;
int[] output = new int[16];
int temp1, temp2;
for (i = 0; i < 4; i++) {
a1 = input[offset + 0] + input[offset + 8];
b1 = input[offset + 0] - input[offset + 8];
for (int i = 0, offset = 0; i < 4; i++) {
a1 = input[offset] + input[offset + 8];
b1 = input[offset] - input[offset + 8];
temp1 = (input[offset + 4] * sinpi8sqrt2) >> 16;
temp2 = input[offset + 12]
@ -60,9 +58,9 @@ final class IDCT {
temp2 = (input[offset + 12] * sinpi8sqrt2) >> 16;
d1 = temp1 + temp2;
output[offset + (0 * 4)] = a1 + d1;
output[offset ] = a1 + d1;
output[offset + (3 * 4)] = a1 - d1;
output[offset + (1 * 4)] = b1 + c1;
output[offset + ( 4)] = b1 + c1;
output[offset + (2 * 4)] = b1 - c1;
offset++;
@ -70,22 +68,19 @@ final class IDCT {
int diffo = 0;
int[][] diff = new int[4][4];
offset = 0;
for (i = 0; i < 4; i++) {
a1 = output[(offset * 4) + 0] + output[(offset * 4) + 2];
b1 = output[(offset * 4) + 0] - output[(offset * 4) + 2];
for (int i = 0, offset = 0; i < 4; i++) {
a1 = output[offset * 4] + output[(offset * 4) + 2];
b1 = output[offset * 4] - output[(offset * 4) + 2];
temp1 = (output[(offset * 4) + 1] * sinpi8sqrt2) >> 16;
temp2 = output[(offset * 4) + 3]
+ ((output[(offset * 4) + 3] * cospi8sqrt2minus1) >> 16);
temp2 = output[(offset * 4) + 3] + ((output[(offset * 4) + 3] * cospi8sqrt2minus1) >> 16);
c1 = temp1 - temp2;
temp1 = output[(offset * 4) + 1]
+ ((output[(offset * 4) + 1] * cospi8sqrt2minus1) >> 16);
temp1 = output[(offset * 4) + 1] + ((output[(offset * 4) + 1] * cospi8sqrt2minus1) >> 16);
temp2 = (output[(offset * 4) + 3] * sinpi8sqrt2) >> 16;
d1 = temp1 + temp2;
output[(offset * 4) + 0] = (a1 + d1 + 4) >> 3;
output[(offset * 4) ] = (a1 + d1 + 4) >> 3;
output[(offset * 4) + 3] = (a1 - d1 + 4) >> 3;
output[(offset * 4) + 1] = (b1 + c1 + 4) >> 3;
output[(offset * 4) + 2] = (b1 - c1 + 4) >> 3;
@ -104,39 +99,35 @@ final class IDCT {
}
public static int[][] iwalsh4x4(int[] input) {
int i;
int a1, b1, c1, d1;
int a2, b2, c2, d2;
int[] output = new int[16];
int[][] diff = new int[4][4];
int offset = 0;
for (i = 0; i < 4; i++) {
a1 = input[offset + 0] + input[offset + 12];
for (int i = 0, offset = 0; i < 4; i++) {
a1 = input[offset ] + input[offset + 12];
b1 = input[offset + 4] + input[offset + 8];
c1 = input[offset + 4] - input[offset + 8];
d1 = input[offset + 0] - input[offset + 12];
d1 = input[offset ] - input[offset + 12];
output[offset + 0] = a1 + b1;
output[offset ] = a1 + b1;
output[offset + 4] = c1 + d1;
output[offset + 8] = a1 - b1;
output[offset + 12] = d1 - c1;
offset++;
}
offset = 0;
for (i = 0; i < 4; i++) {
a1 = output[offset + 0] + output[offset + 3];
for (int i = 0, offset = 0; i < 4; i++) {
a1 = output[offset ] + output[offset + 3];
b1 = output[offset + 1] + output[offset + 2];
c1 = output[offset + 1] - output[offset + 2];
d1 = output[offset + 0] - output[offset + 3];
d1 = output[offset ] - output[offset + 3];
a2 = a1 + b1;
b2 = c1 + d1;
c2 = a1 - b1;
d2 = d1 - c1;
output[offset + 0] = (a2 + 3) >> 3;
output[offset ] = (a2 + 3) >> 3;
output[offset + 1] = (b2 + 3) >> 3;
output[offset + 2] = (c2 + 3) >> 3;
output[offset + 3] = (d2 + 3) >> 3;

View File

@ -31,13 +31,11 @@
package com.twelvemonkeys.imageio.plugins.webp.vp8;
final class LoopFilter {
private static int abs(int v) {
return v < 0 ? -v : v;
}
import static java.lang.Math.*;
private static int c(int v) {
return v < -128 ? -128 : (v > 127 ? 127 : v);
final class LoopFilter {
private static int clamp(int value) {
return max(min(value, 127), -128);
}
private static int common_adjust(boolean use_outer_taps, /* filter is 2 or 4 taps wide */ Segment seg) {
@ -54,18 +52,18 @@ final class LoopFilter {
* can be thought of as a refinement of 2*(q0 - p0) and the adjustment
* is something like (q0 - p0)/4.
*/
int a = c((use_outer_taps ? c(p1 - q1) : 0) + 3 * (q0 - p0));
int a = clamp((use_outer_taps ? clamp(p1 - q1) : 0) + 3 * (q0 - p0));
/*
* b is used to balance the rounding of a/8 in the case where the
* "fractional" part "f" of a/8 is exactly 1/2.
*/
int b = (c(a + 3)) >> 3;
int b = (clamp(a + 3)) >> 3;
/*
* Divide a by 8, rounding up when f >= 1/2. Although not strictly part
* of the "C" language, the right-shift is assumed to propagate the sign
* bit.
*/
a = c(a + 4) >> 3;
a = clamp(a + 4) >> 3;
/* Subtract "a" from q0, "bringing it closer" to p0. */
seg.Q0 = s2u(q0 - a);
/*
@ -92,10 +90,10 @@ final class LoopFilter {
* four pixels that straddle the edge, in line with the calculation in
* simple_segment() above.
*/
private static boolean filter_yes(int I, /* limit on interior differences */
int E, /* limit at the edge */
int p3, int p2, int p1, int p0, /* pixels before edge */
int q0, int q1, int q2, int q3 /* pixels after edge */
private static boolean filter_yes(int I, // limit on interior differences
int E, // limit at the edge
int p3, int p2, int p1, int p0, // pixels before edge
int q0, int q1, int q2, int q3 // pixels after edge
) {
return (abs(p0 - q0) * 2 + abs(p1 - q1) / 2) <= E && abs(p3 - p2) <= I
&& abs(p2 - p1) <= I && abs(p1 - p0) <= I && abs(q3 - q2) <= I
@ -106,6 +104,7 @@ final class LoopFilter {
Segment seg = new Segment();
int[][] rdest = rsb.getDest();
int[][] ldest = lsb.getDest();
seg.P0 = ldest[3][a];
seg.P1 = ldest[2][a];
seg.P2 = ldest[1][a];
@ -114,6 +113,7 @@ final class LoopFilter {
seg.Q1 = rdest[1][a];
seg.Q2 = rdest[2][a];
seg.Q3 = rdest[3][a];
return seg;
}
@ -130,6 +130,7 @@ final class LoopFilter {
seg.Q1 = bdest[a][1];
seg.Q2 = bdest[a][2];
seg.Q3 = bdest[a][3];
return seg;
}
@ -137,41 +138,54 @@ final class LoopFilter {
* Filtering is altered if (at least) one of the differences on either side
* of the edge exceeds a threshold (we have "high edge variance").
*/
private static boolean hev(int threshold, int p1, int p0, /*
* pixels before
* edge
*/
int q0, int q1 /* pixels after edge */
private static boolean hev(int threshold,
int p1, int p0, // pixels before edge
int q0, int q1 // pixels after edge
) {
return abs(p1 - p0) > threshold || abs(q1 - q0) > threshold;
}
public static void loopFilter(VP8Frame frame) {
// frame.fireLFProgressUpdate(0);
if (frame.getFilterType() == 2) {
loopFilterUV(frame);
// frame.fireLFProgressUpdate(50);
loopFilterY(frame);
} else if (frame.getFilterType() == 1) {
loopFilterSimple(frame);
// 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);
}
// frame.fireLFProgressUpdate(100);
else {
loopFilterUVBlock(cmb, lmb, tmb, sharpness, frameType);
loopFilterYBlock(cmb, lmb, tmb, sharpness, frameType);
}
}
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 rmb = frame.getMacroBlock(x, y);
MacroBlock bmb = frame.getMacroBlock(x, y);
int loop_filter_level = rmb.getFilterLevel();
// MacroBlock bmb = frame.getMacroBlock(x, y); // TODO: Same..?
int loop_filter_level = cmb.getFilterLevel();
if (loop_filter_level != 0) {
int interior_limit = rmb.getFilterLevel();
int interior_limit = cmb.getFilterLevel();
int sharpnessLevel = frame.getSharpnessLevel();
if (sharpnessLevel > 0) {
interior_limit >>= sharpnessLevel > 4 ? 2 : 1;
if (interior_limit > 9 - sharpnessLevel) {
@ -182,21 +196,20 @@ final class LoopFilter {
interior_limit = 1;
}
/* Luma and Chroma use the same inter-subblock edge limit */
// Luma and Chroma use the same inter-subblock edge limit
int sub_bedge_limit = (loop_filter_level * 2) + interior_limit;
if (sub_bedge_limit < 1) {
sub_bedge_limit = 1;
}
/* Luma and Chroma use the same inter-macroblock edge limit */
// Luma and Chroma use the same inter-macroblock edge limit
int mbedge_limit = sub_bedge_limit + 4;
// left
if (x > 0) {
MacroBlock lmb = frame.getMacroBlock(x - 1, y);
if (lmb != null) {
for (int b = 0; b < 4; b++) {
SubBlock rsb = rmb.getSubBlock(SubBlock.PLANE.Y1, 0, b);
SubBlock lsb = lmb.getSubBlock(SubBlock.PLANE.Y1, 3, b);
SubBlock rsb = cmb.getSubBlock(SubBlock.Plane.Y1, 0, b);
SubBlock lsb = lmb.getSubBlock(SubBlock.Plane.Y1, 3, b);
for (int a = 0; a < 4; a++) {
Segment seg = getSegH(rsb, lsb, a);
// MBfilter(hev_threshold, interior_limit,
@ -209,14 +222,11 @@ final class LoopFilter {
}
// sb left
if (!rmb.isSkip_inner_lf()) {
if (!cmb.isSkip_inner_lf()) {
for (int a = 1; a < 4; a++) {
for (int b = 0; b < 4; b++) {
SubBlock lsb = rmb.getSubBlock(SubBlock.PLANE.Y1,
a - 1, b);
SubBlock rsb = rmb.getSubBlock(SubBlock.PLANE.Y1,
a, b);
SubBlock lsb = cmb.getSubBlock(SubBlock.Plane.Y1, a - 1, b);
SubBlock rsb = cmb.getSubBlock(SubBlock.Plane.Y1, a, b);
for (int c = 0; c < 4; c++) {
// System.out.println("sbleft a:"+a+" b:"+b+" c:"+c);
Segment seg = getSegH(rsb, lsb, c);
@ -231,11 +241,10 @@ final class LoopFilter {
}
// top
if (y > 0) {
MacroBlock tmb = frame.getMacroBlock(x, y - 1);
if (tmb != null) {
for (int b = 0; b < 4; b++) {
SubBlock tsb = tmb.getSubBlock(SubBlock.PLANE.Y1, b, 3);
SubBlock bsb = bmb.getSubBlock(SubBlock.PLANE.Y1, b, 0);
SubBlock tsb = tmb.getSubBlock(SubBlock.Plane.Y1, b, 3);
SubBlock bsb = cmb.getSubBlock(SubBlock.Plane.Y1, b, 0);
for (int a = 0; a < 4; a++) {
Segment seg = getSegV(bsb, tsb, a);
simple_segment(mbedge_limit, seg);
@ -248,13 +257,11 @@ final class LoopFilter {
}
// sb top
if (!rmb.isSkip_inner_lf()) {
if (!cmb.isSkip_inner_lf()) {
for (int a = 1; a < 4; a++) {
for (int b = 0; b < 4; b++) {
SubBlock tsb = bmb.getSubBlock(SubBlock.PLANE.Y1,
b, a - 1);
SubBlock bsb = bmb.getSubBlock(SubBlock.PLANE.Y1,
b, a);
SubBlock tsb = cmb.getSubBlock(SubBlock.Plane.Y1, b, a - 1);
SubBlock bsb = cmb.getSubBlock(SubBlock.Plane.Y1, b, a);
for (int c = 0; c < 4; c++) {
// System.out.println("sbtop");
Segment seg = getSegV(bsb, tsb, c);
@ -269,20 +276,23 @@ final class LoopFilter {
}
}
}
}
}
private static void loopFilterUV(VP8Frame frame) {
for (int y = 0; y < frame.getMacroBlockRows(); y++) {
// frame.fireLFProgressUpdate((100.0f * ((float) (y + 1) / (float) (frame
// .getMacroBlockRows()))) / 2);
for (int x = 0; x < frame.getMacroBlockCols(); x++) {
MacroBlock rmb = frame.getMacroBlock(x, y);
MacroBlock bmb = frame.getMacroBlock(x, y);
int sharpnessLevel = frame.getSharpnessLevel();
int loop_filter_level = rmb.getFilterLevel();
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) {
int interior_limit = rmb.getFilterLevel();
int interior_limit = cmb.getFilterLevel();
if (sharpnessLevel > 0) {
interior_limit >>= sharpnessLevel > 4 ? 2 : 1;
if (interior_limit > 9 - sharpnessLevel) {
@ -294,116 +304,101 @@ final class LoopFilter {
}
int hev_threshold = 0;
if (frame.getFrameType() == 0) /* current frame is a key frame */ {
if (frameType == 0) { // current frame is a key frame
if (loop_filter_level >= 40) {
hev_threshold = 2;
} else if (loop_filter_level >= 15) {
}
else if (loop_filter_level >= 15) {
hev_threshold = 1;
}
} else /* current frame is an interframe */ {
}
else { // current frame is an interframe
if (loop_filter_level >= 40) {
hev_threshold = 3;
} else if (loop_filter_level >= 20) {
}
else if (loop_filter_level >= 20) {
hev_threshold = 2;
} else if (loop_filter_level >= 15) {
}
else if (loop_filter_level >= 15) {
hev_threshold = 1;
}
}
/* Luma and Chroma use the same inter-macroblock edge limit */
int mbedge_limit = ((loop_filter_level + 2) * 2)
+ interior_limit;
/* Luma and Chroma use the same inter-subblock edge limit */
// Luma and Chroma use the same inter-macroblock edge limit
int mbedge_limit = ((loop_filter_level + 2) * 2) + interior_limit;
// Luma and Chroma use the same inter-subblock edge limit
int sub_bedge_limit = (loop_filter_level * 2) + interior_limit;
if (x > 0) {
MacroBlock lmb = frame.getMacroBlock(x - 1, y);
if (lmb != null) {
for (int b = 0; b < 2; b++) {
SubBlock rsbU = rmb.getSubBlock(SubBlock.PLANE.U, 0, b);
SubBlock lsbU = lmb.getSubBlock(SubBlock.PLANE.U, 1, b);
SubBlock rsbV = rmb.getSubBlock(SubBlock.PLANE.V, 0, b);
SubBlock lsbV = lmb.getSubBlock(SubBlock.PLANE.V, 1, b);
SubBlock rsbU = cmb.getSubBlock(SubBlock.Plane.U, 0, b);
SubBlock lsbU = lmb.getSubBlock(SubBlock.Plane.U, 1, b);
SubBlock rsbV = cmb.getSubBlock(SubBlock.Plane.V, 0, b);
SubBlock lsbV = lmb.getSubBlock(SubBlock.Plane.V, 1, b);
for (int a = 0; a < 4; a++) {
Segment seg = getSegH(rsbU, lsbU, a);
MBfilter(hev_threshold, interior_limit,
mbedge_limit, seg);
MBfilter(hev_threshold, interior_limit, mbedge_limit, seg);
setSegH(rsbU, lsbU, seg, a);
seg = getSegH(rsbV, lsbV, a);
MBfilter(hev_threshold, interior_limit,
mbedge_limit, seg);
MBfilter(hev_threshold, interior_limit, mbedge_limit, seg);
setSegH(rsbV, lsbV, seg, a);
}
}
}
// sb left
if (!rmb.isSkip_inner_lf()) {
for (int a = 1; a < 2; a++) {
if (!cmb.isSkip_inner_lf()) {
for (int a = 1; a < 2; a++) { // TODO: This does not loop?
for (int b = 0; b < 2; b++) {
SubBlock lsbU = rmb.getSubBlock(SubBlock.PLANE.U,
a - 1, b);
SubBlock rsbU = rmb.getSubBlock(SubBlock.PLANE.U,
a, b);
SubBlock lsbV = rmb.getSubBlock(SubBlock.PLANE.V,
a - 1, b);
SubBlock rsbV = rmb.getSubBlock(SubBlock.PLANE.V,
a, b);
SubBlock lsbU = cmb.getSubBlock(SubBlock.Plane.U, a - 1, b);
SubBlock rsbU = cmb.getSubBlock(SubBlock.Plane.U, a, b);
SubBlock lsbV = cmb.getSubBlock(SubBlock.Plane.V, a - 1, b);
SubBlock rsbV = cmb.getSubBlock(SubBlock.Plane.V, a, b);
for (int c = 0; c < 4; c++) {
Segment seg = getSegH(rsbU, lsbU, c);
subblock_filter(hev_threshold, interior_limit,
sub_bedge_limit, seg);
subblock_filter(hev_threshold, interior_limit, sub_bedge_limit, seg);
setSegH(rsbU, lsbU, seg, c);
seg = getSegH(rsbV, lsbV, c);
subblock_filter(hev_threshold, interior_limit,
sub_bedge_limit, seg);
subblock_filter(hev_threshold, interior_limit, sub_bedge_limit, seg);
setSegH(rsbV, lsbV, seg, c);
}
}
}
}
// top
if (y > 0) {
MacroBlock tmb = frame.getMacroBlock(x, y - 1);
if (tmb != null) {
for (int b = 0; b < 2; b++) {
SubBlock tsbU = tmb.getSubBlock(SubBlock.PLANE.U, b, 1);
SubBlock bsbU = bmb.getSubBlock(SubBlock.PLANE.U, b, 0);
SubBlock tsbV = tmb.getSubBlock(SubBlock.PLANE.V, b, 1);
SubBlock bsbV = bmb.getSubBlock(SubBlock.PLANE.V, b, 0);
SubBlock tsbU = tmb.getSubBlock(SubBlock.Plane.U, b, 1);
SubBlock bsbU = cmb.getSubBlock(SubBlock.Plane.U, b, 0);
SubBlock tsbV = tmb.getSubBlock(SubBlock.Plane.V, b, 1);
SubBlock bsbV = cmb.getSubBlock(SubBlock.Plane.V, b, 0);
for (int a = 0; a < 4; a++) {
// System.out.println("l");
Segment seg = getSegV(bsbU, tsbU, a);
MBfilter(hev_threshold, interior_limit,
mbedge_limit, seg);
MBfilter(hev_threshold, interior_limit, mbedge_limit, seg);
setSegV(bsbU, tsbU, seg, a);
seg = getSegV(bsbV, tsbV, a);
MBfilter(hev_threshold, interior_limit,
mbedge_limit, seg);
MBfilter(hev_threshold, interior_limit, mbedge_limit, seg);
setSegV(bsbV, tsbV, seg, a);
}
}
}
// sb top
if (!rmb.isSkip_inner_lf()) {
for (int a = 1; a < 2; a++) {
// sb top
if (!cmb.isSkip_inner_lf()) {
for (int a = 1; a < 2; a++) { // TODO: This does not loop...
for (int b = 0; b < 2; b++) {
SubBlock tsbU = bmb.getSubBlock(SubBlock.PLANE.U,
b, a - 1);
SubBlock bsbU = bmb.getSubBlock(SubBlock.PLANE.U,
b, a);
SubBlock tsbV = bmb.getSubBlock(SubBlock.PLANE.V,
b, a - 1);
SubBlock bsbV = bmb.getSubBlock(SubBlock.PLANE.V,
b, a);
SubBlock tsbU = cmb.getSubBlock(SubBlock.Plane.U, b, a - 1);
SubBlock bsbU = cmb.getSubBlock(SubBlock.Plane.U, b, a);
SubBlock tsbV = cmb.getSubBlock(SubBlock.Plane.V, b, a - 1);
SubBlock bsbV = cmb.getSubBlock(SubBlock.Plane.V, b, a);
for (int c = 0; c < 4; c++) {
Segment seg = getSegV(bsbU, tsbU, c);
subblock_filter(hev_threshold, interior_limit,
sub_bedge_limit, seg);
subblock_filter(hev_threshold, interior_limit, sub_bedge_limit, seg);
setSegV(bsbU, tsbU, seg, c);
seg = getSegV(bsbV, tsbV, c);
subblock_filter(hev_threshold, interior_limit,
sub_bedge_limit, seg);
subblock_filter(hev_threshold, interior_limit, sub_bedge_limit, seg);
setSegV(bsbV, tsbV, seg, c);
}
}
@ -411,21 +406,23 @@ final class LoopFilter {
}
}
}
}
}
private static void loopFilterY(VP8Frame frame) {
for (int y = 0; y < frame.getMacroBlockRows(); y++) {
// frame.fireLFProgressUpdate(50 + (100.0f * ((float) (y + 1) / (float) (frame
// .getMacroBlockRows()))) / 2);
for (int x = 0; x < frame.getMacroBlockCols(); x++) {
MacroBlock rmb = frame.getMacroBlock(x, y);
MacroBlock bmb = frame.getMacroBlock(x, y);
int sharpnessLevel = frame.getSharpnessLevel();
int loop_filter_level = rmb.getFilterLevel();
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();
if (loop_filter_level != 0) {
int interior_limit = rmb.getFilterLevel();
int interior_limit = cmb.getFilterLevel();
if (sharpnessLevel > 0) {
interior_limit >>= sharpnessLevel > 4 ? 2 : 1;
@ -438,86 +435,79 @@ final class LoopFilter {
}
int hev_threshold = 0;
if (frame.getFrameType() == 0) /* current frame is a key frame */ {
if (frameType == 0) { // current frame is a key frame
if (loop_filter_level >= 40) {
hev_threshold = 2;
} else if (loop_filter_level >= 15) {
}
else if (loop_filter_level >= 15) {
hev_threshold = 1;
}
} else /* current frame is an interframe */ {
}
else { // current frame is an interframe
if (loop_filter_level >= 40) {
hev_threshold = 3;
} else if (loop_filter_level >= 20) {
}
else if (loop_filter_level >= 20) {
hev_threshold = 2;
} else if (loop_filter_level >= 15) {
}
else if (loop_filter_level >= 15) {
hev_threshold = 1;
}
}
/* Luma and Chroma use the same inter-macroblock edge limit */
int mbedge_limit = ((loop_filter_level + 2) * 2)
+ interior_limit;
int mbedge_limit = ((loop_filter_level + 2) * 2) + interior_limit;
/* Luma and Chroma use the same inter-subblock edge limit */
int sub_bedge_limit = (loop_filter_level * 2) + interior_limit;
// left
if (x > 0) {
MacroBlock lmb = frame.getMacroBlock(x - 1, y);
if (lmb != null) {
for (int b = 0; b < 4; b++) {
SubBlock rsb = rmb.getSubBlock(SubBlock.PLANE.Y1, 0, b);
SubBlock lsb = lmb.getSubBlock(SubBlock.PLANE.Y1, 3, b);
SubBlock rsb = cmb.getSubBlock(SubBlock.Plane.Y1, 0, b);
SubBlock lsb = lmb.getSubBlock(SubBlock.Plane.Y1, 3, b);
for (int a = 0; a < 4; a++) {
Segment seg = getSegH(rsb, lsb, a);
MBfilter(hev_threshold, interior_limit,
mbedge_limit, seg);
MBfilter(hev_threshold, interior_limit, mbedge_limit, seg);
setSegH(rsb, lsb, seg, a);
}
}
}
// sb left
if (!rmb.isSkip_inner_lf()) {
if (!cmb.isSkip_inner_lf()) {
for (int a = 1; a < 4; a++) {
for (int b = 0; b < 4; b++) {
SubBlock lsb = rmb.getSubBlock(SubBlock.PLANE.Y1,
a - 1, b);
SubBlock rsb = rmb.getSubBlock(SubBlock.PLANE.Y1,
a, b);
SubBlock lsb = cmb.getSubBlock(SubBlock.Plane.Y1, a - 1, b);
SubBlock rsb = cmb.getSubBlock(SubBlock.Plane.Y1, a, b);
for (int c = 0; c < 4; c++) {
// System.out.println("sbleft a:"+a+" b:"+b+" c:"+c);
Segment seg = getSegH(rsb, lsb, c);
subblock_filter(hev_threshold, interior_limit,
sub_bedge_limit, seg);
subblock_filter(hev_threshold, interior_limit, sub_bedge_limit, seg);
setSegH(rsb, lsb, seg, c);
}
}
}
}
// top
if (y > 0) {
MacroBlock tmb = frame.getMacroBlock(x, y - 1);
if (tmb != null) {
for (int b = 0; b < 4; b++) {
SubBlock tsb = tmb.getSubBlock(SubBlock.PLANE.Y1, b, 3);
SubBlock bsb = bmb.getSubBlock(SubBlock.PLANE.Y1, b, 0);
SubBlock tsb = tmb.getSubBlock(SubBlock.Plane.Y1, b, 3);
SubBlock bsb = cmb.getSubBlock(SubBlock.Plane.Y1, b, 0);
for (int a = 0; a < 4; a++) {
Segment seg = getSegV(bsb, tsb, a);
MBfilter(hev_threshold, interior_limit,
mbedge_limit, seg);
MBfilter(hev_threshold, interior_limit, mbedge_limit, seg);
setSegV(bsb, tsb, seg, a);
}
}
}
// sb top
if (!rmb.isSkip_inner_lf()) {
if (!cmb.isSkip_inner_lf()) {
for (int a = 1; a < 4; a++) {
for (int b = 0; b < 4; b++) {
SubBlock tsb = bmb.getSubBlock(SubBlock.PLANE.Y1,
b, a - 1);
SubBlock bsb = bmb.getSubBlock(SubBlock.PLANE.Y1,
b, a);
SubBlock tsb = cmb.getSubBlock(SubBlock.Plane.Y1, b, a - 1);
SubBlock bsb = cmb.getSubBlock(SubBlock.Plane.Y1, b, a);
for (int c = 0; c < 4; c++) {
Segment seg = getSegV(bsb, tsb, c);
subblock_filter(hev_threshold, interior_limit,
sub_bedge_limit, seg);
subblock_filter(hev_threshold, interior_limit, sub_bedge_limit, seg);
setSegV(bsb, tsb, seg, c);
}
}
@ -525,20 +515,17 @@ final class LoopFilter {
}
}
}
}
}
private static void MBfilter(int hev_threshold, /* detect high edge variance */
int interior_limit, /* possibly disable filter */
int edge_limit, Segment seg) {
int p3 = u2s(seg.P3), p2 = u2s(seg.P2), p1 = u2s(seg.P1), p0 = u2s(seg.P0);
int q0 = u2s(seg.Q0), q1 = u2s(seg.Q1), q2 = u2s(seg.Q2), q3 = u2s(seg.Q3);
if (filter_yes(interior_limit, edge_limit, q3, q2, q1, q0, p0, p1, p2,
p3)) {
if (filter_yes(interior_limit, edge_limit, q3, q2, q1, q0, p0, p1, p2, p3)) {
if (!hev(hev_threshold, p1, p0, q0, q1)) {
// Same as the initial calculation in "common_adjust",
// w is something like twice the edge difference
int w = c(c(p1 - q1) + 3 * (q0 - p0));
int w = clamp(clamp(p1 - q1) + 3 * (q0 - p0));
// 9/64 is approximately 9/63 = 1/7 and 1<<7 = 128 = 2*64.
// So this a, used to adjust the pixels adjacent to the edge,
@ -556,9 +543,9 @@ final class LoopFilter {
a = (9 * w + 63) >> 7;
seg.Q2 = s2u(q2 - a);
seg.P2 = s2u(p2 + a);
} else
}
else {
// if hev, do simple filter
{
common_adjust(true, seg); // using outer taps
}
}
@ -566,7 +553,7 @@ final class LoopFilter {
/* Clamp, then convert signed number back to pixel value. */
private static int s2u(int v) {
return c(v) + 128;
return clamp(v) + 128;
}
private static void setSegH(SubBlock rsb, SubBlock lsb, Segment seg, int a) {
@ -580,7 +567,6 @@ final class LoopFilter {
rdest[1][a] = seg.Q1;
rdest[2][a] = seg.Q2;
rdest[3][a] = seg.Q3;
}
private static void setSegV(SubBlock bsb, SubBlock tsb, Segment seg, int a) {
@ -594,28 +580,20 @@ final class LoopFilter {
bdest[a][1] = seg.Q1;
bdest[a][2] = seg.Q2;
bdest[a][3] = seg.Q3;
}
private static void simple_segment(int edge_limit, /*
* do nothing if edge
* difference exceeds
* limit
*/
private static void simple_segment(int edge_limit, // do nothing if edge difference exceeds limit
Segment seg) {
if ((abs(seg.P0 - seg.Q0) * 2 + abs(seg.P1 - seg.Q1) / 2) <= edge_limit) {
common_adjust(true, seg); /* use outer taps */
common_adjust(true, seg); // use outer taps
}
else {
// TODO?
}
}
private static void subblock_filter(int hev_threshold, /*
* detect high edge
* variance
*/
int interior_limit, /* possibly disable filter */
private static void subblock_filter(int hev_threshold, // detect high edge variance
int interior_limit, // possibly disable filter
int edge_limit, Segment seg) {
int p3 = u2s(seg.P3), p2 = u2s(seg.P2), p1 = u2s(seg.P1), p0 = u2s(seg.P0);
int q0 = u2s(seg.Q0), q1 = u2s(seg.Q1), q2 = u2s(seg.Q2), q3 = u2s(seg.Q3);
@ -630,7 +608,6 @@ final class LoopFilter {
else {
// TODO?
}
}
/* Convert pixel value (0 <= v <= 255) to an 8-bit signed number. */

View File

@ -36,19 +36,19 @@ import java.io.IOException;
final class MacroBlock {
private int filterLevel;
private boolean keepDebugInfo = false;
private final boolean keepDebugInfo;
private int segmentId;
private int skipCoeff;
private boolean skipInnerLoopFilter;
SubBlock[][] uSubBlocks;
final SubBlock[][] uSubBlocks;
private int uVFilterLevel;
private int uvMode;
SubBlock[][] vSubBlocks;
private int x, y;
SubBlock y2SubBlock;
final SubBlock[][] vSubBlocks;
private final int x, y;
final SubBlock y2SubBlock;
private int yMode;
SubBlock[][] ySubBlocks;
final SubBlock[][] ySubBlocks;
MacroBlock(int x, int y, boolean keepDebugInfo) {
this.x = x - 1;
@ -58,8 +58,8 @@ final class MacroBlock {
ySubBlocks = new SubBlock[4][4];
uSubBlocks = new SubBlock[2][2];
vSubBlocks = new SubBlock[2][2];
SubBlock above = null;
SubBlock left = null;
SubBlock above;
SubBlock left;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
@ -72,7 +72,7 @@ final class MacroBlock {
above = ySubBlocks[j][i - 1];
}
ySubBlocks[j][i] = new SubBlock(this, above, left,
SubBlock.PLANE.Y1);
SubBlock.Plane.Y1);
}
}
@ -87,7 +87,7 @@ final class MacroBlock {
above = uSubBlocks[j][i - 1];
}
uSubBlocks[j][i] = new SubBlock(this, above, left,
SubBlock.PLANE.U);
SubBlock.Plane.U);
}
}
@ -101,12 +101,10 @@ final class MacroBlock {
if (i > 0) {
above = vSubBlocks[j][i - 1];
}
vSubBlocks[j][i] = new SubBlock(this, above, left,
SubBlock.PLANE.V);
vSubBlocks[j][i] = new SubBlock(this, above, left, SubBlock.Plane.V);
}
}
y2SubBlock = new SubBlock(this, null, null, SubBlock.PLANE.Y2);
y2SubBlock = new SubBlock(this, null, null, SubBlock.Plane.Y2);
}
public void decodeMacroBlock(VP8Frame frame) throws IOException {
@ -115,57 +113,51 @@ final class MacroBlock {
if (mb.getYMode() != Globals.B_PRED) {
mb.skipInnerLoopFilter = true;
}
} else if (mb.getYMode() != Globals.B_PRED) {
decodeMacroBlockTokens(frame, true);
} else {
decodeMacroBlockTokens(frame, false);
}
else {
decodeMacroBlockTokens(frame, mb.getYMode() != Globals.B_PRED);
}
}
private void decodeMacroBlockTokens(VP8Frame frame, boolean withY2)
throws IOException {
skipInnerLoopFilter = false;
private void decodeMacroBlockTokens(VP8Frame frame, boolean withY2) throws IOException {
if (withY2) {
skipInnerLoopFilter = skipInnerLoopFilter
| decodePlaneTokens(frame, 1, SubBlock.PLANE.Y2, false);
skipInnerLoopFilter = decodePlaneTokens(frame, 1, SubBlock.Plane.Y2, false);
}
skipInnerLoopFilter = skipInnerLoopFilter
| decodePlaneTokens(frame, 4, SubBlock.PLANE.Y1, withY2);
skipInnerLoopFilter = skipInnerLoopFilter
| decodePlaneTokens(frame, 2, SubBlock.PLANE.U, false);
skipInnerLoopFilter = skipInnerLoopFilter
| decodePlaneTokens(frame, 2, SubBlock.PLANE.V, false);
skipInnerLoopFilter |= decodePlaneTokens(frame, 4, SubBlock.Plane.Y1, withY2);
skipInnerLoopFilter |= decodePlaneTokens(frame, 2, SubBlock.Plane.U, false);
skipInnerLoopFilter |= decodePlaneTokens(frame, 2, SubBlock.Plane.V, false);
skipInnerLoopFilter = !skipInnerLoopFilter;
}
private boolean decodePlaneTokens(VP8Frame frame, int dimentions,
SubBlock.PLANE plane, boolean withY2) throws IOException {
SubBlock.Plane plane, boolean withY2) throws IOException {
MacroBlock mb = this;
boolean r = false;
for (int y = 0; y < dimentions; y++) {
for (int x = 0; x < dimentions; x++) {
int L = 0;
int A = 0;
int lc = 0;
SubBlock sb = mb.getSubBlock(plane, x, y);
SubBlock left = frame.getLeftSubBlock(sb, plane);
SubBlock above = frame.getAboveSubBlock(sb, plane);
if (left.hasNoZeroToken()) {
if (left.hasNoZeroToken()) {
L = 1;
}
lc += L;
if (above.hasNoZeroToken()) {
A = 1;
}
lc += A;
sb.decodeSubBlock(frame.getTokenBoolDecoder(),
frame.getCoefProbs(), lc,
SubBlock.planeToType(plane, withY2), withY2);
sb.decodeSubBlock(frame.getTokenBoolDecoder(), frame.getCoefProbs(), lc, SubBlock.planeToType(plane, withY2), withY2);
r = r | sb.hasNoZeroToken();
}
}
@ -180,7 +172,7 @@ final class MacroBlock {
.getY2ac_delta_q();
int dcQValue = frame.getSegmentQuants().getSegQuants()[this.getSegmentId()].getY2dc();
int input[] = new int[16];
int[] input = new int[16];
input[0] = sb.getTokens()[0] * dcQValue;
for (int x = 1; x < 16; x++) {
@ -206,8 +198,8 @@ final class MacroBlock {
}
}
mb.recon_mb();
} else {
}
else {
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
SubBlock sb = mb.getYSubBlock(i, j);
@ -260,7 +252,7 @@ final class MacroBlock {
return this.filterLevel;
}
public SubBlock getBottomSubBlock(int x, SubBlock.PLANE plane) {
public SubBlock getBottomSubBlock(int x, SubBlock.Plane plane) {
switch (plane) {
case Y1:
return ySubBlocks[x][3];
@ -275,7 +267,7 @@ final class MacroBlock {
throw new IllegalArgumentException("Bad plane: " + plane);
}
public SubBlock getLeftSubBlock(int y, SubBlock.PLANE plane) {
public SubBlock getLeftSubBlock(int y, SubBlock.Plane plane) {
switch (plane) {
case Y1:
return ySubBlocks[0][y];
@ -290,7 +282,7 @@ final class MacroBlock {
throw new IllegalArgumentException("Bad plane: " + plane);
}
public SubBlock getRightSubBlock(int y, SubBlock.PLANE plane) {
public SubBlock getRightSubBlock(int y, SubBlock.Plane plane) {
switch (plane) {
case Y1:
return ySubBlocks[3][y];
@ -309,7 +301,7 @@ final class MacroBlock {
return skipCoeff;
}
public SubBlock getSubBlock(SubBlock.PLANE plane, int i, int j) {
public SubBlock getSubBlock(SubBlock.Plane plane, int i, int j) {
switch (plane) {
case Y1:
return getYSubBlock(i, j);
@ -325,7 +317,7 @@ final class MacroBlock {
}
public int getSubblockX(SubBlock sb) {
if (sb.getPlane() == SubBlock.PLANE.Y1) {
if (sb.getPlane() == SubBlock.Plane.Y1) {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (ySubBlocks[x][y] == sb) {
@ -333,7 +325,8 @@ final class MacroBlock {
}
}
}
} else if (sb.getPlane() == SubBlock.PLANE.U) {
}
else if (sb.getPlane() == SubBlock.Plane.U) {
for (int y = 0; y < 2; y++) {
for (int x = 0; x < 2; x++) {
if (uSubBlocks[x][y] == sb) {
@ -341,7 +334,8 @@ final class MacroBlock {
}
}
}
} else if (sb.getPlane() == SubBlock.PLANE.V) {
}
else if (sb.getPlane() == SubBlock.Plane.V) {
for (int y = 0; y < 2; y++) {
for (int x = 0; x < 2; x++) {
if (vSubBlocks[x][y] == sb) {
@ -349,16 +343,16 @@ final class MacroBlock {
}
}
}
} else if (sb.getPlane() == SubBlock.PLANE.Y2) {
}
else if (sb.getPlane() == SubBlock.Plane.Y2) {
return 0;
}
return -100;
}
public int getSubblockY(SubBlock sb) {
if (sb.getPlane() == SubBlock.PLANE.Y1) {
if (sb.getPlane() == SubBlock.Plane.Y1) {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (ySubBlocks[x][y] == sb) {
@ -366,7 +360,8 @@ final class MacroBlock {
}
}
}
} else if (sb.getPlane() == SubBlock.PLANE.U) {
}
else if (sb.getPlane() == SubBlock.Plane.U) {
for (int y = 0; y < 2; y++) {
for (int x = 0; x < 2; x++) {
if (uSubBlocks[x][y] == sb) {
@ -374,7 +369,8 @@ final class MacroBlock {
}
}
}
} else if (sb.getPlane() == SubBlock.PLANE.V) {
}
else if (sb.getPlane() == SubBlock.Plane.V) {
for (int y = 0; y < 2; y++) {
for (int x = 0; x < 2; x++) {
if (vSubBlocks[x][y] == sb) {
@ -382,7 +378,8 @@ final class MacroBlock {
}
}
}
} else if (sb.getPlane() == SubBlock.PLANE.Y2) {
}
else if (sb.getPlane() == SubBlock.Plane.Y2) {
return 0;
}
@ -445,8 +442,8 @@ final class MacroBlock {
boolean left_available = false;
int Uaverage = 0;
int Vaverage = 0;
int expected_udc = 0;
int expected_vdc = 0;
int expected_udc;
int expected_vdc;
if (x > 0) {
left_available = true;
}
@ -486,19 +483,20 @@ final class MacroBlock {
expected_udc = (Uaverage + (1 << (shift - 1))) >> shift;
expected_vdc = (Vaverage + (1 << (shift - 1))) >> shift;
} else {
}
else {
expected_udc = 128;
expected_vdc = 128;
}
int ufill[][] = new int[4][4];
int[][] ufill = new int[4][4];
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
ufill[x][y] = expected_udc;
}
}
int vfill[][] = new int[4][4];
int[][] vfill = new int[4][4];
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
vfill[x][y] = expected_vdc;
@ -529,8 +527,8 @@ final class MacroBlock {
for (int x = 0; x < 2; x++) {
SubBlock usb = uSubBlocks[y][x];
SubBlock vsb = vSubBlocks[y][x];
int ublock[][] = new int[4][4];
int vblock[][] = new int[4][4];
int[][] ublock = new int[4][4];
int[][] vblock = new int[4][4];
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
ublock[j][i] = aboveUSb[y]
@ -560,8 +558,8 @@ final class MacroBlock {
for (int x = 0; x < 2; x++) {
SubBlock usb = uSubBlocks[x][y];
SubBlock vsb = vSubBlocks[x][y];
int ublock[][] = new int[4][4];
int vblock[][] = new int[4][4];
int[][] ublock = new int[4][4];
int[][] vblock = new int[4][4];
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
ublock[i][j] = leftUSb[y]
@ -610,10 +608,8 @@ final class MacroBlock {
+ aboveVSb[d].getDest()[c][3] - alv;
vpred = Globals.clamp(vpred, 255);
vSubBlocks[d][b].setPixel(c, a, vpred);
}
}
}
}
@ -635,7 +631,7 @@ final class MacroBlock {
boolean left_available = false;
int average = 0;
int expected_dc = 0;
int expected_dc;
if (x > 0) {
left_available = true;
}
@ -671,11 +667,12 @@ final class MacroBlock {
}
expected_dc = (average + (1 << (shift - 1))) >> shift;
} else {
}
else {
expected_dc = 128;
}
int fill[][] = new int[4][4];
int[][] fill = new int[4][4];
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
fill[x][y] = expected_dc;
@ -700,7 +697,7 @@ final class MacroBlock {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
SubBlock sb = ySubBlocks[x][y];
int block[][] = new int[4][4];
int[][] block = new int[4][4];
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
block[i][j] = aboveYSb[x].getPredict(
@ -708,7 +705,6 @@ final class MacroBlock {
}
}
sb.setPredict(block);
}
}
@ -725,7 +721,7 @@ final class MacroBlock {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
SubBlock sb = ySubBlocks[x][y];
int block[][] = new int[4][4];
int[][] block = new int[4][4];
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
block[i][j] = leftYSb[y].getPredict(
@ -736,10 +732,10 @@ final class MacroBlock {
}
}
SubBlock[] leftUSb = new SubBlock[2];
for (int x = 0; x < 2; x++) {
leftUSb[x] = leftMb.getYSubBlock(1, x);
}
// SubBlock[] leftUSb = new SubBlock[2];
// for (int x = 0; x < 2; x++) {
// leftUSb[x] = leftMb.getYSubBlock(1, x);
// }
break;
case Globals.TM_PRED:
@ -756,23 +752,20 @@ final class MacroBlock {
for (int x = 0; x < 4; x++) {
leftYSb[x] = leftMb.getYSubBlock(3, x);
}
fill = new int[4][4];
// fill = new int[4][4];
for (int b = 0; b < 4; b++) {
for (int a = 0; a < 4; a++) {
for (int d = 0; d < 4; d++) {
for (int c = 0; c < 4; c++) {
int pred = leftYSb[b].getDest()[3][a]
+ aboveYSb[d].getDest()[c][3] - al;
ySubBlocks[d][b].setPixel(c, a,
Globals.clamp(pred, 255));
}
}
}
}
@ -803,7 +796,6 @@ final class MacroBlock {
sb.reconstruct();
}
}
}
public void setFilterLevel(int value) {

View File

@ -31,6 +31,8 @@
package com.twelvemonkeys.imageio.plugins.webp.vp8;
import static com.twelvemonkeys.imageio.plugins.webp.vp8.Globals.clamp;
final class SegmentQuant {
private int filterStrength;
private int Qindex;
@ -41,17 +43,6 @@ final class SegmentQuant {
private int y2ac;
private int y2dc;
private int clip(int val, int max) {
int r = val;
if (val > max) {
r = max;
}
if (r < 0) {
r = 0;
}
return r;
}
public int getQindex() {
return Qindex;
}
@ -89,31 +80,31 @@ final class SegmentQuant {
}
public void setUvac_delta_q(int uvac_delta_q) {
this.uvac = Globals.vp8AcQLookup[clip(Qindex + uvac_delta_q, 127)];
this.uvac = Globals.vp8AcQLookup[clamp(Qindex + uvac_delta_q, 127)];
}
public void setUvdc_delta_q(int uvdc_delta_q) {
this.uvdc = Globals.vp8DcQLookup[clip(Qindex + uvdc_delta_q, 127)];
this.uvdc = Globals.vp8DcQLookup[clamp(Qindex + uvdc_delta_q, 127)];
}
public void setY1ac() {
this.y1ac = Globals.vp8AcQLookup[clip(Qindex, 127)];
this.y1ac = Globals.vp8AcQLookup[clamp(Qindex, 127)];
}
public void setY1dc(int y1dc) {
this.y1dc = Globals.vp8DcQLookup[clip(Qindex + y1dc, 127)];
this.y1dc = Globals.vp8DcQLookup[clamp(Qindex + y1dc, 127)];
this.setY1ac();
}
public void setY2ac_delta_q(int y2ac_delta_q) {
this.y2ac = Globals.vp8AcQLookup[clip(Qindex + y2ac_delta_q, 127)] * 155 / 100;
this.y2ac = Globals.vp8AcQLookup[clamp(Qindex + y2ac_delta_q, 127)] * 155 / 100;
if (this.y2ac < 8) {
this.y2ac = 8;
}
}
public void setY2dc(int y2dc_delta_q) {
this.y2dc = Globals.vp8DcQLookup[clip(Qindex + y2dc_delta_q, 127)] * 2;
this.y2dc = Globals.vp8DcQLookup[clamp(Qindex + y2dc_delta_q, 127)] * 2;
}
public int getFilterStrength() {

View File

@ -35,8 +35,17 @@ import java.io.IOException;
final class SegmentQuants {
private static DeltaQ get_delta_q(BoolDecoder bc, int prev)
throws IOException {
private int qIndex;
private final SegmentQuant[] segQuants = new SegmentQuant[Globals.MAX_MB_SEGMENTS];
public SegmentQuants() {
for (int x = 0; x < Globals.MAX_MB_SEGMENTS; x++) {
segQuants[x] = new SegmentQuant();
}
}
private static DeltaQ get_delta_q(BoolDecoder bc, int prev) throws IOException {
DeltaQ ret = new DeltaQ();
ret.v = 0;
ret.update = false;
@ -49,7 +58,7 @@ final class SegmentQuants {
}
}
/* Trigger a quantizer update if the delta-q value has changed */
// Trigger a quantizer update if the delta-q value has changed
if (ret.v != prev) {
ret.update = true;
}
@ -57,16 +66,6 @@ final class SegmentQuants {
return ret;
}
private int qIndex;
private SegmentQuant[] segQuants = new SegmentQuant[Globals.MAX_MB_SEGMENTS];
public SegmentQuants() {
for (int x = 0; x < Globals.MAX_MB_SEGMENTS; x++) {
segQuants[x] = new SegmentQuant();
}
}
public int getqIndex() {
return qIndex;
}
@ -98,7 +97,8 @@ final class SegmentQuants {
for (SegmentQuant s : segQuants) {
if (!segmentation_enabled) {
s.setQindex(qIndex);
} else if (!mb_segement_abs_delta) {
}
else if (!mb_segement_abs_delta) {
s.setQindex(s.getQindex() + qIndex);
}
@ -107,11 +107,6 @@ final class SegmentQuants {
s.setY2ac_delta_q(y2ac_delta_q);
s.setUvdc_delta_q(uvdc_delta_q);
s.setUvac_delta_q(uvac_delta_q);
}
}
public void setSegQuants(SegmentQuant[] segQuants) {
this.segQuants = segQuants;
}
}

View File

@ -34,53 +34,49 @@ package com.twelvemonkeys.imageio.plugins.webp.vp8;
import java.io.IOException;
final class SubBlock {
public enum PLANE {
public enum Plane {
U, V, Y1, Y2
}
public static int planeToType(PLANE plane, Boolean withY2) {
switch (plane) {
case Y2:
return 1;
case Y1:
if (withY2)
return 0;
else
return 3;
case U:
return 2;
case V:
return 2;
}
return -1;
}
private SubBlock above;
private final SubBlock above;
private int[][] dest;
private int[][] diff;
private boolean hasNoZeroToken;
private SubBlock left;
private MacroBlock macroBlock;
private final SubBlock left;
private final MacroBlock macroBlock;
private int mode;
private PLANE plane;
private int predict[][];
private int tokens[];
private final Plane plane;
private int[][] predict;
private int[] tokens = new int[16];
SubBlock(MacroBlock macroBlock, SubBlock above, SubBlock left,
SubBlock.PLANE plane) {
SubBlock(MacroBlock macroBlock, SubBlock above, SubBlock left, Plane plane) {
this.macroBlock = macroBlock;
this.plane = plane;
this.above = above;
this.left = left;
mode = 0;
tokens = new int[16];
for (int z = 0; z < 16; z++)
for (int z = 0; z < 16; z++) {
tokens[z] = 0;
}
}
private int DCTextra(BoolDecoder bc2, int p[]) throws IOException {
public static int planeToType(Plane plane, boolean withY2) {
switch (plane) {
case Y2:
return 1;
case Y1:
return withY2 ? 0 : 3;
case U:
case V:
return 2;
}
return -1;
}
private int DCTextra(BoolDecoder bc2, int[] p) throws IOException {
int v = 0;
int offset = 0;
do {
@ -94,8 +90,9 @@ final class SubBlock {
int ilc, int type, boolean withY2) throws IOException {
SubBlock sb = this;
int startAt = 0;
if (withY2)
if (withY2) {
startAt = 1;
}
int lc = ilc;
int c = 0;
int v = 1;
@ -118,24 +115,30 @@ final class SubBlock {
int dv = decodeToken(bc2, v);
lc = 0;
skip = false;
if (dv == 1 || dv == -1)
if (dv == 1 || dv == -1) {
lc = 1;
else if (dv > 1 || dv < -1)
}
else if (dv > 1 || dv < -1) {
lc = 2;
else if (dv == Globals.DCT_0)
}
else if (dv == Globals.DCT_0) {
skip = true;
}
int tokens[] = sb.getTokens();
int[] tokens = sb.getTokens();
if (v != Globals.dct_eob)
if (v != Globals.dct_eob) {
tokens[Globals.vp8defaultZigZag1d[c + startAt]] = dv;
}
c++;
}
hasNoZeroToken = false;
for (int x = 0; x < 16; x++)
if (tokens[x] != 0)
for (int x = 0; x < 16; x++) {
if (tokens[x] != 0) {
hasNoZeroToken = true;
}
}
}
private int decodeToken(BoolDecoder bc2, int v) throws IOException {
int r = v;
@ -159,9 +162,10 @@ final class SubBlock {
r = 67 + DCTextra(bc2, Globals.Pcat6);
}
if (v != Globals.DCT_0 && v != Globals.dct_eob) {
if (bc2.readBit() > 0)
if (bc2.readBit() > 0) {
r = -r;
}
}
return r;
}
@ -172,30 +176,32 @@ final class SubBlock {
int[] adjustedValues = new int[16];
for (int i = 0; i < 16; i++) {
int QValue;
if (plane == PLANE.U || plane == PLANE.V) {
if (plane == Plane.U || plane == Plane.V) {
QValue = frame.getSegmentQuants().getSegQuants()[this.getMacroBlock().getSegmentId()]
.getUvac_delta_q();
if (i == 0)
if (i == 0) {
QValue = frame.getSegmentQuants().getSegQuants()[this.getMacroBlock().getSegmentId()]
.getUvdc_delta_q();
} else {
}
}
else {
QValue = frame.getSegmentQuants().getSegQuants()[this.getMacroBlock().getSegmentId()].getY1ac();
if (i == 0)
if (i == 0) {
QValue = frame.getSegmentQuants().getSegQuants()[this.getMacroBlock().getSegmentId()]
.getY1dc();
}
}
int inputValue = sb.getTokens()[i];
adjustedValues[i] = inputValue * QValue;
}
if (Dc != null)
if (Dc != null) {
adjustedValues[0] = Dc;
}
int[][] diff = IDCT.idct4x4llm(adjustedValues);
sb.setDiff(diff);
}
public void drawDebug() {
@ -209,7 +215,6 @@ final class SubBlock {
dest[0][2] = 128;
dest[0][3] = 128;
}
}
public void drawDebugH() {
@ -219,7 +224,6 @@ final class SubBlock {
dest[2][0] = 0;
dest[3][0] = 0;
}
}
public void drawDebugV() {
@ -237,20 +241,23 @@ final class SubBlock {
}
public String getDebugString() {
String r = new String();
String r = "";
r = r + " " + plane;
if (getMacroBlock().getYMode() == Globals.B_PRED
&& plane == SubBlock.PLANE.Y1)
&& plane == Plane.Y1) {
r = r + "\n " + Globals.getSubBlockModeAsString(mode);
}
return r;
}
public int[][] getDest() {
if (dest != null)
if (dest != null) {
return dest;
else
}
else {
return new int[4][4];
}
}
public int[][] getDiff() {
@ -267,17 +274,21 @@ final class SubBlock {
}
public int[][] getMacroBlockPredict(int intra_mode) {
if (dest != null)
if (dest != null) {
return dest;
}
else {
int rv = 127;
if (intra_mode == Globals.H_PRED)
if (intra_mode == Globals.H_PRED) {
rv = 129;
int r[][] = new int[4][4];
for (int j = 0; j < 4; j++)
for (int i = 0; i < 4; i++)
}
int[][] r = new int[4][4];
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
r[i][j] = rv;
}
}
return r;
}
}
@ -286,21 +297,24 @@ final class SubBlock {
return mode;
}
public PLANE getPlane() {
public Plane getPlane() {
return plane;
}
public int[][] getPredict() {
if (predict != null)
if (predict != null) {
return predict;
}
return getPredict(Globals.B_DC_PRED, false);
}
public int[][] getPredict(int intra_bmode, boolean left) {
if (dest != null)
if (dest != null) {
return dest;
if (predict != null)
}
if (predict != null) {
return predict;
}
else {
int rv = 127;
@ -310,13 +324,15 @@ final class SubBlock {
|| intra_bmode == Globals.B_HE_PRED
|| intra_bmode == Globals.B_VR_PRED
|| intra_bmode == Globals.B_RD_PRED || intra_bmode == Globals.B_HD_PRED)
&& left)
&& left) {
rv = 129;
int r[][] = new int[4][4];
for (int j = 0; j < 4; j++)
for (int i = 0; i < 4; i++)
}
int[][] r = new int[4][4];
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
r[i][j] = rv;
}
}
return r;
}
}
@ -330,9 +346,7 @@ final class SubBlock {
}
public boolean isDest() {
if (dest == null)
return false;
return true;
return dest != null;
}
public void predict(VP8Frame frame) {
@ -359,19 +373,22 @@ final class SubBlock {
if (!leftSb.isDest() && !aboveSb.isDest()) {
al = AL.getPredict(sb.getMode(), false)[3][3];
} else if (!aboveSb.isDest()) {
}
else if (!aboveSb.isDest()) {
al = AL.getPredict(sb.getMode(), false)[3][3];
} else
}
else {
al = AL.getPredict(sb.getMode(), true)[3][3];
}
SubBlock AR = frame.getAboveRightSubBlock(sb, sb.plane);
int ar[] = new int[4];
int[] ar = new int[4];
ar[0] = AR.getPredict(sb.getMode(), false)[0][3];
ar[1] = AR.getPredict(sb.getMode(), false)[1][3];
ar[2] = AR.getPredict(sb.getMode(), false)[2][3];
ar[3] = AR.getPredict(sb.getMode(), false)[3][3];
int[][] p = new int[4][4];
int pp[];
int[] pp;
switch (sb.getMode()) {
case Globals.B_DC_PRED:
// System.out.println("B_DC_PRED");
@ -383,9 +400,11 @@ final class SubBlock {
}
expected_dc = (expected_dc + 4) >> 3;
for (int y = 0; y < 4; y++)
for (int x = 0; x < 4; x++)
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
p[x][y] = expected_dc;
}
}
break;
case Globals.B_TM_PRED:
@ -398,11 +417,13 @@ final class SubBlock {
for (int c = 0; c < 4; c++) {
int pred = above[c] - al + left[r];
if (pred < 0)
if (pred < 0) {
pred = 0;
}
if (pred > 255)
if (pred > 255) {
pred = 255;
}
p[c][r] = pred;
}
@ -411,7 +432,7 @@ final class SubBlock {
case Globals.B_VE_PRED:
// System.out.println("B_VE_PRED");
int ap[] = new int[4];
int[] ap = new int[4];
ap[0] = (al + 2 * above[0] + above[1] + 2) >> 2;
ap[1] = (above[0] + 2 * above[1] + above[2] + 2) >> 2;
ap[2] = (above[1] + 2 * above[2] + above[3] + 2) >> 2;
@ -421,14 +442,13 @@ final class SubBlock {
for (int c = 0; c < 4; c++) {
p[c][r] = ap[c];
}
}
break;
case Globals.B_HE_PRED:
// System.out.println("B_HE_PRED");
int lp[] = new int[4];
int[] lp = new int[4];
lp[0] = (al + 2 * left[0] + left[1] + 2) >> 2;
lp[1] = (left[0] + 2 * left[1] + left[2] + 2) >> 2;
lp[2] = (left[1] + 2 * left[2] + left[3] + 2) >> 2;
@ -564,25 +584,25 @@ final class SubBlock {
SubBlock sb = this;
int r, c;
int p[][] = sb.getPredict(1, false);
int[][] p = sb.getPredict(1, false);
int dest[][] = new int[4][4];
int diff[][] = sb.getDiff();
int[][] dest = new int[4][4];
int[][] diff = sb.getDiff();
for (r = 0; r < 4; r++) {
for (c = 0; c < 4; c++) {
int a = diff[r][c] + p[r][c];
if (a < 0)
if (a < 0) {
a = 0;
if (a > 255)
a = 255;
dest[r][c] = a;
}
if (a > 255) {
a = 255;
}
dest[r][c] = a;
}
}
sb.setDest(dest);
@ -614,16 +634,15 @@ final class SubBlock {
public void setPredict(int[][] predict) {
this.predict = predict;
}
public String toString() {
String r = "[";
for (int x = 0; x < 16; x++)
for (int x = 0; x < 16; x++) {
r = r + tokens[x] + " ";
}
r = r + "]";
return r;
}
}

View File

@ -31,6 +31,8 @@
package com.twelvemonkeys.imageio.plugins.webp.vp8;
import javax.imageio.IIOException;
import javax.imageio.ImageReadParam;
import javax.imageio.event.IIOReadProgressListener;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.BufferedImage;
@ -41,30 +43,26 @@ import java.util.List;
import static com.twelvemonkeys.imageio.color.YCbCrConverter.convertYCbCr2RGB;
public final class VP8Frame {
private static int BLOCK_TYPES = 4;
private static int COEF_BANDS = 8;
private static int MAX_ENTROPY_TOKENS = 12;
private static int MAX_MODE_LF_DELTAS = 4;
private static int MAX_REF_LF_DELTAS = 4;
private static int PREV_COEF_CONTEXTS = 3;
private static final int BLOCK_TYPES = 4;
private static final int COEF_BANDS = 8;
private static final int MAX_ENTROPY_TOKENS = 12;
private static final int MAX_MODE_LF_DELTAS = 4;
private static final int MAX_REF_LF_DELTAS = 4;
private static final int PREV_COEF_CONTEXTS = 3;
private IIOReadProgressListener listener = null;
// private int bufferCount;
// private int buffersToCreate = 1;
private int[][][][] coefProbs;
private boolean debug = false;
private final int[][][][] coefProbs;
private int filterLevel;
private int filterType;
private ImageInputStream frame;
private final ImageInputStream frame;
private final boolean debug;
private int frameType;
private int height;
// private Logger logger;
private int macroBlockCols;
private int macroBlockNoCoeffSkip;
private int macroBlockRows;
@ -72,58 +70,50 @@ public final class VP8Frame {
private MacroBlock[][] macroBlocks;
private int macroBlockSegementAbsoluteDelta;
private int[] macroBlockSegmentTreeProbs;
private int[] modeLoopFilterDeltas = new int[MAX_MODE_LF_DELTAS];
private final int[] modeLoopFilterDeltas = new int[MAX_MODE_LF_DELTAS];
private int modeRefLoopFilterDeltaEnabled;
private int modeRefLoopFilterDeltaUpdate;
private int multiTokenPartition = 0;
private long offset;
private int[] refLoopFilterDeltas = new int[MAX_REF_LF_DELTAS];
private final int[] refLoopFilterDeltas = new int[MAX_REF_LF_DELTAS];
private int refreshEntropyProbs;
private int refreshLastFrame;
private int segmentationIsEnabled;
private SegmentQuants segmentQuants;
private int sharpnessLevel;
private int simpleFilter;
private boolean simpleFilter;
private BoolDecoder tokenBoolDecoder;
private List<BoolDecoder> tokenBoolDecoders;
private final List<BoolDecoder> tokenBoolDecoders;
private int updateMacroBlockSegmentationMap;
private int updateMacroBlockSegmentatonData;
private int width;
public VP8Frame(ImageInputStream stream) throws IOException {
public VP8Frame(final ImageInputStream stream, boolean debug) throws IOException {
this.frame = stream;
offset = frame.getStreamPosition();
this.coefProbs = Globals.getDefaultCoefProbs();
tokenBoolDecoders = new ArrayList<>();
// logger = new Logger();
}
this.debug = debug;
// public VP8Frame(ImageInputStream stream, int[][][][] coefProbs) throws IOException {
// this.frame = stream;
// offset = frame.getStreamPosition();
// this.coefProbs = coefProbs;
// tokenBoolDecoders = new ArrayList<>();
// logger = new Logger();
// }
offset = frame.getStreamPosition();
coefProbs = Globals.getDefaultCoefProbs();
tokenBoolDecoders = new ArrayList<>();
}
public void setProgressListener(IIOReadProgressListener listener) {
this.listener = listener;
}
private void createMacroBlocks() {
macroBlocks = new MacroBlock[macroBlockCols + 2][macroBlockRows + 2];
for (int x = 0; x < macroBlockCols + 2; x++) {
macroBlocks = new MacroBlock[macroBlockRows + 2][macroBlockCols + 2];
for (int y = 0; y < macroBlockRows + 2; y++) {
macroBlocks[x][y] = new MacroBlock(x, y, debug);
for (int x = 0; x < macroBlockCols + 2; x++) {
macroBlocks[y][x] = new MacroBlock(x, y, debug);
}
}
}
public boolean decodeFrame(boolean debug) throws IOException {
this.debug = debug;
public boolean decode(final WritableRaster raster, final ImageReadParam param) throws IOException {
segmentQuants = new SegmentQuants();
int c = frame.readUnsignedByte();
frameType = getBitAsInt(c, 0);
// logger.log("Frame type: " + frameType);
@ -241,19 +231,14 @@ public final class VP8Frame {
if (updateMacroBlockSegmentationMap > 0) {
macroBlockSegmentTreeProbs = new int[Globals.MB_FEATURE_TREE_PROBS];
for (int i = 0; i < Globals.MB_FEATURE_TREE_PROBS; i++) {
int value = 255;
if (bc.readBit() > 0) {
value = bc.readLiteral(8);
} else {
value = 255;
}
int value = bc.readBit() > 0 ? bc.readLiteral(8) : 255;
macroBlockSegmentTreeProbs[i] = value;
}
}
}
}
simpleFilter = bc.readBit();
simpleFilter = bc.readBit() != 0;
// logger.log("simpleFilter: " + simpleFilter);
filterLevel = bc.readLiteral(6);
@ -271,19 +256,16 @@ public final class VP8Frame {
for (int i = 0; i < MAX_REF_LF_DELTAS; i++) {
if (bc.readBit() > 0) {
refLoopFilterDeltas[i] = bc.readLiteral(6);
if (bc.readBit() > 0) // Apply sign
{
if (bc.readBit() > 0) { // Apply sign
refLoopFilterDeltas[i] = refLoopFilterDeltas[i] * -1;
}
// logger.log("ref_lf_deltas[i]: " + refLoopFilterDeltas[i]);
}
}
for (int i = 0; i < MAX_MODE_LF_DELTAS; i++) {
if (bc.readBit() > 0) {
modeLoopFilterDeltas[i] = bc.readLiteral(6);
if (bc.readBit() > 0) // Apply sign
{
if (bc.readBit() > 0) { // Apply sign
modeLoopFilterDeltas[i] = modeLoopFilterDeltas[i] * -1;
}
// logger.log("mode_lf_deltas[i]: " + modeLoopFilterDeltas[i]);
@ -292,9 +274,6 @@ public final class VP8Frame {
}
}
filterType = (filterLevel == 0) ? 0 : (simpleFilter > 0) ? 1 : 2;
// logger.log("filter_type: " + filterType);
setupTokenDecoder(bc, firstPartitionLengthInBytes, offset);
bc.seek();
@ -317,7 +296,8 @@ public final class VP8Frame {
refreshLastFrame = 0;
if (frameType == 0) {
refreshLastFrame = 1;
} else {
}
else {
refreshLastFrame = bc.readBit();
}
@ -327,7 +307,6 @@ public final class VP8Frame {
for (int j = 0; j < COEF_BANDS; j++) {
for (int k = 0; k < PREV_COEF_CONTEXTS; k++) {
for (int l = 0; l < MAX_ENTROPY_TOKENS - 1; l++) {
if (bc.readBool(Globals.vp8CoefUpdateProbs[i][j][k][l]) > 0) {
int newp = bc.readLiteral(8);
this.coefProbs[i][j][k][l] = newp;
@ -343,100 +322,109 @@ public final class VP8Frame {
if (frameType == 0) {
readModes(bc);
} else {
// TODO
throw new IllegalArgumentException("Bad input: Not an Intra frame");
}
else {
throw new IIOException("Bad input: Not an Intra frame");
}
int ibc = 0;
int num_part = 1 << multiTokenPartition;
for (int mb_row = 0; mb_row < macroBlockRows; mb_row++) {
// final boolean filter = getFilterType() > 0 && getFilterLevel() != 0;
for (int row = 0; row < macroBlockRows; row++) {
if (num_part > 1) {
tokenBoolDecoder = tokenBoolDecoders.get(ibc);
tokenBoolDecoder.seek();
decodeMacroBlockRow(mb_row);
decodeMacroBlockRow(row, raster, param);
ibc++;
if (ibc == num_part) {
ibc = 0;
}
} else {
decodeMacroBlockRow(mb_row);
}
else {
decodeMacroBlockRow(row, raster, param);
}
fireProgressUpdate(mb_row);
}
if (getFilterType() > 0 && getFilterLevel() != 0) {
LoopFilter.loopFilter(this);
fireProgressUpdate(row);
}
return true;
}
private void decodeMacroBlockRow(int mbRow) throws IOException {
private void decodeMacroBlockRow(final int mbRow, final WritableRaster raster, final ImageReadParam param) throws IOException {
final boolean filter = filterLevel != 0;
MacroBlock left = null;
MacroBlock[] prevRow = macroBlocks[mbRow];
MacroBlock[] currRow = macroBlocks[mbRow + 1];
for (int mbCol = 0; mbCol < macroBlockCols; mbCol++) {
MacroBlock mb = getMacroBlock(mbCol, mbRow);
MacroBlock mb = currRow[mbCol + 1];
mb.decodeMacroBlock(this);
mb.dequantMacroBlock(this);
if (filter) {
MacroBlock top = mbRow > 0 ? prevRow[mbCol + 1] : null;
LoopFilter.loopFilterBlock(mb, left, top, frameType, simpleFilter, sharpnessLevel);
}
copyBlock(mb, raster, param);
left = mb;
}
}
private void fireProgressUpdate(int mb_row) {
private void fireProgressUpdate(int mbRow) {
if (listener != null) {
float percentageDone = (100.0f * ((float) (mb_row + 1) / (float) getMacroBlockRows()));
float percentageDone = (100.0f * ((float) (mbRow + 1) / (float) getMacroBlockRows()));
listener.imageProgress(null, percentageDone);
}
}
public SubBlock getAboveRightSubBlock(SubBlock sb, SubBlock.PLANE plane) {
public SubBlock getAboveRightSubBlock(SubBlock sb, SubBlock.Plane plane) {
// this might break at right edge
SubBlock r;
MacroBlock mb = sb.getMacroBlock();
int x = mb.getSubblockX(sb);
int y = mb.getSubblockY(sb);
if (plane == SubBlock.PLANE.Y1) {
if (plane == SubBlock.Plane.Y1) {
// top row
if (y == 0 && x < 3) {
MacroBlock mb2 = this.getMacroBlock(mb.getX(), mb.getY() - 1);
r = mb2.getSubBlock(plane, x + 1, 3);
return r;
}
//top right
else if (y == 0 && x == 3) {
MacroBlock mb2 = this.getMacroBlock(mb.getX() + 1, mb.getY() - 1);
r = mb2.getSubBlock(plane, 0, 3);
if (mb2.getX() == this.getMacroBlockCols()) {
int dest[][] = new int[4][4];
int[][] dest = new int[4][4];
for (int b = 0; b < 4; b++) {
for (int a = 0; a < 4; a++) {
if (mb2.getY() < 0) {
dest[a][b] = 127;
} else {
dest[a][b] = this.getMacroBlock(mb.getX(), mb.getY() - 1).getSubBlock(SubBlock.PLANE.Y1, 3, 3).getDest()[3][3];
}
else {
dest[a][b] = this.getMacroBlock(mb.getX(), mb.getY() - 1).getSubBlock(SubBlock.Plane.Y1, 3, 3).getDest()[3][3];
}
}
}
r = new SubBlock(mb2, null, null, SubBlock.PLANE.Y1);
r = new SubBlock(mb2, null, null, SubBlock.Plane.Y1);
r.setDest(dest);
}
return r;
}
//not right edge or top row
else if (y > 0 && x < 3) {
r = mb.getSubBlock(plane, x + 1, y - 1);
return r;
}
@ -445,34 +433,36 @@ public final class VP8Frame {
SubBlock sb2 = mb.getSubBlock(sb.getPlane(), 3, 0);
return this.getAboveRightSubBlock(sb2, plane);
}
} else {
}
else {
// TODO
throw new IllegalArgumentException("bad input: getAboveRightSubBlock()");
}
}
public SubBlock getAboveSubBlock(SubBlock sb, SubBlock.PLANE plane) {
SubBlock r = sb.getAbove();
if (r == null) {
public SubBlock getAboveSubBlock(SubBlock sb, SubBlock.Plane plane) {
SubBlock above = sb.getAbove();
if (above == null) {
MacroBlock mb = sb.getMacroBlock();
int x = mb.getSubblockX(sb);
MacroBlock mb2 = getMacroBlock(mb.getX(), mb.getY() - 1);
//TODO: SPLIT
while (plane == SubBlock.PLANE.Y2 && mb2.getYMode() == Globals.B_PRED) {
while (plane == SubBlock.Plane.Y2 && mb2.getYMode() == Globals.B_PRED) {
mb2 = getMacroBlock(mb2.getX(), mb2.getY() - 1);
}
r = mb2.getBottomSubBlock(x, sb.getPlane());
above = mb2.getBottomSubBlock(x, sb.getPlane());
}
return r;
return above;
}
private boolean getBit(int data, int bit) {
int r = data & (1 << bit);
return r != 0;
}
// private boolean getBit(int data, int bit) {
// int r = data & (1 << bit);
// return r != 0;
// }
private int getBitAsInt(int data, int bit) {
int r = data & (1 << bit);
@ -492,11 +482,11 @@ public final class VP8Frame {
WritableRaster imRas = bi.getWritableTile(0, 0);
for (int x = 0; x < getWidth(); x++) {
for (int y = 0; y < getHeight(); y++) {
int c[] = new int[3];
int[] c = new int[3];
int yy, u, v;
yy = 127 + this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.Y1, (x % 16) / 4, (y % 16) / 4).getDiff()[x % 4][y % 4];
u = 127 + this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.U, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getDiff()[(x / 2) % 4][(y / 2) % 4];
v = 127 + this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.V, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getDiff()[(x / 2) % 4][(y / 2) % 4];
yy = 127 + this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.Y1, (x % 16) / 4, (y % 16) / 4).getDiff()[x % 4][y % 4];
u = 127 + this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.U, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getDiff()[(x / 2) % 4][(y / 2) % 4];
v = 127 + this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.V, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getDiff()[(x / 2) % 4][(y / 2) % 4];
c[0] = (int) (1.164 * (yy - 16) + 1.596 * (v - 128));
c[1] = (int) (1.164 * (yy - 16) - 0.813 * (v - 128) - 0.391 * (u - 128));
c[2] = (int) (1.164 * (yy - 16) + 2.018 * (u - 128));
@ -521,11 +511,11 @@ public final class VP8Frame {
WritableRaster imRas = bi.getWritableTile(0, 0);
for (int x = 0; x < getWidth(); x++) {
for (int y = 0; y < getHeight(); y++) {
int c[] = new int[3];
int[] c = new int[3];
int yy, u, v;
yy = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.Y1, (x % 16) / 4, (y % 16) / 4).getPredict()[x % 4][y % 4];
u = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.U, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getPredict()[(x / 2) % 4][(y / 2) % 4];
v = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.V, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getPredict()[(x / 2) % 4][(y / 2) % 4];
yy = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.Y1, (x % 16) / 4, (y % 16) / 4).getPredict()[x % 4][y % 4];
u = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.U, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getPredict()[(x / 2) % 4][(y / 2) % 4];
v = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.V, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getPredict()[(x / 2) % 4][(y / 2) % 4];
c[0] = (int) (1.164 * (yy - 16) + 1.596 * (v - 128));
c[1] = (int) (1.164 * (yy - 16) - 0.813 * (v - 128) - 0.391 * (u - 128));
c[2] = (int) (1.164 * (yy - 16) + 2.018 * (u - 128));
@ -550,9 +540,9 @@ public final class VP8Frame {
WritableRaster imRas = bi.getWritableTile(0, 0);
for (int x = 0; x < getWidth(); x++) {
for (int y = 0; y < getHeight(); y++) {
int c[] = new int[3];
int[] c = new int[3];
int u;
u = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.U, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getDest()[(x / 2) % 4][(y / 2) % 4];
u = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.U, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getDest()[(x / 2) % 4][(y / 2) % 4];
c[0] = u;
c[1] = u;
c[2] = u;
@ -577,9 +567,9 @@ public final class VP8Frame {
WritableRaster imRas = bi.getWritableTile(0, 0);
for (int x = 0; x < getWidth(); x++) {
for (int y = 0; y < getHeight(); y++) {
int c[] = new int[3];
int[] c = new int[3];
int u;
u = 127 + this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.U, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getDiff()[(x / 2) % 4][(y / 2) % 4];
u = 127 + this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.U, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getDiff()[(x / 2) % 4][(y / 2) % 4];
c[0] = u;
c[1] = u;
c[2] = u;
@ -604,9 +594,9 @@ public final class VP8Frame {
WritableRaster imRas = bi.getWritableTile(0, 0);
for (int x = 0; x < getWidth(); x++) {
for (int y = 0; y < getHeight(); y++) {
int c[] = new int[3];
int[] c = new int[3];
int u;
u = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.U, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getPredict()[(x / 2) % 4][(y / 2) % 4];
u = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.U, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getPredict()[(x / 2) % 4][(y / 2) % 4];
c[0] = u;
c[1] = u;
c[2] = u;
@ -631,9 +621,9 @@ public final class VP8Frame {
WritableRaster imRas = bi.getWritableTile(0, 0);
for (int x = 0; x < getWidth(); x++) {
for (int y = 0; y < getHeight(); y++) {
int c[] = new int[3];
int[] c = new int[3];
int v;
v = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.V, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getDest()[(x / 2) % 4][(y / 2) % 4];
v = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.V, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getDest()[(x / 2) % 4][(y / 2) % 4];
c[0] = v;
c[1] = v;
c[2] = v;
@ -658,9 +648,9 @@ public final class VP8Frame {
WritableRaster imRas = bi.getWritableTile(0, 0);
for (int x = 0; x < getWidth(); x++) {
for (int y = 0; y < getHeight(); y++) {
int c[] = new int[3];
int[] c = new int[3];
int v;
v = 127 + this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.V, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getDiff()[(x / 2) % 4][(y / 2) % 4];
v = 127 + this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.V, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getDiff()[(x / 2) % 4][(y / 2) % 4];
c[0] = v;
c[1] = v;
c[2] = v;
@ -685,9 +675,9 @@ public final class VP8Frame {
WritableRaster imRas = bi.getWritableTile(0, 0);
for (int x = 0; x < getWidth(); x++) {
for (int y = 0; y < getHeight(); y++) {
int c[] = new int[3];
int[] c = new int[3];
int v;
v = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.V, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getPredict()[(x / 2) % 4][(y / 2) % 4];
v = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.V, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getPredict()[(x / 2) % 4][(y / 2) % 4];
c[0] = v;
c[1] = v;
c[2] = v;
@ -712,9 +702,9 @@ public final class VP8Frame {
WritableRaster imRas = bi.getWritableTile(0, 0);
for (int x = 0; x < getWidth(); x++) {
for (int y = 0; y < getHeight(); y++) {
int c[] = new int[3];
int[] c = new int[3];
int yy;
yy = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.Y1, (x % 16) / 4, (y % 16) / 4).getDest()[x % 4][y % 4];
yy = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.Y1, (x % 16) / 4, (y % 16) / 4).getDest()[x % 4][y % 4];
c[0] = yy;
c[1] = yy;
c[2] = yy;
@ -739,9 +729,9 @@ public final class VP8Frame {
WritableRaster imRas = bi.getWritableTile(0, 0);
for (int x = 0; x < getWidth(); x++) {
for (int y = 0; y < getHeight(); y++) {
int c[] = new int[3];
int[] c = new int[3];
int yy;
yy = 127 + this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.Y1, (x % 16) / 4, (y % 16) / 4).getDiff()[x % 4][y % 4];
yy = 127 + this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.Y1, (x % 16) / 4, (y % 16) / 4).getDiff()[x % 4][y % 4];
c[0] = yy;
c[1] = yy;
c[2] = yy;
@ -766,9 +756,9 @@ public final class VP8Frame {
WritableRaster imRas = bi.getWritableTile(0, 0);
for (int x = 0; x < getWidth(); x++) {
for (int y = 0; y < getHeight(); y++) {
int c[] = new int[3];
int[] c = new int[3];
int yy;
yy = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.PLANE.Y1, (x % 16) / 4, (y % 16) / 4).getPredict()[x % 4][y % 4];
yy = this.getMacroBlock(x / 16, y / 16).getSubBlock(SubBlock.Plane.Y1, (x % 16) / 4, (y % 16) / 4).getPredict()[x % 4][y % 4];
c[0] = yy;
c[1] = yy;
c[2] = yy;
@ -788,23 +778,19 @@ public final class VP8Frame {
return bi;
}
public int getFilterLevel() {
return filterLevel;
}
public int getFilterType() {
return filterType;
}
public int getFrameType() {
return frameType;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public SubBlock getLeftSubBlock(SubBlock sb, SubBlock.PLANE plane) {
public SubBlock getLeftSubBlock(SubBlock sb, SubBlock.Plane plane) {
SubBlock r = sb.getLeft();
if (r == null) {
MacroBlock mb = sb.getMacroBlock();
@ -812,19 +798,19 @@ public final class VP8Frame {
MacroBlock mb2 = getMacroBlock(mb.getX() - 1, mb.getY());
//TODO: SPLIT
while (plane == SubBlock.PLANE.Y2 && mb2.getYMode() == Globals.B_PRED) {
while (plane == SubBlock.Plane.Y2 && mb2.getYMode() == Globals.B_PRED) {
mb2 = getMacroBlock(mb2.getX() - 1, mb2.getY());
}
r = mb2.getRightSubBlock(y, sb.getPlane());
}
return r;
}
public MacroBlock getMacroBlock(int mbCol, int mbRow) {
return macroBlocks[mbCol + 1][mbRow + 1];
// return macroBlocks[mbCol + 1][mbRow + 1];
return macroBlocks[mbRow + 1][mbCol + 1];
}
public int getMacroBlockCols() {
@ -832,18 +818,18 @@ public final class VP8Frame {
}
public String getMacroBlockDebugString(int mbx, int mby, int sbx, int sby) {
String r = new String();
String r = "";
if (mbx < this.macroBlockCols && mby < this.getMacroBlockRows()) {
MacroBlock mb = getMacroBlock(mbx, mby);
r = r + mb.getDebugString();
if (sbx < 4 && sby < 4) {
SubBlock sb = mb.getSubBlock(SubBlock.PLANE.Y1, sbx, sby);
SubBlock sb = mb.getSubBlock(SubBlock.Plane.Y1, sbx, sby);
r = r + "\n SubBlock " + sbx + ", " + sby + "\n " + sb.getDebugString();
sb = mb.getSubBlock(SubBlock.PLANE.Y2, sbx, sby);
sb = mb.getSubBlock(SubBlock.Plane.Y2, sbx, sby);
r = r + "\n SubBlock " + sbx + ", " + sby + "\n " + sb.getDebugString();
sb = mb.getSubBlock(SubBlock.PLANE.U, sbx / 2, sby / 2);
sb = mb.getSubBlock(SubBlock.Plane.U, sbx / 2, sby / 2);
r = r + "\n SubBlock " + sbx / 2 + ", " + sby / 2 + "\n " + sb.getDebugString();
sb = mb.getSubBlock(SubBlock.PLANE.V, sbx / 2, sby / 2);
sb = mb.getSubBlock(SubBlock.Plane.V, sbx / 2, sby / 2);
r = r + "\n SubBlock " + sbx / 2 + ", " + sby / 2 + "\n " + sb.getDebugString();
}
}
@ -871,72 +857,65 @@ public final class VP8Frame {
return tokenBoolDecoder;
}
public int[][] getUBuffer() {
int r[][] = new int[macroBlockCols * 8][macroBlockRows * 8];
for (int y = 0; y < macroBlockRows; y++) {
for (int x = 0; x < macroBlockCols; x++) {
MacroBlock mb = macroBlocks[x + 1][y + 1];
for (int b = 0; b < 2; b++) {
for (int a = 0; a < 2; a++) {
SubBlock sb = mb.getUSubBlock(a, b);
for (int d = 0; d < 4; d++) {
for (int c = 0; c < 4; c++) {
r[(x * 8) + (a * 4) + c][(y * 8) + (b * 4) + d] = sb.getDest()[c][d];
// public int[][] getUBuffer() {
// int[][] r = new int[macroBlockCols * 8][macroBlockRows * 8];
// for (int y = 0; y < macroBlockRows; y++) {
// for (int x = 0; x < macroBlockCols; x++) {
// MacroBlock mb = macroBlocks[x + 1][y + 1];
// for (int b = 0; b < 2; b++) {
// for (int a = 0; a < 2; a++) {
// SubBlock sb = mb.getUSubBlock(a, b);
// for (int d = 0; d < 4; d++) {
// for (int c = 0; c < 4; c++) {
// r[(x * 8) + (a * 4) + c][(y * 8) + (b * 4) + d] = sb.getDest()[c][d];
// }
// }
// }
// }
// }
// }
// return r;
// }
//
// public int[][] getVBuffer() {
// int[][] r = new int[macroBlockCols * 8][macroBlockRows * 8];
// for (int y = 0; y < macroBlockRows; y++) {
// for (int x = 0; x < macroBlockCols; x++) {
// MacroBlock mb = macroBlocks[x + 1][y + 1];
// for (int b = 0; b < 2; b++) {
// for (int a = 0; a < 2; a++) {
// SubBlock sb = mb.getVSubBlock(a, b);
// for (int d = 0; d < 4; d++) {
// for (int c = 0; c < 4; c++) {
// r[(x * 8) + (a * 4) + c][(y * 8) + (b * 4) + d] = sb.getDest()[c][d];
// }
// }
// }
// }
// }
// }
// return r;
// }
}
}
}
}
}
}
return r;
}
public int[][] getVBuffer() {
int r[][] = new int[macroBlockCols * 8][macroBlockRows * 8];
for (int y = 0; y < macroBlockRows; y++) {
for (int x = 0; x < macroBlockCols; x++) {
MacroBlock mb = macroBlocks[x + 1][y + 1];
for (int b = 0; b < 2; b++) {
for (int a = 0; a < 2; a++) {
SubBlock sb = mb.getVSubBlock(a, b);
for (int d = 0; d < 4; d++) {
for (int c = 0; c < 4; c++) {
r[(x * 8) + (a * 4) + c][(y * 8) + (b * 4) + d] = sb.getDest()[c][d];
}
}
}
}
}
}
return r;
}
public int getWidth() {
return width;
}
public int[][] getYBuffer() {
int r[][] = new int[macroBlockCols * 16][macroBlockRows * 16];
for (int y = 0; y < macroBlockRows; y++) {
for (int x = 0; x < macroBlockCols; x++) {
MacroBlock mb = macroBlocks[x + 1][y + 1];
for (int b = 0; b < 4; b++) {
for (int a = 0; a < 4; a++) {
SubBlock sb = mb.getYSubBlock(a, b);
for (int d = 0; d < 4; d++) {
for (int c = 0; c < 4; c++) {
r[(x * 16) + (a * 4) + c][(y * 16) + (b * 4) + d] = sb.getDest()[c][d];
}
}
}
}
}
}
return r;
}
// public int[][] getYBuffer() {
// int[][] r = new int[macroBlockCols * 16][macroBlockRows * 16];
// for (int y = 0; y < macroBlockRows; y++) {
// for (int x = 0; x < macroBlockCols; x++) {
// MacroBlock mb = macroBlocks[x + 1][y + 1];
// for (int b = 0; b < 4; b++) {
// for (int a = 0; a < 4; a++) {
// SubBlock sb = mb.getYSubBlock(a, b);
// for (int d = 0; d < 4; d++) {
// for (int c = 0; c < 4; c++) {
// r[(x * 16) + (a * 4) + c][(y * 16) + (b * 4) + d] = sb.getDest()[c][d];
// }
// }
// }
// }
// }
// }
// return r;
// }
private void readModes(BoolDecoder bc) throws IOException {
int mb_row = -1;
@ -949,7 +928,6 @@ public final class VP8Frame {
while (++mb_row < macroBlockRows) {
int mb_col = -1;
while (++mb_col < macroBlockCols) {
//if (this.segmentation_enabled > 0) {
// logger.log(Level.SEVERE, "TODO:");
// throw new IllegalArgumentException("bad input: segmentation_enabled()");
@ -966,9 +944,10 @@ public final class VP8Frame {
if (modeRefLoopFilterDeltaEnabled > 0) {
int level = filterLevel;
level = level + refLoopFilterDeltas[0];
level = (level < 0) ? 0 : (level > 63) ? 63 : level;
level = (level < 0) ? 0 : Math.min(level, 63);
mb.setFilterLevel(level);
} else {
}
else {
mb.setFilterLevel(segmentQuants.getSegQuants()[mb.getSegmentId()].getFilterStrength());
}
@ -983,35 +962,27 @@ public final class VP8Frame {
if (y_mode == Globals.B_PRED) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
SubBlock sb = mb.getYSubBlock(j, i);
SubBlock A = getAboveSubBlock(sb, SubBlock.PLANE.Y1);
SubBlock L = getLeftSubBlock(sb, SubBlock.PLANE.Y1);
SubBlock A = getAboveSubBlock(sb, SubBlock.Plane.Y1);
SubBlock L = getLeftSubBlock(sb, SubBlock.Plane.Y1);
int mode = readSubBlockMode(bc, A.getMode(), L.getMode());
sb.setMode(mode);
}
}
if (modeRefLoopFilterDeltaEnabled > 0) {
int level = mb.getFilterLevel();
level = level + this.modeLoopFilterDeltas[0];
level = (level < 0) ? 0 : (level > 63) ? 63 : level;
level = (level < 0) ? 0 : Math.min(level, 63);
mb.setFilterLevel(level);
}
}
else {
int BMode;
switch (y_mode) {
case Globals.DC_PRED:
BMode = Globals.B_DC_PRED;
break;
case Globals.V_PRED:
BMode = Globals.B_VE_PRED;
break;
@ -1021,10 +992,12 @@ public final class VP8Frame {
case Globals.TM_PRED:
BMode = Globals.B_TM_PRED;
break;
case Globals.DC_PRED:
default:
BMode = Globals.B_DC_PRED;
break;
}
for (int x = 0; x < 4; x++) {
for (int y = 0; y < 4; y++) {
SubBlock sb = mb.getYSubBlock(x, y);
@ -1041,7 +1014,6 @@ public final class VP8Frame {
private int readPartitionSize(long l) throws IOException {
frame.seek(l);
return frame.readUnsignedByte() + (frame.readUnsignedByte() << 8) + (frame.readUnsignedByte() << 16);
}
private int readSubBlockMode(BoolDecoder bc, int A, int L) throws IOException {
@ -1056,11 +1028,6 @@ public final class VP8Frame {
return bc.readTree(Globals.vp8KeyFrameYModeTree, Globals.vp8KeyFrameYModeProb, 0);
}
// public void setBuffersToCreate(int count) {
// this.buffersToCreate = 3 + count;
//// this.bufferCount = 0;
// }
private void setupTokenDecoder(BoolDecoder bc, int first_partition_length_in_bytes, long offset) throws IOException {
long partitionSize;
long partitionsStart = offset + first_partition_length_in_bytes;
@ -1073,15 +1040,12 @@ public final class VP8Frame {
partition += 3 * (num_part - 1);
}
for (int i = 0; i < num_part; i++) {
/*
* Calculate the length of this partition. The last partition size
* is implicit.
*/
// Calculate the length of this partition. The last partition size is implicit.
if (i < num_part - 1) {
partitionSize = readPartitionSize(partitionsStart + (i * 3));
bc.seek();
} else {
}
else {
partitionSize = frame.length() - partition;
}
@ -1092,40 +1056,27 @@ public final class VP8Frame {
tokenBoolDecoder = tokenBoolDecoders.get(0);
}
public void copyTo(final WritableRaster byteRGBRaster) {
private final byte[] yuv = new byte[3];
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
// We might be copying into a smaller raster
int w = Math.min(width, byteRGBRaster.getWidth());
int h = Math.min(height, byteRGBRaster.getHeight());
int yStart = macroBlock.getY() * 16;
int yEnd = Math.min(16, byteRGBRaster.getHeight() - yStart);
int xStart = macroBlock.getX() * 16;
int xEnd = Math.min(16, byteRGBRaster.getWidth() - xStart);
byte[] yuv = new byte[3];
byte[] rgb = new byte[4]; // Allow decoding into RGBA, leaving the alpha out.
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
MacroBlock macroBlock = getMacroBlock(x / 16, y / 16);
yuv[0] = (byte) macroBlock.getSubBlock(SubBlock.PLANE.Y1, (x % 16) / 4, (y % 16) / 4).getDest()[x % 4][y % 4];
yuv[1] = (byte) macroBlock.getSubBlock(SubBlock.PLANE.U, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getDest()[(x / 2) % 4][(y / 2) % 4];
yuv[2] = (byte) macroBlock.getSubBlock(SubBlock.PLANE.V, ((x / 2) % 8) / 4, ((y / 2) % 8) / 4).getDest()[(x / 2) % 4][(y / 2) % 4];
for (int y = 0; y < yEnd; y++) {
for (int x = 0; x < xEnd; x++) {
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];
convertYCbCr2RGB(yuv, rgb, 0);
byteRGBRaster.setDataElements(x, y, rgb);
byteRGBRaster.setDataElements(xStart + x, yStart + y, rgb);
}
}
}
// public void setFrame(ImageInputStream frame) {
// try {
// this.frame.flush();
// this.frame.close();
// this.frame = frame;
// offset = frame.getStreamPosition();
// this.coefProbs=Globals.getDefaultCoefProbs();
// tokenBoolDecoders = new ArrayList<>();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
}