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

Источник: habrahabr

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

Итак, пишем генератор кода "Матрицы" на 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); // уничтожаем картинку и удаляем её из памяти.

 В результате получаем вот такое вот изображение:

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

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


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=28481