Разбираемся в некоторой магии битов в стандартной библиотеке Go

поэтому я просматривал некоторый код в стандартной библиотеке Go, пытаясь разобраться в их пакетах изображений и цветов, но нашел код, в котором я просто не могу разобраться. с http://golang.org/src/pkg/image/color/color.go?s=794:834#L14

Насколько я понимаю, он должен преобразовывать 8-битные предварительно альфа-умноженные значения RGB в 16-битные, сохраненные в 32-битных переменных, чтобы предотвратить их переполнение при умножении в графическом изображении.

Что я не могу понять, так это строки типа r |= r << 8, насколько я понимаю, это эквивалентно r = r*2^8+r, потому что r << 8 вставляет нули справа, и они получаются или объединяются со старым r.

Для ввода r = 255 это оценивается как 65535 = 2 ^ 16 - 1, что соответствует ожидаемому, но это не имеет смысла для значений в середине, которые на самом деле не сопоставляются с чем-то пропорциональным в большем диапазоне . Например, 127 сопоставляется с 32639, в то время как я ожидаю, что 32767 будет представлять 127. Что мне не хватает? Я думаю, что это как-то связано с пре-альфа-умножением...

 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
}

person Niklas Schnelle    schedule 09.05.2012    source источник


Ответы (1)


Нет, то, что вы видите, действительно имеет смысл.

Подумайте об одном (например, красном) значении. Он диктует степень красноты в пикселе и, как 8-битная величина, находится где-то между 0 и 255. Таким образом, вы можете представить все значения красноты в диапазоне.

Если вы просто сдвинете это значение на восемь бит (или умножите на 256), чтобы получить 16-битное значение цвета, вы получите число, кратное 256, где-то между 0 и 255*256 (65280) включительно.

Хотя это относительно хорошо масштабирует покраснение, оно не распределяет его должным образом по всему 16-битному диапазону.

Например, 255 в 8-битном диапазоне означает максимальную красноту. Простое умножение на 256 не дает максимального количества покраснений по 16-битной шкале, которое будет равно 65535.

При умножении на 256 и затем добавлении исходного значения (фактически умножении на 257) оно правильно распределяется в диапазоне 0..65535.

Это то же самое, что масштабировать однозначные целые числа 0..9 до диапазона 0..99. Умножение на десять — это один из способов, но лучше умножить на десять и добавить исходное значение (или умножить на одиннадцать):

n     n*10     n*10+n
-     ----     ------
0        0          0
1       10         11
2       20         22
3       30         33
4       40         44
5       50         55
6       60         66
7       70         77
8       80         88
9       90         99
person paxdiablo    schedule 09.05.2012
comment
Я понимаю, что это лучше, чем просто переключение передач, но разве это не неправильно? Например, в десятичном случае 5 сопоставляется с 55, поэтому он теряет свойство быть прямо посередине. - person Niklas Schnelle; 09.05.2012
comment
@NiklasSchnelle, 5 находится только посередине, если вы считаете, что верхнее значение равно 10 (или 9,9999 ...). На самом деле верхнее значение равно 9, поэтому 4,5 будет средним значением. Следовательно, позиция, в которой существует 5, равна 5/9, что точно такое же место, где 55 существует в диапазоне 0..99 (55/99). Ваша проблема, похоже, заключается в том, что вы думаете, что диапазон байтов равен [0,256) (включая ноль, но исключая 256, то есть до 255,99...), тогда как на самом деле он равен [0,255] (включительно на обоих концах). - person paxdiablo; 10.05.2012
comment
Другими словами, середина — это среднее значение верхней и нижней границы (0+9)/2 = 4.5. Если вы умножите 4,5 на 11, вы получите 49,5, обладающее тем же свойством в двузначном диапазоне: (0+99)/2 = 49.5. - person paxdiablo; 10.05.2012