Bitmap的mutable屬性引發的血案


  最近做的一個項目,需要獲取圖片中每個像素點的信息,然后再進行一定順序的重新排列。一開始,將需要處理的圖片放到了Drawable中,通過getPixels()方法獲取了整張圖片的信息,然后處理完之后直接通過setPixels()就得到了新的圖片。很好,然后抽風將圖片放到了sdcard中,用同樣的方法處理后報異常:java.lang.IllegalStateException……

  最后發現Bitmap中有一個isMutable的字段,我的理解就是是否可變,也不知道對不對。而通過BitmapFactory.decodeResource(Resources res, int id)獲取到的bitmap是mutable的,而BitmapFactory.decodeFile(String path)獲取到的是immutable的,因此導致了前面所說的異常。(查看源碼時看到走了好多方法,並沒有仔細研究為什么兩者生成的bitmap一個是mutable,一個是immutable的,如果哪位大蝦能說清楚些希望指點一下…)

  通過查找資料,stackoverflow有位大神給出了如下的方法,將immutable的bitmap轉為了mutable的:

/**
 * 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;
}

  用這個方法轉一下,解決了異常。后來查找每個生成bitmap的方法,發現只有Bitmap createBitmap(int width, int height, Config config) 和Bitmap createBitmap(int width, int height,Config config, boolean hasAlpha)是Returns a mutable bitmap。(前者就是hasAlpha為true的后者,你懂得),故在進行setPixels前重新通過createBitmap(width, height, Config.ARGB_8888)創建了一個bitmap(他是mutable的),然后再setPixels(),也解決了異常,不過感覺后者的效率能夠高些,看第一個方法還創建了個臨時文件,感覺可能會很卡,但測試是兩個都挺卡……准備做個像ViewPager一樣的緩存,這樣可能不會那么卡吧……

  參考資料:

http://stackoverflow.com/questions/4349075/bitmapfactory-decoderesource-returns-a-mutable-bitmap-in-android-2-2-and-an-immu

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM