Portable Network Javascript

в 17:53, , рубрики: PNG, Веб-разработка, компрессия, ненормальное программирование, обработка изображений, метки: ,

Приукрасим забытое старое

Для начала — небольшая картинка в качестве эпиграфа. Продолжение — под катом.

image

На самом деле, сразу после неё следует строка

<script type="text/javascript" src="pnj.js">

Да, это — не опечатка, я действительно имею в виду PNJ, а не PNG. Расшифровку сего извращенного термина см. в названии статьи. При следующих упоминаниях этого термина в данной статье имеется в виду обычное PNG-изображение.

Но зачем?

Сжимание кода в PNG — это типичный пример стенографии. Этот прием — давно не новость и иногда используется, например, в разных соревнованиях, где каждый байт — на вес золота.
Кроме того, для среднестатистических скриптов размером больше 100 kB статистически наблюдается неплохое уменьшение файла.

image

Как это работает?

Я, грубо говоря, изобрёл велосипед:

  • Из JS-кода генерируется PNG-изображение, данные которого заполняются соответствующими байтами из кода по очереди, т. е. в 1 пиксель зашивается 3 байта кода.
  • Изображение подгружается браузером
  • Специальная JS-библиотека считывает код из изображения на клиенте и выполняет его. Выполняется этот процесс с помощью рисования изображения на HTML5 canvas и считывания значений байтов, которые возвращает метод getImageData из canvas.getContext('2d').

Как видно, мы возлагаем все на плечи браузера и libpng (или что там у вас), которые и делают всё грязное дело, имплементируя таким способом кроссбраузерный механизм декомпрессии данных.

Кроме того, я заметил, что лучше всего сжимаются изображения ширины и высоты n/3 и 1 пикселей соответственно, поскольку, как я понимаю со своими недалёкими знаниями механизма компрессии PNG, пропадает необходимость сохранения данных о строке пикселя. Например, изображение в форме квадрата со стороной sqrt(n), хоть и выглядит более красиво, но весит почти также, как и JS-код.
И всё-таки, так выглядит jQuery v1.10.2 (minified) в виде квадратной стенограммы:

image

Интеграция в реальный продукт

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

Пример страницы

<html>
    <head>
        <title>PNJ Test</title>
        <script type="image/javascript" src="jquery.png"></script>
        <script type="text/javascript" src="pnj.min.js"></script>
        <script type="text/javascript">
            pnj.ready(function() {
                $('.foo').html('Hello world!');
            });
        </script>
    </head>
    <body>
        <div class="foo">
            Loading...
        </div>
    </body>
</html>

Увы, обойтись без метода pnj.ready(...) не получилось, поскольку простого способа контроллировать ивенты а-ля *DOMReady я не придумал, а он может сработать до подгрузки всех PNJ-скриптов. Поэтому inline-код страницы приходится заворачивать в такой callback.

Замечания

  • Я не эксперементировал с другими форматами и другими типами палитры PNG
  • Код тестировался на работоспособность только в Firefox 26.0 и Chromium 31.0.1650.63
  • Для mime-типа скрипта, наверное, правильнее было бы использовать image/png или image/pnj, но хотелось заинтриговать читателя первой картинкой, поэтому осталось javascript.

Боевая команда, вперёд!

Демо-страница: andrewdunai.com/misc/pnj-demo/
Генератор PNJ-изображений: andrewdunai.com/misc/pnj-demo/generate/

Дев-версия скрипта-декодера: andrewdunai.com/misc/pnj-demo/pnj.js
Минифицированная версия: andrewdunai.com/misc/pnj-demo/pnj.min.js

PNJ-вариант jQuery 1.10.2 (min): andrewdunai.com/misc/pnj-demo/jquery.png

Автор: AndersonDunai

Источник

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


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