ermouth: (ang)
[personal profile] ermouth
Я примерно в феврале прорабатывал вариант создания полностью браузерной 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. Не, не подошло, увы. Ищем дальше.

Date: 2014-08-18 05:37 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Исходники jpeg компрессии ты наверняка найдёшь здесь: http://sourceforge.net/projects/imagemagick/
Правда, не факт, что компактные.

Это мегаклассная тула по command line конвертации из всего во всё.


Простая промежуточная идея: подготовь две версии файлов - в хорошем качестве и в плохом, и скачивай их параллельно. Если хорошее ещё не скачалось, а плохое уже скачалось, его можно показать. Вот тебе как бы типа progressive rendering, платим трафиком (но, может, пох?), а latency not affected за счёт параллелизации.

Date: 2014-08-18 08:26 pm (UTC)
From: [identity profile] ermouth.livejournal.com
Мы используем imagemagick на серверах, и очень давно. Собсно, на мой вкус это лучшая конвертилка. Единственный встреченный с ней редкий баг – она иногда (и не понятно когда именно) дропит экзиф из мобильных фоток и их не разворачивает как надо.

Имиджмэджик устроен так, что из него весьма затруднительно выковырять отдельные части, а целиком его перекомпилить – это, как помнится, в районе 10 мегов получалось.

Бубен с двумя картинками – это даже в теге img есть такой атрибут lowsrc, я этот вариант пробовал. Выяснилось – к немалому моему удивлению – что все до единого современные браузеры этот атрибут просто игнорируют и он уже лет 5 как deprecated числится. Зато в ИЕ6 работает )

Делать функционал подстановки руками – это очень плохая идея именно в этом случае в силу целого ряда причин, я её отбросил. Вообще, мы так делаем где релевантно – так сделан и двиналэнд, и двинаньюс в некоторых местах, но там это только для маленьких превьюшек в галереях.

Для крупных картинок двух версий мало – сначала появляется лоурес, а потом, очень небыстро, фуллрес. Поэтому нагруженный графикой сайт ощутимо долго выглядит как говно. С прогрессивным джипегом то видно, что чёто грузится, сразу, визуально – а тут нет.

Нарезка бинарного потока – совсем плохая идея, потому что а) мы получаем картинки в формате, больше ни с чем не совместимом б) яваскрипт – однопоточная система, и резать в ней большие бинари стримы на лету очень геморройно. Чаще всего ты даже не сможешь сделать это «на лету».

В общем, тут много ещё есть трюковых таких вариантов – но они все для желаемого эффекта слишком черезжопные. В таких ситуациях я задумки торможу, пока мне вселенная не подкинет варианта получше )

Date: 2014-08-18 08:42 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Я немного запутался, а что именно ты хочешь сделать? Скачивать однопоточно gzip-поток, при этом прогрессивно отображая вытаскиваемые из него jpeg'и? что конкретно это тебе даст, ты ведь все равно не сможешь показать кусочек второго jpeg'а, пока полностью не загрузился первый?

Date: 2014-08-18 08:51 pm (UTC)
From: [identity profile] ermouth.livejournal.com
Прости, я не понял вопрос. Я хочу progressive jpeg же.

Date: 2014-08-18 08:58 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Ну смотри, ты пишешь "готовые страницы и ресурсы пакуются gzip и пишутся прямо на Amazon S3". Т.е. ты хочешь весь static content для небольшого сайта/страницы в одном zip файле. Он будет распаковываться javascript'ом (по всей видимости, со всей его замечательной однопоточностью, да?) и отображаться на странице. Тут ты жалуешься, что не получается javascript'ом соорудить progressive jpeg, вероятно, желая отображать недокаченные картинки из того mega-gzip'а как можно быстрее. Но ведь gzip это уже сериализация, если картинок 10 штук, то они, как ни крути, будут появляться по одной. Большого смысла в progressive нет, т.к. по-любому сериализация из-за самой концепции "одного gzip'а".

Или я что-то не так понял?

Date: 2014-08-18 09:03 pm (UTC)
From: [identity profile] ermouth.livejournal.com
Да, ты не так понял, я кривовато написал и поправлю в посте. _Каждая_ страница и каждый ресурс, который можно паковать, пакуется в отдельный gzip.

