英文:
Making sense of some bit magic in the Go standard library
问题
所以我一直在查看Go标准库中的一些代码,试图理解它们的图像和颜色包,但发现了一些我无法理解的代码。
从http://golang.org/src/pkg/image/color/color.go?s=794:834#L14
根据我的理解,这段代码应该将8位预乘alpha的RGB值转换为16位的值,并保存在32位变量中,以防止在图像运算中溢出。
我无法理解的是像r |= r << 8
这样的代码行,据我所知,这等同于r = r*2^8+r
,因为r << 8
在右侧插入了零,并与旧的r进行或运算。
对于r=255的输入,这将计算为65535=2^16 - 1,这是预期的结果,但对于中间的值来说,这并没有意义,因为它们在更大的范围内并没有被映射到与之成比例的值。
例如,127被映射为32639,而我期望32767表示127。
我错过了什么?我认为这与预乘alpha有关...
func (c RGBA) RGBA() (r, g, b, a uint32) {
r = uint32(c.R)
r |= r << 8
g = uint32(c.G)
g |= g << 8
b = uint32(c.B)
b |= b << 8
a = uint32(c.A)
a |= a << 8
return
}
英文:
so I've been sifting through some code in the Go standard library trying to make sense of their image and color packages but found some code I just can't make sense of.
from http://golang.org/src/pkg/image/color/color.go?s=794:834#L14
From my understanding it should convert 8bit pre-alpha-multiplied RGB values to 16 bit ones, saved in 32bit variables to stop them from overflowing when multiplying in image arthimetic.
What I can't make sense of are the lines like r |= r << 8
as I understand it this is equivalent to r = r*2^8+r
because r << 8
inserts zeros to the right and they get or'ed with the old r.
For an input of r=255 this evaluates to 65535=2^16 - 1 which is as expected, but it doesn't make sense for the values in the middle, which don't really get mapped to something proportional in the bigger range.
For example 127 get's mapped to 32639 while I'd expect 32767 to represent 127.
What am I missing? I think it has something to do with the pre-alpha-multiplication...
func (c RGBA) RGBA() (r, g, b, a uint32) {
r = uint32(c.R)
r |= r << 8
g = uint32(c.G)
g |= g << 8
b = uint32(c.B)
b |= b << 8
a = uint32(c.A)
a |= a << 8
return
}
答案1
得分: 10
不,你所看到的实际上是有道理的。
想象一个单一的(例如红色)值。它决定了像素中红色的数量,并且作为一个8位的量,它的取值范围在0到255之间。因此,你可以表示范围内所有红色的值。
如果你只是将其左移8位(或者乘以256)以获得一个16位的颜色值,你将得到一个在0到255*256(65280)之间的256的倍数。
虽然这样可以相对较好地扩大红色的值,但它不能正确地分布在整个16位范围内。
例如,在8位范围内,255表示最大的红色。简单地将其乘以256并不能给你在16位范围内的最大红色值,这个值应该是65535。
通过先乘以256,然后再加上原始值(实际上是乘以257),它可以正确地分布在0到65535的范围内。
这与将一位数的整数0..9
扩大到范围0..99
是一样的。乘以十是一种方法,但更好的方法是乘以十然后加上原始值(或者乘以十一):
n n*10 n*10+n
- ---- ------
0 0 0
1 10 11
2 20 22
3 30 33
4 40 44
5 50 55
6 60 66
7 70 77
8 80 88
9 90 99
英文:
No, what you're seeing actually makes sense.
Think of a single (red, for example) value. It dictates the amount of redness in the pixel and, as an 8-bit quantity, it's somewhere between 0 and 255. Thus you can represent all values of redness in the range.
If you just bitshifted that by eight bits (or multiplied by 256) to get a 16-bit colour value, you'd end up with a multiple of 256 somewhere between 0 and 255*256 (65280) inclusive.
While that scales the redness up relatively well, it doesn't distribute it properly across the full 16-bit range.
For example, 255 in the 8-bit range means maximum redness. Simply multiplying that by 256 doesn't give you the maximum amount of redness on the 16-bit scale, which would be 65535.
By multiplying by 256 and then adding the original (effectively multiplying by 257), it distributes correctly across the 0..65535 range.
It's the same as scaling up the single-digit integers 0..9
into the range 0..99
. Multiplying by ten is one way but a better way is to multiply by ten and add the original value (or multiply by eleven):
n n*10 n*10+n
- ---- ------
0 0 0
1 10 11
2 20 22
3 30 33
4 40 44
5 50 55
6 60 66
7 70 77
8 80 88
9 90 99
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论