golang的RGBA.RGBA()方法为什么使用|和<<运算符?

huangapple go评论80阅读模式
英文:

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 &lt;&lt; 8
	g = uint32(c.G)
	g |= g &lt;&lt; 8
	b = uint32(c.B)
	b |= b &lt;&lt; 8
	a = uint32(c.A)
	a |= a &lt;&lt; 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 &lt;&lt; 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 &lt;&lt; 8

effectively copies the first byte to the second byte.

   00000000000000000000000010101010 (r)
 | 00000000000000001010101000000000 (r &lt;&lt; 8)
--------------------------------------
   00000000000000001010101010101010 (r |= r &lt;&lt; 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 &lt;= c &lt;= 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 &lt;&lt; 8) + c.R    # multiply by power of 2 is equivalent to shift
  = (c.R &lt;&lt; 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.

huangapple
  • 本文由 发表于 2016年2月13日 08:02:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/35374300.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定