Парсер анимации в чанки 4х4

Некоторых заинтересовали быстрые чанки, показанные в деме Lmao на прошедшем недавно демопати DiHalt 2018 Lite. Как многие из этих некоторых догадались, всё дело в неполном обновлении экрана. Действительно, в каждом фрейме обновляются только изменившиеся чанки. Для этого был написан специальный парсер. Давайте более подробно рассмотрим, как он работает.




Подготовка источника

В качестве исходного материала используется анимированный GIF. Размер картинки — 64x48. Количество кадров — тут всё зависит от сложности анимации (количества изменений между кадрами), и Вашей оптимистичности. Главное, количество кадров должно быть четным.

Парсер предназначен для подготовки двухбитных чанков, поэтому количество цветов лучше заранее уменьшить до трех. В случае гигаскрина количество цветов можно варьировать от 3 до 6, и выбирать наиболее интересные варианты. О гигаскрине я еще напишу отдельно ниже.

Парсер

Парсер написан на JavaScript. Работает в браузере, проверялся в последних версиях Firefox. Для запуска просто открыть файл make.html, но сначала нужно произвести настройку.

Сохраняем gif-файл источник в каталог с парсером. Далее, открываем на редактирование make.html и вручную настраиваем переменные конфигурации (да, такой вот олдскул):

srcGIF — Имя gif-файла источника.
destFilename — Имя файла для сохранения, без расширения.
destExtension — Расширение сохраненяемого файла, по-умолчанию asm.
parserType — Тип парсера. Может быть '1screen' — последовательный парсинг кадров, или '2screen' — парсинг кадров «через один», для вывода на два экрана (gigascreen).
useAttrubutes — Использовать область атрибутов для «стирания» чанков. Такой вариант работает несколько быстрее, чем «стандартный» вариант, при котором область экранных атрибутов не модифицируется плеером.
saveSeparateOddEven — Сохранять раздельно четные и нечетные фреймы. Это может пригодиться при нехватке памяти. В этом случае мы получаем два блока данных, которые можно раскидать по страницам памяти.
justColorInfo — Только сбор информации о цветах. Поскольку парсер работает довольно медленно, эта опция может пригодиться чтобы оценить предполагаемый результат, и решить, стоит ли доработать gif-файл, сменить настройки парсера, или приступать к парсингу как есть.

Теперь запускаем make.html и ждем. После окончания парсинга браузер предложит сохранить полученный файл данных. А на экране мы видим примерно такой результат:



Разноцветные квадратики рядом с исходной картинкой — это все найденые цвета. Для каждого цвета выводится общее количество пикселей и процентное соотношение с остальными цветами. Это бывает полезно для оценки дальнейших действий.

Ниже видим количество найденных цветов, количество фреймов, количество байт, которые займут полученные данные.

Далее таблица с информацией по каждому фрейму: внешний вид, блок данных, количество байт, которые фрейм занимает в памяти.

Формат хранения данных

Если Вы планируете писать свой плеер, или оптимизировать стоковый (а он не оптимальный, к гадалке не ходи), то Вам нужна информация о формате генерируемых данных.

Каждый фрейм хранится тремя блоками, соответствующими каждой из 1/3 спектрумовского экрана. Признак окончания блока — сигнатура #ffff. Параллельно это означает, что нижние правые четыре чанка каждого блока не могут быть одновременно максимальной яркости.

Сами данные хранятся двухбайтовыми значениями.

Первый байт — атрибутное смещение внутри 1/3 экрана
Второй байт- четыре чанка в этой атрибутной области, каждому выделено по 2 бита: %44332211 — 44: правый-верхний, 33: левый-верхний, 22: правый-нижний, 11: левый-нижний


Для варианта с использованием атрибутов формат хранения данных такой же, но сигнатурой окончания блока данных является последовательность #xx00 (00 — это полностью черный атрибут. Его мы закрашиваем черным paper-ом, поэтому использование #00 в качестве сигнатуры вполне безопасно).
После блока данных идет еще два блока: блок атрибутов, которые нужно «загасить» в этом фрейме, и блок атрибутов, которые нужно восстановить (т.е. парсер знает, что в этом знакоместе уже отрисован необходимый спрайт, нужно просто сменить его цвет, чтобы отобразить).

