Зачем столько памяти?

У меня есть растровое изображение размером 1000x1500 пикселей, из которого я хочу сделать изменяемую копию в Android.

Когда я запускаю следующий код...

// int width = original.getWidth(); // 1000px
// int height = original.getHeight(); // 1500px
final Bitmap result = original.copy(original.getConfig(), true);
original.recycle();

... Я получаю OutOfMemoryError в строке copy:

java.lang.OutOfMemoryError: bitmap size exceeds VM budget
ERROR/GraphicsJNI(419): VM won't let us allocate 6000000 bytes

Почему для инструкции копирования требуется 6 МБ (!) для растрового изображения размером 1000x1500 пикселей?

Как я могу создать изменяемое растровое изображение из неизменяемого более эффективным с точки зрения памяти способом?

Редактировать

BitmapFactory возвращает неизменяемые растровые изображения. По-видимому, единственный способ создать изменяемое растровое изображение из неизменного — это скопировать его в новое изменяемое растровое изображение. В случае растрового изображения 1000 x 1500 для этого, очевидно, требуется 12 МБ (1000 x 1500 x 4 x 2), что вызывает ошибку OutOfMemoryError на большинстве устройств Android.

Значит ли это, что эта проблема неразрешима в Android?


person hpique    schedule 18.01.2011    source источник
comment
1000 пикселей на 1500 пикселей на 4 байта на пиксель разделить на (1024*1024)... около 5,7 МБ...   -  person asveikau    schedule 18.01.2011


Ответы (4)


Чтобы ответить на ваш первый вопрос:

1000*1500*32/8=6000000

(32 бита/пиксель для информации о цвете)

Чтобы ответить на ваш второй вопрос: вам нужно уменьшить размер изображения, либо обработав его кусками, либо уменьшив разрешение или глубину цвета.

person cdonner    schedule 18.01.2011
comment
Спасибо. Значит, нет способа сделать растровое изображение изменяемым, не требуя удвоения его памяти? - person hpique; 18.01.2011
comment
Я не знаю ни одного. См. также это: groups.google.com /group/android-developers/browse_thread/thread/ - person cdonner; 18.01.2011
comment
Как уменьшить глубину цвета? - person hpique; 18.01.2011
comment
И да, эта ссылка описывает ту же проблему, что и у меня. - person hpique; 18.01.2011

cdonner направил меня в правильном направлении.

Оказывается, исходное растровое изображение использовало ARGB_8888, что требует 32 бита на пиксель, а это больше, чем нужно для этого конкретного приложения.

Изменение растрового изображения config на RGB_565, который требует 16 бит на пиксель, снижает потребление памяти вдвое.

person hpique    schedule 18.01.2011

Существует хитрый обходной путь, который я использовал, чтобы избежать OutOfMemoryError. Я зарегистрировал приемник, чтобы он работал в другом процессе:

<receiver android:name=".ImageTransformReceiver"
             android:exported="true" android:process=":imageTransformProcess"/>

Внутри ресивера я делаю дорогостоящие операции с памятью (загрузка двух больших изображений и объединение их в одно). Я записываю результирующее изображение в файл и отправляю широковещательную рассылку обратно в основной процесс, ссылаясь на путь к файлу результирующего изображения в Intent.

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

person Nikolay Chorniy    schedule 16.04.2011

На уровне API 11 BitmapFactory.Options имеет логическое значение «inMutable», которое можно настроить для создания изменяемых растровых изображений.

Хотя это не меняет использование памяти для отдельного растрового изображения, это избавит вас от необходимости хранить две копии в памяти.

person Captain Blammo    schedule 28.03.2012