На днях добавил к блогу поддержку формата WEBP. Работать начало всё сразу, но без костылей не обошлось.
Каналы передачи данных становятся всё шире, скорости растут, а дух оптимизатора все никак не унимается. WEBP обещает выигрыш до 30% на размер файла по сравнению с JPG при примерно одинаковом количестве деталей. Проверено, так и есть.

Реализация WEBP на этом блоге

Сегодня, из всех браузеров, полноценно поддерживают WEBP только Chromium-подобные браузеры. Не очень-то и обширно, но радует, что крупные игроки один за одним сообщают о внедрении технологии в ближайшее время. Тем не менее, реализуя поддержку WEBP уже сегодня, мы получаем ощутимый прирост скорости загрузки. А по данным comss.ru, мы покрываем более 60% всех пользователей.

Браузер Покрытие, % webp Примечание
Chromium 61,29 да все Chromium-подобные браузеры
(Chrome, Yandex-браузер, новая Opera и.т.д)
Firefox 12,38 нет говорят, поддержку уже добавили в тестовую версию
Edge 4,03 нет поддержку грозятся внести не только в Edge, но ещё и в Windows

Как же быть с браузерами, не поддерживающими WEBP? Умные головы придумали тег <picture>, а в нём теги <source> с указанием типа изображения и пути к файлу. Браузеры сами решают, какое изображение они будут показывать. Например, Chrome выбирает WEBP. Современные браузеры корректно обработают эту конструкцию, а старые пропустят неизвестные теги. Особое внимание нужно обратить на атрибут type, в нем указывается mime-тип изображения.

<picture>
	<source type="image/webp" srcset="/images/coolimg.webp"/>
    <source type="image/jpeg" srcset="/images/coolimg.jpg"/>
	<img src="/images/coolimg.jpg"/>
</picture>

Я был приятно удивлен, обнаружив поддержку WEBP в библиотеке php-gd. На стороне сервера за сохранение изображений отвечают следующие функции:

	imagejpeg($imageObject, $jpgDestination,  $quality); //в JPG
	imagewebp($imageObject, $webpDestination, $quality); //в WEBP

PHP ломает WEBP

Как и положено, у этого блога есть 2 сервера. На тесте стоит php 5.6.36, на проме php 5.6.37. Внеся все необходимые правки, я протестировал сайт на домашнем компьютере и обновил пром. Я был удивлен, изображения на проме не отображались, вообще, хотя на тестовом сервере работали без проблем.

В консоли хрома ошибок не было, более того Хром рапортовал о нормальной загрузке изображений.

Консоль хрома рапортовала о успешной загрузке изображений. Консоль хрома рапортовала о успешной загрузке изображений.

Я скачал сгенерированный WEBP-файл с блога и... файл отлично открылся в ACDSee. Сравнивая файлы прома и теста, я обнаружил несоответствие размеров для сгенерированных WEBP-файлов на сервере и на домашнем компьютере. Размер файлов отличался всего лишь в 1 байт.

Изображение с теста и прома. Размер файлов не совпадает. Изображение с теста и прома. Размер файлов не совпадает.

Вот эти 2 файла:

Скачать изображение с теста
jcup_gen.webp
157 КБ
Скачать изображение с прома
jcup_ru.webp
157 КБ

Сдул пыль с HEX-редактора и полез смотреть, чего там не хватает. Оказалось, что промовкий PHP не дописывает 1 байт в конец файла, файл оказывается не валидным и Chrome не может его отобразить. Обновление пакетов php5.6 и php5.6-gd на проме не дало, ровным счётом, никаких результатов.

В конце файла отсутствует байт x00 Проблема невалидности WEBP. В конце файла отсутствует байт x00 Проблема невалидности WEBP.

Немного поисследовав структуру WEBP, я пришел к выводу, что все живые файлы всегда заканчиваются байтом x00, кроме того WEBP продолжает нормально работать, если добавить любое количество x00 в конец. Можете проверить сами, всё отлично работает:

Скачать webp с огромным кол-ом x00 в конце
ru_with_many_x00.webp
157.1 КБ

Так и появился на свет очередной костыль. Сразу после генерации изображения, дописываем x00 в конец файла.

    //x00 webp generation fix
    $fpr=fopen($webpDestination, "a+");
    fwrite($fpr, chr(0x00));
    fclose($fpr);