КОМПРЕССОРЫ Часть 1: Дельта-компрессор анимации
Существует множество способов сжатия содержимого экрана, многие из них используются в различных компрессорах, призванных уменьшить титульные экраны игр до приемлемых размеров. Один из таких компрессоров постоянным читателям SCENE+ уже знаком — это Screen Compressor+D, выходивший в одном из прошлых номеров. Его плюсом было то, что он пробовал 4 разных метода и затем выбирал наиболее выгодный. Минусом было то, что работал он сравнительно медленно. С другой стороны, компрессор, представленный в YS, был способен плавно анимировать 1/3 экрана и использовался мной для вращающегося логотипа SCENE+.
Но как добиться плавной анимации на весь экран? Насколько мне известно, до меня никто не реализовывал Delta Compressor для Спектрума, а ведь именно с его помощью можно плавно анимировать полноэкранное изображение, если соблюдены некоторые условия.
Delta Compressor, который я для простоты в дальнейшем буду называть «Дельта», применялся в конце 80-х годов, чтобы заставить ультрасовременные тогда компьютеры вроде Amiga или Atari ST воспроизводить плавную анимацию. Одной из программ того времени у меня была Videoscape 3D — самая недружелюбная к пользователю программа на свете! Ее единственным плюсом было то, что она использовала Дельту для проигрывания очень длинных анимаций.
Что дозволено Амиге, то должно быть по плечу и Спектруму, подумал я и стал размышлять, как перенести эту процедуру на Speccy. Ассемблер 68000 я не знаю, я же не мазохист! Поэтому я стал собирать информацию из «Happy Computer» и других журналов о том, как работает этот компрессор. И ведь действительно: гениальная штука для тех времен, когда слово MPEG еще никому ни о чем не говорило.
Дельта работает очень просто: сохраняются только изменения относительно предыдущего кадра. Это означает, что не нужен ни байт-флаг, отсутствующий в данных экрана, ни большое количество процессорного времени, ведь на экран выводятся только изменившиеся байты. При небольших изменениях картинки можно достичь частоты 50 FPS и скромного размера файла анимации. При значительных изменениях у меня получался размер около 3-4 Кб на кадр и совершенно неприемлемый фреймрейт, но при мелких изменениях все шло как по маслу.
Имея в общей сложности 640 Кб памяти на моем MB02 (512 Кб) и Спектруме (128 Кб), я теоретически мог бы делать потрясающие, длинные и плавные анимации, даже в интерлейсе. Для сжатия я использовал небольшую BASIC-программу, в то время как декомпрессия благодаря крошечной MC-процедуре происходит очень быстро.
Вопрос, который передо мной встал: как сохранить изменения без необходимости городить буфер на 12 Кб, в котором перед каждым байтом хранилась бы информация о том, изменился он или нет? Довольно глупая идея, правда? Из кода 68000 я ничего понять не мог, поэтому начал всерьез ломать голову сам и в итоге пришел к окончательному решению:
Поскольку неизмененные байты всегда чередуются с измененными, достаточно использовать байт-маркер, чтобы сообщить компьютеру, с каких байтов начинается последовательность — с измененных или неизмененных. Затем идут цепочки по 2 байта:
- Байты остаются без изменений: (2-байтовая последовательность задает количество неизмененных байтов, то есть теоретически до 65535, поэтому метод сработает и на SAM Coupé. Счетчик DEstination просто увеличивается на это число байтов).
- Байты изменяются: (2-байтовая последовательность — теоретически хватило бы и 1 байта — задает количество последующих байтов, которые берутся без изменений, просто копируясь через
LDIR).
Мало того, что такой способ работает невероятно быстро, он еще и гениально прост — даже я сам удивился полученному решению! Неужели все правда может быть настолько просто? Да! Мой депакер, конечно, не предел совершенства, но благодаря ему я снова смог узнать что-то новое о машинном коде, и понял следующее... Скорость огромна, но и шанс зависнуть не меньше!
С тех пор я еще больше улучшил и оптимизировал программу, чтобы рано или поздно задействовать ее в своих игровых проектах. После этого описания любой кодер уже должен понять, как устроена Дельта, раз уж даже австриец смог в этом разобраться. Нет, ничего против наших краев не имею, но единственным моим серьезным конкурентом здесь был Spectec — где бы он сейчас ни находился.
По поводу Дельты могу добавить, что ей требуется так называемый ключевой кадр (Keyframe), который в идеале нужно сжать другим экранным компрессором. Если же различия между отдельными кадрами довольно велики, можно дополнительно использовать Shrinker-компрессор, а всю готовую анимацию затем «схлопнуть» (Implode).
BASIC-часть компрессора я затем доработал таким образом, чтобы одиночный неизмененный байт, оказавшийся среди нескольких изменившихся, тоже сохранялся как измененный. Благодаря этому декомпрессия не растягивается понапрасну.
Tiger's Claw.
Это была первая часть цикла о компрессорах. Если кто-то захочет продолжить его — без проблем! Пишите мне! В следующей части речь пойдет об обычном алгоритме сжатия (Shrinker). Буду также рад увидеть какие-нибудь ваши примеры анимаций.
Ассемблерная процедура декомпрессора Delta:
LD HL,Comprscradr ; Адрес сжатого экрана
LD DE,16384 ; Адрес экрана
LD A,(HL) ; Первый байт в A
INC HL ; Увеличиваем указатель адреса
CP 1 ; Сравниваем A с 1
JR Z,vers ; Если да, то измененные байты! Переход к vers
Start: LD C,(HL) ; Загружаем BC содержимым (HL)
INC HL
LD B,(HL)
INC HL
EX DE,HL ; Меняем DE и HL местами, так как
ADD HL,BC ; сложение возможно только с HL
EX DE,HL ; Возвращаем значения обратно
vers: LD C,(HL) ; Количество изменившихся байтов
INC HL ; загружаем в BC
LD B,(HL)
INC HL
LDIR ; Копируем все изменившиеся байты
LD A,D ; Старший байт DEstination
CP 88 ; сравниваем с 88 (22528)
JR Z,Ende ; Если значение достигнуто, выход
JR Start ; Иначе возвращаемся в начало
Ende: LD BC,HL ; Для LET X=USR adr, X=следующий свободный байт
RET ; Возврат в BASIC
Программа может работать и при включенных прерываниях (например, с музыкой) и компилироваться под любой адрес. Надеюсь, мои комментарии окажутся полезными! Мне пришлось переписывать программу с черновика, на котором было полно правок, так что надеюсь, что все без ошибок.
По поводу сопутствующей BASIC-программы могу дать такие советы:
- Экран после сжатия нужно сохранять где-то в памяти в несжатом виде, чтобы иметь опорный кадр для следующего изображения.
- Программу обязательно нужно скомпилировать, иначе придется очень долго ждать.
Впрочем, написать BASIC-программу каждый из вас должен суметь и сам. Если возникнут какие-либо проблемы или появятся предложения по улучшению от профи ассемблера — пишите в редакцию ASC.
Поделитесь вашим мнением о статье