Bitmap加载原理

112 阅读4分钟

code 方法区 stack 栈区 graphics opengl gup内存 native java 堆区

图像展示原理

  • 屏幕图像是由一个一个像素点拼接成的
  • 1080*1920 px 代表 每一行有1080个像素点 有1920行
  • 屏幕图像是由一个一个像素点的不同颜色拼接而成
  • 每个像素点的颜色大小由展示类型决定
  • bitmap 展示内容就是一组颜色的二位数组

image.png

图像大小换算

A:透明度 R:红色 G:绿 B:蓝

Bitmap.Config ARGB_4444:每个像素占四位,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位 

Bitmap.Config ARGB_8888:每个像素占四位,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位=4字节

Bitmap.Config RGB_565:每个像素占四位,即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位

Bitmap.Config ALPHA_8:每个像素占四位,只有透明度,没有颜色。
//计算大小
假设有一张480x800的图片,如果格式为ARGB_8888,那么将会占用1500KB的内存。
480*800*4=1536KB

Bitmap 对象的产生

Bitmap 对象大小只跟width、height、colorType有关

//创建Bitmap对象
//传入的宽高影响Bitmap大小
Bitmap bm = Bitmap.createBitmap(dest, width, height, Config.RGB_565);

//内部通过native 层产生对象. 
Bitmap bm = nativeCreate(colors, offset, stride, width, height,config.nativeInt, false, null, null);
// 调用链nativeCreate -- > Bitmap_creator
//创建SKBitmao 对象
SkBitmap bitmap;
设置参数
bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType, colorSpace));
//申请bitmap大小
sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap, NULL);
if (!nativeBitmap) {
    return NULL;
}

  • 源码JNI 地址 image.png

Bitmap.java

  • 无法获取到真正的bitmap native层只返回了bitmap对象的指针地址
mNativePtr = nativeBitmap;
  • java层将long 类型指针地址传递给native 可以获取到对象地址

native 层Bitmap 显示原理

//绘制操作
    AndroidBitmapInfo info;
    //根据java层的bitmap对象获取到native需要的AndroidBitmapInfo对象内的信息
    AndroidBitmap_getInfo(env, bitmap, &info);
    int width = info.width;
    int height = info.height;
    LOGE("宽%d  高%d:  ",width,height);
    //此处相等于是二级指针1080*1920,对二级指针每一个像素点复制则可以绘制出当前像素矩阵图像
    int *pixels=NULL;
//2   我们要拿到native层的那个指针,指针代表需要展示图片的第一个像素点的指针
    AndroidBitmap_lockPixels(env, bitmap, reinterpret_cast<void **>(&pixels));
//        pixels  hua

    int* px = (int *)pixels;
    int *line;
    for (int y =0 ; y<frameInfo.Height; ++y) {
        line = px;
        for (int x =  0; x < frameInfo.Width ; ++x) {
//            里面写出   数据
            pointPixel = (y ) * frameInfo.Width + (x);
            gifByteType=  savedImage.RasterBits[pointPixel];
            gifColorType= colorMapObject->Colors[gifByteType];
            //对矩阵的每一个像素点赋值
            line[x] = argb(255, gifColorType.Red, gifColorType.Green, gifColorType.Blue);
        }
        px = px + info.stride / 4;
    }
    AndroidBitmap_unlockPixels(env, bitmap);

gif

  • gif 不是一张一张图片叠加
  • gif 是第一帧颜色值数据和后面每一帧颜色差异值的集合体。
  • native 加载 gif 内存开销小
//    取出待绘制的第几帧
    SavedImage savedImage = gifFileType->SavedImages[gifBean->current_frame];

//    savedImage数据 怎么 最大难题

    GifImageDesc frameInfo=  savedImage.ImageDesc;
//    颜色表
    //   数组下标 索引的
    int  pointPixel;
//颜色表的索引
    GifByteType  gifByteType;
    //颜色
    GifColorType gifColorType;
    ColorMapObject *colorMapObject = frameInfo.ColorMap;
    int* px = (int *)pixels;
    int *line;
    for (int y =0 ; y<frameInfo.Height; ++y) {
        line = px;
        for (int x =  0; x < frameInfo.Width ; ++x) {
//            里面写出   数据
            pointPixel = (y ) * frameInfo.Width + (x);
            gifByteType=  savedImage.RasterBits[pointPixel];
            //取出map中的颜色值
            gifColorType= colorMapObject->Colors[gifByteType];
            //对应像素点上复制
            line[x] = argb(255, gifColorType.Red, gifColorType.Green, gifColorType.Blue);
        }
        px = px + info.stride / 4;
    }

图片压缩手段

  • 尺寸压缩
  • 质量压缩
  • Skia (封装的jpej)

    Android系统在7.0版本之前内部使用的是libjpeg非turbo版,并且为了性能关闭了Huffman编码。在7.0之后的系统内部使用了libjpeg-turbo库并且启用Huffman编码

  • jpej引擎
    • 自定义图片压缩,实现Luban原理
    • jpej使用 参考simple
    • 抽离a、r、g、b 配套 jpej 进行huffman进行图片压缩
  • Luban
  • huffman无损原理与使用

Bitmap优化与原理

  • Bitmap大小只与宽高有关可以尽量复用
  • Bitmap 内存属于Native层
  • 尺寸优化
  • 内存优化
  • 大图原理:加载显示区域大小的分辨率图片
    //获取Bitmap解码器
    mDecoder = BitmapRegionDecoder.newInstance(is, false);
    //核心-区域解码只加载对应区域需要的分辨率
    Bitmap bitmap = mDecoder.decodeRegion(mRect, mDecodeOptions);
    //放大从模糊到清晰原理- 先把加载出来的布局直接放大,在开启任务获取需要加载区域的原图分辨率替换当前布局Bitmap
    //其他-- 根据自己的逻辑去做缩放 滑动等操作