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

Формат картинок для OpenComputers

Рекомендованные сообщения

"Недавно" почитал о формате .png и под впечатлением(ради чего сделал бесполезную семибайтную подпись файла) решил придумать свой формат изображения для OpenComputers и, естественно библиотеку для работы с ним.

 

Изображение закодированное в этом формате имеет малый размер, поддерживает альфа-канал и в нем возможно закодировать utf8 символ на "пискель".

 

Картинки:

 

 

nAODBSZ.png

FQVPCOk.png

cxJVJhq.png

 

 

Библиотека для работы с .ocif

Конечно же я сделал библиотеку для работы с форматом. Точнее сказать библиотека сделала формат, но это неважно.

Сама же библиотека умеет делать три тривиальные вещи:

  • Кодировать изображение и записывать в файл: write( filename:string, image:table, full_array:bool ):nil
    filename - название файла, в который запишется закодированное изображение
    image - массив изображения; может быть представлен в двух форматах: удобном для восприятия и оптимизированным для уменьшения занимаемой оперативной памяти
    full_array - выбор формата массива: true - "удобный" массив, в ином случае оптимизированный
     
  • Читать изображение: read( filename:string, full_array:bool ):table
    filename
    - название файла, который будет прочитан и декодирован. Результат возвращается в виде оптимизированного формата массива
  • full_array - выбор формата массива: true - "удобный" массив, в ином случае оптимизированный
     
  • Рисовать прочитанное изображение: draw(image:table, frame:number, rawPosX:number, drawPosY:number, gpu:table ):nil
    image - принимает оптимизированный формат изображения
    frame - выбор фрейма, который будет отрисован.
    drawPosX, drawPosY - позиция с которой происходит отрисовка
    gpu - ваша видеокарта
    Функция неоптимизирована и нестабильна!

Так же теперь есть небольшие настройки:

  • Выбор режима работы: setMode( mode:string ):bool
    на данный момент режима только два, это: "24bit" - 24bit формат, и стандартный: "8bit" или nil - то есть 8bit.
    24bit требует больше места для хранения, но точно передает все доступные цвета; 8bit занимет меньше места на диске, но у него хромает цветопередача(240 цветных оттенков + 16 оттенков серого).
  • Выбор месторасположения файла ралитры: setPalette( palette_path:string ):nil
    выбирает местораположение файла с палитрой, т.к. вам, вероятно, будет неудобен стандартный вариант с расположением в корне и названием "palette.cia".
    В режиме 8bit необходимо загружать палитру этой функцией!

 

Это собственно и все, что она умеет делать.

 

.OCIF - Open Computers Image Format

 

Так как объяснить как работает данный формат стало тяжело даже мне, я решил удалить этот раздел, пока не придумаю как описать то, что происходит в этой адской системе.

 

Но вкраце расскажу. "Пиксель" в данном формате кодируется 4 байтами в лучшем случае и 9 байтами в худшем(т.к размер utf8 символа в байтах имеет переменную длину). Первый байт кодирует цвет символа, второй - цвет фона, третий - альфа-канал, а остальными байтами кодируется символ. В файл также записывается такая служебная информация как подпись формата и ширина с высотой изображения.

 

Расскажу немного о массивах, в которых у нас хранится раскодированное изображение.

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

 

Так же данный формат поддерживает альфа-канал.

 

Вот так выглядит обыный массив:

 

 

--Константы(можно скопипастить)--
local IMAGE_WIDTH  = 1
local IMAGE_HEIGHT = 2
local IMAGE_FRAMES = 3
local IMAGE        = 4

--Сырое изображение, т.е. массив(первый формат, удобен для редактирования из редактора)
local ocif_image_raw = {
    [IMAGE_WIDTH] = 8, --Ширина изображения
    [IMAGE_HEIGHT] = 4, --Высота изображения
    [IMAGE_FRAMES] = 2, --Кол-во изображений
    [IMAGE] = {
        0xffffff, 0x000000, 0, '┌',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x000000, 0, '┐',

        0xffffff, 0x114B96, 120, '╞',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, 'O',
        0xffffff, 0x114B96, 120, 'C',
        0xffffff, 0x114B96, 120, 'I',
        0xffffff, 0x114B96, 120, 'F',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, '╡',
    
        0xffffff, 0x114B96, 120, '╞',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, 'L',
        0xffffff, 0x114B96, 120, 'I',
        0xffffff, 0x114B96, 120, 'B',
        0xffffff, 0x114B96, 120, 'R',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, '╡',

        0xffffff, 0x114B96, 120, '└',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, '─',
        0xffffff, 0x114B96, 120, '┘'
    },
    [IMAGE+1] = {
        0xffffff, 0x000000, 0, '┌',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x000000, 0, '┐',

        0xffffff, 0x114B96, 0, '╞',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, 'O',
        0xffffff, 0x114B96, 0, 'C',
        0xffffff, 0x114B96, 0, 'I',
        0xffffff, 0x114B96, 0, 'F',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, '╡',
    
        0xffffff, 0x114B96, 0, '╞',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, 'L',
        0xffffff, 0x114B96, 0, 'I',
        0xffffff, 0x114B96, 0, 'B',
        0xffffff, 0x114B96, 0, 'R',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, '╡',

        0xffffff, 0x114B96, 0, '└',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, '─',
        0xffffff, 0x114B96, 0, '┘'
    }
}

 

 

 

