Xytabich 257 Опубликовано: 19 мая, 2020 (изменено) Сделал простенькую читалку для файлов GIF, поддерживает весь функционал со спецификации: слои/кадры, прозрачность, текст, комментарии, расширение NETSCAPE2.0 и прочее. Но не все доп. расширения были найдены, потому реализовал только описанные в интернете. Небольшая предыстория: В общем говоря, захотел я себе как-то анимированный "телек" для дома, в качестве украшения. С отображением были конечно проблемы - голо-проектор не вмещал больше 3х цветов в палитре, но это решалось количеством. Второй проблемой встала сама картинка - пнгшки кадрами собирать было лень, да ещё и в индексы надо переводить все цвета... Подождите-ка индексы, анимация, встроенная палитра... Это-же GIF! Эврика подумал я... Пока не увидел реализацию, а потом меня поглотила пучина гифа... При первом знакомстве GIF показался простым, как пробка, форматом (хотя, так и есть). Но сжатие изображения в LZW немного остудило мой пыл. Мне довольно трудно понимать работу хитроскрученных алгоритмов сжатия, но всё оказалось не так плачевно, и вооружившись методом проб и ошибок, я начал познавать это.. что-то. В конце концов, подняв несколько хабро-статеек, спецификацию, форумы покрывшиеся пылью, я начал открывать другую сторону GIF - она идеально подходит под реалии OpenComputers: 256 цветов, неплохое сжатие, встроенный текст. Всё это приправлено расширениями в виде анимаций, прозрачности слоёв, и разными дополнительными блоками. В конце-концов, этот формат может стать аналогом флеша в истории опенкомпов! Да здравствует новая эра, эра флеш-игр, когда-то покорившая интернет! В итоге всех скитаний по интернетам, мне удалось сделать более-менее полную читалку, хотя некоторые расширения потерялись в могилах истории, и я не решился пока их выкапывать. Саму читалку можно скачать для двух версий Lua: 5.2: wget -f https://github.com/Xytabich/GIF-Lua/raw/master/5.2/gif.lua /lib/gif.lua 5.3: wget -f https://github.com/Xytabich/GIF-Lua/raw/master/5.3/gif.lua /lib/gif.lua Различия между ними несущественны - Lua 5.3 использует встроенные битовые операции, а в Lua 5.2 используется библиотека bit32, соответственно. Функционал читалки: -- Сруктуры -- Заголовок GIF - базовая информация гифки. - width:number - ширина гифки - height:number - высота гифки - colorBits:number - кол-во бит, используемое для цветовой палитры. Устарело, но добавил на всякий. - bgIndex:number - индекс фонового цвета - aspectRatio:number - соотношение сторон, считается как: ширина/высота. Тоже устарело, но мало-ли. - colorsCount:number? - количество цветов в глобальной палитре - colors:number[]? - глобальная палитра цветов 0xRRGGBB, индексация начинается с 0 - extensions:table - список расширений гифки Блок изображения (image) - информация о слое/кадре гифки. Содерижит полное изображение, или только его часть. - x:number - отступ от левого края - y:number - отступ от верхнего края - width:number - ширина изображения - height:number - высота изображения - interlaced:bool - построчная или чересстрочная развертка - colorsCount:number? - количество цветов в локальной палитре - colors:number[]? - локальная палитра цветов 0xRRGGBB, индексация начинается с 0 - pixels:string - пиксели изображения, массив байт 0-255. Пиксель является индексом в локальной/глобальной палитре, и записаны в виде линий, слева-направо сверху-вниз. Порядок линий зависит от interlaced. - extension:table? - расширение изображения, параметры анимации и прозрачности - dispMethod:number - метод очистки экрана перед отображением: 1 - не очищается, 2 - закрасить фоновым цветом, 3 - восстановить состояние перед предыдущим кадром - delay:number - время до следующего кадра, в секундах - inputFlag:bool - ожидать ли ввода пользователя для продолжения отображения - transparentIndex:number - индекс цвета в палитре, использующегося в качестве прозрачного Блок текста (text) - отображает текст над изображением. По спецификации символы 7-bit ASCII, но ничто не мешает засунуть туда utf-8. - x:number - отступ от левого края - y:number - отступ от верхнего края - width:number - ширина текстовой области - height:number - высота текстовой области - charWidth:number - ширина символа - charHeight:number - высота символа - fgIndex:number - цвет символа - bgIndex:number - цвет фона символа - text:string - собственно, сам текст Блок комментария (comment) - произвольная текстовая запись, не влияющая на отображение. По спецификации так-же символы 7-bit ASCII, но и юникод спокойно помещается. Расширение гифки: NETSCAPE2.0 - информация о количестве циклов анимации. - iterations:number - количество итераций цикла (0 - бесконечное) - loop:bool - бесконечный ли цикл -- Функции -- gif.read(stream[, pos=0]):table -- читает весь файл. -- stream - поток данных, например - полученный через filesystem.open(...) -- возвращает заголовок с дополнительным полем - blocks:table {type:string, block:object}. В этом поле содержится список всех блоков в порядке чтения. gif.images(stream[, pos=0]):head, image -- итератор, последовательно считывает изображения. -- stream - поток данных, например - полученный через filesystem.open(...) gif.blocks(stream[, pos=0]):head, type, block -- итератор, последовательно считывает все блоки. -- stream - поток данных, например - полученный через filesystem.open(...) Особо сильно оптимизировать не стал, да и не знаю как, так что если есть предложения - выслушаю и внесу поправки. Один из вариантов ускорения чтения - сделать буферизированный поток данных. Код и тестовые изображения доступны в репозитории на github. В чересстрочной развёртке линии следуют не по прямому порядку, подробнее в выписке из спецификации: Скрытый текст Appendix E. Interlaced Images. The rows of an Interlaced images are arranged in the following order: Group 1 : Every 8th. row, starting with row 0. (Pass 1) Group 2 : Every 8th. row, starting with row 4. (Pass 2) Group 3 : Every 4th. row, starting with row 2. (Pass 3) Group 4 : Every 2nd. row, starting with row 1. (Pass 4) The Following example illustrates how the rows of an interlaced image are ordered. Row Number Interlace Pass 0 ----------------------------------------- 1 1 ----------------------------------------- 4 2 ----------------------------------------- 3 3 ----------------------------------------- 4 4 ----------------------------------------- 2 5 ----------------------------------------- 4 6 ----------------------------------------- 3 7 ----------------------------------------- 4 8 ----------------------------------------- 1 9 ----------------------------------------- 4 10 ----------------------------------------- 3 11 ----------------------------------------- 4 12 ----------------------------------------- 2 13 ----------------------------------------- 4 14 ----------------------------------------- 3 15 ----------------------------------------- 4 16 ----------------------------------------- 1 17 ----------------------------------------- 4 18 ----------------------------------------- 3 19 ----------------------------------------- 4 Пример приведу на тестовом коде, для проектора на картинке выше. Этот код просто рисует файл, так что настроить пересечение областей проекторов нужно вручную, или использовать один. Скрытый текст local file = ... if not file then print("Usage: program filename") end local fs = require("filesystem") if not fs.exists(file) then print("File does not exist") end local com = require("component") local computer = require("computer") local gif = require("gif") local holo = {} for k,v in com.list("hologram") do table.insert(holo, com.proxy(k)) holo[#holo].clear() end local function getColorIndex(c, i) -- возвращает индекс цвета для конкретной голограммы if c >= (i-1)*3 and c < i*3 then return c - (i-1)*3 + 1 else return 0 end end local function buildFrame(fx, fy, w, h, str) -- рисует кадр на голограммах local c for x=1,w do for y=1,h do c = str:byte((y-1)*w + x) for i=1,#holo do holo[i].set(fx+x-1, 34-(fy+y), 1, getColorIndex(c, i)) end end end end local loadPalette, time = true local f = fs.open(file) for info,img in gif.images(f) do -- последовательное чтение блоков с изображением if loadPalette then -- установка палитры голограмм, тут используется глобальная палитра, потому устанавливается только один раз local ind for i,c in pairs(info.colors) do ind = math.floor(i/3)+1 if holo[ind] then holo[ind].setPaletteColor(i%3+1, c) end end loadPalette = false end time = computer.uptime() buildFrame(img.x+1, img.y+1, img.width, img.height, img.pixels) os.sleep(img.extension and math.max(0, img.extension.delay - (computer.uptime()-time)) or 0) -- задержка до следующего кадра end f:close() Для OpenComputers этот формат имеет хорошие возможности, как для простого хранения сжатого изображения, или даже составного изображения из различных слоёв, так и для продвинутых действий: презентаций, анимаций, картинок с подписями. А если совсем заморочиться, то можно сделать свой Anone Gif Player, используя комментарии для записи кода, или собственные расширения приложения. По крайней мере, кинетические новелки возможно сделать и в стоковом варианте. История изменений: Скрытый текст Версия 1.02: - Исправлено побитовое декодирование Версия 1.01: - Оптимизация кода - Убрано использование глобальных ссылок Версия 1.0: - Создана читалка Изменено 24 августа, 2022 пользователем Xytabich 11 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Asior 586 Опубликовано: 20 мая, 2020 Прикольно очень, а можно образец для рисования на экране, я попробовал вывести и как-то не особо получилось (наверное мало думал). Для тех кто будет тестировать, прописывайте полный путь до файла типа: testProgram.lua /home/plaintext.gif И еще такой вопрос, скорее всего в программе не указано подключение filesystem. Хотя я в эмуляторе тестировал и может в реальных компах там не требуется ... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Totoro 3 563 Опубликовано: 20 мая, 2020 Охренительно. А идею с монитором из голографических проекторов совсем недавно обсуждали. Прикольно увидеть это вживую. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Xytabich Автор темы 257 Опубликовано: 20 мая, 2020 (изменено) @Asior в тесте есть подключение: В 19.05.2020 в 22:34, Xytabich сказал: local fs = require("filesystem") if not fs.exists(file) then print("File does not exist") end А сама либа уже использует файловый поток. Лёгких библиотек по рисованию цветом не знаю, и не делал примеров с экраном, потому вот псевдокод: local canvas, x, y = ... -- холст и позиция изображения local file = filesystem.open("/image.gif", "r") local gifFile = gif.read(file) canvas.fill(x, y, gifFile.width, gifFile.height, gifFile.colors[gifFile.bgIndex]) for _,block in pairs(gifFile.blocks) do if block.type == "image" then block = block.block for py=1, block.height do for px=1, block.width do canvas.setPixel(x+block.x+px, y+block.y+py, gifFile.colors[block.pixels:byte((py-1)*block.width + px)]) end end end end Пиксель - это индекс цвета в палитре. Записаны они в виде линий, сверху-вниз, друг за другом, надо думаю дописать об этом. Изменено 25 мая, 2020 пользователем Xytabich 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Xytabich Автор темы 257 Опубликовано: 20 мая, 2020 (изменено) @Totoro если оптимизировать запись пикселей в голограмму, можно целые фильмы показывать) Изменено 20 мая, 2020 пользователем Xytabich Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
hohserg 197 Опубликовано: 21 мая, 2020 23 часа назад, Totoro сказал: идею с монитором из голографических проекторов Сколько минимум проекторов нужно, чтобы разрешения хватало для вывода одного любого символа, что можно вывести на обычный монитор? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
eu_tomat 2 154 Опубликовано: 21 мая, 2020 27 минут назад, hohserg сказал: Сколько минимум проекторов нужно, чтобы разрешения хватало для вывода одного любого символа, что можно вывести на обычный монитор? Вернее будет говорить не о количестве проекторов на один символ, а о количестве символов, отображаемых одним проектором. Размер обычного символа на мониторе OC составляет 8x16, а проектор обеспечивает разрешение до 48x48. Получается, что один проектор может вывести поле символов размером 6x3. А для символов двойной ширины (встречаются и такие) поле будет иметь размер 3x3. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
hohserg 197 Опубликовано: 22 мая, 2020 (изменено) 23 часа назад, eu_tomat сказал: Вернее будет говорить не о количестве проекторов на один символ, а о количестве символов, отображаемых одним проектором. Если один проектор позволяет отобразить 18 символов, то нужно 1/18 проекторов для одного символа. Конечно, поделить проектор нельзя, но такое представление удобно, чтобы подсчитать, столько проекторов дадут разрешение эквивалентное монитору. На мониторе т3 8000 символов(160*50), без учета символов двойной ширины нужно 445 проекторов Изменено 22 мая, 2020 пользователем hohserg Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
eu_tomat 2 154 Опубликовано: 22 мая, 2020 8 минут назад, hohserg сказал: Если один проектор позволяет отобразить 18 символов, то нужно 1/18 проекторов для одного символа. Конечно, поделить проектор нельзя, но такое представление удобно, чтобы подсчитать, столько проекторов дадут разрешение эквивалентное монитору. На мониторе т3 8000 символов(160*50), без учета символов двойной ширины нужно 445 проекторов Да, удобно. Считать можно и так, если к результатам расчётов добавлять слово "приблизительно". Для отображения поля символов размером 160x50 требуется приблизительно 445 проекторов, а точнее — 459. 1 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
hohserg 197 Опубликовано: 22 мая, 2020 Ок, такой метод подсчета действительно ошибочный, потому что монитор прямоугольник, а не линия. С учетом пикселей символов разрешение монитора т3 - 1280x800, поэтому нужно 27 проекторов по горизонтали и 17 по вертикали, что составляет 459. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Xytabich Автор темы 257 Опубликовано: 27 мая, 2020 Версия 1.01: - Оптимизация кода - Убрано использование глобальных ссылок 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
ProgramCrafter 544 Опубликовано: 24 августа, 2022 В 20.05.2020 в 00:34, Xytabich сказал: Lua 5.3 использует встроенные битовые операции Не везде. Вижу часто 2^count. Ещё есть баг, что для больших картинок нижняя часть съезжает - некорректно декодируется. Послал PR с фиксом. Протестить можно на прикреплённой картинке. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Xytabich Автор темы 257 Опубликовано: 24 августа, 2022 @ProgramCrafter в основном были заменены функции на операции, а работающие трюки не были затронуты. За фикс спасибо, поправил. 3 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах