非原創,只是整理,如果里面發現引用的內容沒有標識出來,歡迎指出。
一、基本知識
(1)兩種圖片:
1)矢量圖:
矢量圖又叫向量圖,是用一系列計算機指令來描述和記錄一幅圖,一幅圖可以解為一系列由點、線、面等到組成的子圖,它所記錄的是對象的幾何形狀、線條粗細和色彩等。生成的矢量圖文件存儲量很小,無論放大和縮小多少倍,圖形都有一樣的平滑邊緣和清晰的視覺效果。
例如,輸入法的文字就是矢量圖。
2)點陣圖(位圖):
就是圖象由無數個點(像素)組成。每個像素里都由一個顏色表現,所以點陣圖是有一個個有顏色的點(像素)排列而成。我們平時看到的文件格式有PSD、TIF、JPG、GIF等都是點陣圖,數碼相機拍攝的照片就是點陣圖。
(2)像素(pixel簡稱px):
在位圖中,圖片是由很多點組成,那每個點就是1個像素。像素是屏幕中可以顯示的最小元素單元,我們應用里任何可見的東西都是由一個個像素點組成的。單獨一個像素點非常的微小,肉眼是無法看見的,可是當許許多多的像素點聚集到一起時,就可以拼接成五彩繽紛的圖案。
(3)屏幕尺寸
屏幕尺寸指屏幕的對角線的長度,單位是英寸,1英寸=2.54厘米
比如常見的屏幕尺寸有2.4、2.8、3.5、3.7、4.2、5.0、5.5、6.0等
(4)分辨率:
圖像分辨率:圖像分辨率是指每英寸圖像內的像素點數。這里的分辨率其實是密度,是指1英寸里有多少像素。它以 ?像素/英尺(ppi)為單位,如一個圖像的分辨率為72ppi,就表示該圖像中每英尺包含72個像素。(但是現在通常將分辨率表示成每一個方向上的像素數量,比如 640X480 等。)
屏幕分辨率:屏幕分辨率是指在橫縱向上的像素點數,單位是px,1px=1個像素點,每個屏幕有自己的分辨率。一般以縱向像素*橫向像素,如1960*1080。屏幕分辨率越高,所呈現的色彩越多,清晰度越高。
(5)色彩模式
圖像的大小和質量不僅僅由像素總數決定,還跟色彩模型有關。色彩模型有RGB模式,CMYK模式,位圖模式,灰度模式等等。
(6)圖片大小
通過一個例子來理解:
選擇一個24位深度的分辨率為225×225的位圖,
由于24位位圖是真彩色,沒有顏色表,所以
其文件大小為:152154=14+40+(225×3+1)×225
注:因為1個像素用三個字節來表示,所以乘以3;因為位圖存儲時,Windows規定一個掃描行所占的字節數必須是4的倍數(即以long為單位),不足的以0填充,225×3+1剛好是4的倍數;14和40是位圖文件頭和位圖信息頭的字節大小;
當利用繪圖程序將文件保存為256色,即8位深度的位圖后,文件大小變為52378字節:
52378 = 14 + 50 + 256 × 4 + (225 + 3)× 225
注:256色位圖有顏色表,每個顏色表結構體是4字節,所以顏色表這一項占256*4 個字節;225+3 剛好湊成4的倍數
可見分辨率沒變,因為位深從24變成了8。所以圖像質量下降了,圖像大小也變小了。
二、圖片的存在形式:
1.文件形式(即以二進制形式存在于硬盤上)
獲取大小(Byte):File.length()
2.流的形式(即以二進制形式存在于內存中)
獲取大小(Byte):new FileInputStream(File).available()
3.Bitmap形式
獲取大小(Byte):Bitmap.getByteCount()
這三種形式的區別:
文件形式和流的形式對圖片體積大小并沒有影響,也就是說,如果你手機SD卡上的如果是100K,那么通過流的形式讀到內存中,也一定是占100K的內存,注意是流的形式,不是Bitmap的形式。當圖片以Bitmap的形式存在時,其占用的內存會變大。
三、bitmap介紹:
(1) Bitmap.Config
一張圖片Bitmap所占用的內存 =圖片長度 x 圖片寬度 x 一個像素點占用的字節數
而Bitmap.Config,正是指定單位像素占用的字節數的重要參數。
其中,A代表透明度;R代表紅色;G代表綠色;B代表藍色。
ALPHA_8
表示8位Alpha位圖,即A=8,一個像素點占用1個字節,它沒有顏色,只有透明度
ARGB_4444
表示16位ARGB位圖,即A=4,R=4,G=4,B=4,一個像素點占4+4+4+4=16位,2個字節
ARGB_8888
表示32位ARGB位圖,即A=8,R=8,G=8,B=8,一個像素點占8+8+8+8=32位,4個字節
RGB_565
表示16位RGB位圖,即R=5,G=6,B=5,它沒有透明度,一個像素點占5+6+5=16位,2個字節
Bitmap.Config主要作用是:
以何種方式像素存儲。不同的配置將會影響圖像的畫質(色彩深度),位數越高畫質越高,顯然在這里ARGB_8888是最占內存的。當然,畫質越高也就越占內存了。
Tips:由于ARGB_4444的畫質慘不忍睹,一般假如對圖片沒有透明度要求的話,可以改成RGB_565,相比ARGB_8888將節省一半的內存開銷。
配置不同Bitmap.Config在相同分辨率下的占用內存情況:
一張圖片Bitmap所占用的內存 = 圖片長度 x 圖片寬度 x 一個像素點占用的字節數
在Android里面可以通過下面的代碼來設置解碼率:
(2) Bitmap.CompressFormat
從字面上理解,它的含義是:Bitmap壓縮格式
其實這個參數很簡單,就是指定Bitmap是以JPEG、PNG還是WEBP格式來壓縮
(3) Bitmap.compress()方法
重磅方法來了,通過這個方法,可以實現圖片的壓縮。使用該方法需要傳三個參數進去:
CompressFormat
指定Bitmap的壓縮格式,可選擇JPEG、PNG、WEBP
int類型的quality
指定Bitmap的壓縮品質,范圍是0 ~ 100;該值越高,畫質越高。0表示畫質最差,100畫質最高。
OutputStream
指定Bitmap的字節輸出流。一般使用:
ByteArrayOutputStream stream = new ByteArrayOutputStream();
這個方法不改變圖片分辨率,只通過改變編碼格式來改變每個像素大小,從而改變圖片大小和質量。
四、BitmapFactory
BitmapFactory是獲取Bitmap和壓縮Bitmap的重要類,下面開始介紹BitmapFactory幾個重要的成員變量和方法:
(1)通過BitmapFactory解碼(獲取)Bitmap的幾種方式
1)decodeFile()//從SD卡文件讀取
Bitmap ?bm=BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/photo.jpg");
2)decodeResource()//從資源文件res讀取
Bitmap bm=BitmapFactory.decodeResource(this.getResources(), R.mipmap.test_pic);
3)decodeStream()//從輸入流讀取
Bitmap bm=BitmapFactory.decodeStream(inputStream);
4)decodeByteArray()//從字節數組讀取
Bitmap bm=BitmapFactory.decodeByteArray(bytes,0,bytes.length);
(2)BitmapFactory.Options
BitmapFactory在使用方法decodeFile()、decodeResource()解碼圖片時,可以指定它的BitmapFactory.Options。
這個參數作用非常大,它可以設置Bitmap的采樣率,通過改變圖片的寬度、高度、縮放比例等,以達到降低圖片的像素的目的,這樣可以做到圖片壓縮,減少Bitmap的內存。
下面列出BitmapFactory.Options的部分成員變量:
in開頭的代表的就是設置某參數;out開頭的代表的就是獲取某參數。比如,inSampleSize就是設置Bitmap的縮放比例、outWidth就是獲取Bitmap的高度。
1)?inJustDecodeBounds 設置只去讀圖片的附加信息(寬高),不去解析真實的Bitmap
從字面上理解,它的含義是:”設置僅解碼Bitmap的邊界”。那它真正的作用是啥呢?
當inJustDecodeBounds設置為true的時候,BitmapFactory通過decodeResource或者decodeFile解碼圖片時,將會返回空(null)的Bitmap對象,這樣可以避免Bitmap的內存分配,但是它可以返回Bitmap的寬度、高度以及MimeType。
這樣做的意義就在于,可以先不用產生Bitmap內存,從而獲得圖片的寬高信息,盡可能的做到節約內存。
2)inSampleSize 設置圖片的縮放比例(寬和高)
這里注意,google推薦用2的倍數:
在這里著重講一下這個inSampleSize。從字面上理解,它的含義是:”設置取樣大小“。
它的作用是:設置inSampleSize的值(int類型)后,假如設為4,則寬和高都為原來的1/4,寬高都減少了,自然內存也降低了。
如圖所示:
在這里參考Google官方文檔來解釋:
http://developer.android.com/intl/zh-cn/training/displaying-bitmaps/load-bitmap.html#load-bitmap
如何理解”設置取樣大小“呢?如果你認真看了上面的內容話,聰明的你一定知道,肯定需要配合inJustDecodeBounds,先獲取圖片的寬、高【這個過程就是取樣】,然后通過獲取的寬高,動態的設置inSampleSize的值。
【當然,你也可以不動態,可以寫死inSampleSize的值。比如設置inSampleSize = 4的話,一張分辨率為2048x1536px的圖像將使用inSampleSize值為4的設置來解碼,產生的Bitmap大小約為512*384px。相較于完整圖片占用12M的內存,這種方式只需0.75M內存(假設Bitmap配置為ARGB_8888)?!?/p>
四、兩種壓縮方式:
(1)質量壓縮:
將圖片保存到本地時進行壓縮, 即將圖片從Bitmap形式變為File形式時進行壓縮
該方法是利用compress方法。壓縮圖片的質量, 它不會減少圖片的像素。
比方說, 你的圖片是300K的, 1280*700像素的, 經過該方法壓縮后, File形式的圖片是在100k以下, 但是你BitmapFactory.decodeFile到內存中,變成Bitmap時,它的像素仍然是1280*700。計算圖片像素的方法是?bitmap.getWidth()和bitmap.getHeight()。
(2)取樣壓縮
通過調整inSampleSize的值,在圖片從本地讀到內存時,進行壓縮 ,即圖片從File形式變為Bitmap形式。這種方式通過改變圖像的尺寸(像素總數)來壓縮,每個像素所占用的大小不變。
(3)常用的圖片壓縮方法是將兩者結合。
參考資料:
http://blog.sina.com.cn/s/blog_9e1e8c1301015xat.html
http://www.tuicool.com/articles/eue6z2
http://anany.me/2015/10/15/bitmap1/
https://developer.android.com/training/displaying-bitmaps/load-bitmap.html