如果从右侧没有进位,
(a ^ b) & 0x00010101
就是通道最不重要的位在
a + b
中的值。将其从总和中减去可确保位移入下一个通道的最高有效位的位刚好是该通道的进位,未被此通道影响。当然,这也意味着此通道不再受到下一个通道的进位的影响。
另一种理解方法是,有效地更改输入,使它们的总和对于所有通道都是偶数。进位很好地进入了最不重要的位(因为是偶数),而没有干扰任何内容。当然,它实际上所做的事情有点相反,首先只是对它们求和,然后才确保所有通道的总和都是偶数。但顺序并不重要。
更具体地说,有4种情况(在应用来自下一个通道的进位之前):
- 通道的最不重要位为0,并且没有来自下一个通道的进位。
- 通道的最不重要位为0,并且存在来自下一个通道的进位。
- 通道的最不重要位为1,并且没有来自下一个通道的进位。
- 通道的最不重要位为1,并且存在来自下一个通道的进位。
前两种情况很简单。移位将带有进位的比特放回到它所属的通道中,无论它是0还是1都无关紧要。
第三种情况更有趣。如果最不重要位为1,则意味着移位会将该位移入下一个通道的最高有效位。那很糟糕。必须以某种方式取消该位-但您不能只是屏蔽它,因为也许您处于第4种情况。
第四种情况最有趣。如果最不重要位为1,并且存在进位,则会滚动到0并传播进位。这不能通过屏蔽来撤消,但可以通过反转过程来完成,即从最不重要位减去1(将其放回1并撤消由传播进位造成的任何损坏)。
正如您所看到的,在第3和第4种情况下,治愈方法是从最不重要位中减去1,并且这些情况下最不重要位确实希望是1(尽管可能不再是,由于来自下一个通道的进位),而在第1和第2种情况下,您无需执行任何操作(换句话说,减去0)。这恰好对应于减去“如果没有来自右侧的进位,
a + b
中最不重要的位将是什么”。
此外,蓝色通道只能落入情况1或3(没有下一个可用的通道),移位操作将丢弃该位而不是将其放入下一个通道中(因为不存在)。因此,你可以这样写(请注意掩码已失去最低有效位)。
public static int blendRGB(int a, int b) {
return (a + b - ((a ^ b) & 0x00010100)) >> 1;
}
虽然并没有什么区别。
要使其适用于ARGB8888,您可以切换到传统的"SWAR平均":
public static int blendARGB(int a, int b) {
return (a & b) + (((a ^ b) & 0xFEFEFEFE) >>> 1);
}
这是一种递归定义加法的变体:
x + y = (x ^ y) + ((x & y) << 1)
,它可以计算出没有进位的和,然后单独添加进位。其中一个操作数为零时,基本情况就出现了。
两个半部分都被有效地向右移动了1位,这样最高位的进位就不会丢失。掩码确保位不会移动到右侧的通道,并同时确保进位不会传播到其通道外。