Формат «атрибутных» подблоков такой: количество изменяемых байт, дальше список изменяемых адресов (смещения внутри текущей 1/3 экрана).

И только после этого идет переход к следующей 1/3 экрана или окончание данных фрейма.

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

Плеер

Плеер ничего особенного из себя не представляет. Классический c2p (что бы это не значило). В комплекте два варианта плеера: обычный, и «атрибутный». В зависимости от того, какой вариант парсинга вы выбрали (опция useAttrubutes), такой плеер и необходимо использовать.

Поскольку данные у нас 2-битные (4 градации яркости), а чанки классические 4-битные (16 градаций яркости), то появилась возможность менять яркость анимации в целом, просто заменяя «плотные» чанки «разреженными» и наоборот. Всего 32 градации, где 0 — полное гашение экрана, 16 — это примерно средняя яркость, далее начинается «засветка», и при яркости 31 все чанки отображаются с максимальной яркостью.

Дефолтная таблица яркостей содержится в файле chunks.bright.table.asm (спасибо bfox за подбор значений). При желании Вы можете сформировать свою таблицу градаций яркости.

Еще у есть таблица самих чанков chunks.src.table.asm, её тоже можно менять и получать довольно необычные эффекты. А еще можно играться с этой таблицей «на лету», тоже будет забавно. Эта возможность показана во втором эффекте демы Lmao, где используются две таблицы: отдельная для четных и для нечетных кадров:


При инициализации плеера подготавливается четыре 256-байтные таблицы:
  • 16 декранченых процедуры вывода чанков с выходом по ret
  • 16 аналогичных процедур, но выход по jp
  • таблица зеркалирования по вертикали
  • таблица зеркалирования по горизонтали

Ваша программа должна самостоятельно резервировать этот 1к памяти.

Переменные и функции плеера
CUR_ATTR — переменная, в которой хранится цвет, которым отображаются чанки (только «атрибутный» плеер, стандартный плеер не использует область атрибутов). Модифицируя это значение на несколько интов можно получить частичную перекраску, блики на общей картинке. В первом и втором эффектах Lmao была использована эта возможность.

INIT — инициализация плеера. Принимает следующие значения:

; a — начальная яркость / выбор экрана #4000 / #c000
; hl — начало данных анимации
; de — конец данных анимации

Выбор экрана, с которым будет работать плеер осуществляется двумя старшими битами регистра A. Возможны два варианта: %01xxxxxx — использовать экран с адреса #4000, %11xxxxxx — использовать экран с адреса #c000.

Яркость задается пятью младшими битами регистра A.

PLAY — воспроизвести один кадр анимации. Воспроизведение зациклено, после окончания анимации идет переход обратно к первому фрейму.

INC_BRGHT — увеличить яркость. При достижении максимального значение #1f текущая яркость меняться не будет.

DEC_BRGHT — уменьшить яркость. При достижении минимального значение #00 текущая яркость меняться не будет.

FLIP_HORIZ — зеркальный разворот всех кадров анимации по горизонтали. Перед вызовом желательно полностью погасить яркость (DEC_BRGHT). Во время выполнения разворота анимация должна быть остановлена (функция PLAY не вызывается).

FLIP_VERT — зеркальный разворот всех кадров анимации по вертикали. Ограничения такие же, как и при горизонтальном развороте.

Гигаскрин

Гигаскрин парсер поддерживает. Достаточно установить опцию parserType = '2screen'. Сам плеер модифицировать не нужно. Достаточно «щелкать» экранами после каждого вызова PLAY, ну и экран естественно использовать #c000.

