为什么golang RGBA.RGBA()方法使用|和&lt; <! - ?

时间:2016-02-13 00:02:32

标签: go bit-manipulation

在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
}

如果我要实现这个简单的函数,我会写这个

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的原因是什么?

4 个答案:

答案 0 :(得分:7)

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)
}

现在RGBA类型代表uint8类型的颜色通道,给出范围[0,0xff]。简单地将这些值转换为uint32不会将范围扩展到[0,0xffff]。

适当的转换类似于:

r = uint32((float64(c.R) / 0xff) * 0xffff)

但是,他们希望避免浮点运算。幸运的是0xffff / 0xff0x0101,因此我们可以简化表达式(暂时忽略类型转换):

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

这基本上就是标准库中的代码所做的事情。

答案 1 :(得分:6)

来自优秀&#34; The Go image package&#34;志文章:

  

[...]通道具有16位有效范围:100%红色表示为   RGBA返回65535的r,而不是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

有效地将第一个字节复制到第二个字节。

   00000000000000000000000010101010 (r)
 | 00000000000000001010101000000000 (r << 8)
--------------------------------------
   00000000000000001010101010101010 (r |= r << 8)

这相当于与因子0x101的乘法,并在[0,65535]范围内均匀分布所有256个可能的值。

答案 2 :(得分:2)

要将每个RGB分量从8位转换为16位,请将字节复制到16位值的高字节中。例如,0x03变为0x0303,0xFE变为0xFEFE,因此8位值0到255(0xFF)产生16位值0到65,535(0xFFFF),值均匀分布。

答案 3 :(得分:2)

将0到255(8位RGB分量)范围内的值转换为0到65535(16位RGB分量)范围内的值,将8位值乘以65535 / 255; 65535/255正好是257,即十六进制101,因此将一个字节乘以65535/255可以通过将该字节值左移8位并将其与原始值进行“或”来完成。

(没有什么特别针对此问题;当将8位RGB / RGBA组件转换为16位RGB / RGBA组件时,其他语言也会采用类似的技巧。)