问题描述
我看到这个Java code,它非常有效地做两RGB888色彩之间完美的50%混合:
I saw this Java code that does a perfect 50% blend between two RGB888 colors extremely efficiently:
public static int blendRGB(int a, int b) {
return (a + b - ((a ^ b) & 0x00010101)) >> 1;
}
这显然等同于提取并分别平均的通道。事情是这样的:
That's apparently equivalent to extracting and averaging the channels individually. Something like this:
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;
}
但是第一种方法是更有效的。我的问题是:这是如何神奇的工作?我可以用它做什么?以及是否有与此类似更多的招数?
But the first way is much more efficient. My questions are: How does this magic work? What else can I do with it? And are there more tricks similar to this?
推荐答案
(A ^ B)及0x00010101
是通道的最小显著位本来在 A + B
如果没有随身携带都来自权。
(a ^ b) & 0x00010101
is what the least significant bits of the channels would have been in a + b
if no carry had come from the right.
这总和减去它保证被移位到下一个信道的最显著位的位是刚刚从该通道的进,玷污通过此通道。当然这也意味着,这个信道是由从下一个信道的进位不再进行。
Subtracting it from the sum guarantees that the bit that is shifted into the most significant bit of the next channel is just the carry from that channel, untainted by this channel. Of course that also means that this channel is no longer effected by the carry from the next channel.
这是其他的方式来看待这一点,不是它的方式,但一个方法,可以帮助你了解它,就是有效的输入被改变从而使它们的和甚至是所有通道。然后将携带去很好地进入至少显著位(其是零,因为即使),而不会干扰任何东西。当然,其实际作用是有点周围的其他方法,首先它只是对其求和,才把它保证的款项甚至所有通道。但顺序并不重要。
An other way to look this, not the way it does it but a way that may help you understand it, is that effectively the inputs are changed so that their sum is even for all channels. The carries then go nicely into the least significant bits (which are zero, because even), without disturbing anything. Of course what it actually does is sort of the other way around, first it just sums them, and only then does it ensure that the sums are even for all channels. But the order doesn't matter.
更具体地说,有4例(被施加从下一个信道的进前):
More concretely, there are 4 cases (before the carry from the next channel is applied):
- 的最低位的信道为0,不存在来自下一个通道进
- LSB的一个渠道是0,有是的从下通道矣。
- 的最低位的信道为1,并且不存在来自下一个通道进
- LSB的一个通道是1,有是的从下通道矣。
- the lsb of a channel is 0 and there is no carry from the next channel.
- the lsb of a channel is 0 and there is a carry from the next channel.
- the lsb of a channel is 1 and there is no carry from the next channel.
- the lsb of a channel is 1 and there is a carry from the next channel.
前两种情况是微不足道的。该变化将携带位早在通道属于,它甚至不不管它是否是0或1。
The first two cases are trivial. The shift puts the carried bit back in channel it belongs to, it doesn't even matter whether it was 0 or 1.
案例3更富有情趣。如果LSB为1,这意味着该转变将该位转移到下一个信道的最显著位。那很糟。这一点已经被取消设置某种方式 - 但你不能只是掩盖它扔掉,因为也许你的情况下,4是
Case 3 is more interesting. If the lsb is 1, that means the shift would shift that bit into the most significant bit of the next channel. That's bad. That bit has to be unset somehow - but you can't just mask it away because maybe you're in case 4.
案例4是最有趣的。如果LSB为1,有进到该位,它翻转到0,进位传播。不能被掩蔽复原,但它可以通过反转的过程中,即从减1来完成的LSB(这使回1和撤消由传播进位完成的任何损坏)。
Case 4 is the most interesting. If the lsb is 1 and there is a carry into that bit, it rolls over to a 0 and the carry is propagated. That can't be undone by masking, but it can be done by reversing the process, ie subtracting 1 from the lsb (which puts it back to 1 and undoes any damage done by the propagated carry).
正如你所看到的,在这两种情况下,3和4的情况下,治愈是从LSB减1,而这些也是其中LSB真的想成为1的情况下(但也许这是没有任何更多,由于来自下一个信道的进位),以及在这两个壳体1和2中,没有任何东西(换言之,减0)。这正好对应减去什么LSB会一直在 A + B
如果没有随身携带都来自正确的。
As you can see, in both case 3 and case 4, the cure is subtracting 1 from the lsb, and those are also the cases in which the lsb really wanted to be 1 (though maybe it isn't any more, due to a carry from the next channel), and in both case 1 and 2, you don't have to anything (in other words, subtract 0). That exactly corresponds to subtracting "what the lsb would have been in a + b
if no carry had come from the right".
此外,蓝色通道只能陷入个案1或3(有没有下一个频道上携带),并且移位只会丢弃位,而不是把它在下一通道的(因为没有)。所以,您也可写(注意面膜已经失去了至少显著1)
Also, the blue channel can only fall into cases 1 or 3 (there is no next channel which could carry), and the shift would just discard that bit instead of putting it in the next channel (because there is none). So alternatively, you may write (note the mask has lost the least significant 1)
public static int blendRGB(int a, int b) {
return (a + b - ((a ^ b) & 0x00010100)) >> 1;
}
并没有真正做任何区别,虽然。
Doesn't really make any difference, though.
要使其成为ARGB8888工作,你可以切换到良好的老SWAR平均
To make it work for ARGB8888, you can switch to the good old "SWAR average":
// channel-by-channel average, no alpha blending
public static int blendARGB(int a, int b) {
return (a & b) + (((a ^ b) & 0xFEFEFEFE) >>> 1);
}
这是一个递归的方式来定义另外一个变化: X + Y =(X ^ Y)+((X安培; Y)LT;&LT; 1)
而计算的总和,而不进行,然后添加分别进行英寸基本情况是,当一个操作数是零。
Which is a variation on a recursive way to define addition: x + y = (x ^ y) + ((x & y) << 1)
which computes the sum without carries, then adds the carries in separately. The base case is when one of the operands is zero.
两半被有效地由1右移,以这样的方式,该进位出最显著位不会丢失。掩模确保比特不移动到信道到右侧,并且同时确保了进位将不会传播其信道的
Both halves are effectively shifted right by 1, in such a way that the carry out of the most significant bit is never lost. The mask ensures that bits don't move to a channel to the right, and simultaneously ensures that a carry won't propagate out of its channel.
这篇关于请问这个混色招工作的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!