Декодирование растровых изображений в Android с нужным размером
Я декодирую растровые изображения с SD-карты с помощью BitmapFactory.decodeFile
. Иногда растровые изображения больше, чем того требует приложение или что куча позволяет, поэтому я использую BitmapFactory.Options.inSampleSize
для запроса подвыборки (меньшего) растрового изображения.
Проблема в том, что платформа не обеспечивает точное значение inSampleSize, и иногда я получаю растровое изображение слишком маленькое или все еще слишком большое для доступной памяти.
Из http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize:
Примечание: декодер попытается выполнить этот запрос, но полученный битмап могут иметь разные размеры, которые именно то, что было запрошено. Кроме того, полномочия 2 часто быстрее/проще для декодера честь.
Как мне декодировать растровые изображения с SD-карты, чтобы получить растровое изображение точного размера, которое мне нужно, потребляя как можно меньше памяти для его декодирования?
Edit:
Текущий исходный код:
BitmapFactory.Options bounds = new BitmapFactory.Options();
this.bounds.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, bounds);
if (bounds.outWidth == -1) { // TODO: Error }
int width = bounds.outWidth;
int height = bounds.outHeight;
boolean withinBounds = width <= maxWidth && height <= maxHeight;
if (!withinBounds) {
int newWidth = calculateNewWidth(int width, int height);
float sampleSizeF = (float) width / (float) newWidth;
int sampleSize = Math.round(sampleSizeF);
BitmapFactory.Options resample = new BitmapFactory.Options();
resample.inSampleSize = sampleSize;
bitmap = BitmapFactory.decodeFile(filePath, resample);
}
Ответы
Ответ 1
Вы на правильном пути, однако вы пытаетесь сделать сразу две вещи: прочитайте файл и масштабируйте его до нужного размера.
Первым шагом является чтение файла в битмап немного больше, чем вам нужно, используя BitmapFactory.Options.inSampleSize, чтобы убедиться, что вы не потребляете чрезмерную память, читающую большое растровое изображение, когда все, что вам нужно, - это меньшая эскиз или разрешение экрана изображение.
Второй шаг - вызвать Bitmap.createScaledBitmap(), чтобы создать новое растровое изображение с требуемым требуемым разрешением.
Убедитесь, что вы очистили после временного растрового изображения, чтобы восстановить его память. (Либо пусть переменная выходит за пределы области действия и позволяет GC справиться с ней, либо называть ее .recycle(), если вы загружаете большое количество изображений и плотно работаете в памяти.)
Ответ 2
Вы можете использовать inJustDecodeBounds
. Установите его в TRUE
и загрузите файл как есть.
Изображение не будет загружено в память. Но свойства outheight и outwidth BitmapFactory.Options будут содержать параметры фактического размера указанного изображения. Вычислите, сколько U хотите подзапрос. то есть 1/2 или 1/4 или 1/8 и т.д. и назначьте 2/4/8 и т.д., соответственно, в inSampleSize.
Теперь установите inJustDecodeBounds
на FALSE
и вызовите BitmapFactory.decodeFile()
, чтобы загрузить изображение точного размера, рассчитанное выше.
Ответ 3
Сначала вам нужно пробовать изображение до ближайшего значения выборки, чтобы растровое изображение стало эффективным с точки зрения памяти, которое вы прекрасно описали. После этого вы можете масштабировать его, чтобы он соответствовал вашему экрану.
// This function taking care of sampling
backImage =decodeSampledBitmapFromResource(getResources(),R.drawable.back, width, height);
// This will scale it to your screen width and height. you need to pass screen width and height.
backImage = Bitmap.createScaledBitmap(backImage, width, height, false);
Ответ 4
Поскольку API уже заявляет, что размер выборки может быть или не быть выполнен.
Поэтому я думаю, что ур из-за удачи при использовании BitmapFactory
.
Но выход будет использовать ваш собственный растровый читатель. Но я бы посоветовал вам придерживаться BitmapFactory, поскольку он достаточно хорошо проверен и стандартизирован.
Я бы постарался и не слишком беспокоился о небольшом избыточном потреблении памяти, но попытаюсь выяснить, почему он не соблюдает значения. К сожалению, я не знаю об этом: (
Ответ 5
ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(filesPath), THUMBSIZE, THUMBSIZE))
Вы можете напрямую установить THUMBSIZE в зависимости от вашего требования, однако я не уверен в деталях реализации более низкого уровня, качестве вывода изображения и его производительности, но я обычно предпочитаю его, когда мне нужно показать эскиз.
Ответ 6
Для тех, кто ищет точный размер изображения из ресурсов.
Для точной ширины pass reqHight = 0 и для точного прохода высоты reqWidth = 0
public static Bitmap decodeBitmapResource(Context context, int resId, int reqWidth, int reqHeight)
{
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(context.getResources(), resId, options);
float scale = 1;
if (reqWidth != 0 && reqHeight == 0)
{
options.inSampleSize = (int) Math.ceil(options.outWidth / (float) reqWidth);
scale = (float) options.outWidth / (float) reqWidth;
}
else if (reqHeight != 0 && reqWidth == 0)
{
options.inSampleSize = (int) Math.ceil(options.outHeight / (float) reqHeight);
scale = (float) options.outHeight / (float) reqHeight;
}
else if (reqHeight != 0 && reqWidth != 0)
{
float x = (float) options.outWidth / (float) reqWidth;
float y = (float) options.outHeight / (float) reqHeight;
scale = x > y ? x : y;
}
reqWidth = (int) ((float) options.outWidth / scale);
reqHeight = (int) ((float) options.outHeight / scale);
options.inJustDecodeBounds = false;
Bitmap tmp = BitmapFactory.decodeResource(context.getResources(), resId, options);
Bitmap out = Bitmap.createScaledBitmap(tmp, reqWidth, reqHeight, false);
tmp.recycle();
tmp = null;
return out;
}
Ответ 7
Поскольку я использую inSampleSize, я больше не сталкиваюсь с проблемами памяти. Поэтому inSampleSize работает достаточно стабильно для меня. Я бы рекомендовал вам дважды проверить проблему. Размер может быть не таким же, как вы просили. Но это все равно должно быть уменьшено, и не должно быть больше "Недостаточно памяти". Я также рекомендую опубликовать фрагмент кода здесь, возможно, что-то не так.
Ответ 8
Как сказал 100rabh, если вы используете BitmapFactory, у вас действительно нет большого выбора. Однако растровое декодирование действительно простое, и в зависимости от алгоритма масштабирования, который вы хотите использовать, обычно довольно просто масштабировать его "на лету" без необходимости считывать большие части исходного растрового изображения в память (в общем, вы будете требуется только удвоить объем памяти, необходимый для 1-2 строк исходного растрового изображения).
Ответ 9
Когда установлен флаг inScaled (по умолчанию это TRUE), если inDensity и inTargetDensity не равны 0, растровое изображение будет масштабироваться в соответствии с inTargetDensity при загрузке, вместо того, чтобы полагаться на графическую систему, масштабируя ее каждый раз, когда она нарисована Холст. Поэтому просто установите значение inScaled в false.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Ответ 10
Мне удалось приблизительно получить "правильный размер", изменив размер файла декодированного изображения на 70% его размер растрового изображения.
Bitmap myBitmap = BitmapFactory.decodeFile(path);
if(myBitmap != null)
{
//reduce to 70% size; bitmaps produce larger than actual image size
Bitmap rescaledMyBitmap = Bitmap.createScaledBitmap(
myBitmap,
myBitmap.getWidth()/10*7,
myBitmap.getHeight()/10*7,
false);
image.setImageBitmap(rescaledMyBitmap);
}
Надеюсь, это поможет вам как-нибудь! Мир!