package org.jcodec.codecs.h264.decode;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* Quarter pixel image interpolator for inter prediction
*
* @author The JCodec project
*
*/
public class Interpolator {
private static int PADDING = 16;
public int[] interpolateChroma(int[] src, int width, int height) {
int refWidth = (width << 3);
int refHeight = (height << 3);
int[] result = new int[refWidth * refHeight];
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
int resultOff = ((j << 3) + y) * refWidth + (i << 3) + x;
int w00 = j * width + i;
int w01 = w00 + (j < height - 1 ? width : 0);
int w10 = w00 + (i < width - 1 ? 1 : 0);
int w11 = w10 + w01 - w00;
int eMx = 8 - x;
int eMy = 8 - y;
result[resultOff] = (eMx * eMy * src[w00] + x * eMy * src[w10] + eMx * y * src[w01] + x * y
* src[w11] + 32) >> 6;
}
}
}
}
return result;
}
public int[] interpolateLuma(int[] src, int width, int height) {
int refWidth = (width + PADDING * 2) * 4;
int refHeight = (height + PADDING * 2) * 4;
int[] result = new int[refWidth * refHeight];
fillFullPel(src, width, height, result);
scanHPelHorizontalWithRound(refWidth, refHeight, result);
scanHPelVertical(refWidth, refHeight, result);
scanHPelCenterWidhRound(refWidth, refHeight, result);
roundHPelVertical(refWidth, refHeight, result);
scanQPel(refWidth, refHeight, result);
return result;
}
protected void scanQPel(int width, int height, int[] result) {
for (int j = 0; j < height; j += 2) {
for (int i = 0; i < width; i += 2) {
int pos = j * width + i;
int bottomHpel = j < height - 2 ? result[pos + 2 * width] : result[pos];
int rightHpel = i < width - 2 ? result[pos + 2] : result[pos];
int rightBottomHpel;
if (j < height - 2 && i < width - 2)
rightBottomHpel = result[pos + 2 * width + 2];
else if (j < height - 2)
rightBottomHpel = result[pos + 2 * width];
else if (i < width - 2)
rightBottomHpel = result[pos + 2];
else
rightBottomHpel = result[pos];
result[pos + width] = (result[pos] + bottomHpel + 1) >> 1;
result[pos + 1] = (result[pos] + rightHpel + 1) >> 1;
if ((i % 4) == (j % 4)) {
result[pos + width + 1] = (rightHpel + bottomHpel + 1) >> 1;
} else {
result[pos + width + 1] = (result[pos] + rightBottomHpel + 1) >> 1;
}
}
}
}
protected void fillFullPel(int[] src, int width, int height, int[] result) {
int stride = (width + PADDING * 2) * 4;
for (int j = 0; j < height; j++) {
int y = (j + PADDING) * 4;
for (int i = 0; i < width; i++) {
int x = (i + PADDING) * 4;
result[y * stride + x] = src[j * width + i];
}
for (int i = 0; i < PADDING; i++) {
int x = i * 4;
result[y * stride + x] = src[j * width];
}
for (int i = width + PADDING; i < width + PADDING * 2; i++) {
int x = i * 4;
result[y * stride + x] = src[j * width + width - 1];
}
}
for (int j = 0; j < PADDING; j++) {
int y = j * 4;
for (int i = 0; i < width; i++) {
int x = (i + PADDING) * 4;
result[y * stride + x] = src[i];
}
for (int i = 0; i < PADDING; i++) {
int x = i * 4;
result[y * stride + x] = src[0];
}
for (int i = width + PADDING; i < width + PADDING * 2; i++) {
int x = i * 4;
result[y * stride + x] = src[width - 1];
}
}
for (int j = height + PADDING; j < height + PADDING * 2; j++) {
int y = j * 4;
for (int i = 0; i < width; i++) {
int x = (i + PADDING) * 4;
result[y * stride + x] = src[(height - 1) * width + i];
}
for (int i = 0; i < PADDING; i++) {
int x = i * 4;
result[y * stride + x] = src[(height - 1) * width];
}
for (int i = width + PADDING; i < width + PADDING * 2; i++) {
int x = i * 4;
result[y * stride + x] = src[(height - 1) * width + width - 1];
}
}
}
protected void scanHPelVertical(int width, int height, int[] result) {
for (int i = 0; i < width; i += 4) {
int E = result[i];
int F = result[i];
int G = result[i];
int H = result[i + 4 * width];
int I = result[i + 8 * width];
int J = result[i + 12 * width];
for (int j = 0; j < height; j += 4) {
int val = E - 5 * F + 20 * G + 20 * H - 5 * I + J;
result[(j + 2) * width + i] = val;
E = F;
F = G;
G = H;
H = I;
I = J;
int nextPix = j + 16;
if (nextPix < height) {
J = result[nextPix * width + i];
}
}
}
}
protected void roundHPelVertical(int width, int height, int[] result) {
for (int i = 0; i < width; i += 4) {
for (int j = 0; j < height; j += 4) {
result[(j + 2) * width + i] = roundAndClip32(result[(j + 2) * width + i]);
}
}
}
protected void scanHPelHorizontalWithRound(int width, int height, int[] result) {
for (int j = 0; j < height; j += 4) {
int lineStart = j * width;
int E = result[lineStart];
int F = result[lineStart];
int G = result[lineStart];
int H = result[lineStart + 4];
int I = result[lineStart + 8];
int J = result[lineStart + 12];
for (int i = 0; i < width; i += 4) {
int val = E - 5 * F + 20 * G + 20 * H - 5 * I + J;
result[lineStart + i + 2] = roundAndClip32(val);
E = F;
F = G;
G = H;
H = I;
I = J;
int nextPix = i + 16;
if (nextPix < width) {
J = result[lineStart + nextPix];
}
}
}
}
protected void scanHPelCenterWidhRound(int width, int height, int[] result) {
for (int j = 0; j < height; j += 4) {
int lineStart = (j + 2) * width;
int E = result[lineStart];
int F = result[lineStart];
int G = result[lineStart];
int H = result[lineStart + 4];
int I = result[lineStart + 8];
int J = result[lineStart + 12];
for (int i = 0; i < width; i += 4) {
int val = E - 5 * F + 20 * G + 20 * H - 5 * I + J;
result[lineStart + i + 2] = roundAndClip1024(val);
E = F;
F = G;
G = H;
H = I;
I = J;
int nextPix = i + 16;
if (nextPix < width) {
J = result[lineStart + nextPix];
}
}
}
}
private int roundAndClip32(int val) {
val = (val + 16) >> 5;
val = val < 0 ? 0 : (val > 255 ? 255 : val);
return val;
}
private int roundAndClip1024(int val) {
val = (val + 512) >> 10;
val = val < 0 ? 0 : (val > 255 ? 255 : val);
return val;
}
}