 2016-02-13 00:02

# 为什么golang RGBA.RGBA（）方法使用| 和<<？

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?

• 点赞
• 写回答
• 关注问题
• 收藏
• 邀请回答

#### 4条回答默认 最新

• dongyu2300 2016-02-13 00:28
已采纳

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].

点赞 打赏 评论
• duanshan1511 2016-02-13 00:24

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.

点赞 打赏 评论
• doucan9079 2016-02-13 00:31

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.

点赞 打赏 评论
• dongshi1424 2016-02-13 00:36

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

点赞 打赏 评论