一、介绍
Bitmap就是指一张图片,图片格式一般是png、jpg或是webp。
二、Bitmap.Config
Possible bitmap configurations. A bitmap configuration describeshow pixels are stored. This affects the quality (color depth) as well as the ability to display transparent/translucent colors.
这是Bitmap中的一个内部类,类型为enum,共有6个属性:
1. ALPHA_8 8位位图,每个像素占用1个字节,只有透明度,没有颜色值。只单纯做透明度的处理。
2. RGB_565 16位位图,每个像素占用2个字节。r=5,g=6,b=5, 一个像素点 = 5+6+5 = 16。用于不透明的图片。
3. ARGB_4444 16位位图,每个像素占用2个字节。a=4,r=4,g=4,b=4, 一个像素点 = 4+4+4+4 = 16
4. ARGB_8888 32位位图,每个像素占用4个字节。a=8,r=8,g=8,b=8, 一个像素点 = 8+8+8+8 = 32 被推荐使用的一种格式,综合透明苏及色值两个通道。
5. RGBA_F16 Android 8.0 新增(更丰富的色彩表现HDR)每个像素占用8个字节,“F”代表的就是以半浮点数存储。Google的注释中这个属性非常适用于广色域宽屏和HDR(高动态范围的图片),所以它占用的内存最高,显示也是最好的。
6. HARDWARE Android 8.0 新增 (Bitmap直接存储在graphic memory)
Glide加载图片默认格式为RGB_565,Picasso加载图片默认格式为ARGB_8888。默认情况下,Glide加载图片会比Picassso占用内存大,当然清晰度会比Picasso高。
三、BitmapFactory
通过BitmapFactory从系统文件、资源、输入流、字节数组中加载到一个Bitmap对象。BitmapFactory共有6个重载方法,且都是静态的:
- decodeByteArray() 字节数组
- decodeFile() 系统文件
- decodeResource() 资源文件
- decodeStream() 输入流
- decodeFileDescriptor() 本地文件,与decodeFile()相比更节省内存
- decodeResourceStream() 资源文件,与decodeResource()相比更节省内存
四、Bitmap的高效加载
思路:利用BitmapFactory.options加载图片实际所需要的尺寸
BitmapFactory.options是一个静态内部类,且这个类只有一个方法:requestCancelDecode(),剩下的全部是一些常量值
BitmapFactory.options缩放图片主要采用inSample采样率
inSample = 1 采样后的宽高是原始宽高
inSample > 1 例如2,宽高均为原图的宽高的1/2
inSample的值最小为1,小于一这个值是无效的。
BitmapFactory.options中的常量值:
boolean inJustDecodeBounds | 是否只扫描轮廓 |
int inSample | 采样率 |
Bitmap.Config inPreferredConfig | 格式、色彩模式 |
int outWidth | bitmap的宽 |
int outHeight | bitmap的高 |
boolean inDither | 防抖动,默认false |
int inDensity | 像素密度 |
boolean inScaled | 是否可以缩放,默认true |
boolean inMutable | 是否可变,设为ture,decode转换方法返回的结果全部可改变 |
获取采样率的流程,一资源文件为例:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; //BitmapFactory只会解析图片的原始信息,并不会加载图片
BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher, options);
options.inJustDecodeBounds = false;
Bitmap simplingAftetBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher, options);
img1.setImageBitmap(simplingAftetBitmap);
上面代码中,我们先创建了一个Options对象,然后设置inJustDecodeBounds=true,只扫描轮廓,然后用BitmapFactory.decodeResore获取Bitmap时就不会加载图片。之后通过options.outWidth和optionsHeight获取到图片的真正宽度与高度。
五、尺寸压缩
/**
* 图片尺寸压缩实例
*/
private Bitmap imgComPress(int width, int height) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; //只查看图片信息,不加载图片
BitmapFactory.decodeResource(getResources(), R.drawable.img, options);
options.inPreferredConfig = Bitmap.Config.RGB_565; //通过Options,把图片的格式设置为RGB_565,内存占用会少一般较ARGB_8888
options.inSampleSize = countInSample(options, width, height);
options.inJustDecodeBounds = false;
Bitmap comPressAfter = BitmapFactory.decodeResource(getResources(), R.drawable.img, options);
return comPressAfter;
}
第一步:获取inSample
创建一个Options,并将inJustDecodeBounds设置为true,只获取图片轮廓,不加载。设置图片的加载格式为RGB_555。通过对图片的宽高与view的宽高计算出采样率inSample,并通过options.inSanpleSize设置。最后得到一个压缩之后的图片。
/**
* 计算inSample
*/
private int countInSample(BitmapFactory.Options options, int width, int height) {
int rawWidth = options.outWidth; //图片的原始宽
int rawHeight = options.outHeight; //图片的原始高
int inSample = 1;
if (rawWidth > width || rawHeight > height) { //如果图片的宽高大于view的宽高才压缩
20行 int halfWidth = rawWidth / 2; //避免过渡压缩 例如图片宽高为250 250 view宽高为200 200
int halfHeight = rawHeight / 2;
while ((halfWidth / inSample) >= width && (halfHeight / inSample) >= height) {
inSample = inSample * 2;
}
}
return inSample;
}
第二步:计算inSample
首先呢,我们通过options.outWidth和options.outHeight拿到图片的原始宽高。图片的宽高大于view的宽高时就进行压缩。我们看到20行为什么还要除以2呢,直接比较不就行了吗?举个例子:图片的原始宽高是250*250 view:200*200 ,那么在这种情况下,图片还需要尺寸压缩吗?答案是不需要的,因为inSample的值一般是2的倍数,250/2 = 125,压缩之后会导致图片严重变形的。
img1.postDelayed(new Runnable() {
@Override
public void run() {
int ivWidth = img1.getWidth();
int ivHeight = img1.getHeight();
img1.setImageBitmap(imgComPress(ivWidth,ivHeight));
}
}, 1000);
第三步:调用
我们如果要是在onCreate方法中获取view的宽高时,要使用postDelayed延时,否则获取到的值为0。
六、质量压缩
public Bitmap quality() {
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img3);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024 * 8);
srcBitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565; //mipmap文件夹中的图片,不能通过这种方式改变Config,设置了也不生效
Bitmap bitmap = BitmapFactory.decodeByteArray(byteArrayOutputStream.toByteArray(), 0, byteArrayOutputStream.size(), options);
Log.i("sss", "quality: 与原图片信息是否相同:"+bitmap.sameAs(srcBitmap));
return bitmap;
}
质量压缩不会减少图片的像素,它是在保持像素的前提下改变图片的位深及透明度,来达到压缩图片的目的,图片的长,宽,像素都不会改变,那么bitmap所占内存大小是不会变的。
我们可以看到有个参数:quality,可以调节你压缩的比例,但是还要注意一点就是,质量压缩对png格式这种图片没有作用,因为png是无损压缩。
七、bitmap.copy()
private Bitmap copy() {
Bitmap oneBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img);
Bitmap twoBitmap = oneBitmap.copy(Bitmap.Config.RGB_565, true);//配置信息、是否支持可改变可写入
return twoBitmap;
}
将一个bitmap的信息copy到另一个bitmap当中。用到copy方法,参数是图片格式、是否支持改变此图片。返回值是一个Bitmap。
八、Bitmap.createBitmap()
此方法共有9个重载方法:
1.createBitmap(@NonNull Bitmap source, int x, int y, int width, int height,@Nullable Matrix m, boolean filter)
2.createBitmap(@NonNull Bitmap source, int x, int y, int width, int height)
3.Bitmap.createBitmap(bitmap)
- source 原始图片
- x 第一个像素的x轴坐标
- y 第一个像素的y轴坐标
- width 图片宽度
- height 图片高度
- m 矩阵
- filter 是否过滤资源bitmap
- 返回一个不可改变的bitmap
4. createBitmap(int width, int height, @NonNull Config config)
5. createBitmap(@Nullable DisplayMetrics display, int width,int height, @NonNull Config config)
- display 原始密度
- config 图片格式
- 返回一个可改变的bitmap
6. createBitmap(new DisplayMetrics(), int colors[],int offset, int stride, int width, int height, Config config);
- display 原始密度
- colors 初始化颜色的数组,数量大于width*height
- offset 偏移量
- stride 对象的跨距宽度,也称为扫描宽度 具体参考:
- 返回一个不可改变的bitmap
7. createBitmap(@NonNull @ColorInt int[] colors,int width, int height, Config config)
8. createBitmap(DisplayMetrics display, int colors[], int width, int height, Config config)
9. createBitmap(int colors[], int offset, int stride, int width, int height, Config config)
九、其它方法
- srcBitmap.isRecycled() 是否回收了内存
- srcBitmap.isMutable() 是否可以改变
- srcBitmap.sameAs(bitmap) 判断两个bitmap是否相同:大小、像素、格式信息