英文:
Why does golang RGBA.RGBA() method use | and <<?
问题
在golang的color包中,有一个方法可以从RGBA对象中获取r、g、b、a值:
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
}
如果我要实现这个简单的函数,我只会写成这样:
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
}
为什么要使用r |= r << 8
这样的写法?
英文:
In the golang color package, there is a method to get r,g,b,a values from an RGBA
object:
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
}
If I were to implement this simple function, I would just write this
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
}
What's the reason r |= r << 8
is used?
答案1
得分: 14
从优秀的“The Go image package”博文中可以得知:
通道具有16位有效范围:100%的红色由RGBA表示,返回的r为65535,而不是255,因此从CMYK或YCbCr转换不会有太多损失。其次,返回的类型是uint32,即使最大值为65535,也可以保证两个值相乘不会溢出。
并且
请注意,RGBA的R字段是范围为[0, 255]的8位alpha预乘颜色。RGBA通过将该值乘以0x101来生成范围为[0, 65535]的16位alpha预乘颜色,从而满足Color接口。
因此,如果我们查看值为c.R = 10101010
的颜色的位表示,则此操作
r = uint32(c.R)
r |= r << 8
实际上是将第一个字节复制到第二个字节。
00000000000000000000000010101010 (r)
| 00000000000000001010101000000000 (r << 8)
--------------------------------------
00000000000000001010101010101010 (r |= r << 8)
这相当于使用因子0x101
进行乘法,并将所有256个可能的值均匀分布在范围[0, 65535]内。
英文:
From the the excellent "The Go image package" blogpost:
> [...] the channels have a 16-bit effective range: 100% red is represented by
> RGBA returning an r of 65535, not 255, so that converting from CMYK or
> YCbCr is not as lossy. Third, the type returned is uint32, even though
> the maximum value is 65535, to guarantee that multiplying two values
> together won't overflow.
and
> Note that the R field of an RGBA is an 8-bit alpha-premultiplied color in the range [0, 255]. RGBA satisfies the Color interface by multiplying that value by 0x101 to generate a 16-bit alpha-premultiplied color in the range [0, 65535]
So if we look at the bit representation of a color with the value c.R = 10101010
then this operation
r = uint32(c.R)
r |= r << 8
effectively copies the first byte to the second byte.
00000000000000000000000010101010 (r)
| 00000000000000001010101000000000 (r << 8)
--------------------------------------
00000000000000001010101010101010 (r |= r << 8)
This is equivalent to a multiplication with the factor 0x101
and distributes all 256 possible values evenly across the range [0, 65535].
答案2
得分: 8
color.RGBA
类型实现了RGBA
方法,以满足color.Color
接口的要求:
type Color interface {
// RGBA返回颜色的预乘alpha值、红色、绿色和蓝色值。
// 每个值的范围在[0, 0xffff]之间,但由于使用uint32表示,
// 因此乘以最大为0xffff的混合因子不会溢出。
//
// 预乘alpha颜色分量c已经乘以alpha(a)进行了缩放,
// 因此其有效值为0 <= c <= a。
RGBA() (r, g, b, a uint32)
}
现在,RGBA类型使用uint8
类型表示颜色通道,范围为[0, 0xff]。简单地将这些值转换为uint32
不会扩展范围到[0, 0xffff]。
一个适当的转换可以是:
r = uint32((float64(c.R) / 0xff) * 0xffff)
然而,他们想要避免浮点运算。幸运的是,0xffff / 0xff
等于0x0101
,因此我们可以简化表达式(暂时忽略类型转换):
r = c.R * 0x0101
= c.R * 0x0100 + c.R
= (c.R << 8) + c.R // 乘以2的幂等于左移
= (c.R << 8) | c.R // 等价,因为第一个操作数的低8位为0
这基本上就是标准库中的代码所做的事情。
英文:
The color.RGBA
type implements the RGBA
method to satisfy the color.Color
interface:
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)
}
Now the RGBA type represents the colour channels with the uint8
type, giving a range of [0, 0xff]. Simply converting these values to uint32
would not extend the range up to [0, 0xffff].
An appropriate conversion would be something like:
r = uint32((float64(c.R) / 0xff) * 0xffff)
However, they want to avoid the floating point arithmetic. Luckily 0xffff / 0xff
is 0x0101
, so we can simplify the expression (ignoring the type conversions for now):
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
And that's essentially what the code in the standard library is doing.
答案3
得分: 4
将范围在0到255之间的值(8位RGB分量)转换为范围在0到65535之间的值(16位RGB分量),可以通过将8位值乘以65535/255来完成;65535/255恰好是257,即十六进制101,因此通过将一个字节值左移8位并与原始值进行OR运算,可以将一个字节乘以65535/255。
(这与Go语言无关;在其他语言中,在将8位RGB/RGBA分量转换为16位RGB/RGBA分量时也会使用类似的技巧。)
英文:
Converting a value in the range 0 to 255 (an 8-bit RGB component) to a value in the range 0 to 65535 (a 16-bit RGB component) would be done by multiplying the 8-bit value by 65535/255; 65535/255 is exactly 257, which is hex 101, so multiplying a one-byte by 65535/255 can be done by shifting that byte value left 8 bits and ORing it with the original value.
(There's nothing Go-specific about this; similar tricks are done elsewhere, in other languages, when converting 8-bit RGB/RGBA components to 16-bit RGB/RGBA components.)
答案4
得分: 2
将RGB组件从8位转换为16位,将字节复制到16位值的高字节中。例如,0x03变为0x0303,0xFE变为0xFEFE,这样8位值0到255(0xFF)将产生16位值0到65,535(0xFFFF),并且值的分布均匀。
英文:
To convert from 8- to 16-bits per RGB component, copy the byte into the high byte of the 16-bit value. e.g., 0x03 becomes 0x0303, 0xFE becomes 0xFEFE, so that the 8-bit values 0 through 255 (0xFF) produce 16-bit values 0 to 65,535 (0xFFFF) with an even distribution of values.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论