mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 04:25:29 -04:00
Implement applying of the inverse transforms
This commit is contained in:
parent
fafa58b718
commit
326b98d5e5
@ -120,6 +120,10 @@ public final class VP8LDecoder {
|
||||
// Use the Huffman trees to decode the LZ77 encoded data.
|
||||
decodeImage(writableChild, huffmanInfo, colorCache);
|
||||
|
||||
for (Transform transform : transforms) {
|
||||
transform.applyInverse(raster);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void decodeImage(WritableRaster raster, HuffmanInfo huffmanInfo, ColorCache colorCache) throws IOException {
|
||||
|
@ -14,5 +14,30 @@ public class ColorIndexingTransform implements Transform {
|
||||
|
||||
@Override
|
||||
public void applyInverse(WritableRaster raster) {
|
||||
|
||||
int width = raster.getWidth();
|
||||
int height = raster.getHeight();
|
||||
|
||||
byte[] rgba = new byte[4];
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
//Reversed so no used elements are overridden (in case of packing)
|
||||
for (int x = width - 1; x >= 0; x--) {
|
||||
|
||||
int componentSize = 8 >> bits;
|
||||
int packed = 1 << bits;
|
||||
int xC = x / packed;
|
||||
int componentOffset = componentSize * (x % packed);
|
||||
|
||||
int sample = raster.getSample(xC, y, 1);
|
||||
|
||||
int index = sample >> componentOffset & ((1 << componentSize) - 1);
|
||||
|
||||
//Arraycopy for 4 elements might not be beneficial
|
||||
System.arraycopy(colorTable, index * 4, rgba, 0, 4);
|
||||
raster.setDataElements(x, y, rgba);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,24 @@ public class ColorTransform implements Transform {
|
||||
|
||||
@Override
|
||||
public void applyInverse(WritableRaster raster) {
|
||||
int width = raster.getWidth();
|
||||
int height = raster.getHeight();
|
||||
|
||||
byte[] rgba = new byte[4];
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
|
||||
data.getDataElements(x >> bits, y >> bits, rgba);
|
||||
ColorTransformElement trans = new ColorTransformElement(rgba);
|
||||
|
||||
raster.getDataElements(x, y, rgba);
|
||||
|
||||
trans.inverseTransform(rgba);
|
||||
|
||||
raster.setDataElements(x, y, rgba);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: For encoding!
|
||||
@ -43,47 +61,33 @@ public class ColorTransform implements Transform {
|
||||
return (byte) ((t * c) >> 5);
|
||||
}
|
||||
|
||||
private static void inverseTransform(final byte red, final byte green, final byte blue,
|
||||
final ColorTransformElement trans,
|
||||
final int[] newRedBlue) {
|
||||
// Applying inverse transform is just subtracting the
|
||||
// color transform deltas
|
||||
// Transformed values of red and blue components
|
||||
int tmp_red = red;
|
||||
int tmp_blue = blue;
|
||||
private static final class ColorTransformElement {
|
||||
|
||||
tmp_red -= colorTransformDelta((byte) trans.green_to_red, green);
|
||||
tmp_blue -= colorTransformDelta((byte) trans.green_to_blue, green);
|
||||
tmp_blue -= colorTransformDelta((byte) trans.red_to_blue, red); // Spec has red & 0xff
|
||||
|
||||
newRedBlue[0] = tmp_red & 0xff;
|
||||
newRedBlue[1] = tmp_blue & 0xff;
|
||||
}
|
||||
|
||||
private static void inverseTransform(final byte[] rgb, final ColorTransformElement trans) {
|
||||
// Applying inverse transform is just subtracting the
|
||||
// color transform deltas
|
||||
// Transformed values of red and blue components
|
||||
int tmp_red = rgb[0];
|
||||
int tmp_blue = rgb[2];
|
||||
|
||||
tmp_red -= colorTransformDelta((byte) trans.green_to_red, rgb[1]);
|
||||
tmp_blue -= colorTransformDelta((byte) trans.green_to_blue, rgb[1]);
|
||||
tmp_blue -= colorTransformDelta((byte) trans.red_to_blue, rgb[0]); // Spec has red & 0xff
|
||||
|
||||
rgb[0] = (byte) (tmp_red & 0xff);
|
||||
rgb[2] = (byte) (tmp_blue & 0xff);
|
||||
}
|
||||
|
||||
static final class ColorTransformElement {
|
||||
final int green_to_red;
|
||||
final int green_to_blue;
|
||||
final int red_to_blue;
|
||||
|
||||
ColorTransformElement(final int green_to_red, final int green_to_blue, final int red_to_blue) {
|
||||
this.green_to_red = green_to_red;
|
||||
this.green_to_blue = green_to_blue;
|
||||
this.red_to_blue = red_to_blue;
|
||||
ColorTransformElement(final byte[] rgba) {
|
||||
this.green_to_red = rgba[2];
|
||||
this.green_to_blue = rgba[1];
|
||||
this.red_to_blue = rgba[0];
|
||||
}
|
||||
|
||||
private void inverseTransform(final byte[] rgb) {
|
||||
// Applying inverse transform is just adding (!, different from specification) the
|
||||
// color transform deltas 3
|
||||
|
||||
// Transformed values of red and blue components
|
||||
int tmp_red = rgb[0];
|
||||
int tmp_blue = rgb[2];
|
||||
|
||||
tmp_red += colorTransformDelta((byte) this.green_to_red, rgb[1]);
|
||||
tmp_blue += colorTransformDelta((byte) this.green_to_blue, rgb[1]);
|
||||
tmp_blue += colorTransformDelta((byte) this.red_to_blue, (byte) tmp_red); // Spec has red & 0xff
|
||||
|
||||
rgb[0] = (byte) (tmp_red & 0xff);
|
||||
rgb[2] = (byte) (tmp_blue & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,45 +15,190 @@ public class PredictorTransform implements Transform {
|
||||
|
||||
@Override
|
||||
public void applyInverse(WritableRaster raster) {
|
||||
|
||||
int width = raster.getWidth();
|
||||
int height = raster.getHeight();
|
||||
|
||||
byte[] rgba = new byte[4];
|
||||
|
||||
//Handle top and left border separately
|
||||
|
||||
//(0,0) Black (0x000000ff) predict
|
||||
raster.getDataElements(0, 0, rgba);
|
||||
rgba[3] += 0xff;
|
||||
raster.setDataElements(0, 0, rgba);
|
||||
|
||||
|
||||
byte[] predictor = new byte[4];
|
||||
byte[] predictor2 = new byte[4];
|
||||
byte[] predictor3 = new byte[4];
|
||||
|
||||
//(x,0) L predict
|
||||
for (int x = 1; x < width; x++) {
|
||||
raster.getDataElements(x, 0, rgba);
|
||||
raster.getDataElements(x - 1, 0, predictor);
|
||||
addPixels(rgba, predictor);
|
||||
|
||||
raster.setDataElements(x, 0, rgba);
|
||||
}
|
||||
|
||||
//(0,y) T predict
|
||||
for (int y = 1; y < height; y++) {
|
||||
raster.getDataElements(0, y, rgba);
|
||||
raster.getDataElements(0, y - 1, predictor);
|
||||
addPixels(rgba, predictor);
|
||||
|
||||
raster.setDataElements(0, y, rgba);
|
||||
}
|
||||
|
||||
for (int y = 1; y < height; y++) {
|
||||
for (int x = 1; x < width; x++) {
|
||||
|
||||
int transformType = data.getSample(x >> bits, y >> bits, 1);
|
||||
|
||||
raster.getDataElements(x, y, rgba);
|
||||
|
||||
int lX = x - 1; //x for left
|
||||
|
||||
int tY = y - 1; //y for top
|
||||
|
||||
//top right is not (x+1, tY) if last pixel in line instead (0, y)
|
||||
int trX = x == width - 1 ? 0 : x + 1;
|
||||
int trY = x == width - 1 ? y : tY;
|
||||
|
||||
switch (transformType) {
|
||||
case PredictorMode.BLACK:
|
||||
rgba[3] += 0xff;
|
||||
break;
|
||||
case PredictorMode.L:
|
||||
raster.getDataElements(lX, y, predictor);
|
||||
addPixels(rgba, predictor);
|
||||
break;
|
||||
case PredictorMode.T:
|
||||
raster.getDataElements(x, tY, predictor);
|
||||
addPixels(rgba, predictor);
|
||||
break;
|
||||
case PredictorMode.TR:
|
||||
raster.getDataElements(trX, trY, predictor);
|
||||
addPixels(rgba, predictor);
|
||||
break;
|
||||
case PredictorMode.TL:
|
||||
raster.getDataElements(lX, tY, predictor);
|
||||
addPixels(rgba, predictor);
|
||||
break;
|
||||
case PredictorMode.AVG_L_TR_T:
|
||||
raster.getDataElements(lX, y, predictor);
|
||||
raster.getDataElements(trX, trY, predictor2);
|
||||
average2(predictor, predictor2);
|
||||
|
||||
raster.getDataElements(x, tY, predictor2);
|
||||
average2(predictor, predictor2);
|
||||
|
||||
addPixels(rgba, predictor);
|
||||
break;
|
||||
case PredictorMode.AVG_L_TL:
|
||||
raster.getDataElements(lX, y, predictor);
|
||||
raster.getDataElements(lX, tY, predictor2);
|
||||
average2(predictor, predictor2);
|
||||
|
||||
addPixels(rgba, predictor);
|
||||
break;
|
||||
case PredictorMode.AVG_L_T:
|
||||
raster.getDataElements(lX, y, predictor);
|
||||
raster.getDataElements(x, tY, predictor2);
|
||||
average2(predictor, predictor2);
|
||||
|
||||
addPixels(rgba, predictor);
|
||||
break;
|
||||
case PredictorMode.AVG_TL_T:
|
||||
raster.getDataElements(lX, tY, predictor);
|
||||
raster.getDataElements(x, tY, predictor2);
|
||||
average2(predictor, predictor2);
|
||||
|
||||
addPixels(rgba, predictor);
|
||||
break;
|
||||
case PredictorMode.AVG_T_TR:
|
||||
raster.getDataElements(x, tY, predictor);
|
||||
raster.getDataElements(trX, trY, predictor2);
|
||||
average2(predictor, predictor2);
|
||||
|
||||
addPixels(rgba, predictor);
|
||||
break;
|
||||
case PredictorMode.AVG_L_TL_T_TR:
|
||||
raster.getDataElements(lX, y, predictor);
|
||||
raster.getDataElements(lX, tY, predictor2);
|
||||
average2(predictor, predictor2);
|
||||
|
||||
raster.getDataElements(x, tY, predictor2);
|
||||
raster.getDataElements(trX, trY, predictor3);
|
||||
average2(predictor2, predictor3);
|
||||
|
||||
average2(predictor, predictor2);
|
||||
|
||||
addPixels(rgba, predictor);
|
||||
break;
|
||||
case PredictorMode.SELECT:
|
||||
raster.getDataElements(lX, y, predictor);
|
||||
raster.getDataElements(x, tY, predictor2);
|
||||
raster.getDataElements(lX, tY, predictor3);
|
||||
|
||||
|
||||
addPixels(rgba, select(predictor, predictor2, predictor3));
|
||||
break;
|
||||
case PredictorMode.CLAMP_ADD_SUB_FULL:
|
||||
raster.getDataElements(lX, y, predictor);
|
||||
raster.getDataElements(x, tY, predictor2);
|
||||
raster.getDataElements(lX, tY, predictor3);
|
||||
clampAddSubtractFull(predictor, predictor2, predictor3);
|
||||
|
||||
addPixels(rgba, predictor);
|
||||
break;
|
||||
case PredictorMode.CLAMP_ADD_SUB_HALF:
|
||||
raster.getDataElements(lX, y, predictor);
|
||||
raster.getDataElements(x, tY, predictor2);
|
||||
average2(predictor, predictor2);
|
||||
|
||||
raster.getDataElements(lX, tY, predictor2);
|
||||
clampAddSubtractHalf(predictor, predictor2);
|
||||
|
||||
addPixels(rgba, predictor);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
raster.setDataElements(x, y, rgba);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int ALPHA(final int ARGB) {
|
||||
return ARGB >>> 24;
|
||||
}
|
||||
|
||||
private static int RED(final int ARGB) {
|
||||
return (ARGB >> 16) & 0xff;
|
||||
}
|
||||
|
||||
private static int GREEN(final int ARGB) {
|
||||
return (ARGB >> 8) & 0xff;
|
||||
}
|
||||
|
||||
private static int BLUE(final int ARGB) {
|
||||
return ARGB & 0xff;
|
||||
}
|
||||
|
||||
private static int select(final int L, final int T, final int TL) {
|
||||
// L = left pixel, T = top pixel, TL = top left pixel.
|
||||
private static byte[] select(final byte[] l, final byte[] t, final byte[] tl) {
|
||||
// l = left pixel, t = top pixel, tl = top left pixel.
|
||||
|
||||
// ARGB component estimates for prediction.
|
||||
int pAlpha = ALPHA(L) + ALPHA(T) - ALPHA(TL);
|
||||
int pRed = RED(L) + RED(T) - RED(TL);
|
||||
int pGreen = GREEN(L) + GREEN(T) - GREEN(TL);
|
||||
int pBlue = BLUE(L) + BLUE(T) - BLUE(TL);
|
||||
|
||||
int pAlpha = addSubtractFull(l[3], t[3], tl[3]);
|
||||
int pRed = addSubtractFull(l[0], t[0], tl[0]);
|
||||
int pGreen = addSubtractFull(l[1], t[1], tl[1]);
|
||||
int pBlue = addSubtractFull(l[2], t[2], tl[2]);
|
||||
|
||||
// Manhattan distances to estimates for left and top pixels.
|
||||
int pL = abs(pAlpha - ALPHA(L)) + abs(pRed - RED(L)) +
|
||||
abs(pGreen - GREEN(L)) + abs(pBlue - BLUE(L));
|
||||
int pT = abs(pAlpha - ALPHA(T)) + abs(pRed - RED(T)) +
|
||||
abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));
|
||||
int pL = manhattanDistance(l, pAlpha, pRed, pGreen, pBlue);
|
||||
int pT = manhattanDistance(t, pAlpha, pRed, pGreen, pBlue);
|
||||
|
||||
// Return either left or top, the one closer to the prediction.
|
||||
return pL < pT ? L : T;
|
||||
return pL < pT ? l : t;
|
||||
}
|
||||
|
||||
private static int average2(final int a, final int b) {
|
||||
return (a + b) / 2;
|
||||
private static int manhattanDistance(byte[] rgba, int pAlpha, int pRed, int pGreen, int pBlue) {
|
||||
return abs(pAlpha - (rgba[3] & 0xff)) + abs(pRed - (rgba[0] & 0xff)) +
|
||||
abs(pGreen - (rgba[1] & 0xff)) + abs(pBlue - (rgba[2] & 0xff));
|
||||
}
|
||||
|
||||
private static void average2(final byte[] rgba1, final byte[] rgba2) {
|
||||
rgba1[0] = (byte) (((rgba1[0] & 0xff) + (rgba2[0] & 0xff)) / 2);
|
||||
rgba1[1] = (byte) (((rgba1[1] & 0xff) + (rgba2[1] & 0xff)) / 2);
|
||||
rgba1[2] = (byte) (((rgba1[2] & 0xff) + (rgba2[2] & 0xff)) / 2);
|
||||
rgba1[3] = (byte) (((rgba1[3] & 0xff) + (rgba2[3] & 0xff)) / 2);
|
||||
}
|
||||
|
||||
// Clamp the input value between 0 and 255.
|
||||
@ -61,12 +206,33 @@ public class PredictorTransform implements Transform {
|
||||
return max(0, min(a, 255));
|
||||
}
|
||||
|
||||
private static int clampAddSubtractFull(final int a, final int b, final int c) {
|
||||
return clamp(a + b - c);
|
||||
private static void clampAddSubtractFull(final byte[] a, final byte[] b, final byte[] c) {
|
||||
a[0] = (byte) clamp(addSubtractFull(a[0], b[0], c[0]));
|
||||
a[1] = (byte) clamp(addSubtractFull(a[1], b[1], c[1]));
|
||||
a[2] = (byte) clamp(addSubtractFull(a[2], b[2], c[2]));
|
||||
a[3] = (byte) clamp(addSubtractFull(a[3], b[3], c[3]));
|
||||
}
|
||||
|
||||
private static int clampAddSubtractHalf(final int a, final int b) {
|
||||
return clamp(a + (a - b) / 2);
|
||||
private static void clampAddSubtractHalf(final byte[] a, final byte[] b) {
|
||||
a[0] = (byte) clamp(addSubtractHalf(a[0], b[0]));
|
||||
a[1] = (byte) clamp(addSubtractHalf(a[1], b[1]));
|
||||
a[2] = (byte) clamp(addSubtractHalf(a[2], b[2]));
|
||||
a[3] = (byte) clamp(addSubtractHalf(a[3], b[3]));
|
||||
}
|
||||
|
||||
private static int addSubtractFull(byte a, byte b, byte c) {
|
||||
return (a & 0xff) + (b & 0xff) - (c & 0xff);
|
||||
}
|
||||
|
||||
private static int addSubtractHalf(byte a, byte b) {
|
||||
return (a & 0xff) + ((a & 0xff) - (b & 0xff)) / 2;
|
||||
}
|
||||
|
||||
private static void addPixels(byte[] rgba, byte[] predictor) {
|
||||
rgba[0] += predictor[0];
|
||||
rgba[1] += predictor[1];
|
||||
rgba[2] += predictor[2];
|
||||
rgba[3] += predictor[3];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,5 +12,18 @@ public class SubtractGreenTransform implements Transform {
|
||||
|
||||
@Override
|
||||
public void applyInverse(WritableRaster raster) {
|
||||
|
||||
int width = raster.getWidth();
|
||||
int height = raster.getHeight();
|
||||
|
||||
byte[] rgba = new byte[4];
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
raster.getDataElements(x, y, rgba);
|
||||
addGreenToBlueAndRed(rgba);
|
||||
raster.setDataElements(x, y, rgba);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user