BitmapFactory.decodeResource returns a mutable Bitmap in Android 2.2 and an immutable Bitmap in Android 1.6
BitmapFactory.decodeResource возвращает изменяемое растровое изображение в Android 2.2 и неизменяемое растровое изображение в Android 1.6
Я разрабатываю приложение и тестирую его на своем устройстве под управлением Android 2.2. В моем коде я использую растровое изображение, которое я извлекаю с помощью BitmapFactory.decodeResource , и я могу вносить изменения, вызывая bitmap.setPixels() в нем. Когда я тестирую это на устройстве друга под управлением Android 1.6, я получаю IllegalStateException в вызове bitmap.setPixels. В онлайн-документации говорится, что из этого метода выбрасываетсяIllegalStateException, когда растровое изображение является неизменяемым. В документации ничего не говорится о decodeResource возврате неизменяемого растрового изображения, но очевидно, что так и должно быть.
Есть ли другой вызов, который я могу выполнить, чтобы надежно получить изменяемое растровое изображение из ресурса приложения без использования второго Bitmap объекта (я мог бы создать изменяемое растровое изображение того же размера и нарисовать его на холсте, обернув его, но для этого потребовалось бы два растровых изображения одинакового размера, используя в два раза больше памяти, чем я предполагал)?
Переведено автоматически
Ответ 1
Вы можете преобразовать свое неизменяемое растровое изображение в изменяемое растровое изображение.
Я нашел приемлемое решение, которое использует память только одного растрового изображения.
Исходное растровое изображение сохраняется в необработанном виде (RandomAccessFile) на диске (без оперативной памяти), затем исходное растровое изображение освобождается (теперь в памяти нет растрового изображения), и после этого информация о файле загружается в другое растровое изображение. Таким образом, можно создать растровую копию, содержащую только одно растровое изображение, хранящееся в оперативной памяти за каждый раз.
Я добавляю улучшение в это решение, которое теперь работает с любыми типами растровых изображений (ARGB_8888, RGB_565 и т.д.) И удаляет временный файл. Смотрите мой метод.:
/** * Converts a immutable bitmap to a mutable bitmap. This operation doesn't allocates * more memory that there is already allocated. * * @param imgIn - Source image. It will be released, and should not be used more * @return a copy of imgIn, but muttable. */ publicstatic Bitmap convertToMutable(Bitmap imgIn) { try { //this is the file going to use temporally to save the bytes. // This file will not be a image, it will store the raw image data. Filefile=newFile(Environment.getExternalStorageDirectory() + File.separator + "temp.tmp");
//Open an RandomAccessFile //Make sure you have added uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" //into AndroidManifest.xml file RandomAccessFilerandomAccessFile=newRandomAccessFile(file, "rw");
// get the width and height of the source bitmap. intwidth= imgIn.getWidth(); intheight= imgIn.getHeight(); Configtype= imgIn.getConfig();
//Copy the byte to the file //Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888; FileChannelchannel= randomAccessFile.getChannel(); MappedByteBuffermap= channel.map(MapMode.READ_WRITE, 0, imgIn.getRowBytes()*height); imgIn.copyPixelsToBuffer(map); //recycle the source bitmap, this will be no longer used. imgIn.recycle(); System.gc();// try to force the bytes from the imgIn to be released
//Create a new bitmap to load the bitmap again. Probably the memory will be available. imgIn = Bitmap.createBitmap(width, height, type); map.position(0); //load it back from temporary imgIn.copyPixelsFromBuffer(map); //close the temporary file and channel , then delete that also channel.close(); randomAccessFile.close();
Скопируйте растровое изображение в себя с изменяемой опцией true . Таким образом, не требуется ни дополнительного потребления памяти, ни длинных строк кода.
Сначала мы можем задать параметры для BitmapFactory, создав экземпляр BitmapFactory.Класс Options, а затем установите для поля options с именем 'inMutable' значение true, а затем передайте этот экземпляр options в decodeResource.
Вот созданное мной решение, которое использует внутреннее хранилище и не требует никаких новых разрешений, основанное на идее "Derzu" и том факте, что, начиная с honeycomb, это встроено в :
/**decodes a bitmap from a resource id. returns a mutable bitmap no matter what is the API level.<br/> might use the internal storage in some cases, creating temporary file that will be deleted as soon as it isn't finished*/ @TargetApi(Build.VERSION_CODES.HONEYCOMB) publicstatic Bitmap decodeMutableBitmapFromResourceId(final Context context, finalint bitmapResId) { finalOptionsbitmapOptions=newOptions(); if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) bitmapOptions.inMutable = true; Bitmapbitmap= BitmapFactory.decodeResource(context.getResources(), bitmapResId, bitmapOptions); if (!bitmap.isMutable()) bitmap = convertToMutable(context, bitmap); return bitmap; }
другой альтернативой является использование JNI для помещения в него данных, переработки исходного растрового изображения и использования данных JNI для создания нового растрового изображения, которое будет (автоматически) изменяемым, поэтому вместе с моим решением JNI для растровых изображений можно сделать следующее: