Производительность

В предыдущей статье мы рассказали, как быстро превратить сжатые байты в пиксели. Здесь продолжение: как добиться того, чтобы даже изображение в сотни миллионов пикселей просматривалось плавно — чтобы перетаскивание и масштабирование поспевали за рукой.

Насколько большим бывает изображение

Снимок на телефон — 12 мегапикселей, зеркальная камера — 24, а спутниковый снимок может достигать 18641 × 18641 ≈ 350 мегапикселей. Если честно декодировать его в память целиком:

Декодировать всё в память ≈ 1.4 GB 350 Мп × 4 байта Сколько реально покажет экран ≈ 8 Мп 4K = 3840 × 2160 ≫

1,4 ГБ памяти и несколько секунд ожидания — лишь чтобы показать изображение, уменьшенное до размеров экрана, где глаз всё равно не различит отдельных пикселей. Большинство пикселей так и не получат шанса быть увиденными. Подход GuoheView — тратить усилия только на нужную детализацию и нужную область. Для этого работают вместе три механизма: пирамида, тайлы и загрузка по требованию.

Пирамида: декодируем с нужной детализацией

Если раз за разом уменьшать исходное изображение вдвое и сохранять результаты стопкой слоёв от большого к маленькому, получится пирамида изображения. Нижний слой — исходный размер, каждый следующий вдвое меньше по стороне, пока всё не уместится в миниатюру 256×256. У изображения 18641² около 8 слоёв.

Level 0 — 18641², оригинал Level 1 — 9320² Level 2 … Верхний слой ≤ 256² Чем сильнее уменьшено, → тем выше слой Чем сильнее увеличено, → тем ниже слой

Когда изображение открывается «по размеру окна», оно целиком уменьшается до экрана, и исходные данные 18641² вообще не нужны — достаточно показать один из средних слоёв, это быстро и экономно. Когда вы увеличиваете фрагмент, чтобы рассмотреть детали, программа переключается на слой ниже и чётче. Всегда используется слой ровно той чёткости, которой достаточно — в этом и смысл пирамиды. Поскольку каждый дополнительный слой вчетверо меньше нижележащего, все вместе они добавляют к оригиналу лишь около трети — небольшая цена.

Тайлы: декодируем только видимую часть

Одной пирамиды мало: при увеличении для деталей используется нижний слой, а в нём всё равно сотни миллионов пикселей. Поэтому каждый слой дополнительно делится на квадратики 256×256 (тайлы), и при отображении обрабатываются только те, что попадают на экран.

Область просмотра Тайлы в кадре: декодируются Тайлы вне кадра: подождут По 256×256; весь экран 4K — всего около 135 тайлов

Чтобы заполнить весь экран 4K, нужно всего около 135 тайлов — совсем другой порядок величин, чем «сотни миллионов пикселей всего изображения». К тому же тайлы можно декодировать параллельно: сотня с лишним тайлов распределяется по нескольким ядрам процессора одновременно, и картинка появляется быстрее.

Загрузка по требованию: декодируем там, куда вы смотрите

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

Масштаб / сдвиг кадр изменился Видимые тайлы слой + пересечение Декодирование в фоне, все ядра Вывод тайл за тайлом продолжаете → по кругу

Всё декодирование идёт в фоновых потоках, поэтому интерфейс не зависает; уже декодированные тайлы кэшируются, и при возврате их не нужно декодировать заново. Так что каким бы большим ни было изображение, в каждый момент декодируется лишь небольшой участок, видимый на экране.

Сначала увидеть, потом — чёткость

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

Здесь есть коварная деталь: превью и тайлы рисуются двумя независимыми слоями, и если неправильно выбрать момент переключения, получится откат: «чёткое превью → на миг размыто → снова чётко». Мы специально гарантируем этот путь монотонного роста чёткости, чтобы на всём его протяжении не было обратных вспышек.

Всё вместе

  • Пирамида решает, «насколько детальные данные брать», — слой выбирается по масштабу отображения, без траты сил на невидимые детали;
  • Тайлы решают, «какую область декодировать», — затрагивается только участок на экране;
  • Загрузка по требованию + фоновый параллелизм решают, «когда декодировать», — декодируется то, куда вы перешли, без зависаний интерфейса;
  • Превью в низком разрешении решает, «что показать сначала», — картинка сразу, а затем всё чётче.

Вместе они тратят память и время только на те пиксели, которые вы действительно видите. Поэтому изображение в сотни миллионов пикселей можно свободно перетаскивать и масштабировать, и всё поспевает за рукой.