Перейти к публикации
Форум - ComputerCraft

Fingercomp's Playground

  • записей
    85
  • комментария
    323
  • просмотров
    217 412

Палитры OpenComputers

Fingercomp

1 275 просмотров

Немногие знают, как работают палитры в OpenComputers. Расскажу здесь, как избавиться от необходимости прописывать гектары цветов в палитре, покажу, как упаковываются цвета в OpenComputers и дам пару алгоритмов для работы с индексами.

 

Сразу условимся, что индексы палитр у нас будут начинаться с нуля.

 

На каждой из трёх уровней видеокарт и мониторов своя поддерживаемая палитра цветов. Будем двигаться снизу вверх.

 

Первый уровень
Палитра состоит из двух цветов: чёрного и заданного в конфиге (по умолчанию белого). При конвертации цвета в индекс палитры вернётся ближайший к данному цвет из палитры.

 

Для определения разницы между цветами здесь и далее используется функция delta. Вот как она выглядит (вместе с функций extract, выделяющей из числа вида 0xABCDEF числа 0xAB, 0xCD, 0xEF):

local function extract(color)  color = color % 0x1000000  local r = math.floor(color / 0x10000)  local g = math.floor((color - r * 0x10000) / 0x100)  local b = color - r * 0x10000 - g * 0x100  return r, g, bendlocal function delta(color1, color2)  local r1, g1, b1 = extract(color1)  local r2, g2, b2 = extract(color2)  local dr = r1 - r2  local dg = g1 - g2  local db = b1 - b2  return (0.2126 * dr^2 +          0.7152 * dg^2 +          0.0722 * db^2)end


Теперь можно конвертировать цвет в индекс палитры. Суть такова: выбираем из двух цветов ближайший и возвращаем его.

local palette = {  0x000000,  CONFIG.monochromeColor}local function t1deflate(color)  -- Сначала проверяем, совпадает ли данный цвет  -- с каким-либо из палитры  for idx, v in pairs(palette) do    if v == color then      return idx    end  end  -- Составляем таблицу разниц между цветами  local deltas = {}  for idx, v in pairs(palette) do    table.append(deltas, {idx, delta(v, color)})  end  -- Сортируем по увеличению разницы  table.sort(deltas, function(a, b)    return a[2] < b[2]  end)  -- Первый элемент будет с наимешьней разницей,  -- то есть искомый. Возвращаем индекс.  return deltas[1][1] - 1end


В случае с конвертацией из из индекса в цвет всё просто.

local function t1inflate(index)  return palette[index + 1]end


Как и говорил.

 


Второй уровень
В палитре второго уровня имеется 16 закреплённых цветов:

local palette = {0xFFFFFF, 0xFFCC33, 0xCC66CC, 0x6699FF,                 0xFFFF33, 0x33CC33, 0xFF6699, 0x333333,                 0xCCCCCC, 0x336699, 0x9933CC, 0x333399,                 0x663300, 0x336600, 0xFF3333, 0x000000}


В остальном конвертация из иднекса в цвет и из цвета в иднекс аналогична алгоритмам для первого уровня. Так как они были даны в общем виде, код для них тот же самый.

local t2deflate = t1deflatelocal t2inflate = t1inflate


Третий уровень
Палитра третьего уровня содержит уже 256 цветов: первые 16 цветов изменяются, а остальные соответствуют цветам палитры RGB-6-8-5. Это означает, что можно смешивать 6 оттенков красного, 8 оттенков зелёного и 5 оттенков синего. В общем-то, довольно очевидна причина такого выбора: человеческий глаз лучше всего различает оттенки зелёного и хуже всего - оттенки синего.

 


В любом случае, здесь алгоритмец будет посложнее. Сначала нужно сгенерировать палитру.
Начнём с первых 16 цветов. Они не включаются в палитру RGB-6-8-5, поэтому их заполнять нужно отдельно. В OpenComputers по умолчанию они содержат оттенки серого. Так как чёрный и белый уже включены в основную, зафиксированную палитру, то заново их дублировать смысла нет.

local palette = {}-- grayscalefor i = 1, 16, 1 do  palette[i] = 0xFF * i / (16 + 1) * 0x10101end


