踩的坑,奋笔记录一次
在开发中,使用画布,直接用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. 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;
}