package org.jcodec.scale;
import org.jcodec.common.model.Picture8Bit;
import org.jcodec.common.model.Size;
import org.jcodec.common.tools.MathUtil;
/**
* Resamples image interpolating points using Lanczos sinc over sine windowed
* filter.
*
* @author Stanislav Vitvitskiy
*/
public abstract class BaseResampler {
private ThreadLocal<int[]> tempBuffers = new ThreadLocal<int[]>();
private Size toSize;
private Size fromSize;
private double scaleFactorX;
private double scaleFactorY;
public BaseResampler(Size from, Size to) {
this.toSize = to;
this.fromSize = from;
scaleFactorX = (double) from.getWidth() / to.getWidth();
scaleFactorY = (double) from.getHeight() / to.getHeight();
}
byte getPel(Picture8Bit pic, int plane, int x, int y) {
if (x < 0)
x = 0;
if (y < 0)
y = 0;
int w = pic.getPlaneWidth(plane);
if (x > w - 1)
x = w - 1;
int h = pic.getPlaneHeight(plane);
if (y > h - 1)
y = h - 1;
return pic.getData()[plane][x + y * w];
}
protected abstract short[] getTapsX(int dstX);
protected abstract short[] getTapsY(int dstY);
protected abstract int nTaps();
/**
* Converts floating point taps to fixed precision taps.
*
* @param taps
* The 64 bit double representation
* @param precBits
* Precision bits
* @param out
* Taps converted to fixed precision
*/
public static void normalizeAndGenerateFixedPrecision(double[] taps, int precBits, short[] out) {
double sum = 0;
for (int i = 0; i < taps.length; i++) {
sum += taps[i];
}
int sumFix = 0;
int precNum = 1 << precBits;
for (int i = 0; i < taps.length; i++) {
double d = (taps[i] * precNum) / sum + precNum;
int s = (int) d;
taps[i] = d - s;
out[i] = (short) (s - precNum);
sumFix += out[i];
}
long tapsTaken = 0;
while (sumFix < precNum) {
int maxI = -1;
for (int i = 0; i < taps.length; i++) {
if ((tapsTaken & (1 << i)) == 0 && (maxI == -1 || taps[i] > taps[maxI]))
maxI = i;
}
out[maxI]++;
sumFix++;
tapsTaken |= (1 << maxI);
}
for (int i = 0; i < taps.length; i++) {
taps[i] += out[i];
if ((tapsTaken & (1 << i)) != 0)
taps[i] -= 1;
}
}
/**
* Interpolates points using a 2d convolution
*/
public void resample(Picture8Bit in, Picture8Bit out) {
int[] temp = tempBuffers.get();
if (temp == null) {
temp = new int[toSize.getWidth() * (fromSize.getHeight() + nTaps())];
tempBuffers.set(temp);
}
for (int p = 0; p < in.getColor().nComp; p++) {
// Horizontal pass
for (int y = 0; y < in.getPlaneHeight(p) + nTaps(); y++) {
for (int x = 0; x < out.getPlaneWidth(p); x++) {
short[] tapsXs = getTapsX(x);
int srcX = (int) (scaleFactorX * x) - nTaps() / 2 + 1;
int sum = 0;
for (int i = 0; i < nTaps(); i++) {
sum += (getPel(in, p, srcX + i, y - nTaps() / 2 + 1) + 128) * tapsXs[i];
}
temp[y * toSize.getWidth() + x] = sum;
}
}
// Vertical pass
for (int y = 0; y < out.getPlaneHeight(p); y++) {
for (int x = 0; x < out.getPlaneWidth(p); x++) {
short[] tapsYs = getTapsY(y);
int srcY = (int) (scaleFactorY * y);
int sum = 0;
for (int i = 0; i < nTaps(); i++) {
sum += temp[x + (srcY + i) * toSize.getWidth()] * tapsYs[i];
}
out.getPlaneData(p)[y * out.getPlaneWidth(p) + x] = (byte) (MathUtil.clip((sum + 8192) >> 14, 0,
255) - 128);
}
}
}
}
}