我正在尝试向我的渲染引擎添加伽马校正。我遇到了两个问题:
1)Math.pow非常缓慢(相对于每秒调用数千次)。因此,我需要创建一个预先计算的伽马表,可以访问它而不是即时计算。(这是额外的信息,不是实际问题)。
2)目前,我只能通过拆包整数像素、将伽马应用于替换RGBA通道及其相应的修正值,然后重新打包像素并将其发送回图像缓冲区来完成。性能损失并不可怕,但在渲染多幅图像时,它会将稳定的60fps固定时间步长降至约40fps左右。
我尝试在本地代码中实现整数拆包/打包,但发现没有性能提升,并获得VM崩溃(可能是内存检查错误,但我现在并不关心如何解决它)。
有没有一种方法可以在不解包/打包像素的情况下应用伽马?如果没有,您推荐使用什么方法来解决这个问题?
注:不要说使用BufferedImageOp。它很慢,只能操作整个图像(我需要特定像素)。
附加信息:
像素打包:
应用伽马值时,GammaTable会获取一个整数像素,将其解包,查找修改后的伽马值,并返回重新打包的整数。
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));
}
}
应用伽马值时,GammaTable会获取一个整数像素,将其解包,查找修改后的伽马值,并返回重新打包的整数。
/**
* 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
一起使用,因为它似乎导致了巨大的性能损失。在当前上下文中,允许解包方法每次调用创建一个新数组似乎不会影响性能。