Дело в том, что S3 сам не умеет паковать выдачу в канал. Чтобы он отдал, например, html с канальной упаковкой gzip, ему надо прямо gzip туда и положить, и для этого упакованного html указать заголовки Content-type: text/html; Content-encoding: gzip.

Date: 2014-08-18 09:11 pm (UTC)
From: [identity profile] morfizm.livejournal.com
А, ну тогда понятно. А зачем тогда паковать jpeg и/или как-то ещё encode'ить? Они же несжимаемы. Выкладывай прогрессив jpeg-и напрямую в s3, и делай img src="http://s3... " будет тебе прогрессив jpeg из s3. Или я опять что-то недогоняю?

Date: 2014-08-18 09:13 pm (UTC)
From: [identity profile] ermouth.livejournal.com
А откуда я возьму этот progressive jpeg? Ну вот есть у меня фотка, я её в CMS вставляю, кроплю, ресайзю – и? Дальше то как что получилось в прогрессив джипег превратить?

Date: 2014-08-18 09:36 pm (UTC)
From: [identity profile] morfizm.livejournal.com
О! Теперь до меня окончательно дошло. Ты решаешь проблему создания контента, а не выдачи/рендеринга, и хочешь чтобы весь конструктор был на javascript.


Ну создай 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'а, и рекламу показывать, пока конвертация конвертируется :)
Edited Date: 2014-08-18 09:38 pm (UTC)

Date: 2014-08-18 09:45 pm (UTC)
From: [identity profile] ermouth.livejournal.com
Да есть у меня этот бэкэнд сервис, в нагруженном боевом продакшене больше года уже, ни разу не пикнуло. Но мне по ряду причин надо чисто браузерное решение – такова задумка и задача, для чего мне это.

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

Дело в том, что не существует способа взять существенно упакованные джипег, распаковать его и запаковать снова в другой джипег, не ухудшив картинку весьма существенно. То-есть тебе либо надо первую версию выкладывать в разы больше по объёму (менее упакованную), либо рядом класть fullres, что ещё хуже.

Date: 2014-08-18 09:49 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Ну-ка, ну-ка, почему не вариант?

"Дело в том, что не существует способа..."

Гуглим: 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."

Date: 2014-08-18 09:52 pm (UTC)
From: [identity profile] ermouth.livejournal.com
Ну я же не идиот и конечно это попробовал ) Там даже написано, что есть compatibility issues, и это чистая правда.

Date: 2014-08-18 09:56 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Ну идиотом тебя никто не называл. Я не имею ожиданий, будто бы каждый человек прочёл всё, что можно было найти по его теме, прежде, чем писать в ЖЖ вопросы (зачем такой строгий барьер?) так что было бы вполне OK, если бы ты это не читал :)

Мне batch обработка видится очень естественным решением, даже если изначально нужно будет закачивать чуть менее сжатый, но более compatible формат.

Date: 2014-08-18 10:05 pm (UTC)
From: [identity profile] ermouth.livejournal.com
Да jpegtran сам по себе достаточно короткий, чтобы его перекомпилить в js и загружать как бибилиотеку. Беда в том, что при конвертации из обычного джипега в прогрессив иногда увеличивается размер, и иногда – довольно сильно (там быть не должно), и сильно не каждая картинка потом _показывается_ как прогрессив, мало того, некотоыре вообще перестают показываться пока не загрузятся целиком. этому феномену я не нашёл точного объяснения.

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

Date: 2014-08-18 10:10 pm (UTC)
From: [identity profile] ermouth.livejournal.com
Мне тут надо пояснить, что мы в четыре руки полтора года назад систематически занимались вопросом обработки снимков, когда писали CMS. То-есть перерыто было буквально всё носом, вдоль и поперёк.

Date: 2014-08-18 10:59 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Понятно.

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

Кроме того, по более простым вопросам легче помогать. Вот я спрашиваю своих читателей, как заработать несколько миллионов для early retirement, и никто не говорит (нычут полезную инфу, сволочи;)). А спрашиваю, скажем, помощь с трудоустройством (т.е. с чем-то более простым и short-term, но тем не менее достаточно трудным) и получаю сумасшедшее количество поддержки и реальной помощи.

Date: 2014-08-18 10:27 pm (UTC)
From: [identity profile] ermouth.livejournal.com
В общем, я идиот.

Пошёл почитать новости про jpegtran и арифметическое кодирование – и нашёл libjpeg портированный в js, в 400 Кб всего. https://github.com/jrmuizel/cjpeg.js