А так оптимизированный:

 

 

local IMAGE_WIDTH  = 1
local IMAGE_HEIGHT = 2
local IMAGE        = 3

local image_raw_optimized {
[IMAGE_WIDTH] = 8,
[IMAGE_HEIGHT] = 5,
	[IMAGE] = {
		65480,"1",
		65480,"0",
		65480,"З",
		65480,"0",
		65480,"1",
		2760," ",
		2760," ",
		2760," ",

		2560," ",
		14745344,"1",
		65280,"0",
		65280,"1",
		65280,"0",
		65280,"1",
		2560," ",
		2560," ",

		2560," ",
		2560," ",
		65280,"1",
		65280,"0",
		65280,"1",
		65280,"0",
		65280,"1",
		2560," ",

		65280,"P",
		65280,"A",
		65280,"S",
		65280,"░",
		65280,"Ж",
		65280,"B",
		65280,"I",
		65280,"N",

		22783," ",
		22783," ",
		22783," ",
		22728," ",
		22728," ",
		22728," ",
		22728," ",
		22728," "
	}
}

 

 

Думаю это наглядно объяснит, почему лучше не надо редактировать во втором формате массива?

 

Установка

Установка библиотеки: wget https://raw.githubusercontent.com/Pirnogion/OpenComputers_library/master/ocif/ocif.lua lib/ocif.lua
Установка цветовой палитры: wget https://raw.githubusercontent.com/Pirnogion/OpenComputers_library/master/ocif/palette.cia lib/palette.cia
Установка примера:    wget https://raw.githubusercontent.com/Pirnogion/OpenComputers_library/master/ocif/example.lua /example

В завершение

Весь исходный код можно посмотреть на GitHub: https://github.com/Pirnogion/OpenComputers_library/tree/master/ocif

 

З.Ы. Насчет бита-терминатора - так его обозвали в википедии, ко мне никаких претензий.

З.Ы.Ы. Насчет моего английского - ОТСТАНЬТЕ! Во всем виноват Google translate.

 

Обновления

Обновление 1:

  • Добавил альфа-канал, теперь есть возможность делать изображение полупрозрачными.
  • Так же изменил "форму" массива(сырого изображения) для того, чтобы он занимал меньше оперативной памяти, к сожалению массив стал менее понятным "из кода".
  • Добавил пример, который показывает как пользоваться библиотекой.
     

Обновление 2:

  • Изменил способ кодировки, теперь файл занимает вдвое меньший размер на диске.
  • Добавил оптимизированный формат массива сырого изображения.
  • Сделал возможность передавать массив в двух форматах.
  • Небольшой фикс примера.

 

Обновление 3:

  • Изменил способ конвертации цветов при помощи формулы на таблицу цветов.
  • Теперь для работы нужен файл цветовой палитры.
  • Появились небольшие цветопотери. Неясно почему.

 

Обновление 4:

  • Исправил ситуацию с потерей цветов.
  • Теперь можно выбирать режим работы 24bit(без цветопотерь) или 8bit(с небольшой потерей цветов). В первом случае размер файла выходит больше, а во-втором, соответственно, меньше.

Обновление 5:

  • Выловил кучу багов и сделал некоторые оптимизации

Обновление 6:

  • Добавил по предложению одного человека, возможность записывать в один файл до нескольких изображений.
  • Добавил возможность чтения изображения в "удобной", неоптимизированной формате массива.
  • Убрал принудительную загрузку палитры. Теперь ее необходимо прогружать функцией setPalette.
Изменено пользователем JaggerDer
  • Like 7

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

 

 

Во всем виноват Google translate.
  :giggle:

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Сделал пару обновлений библиотеки. Добавил альфа-канал и провел пару оптимизаций.

 

Кстати, насчет того, что нельзя засунуть в 1 байт целый цвет. Оказывается я ошибался и кодировал цвета в 24 битном формате, что очень избыточно для 8 битного цвета, который используется в OpenComputers. Можно цвет закодировать и 1 байтом. Проверено на практике. А совсем недавно пытался доказать паре человек обратное :)


Достижение получено

Написать и придумать РАБОЧИЙ формат картинок

  • Like 2

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Давно юзаю данную библиотеку в своей "ОС", каждая иконка - файл со структурой ocif. Удобная штука, рекомендую :)

 

c1a2f30d010a63a75f1ac41b97575b8d.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

 

 

А совсем недавно пытался доказать паре человек обратное
Похоже, я на подсознательном уровне понимал, что это возможно, но не мог объяснить =)) 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Похоже, я на подсознательном уровне понимал, что это возможно, но не мог объяснить =)) 

 

Кстати, знаешь, какие костыли мы придумываем, чтобы сделать  адекватную трансляцию 0xffffff -> 0xff? Кошмар какой-то - начинали с генерации 8-битной палитры и заканчиваем какими-то совершенно безумными формулами :D

 

