Wan*_* Ke 9 bit-manipulation go
在golang颜色包中,有一种方法可以从RGBA对象中获取r,g,b,一个值:
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
}
Run Code Online (Sandbox Code Playgroud)
如果我要实现这个简单的函数,我会写这个
func (c RGBA) RGBA() (r, g, b, a uint32) {
r = uint32(c.R)
g = uint32(c.G)
b = uint32(c.B)
a = uint32(c.A)
return
}
Run Code Online (Sandbox Code Playgroud)
是什么原因r |= r << 8使用?
该color.RGBA类型实现了RGBA满足color.Color接口的方法:
type Color interface {
// RGBA returns the alpha-premultiplied red, green, blue and alpha values
// for the color. Each value ranges within [0, 0xffff], but is represented
// by a uint32 so that multiplying by a blend factor up to 0xffff will not
// overflow.
//
// An alpha-premultiplied color component c has been scaled by alpha (a),
// so has valid values 0 <= c <= a.
RGBA() (r, g, b, a uint32)
}
Run Code Online (Sandbox Code Playgroud)
现在,RGBA类型表示具有类型的颜色通道uint8,给出范围[0,0xff].简单地将这些值转换为uint32不会将范围扩展到[0,0xffff].
适当的转换将是这样的:
r = uint32((float64(c.R) / 0xff) * 0xffff)
Run Code Online (Sandbox Code Playgroud)
但是,他们希望避免浮点运算.幸运的0xffff / 0xff是0x0101,我们可以简化表达式(暂时忽略类型转换):
r = c.R * 0x0101
= c.R * 0x0100 + c.R
= (c.R << 8) + c.R # multiply by power of 2 is equivalent to shift
= (c.R << 8) | c.R # equivalent, since bottom 8 bits of first operand are 0
Run Code Online (Sandbox Code Playgroud)
这基本上就是标准库中的代码所做的事情.
来自优秀的" The Go image package "博文:
[...]通道具有16位有效范围:100%红色由RGBA表示,返回65535而不是255,因此从CMYK或YCbCr转换不是有损的.第三,返回的类型是uint32,即使最大值是65535,也要保证将两个值相乘不会溢出.
和
注意,RGBA的R字段是[0,255]范围内的8位alpha预乘颜色.RGBA通过将该值乘以0x101来满足Color接口,以生成[0,65535]范围内的16位alpha预乘颜色
因此,如果我们查看具有该值的颜色的位表示,c.R = 10101010那么此操作
r = uint32(c.R)
r |= r << 8
Run Code Online (Sandbox Code Playgroud)
有效地将第一个字节复制到第二个字节.
00000000000000000000000010101010 (r)
| 00000000000000001010101000000000 (r << 8)
--------------------------------------
00000000000000001010101010101010 (r |= r << 8)
Run Code Online (Sandbox Code Playgroud)
这相当于与因子的乘法,0x101并在[0,65535]范围内均匀分布所有256个可能的值.