Date: 2014-08-18 11:02 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Ну вот, ты сначала защищался от идиота, а потом нарёк себя идиотом. Как по мне так всё нормально было изначально, всё нормально и осталось.
Но я рад, что в результате, ты обнаружил то, что тебе нужно :)

Date: 2014-08-18 11:05 pm (UTC)
From: [identity profile] ermouth.livejournal.com
вселенная в твоём лице довольно опосредованным способом подкинула мне идею поискать не progressive jpeg javascript encoder, а libjpeg emscripten – и я нашёл что нужно по третьей ссылке.

я мог бы раньше догадаться.

Date: 2014-08-18 11:36 pm (UTC)
From: [identity profile] morfizm.livejournal.com
"вселенная в твоём лице довольно опосредованным способом подкинула мне идею..."

Брр :) Я прямо почувствовал, как лапы вселенной прошли через меня насквозь и шевелили моим языком пальцами, чтобы тебе опосредованно помочь :)


"я мог бы раньше догадаться."

Я напишу потом пост на родственную тему (психология рабочих отношений). Уже давно хочу.

Date: 2014-08-19 02:56 pm (UTC)
From: [identity profile] ermouth.livejournal.com
Увы, не подошло (

Date: 2014-08-19 09:07 pm (UTC)
From: [identity profile] morfizm.livejournal.com
А чем именно не подошло? Не кушает нужные тебе форматы?

Date: 2014-08-19 11:11 pm (UTC)
From: [identity profile] ermouth.livejournal.com
ещё проще – не выдаёт нужные мне форматы, это какая то обгрызенная версия libjpeg

Date: 2014-08-18 09:45 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Ещё идея - если всё это на S3, то кто-то же будет платить за S3 ресурсы (трафик, storage). Пусть они же и платят за micro-instance, на котором будут гоняться всякие полезные batch script'ы, в т.ч. daily JPEG optimizer. Скрипт будет тривиальный, думаю, в 1-2 экрана кода можно уложиться.

Date: 2014-08-18 09:09 pm (UTC)
From: [identity profile] ermouth.livejournal.com
Кстати, засериализованные прямо в html картинки ты можешь посмотреть, например, вот здесь http://cloudwall.me/app.html.

Эти страницы как раз сгенерёны на клиенте, и заинлайненный jpeg не просто не progressive, он ещё и base64 (+33%). Это совершенно не заметно визуально.

Но это маны, такая техника не подходит для презентационных сайтов.
Edited Date: 2014-08-18 09:10 pm (UTC)

Date: 2014-08-18 09:11 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Я из дома потом посмотрю (сейчас на работе).

Date: 2014-08-18 07:56 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Ещё идея - запакуй несколько jpegs разной степени компрессии в один binary stream, и скачивай его. Распаковать на лету javascript'ом должно быть очень просто (раз ты умеешь raw bitmap data кодировать в jpeg на лету, то разбить binary array на куски должно быть тривиально). Это будет менее эффективно, чем настоящий progressive jpeg (будет больше байт), зато simple/straightforward implementation for desired effect.

Date: 2014-08-20 11:38 am (UTC)
From: [identity profile] tonsky.livejournal.com
А вообще алгоритм транформации обычного jpeg (как я понял, ты умеешь это делать?) и сделать из него прогрессивный, он очень сложный? Просто может взять спецификацию да запилить?

Date: 2014-08-20 11:56 am (UTC)
From: [identity profile] ermouth.livejournal.com
Ни я, ни мои коллеги не умеем писать высокооптимизированный js-код, перерабатывающий бинари – это для js «неродная» задача всё же.

То-есть это неизбежно попадалово на:
— время на разбор стандарта, он весьма кучерявый и, как водится, написан не для людей
— время на создание самого кода
— время на оптимизацию кода
— время на всестороннее тестирование.

У меня нет на это столько времени (и соотв денег), это далеко не первостепенная задача.

Всё же компиляция в js готового кода на языке, из которого LLVM байткод можно сделать, мне видится более реалистичным вариантом.

Profile

ermouth: (Default)
ermouth

November 2021

S M T W T F S
 123456
78910111213
14151617181920
21 222324252627
282930    

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Feb. 2nd, 2026 04:43 am
Powered by Dreamwidth Studios