Вот примерчик:

 

85b8f57323cdd05b5fdfba050d7c0d5f.jpg

Изменено пользователем EliteClubSessions

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Кстати, знаешь, какие костыли мы придумываем, чтобы сделать  адекватную трансляцию 0xffffff -> 0xff? Кошмар какой-то - начинали с генерации 8-битной палитры и заканчиваем какими-то совершенно безумными формулами :D

 

 

Сделал пару обновлений библиотеки. Добавил альфа-канал и провел пару оптимизаций.

Кстати, насчет того, что нельзя засунуть в 1 байт целый цвет. Оказывается я ошибался и кодировал цвета в 24 битном формате, что очень избыточно для 8 битного цвета, который используется в OpenComputers. Можно цвет закодировать и 1 байтом. Проверено на практике. А совсем недавно пытался доказать паре человек обратное :)

 

OC, для мониторов тир3, юзает стандартную программную 8-битную палитру 6*8*5+16

 

Внутри кода sicot.lua есть простая формула генерации, и комментарии по безопасной палитре.

Изменено пользователем swg2you
  • Like 2

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

 

 

Внутри кода sicot.lua есть простая формула генерации, и комментарии по безопасной палитре.
 

 

Красава, спасибо!

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

OC, для мониторов тир3, юзает стандартную программную 8-битную палитру 6*8*5+16

 

Внутри кода sicot.lua есть простая формула генерации, и комментарии по безопасной палитре.

Но монитор третьего уровня почему-то выводит больше чем 240 цветов и больше 16 цветов grayscale. Но за формулу все-равно спасибо. Вот таблица цветов, которую я сгенерировал по вашей формуле. Может быть я где-то накосячил.

 

Код генерации таблицы:

 

 

local f = io.open("colors", "w")
local line = 0
local x = 0

f:write( "colors = {\n" )
for r=0,5 do
  for g=0,7 do
    for b=0,4 do
      line = "	[" .. x .. "] = 0x" .. string.format("%X", r*0x330000 + g*0x2400 + b*0x3F) .. ",\n"
      f:write( line )
      x = x + 1
    end 
  end
end

for i=1, 15, 1 do
	line = "	[" .. 240+i .. "] = 0x" .. string.format("%X", gpu.getPaletteColor(i)) .. ",\n"
    f:write( line )
end

f:write( "}\n" )

f:close()

 

 

Изменено пользователем JaggerDer

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Но монитор третьего уровня почему-то выводит больше чем 240 цветов и больше 16 цветов grayscale. Но за формулу все-равно спасибо. Вот таблица цветов, которую я сгенерировал по вашей формуле. Может быть я где-то накосячил.

 

Код генерации таблицы:

 

 

local f = io.open("colors", "w")
local line = 0
local x = 0

f:write( "colors = {\n" )
for r=0,5 do
  for g=0,7 do
    for b=0,4 do
      line = "	[" .. x .. "] = 0x" .. string.format("%X", r*0x330000 + g*0x2400 + b*0x3F) .. ",\n"
      f:write( line )
      x = x + 1
    end 
  end
end

for i=1, 15, 1 do
	line = "	[" .. 240+i .. "] = 0x" .. string.format("%X", gpu.getPaletteColor(i)) .. ",\n"
    f:write( line )
end

f:write( "}\n" )

f:close()

 

 

Таблица и код вроде верные. 

 

При записи цвета 0xRRGGBB в монитор, значение 0xRRGGBB приводится к сокращенной палитре, реальное значение записанного цвета можно посмотреть вычитав знакоместо с помощью gpu.get. Т.е. если мы сделаем:

gpu.setForeground(0x123456)
gpu.set(5,5,' ')
local s, fg, bg = gpu.get(5,5)

То в fg мы получим вовсе не 0x123456, а приведенное значение. Алгоритм приведения неизвестен, но приведенные значения наиболее близки к палитре 6*8*5+16.

 

Попробуй в sicot.lua покликать по битам меняя цвет, а потом кликнув на цвет посмотреть реальное значение приведенного цвета, чтобы узнать, какие именно цвета выводятся сверх палитры.

 

В палитре Тир2 я находил аномальный 17-й цвет, возможно такие цвета есть и в Тир3.

 

ЗЫ

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

Изменено пользователем swg2you
  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Порой необходимо использовать много маленьких картинок, держать их в отдельных файлах не целесообразно из-за того, что загружать через http.get по отдельности довольно долгое занятие. Будет очень удобным возможность собирать пакеты десятков картинок в одном файле, а при запуске программы загружать разом в один массив

  • Like 3

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Порой необходимо использовать много маленьких картинок, держать их в отдельных файлах не целесообразно из-за того, что загружать через http.get по отдельности довольно долгое занятие. Будет очень удобным возможность собирать пакеты десятков картинок в одном файле, а при запуске программы загружать разом в один массив

Интересная идея, мне понравилась. Уже сделал :) Можно хранить до 255 картинок в одном файле(выделить больше 1 байта на кол-во картинок не позволила большая жаба).

Изменено пользователем JaggerDer
  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

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

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

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

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

Войти

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

Войти сейчас

×