Boa*_*ann 8 java language-agnostic optimization bit-manipulation color-blending
我看到这个Java代码可以非常有效地在两种RGB888颜色之间进行完美的50%混合:
public static int blendRGB(int a, int b) {
return (a + b - ((a ^ b) & 0x00010101)) >> 1;
}
Run Code Online (Sandbox Code Playgroud)
这显然相当于单独提取和平均通道.像这样的东西:
public static int blendRGB_(int a, int b) {
int aR = a >> 16;
int bR = b >> 16;
int aG = (a >> 8) & 0xFF;
int bG = (b >> 8) & 0xFF;
int aB = a & 0xFF;
int bB = b & 0xFF;
int cR = (aR + bR) >> 1;
int cG = (aG + bG) >> 1;
int cB = (aB + bB) >> 1;
return (cR << 16) | (cG << 8) | cB;
}
Run Code Online (Sandbox Code Playgroud)
但第一种方式更有效率.我的问题是:这个神奇的工作原理如何?我还能用它做什么?还有更多类似的技巧吗?
(a ^ b) & 0x00010101a + b如果没有来自右翼的进位,那么这些通道中最不重要的位是什么.
从总和中减去它可以保证转移到下一个通道的最高有效位的位只是来自该通道的进位,不受该通道的影响.当然,这也意味着该通道不再受来自下一个通道的进位的影响.
另一种看待这种情况的方式,不是它的方式,而是一种可以帮助你理解它的方式,就是有效地改变输入,使得它们的总和对于所有通道都是均匀的.然后,进位很好地进入最低有效位(零,因为均匀),而不会打扰任何东西.当然它实际上做的是反过来,首先它只是对它们求和,然后才确保所有通道的总和均匀.但顺序并不重要.
更具体地说,有4种情况(在应用下一个频道的进位之前):
前两个案件是微不足道的.该移位将所携带的位置回其所属的通道中,即使它是0还是1也无关紧要.
案例3更有趣.如果lsb为1,则意味着移位会将该位移位到下一个通道的最高位.那很糟.这一点必须以某种方式取消 - 但你不能只是掩盖它,因为也许你是在案件4.
案例4是最有趣的.如果lsb为1并且该位有进位,则它会翻转到0并且进位传播.这不能通过掩蔽来解除,但可以通过反转过程来完成,即从lsb中减去1(将其放回1并撤消传播进位所造成的任何损坏).
正如你所看到的,在案例3和案例4中,治愈从lsb中减去1,这些也是lsb真正想要为1的情况(尽管可能不再是,因为从下一个频道开始),在情况1和2中,你没有任何东西(换句话说,减去0).这恰好相当于减去" a + b如果没有来自右边的东西,那将是什么lsb ".
此外,蓝色通道只能落入情况1或3(没有下一个可以携带的通道),并且移位只会丢弃该位而不是将其放入下一个通道(因为没有).所以,你也可以写(注意面具已失去最不重要的1)
public static int blendRGB(int a, int b) {
return (a + b - ((a ^ b) & 0x00010100)) >> 1;
}
Run Code Online (Sandbox Code Playgroud)
但是,实际上没有任何区别.
为了使其适用于ARGB8888,您可以切换到旧的"SWAR平均值":
// channel-by-channel average, no alpha blending
public static int blendARGB(int a, int b) {
return (a & b) + (((a ^ b) & 0xFEFEFEFE) >>> 1);
}
Run Code Online (Sandbox Code Playgroud)
这是定义加法的递归方式的变体:x + y = (x ^ y) + ((x & y) << 1)它计算没有进位的和,然后分别添加进位.基本情况是其中一个操作数为零.
两半都有效地向右移动1,这样就不会丢失最重要位的执行.掩码确保位不会移动到右侧的通道,同时确保进位不会传播出其通道.
| 归档时间: |
|
| 查看次数: |
322 次 |
| 最近记录: |