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

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

4个回答

14

来自优秀的 "Go图像包" 博客文章:

[...] 通道具有16位有效范围:100%红色由RGBA表示,返回r为65535,而不是255,因此从CMYK或YCbCr转换不会丢失太多信息。 第三,即使最大值为65535,返回类型仍为uint32,以保证将两个值相乘时不会导致溢出。

以及

请注意,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]内。


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

现在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    # multiply by power of 2 is equivalent to shift
  = (c.R << 8) | c.R    # equivalent, since bottom 8 bits of first operand are 0

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


4
将在0到255范围内的值(8位RGB分量)转换为在0到65535范围内的值(16位RGB分量),需要将8位值乘以65535/255;65535/255恰好是257,也就是十六进制101,因此将一个字节乘以65535/255可以通过将该字节值向左移动8位并与原始值进行OR运算来完成。这并不特定于Go语言;在其他语言中,当将8位RGB/RGBA分量转换为16位RGB/RGBA分量时,也会使用类似的技巧。

2

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接