Android

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) на диске (без оперативной памяти), затем исходное растровое изображение освобождается (теперь в памяти нет растрового изображения), и после этого информация о файле загружается в другое растровое изображение. Таким образом, можно создать растровую копию, содержащую только одно растровое изображение, хранящееся в оперативной памяти за каждый раз.

Смотрите полное решение и реализацию здесь: Android: преобразование неизменяемого растрового изображения в изменяемое

Я добавляю улучшение в это решение, которое теперь работает с любыми типами растровых изображений (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.
*/

public static 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.
File file = new File(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
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");

// get the width and height of the source bitmap.
int width = imgIn.getWidth();
int height = imgIn.getHeight();
Config type = imgIn.getConfig();

//Copy the byte to the file
//Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888;
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = 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();

// delete the temp file
file.delete();

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

return imgIn;
}
Ответ 2

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

Bitmap bitmap= BitmapFactory.decodeResource(....);
bitmap= bitmap.copy(Bitmap.Config.ARGB_8888, true);
Ответ 3

Сначала мы можем задать параметры для BitmapFactory, создав экземпляр BitmapFactory.Класс Options, а затем установите для поля options с именем 'inMutable' значение true, а затем передайте этот экземпляр options в decodeResource.

 BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inMutable = true;
Bitmap bp = BitmapFactory.decodeResource(getResources(), R.raw.white, opt);
Ответ 4

Вот созданное мной решение, которое использует внутреннее хранилище и не требует никаких новых разрешений, основанное на идее "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)
public static Bitmap decodeMutableBitmapFromResourceId(final Context context, final int bitmapResId) {
final Options bitmapOptions = new Options();
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB)
bitmapOptions.inMutable = true;
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), bitmapResId, bitmapOptions);
if (!bitmap.isMutable())
bitmap = convertToMutable(context, bitmap);
return bitmap;
}

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public static Bitmap convertToMutable(final Context context, final Bitmap imgIn) {
final int width = imgIn.getWidth(), height = imgIn.getHeight();
final Config type = imgIn.getConfig();
File outputFile = null;
final File outputDir = context.getCacheDir();
try {
outputFile = File.createTempFile(Long.toString(System.currentTimeMillis()), null, outputDir);
outputFile.deleteOnExit();
final RandomAccessFile randomAccessFile = new RandomAccessFile(outputFile, "rw");
final FileChannel channel = randomAccessFile.getChannel();
final MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, imgIn.getRowBytes() * height);
imgIn.copyPixelsToBuffer(map);
imgIn.recycle();
final Bitmap result = Bitmap.createBitmap(width, height, type);
map.position(0);
result.copyPixelsFromBuffer(map);
channel.close();
randomAccessFile.close();
outputFile.delete();
return result;
} catch (final Exception e) {
} finally {
if (outputFile != null)
outputFile.delete();
}
return null;
}

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

Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
final JniBitmapHolder bitmapHolder=new JniBitmapHolder(bitmap);
bitmap.recycle();
bitmap=bitmapHolder.getBitmapAndFree();
Log.d("DEBUG",""+bitmap.isMutable()); //will return true

однако я не уверен, каковы минимальные требования уровня API. это очень хорошо работает в API 8 и выше.

2023-05-21 14:40 java android bitmap