Тимоти Никкел, разработчик Mozilla, сообщил в списке рассылки dev-platform о намерении включить декодирование JPEG XL по умолчанию во всех сборках Firefox начиная с версии 157. Формат разрабатывался за флагом image.jxl.enabled, который сейчас включён по умолчанию только в Nightly, а начиная с 152-й версии присутствует как переключатель в Firefox Labs на всех каналах. За декодирование отвечает библиотека jxl-rs, написанная на Rust.

Основные параметры

Среди других браузеров: Safari поставляет поддержку JPEG XL начиная с версии 17.0, выпущенной в 2023 году. В Chrome формат доступен за флагом #enable-jxl-image-format и использует ту же Rust-библиотеку, но намерения включить его по умолчанию пока не объявлено.

Что изменилось с момента прототипирования

На стадии прототипирования участники обсуждения высказывали опасения по поводу производительности. С тех пор вышла jxl-rs 0.6.0 с поддержкой многопоточного декодирования, а патчи для подключения и включения многопоточности ожидаются в ближайшее время. С учётом этих патчей Никкел провёл бенчмарк декодирования по пяти форматам на одних и тех же изображениях разного размера: на его машине результат немного превзошёл Safari (использующий C++-реализацию libjxl). По сравнению с другими декодерами изображений в Firefox, JXL показывает близкие результаты на крупных изображениях, но заметно отстаёт на маленьких.

По функциональности JXL сравним с другими форматами изображений в Firefox и с реализацией JXL в Blink, включая анимацию и прогрессивное отображение. Исключение — HDR: HDR-изображения отображаются как SDR, как и во всех остальных поддерживаемых форматах, однако тональное отображение (tone mapping) для JXL реализовано существенно лучше, чем для других форматов. У Safari при этом нет ни прогрессивного рендеринга, ни поддержки анимации.

Тестовый набор wpt для директории jpegxl охватывает корректность декодирования по битовой глубине, альфа-каналу, градациям серого, CMYK, управлению цветом, ориентации и инструментам кодирования, а также способы использования изображений в HTML и CSS. Там, где wpt не позволял выразить нужные сценарии, добавлены собственные тесты Gecko: около 30 gtest-тестов для чанкового и инкрементального декодирования, подсчёта кадров анимации, уменьшения размера при декодировании и обработки повреждённых файлов, mochitest-тесты для прогрессивного рендеринга и телеметрии, reftest-тесты и бенчмарки декодирования, отправляющие данные в Perfherder. Команда фаззинга уже тестировала jxl-rs до включения в Nightly и повторит фаззинг перед переключением настройки по умолчанию.

Реакция сообщества

В ответ на сообщение Никкела появился вопрос от пользователя под именем 一丝 о поддержке анимированного JPEG XL. Никкел подтвердил: анимация поддерживается.

Отдельно высказал опасения разработчик Сергей Давыдов, обративший внимание на производительность безлосслесс-режима (lossless) JPEG XL:

Меня беспокоит производительность lossless JPEG XL. По моим измерениям декодирование занимает в 30 раз больше времени, чем у lossless WebP, при выигрыше всего 10% в размере файла. Это спорный компромисс, особенно на ноутбуках и телефонах, где это может расходовать заряд батареи и ухудшать пользовательский опыт.

Предлагаю поставлять в Firefox 157 только lossy JPEG XL, а вопрос lossless-формата рассмотреть отдельно.

Давыдов привёл методику измерений: использовался инструмент hyperfine, который замеряет время выполнения на нескольких прогонах и собирает статистику. Тестировалась библиотека jxl-rs из репозитория github.com/libjxl/jxl-rs (коммит 775837f57dfe4294d89c1c6317dd91a1ed8d3cfa), собранная командой cargo build --release. В качестве исходного изображения использовалась картинка с Wikimedia Commons, сконвертированная в WebP командой cwebp -lossless и в JPEG XL — командой cjxl -d 0. Оба декодера запускались в однопоточном режиме для замера полного процессорного времени с помощью taskset -c 0.

$ hyperfine --warmup 5 'taskset -c 0 target/release/jxl_cli --speedtest 55_Cancri_e_Final_1_30.jxl' 'taskset -c 0 dwebp 55_Cancri_e_Final_1_30.png.webp'
    Benchmark 1: taskset -c 0 target/release/jxl_cli --speedtest 55_Cancri_e_Final_1_30.jxl
      Time (mean ± σ):     20.632 s ±  0.061 s    [User: 20.605 s, System: 0.027 s]
      Range (min … max):   20.549 s … 20.743 s    10 runs
     
    Benchmark 2: taskset -c 0 dwebp 55_Cancri_e_Final_1_30.png.webp
      Time (mean ± σ):     667.0 ms ±   2.2 ms    [User: 449.5 ms, System: 217.5 ms]
      Range (min … max):   664.3 ms … 670.1 ms    10 runs
     
    Summary
      taskset -c 0 dwebp 55_Cancri_e_Final_1_30.png.webp ran
       30.93 ± 0.14 times faster than taskset -c 0 target/release/jxl_cli --speedtest 55_Cancri_e_Final_1_30.jxl

Для сравнения, инструмент djxl из libjxl показывает результат в 20 раз медленнее WebP при таком же тесте. По мнению Давыдова, это говорит о том, что дальнейшая оптимизация Rust-кода вряд ли сможет исправить ситуацию — общий расклад останется прежним.