Работает всё это не идеально. Можно было бы гораздо красивее парсить в гигаскрин, но у меня пока не получилось. Любителям эксперементов я предлагаю посмотреть функцию makeFullFrame парсера. Там есть несколько альтернативных вариантов обработки данных, можно их поперебирать. Причем, на каких-то исходных анимациях хорошо срабатывают одни настройки, на каких-то другие.

Здесь как раз помогает информация о процентном соотношении цветов в исходной анимации, которую выдает парсер. Основываясь на этих данных можно попробовать перекидывать какие-то цвета с экрана на экран, какие-то отображать на обоих экранах, какие-то игнорировать… В общем, непаханое поле для экспериментов.

Тесты / примеры

В каталоге tests лежат четыре исходника, которые можно использовать и как тесты, и как примеры использования плеера. Собираются они все сразу файлом make.cmd, только нужно подправить пути к ассемблеру и эмулятору. На выходе мы получаем четыре снэпшота:
test-1s.sna — анимация на один экран
test-1s-attr.sna — анимация на один экран с использованием атрибутов
test-2s.sna — анимация на два экрана (гигаскрин)
test-2s-attr.sna — анимация на два экрана (гигаскрин) с использованием атрибутов


Я понимаю, что не смотря на количество текста, это описание все равно остается неполным. Но, я надеюсь, кому это действительно нужно, те разберутся сами, или спросят. За спрос денег не берут, как правило.

Текущую версию парсера, плееры, тесты, в общем всё, о чем тут писал, выложил здесь: https://github.com/akanyuk/chunks-parser-zx

Всем быстрых и красивых чанков, посоны!

16 комментариев

avatar
make chunks great again!
avatar
Спасибо, за статью. Надеюсь чанки опять будут в демах. :)
avatar
Чанки будут опять в демах, обязательно :)
avatar
Сейчас и мегадемы чанковые пойдут в массовом количестве.
avatar
В массовом ли? Публикация отдельных основных механизмов запилятора не вызвала массового засилья анимаций, как мне кажется.
Чтобы добиться массовости, нужно городить готовый конструктор, собирающий готовый результат с музыкой и бегущей строкой, как это было с запилятором ранее.
avatar
Не, дело не в этом. Просто некоторые деятели не отличают тушёное мясо от тушенки.
Очень легко завалить ААА.КУЧАДЕМ.КОМ кучей плейеров чанковой анимации. Очень сложно добиться при этом чтобы тебя, даже не услышали, просто послушали…
avatar
В целом по статье: отличная и правильная идея, скорость работы в LMAO была шокирующей.
А что происходит, если изменений в кадре больше, чем можно уложиться во фрейм?
avatar
Ну тут уже от автора всё зависит. Можно заняться оптимизацией. Я раза четыре полностью плеер переписал, пока не решил, что лучше уже не смогу. Очень кстати хорошо экспириенс поднял на этом.

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

Ну или скатываться на 25фпс. Иногда это не так уж и плохо.
avatar
«или скатываться на 25фпс» — очень прикольная лично для меня смена оптики. У меня есть куча каких-то набросков, про которые я привычно думаю так: «смириться с 4 фреймами» или «влезть в 3 фрейма».

«Влезть в 25фпс» это как мегабонус часто!
avatar
Ну я к тому и написал «Иногда это не так уж и плохо», что некоторые чанки от замедления только выигрывают. Правда, тут уже внутренний интроспек просыпается и начинает бурчать «чому такты так не плотно используешь» :-)
avatar
Нет уж, вот этого не надо. «скатываться на 25фпс» — это внутренний Titus! Или, как минимум, внутренний AloneCoder :)
avatar
да нет же! я сейчас не про 25фпс, а про то, что процессор начинает простаивать, если хальты вставлять
avatar
если нет стекования (в c2p не смотрел), то просто свалится в 25фпс, иначе… ну вы поняли :)
avatar
стекование пробовал в одном из вариантов. лукап по таблице функций быстрее получился. в общем, нету там стекования
avatar
tip of the day: инвитка на дихалт зима 19 стала бы быстрее, если бы вместо скролла я использовал этот парсер с подготовленной анимацией
avatar
Ты это к чему?
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.