Таким образом в таблице получаются следующие оттенки серого:

0x0F, 0x1E, 0x2D, 0x3C, 0x4B, 0x5A, 0x69, 0x78,0x87, 0x96, 0xA5, 0xB4, 0xC3, 0xD2, 0xE1, 0xF0


Эти цвета мы записываем в индексы от 0 до 15. Теперь нужно сгенерировать остальные цвета - они не изменяются. Здесь будет посложнее.
Посмотрим на картинку с палитрой:
RGB_6-8-5levels_palette.png
В OpenComputers левая верхняя ячейка палитры (0x000000) имеет индекс 16, а правая нижняя (0xFFFFFF) имеет индекс 255. Индексы распределяются слева направо, сверху вниз. То есть правая верхняя ячейка (0x00FFFF) имеет индекс 55, а вторая сверху и левая (0x330000) - это номер 56. Отсюда вытекает следующий алгоритм нахождения цвета: сначала найти индексы отдельно по R, G, B, затем для каждого из этих трёх индексов найти соответствующий ему оттенок цвета, а затем всё сложить.

for idx = 16, 255, 1 do  local i = idx - 16  local iB = i % 5  local iG = (i / 5) % 8  local iR = (i / 5 / 8) % 6  local r = math.floor(iR * 0xFF / (6 - 1) + 0.5)  local g = math.floor(iG * 0xFF / (8 - 1) + 0.5)  local b = math.floor(iB * 0xFF / (5 - 1) + 0.5)  palette[idx + 1] = r * 0x10000 + g * 0x100 + bend


К слову сказать, math.floor(x + 0.5) - это округление до ближайшего целого.

 


Всё. Палитра есть, теперь можно, наконец-то, конвертировать индексы между цветами.
Из индексов получить цвет довольно просто. Достаточно использовать ту же функцию, что и для предыдущих уровней:

t3inflate = t2inflate


С обратной же конвертацией всё в разы сложнее. Функция, используемая в OC, подбирает ближайший цвет очень хитрым алгоритмом, который я привожу ниже.

local function t3deflate(color)  local paletteIndex = t2deflate(color)  -- Если цвет из палитры, то используем значение выше  for k, v in pairs(palette) do    if v == color then      return paletteIndex    end  end  -- Иначе используем хитромудрый код  local r, g, b = extract(color)  local idxR = math.floor(r * (6 - 1) / 0xFF + 0.5)  local idxG = math.floor(g * (8 - 1) / 0xFF + 0.5)  local idxB = math.floor(b * (5 - 1) / 0xFF + 0.5)  local deflated = 16 + idxR * 8 * 5 + idxG * 5 + idxB  if (delta(t3inflate(deflated % 0x100), color) <      delta(t3inflate(paletteIndex & 0x100), color)) then    return deflated  else    return paletteIndex  endend


В общем-то, это всё. Показал портированный со Scala на Lua код, который используется в OpenComputers. С помощью этого можно оптимизировать операции с экраном, выбирая поддерживаемые монитором цвета. И заодно избавиться от таблиц цветов, которые некоторые буквально берут и копипастят в файл, даже не задумываясь об изменяемых цветах палитры.
Особенно это важно, когда берётся значение цвета через gpu.get, потому что следующий код всегда вернёт false:

local gpu = require("component").gpugpu.setForeground(0x20AFFF)gpu.setBackground(0x20AFFF)gpu.set(1, 1, "HI")return select(2, gpu.get(1, 1)) == 0x20AFFF


И всё потому, что gpu.get возвращает уже приведённый к индексу из палитры цвет. А 0x20AFFF в палитре, если не менять первые 16 цветов, не имеется.

 


Enjoy :P

  • Like 11


3 комментария


Рекомендованные комментарии

Отлично! Давно хотел написать статью по работе с цветом. А теперь можно просто оставить ссылку, т. к. все основы хорошо разобраны.

Поделиться комментарием


Ссылка на комментарий

Создайте аккаунт или войдите в него для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать аккаунт

Зарегистрируйтесь для получения аккаунта. Это просто!

Зарегистрировать аккаунт

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас
×