Bitmap 之Mutable轉化

porterking發表於2018-02-01

Bitmap 之Mutable轉化

踩的坑,奮筆記錄一次

在開發中,使用畫布,直接用bitmap物件創立

Bitmap tempBitmap = Bitmap.createBitmap(bitmap);

Canvas canvas = new Canvas(tempBitmap);
複製程式碼

程式碼跟到這裡,發現會報IllegalSatateException 異常,看了原始碼發現:

 /**
     * Construct a canvas with the specified bitmap to draw into. The bitmap
     * must be mutable.
     *
     * <p>The initial target density of the canvas is the same as the given
     * bitmap's density.
     *
     * @param bitmap Specifies a mutable bitmap for the canvas to draw into.
     */
    public Canvas(@NonNull Bitmap bitmap) {
        if (!bitmap.isMutable()) {
            throw new IllegalStateException("Immutable bitmap passed to Canvas constructor");
        }
        throwIfCannotDraw(bitmap);
        mNativeCanvasWrapper = initRaster(bitmap);
        mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
        mBitmap = bitmap;
        mDensity = bitmap.mDensity;
}
複製程式碼

發現這裡的bitmap,需要有個屬性 mutable 為true情況下才可以成功建立。 然而並不瞭解這個屬性的相關資訊,原始碼中只有介紹

   /**
     * Returns true if the bitmap is marked as mutable (i.e.&nbsp;can be drawn into)
     */
    public final boolean isMutable() {
        return mIsMutable;
    }
複製程式碼

字面意思就是bitmap是否可變的,至少有道翻譯是這樣的。。。控制bitmap的setPixel方法能否使用,也就是外界能否修改bitmap的畫素。那什麼時候會造成bitmap不可變?焦點到Bitmap.createBitmap(bitmap);


 /**
     * Returns an immutable bitmap from the source bitmap. The new bitmap may
     * be the same object as source, or a copy may have been made.  It is
     * initialized with the same density as the original bitmap.
     */
    public static Bitmap createBitmap(Bitmap src) {
        return createBitmap(src, 0, 0, src.getWidth(), src.getHeight());
    }
複製程式碼

裡面明確指出,返回的bitmap是不可變,而且有可能是和傳入的物件是同一個。我的理解,該方法返回的bitmap mutable 是不確定的,當傳入的src的mutable 屬性為true時,經測試,返回出來的bitmap也是可操作的,既然它解釋著may be the same object as source,還是有些依據。 那既然這個方法不能保證建立的bitmap具有mutable 性,肯定有有建立具有mutable 性的方法。

 /**
     * Returns a mutable bitmap with the specified width and height.  Its
     * initial density is as per {@link #getDensity}.
     *
     * @param width    The width of the bitmap
     * @param height   The height of the bitmap
     * @param config   The bitmap config to create.
     * @throws IllegalArgumentException if the width or height are <= 0
     */
    public static Bitmap createBitmap(int width, int height, Config config) {
        return createBitmap(width, height, config, true);
    }
複製程式碼

只有這個方法返回的bitmap是mutable 的,總結下來:建立自圖形的Bitmap是immutable,而給定寬高以及其他一些引數建立的Bitmap是mutable. 當前的問題是,提供了一個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;
}
複製程式碼

###拿走不謝!

相關文章