Переменная плотность пикселей

Методы просмотра изображений с разной плотностью пикселей на разных размерах экрана в хорошем качестве. Решая следующие проблемы:

  • Разнообразие устройств с разными размерами экранов.
  • Ограниченная пропускная способность сети интернет на мобильных устройствах.

По возможности избегать изображения

Технологии такие как SVG и CSS независимы от разрешения DPI, поскольку они автоматически масштабируются. Конечно не всегда можно обойтись без растровых изображений, например фотографии. Их можно попробовать преобразовать в SVG, но в оцифровке цифровой фотографий нет смысла.

к меню ↑

История дисплея

Сначала, компьютерные мониторы были с плотностью 72 пикселя (96 dpi, 96 точек на дюйм). Постепенно качество изображения улучшалось с ним росла плотность пикселей, в основном из-за мобильного использования. Когда пользователь подносит свой телефон ближе к лицу, пиксели становятся более заметными.

В 2008 году телефоны с 150dpi стали новой нормой. Тенденция к увеличению плотности дисплея продолжалась, теперь новые телефоны 300dpi. Конечно Святым Граалем будет тот дисплей, в котором пикселей невидно. Нынешнее поколение дисплеев Retina близки к идеалу.

Если сравнить изображение с высокой плотностью, с изображением с низкой плотностью оно выглядеть не четко. Ниже грубая имитация изображения (1x) с низкой плотностью на дисплее с высокой плотностью пикселей.

Разная плотность пикселей
Низкая плотность пикселей
Разная плотность пикселей
Высокая плотность пикселей

В отличие от изображения (1x), изображение (2x) выглядит неплохо.

к меню ↑

Спецификация для размера пикселя

Из-за большого набора разных размеров дисплеев и их плотности, появилась потребность создать стандартный способ для просмотра изображений с разной плотности пикселей.

Как спецификация HTML5 решала эту проблему.

Рекомендуемый опорный пиксель для угла зрения на устройстве с плотностью пикселей 96dpi на расстоянии вытянутой руки. Длины руки от 28 дюймов, следовательно угол зрения от 0,0213 градуса.

Теперь производитель дисплеев может определить размер физического пикселя относительно устройства к стандартным или идеальным пикселям. Это соотношение называется: Соотношение пикселя к устройству.

к меню ↑

Рассчитать соотношение пикселя к устройству

Предположим что смартфон имеет экран с физическим размером 180 пикселей на дюйм (PPI). Расчет соотношения пикселя к устройству включает три этапа:

  1. Сравнить фактическое расстояние на котором находится устройство от опорного пикселя. Мы знаем что: для 28 дюйм идеально подходит плотность 96dpi. Но так как это смартфон, и люди его держат к лицу ближе чем ноутбук. Значит размер приблизительно 18 дюймов.
  2. Умножить соотношение расстояния от стандартной плотности 96dpi, чтобы получить идеальную плотность пикселей для данного расстояния.
    idealPixelDensity = 28/18 * 96 = 150пикселей на дюйм (приблизительно).
  3. Возьмём физическое соотношение плотности пикселей к идеальной плотности пикселей, чтобы получить соотношение пикселя к устройству.
    devicePixelRatio = 180/150 = 1,2
Диаграмма, показывающая одну ссылку углового пикселя
Связь пикселя для devicePixelRatio

Теперь браузер изменит размер изображения до размеров экрана в соответствии с идеальным или стандартным разрешением. Формула для перехода между идеальным (как определено в веб-спецификации) и физическим (точек на экране устройства) пикселем:

physicalPixels = window.devicePixelRatio * idealPixels

Производители устройств округляют дескриптор devicePixelRatios (DPRS), округление уменьшает количество артефактов на изображении. Но в реальности, устройства гораздо более разнообразны, например телефоны Android часто имеют DPRS 1.5. Планшет Nexus 7 имеет DPR от ~ 1,33 результат вычислений аналогичный. В будущем будет много устройств с переменным DPRS.

к меню ↑

Методы показать качественное изображение

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

  1. Оптимизировать одно изображение для всех устройств.

Что можно сделать умное с одним изображением:

  • Сильное сжатие изображения.
  • Разный формат изображения.
  • Прогрессивный формат изображения.
к меню ↑

Сильное сжатие изображения

Больше половины времени тратится на загрузку веб-страницы с изображением.

Пропускная способность
Изображения составляет 60% пропускной способности

Скрипт для сжатия изображения работает с ImageMagick.

