Bitmap
Bitmap
细说Bitmap
bitmap的六种压缩方式,Android图片压缩
1.先讲讲屏幕密度
1.1 屏幕密度
1.1.1 概念:一英尺内像素点的个数。
1.1.2计算公式
公式:根号下(长的平方+高的平方)➗屏幕尺寸
1.1.3 手机屏幕密度的表示方式和范围
使用dpi表示的实际屏幕密度 | [0dpi,160dpi) | [160dpi,240dpi) | [240dpi,320dpi) | [320,480dpi) | [480dpi,640dpi) |[640dpi,960dpi)
---|---| --- | --- |---| ---| --- |
DisplayMetrics.densityDpi获取的屏幕密度 | 160dpi | 240dpi | 320dpi | 480dpi | 640dpi |960dpi
DisplayMetrics.density获取的屏幕密度 | 0.75 | 1 | 1.5 | 2.0 | 3.0 | 4.0
使用x hpi的表示方式 | ldpi | mdpi | hdpi | xhdpi | xxhdpi | xxxhdpi|
注意:现在市面上常见的android手机的屏幕密度为xxhdpi
1.1.4 dpi与dp的关系
以mdpi为基准 ,1dp=1px。
因为hdpi为mdpi的1.5倍,所以在屏幕密度为hdpi的屏幕上 1dp=1.5px
2.Bitmap的内存大小
图片的宽 * 图片的高 * (手机屏幕密度/图片存放的分类目录)^2 * 颜色模式
2.1 缩放比例是什么
比如我们将图片放在mdpi的文件夹下,而手机的屏幕密度为xxhdpi
则缩放比例=(xxhdpi/mdpi)=3
2.2 颜色模式是什么
颜色模式有4种
ALPHA_8 只有透明通道 一个像素占8个二进制位
RGB_565 红,绿,蓝三个颜色通道分别占 5,6,5个 二进制位,一个像素点占16个二进制位
ARGB_4444 透明通道,红,绿,蓝三个颜色通道都占4个二进制位,一个像素点占16个二进制位。
ARGB_8888 透明通道,红,绿,蓝三个颜色通道都占8个二进制位,一个像素点占
32个二进制位。
BitmapFactory.Options options=new BitmapFactory.Options();
options.inPreferredConfig= Bitmap.Config.ARGB_8888;
2.3 通过代码获取Bitmap的大小
int getByteCount()
int getAllocationByteCount()
通常情况下getByteCount()和getAllocationByteCount()获取到的值相同,
但是如果通过复用的Bitmap来创建Bitmap,
getByteCount()获取到的是新的Bitmap的大小
getAllocationByteCount()获取到的是原来的Bitmap的大小
BitmapFactory.Options options=new BitmapFactory.Options();
options.inBitmap=bitmap//被复用的Bitmap
2.4实例
实例
一个48*48的图片存放到drawable-mdpi目录下,然后在各个屏幕密度下的内存大小为
分类目录 | mdpi | hdpi | xhdpi | xxhdpi
---| --- | --- |---| ---| --- |
图片所占内存 | 9216=48 * 484 | 20736=48 * 48((1.5/1)^2)*4 | 36864 =48 * 48 ((2/1)^2)4 | 82944 =48 * 48 ((3/1)^2)4
3.Bitmap的释放
- android 3.0之前
Bitmap对象存储在jvm堆中,Bitmap像素数据存放在Native中,不归jvm管理。 所以要通过Bitmap.recycle()来手动释放Bitmap像素数据
-
android3.0(包括android 3.0)之后
Bitmap对象和像素数据都存储在jvm堆中,由jvm管理,所以不必调用Bitmap.recycle()。
要促进Bitmap的回收,可以将Bitmap的引用置空。
Bitmap bitmap=...
bitmap=null;
4.创建Bitmap
4.1 BitmapFactory
从各种源中创建Bitmap
BitmapFactory.decode***
注意:
decode***得到的Bitmap默认是immutable(不可更改)的。所以有时候我们decode得到Bitmap后,如果想修改此Bitmap,会报IllegalStateException。
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.bitmaptest,options);
Bitmap bitmapNew=Bitmap.createBitmap(bitmap1);
如果想decode***得到的Bitmap是可更改的则Options选项的inMutable要设置为true(默认为false)
BitmapFactory.Options options=new BitmapFactory.Options();
options.inMutable=true;
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.bitmaptest,options);
Canvas canvas=new Canvas(bitmap1);
4.2 Bitmap
从已有Bitmap创建Bitmap(
Bitmap.createBitmap) ,api分为两类
2.2.1 根据已有的Bitmap做Matrix变化,返回的Bitmap为immutable不可修改。且如果新的Bitmap和原来的Bitamp参数一样,则新的Bitmap就是原来的Bitmap
public static Bitmap createBitmap(@NonNull Bitmap src) {
return createBitmap(src, 0, 0, src.getWidth(), src.getHeight());
}
public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height) {
return createBitmap(source, x, y, width, height, null, false);
}
/**
* Returns an immutable bitmap from subset of the source bitmap,
* transformed by the optional matrix. The new bitmap may be the
* same object as source, or a copy may have been made. It is
* initialized with the same density and color space as the original
* bitmap.
*
* If the source bitmap is immutable and the requested subset is the
* same as the source bitmap itself, then the source bitmap is
* returned and no new bitmap is created.
*
* @param source The bitmap we are subsetting
* @param x The x coordinate of the first pixel in source
* @param y The y coordinate of the first pixel in source
* @param width The number of pixels in each row
* @param height The number of rows
* @param m Optional matrix to be applied to the pixels
* @param filter true if the source should be filtered.
* Only applies if the matrix contains more than just
* translation.
* @return A bitmap that represents the specified subset of source
* @throws IllegalArgumentException if the x, y, width, height values are
* outside of the dimensions of the source bitmap, or width is <= 0,
* or height is <= 0
*/
public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height,
@Nullable Matrix m, boolean filter) {
}
2.2.2 根据已有的Bitmap做RGB更改,得到的Bitmap可以更改(mutable)
public static Bitmap createBitmap(int width, int height, @NonNull Config config) {
return createBitmap(width, height, config, true);
}
public static Bitmap createBitmap(int width, int height,
@NonNull Config config, boolean hasAlpha) {
return createBitmap(null, width, height, config, hasAlpha);
}
public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height,
@NonNull Config config, boolean hasAlpha) {
return createBitmap(display, width, height, config, hasAlpha,
ColorSpace.get(ColorSpace.Named.SRGB));
}
/**
* Returns a mutable bitmap with the specified width and height. Its
* initial density is determined from the given {@link DisplayMetrics}.
* The newly created bitmap is in the {@link ColorSpace.Named#SRGB sRGB}
* color space.
*
* @param display Display metrics for the display this bitmap will be
* drawn on.
* @param width The width of the bitmap
* @param height The height of the bitmap
* @param config The bitmap config to create.
* @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
* mark the bitmap as opaque. Doing so will clear the bitmap in black
* instead of transparent.
* @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16},
* {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the
* config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB}
* is assumed.
*
* @throws IllegalArgumentException if the width or height are <= 0, if
* Config is Config.HARDWARE (because hardware bitmaps are always
* immutable), if the specified color space is not {@link ColorSpace.Model#RGB RGB},
* if the specified color space's transfer function is not an
* {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or if
* the color space is null
*/
public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height,
@NonNull Config config, boolean hasAlpha, @NonNull ColorSpace colorSpace) {
}
项目实践
5. 将Bitmap保存到文件
质量压缩:质量压缩会减小图片在手机上的存储大小,但是不会减少对应的Bitmap占用的内存大小(见第3点,因为图片高宽没变)
/**
* Write a compressed version of the bitmap to the specified outputstream.
* If this returns true, the bitmap can be reconstructed by passing a
* corresponding inputstream to BitmapFactory.decodeStream(). Note: not
* all Formats support all bitmap configs directly, so it is possible that
* the returned bitmap from BitmapFactory could be in a different bitdepth,
* and/or may have lost per-pixel alpha (e.g. JPEG only supports opaque
* pixels).
*
* @param format The format of the compressed image
* @param quality Hint to the compressor, 0-100. 0 meaning compress for
* small size, 100 meaning compress for max quality. Some
* formats, like PNG which is lossless, will ignore the
* quality setting
* @param stream The outputstream to write the compressed data.
* @return true if successfully compressed to the specified stream.
*/
Bitmap.compress(CompressFormat format, int quality, OutputStream stream)
File file=new File(Environment.getExternalStorageDirectory()+File.separator+"new.png");
try {
OutputStream outputStream=new FileOutputStream(file);
bitmap1.compress(Bitmap.CompressFormat.PNG,100,outputStream);
outputStream.flush();
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
6. 加载超大图
参考
通过BitmapRegionDecoder只加载大图的一部分,而不把整张图都加载到内存中
而且可以通过options.inBitmap复用同一个Bimtmap进行加载。
InputStream inputStream = getAssets().open("tangyan.jpg");
//获得图片的宽、高
BitmapFactory.Options tmpOptions = new BitmapFactory.Options();
tmpOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream, null, tmpOptions);
int width = tmpOptions.outWidth;
int height = tmpOptions.outHeight;
//设置显示图片的中心区域
BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width / 2 - 100, height / 2 - 100, width / 2 + 100, height / 2 + 100), options);
mImageView.setImageBitmap(bitmap);
补充
1.BitmapFactory.Option
/**
* If set, decode methods will always return a mutable Bitmap instead of
* an immutable one. This can be used for instance to programmatically apply
* effects to a Bitmap loaded through BitmapFactory.
* Can not be set simultaneously with inPreferredConfig =
* {@link android.graphics.Bitmap.Config#HARDWARE},
* because hardware bitmaps are always immutable.
*/
@SuppressWarnings({"UnusedDeclaration"}) // used in native code
public boolean inMutable;
/**
* If set to true, the decoder will return null (no bitmap), but
* the out...
fields will still be set, allowing the caller to
* query the bitmap without having to allocate the memory for its pixels.
*/
public boolean inJustDecodeBounds;
/**
* If set to a value > 1, requests the decoder to subsample the original
* image, returning a smaller image to save memory. The sample size is
* the number of pixels in either dimension that correspond to a single
* pixel in the decoded bitmap. For example, inSampleSize == 4 returns
* an image that is 1/4 the width/height of the original, and 1/16 the
* number of pixels. Any value <= 1 is treated the same as 1. Note: the
* decoder uses a final value based on powers of 2, any other value will
* be rounded down to the nearest power of 2.
*/
public int inSampleSize;
/**
* If this is non-null, the decoder will try to decode into this
* internal configuration. If it is null, or the request cannot be met,
* the decoder will try to pick the best matching config based on the
* system's screen depth, and characteristics of the original image such
* as if it has per-pixel alpha (requiring a config that also does).
*
* Image are loaded with the {@link Bitmap.Config#ARGB_8888} config by
* default.
*/
public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
2.createScaledBitmap简便地得到目标大小的Bitmap
/**
* Creates a new bitmap, scaled from an existing bitmap, when possible. If the
* specified width and height are the same as the current width and height of
* the source bitmap, the source bitmap is returned and no new bitmap is
* created.
*
* @param src The source bitmap.
* @param dstWidth The new bitmap's desired width.
* @param dstHeight The new bitmap's desired height.
* @param filter true if the source should be filtered.
* @return The new scaled bitmap or the source bitmap if no scaling is required.
* @throws IllegalArgumentException if width is <= 0, or height is <= 0
*/
public static Bitmap createScaledBitmap(@NonNull Bitmap src, int dstWidth, int dstHeight,
boolean filter) {
Matrix m = new Matrix();
final int width = src.getWidth();
final int height = src.getHeight();
if (width != dstWidth || height != dstHeight) {
final float sx = dstWidth / (float) width;
final float sy = dstHeight / (float) height;
m.setScale(sx, sy);
}
return Bitmap.createBitmap(src, 0, 0, width, height, m, filter);
}