1.14 Bitmap杂谈

1 minute read

1.14.1 Bitmap优化

  • Bitmap Config配置
  • 配置inDensityinTargetDensity
  • inJustDecodeBounds预判Bitmap大小及使用inSampleSize压缩
  • 2.3版本inNativeAlloc的使用

1.14.2 Bitmap Config

  • ALPHA_8 一个像素8位(一个字节), 只有透明度
  • ARGB_4444 一个像素4+4+4+4 = 16位(2个字节)
  • ARGB_8888 一个像素8+8+8+8 = 32位(4个字节)
  • RGB_565 一个像素5 + 6 + 5 = 16位(2个字节)

1.14.3 Bitmap大小计算

  • width * height * Bitmap Config / 8

1.14.4 Bitmap格式

  • jpg
  • png
  • webp
    • 有损WebP基于VP8视频编码中的预测编码方法来压缩图像数据,其基本步骤类似于JPEG压缩,主要包含格式转换、分割子块、预测编码、FDCT、量化、Z排列、熵编码
    • 有损webp比jpg好, 主要原因是预测编码

1.15.5 Bitmap圆角实现(参考fresco)

  • fresco
    • BITMAP_ONLY
      • BitmapShader实现, new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
  • Android实现圆角的方式
    • 裁剪
      • 将原始Bitmap裁剪放到新Bitmap中
        • Canvas#drawRoundRect(rectF, roundPx, roundPx, paint)
        • Canvas#drawBitmap(bitmap, rect, rect, paint)
    • 覆盖
      • 通过xfermode实现
      • 通过ViewGroup设置, Canvas#clipPath()
      • 使用shape覆盖, 设置圆角shape,盖在目标 imageview
      • 使用Android自带的剪切方法, 即在shape.xml设置corner, 在代码设置ImageView#setClipToOutline(true)

1.14.5 Bitmap加载与解码

  // BitmapFactory
  public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
    // ...
    return decodeStreamInternal(is, outPadding, opts);
  }

  private static Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts) {
    // ...
    // Native 方法
    byte [] tempStorage = new byte[DECODE_BUFFER_SIZE];
    return nativeDecodeStream(is, tempStorage, outPadding, opts);
  }

  //BitmapFactory.cpp
  static jobject nativeDecodeStream(JNIEnv *env, jobject clazz, jobject is, jbyteArray storage, jobject padding, jobject options) {
    // ...
    return doDecode(env, bufferedStream.release(), padding, options);
  }

  static jobject doDecode(JNIEnv *env, SkStreamRewindable *stream, jobject padding, jobject options) {
    ...
    // 创建解码器
    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(streamDeleter.release(), &peeker));

    ...
    // 输出Bitmap的色彩类型
    SkColorType decodeColorType = codec->computeOutputColorType(prefColorType);
    ...
    // SkAndroidCodec完成解码
    SkCodec::Result result = codec->getAndroidPixels(
        decodeInfo,                 // 解码信息 (长度, 宽度, 色彩类型, 透明度等)
        decodingBitmap.getPixels(), // Bitmap 的实际内存地址, 即像素们的内存地址
        decodingBitmap.rowBytes(),  // 每一行的大小, 即每一行所有像素的大小
        &codecOptions);             // 解码器配置 (色码表, 采样大小等)
    ...
  }

  // SkAndroidCodec.cpp
  SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo &info, void *pixels, size_t rowBytes, const AndroidOptions *options)
  {
      // ...
      return this->onGetAndroidPixels(info, pixels, rowBytes, *options);
  }

  SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo &info, void *pixels, size_t rowBytes, const AndroidOptions &options)
{
    ...
    SkCodec::Result result = this->codec()->startScanlineDecode(info.makeWH(scaledSize.width, scaledSize.height()),&codecOptions, options.fColorPtr, options.fColorCount);
    ...
}

SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo &dstInfo, const SkCodec::Options *options, SkPMColor ctable[], int *ctableCount) {
    ...
    // 逐行解码并写入内存
    const Result result = this->onStartScanlineDecode(dstInfo, *options, ctable, ctableCount);
    ...

    // [02] 实际执行逐行解码, 并将结果写入对应的内存地址
    switch (this->codec()->getScanlineOrder()) {
        case SkCodec::kTopDown_SkScanlineOrder:
        case SkCodec::kNone_SkScanlineOrder: {
            // ...
            int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes);
            // ...
            return SkCodec::kSuccess;
        }
        ...
    }
}

// SkJpegCodec.cpp
// 通过jpeglib进行解码
int SkJpegCodec::onGetScanlines(void *dst, int count, size_t dstRowBytes) {
    ...
    for (int y = 0; y < count; y++)
    {
        // 逐行解压 & 读取像素, jpeglib 的方法
        uint32_t rowsDecoded = jpeg_read_scanlines(fDecoderMgr->dinfo(), &dstRow, 1);
        ...
    }
    ...
}

  • 图片解码原理(图片来源于Skia图片解析流程与图片编码原理初探) 图片解码原理
    1. RGB转换YCbCr
    2. 离散余弦变换(DCT)
    3. 量化(Quantize)
    4. 游程(Run-level)编码 & Huffman编码

参考