一、介绍

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个重载方法,且都是静态的:

  1. decodeByteArray()  字节数组
  2. decodeFile() 系统文件
  3. decodeResource() 资源文件
  4. decodeStream() 输入流
  5. decodeFileDescriptor() 本地文件,与decodeFile()相比更节省内存
  6. 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是否相同:大小、像素、格式信息