JPEG, javascript и браузер
Aug. 18th, 2014 06:23 pmЯ примерно в феврале прорабатывал вариант создания полностью браузерной CMS для простеньких сайтов. То-есть сайт хранится в браузерной IDB и собирается в браузере, а готовые страницы и ресурсы пакуются каждая в gzip и пишутся прямо на Amazon S3. Это облачное хранилище статики, ничего кроме выдачи файлов оно не умеет, но там есть милая опция – привязать свой домен к бакету. В gzip пакуем потому что S3 сам по себе не умеет отдавать ресурсы с компрессией, их надо туда сразу класть пред-компресснутые.
Это дело я тормознул на этапе проектирования – хотя технологически оно оказалось много проще, чем сначала виделось. Проблема, как обычно, выросла на совершенно неожиданном месте.
Дело в том, что с помощью средств браузера не сделать progressive jpeg. Чем он для веба важен – хорошо расписано на Хабре. С учётом особенностей статических сайтов – там обычно много графики – использование progressive jpeg это must.
Вообще, обычный JPEG в браузере можно сделать двумя способами.
Первый – сделать canvas, что-то на нём нарисовать и потом экспортнуть в JPEG с помощью .toDataURL, раскодировав получившийся base64 в binary. Сделать таким способом progressive jpeg решительно никак.
Второй способ – вынуть из канваса raw bitmap data и закодировать jpeg прямо яваскриптом. Увы, оказывается на яваскрипте нет ни одной библиотеки, пишущей прогрессивный jpeg и работающей с приемлемой скоростью. Для обычного jpeg – есть, и весьма шустрая. Для progressive – болт.
Это, вообще-то, для js-комьюнити феноменальный случай – чтобы какой-то довольно низкоуровневой либы, реализующей общепринятый стандарт, просто не существовало.
В этой связи и в связи с тем, что вопрос браузерной CMS вновь замаячил, задаю уважаемым френдам из сопредельных комьюнити вопрос.
Может, вам в работе попадалась какая то очень компактная либа, скажем, на C, которая пишет именно progressive jpeg? Я бы её перекомпилил в js и прикрыл бы эту брешь в js-codebase.
UPD. Есть уже вроде, попробую прикрутить. https://github.com/jrmuizel/cjpeg.js
UPD2. Хороший пример выбора нерелевантных кейвордов при поиске. Набери я сразу libjpeg emscripten, я бы получил ответ по третьей ссылке. А я искал progressive jpeg encoder javascript в разных вариациях.
UPD3. Не, не подошло, увы. Ищем дальше.
Это дело я тормознул на этапе проектирования – хотя технологически оно оказалось много проще, чем сначала виделось. Проблема, как обычно, выросла на совершенно неожиданном месте.
Дело в том, что с помощью средств браузера не сделать progressive jpeg. Чем он для веба важен – хорошо расписано на Хабре. С учётом особенностей статических сайтов – там обычно много графики – использование progressive jpeg это must.
Вообще, обычный JPEG в браузере можно сделать двумя способами.
Первый – сделать canvas, что-то на нём нарисовать и потом экспортнуть в JPEG с помощью .toDataURL, раскодировав получившийся base64 в binary. Сделать таким способом progressive jpeg решительно никак.
Второй способ – вынуть из канваса raw bitmap data и закодировать jpeg прямо яваскриптом. Увы, оказывается на яваскрипте нет ни одной библиотеки, пишущей прогрессивный jpeg и работающей с приемлемой скоростью. Для обычного jpeg – есть, и весьма шустрая. Для progressive – болт.
Это, вообще-то, для js-комьюнити феноменальный случай – чтобы какой-то довольно низкоуровневой либы, реализующей общепринятый стандарт, просто не существовало.
В этой связи и в связи с тем, что вопрос браузерной CMS вновь замаячил, задаю уважаемым френдам из сопредельных комьюнити вопрос.
Может, вам в работе попадалась какая то очень компактная либа, скажем, на C, которая пишет именно progressive jpeg? Я бы её перекомпилил в js и прикрыл бы эту брешь в js-codebase.
UPD. Есть уже вроде, попробую прикрутить. https://github.com/jrmuizel/cjpeg.js
UPD2. Хороший пример выбора нерелевантных кейвордов при поиске. Набери я сразу libjpeg emscripten, я бы получил ответ по третьей ссылке. А я искал progressive jpeg encoder javascript в разных вариациях.
UPD3. Не, не подошло, увы. Ищем дальше.
no subject
Date: 2014-08-18 05:37 pm (UTC)Правда, не факт, что компактные.
Это мегаклассная тула по command line конвертации из всего во всё.
Простая промежуточная идея: подготовь две версии файлов - в хорошем качестве и в плохом, и скачивай их параллельно. Если хорошее ещё не скачалось, а плохое уже скачалось, его можно показать. Вот тебе как бы типа progressive rendering, платим трафиком (но, может, пох?), а latency not affected за счёт параллелизации.
no subject
Date: 2014-08-18 07:56 pm (UTC)no subject
Date: 2014-08-18 08:26 pm (UTC)Имиджмэджик устроен так, что из него весьма затруднительно выковырять отдельные части, а целиком его перекомпилить – это, как помнится, в районе 10 мегов получалось.
Бубен с двумя картинками – это даже в теге img есть такой атрибут lowsrc, я этот вариант пробовал. Выяснилось – к немалому моему удивлению – что все до единого современные браузеры этот атрибут просто игнорируют и он уже лет 5 как deprecated числится. Зато в ИЕ6 работает )
Делать функционал подстановки руками – это очень плохая идея именно в этом случае в силу целого ряда причин, я её отбросил. Вообще, мы так делаем где релевантно – так сделан и двиналэнд, и двинаньюс в некоторых местах, но там это только для маленьких превьюшек в галереях.
Для крупных картинок двух версий мало – сначала появляется лоурес, а потом, очень небыстро, фуллрес. Поэтому нагруженный графикой сайт ощутимо долго выглядит как говно. С прогрессивным джипегом то видно, что чёто грузится, сразу, визуально – а тут нет.
Нарезка бинарного потока – совсем плохая идея, потому что а) мы получаем картинки в формате, больше ни с чем не совместимом б) яваскрипт – однопоточная система, и резать в ней большие бинари стримы на лету очень геморройно. Чаще всего ты даже не сможешь сделать это «на лету».
В общем, тут много ещё есть трюковых таких вариантов – но они все для желаемого эффекта слишком черезжопные. В таких ситуациях я задумки торможу, пока мне вселенная не подкинет варианта получше )
no subject
Date: 2014-08-18 08:42 pm (UTC)no subject
Date: 2014-08-18 08:51 pm (UTC)no subject
Date: 2014-08-18 08:58 pm (UTC)Или я что-то не так понял?
no subject
Date: 2014-08-18 09:03 pm (UTC)Дело в том, что S3 сам не умеет паковать выдачу в канал. Чтобы он отдал, например, html с канальной упаковкой gzip, ему надо прямо gzip туда и положить, и для этого упакованного html указать заголовки Content-type: text/html; Content-encoding: gzip.
no subject
Date: 2014-08-18 09:09 pm (UTC)Эти страницы как раз сгенерёны на клиенте, и заинлайненный jpeg не просто не progressive, он ещё и base64 (+33%). Это совершенно не заметно визуально.
Но это маны, такая техника не подходит для презентационных сайтов.
no subject
Date: 2014-08-18 09:11 pm (UTC)no subject
Date: 2014-08-18 09:11 pm (UTC)no subject
Date: 2014-08-18 09:13 pm (UTC)no subject
Date: 2014-08-18 09:36 pm (UTC)Ну создай back-end сервис, который будет жрать обычный jpeg, выплёвывать progressive, запуская тот же imagemagick. Может, хорошее клиентское решение и появится *когда-нибудь*. Ещё вариант - можно browser plugin написать, но тогда придётся для всех browser'ов и тестировать на разных платформах, и поддерживать, это геморно.
Плюс идеи с back-end сервисов в том, что это же просто performance оптимизация, т.е. это можно делать раз в день. Скажем, javascript просто шлёт серверу список url'ов и забыли про них (ну или метаданные какие-то присоединяет к закаченному jpeg'у, чтобы обозначит, что он ещё не progressive). Ночью batch script на сервере берёт, зачитывает список (или делает crawl по всему и зачитывает метаданные), и начинает конвертацию, атомарно подменяя файлы на s3 с обычных jpeg'ов на progressive. s3 поддерживает работу с метаданными без зачитывания и перезаписывания всего value.
Да, нужны серверные ресурсы, но совершенно в минимальном объёме.
Можно отбивать часть денег за серверные ресурсы, если сделать фрон-энд, дающий ту же операцию произвольным visitor'ам web'а, и рекламу показывать, пока конвертация конвертируется :)
no subject
Date: 2014-08-18 09:45 pm (UTC)no subject
Date: 2014-08-18 09:45 pm (UTC)Перекодирование батчем не вариант, я такое решение рассматривал – какого-то централизованного сервиса в облаке, который ночью ходит и порядок наводит.
Дело в том, что не существует способа взять существенно упакованные джипег, распаковать его и запаковать снова в другой джипег, не ухудшив картинку весьма существенно. То-есть тебе либо надо первую версию выкладывать в разы больше по объёму (менее упакованную), либо рядом класть fullres, что ещё хуже.
no subject
Date: 2014-08-18 09:49 pm (UTC)"Дело в том, что не существует способа..."
Гуглим: lossless conversion to progressive jpeg
Получаем:
http://stackoverflow.com/questions/985725/tools-for-jpeg-optimization
http://en.wikipedia.org/wiki/Jpegtran
"The transformations regarding the representation of the coefficients comprise:
* optimisation of the Huffman coding layer of a JPEG file to increase compression and
* conversion between progressive and sequential JPEG formats as well as
* conversion between Huffman and Arithmetic coding in the entropy coding layer.[2][3][4]
These transformations are each completely lossless and reversible."
no subject
Date: 2014-08-18 09:52 pm (UTC)no subject
Date: 2014-08-18 09:56 pm (UTC)Мне batch обработка видится очень естественным решением, даже если изначально нужно будет закачивать чуть менее сжатый, но более compatible формат.
no subject
Date: 2014-08-18 10:05 pm (UTC)по видимому, генезис в каком-то мелком несоблюдении стандарта при трансляции, что позволяет браузеру всё же воспринять всю картинку, но не позволяет её начинать показывать во время предзагрузки.
no subject
Date: 2014-08-18 10:10 pm (UTC)no subject
Date: 2014-08-18 10:27 pm (UTC)Пошёл почитать новости про jpegtran и арифметическое кодирование – и нашёл libjpeg портированный в js, в 400 Кб всего. https://github.com/jrmuizel/cjpeg.js
no subject
Date: 2014-08-18 10:59 pm (UTC)Мне было бы интересно, если бы ты почаще делился сырыми идеями, которые ещё в процессе раннего cooking. По-моему, ты недоиспользуешь социальные ресурсы своего ЖЖ.
Кроме того, по более простым вопросам легче помогать. Вот я спрашиваю своих читателей, как заработать несколько миллионов для early retirement, и никто не говорит (нычут полезную инфу, сволочи;)). А спрашиваю, скажем, помощь с трудоустройством (т.е. с чем-то более простым и short-term, но тем не менее достаточно трудным) и получаю сумасшедшее количество поддержки и реальной помощи.
no subject
Date: 2014-08-18 11:02 pm (UTC)Но я рад, что в результате, ты обнаружил то, что тебе нужно :)
no subject
Date: 2014-08-18 11:05 pm (UTC)я мог бы раньше догадаться.
no subject
Date: 2014-08-18 11:36 pm (UTC)Брр :) Я прямо почувствовал, как лапы вселенной прошли через меня насквозь и шевелили моим
языкомпальцами, чтобы тебе опосредованно помочь :)"я мог бы раньше догадаться."
Я напишу потом пост на родственную тему (психология рабочих отношений). Уже давно хочу.
no subject
Date: 2014-08-19 02:56 pm (UTC)no subject
Date: 2014-08-19 09:07 pm (UTC)no subject
Date: 2014-08-19 11:11 pm (UTC)no subject
Date: 2014-08-20 11:38 am (UTC)no subject
Date: 2014-08-20 11:56 am (UTC)То-есть это неизбежно попадалово на:
— время на разбор стандарта, он весьма кучерявый и, как водится, написан не для людей
— время на создание самого кода
— время на оптимизацию кода
— время на всестороннее тестирование.
У меня нет на это столько времени (и соотв денег), это далеко не первостепенная задача.
Всё же компиляция в js готового кода на языке, из которого LLVM байткод можно сделать, мне видится более реалистичным вариантом.