Рисуем код из «Матрицы» на PHP

в 11:17, , рубрики: php, матрица, фон

Однажды мне пришла в голову идея сделать динамически создаваемый фон для блога в виде пресловутого кода из фильма «Матрицы». После убийства вечера и половины ночи я-таки достиг желаемого результата, и решил поделиться им с народом. К сожалению, я не нашёл подобной реализации, а иметь динамически создаваемую «матрицу» как фон бложика таки хочется.
Итак, пишем генератор кода «Матрицы» на PHP с использованием библиотеки gd.

Итак, поставлены следующие требования к генерируемой картинке:
1. Столбики кода не должны быть равны по длине, длина должна выбираться случайно
2. Яркость цвета должна нарастать сверху вниз
3. Расположение столбиков должно быть случайным, но они не должны налезать друг на друга
4. Полученная картинка не должна кэшироваться браузерами, дабы при каждом обновлении получался новый код
5. Код не должен улетать за пределы картинки.

Приступим, собственно, к генерации картинки.

Для начала надо придумать, что будет выступать в роли элементов кода. В «Матрице» использовались как цифры, так и японские иероглифы. Последние выглядят более эффектно, следовательно, их и возьмём.
Создадим функцию getJapanSym(), возвращающую HTML-Entity код (его использует функция imagettftext(), но о ней позже).
В Unicode японские иероглифы (катакана и хирагана, кандзи не берём, ибо не использовались в фильме) расположены в диапазоне кодов от 0x3040 до 0x30FF. Из этого диапазона и требуется брать случайный код иероглифа. В результате получаем вот такую функцию:

function getJapanSym()
{
	$rnd = rand(hexdec("3040"), hexdec("30FF")); // то-ли лыжи не едут, то-ли у меня кривой сервер, но с числами в формате 0xXXXX он работать отказался
	return "&#x".dechex($rnd).";"; // формат HTML-Entity, нечто вроде ア
}

Далее нам требуется отрисовать собственно картинку. Давайте по порядку.
Первым делом нам нужно нарисовать столбик иероглифов с заданным количеством знаков. Для отрисовки будем использовать упомянутую ранее функцию imagettftext() из библиотеки gd. Более того, цвет каждого иероглифа должен отличаться от предыдущего, так что простым n тут не обойтись, придётся писать целый цикл.
Японские иероглифы можно найти в шрифте Arial Unicode MS, который мы, собственно, и используем.
Функция для рисования иероглифов в столбик выглядит так:

for ($i = 0; $i < $symCount; $i++) // переменная $symCount отвечает за количество знаков в столбике
	{
		imagettftext(
				$img, // ресурс картинки
				10, // размер шрифта
				0, // угол наклона, нам он не нужен, так что 0
				$codePlacement, // ось X, она же начальная координата столбика
				$symPadding, // ось Y, она должна меняться для каждого иероглифа
				hexdec("00".dechex($printCol)."00"), // цвет, о нём мы поговорим позже
				"./arial.ttf", // указание файла шрифта. Маны PHP рекомендуют абсолютный путь
				getJapanSym() // собственно, элемент, иероглиф, записанный в формате HTML-Entity
			    );

		$symPadding += 15; // прибавляем к значению оси Y для следующего иероглифа. Значение подбирается экспериментально, в зависимости от размера шрифта и самих иероглифов
	}

Для удобочитаемости и комментирования каждый параметр вынесен на отдельную строку.

Описанный выше код нарисует просто столбик иероглифов. Но нам нужно его ещё и раскрасить, причём первый иероглиф должен быть еле виден на чёрном фоне, а последний, соответственно, должен быть самым ярким. Для этого введена переменная $printCol, отвечающая за зелёный компонент в RGB-представлении.
Допишем перед циклом несколько переменных:

	$colorIncrement = round(254 / $symCount);
	$printCol = 16;

Первая переменная — инкремент цвета — рассчитывается как отношение всех возможных цветов к количеству иероглифов в столбце. Этот инкремент будет плюсоваться после отрисовки каждого иероглифа.
Вторая переменная, $printCol — собственно, сам цвет. Начальным цветом выбран 16, и не случайно. 16 в шестнадцатеричной системе счисления равно 10, то бишь два символа, и это нужно, чтобы не «сломать» вид кода цвета «00XX00», ровно 6 символов в шестнадцатеричной системе.
Теперь используем наше нововведение в основном цикле:

$colorIterate = round(254 / $symCount);
$printCol = 16;
for ($i = 0; $i < $symCount; $i++)
	{
		imagettftext($img, 10, 0, $codePlacement, $symPadding, hexdec("00".dechex($printCol)."00"), "./arial.ttf", getJapanSym());
		$symPadding += 15;
		$printCol += $colorIncrement; // прибавляем к цвету коэффициент пропорциональности для следующего иероглифа
	}

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

hexdec("00".dechex($printCol)."00")

. Возможно, это выглядит глупо, но в php инкремент выполняется в десятичной системе счисления, а нам нужно число в шестнадцатеричной, причём, как сказано ранее, меняем только зелёный цвет.

Итак, теперь нам нужно нарисовать несколько таких столбиков, чтобы получился полноценный «матричный» код. Думаю, здесь потребуется меньше разъяснений, и хватит кода с комментариями.

$img = imagecreatetruecolor (500, 500); // создание картинки размером 500x500. Фон у неё по умолчанию чёрный.

$position = 4; // начальная позиция первого столбика с иероглифами
for ($rows = 0; $rows < 50; $rows++) // рисуем 49 столбиков с кодом (число подбирается также экспериментально, сколько влезет на картинку)
{
	$symPadding = rand(3, 450); // выбираем положение по координате Y для самого первого иероглифа. Не меньше трёх и не больше 450, иначе нет смысла рисовать
	$symCount = round((rand($symPadding + 100, 500) - $symPadding) / 17); // рассчитываем, сколько надо нарисовать иероглифов, чтобы не залезть за пределы картинки и при этом выбрать случайное кол-во
	$colorIncrement = round(254 / $symCount); // инкремент цвета, был описан ранее
	$printCol = 16; // первоначальный цвет, также описан ранее
	$codePlacement = rand($position - 4, $position + 1); // размещение столбика с иероглифами по оси X, всегда постоянно, но выбирается в промежутке, причём таком, чтобы столбики не наползали друг на друга

	for ($i = 0; $i < $symCount; $i++) // рисуем столбик. Это описано ранее.
	{
		imagettftext($img, 10, 0, $codePlacement, $symPadding, hexdec("00".dechex($printCol)."00"), "./arial.ttf", getJapanSym());
		$symPadding += 15;
		$printCol += $colorIncrement;
	}

	$position += 20; // прибавляем 20 пикселов к возможному расположению следующего столбика

}

Итак, мы уже имеем готовый код «Матрицы» в переменной $img, осталось только его вывести. Но здесь маленькая хитрость: браузеры любят кэшировать фон. Следовательно, чтобы юзер каждый раз видел новый фон, требуется запретить кэширование при помощи заголовков. Делаем это вот так:

header("Cache-Control: no-store"); // попытка запрета номер "раз"
header("Expires: " . date("r")); // попытка запрета номер два
header("Content-Type: image/png"); // сообщаем, что сейчас будет картинка, а не текст
imagepng ($img); // выводим картинку
imagedestroy($img); // уничтожаем картинку и удаляем её из памяти.

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

К сожалению, браузер Firefox не умеет правильно отображать фон после обновления, и получается слияние двух версий, остальные же браузеры подобным не страдают.

Автор:

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js