[TOC]
# RGB介绍
> RGB颜色模型: 最常见的颜色模型,设备相关。R、G、B分别代表红、绿和蓝色三种颜色通道,取值均为[0,255]。
> RGB 8位色: 表示使用8位(bit)表示颜色,一共能表示2^8 = 128种颜色。
依次类推RGB 16位色,RGB 24位色,RGB 32位色,使用的位数越多,能表示的颜色越多,24位能表示的颜色数量已经很多了,称之为“真彩色”。
> 32位和24位能表示的颜色一样多,多一个了透明度。
> Android Bitmap使用的三种颜色格式:
>- ALPHA_8–每个像素占1个字节,存储透明度信息,没有颜色信息。
>- RGB_565--每个像素占2个字节存储颜色信息,R 5位,G 6位,B 5位,能表示2^16种颜色。
>- ARGB_8888--每个像素占4个字节存储颜色信息,A R G B各一个字节,能表示2^24种颜色,还有一个字节存储透明度信息。
# 压缩原理
在 `Android` 中进行图片压缩是非常常见的开发场景,主要的压缩方法有两种:其一是下 **采样压缩**,其二是 **质量压缩**。
- 前者是降低图像尺寸,改变图片的存储体积;
- 后者则是在不改变图片尺寸的情况下,通过损失颜色精度,达到相同目的;
## 压缩Bitmap磁盘占用空间的大小
```java
//如果成功地把压缩数据写入输出流,则返回true。
public boolean compress(
Bitmap.CompressFormat format, //图像的压缩格式;
int quality,//图像压缩率,0-100。 0 压缩100%,100意味着不压缩;
OutputStream stream) ;//写入压缩数据的输出流;
```
- `Bitmap.CompressFormat.PNG` ,那不管第二个值如何变化,图片大小都不会变化,不支持 `png图片` 的压缩。因为 `PNG` 格式是无损的,它无法再进行质量压缩,`quality `这个参数就没有作用了,会被忽略,所以最后图片保存成的文件大小不会有变化;
- `CompressFormat.WEBP` ,这个格式是 `google` 推出的图片格式,它会比 `JPEG` 更加省空间。官方表示能节省 `25%-34%` 的空间;
## 压缩Bitmap占用内存的大小
要知道怎么压缩才能使 `Bitmap` 占用的内存变小,首先需要知道 `Bitmap` 的内存占用怎么计算。 [计算图片的内存占用](https://www.kancloud.cn/book/stven\_king/stven\_king\_android\_interview\_topic/preview/Bitmap/%E8%AE%A1%E7%AE%97%E5%9B%BE%E7%89%87%E7%9A%84%E5%86%85%E5%AD%98%E5%8D%A0%E7%94%A8.md) 这篇文章有详细讲解。
### 使用inSampleSize进行压缩
```java
// 设置参数
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 只获取图片的大小信息,而不是将整张图片载入在内存中,避免内存溢出
BitmapFactory.decodeFile(imagePath, options);
int height = options.outHeight;
int width= options.outWidth;
int inSampleSize = 2; // 默认像素压缩比例,压缩为原图的1/2
int minLen = Math.min(height, width); // 原图的最小边长
if(minLen > 100) { // 如果原始图像的最小边长大于100dp(此处单位我认为是dp,而非px)
float ratio = (float)minLen / 100.0f; // 计算像素压缩比例
inSampleSize = (int)ratio;
}
options.inJustDecodeBounds = false; // 计算好压缩比例后,这次可以去加载原图了
options.inSampleSize = inSampleSize; // 设置为刚才计算的压缩比例
Bitmap bm = BitmapFactory.decodeFile(imagePath, options); // 解码文件
```
图片尺寸的修改其实就是通过修改像素数,放大的过程称之为**上采样**,缩小的过程称之为**下采样**。
`Android` 使用的 `inSampleSize` 计算采样率使用的采样算法是**邻近采样(Nearest Neighbour Resampling)**, `x`(`x` 为 2 的倍数)个像素最后对应一个像素。比如采样率设置为 `1/2` ,所以是两个像素生成一个像素。邻近采样的方式比较粗暴,直接选择其中的一个像素作为生成像素,另一个像素直接抛弃。
### 使用createScaledBitmap或Matrix
```java
Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test.png");
Bitmap compress = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth()/2, bitmap.getHeight()/2, true);
//或者直接使用 matrix 进行缩放,查看Bitmap.createScaledBitmap源码其实就是使用 matrix 缩放
Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test.png");
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 0.5f);
bm = Bitmap.createBitmap(bitmap, 0, 0, bit.getWidth(), bit.getHeight(), matrix, true);
```
同样是图片宽高各为原来的`1/2`,这种方式采用**双线性采样(Bilinear Resampling)**,这个算法不像邻近采样算法直接粗暴的选择一个像素,而是参考了源像素相应位置周围 `2x2` 个点的值,根据相对位置取对应的权重,经过计算之后得到目标图像。
不同的采样算法会产生不同效果,除了 `Android` 中这两种常用的采样算法之外,还有比较常见如:`双立方/双三次采样(Bicubic Resampling)` 和 `Lanczos Resampling` 等。如果对 `Android` 使用的这两种采样算法效果不满意,必要时可以引入其他的算法。
### BitmapFactory.Options三件套
> `inScaled` + `inDensity` + `inTargetDensity`
当**inScaled**设置为true时(设置此标志时),如果**inDensity**与**inTargetDensity**不为0,Bitmap就会在加载的时候直接进行缩放以匹配inTargetDensity,而不是绘制的时候进行缩放。(加载到堆内存时已经缩放了大小了,`.9图` 会忽略此标志)
**inDensity**:加载图片的原始宽度,如果此密度与`inTargetDensity`不匹配,则在返回Bitmap前会将它缩放至目标密度。
**inTargetDensity** :目标图片的显示宽度,它与`inScaled`与`inDensity`结合使用,确定如何在返回`Bitmap`前对其进行缩放。
[计算图片的内存占用](https://www.kancloud.cn/book/stven_king/stven_king_android_interview_topic/preview/Bitmap/%E8%AE%A1%E7%AE%97%E5%9B%BE%E7%89%87%E7%9A%84%E5%86%85%E5%AD%98%E5%8D%A0%E7%94%A8.md) 这篇文章中尝试将相同图片加载放到不同的 `drawable-dpi` 的文件目录下去加载到内存中的 `Bitmap` 大小不同,其原因就是 `inDensity` 和 `inTargetDensity` 不一致导致。
*****
文章到这里就全部讲述完啦,若有其他需要交流的可以留言哦~!~!
想阅读作者的更多文章,可以查看我 [个人博客](http://dandanlove.com/) 和公共号:![振兴书城](https://imgconvert.csdnimg.cn/aHR0cDovL3VwbG9hZC1pbWFnZXMuamlhbnNodS5pby91cGxvYWRfaW1hZ2VzLzEzMTk4NzktNjEyYzRjNjZkNDBjZTg1NS5qcGc?x-oss-process=image/format,png)
- 写在前面的话
- Java
- 基础
- Double的比较
- 小数怎么用二进制表示
- 多线程
- 并发和并行
- 线程池
- 线程池背景
- 线程池构造
- 任务阻塞队列
- Flutter
- 基础知识
- Dart基础
- Android
- 项目架构
- View
- 非UI线程更新View
- AlarmManager
- 对比postDelaryed和Timer
- Bitmap
- 加载100M的图片却不撑爆内存
- Bitmap压缩
- Bitmap局部解码
- 计算图片的内存占用
- Android动画
- Android动画类型
- Android动画原理
- 属性动画
- 帧动画
- 补间动画
- 使用动画的注意事项
- Android新特性
- 权限组
- Android23(Marshmallow)-6.0
- Android24(Nougat)-7.0
- Android26(Oreo)-8.0
- Android28(Pie)-9.0
- Android29(Q)-10.0
- AndroidX迁移
- Kotlin
- 关键字
- Kotlin操作符
- CoroutineScope
- Flow
- CoroutineException