Я создаю социальное приложение с интенсивным использованием изображений, в котором изображения отправляются с сервера на устройство. Когда устройство имеет меньшее разрешение экрана, мне нужно изменить размер растровых изображений на устройстве, чтобы они соответствовали предполагаемым размерам экрана.
Проблема в том, что использование createScaledBitmap приводит к тому, что я сталкиваюсь с множеством ошибок нехватки памяти после изменения размера множества миниатюрных изображений.
Какой способ изменения размера растровых изображений на Android является наиболее эффективным с точки зрения памяти?
android
performance
bitmap
out-of-memory
Кольт МакАнлис
источник
источник
Ответы:
Существует три основных способа изменения размера растрового изображения на Android, которые имеют разные свойства памяти:
createScaledBitmap API
Этот API примет существующее растровое изображение и создаст НОВОЕ растровое изображение с точными размерами, которые вы выбрали.
С другой стороны, вы можете получить изображение именно того размера, который вам нужен (независимо от того, как оно выглядит). Но недостатком является то , что этому API для работы требуется существующее растровое изображение . Это означает, что изображение должно быть загружено, декодировано и создано растровое изображение, прежде чем можно будет создать новую, меньшую версию. Это идеально с точки зрения получения точных размеров, но ужасно с точки зрения дополнительных накладных расходов на память. Таким образом, это своего рода нарушение условий для большинства разработчиков приложений, которые склонны уделять внимание памяти.
флаг inSampleSize
BitmapFactory.Options
имеет свойство, отмеченное какinSampleSize
изменение размера изображения при его декодировании, чтобы избежать необходимости декодировать во временное растровое изображение. Это целое число, используемое здесь, загрузит изображение с уменьшенным размером в 1 / x. Например, установка значенияinSampleSize
2 возвращает изображение, размер которого вдвое меньше, а установка значения 4 возвращает изображение, размер которого составляет 1/4. Обычно размеры изображений всегда будут в несколько раз меньше размера исходного изображения.С точки зрения памяти использование
inSampleSize
- действительно быстрая операция. Фактически, он будет декодировать только каждый X-й пиксель вашего изображения в полученное растровое изображение. Однако есть две основные проблемыinSampleSize
:Он не дает вам точного разрешения . Он только уменьшает размер вашего растрового изображения на некоторую степень 2.
Изменение размера не самого лучшего качества . Большинство фильтров изменения размера создают красивые изображения, считывая блоки пикселей и затем взвешивая их, чтобы получить соответствующий пиксель с измененным размером.
inSampleSize
позволяет избежать всего этого, просто считывая каждые несколько пикселей. Результат довольно производительный, мало памяти, но страдает качество.Если вы имеете дело только с уменьшением изображения на некоторый размер pow2, и фильтрация не является проблемой, то вы не можете найти более эффективный с точки зрения памяти (или производительный) метод, чем
inSampleSize
.флаги inScaled, inDensity, inTargetDensity
Если вам нужно масштабировать изображение до размера, не равного степени двойки, вам понадобятся флаги
inScaled
,inDensity
и . Когда флаг установлен, система вычислит значение масштабирования для применения к растровому изображению путем деления на значения.inTargetDensity
BitmapOptions
inScaled
inTargetDensity
inDensity
Использование этого метода изменит размер вашего изображения, а также применит к нему «фильтр изменения размера», то есть конечный результат будет выглядеть лучше, потому что на этапе изменения размера были учтены некоторые дополнительные математические вычисления. Но имейте в виду: этот дополнительный шаг фильтрации требует дополнительного времени обработки и может быстро складываться для больших изображений, что приводит к медленному изменению размера и дополнительному выделению памяти для самого фильтра.
Как правило, не рекомендуется применять эту технику к изображению, размер которого значительно превышает ваш желаемый размер, из-за дополнительных накладных расходов на фильтрацию.
Магическая комбинация
С точки зрения памяти и производительности вы можете комбинировать эти параметры для достижения наилучших результатов. (установив
inSampleSize
,inScaled
,inDensity
иinTargetDensity
флаги)inSampleSize
сначала будет применен к изображению, доведя его до следующей степени двойки БОЛЬШЕ, чем ваш целевой размер. ЗатемinDensity
&inTargetDensity
используются для масштабирования результата до точных размеров, которые вы хотите, применяя операцию фильтрации для очистки изображения.Комбинирование этих двух параметров - намного более быстрая операция, поскольку
inSampleSize
шаг уменьшит количество пикселей, к которым на итоговом шаге на основе плотности потребуется применить фильтр изменения размера.Если вам нужно подогнать изображение к определенным размерам и немного улучшить фильтрацию, то этот метод является лучшим мостом к получению нужного размера, но выполняется с помощью быстрой операции с низким объемом памяти.
Получение размеров изображения
Получение размера изображения без декодирования всего изображения Чтобы изменить размер растрового изображения, вам необходимо знать входящие размеры. Вы можете использовать
inJustDecodeBounds
флаг, чтобы помочь вам получить размеры изображения, без необходимости фактического декодирования данных пикселей.Вы можете использовать этот флаг, чтобы сначала декодировать размер, а затем вычислить правильные значения для масштабирования до вашего целевого разрешения.
источник
destination width
или dstWidth для краткостиКаким бы красивым (и точным) ни был этот ответ, он также очень сложен. Вместо того, чтобы заново изобретать колесо, подумайте о таких библиотеках, как Glide , Picasso , UIL , Ion или о любых других, которые реализуют эту сложную и подверженную ошибкам логику.
Сам Кольт даже рекомендует взглянуть на Glide и Picasso в видеоролике Pre-scaling Bitmaps Performance Patterns .
Используя библиотеки, вы можете получить каждый бит эффективности, упомянутый в ответе Кольта, но с гораздо более простыми API-интерфейсами, которые работают согласованно во всех версиях Android.
источник