Почему применяется метод 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.)