2015-08-20 4 views
107

Я создаю приложение для интенсивного изображения, в котором изображения отправляются с сервера на устройство. Когда устройство имеет меньшее разрешение экрана, мне нужно изменить размеры растровых изображений на устройстве, чтобы они соответствовали их размерам дисплея.Самый эффективный способ изменения размера растровых изображений на Android?

Проблема заключается в том, что с помощью createScaledBitmap заставляет меня работать на много вышедших из памяти ошибок после изменения орды изображений.

Каков наиболее эффективный способ изменения размера растровых изображений на Android?

+7

Возможно, ваш сервер не отправит правильный размер, чтобы сохранить оперативную память и пропускную способность вашего клиента !? – James

+2

Это справедливо только в том случае, если у меня был ресурс сервера, у него был доступный компонент вычисления, и во всех случаях он мог предсказать точные размеры изображений для пропорций, которые он еще не видел. Поэтому, если вы загружаете контент ресурсов из стороннего CDN (например, я), он не работает :( –

ответ

154

Этот ответ суммируется из Loading large bitmaps Efficiently , который объясняет, как использовать inSampleSize, чтобы загрузить вниз масштабируется версию растровый .

В частности, Pre-scaling bitmaps объясняет детали различных методов , как их сочетать и которые являются наиболее эффективными с точки зрения памяти.

Есть три доминирующих способа изменить размер растрового изображения на Android, которые имеют различные свойства памяти:

createScaledBitmap API

Этот API будет принимать в существующем растровом изображении, а также создать новое растровое изображение с точные размеры, которые вы выбрали.

С положительной стороны вы можете получить именно тот размер изображения, который вы ищете (независимо от того, как он выглядит). Но недостатком, является то, что этот API требует существующего растрового изображения, чтобы работать. Это означает, что изображение должно быть загружено, декодировано и создано растровое изображение, прежде чем сможет создать новую меньшую версию. Это идеально подходит для получения ваших точных измерений, но ужасно с точки зрения дополнительных издержек памяти. Таким образом, это добрейшей сделки прерыватель для большинства разработчиков приложений, которые, как правило, быть памятью сознательного

inSampleSize flag

BitmapFactory.Options имеет свойство отмечен как inSampleSize что будет изменить размер изображения при декодировании его, чтобы избежать необходимость декодировать временное растровое изображение. Это целочисленное значение, используемое здесь, будет загружать изображение с уменьшенным размером 1/x. Например, установка inSampleSize в 2 возвращает изображение, которое в два раза меньше размера, а для параметра «4» - изображение, размер которого равен 1/4. В основном размеры изображений всегда будут немного меньше, чем ваш размер источника.

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

  • Это не дает вам точные решения. Это уменьшает размер вашего растрового изображения на некоторую мощность 2.

  • Он не производит наилучшее качество resize. Большинство фильтров изменения размера создают хорошие изображения, считывая блоки пикселей, а затем взвешивая их, чтобы создать размер пикселя, о котором идет речь.inSampleSize избегает всего этого, просто считывая каждые несколько пикселей. Результат довольно впечатляющий, и низкая память, но качество страдает.

Если вы имеете дело только с сокращения изображения каким-то размером pow2, и фильтрация не является проблема, то вы не можете найти больше памятей эффективного (или производительности эффективно), чем метод inSampleSize.

inScaled, inDensity, inTargetDensity flags

Если вам нужно масштабировать изображение до размера, это не равно степени двойки, то вы будете нуждаться в inScaled, inDensity и inTargetDensity флаги BitmapOptions. Когда установлен флаг inScaled, система выведет значение масштабирования для применения к вашему растровому изображению, разделив inTargetDensity на значения inDensity.

mBitmapOptions.inScaled = true; 
mBitmapOptions.inDensity = srcWidth; 
mBitmapOptions.inTargetDensity = dstWidth; 

// will load & resize the image to be 1/inSampleSize dimensions 
mCurrentBitmap = BitmapFactory.decodeResources(getResources(), 
     mImageIDs, mBitmapOptions); 

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

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

Магия Комбинация

Из памяти и точки зрения производительности, вы можете комбинировать эти варианты для достижения наилучших результатов. (Установки inSampleSize, inScaled, inDensity и inTargetDensity флаги)

inSampleSize сначала будет применен к изображению, получить его к следующей мощности из-два больше, чем ваш размер цели. Затем inDensity & inTargetDensity используются для масштабирования результата до требуемых размеров, применяя операцию фильтрации для очистки изображения.

Объединение этих двух функций намного быстрее, так как шаг inSampleSize уменьшит количество пикселей, на которые будет приходиться результат, основанный на плотности, для применения фильтра изменения размера.

mBitmapOptions.inScaled = true; 
mBitmapOptions.inSampleSize = 4; 
mBitmapOptions.inDensity = srcWidth; 
mBitmapOptions.inTargetDensity = dstWidth * mBitmapOptions.inSampleSize; 

// will load & resize the image to be 1/inSampleSize dimensions 
mCurrentBitmap = BitmapFactory.decodeFile(fileName, mBitmapOptions); 

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

Попадая размеры изображения

Получение размера изображения без декодирования всего изображения Для того, чтобы уменьшить размер растрового изображения, вы должны знать, входящих размеры. Вы можете использовать флаг inJustDecodeBounds, чтобы помочь вам получить размеры изображения, без необходимости фактического декодирования данных пикселя.

// Decode just the boundaries 
mBitmapOptions.inJustDecodeBounds = true; 
BitmapFactory.decodeFile(fileName, mBitmapOptions); 
srcWidth = mBitmapOptions.outWidth; 
srcHeight = mBitmapOptions.outHeight; 


//now go resize the image to the size you want 

Вы можете использовать этот флаг для первоначального декодирования размера, а затем рассчитать правильные значения для масштабирования до целевого разрешения.

+1

было бы здорово, если бы вы могли рассказать нам, что такое dstWidth? – k0sh

+0

@ k0sh dstWIdth - это ширина ImageView for где его собираются, т. е. 'ширина назначения 'или dstWidth для коротких – tyczj

+0

@tyczj спасибо за ответ, я знаю, что это такое, но некоторые могут не знать об этом, и поскольку Колт, который действительно ответил на этот вопрос, возможно, он мог бы объяснить это так что люди не путаются. – k0sh

11

Как хороший (и точный), как этот ответ, это также очень сложно. Вместо того, чтобы повторно изобретать колесо, рассмотрите библиотеки, например, Glide, Picasso, , Ion или любое количество других пользователей, которые реализуют эту сложную и подверженную ошибкам логику для вас.

Колт сам даже рекомендует взглянуть на Glide и Picasso в Pre-scaling Bitmaps Performance Patterns Video.

Используя библиотеки, вы можете получить каждый бит эффективности, упомянутый в ответе Кольта, но с гораздо более простыми API-интерфейсами, которые работают последовательно в каждой версии Android.

Смежные вопросы