web-dev-qa-db-ja.com

BitmapFactory.decodeResourceは、可変ビットマップをAndroid 2.2で、不変ビットマップをAndroid 1.6で返します

Android 2.2を実行しているデバイスでアプリケーションを開発し、テストしています。私のコードでは、BitmapFactory.decodeResourceを使用して取得するビットマップを使用し、 bitmap.setPixels()を呼び出します。Android 1.6を実行している友人のデバイスでこれをテストすると、bitmap.setPixelsへの呼び出しでIllegalStateExceptionを取得します。オンラインのドキュメントでは、ビットマップが不変の場合、このメソッドからIllegalStateExceptionがスローされると書かれています。ドキュメントでは、decodeResourceが不変のビットマップを返すことについては何も述べていません。

2番目のBitmapオブジェクトを必要とせずにアプリケーションリソースから確実に可変ビットマップを取得するために行うことができる別の呼び出しがあります(同じサイズの可変可変ビットを作成し、それをラップするCanvasに描画できますが、私が意図した2倍のメモリを使用して同じサイズの2つのビットマップを必要としますか?

33
Rich

不変ビットマップを可変ビットマップに変換できます。

1つのビットマップのメモリのみを使用する許容可能なソリューションを見つけました。

ソースビットマップはディスク上に未加工で保存(RandomAccessFile)され(RAMメモリなし)、ソースビットマップが解放され(メモリにビットマップがなくなりました)、その後、ファイル情報が別のビットマップにロードされます。この方法により、一度にRAMメモリに1つのビットマップのみが保存されたビットマップコピーを作成できます。

完全なソリューションと実装については、こちらをご覧ください。 Android:Immutable Bitmapを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;
}
61
Derzu

可変オプションtrueを使用して、ビットマップを自分自身にコピーします。このように、余分なメモリ消費も長いコード行も必要ありません。

Bitmap bitmap= BitmapFactory.decodeResource(....);
bitmap= bitmap.copy(Bitmap.Config.ARGB_8888, true);
47
Jesus Oliva

最初にBitmapFactory.Optionsクラスをインスタンス化してBitmapFactoryのオプションを設定し、次に「inMutable」という名前のオプションフィールドをtrueに設定してから、このオプションインスタンスをdecodeResourceに渡すことができます。

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

私はパーティーに遅れていることを知っていますが、これはこの痛みを伴う厄介なAndroid=の問題を回避し、メモリ内に1つだけのコピーがある画像をトリミングおよび変更する方法です。

状況
ファイルに保存された画像のトリミングされたバージョンのピクセルを処理します。メモリの需要が高いため、このイメージの複数のコピーを常にメモリに保存することは望ましくありません。

動作するはずだったが動作しなかったもの
BitmapRegionDecoderで画像サブセクション(切り取りたいビット)を開き、BitmapFactory.option with inMutable = true、ピクセルを処理してからファイルに保存します。
アプリはAPIの最小値14とターゲット19を宣言しましたが、BitmapRegionDecoderは不変のビットマップを返し、BitMapFactory.options

機能しない

  • BitmapFactory(これはinMutableオプションを尊重します)で可変イメージを開き、それをトリミングします。すべてのトリミング手法は非命令的です(一度にメモリ内に存在するためには、イメージ全体のコピーが必要です)。上書きしてリサイクルした直後にゴミが収集された場合でも)
  • BitmapRegionDecoder(効果的にトリミングされた)で不変の画像を開き、それを変更可能な画像に変換します。使用可能なすべての手法では、メモリ内のコピーが必要です。

2014年のすばらしい回避策

  • 可変ビットマップとしてBitmapFactoryを使用してフルサイズの画像を開き、ピクセル操作を実行します
  • ビットマップをファイルに保存し、メモリからリサイクルします(まだトリミングされていません)
  • 保存されたビットマップをBitmapRegionDecoderで開き、トリミングする領域のみを開きます(ビットマップが不変かどうかは関係ありません)
  • このビットマップ(効果的にトリミングされた)をファイルに保存し、以前に保存されたビットマップ(トリミングされていない)を上書きします。

この方法を使用すると、メモリ内に1つのコピーのみを含むビットマップでトリミングおよびピクセル処理を実行できます(そのため、これらの厄介なOOMエラーを可能な限り回避できます)、RAM余分な(遅い)ファイルIOを実行する必要があります。

2
Anti Earth