decodeResource在Android 2.2中返回可变位图,在Android 1.6中返回不变位图

brc7rcf0  于 2022-12-28  发布在  Android
关注(0)|答案(7)|浏览(148)

我正在开发一个应用程序,并在我运行Android 2.2的设备上进行测试。在我的代码中,我使用了一个使用BitmapFactory.decodeResource检索的位图,并且我能够通过调用bitmap.setPixels()对它进行更改。当我在朋友运行Android 1.6的设备上进行测试时,我在调用bitmap.setPixels时得到了一个IllegalStateException。在线文档说当位图是不可变的时,这个方法会抛出一个IllegalStateException。文档没有提到decodeResource返回一个不可变的位图,但很明显,这是必须的。
是否有其他调用可以从应用程序资源中可靠地获取可变位图,而不需要第二个Bitmap对象(我可以创建一个相同大小的可变位图,并将其绘制到Canvas中进行 Package ,但这将需要两个相同大小的位图,占用的内存是我预期的两倍)?

llycmphe

llycmphe1#

可以将不可变位图转换为可变位图。
我找到了一个可以接受的解决方案,它只使用一个位图的内存。
一个源位图被原始保存(RandomAccessFile)在磁盘上(没有内存),然后源位图被释放,(现在内存中没有位图),然后文件信息被加载到另一个位图中。这种方法可以使位图副本每次只在内存中存储一个位图。
在此处查看完整的解决方案和实施:Android: convert Immutable Bitmap into Mutable
我对这个解决方案做了改进,现在可以处理任何类型的位图(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;
}
6mw9ycah

6mw9ycah2#

使用mutable选项true将位图复制到自身。这样既不需要额外的内存消耗也不需要长代码行。

Bitmap bitmap= BitmapFactory.decodeResource(....);
bitmap= bitmap.copy(Bitmap.Config.ARGB_8888, true);
cmssoen2

cmssoen23#

我们可以先通过示例化BitmapFactory.Options类来设置BitmapFactory的选项,然后将名为“inMutable”的选项字段设置为true,然后将此选项示例传递给decodeResource。

BitmapFactory.Options opt = new BitmapFactory.Options();
 opt.inMutable = true;
 Bitmap bp = BitmapFactory.decodeResource(getResources(), R.raw.white, opt);
x33g5p2x

x33g5p2x4#

下面是我创建的一个解决方案,它使用内部存储,不需要任何新的权限,基于“Derzu”的想法,以及从Hive开始,这是内置的事实:

/**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数据创建一个新位图,该位图将是(自动)可变的,因此与my JNI solution for bitmaps一起,可以执行以下操作:

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和更高版本上运行得很好。

vm0i2vca

vm0i2vca5#

我知道我迟到了,但这就是我们如何避免这个痛苦恼人的Android问题,并裁剪和修改一个图像只有一个副本在内存中。

情况

我们要处理保存到文件中的图像的裁剪版本的像素。2由于内存需求很高,我们不希望在任何给定时间在内存中拥有该图像的多个副本。

本应成功却失败了

使用BitmapRegionDecoder打开图像子部分(我们想要裁剪到的位),使用inMutable = true传入BitmapFactory.option,处理像素,然后保存到文件。
尽管我们的应用程序声明API最小值为14,目标值为19,但BitmapRegionDecoder返回的是一个不可变的位图,实际上忽略了我们的BitMapFactory.options
"什么行不通"

  • 使用BitmapFactory(这与我们的inMutable选项有关)打开可变图像并裁剪它:所有裁剪技术都是非强制性的(要求整个图像的副本同时存在于内存中,即使在覆盖和回收之后立即收集垃圾)
  • BitmapRegionDecoder(有效裁剪)打开不可变图像并将其转换为可变图像;所有可用的技术再次需要存储器中的副本。
    2014年最佳解决方案
  • BitmapFactory打开全尺寸图像作为可变位图,并执行我们的像素操作
  • 保存位图到文件并从内存中回收(它仍然是未裁剪的)
  • BitmapRegionDecoder打开保存的位图,只打开要裁剪的区域(现在我们不关心位图是否不可变)
  • 保存这位图(这已经有效地被裁剪)到文件,覆盖这以前保存的位图(这是un-cripped)

使用这种方法,我们可以裁剪位图并在位图上执行像素处理,而内存中只有一个副本(因此我们可以尽可能避免那些讨厌的OOM错误),因为我们必须执行额外的(缓慢的)文件IO,所以用RAM来换取时间。

gzszwxb4

gzszwxb46#

这是因为您希望通过调用setHeight()setWidth()来调整位图的大小
调整任何位图或可绘制对象(Png、Svg、矢量等)的大小

public Bitmap editMyBitmap(int drawableId, int newHeight, int newWidth) {
        Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), drawableId);
        myBitmap = Bitmap.createScaledBitmap(myBitmap, newWidth, newHeight, false);
        return myBitmap;
    }

用法示例:

Bitmap facebookIcon = editMyBitmap(R.drawable.facebookImage);
// now use it anywhere
imageView.setBitmapImage(facebookIcon); 
canvas.drawBitmap(facebookIcon, 0, 0, null);
yqyhoc1h

yqyhoc1h7#

我知道这个问题已经解决了,但是:
第一个月

相关问题