Командная строка:

#!/usr/bin/env bash
QUAL_HI=90
QUAL_MID=50
QUAL_LOW=20
ORIG=hq2x.jpg
# Create 2x version.
convert original.jpg -quality $QUAL_HI hq2x.jpg
# Create 1x version.
convert hq2x.jpg -resize 50% hq1x.jpg
# Create compressed versions.
convert hq2x.jpg -quality $QUAL_MID lq2x.jpg
convert hq1x.jpg -quality $QUAL_MID lq1x.jpg
# Create very compressed versions.
convert hq2x.jpg -quality $QUAL_LOW ulq2x.jpg
convert hq1x.jpg -quality $QUAL_LOW ulq1x.jpg
# Crop everything into little samples for clarity.
for img in *1x.jpg
do
convert $img -crop 160x120+0+0 preview/$img;
done
for img in *2x.jpg
do
convert $img -crop 320x240+0+0 preview/$img;
done
# Tile into a preview.
montage -label '%f, %b -- quality: %Q' preview/*x.jpg -shadow -border 5 -geometry 320x240+5+5 preview/tile.jpg

Сжатые изображения с качеством 90, 50 и 20

Образцы изображений с различным сжатием и плотности пикселей
Сжатое изображение с качеством 90, 50 и 20
Образцы изображений с различным сжатием и плотности пикселей
Сжатое изображение с качеством 90, 50 и 20
Образцы изображений с различным сжатием и плотности пикселей
Образцы изображений с различным сжатием

Сильно сжатое изображение в некоторых случаях даже выглядит лучше, чем несжатое. Конечно, изображение с высокой степенью сжатия для устройств 2x хуже, чем с хорошим качеством. Если вы сравните изображение 90 с качеством изображения 20, вы увидите уменьшение четкости и увеличение зернистости. Эти артефакты не нужны в тех случаях, когда высокое качество изображения являются ключевым моментом (например фотография). Сравнение было сделано исключительно со сжатым изображением в формате JPEG. Стоит отметить, что существует много подходов, которые широко применяются для сжатия графических форматов (JPEG, PNG, GIF).

к меню ↑

Разный формат изображения

WebP довольно убедительный формат, хорошо сжимается и сохраняет при этом четкость изображения. Но поддержка этого формата через Modernizr.webp.

Как запасной вариант в CSS можно написать следующее:

#pic {
  background: image("foo.webp", "foo.jpg");
}

Есть две проблемы с этим подходом. Во-первых image() не для контента. Во-вторых, дует WebP и JPEG улучшает меньше чем на 30% если отобразился WebP. Поэтому формата WebP не достаточно для решения проблемы.

к меню ↑

Прогрессивный формат изображения

Форматы JPEG 2000, Progressive JPEG, PNG и Progressive GIF.

На первый взгляд прогрессивное изображение выглядит очень перспективным в контексте для лучшего качества. Идея состоит в том, что браузер может остановить загрузку и декодирование изображения, дополнительные данные не приведут к увеличению качества изображения (т.е. все улучшения точности являются суб-пикселями). Для сайта с большим количеством изображений нужно сохранить соединение HTTP так долго, как это возможно. Если соединение закроется преждевременно, изображение не будет до конца загружено, браузеру необходимо создать новое соединение, которое может быть очень медленным. Можно использовать запрос HTTP Range, который позволяет браузерам указать диапазон байтов для выборки. Браузер делает запрос сколько изображений необходимо отображать. HTTP Range слабо поддерживаются на веб-сервере, что делает этот подход непрактичным. Наконец, у такого подхода нет возможности выбора, какие изображения должны отображаться. В результате это не решение для «художественного направления«.

  1. Устройство выбирает оптимизированное изображения из нескольких изображений.
к меню ↑

JavaScript для изображения

На стороне клиента можно применить JavaScript, для выбора отображения картинки на странице. Получить ширину и высоту экрана методом window.devicePixelRatio. И даже сделать подключение к сети через navigator.connection или выдачу поддельных запросов, как это делает библиотека foresight.js. После собора информации, вы сможете решить какая картинка должна отображаться.

Есть приблизительно один миллион библиотек JavaScript, которые делают это, и все они с недостатками. Один большой недостаток в этом подходе, что при использовании JavaScript, у вас будут медленно грузится изображения до тех пор, пока не будет закончен look-ahead парсер. Это означает, что изображения не будут даже начинать грузится сразу после события PageLoad. Подробнее об этом в статье Джейсон Григсби.

к меню ↑

Хак User-Agent

Отложить загрузку изображения с сервера путем написания пользовательских обработчиков запроса для каждого изображения. Такой обработчик проверит поддержку Retina на основе User-Agent (только часть информации передается на сервер). Затем, в зависимости от логики на стороне сервера заработают активы HiDPI, вы загрузите соответствующий актив (названы в соответствии с некоторыми известными конвенциями). К сожалению, User-Agent не всегда даёт достаточно информации устройству, когда именно должно грузится изображение высокого или низкого качества. Разумеется, всё что относится к User-Agent — это хак, по возможности его следует избегать.

к меню ↑

Медиа-запросы

Медиа-запросы указывают браузеру как сделать всё правильно. В media queries можно добавить devicePixelRatio. Если вы хотите загрузить изображение с высоким разрешением, вот что нужно сделать:

#my-image { 
   background: (low.png); 
}

  @media only screen and (min-device-pixel-ratio: 1.5) {
    #my-image { 
     background: (high.png); 
     }
}

Со всеми префиксами CSS всё становится сложнее, особенно из-за безумного различия в размещении с min и max:

@media only screen and (min--moz-device-pixel-ratio: 1.5),
    (-o-min-device-pixel-ratio: 3/2),
    (-webkit-min-device-pixel-ratio: 1.5),
    (min-device-pixel-ratio: 1.5) {

  #my-image {
    background:url(high.png);
    }
}

Такой подход хорош только тем, что избавит от JavaScript. Но кода много и требует предварительной обработки. Кроме этого подход ограничен свойствами CSS.. Наконец, опираясь исключительно на соотношение пикселей к устройству, вы можете оказаться в ситуации, когда ваш смартфон заканчивает загрузку изображений 2x, EDGE соединение. Это не лучший пользовательский способ.

к меню ↑

Использовать функции браузера

image-set() — это функция CSS WebKit! Поддерживают её Safari и Chrome. Так как image-set () функция CSS, она не имеет отношения к тегам img.

Синтаксис image-set() довольно прост, строки url или url()для адреса изображения с соответствующим разрешением.

Например:

background-image: -webkit-image-set (
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);

Это говорит браузеру, что есть два изображения на выбор. Один из них оптимизирован для 1x дисплеев, а другой для 2x дисплеев. Выбор отображения основан по целому ряду факторов, даже учитывать скорость сети, если браузер достаточно умён (насколько я знаю, в настоящее время нет такого). Другими словами выбор браузера будет соответствовать размеру окна.

Не обязательно указывать 1x, 1.5x или Nx, вы также можете указать определенную плотность пикселей устройств. Это не работает в браузере, который не поддерживает свойство набора изображений, он будет показывать изображение не везде! Поэтому нужно добавить резерв:

background-image: url(icon1x.jpg);
background-image: -webkit-image-set (
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);

background-image: image-set (
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);

image-set() для HTML

<div 
   id="my-content-image" 
   style="content: 
         -webkit-image-set( url('icon1x.jpg') 1x, 
                            url('icon2x.jpg') 2x);">

свойство image-set() браузерами почти не поддерживается.

Свойство srcset:

<img alt="my awesome image" 
   src="banner.jpeg" 
   srcset="banner-HD.jpeg 2x, 
   banner-phone.jpeg 640w, 
   banner-phone-HD.jpeg 640w 2x" />

В дополнение к значению х элемент srcset также принимает значение w, которое соответствуют размеру окна.

Изображение banner-phone.jpeg 640w — для устройств с шириной окна до 640px, banner-HD.jpeg 2x — для маленького окна с высоким разрешением, banner-phone-HD.jpeg 640w — для экрана больше чем 640px, banner.jpeg — для всех остальных.

Такой метод добавлен в элемент HTML5 picture.

к меню ↑

Заключение

Выбор за вами, обойтись без изображений в целом или применить SVG и CSS. Всегда можно применить способы выше, особенно если на вашем сайте есть изображения с высоким качеством. Подходы JS, CSS имеют свои сильные и слабые стороны. Наиболее перспективный подход — это использовать функций браузера.

Мои рекомендации:

  • Для фонового изображения использовать image-set с резервом для браузеров, которые не поддерживают функцию image-set.
  • Для изображения в содержании использовать srcset.
  • Когда требуется качество изображения, рассмотрите вариант сжатия изображения.