Почему применяется метод golang RGBA.RGBA() | и <<?
В цветовом пакете golang существует способ получить r, g, b, значения из объекта RGBA
:
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
?
Ответы
Ответ 1
Из отличного "" Пакет изображений Go "" blogpost:
[...] каналы имеют 16-битный эффективный диапазон: 100% красный представлен RGBA возвращает r из 65535, а не 255, так что преобразование из CMYK или YCbCr не является убыточным. В-третьих, возвращаемый тип - uint32, хотя максимальное значение равно 65535, чтобы гарантировать, что умножение двух значений вместе не будут переполняться.
и
Обратите внимание, что поле R RGBA является 8-битным альфа-преломленным цветом в диапазоне [0, 255]. RGBA удовлетворяет интерфейсу Color, умножая это значение на 0x101 для генерации 16-битного альфа-преломляющего цвета в диапазоне [0, 65535]
Итак, если мы посмотрим на представление битов цвета со значением c.R = 10101010
, то эта операция
r = uint32(c.R)
r |= r << 8
эффективно копирует первый байт во второй байт.
00000000000000000000000010101010 (r)
| 00000000000000001010101000000000 (r << 8)
--------------------------------------
00000000000000001010101010101010 (r |= r << 8)
Это эквивалентно умножению с коэффициентом 0x101
и равномерно распределяет все 256 возможных значений по диапазону [0, 65535].
Ответ 2
Тип 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
И это существенно то, что делает код в стандартной библиотеке.
Ответ 3
Для преобразования от 8 до 16 бит на компонент RGB скопируйте байт в старший байт 16-разрядного значения. например, 0x03 становится 0x0303, 0xFE становится 0xFEFE, так что 8-битные значения от 0 до 255 (0xFF) генерируют 16-битные значения от 0 до 65535 (0xFFFF) с равномерным распределением значений.
Ответ 4
Преобразование значения в диапазоне от 0 до 255 (8-разрядный компонент RGB) в значение от 0 до 65535 (16-разрядный компонент RGB) будет выполняться путем умножения 8-битного значения на 65535/255; 65535/255 - это ровно 257, то есть шестнадцатеричный 101, поэтому умножение однобайтного на 65535/255 может быть выполнено сдвигом этого байтового значения влево на 8 бит и ORing с исходным значением.
(Нет ничего особенного в этом отношении, подобные трюки выполняются в другом месте на других языках при преобразовании 8-разрядных компонентов RGB/RGBA в 16-разрядные компоненты RGB/RGBA.)