Поиск
Подписаться
Разное

Валидный XHTML 1.0 Transitional

Валидный RSS

Сжатие JavaScript в PNG изображение Категория: Веб-мастерство
Название: Сжатие JavaScript в PNG изображение
Дата добавления: 22-05-2013
Раз просмотрено: 1405
Комментарии: 0
Рейтинг: · · · · · · · · · · (0 голосов)

Сжатие JavaScript в PNG изображение

В этой статье вы узнаете об интересном способе сжатия JavaScript кода в PNG изображение и чтение его с помощью метода getImageData элемента canvas. К сожалению, к настоящему моменту это работает только в последних версиях Firefox, Opera и WebKit браузеров. Естественно, Gzip сжатие является более эффективным, но этот способ не является его заменой, а всего лишь интересным и необычным способом.

Изображение ниже выглядит как шум, но на самом деле это 124 килобайтная JavaScript библиотека Prototype закодированная в 30 килобайтное 8 битное PNG изображение. В изображение с сжатием без потерь теоритически возможно закодировать любую информацию в виде пикселей, в нашем случае это JavaScript код. А так как многие форматы изображений предлагают некоторую форму сжатия, мы можем использовать это для сжатия кода, и извлечения его.

Библиотека Prototype закодированная в PNG изображение
Библиотека Prototype закодированная в PNG изображение

Вначале, нам нужно определиться с форматом изображений, который мы будем использовать. Как впрочем, вы догадались из названия, мы будем использовать PNG, однако давайте аргументируем наш выбор. Самое главное, наш формат должен иметь сжатие без потерь, а это сразу сужает наш выбор. Самым первым отпадает JPEG формат, и мы остаемся в выборе между GIF и PNG. Для PNG у нас есть два варианта: 24 битное и 8 битное. Используя 24 битное, мы можем хранить 3 байта данных в 1 пикселе, а в 8 битном только 1 байт. Быстрый тест в Adobe Photoshop покажет, что 8 битное (черно-белое) изображение 300×300 пикселей с монохроматичным шумом будет весить до 5 килобайтов, а изображение 100×100 писклей в 24 битном цвете (цветное) с цветовым шумом будет весить до 24 килобайт. Как видим в этих изображениях поместиться одинаковое количество данных, однако цветное весит почти в 5 раз больше, поэтому остановимся на 8 битном черно-белом изображении. Аналогичное 8 битное GIF изображение, весит немного больше поэтому нашим выбором будет PNG формат.

Теперь нам нужно конвертировать JavaScript файл в цветовые данные и поместить их в PNG файл. Для этого мы будем использовать следующий PHP скрипт, который читает JavaScript файл, создает PNG изображение и задает каждому пикселю значение от 0 до 255 исходя из ASCII кода каждого символа скрипта:

$filename = 'prototype-1.6.0.2.packed.js';

if (file_exists($filename)) {
	$iFileSize = filesize($filename);
	
	$iWidth = ceil(sqrt($iFileSize / 1));
	$iHeight = $iWidth;

	$im = imagecreatetruecolor($iWidth, $iHeight);

	$fs = fopen($filename, 'r');
	$data = fread($fs, $iFileSize);
	fclose($fs);

	$i = 0;

	for($y = 0; $y < $iHeight ; $y++) {
		for($x = 0; $x < $iWidth; $x++) {
			$ord = ord($data[$i]);
			imagesetpixel($im, $x, $y, imagecolorallocate($im, $ord, $ord, $ord));
			$i++;
		}
	}

	header('Content-Type: image/png');
	imagepng($im);
	imagedestroy($im);
}

Однако, тут появляется проблема, которая заключается в том, что PHP создает полноцветное, то есть 24 битное изображение, что не является идеальной оптимизацией, а в PHP нет средств конвертирования изображения в 8 битное. Решением будет прогнать полученное изображение в Adobe Photoshop и сохранить его в 24 битном варианте для идеальной оптимизации. Довольно хорошие результаты получаются даже при кодировании сжатых (минифицированных) скриптов, экономия получается порядка 50%.

Теперь, когда JavaScrip код сжат в PNG изображение, нам нужно декодировать его на стороне клиента. Используя элемент canvas, мы просто рисуем изображение с помощью метода drawImage()и читаем каждый пиксель с помощью метода getImageData. Полученные данные являются большим массивом значений, которые мы декодируем и исполняем функцией eval() с помощью этой JavaScript функции:

function loadPNGData(strFilename, fncCallback) {
	var bCanvas = false;
	var oCanvas = document.createElement('canvas');
	if (oCanvas.getContext) {
		var oCtx = oCanvas.getContext('2d');
		if (oCtx.getImageData) {
			bCanvas = true;
		}
	}
	if (bCanvas) {
		var oImg = new Image();
		oImg.style.position = 'absolute';
		oImg.style.left = '-10000px';
		document.body.appendChild(oImg);
		oImg.onload = function() {
			var iWidth = this.offsetWidth;
			var iHeight = this.offsetHeight;
			oCanvas.width = iWidth;
			oCanvas.height = iHeight;
			oCanvas.style.width = iWidth + 'px';
			oCanvas.style.height = iHeight + 'px';
			var oText = document.getElementById('output');
			oCtx.drawImage(this, 0, 0);
			var oData = oCtx.getImageData(0, 0, iWidth, iHeight).data;
			var a = [];
			var len = oData.length;
			var p = -1;
			for(var i=0; i < len; i += 4) {
				if (oData[i] > 0)
					a[++p] = String.fromCharCode(oData[i]);
			};
			var strData = a.join('');
			if (fncCallback) {
				fncCallback(strData);
			}
			document.body.removeChild(oImg);
		}
		oImg.src = strFilename;
		return true;
	} else {
		return false;
	}
}

Но не стоит использовать кодирование JavaScript в PNG для больших скриптов, так как их декодирование на стороне клиента займет слишком много времени. Например 255 килобайтный скрипт, сжатый в 69 битное PNG изображение будет декодироваться в течении 5-6 секунд, что является неприемлемым. Однако, 16 килобайтное изображение будет декодироваться 500–1000 миллисекунд, что является хорошим результатом. И в заключении, повторюсь, что этот способ не является особенно практичным в применении в реальных проектах, из-за слабой кроссбраузерности и других недостатков, а всего лишь демонстрирует оригинальное применение элемента canvas.

Это интересно: «Красивая поисковая форма».

Понравился материал? Расскажи о нем всем:
Оцените эту статью:
Прокомментируйте:
Ваше имя:

Ваш комментарий будет первым.