Bitmap 之Mutable转化

2018-01-02  本文已影响0人  空而小sao

踩的坑,奋笔记录一次

在开发中,使用画布,直接用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;
}

拿走不谢!

上一篇下一篇

猜你喜欢

热点阅读