[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` 的內存占用怎么計算。 [計算圖片的內存占用](http://www.hmoore.net/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`前對其進行縮放。
[計算圖片的內存占用](http://www.hmoore.net/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/) 和公共號:
- 寫在前面的話
- 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