![](https://habrastorage.org/webt/59/e4/65/59e4652877a53403484134.png)
Теория
Каталоги ldpi-mdpi-hdpi-xhdpi… попарно отстоят друг от друга на ~33.(3) или 50%, то есть MDPI ресурс должен быть примерно в 1.33 раза больше LDPI, а HDPI — уже в 2 раза больше. При преобразованиях 2 > 1 и 4 > 1 теоретически возможны «дешевые» отбрасыванием каждого второго пикселя, при переходе 1.5 > 1 теоретически возможна оптимизация с отбрасыванием одного пикселя из трех, а при переходе 1.33 > 1 — одного из четырех, осталось проверить какие оптимизации и алгоритмы использует Android в действительности.
Методика тестирования
Создаем файл с повторяющимся паттерном из разноцветных полос шириной в один и два пикселя, чтобы проще было видеть результат свертки. Паттерны выглядят так:
???????? и ????????
и размещены со смещением 0, 1 и 2 пикселя от границы (паттерны однонаправленные, так как вертикальная и горизонтальные свертки очевидно должны использовать одинаковый алгоритм).
Помещаем картинку (одну и ту же) в каталоги LDPI, MDPI и т.д под разными именами и на каждой копии рисуем «водяной знак» обозначающий каталог, в котором она находится, чтобы знать, откуда Android взял исходник для преобразования. Отображаем картинки изо всех (MDPI-XXXDPI) каталогов на разрешениях от LDPI до XXHDPI. Смотрим под лупой, что же получилось, и отвечаем на вопросы.
Одинаков ли алгоритм свертки для переходов 1.5 > 1 и 1.33 > 1 ?
Очевидно нет, можно сравнить, как выглядит паттерн из каталога HDPI на MDPI экране и паттерн MDPI на LDPI
1.5 > 1 vs. 1.33 > 1![h->m](https://habrastorage.org/webt/59/e4/3a/59e43afc3e7f4480303005.png)
![m->l](https://habrastorage.org/webt/59/e4/3a/59e43afc91b46577498840.png)
![h->m](https://habrastorage.org/webt/59/e4/3a/59e43afc3e7f4480303005.png)
![m->l](https://habrastorage.org/webt/59/e4/3a/59e43afc91b46577498840.png)
при этом результаты сверток x > h и m > l совпадают, что подтверждает теорию.
Отбрасывает ли Android «лишний» пиксель при свертке 1.5 > 1 ?
Судя по всему, да! Для этого сделаем другой паттерн (также со смещением 0, 1 и 2 пикселя)
?????? и смасштабируем его из HDPI в MDPI
1.5 > 1 RGB![h->m](https://habrastorage.org/webt/59/e4/4f/59e44ff92d3bd516473739.png)
![h->m](https://habrastorage.org/webt/59/e4/4f/59e44ff92d3bd516473739.png)
на итоговом паттерне поочередно выпадают то красный, то синий, то зеленый цвета, значит при калькуляции 2 пикселей из 3, один не принимается в расчёт. Итоговые цвета не чистые, значит, оставшиеся два пикселя не берутся как есть, а смешиваются в разной пропорции.
Как происходит свертка 2 > 1 ?
Простым замешиванием соседних пикселей парами в равных пропорциях. Тут результаты переходов h > l, xh > m, xxh > h идентичны.
2 > 1![h->l](https://habrastorage.org/webt/59/e4/3a/59e43afbe808b694459961.png)
![h->l](https://habrastorage.org/webt/59/e4/3a/59e43afbe808b694459961.png)
Артефактов отбрасывания пикселей не замечено.
Как происходит свертка 4 > 1 ?
А вот тут Android все-таки выбрасывает часть пикселей. Судя по всему, берутся четверки пикселей, два крайних удаляются, а центральные замешиваются в равной пропорции. Примерно так:
???? > ?? > ?
4 > 1![xxxh->m](https://habrastorage.org/webt/59/e4/3a/59e43afce02df818564783.png)
![xxxh->m](https://habrastorage.org/webt/59/e4/3a/59e43afce02df818564783.png)
Именно этот алгоритм является причиной интересного артефакта — полного пропадания синего и красного цветов из паттернов ???? и ????, которые отличаются только смещением.
Какой ресурс выбирает Android если вариантов несколько?
При свертке до MDPI ресурса, который имеется в HDPI и XHPI каталогах можно было бы применить потенциально более простой алгоритм 2 > 1 вместо 1.5 > 1, пропустив одну ступень, но Android всегда выбирает ближайший ресурс. Вероятно, еще и потому, что свертка 2 > 1 использует более ресурсоемкое замешивание, а не отбрасывание (как мы видели выше), и сэкономить на ней сильно не получится.
Какой из этого всего можно сделать вывод?
Ну во-первых, надо не забывать напоминать своему дизайнеру, что рисовать нужно по сетке и что 1dp != 1px: любая однопиксельная линия на всех разрешениях от MDPI и выше может превратиться в непредсказуемую размазню или вовсе пропасть. Во-вторых, SVG/XML все-таки более надежный способ экономии на графике, если характер картинки позволяет. В-третьих, если итоговая картинка на экрана должна иметь четкие грани, все разрешения должны присутствовать в проекте. Ну и наконец, Android, действительно применяет интересные оптимизации, чтобы сэкономить процессорные ресурсы, и этим можно пользоваться, если делать это с умом.
Пример работы тестового приложения на разных разрешениях (на каждой картинке написано из какого каталога, она взята, рядом с ней — в каких каталогах она присутствовала):
LDPI![l](https://habrastorage.org/webt/59/e4/3a/59e43afdb0523492420613.png)
![l](https://habrastorage.org/webt/59/e4/3a/59e43afdb0523492420613.png)
MDPI![m](https://habrastorage.org/webt/59/e4/3a/59e43afe3f12b852205164.png)
![m](https://habrastorage.org/webt/59/e4/3a/59e43afe3f12b852205164.png)
HDPI![h](https://habrastorage.org/webt/59/e4/3a/59e43afd66878263461766.png)
![h](https://habrastorage.org/webt/59/e4/3a/59e43afd66878263461766.png)
XHDPI![xh](https://habrastorage.org/webt/59/e4/3a/59e43afebfffc559035391.png)
![xh](https://habrastorage.org/webt/59/e4/3a/59e43afebfffc559035391.png)
XXHDPI![xxh](https://habrastorage.org/webt/59/e4/3a/59e43aff43ebe160290685.png)
![xxh](https://habrastorage.org/webt/59/e4/3a/59e43aff43ebe160290685.png)
> Исходники на GitHub
Спасибо за внимание!