我正在尝试将 Gamma 校正添加到我的渲染引擎中。我有两个问题:
1) Math.pow 真的很慢(相对于每秒被调用数千次)。所以我需要创建一个预先计算的伽玛表,可以访问而不是动态计算。(这是额外信息,不是实际问题)。
2)目前,我只能通过解包整数像素,通过用相应的伽马修改值替换 RGBA 通道来应用伽马,然后重新打包像素并将其发送回图像缓冲区。性能影响并不可怕...... 但它正在将稳定的 60fps 固定时间步长降低到大约 40fps 左右(渲染了几张图像)。
我尝试在本机代码中实现整数解包/打包,但没有看到性能改进并导致 VM 崩溃(可能是内存检查错误,但我现在并不关心修复它)。
有没有办法在不拆包/打包像素的情况下应用伽马?如果没有,你会推荐使用什么方法来做到这一点?
注意不要说使用 BufferedImageOp。它很慢,只能对整个图像进行操作(我需要特定像素)。
附加信息:
像素包装:
public static int[] unpackInt(int argb, int type) {
int[] vals = null;
int p1 = 0;
int p2 = 1;
int p3 = 2;
int p4 = 3;
switch (type) {
case TYPE_RGB:
vals = new int[3];
vals[p1] = argb >> 16 & 0xFF;
vals[p2] = argb >> 8 & 0xFF;
vals[p3] = argb & 0xFF;
break;
case TYPE_RGBA:
case TYPE_ARGB:
vals = new int[4];
vals[p4] = argb & 0xFF;
vals[p3] = argb >> 8 & 0xFF;
vals[p2] = argb >> 16 & 0xFF;
vals[p1] = argb >> 24 & 0xFF;
break;
default:
throw (new IllegalArgumentException(
"type must be a valid field defined by ColorUtils class"));
}
return vals;
}
public static int packInt(int... rgbs) {
if (rgbs.length != 3 && rgbs.length != 4) {
throw (new IllegalArgumentException(
"args must be valid RGB, ARGB or RGBA value."));
}
int color = rgbs[0];
for (int i = 1; i < rgbs.length; i++) {
color = (color << 8) + rgbs[i];
}
return color;
}
我之前废弃了代码,但我使用这个算法进行伽马校正:
protected int correctGamma(int pixel, float gamma) {
float ginv = 1 / gamma;
int[] rgbVals = ColorUtils.unpackInt(pixel, ColorUtils.TYPE_ARGB);
for(int i = 0; i < rgbVals.length; i++) {
rgbVals[i] = (int) Math.round(255 - Math.pow(rgbVals[i] / 255.0, ginv));
}
return ColorUtils.packInt(rgbVals);
}
解决方案
我最终将 GargantuChet 提出的许多想法结合到一个似乎运行良好(性能没有下降)的系统中。
一个名为 GammaTable 的类使用 gamma 值修饰符进行实例化(0.0-1.0 更暗,>1.0 更亮)。构造函数调用一个内部方法,该方法为此值构建 gamma 表。此方法也用于稍后重置 gamma:
/**
* Called when a new gamma value is set to rebuild the gamma table.
*/
private synchronized void buildGammaTable() {
table = new int[TABLE_SIZE];
float ginv = 1 / gamma;
double colors = COLORS;
for(int i=0;i<table.length;i++) {
table[i] = (int) Math.round(colors * Math.pow(i / colors, ginv));
}
}
为了应用 gamma,GammaTable 获取一个整数像素,将其解包,查找修改后的 gamma 值,并返回重新打包的整数*
/**
* Applies the current gamma table to the given integer pixel.
* @param color the integer pixel to which gamma will be applied
* @param type a pixel type defined by ColorUtils
* @param rgbArr optional pre-instantiated array to use when unpacking. May be null.
* @return the modified pixel value
*/
public int applyGamma(int color, int type, int[] rgbArr) {
int[] argb = (rgbArr != null) ? ColorUtils.unpackInt(rgbArr, color):ColorUtils.unpackInt(color, type);
for(int i = 0; i < argb.length; i++) {
int col = argb[i];
argb[i] = table[col];
}
int newColor = ColorUtils.packInt(argb);
return newColor;
}
为屏幕上的每个像素调用该applyGamma
方法。
*事实证明,拆包和重新包装像素并没有减慢任何速度。出于某种原因,嵌套调用(即ColorUtils.packInt(ColorUtils.unpackInt))
导致该方法花费更长的时间。有趣的是,我还不得不停止使用预先实例化的数组,ColorUtils.unpackInt
因为它似乎会造成巨大的性能损失。允许解包方法创建一个每次调用的新数组似乎不会影响当前上下文中的性能。