1. 程式人生 > >Bitmap的建立使用( 一)

Bitmap的建立使用( 一)

bitmap點陣圖類

bitmap點陣圖是一種基於rgb顏色編碼的點陣圖形,由畫素組成(一個一個小方塊排列組成)**

  • Bitmap.CompressFormat 列舉類有三個值,指定點陣圖可以被壓縮成什麼格式

JPEG:不能儲存透明背景(缺少Alpha通道)?有失真壓縮故圖片大小比較小
PNG:能儲存透明背景,無失真壓縮,圖片質量好,但是比較大
WEBP:好像是谷歌推出的一種圖片格式,壓縮率高
注意:WEBP支援不好(略),在android有時候我們做圓角頭像如果用到了jpeg的圖片你會發現圖片後面有黑色背景,這就是因為jpeg不支援透明的原因。

  • Bitmap.Config 列舉類4個值,描述畫素是如何儲存的色彩,不同值會影響圖片質量

Bitmap.Config getConfig () 返回點陣圖的Config 值 setConfig(Bitmap.Config config) 設定config值 為了更好理解下面幾個引數的意思這裡解釋下相關概念:
圖片是由N個畫素組成,圖片大小和畫素多少有關,色位、色深表示單個畫素點自身的細化程度即能容納顏色程度(色位深度),單位是bit(位),這個值的大小衡量了一個圖片的質量。
ALPHA_8 :1個畫素能儲存8位alpha值 二進位制:2^8=256 (256色)
ARGB_8888:1個畫素能儲存32位ARGB值 二進位制:2^32=16777216 (真彩色)
RGB_565 :1個畫素能儲存16位RGB值 二進位制:2^16=65536 (高彩色) (預設)
ARGB_4444:這個被廢棄了建議使用ARGB_8888
注意:通過如上解釋我們知道單個畫素能儲存的值越大,那麼圖片質量肯定越好,相印的圖片的大小就越大。A-代表alpha透明,R-紅色,G-綠色,B-藍色。
一張800*480的ARGB_8888的圖片他大小就是 800x480=384000畫素,ARGB_8888 4個8位就是4位元組
384000*4/1024=1.4MB

  • compress(Bitmap.CompressFormat format, int quality, OutputStreamstream) 壓 縮圖片方法解決OOM的一大途徑
//如果format引數是Bitmap.CompressFormat.PNG,那麼quality隨便設定多少壓縮是無效
public void setCompress(Bitmap bitmap){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100
, baos); }
  • copy(Bitmap.Config config, boolean isMutable) 從源點陣圖賦值一個新的點陣圖
  isMutable:true表示賦值出來的新點陣圖的畫素點是可以本修改的
  Bitmap bitmap2=BitmapFactory.decodeResource(getResources(), R.raw.bit);
  Bitmap newB1=bitmap2.copy(Bitmap.Config.RGB_565, false);
  • copyPixelsFromBuffer(Buffer src)、copyPixelsToBuffer(Buffer dst)
copyPixelsFromBuffer:從buffer快取中獲取資料
copyPixelsToBuffer:把資料賦值到buffer快取中
Buffer是java.nio包下的一個快取類
//獲取一張點陣圖    
Bitmap bitmap2=BitmapFactory.decodeResource(getResources(), R.raw.bit);
//給buffer緩衝設定大小,這裡就設定點陣圖的大小
Buffer buffer= ByteBuffer.allocate(bitmap2.getByteCount());
//把點陣圖bitmap2的資料複製到buffer中
bitmap2.copyPixelsToBuffer(buffer);
buffer.position(0);
//bitmap2再從buffer中獲取資料
bitmap2.copyPixelsFromBuffer(buffer);
iv.setImageBitmap(bitmap2);

建立點陣圖的方法 9種

系統提供了不同方式來建立點陣圖,但是常用的就幾個

  • createBitmap(Bitmap src)

從源點陣圖src中建立一個新點陣圖,寬高密度都一樣

  • createBitmap(int width, int height, Bitmap.Config config)
建立一個空點陣圖,就是說這個點陣圖創建出來裡面的畫素點沒有色彩
  //獲取一張已有的點陣圖   
Bitmap bitmap2=BitmapFactory.decodeResource(getResources(), R.raw.bit);
 //建立一個空點陣圖,沒有色彩,寬高和bitmap2一樣
Bitmap bitmap=Bitmap.createBitmap(bitmap2.getWidth(), bitmap2.getHeight(),  Bitmap.Config.ARGB_8888);
//把bitmap2點陣圖資料複製到buff緩衝中
 Buffer buffer= ByteBuffer.allocate(bitmap2.getByteCount());
 bitmap2.copyPixelsToBuffer(buffer);
 buffer.position(0);
 //然後我們建立的空bitmap再從buffer中獲取bitmap2的點陣圖資料
 bitmap.copyPixelsFromBuffer(buffer);
 iv.setImageBitmap(bitmap);//成功顯示
  • createBitmap(Bitmap source, int x, int y, int width, int height)

(x,y)定義擷取原點的座標,就是從source點陣圖的那個點開始擷取預設是0,0 擷取的寬width和高height

  • createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)

從目標bitmap中擷取一段點集返回一個不可改變的bitmap,擷取的範圍由Matrix
m來 決定,返回 的bitmap和目標有相同的density,x,y,width,height分別為目標的起點 和寬高,filter表示目標 是否需要過濾,只有在Matrix包含更多資訊的時候才會用到。

  • createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)

把src點陣圖縮放到指定的dstWidth、dstHeight,縮放點陣圖到指定寬高 可以看下原始碼就是計算縮放比然後用矩陣來縮放

  • createBitmap(int[] colors, int width, int height, Bitmap.Configconfig)

圖片寬高就是畫素,畫素包含色彩,這裡就是自己定義若干colors來組成圖

  • createBitmap(int[] colors, int offset, int stride, int width, int height, Bitmap.Config config)

返回一個不可改變的bitmap,裡邊的點的值有colors陣列一一對應的設定,offset表示colors陣列中的顏色值是從哪個下標開始,stride表示colors陣列中每一行有多少個顏色值,該值必須大於width。當width,height沒有大於0,或者陣列的長度小於bitmap的點的數量時就會丟擲異常。

  • createBitmap(DisplayMetrics display, int width, int height,Bitmap.Config config)
  • createBitmap(DisplayMetrics display, int[] colors, int width, intheight, Bitmap.Config config)
  • createBitmap(DisplayMetrics display, int[] colors, int offset, intstride, int width, int height, Bitmap.Config config)

下面三個同上,display引數只是包含了當前顯示其他資訊如密度等……….

  • eraseColor (int c)

將指定顏色填滿到bitmap中,當該bitmap不可改變時將會丟擲異常

  • Bitmap extractAlpha () 提取指定點陣圖的alpha值
  • Bitmap extractAlpha (Paint paint, int[] offsetXY)按照offsetXY偏移去繪製
Bitmap mBitmap=BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
 Bitmap mAlphaBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(),Bitmap.Config.ARGB_8888);
 Canvas mCanvas = new Canvas(mAlphaBitmap);
 Paint mPaint = new Paint();
 mPaint.setColor(Color.BLUE);
        //從原點陣圖中提取只包含alpha的點陣圖
 Bitmap alphaBitmap = mBitmap.extractAlpha();
           //在畫布上(mAlphaBitmap)繪製alpha點陣圖
 mCanvas.drawBitmap(alphaBitmap, 0, 0, mPaint);
 iv.setImageBitmap(mAlphaBitmap);

這個demo的效果就是通過extractAlpha提取只帶有alpha值的點陣圖,然後畫筆設定藍色再繪製這個點陣圖,結果就是點陣圖只有alpha值,保留了圖形的輪廓,但是裡面填充的是藍色
擴充套件:通常要給一張圖片外面加一個輪廓陰影就用這個提取,然後和原圖一起畫

  • getAllocationByteCount ()、getByteCount () 返回bitmap大小
  • getAllocationByteCount :返回用於儲存該點陣圖的記憶體大小(byte)
  • getByteCount :返回儲存該點陣圖的畫素位元組數(getRowBytes() * getHeight())
 //區別demo說明:
//獲取一張圖片列印值8294482944
Bitmap mBitmap=BitmapFactory.decodeResource(getResources(), R.mipmap.ic_); Bitmap   
//原圖copy一張圖片列印值8294482944 (說明另外一個問題圖片預設是ARGB_8888,)
mBitmap2=mBitmap.copy(Bitmap.Config.ARGB_8888, true); 
//reconfigure改變圖片畫素色彩儲存引數,列印值//4147282944
mBitmap2.reconfigure(mBitmap2.getWidth(),mBitmap2.getHeight(), Bitmap.Config.RGB_565);  

總結:上面的值第一個是getByteCount(),第二個是getAllocationByteCount ()的值
當我們使用reconfigure(..)方法把原圖的畫素色彩儲存改為Bitmap.Config.RGB_565,這樣圖片的質量降低,大小就會變小,通過組後一組值發現getByteCount()的值變小了,但是另外一個沒變。
故:getByteCount()返回的只是當前點陣圖的畫素位元組數的大小值,這個值會隨著圖片質量改變而改變,而getAllocationByteCount()是返回圖片在記憶體中分配的大小,無論圖片在程式中如何改變他記憶體佔有大小不變。當源點陣圖沒經過任何操作如reconfigure(int,
int, Config), setWidth(int), setHeight(int),等2個方法返回的大小始終相等
另1:圖片在sdcard上大小可能是200KB,但是載入到程式中後會變成幾MB?why?
在程式中位圖表示了影象的全畫素的資料,這樣才可以在螢幕上完全顯示。當您將檔案儲存在磁碟上的資訊,這些資訊可以通過像JPG或PNG壓縮演算法壓縮,
另2:圖片加入到程式時系統會根據當前手機分辨、密度等重新處理圖片,這也會導致圖片加入到程式變大。(OOM相關 1.3小節)

  • public int getDensity () 返回點陣圖密度
  • setDensity(int density) 設定點陣圖密度

如果程式不支援不同螢幕密度那麼這個值預設是density_default=160?
關於密度對圖片的影響和不同的drawable資料夾中圖片的自動縮放等在後面擴充套件說明!

  • getGenerationId () 返回點陣圖是否被更改(修改)的標誌
  • int getHeight () 、getWidth()返回點陣圖高\寬(點陣圖的畫素高\寬)
  • setHeight(int height)、setWidth(int width 設定點陣圖寬高
  • byte[] getNinePatchChunk () 點9圖片相關
  • getPixel (int x, int y) 獲取影象在xy座標的顏色值,可轉成16進位制即可使用該顏色
  • getPixels (int[] pixels, int offset, int stride, int x, int y, intwidth, int height)同上

設定、獲取畫素在某個座標點的顏色值 pixels構建一個數組來裝返回的顏色值,offset取值時從第幾個下標開始取、stride:每行高

  • setPixel(int x, int y, int color)
  • setPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height)

設定畫素的顏色值,引數和上面的getXX方法一直

  • getRowBytes()

返回點陣圖行畫素值

  • public int getScaledHeight (DisplayMetrics metrics)
  • public int getScaledHeight (int targetDensity)
  • public int getScaledHeight (Canvas canvas)
  • public int getScaledWidth (int targetDensity)
  • public int getScaledWidth (DisplayMetrics metrics)
  • public int getScaledWidth (Canvas canvas)

獲取縮放後的點陣圖寬高,縮放是按照密度來計算縮放比

引數講解: 上面三種類型引數其實只是使用了他裡面封裝的密度值 如DisplayMetrics metrics
他只是metrics.densityDpi獲取封裝的密度值 Canvas canvas引數
他只是canvas.mDensity獲取裡面封裝的密度值
這幾個方法在原始碼裡面計算很簡單,就是通過獲取到的密度值,和點陣圖預設密度值相除得到一個縮放比然後點陣圖的寬高乘以這個縮放比就是得到的縮放後寬高

原始碼如下:(某一個方法的原始碼其他自己看)
public int getScaledHeight(DisplayMetrics metrics) { //mDensity是點陣圖預設密度,可獲取
return scaleFromDensity(getHeight(), mDensity, metrics.densityDpi); //引數目標密度
}
static public int scaleFromDensity(int size, int sdensity, int tdensity) { //DENSITY_NONE=0
if (sdensity == DENSITY_NONE || tdensity == DENSITY_NONE || sdensity == tdensity) {
return size; }
return ((size * tdensity) + (sdensity >> 1)) / sdensity;
}

這個算演算法是 寬* tdensity/mDensity 另外如果獲取到的點陣圖密度等於傳遞進來的密度或者傳遞的密度為0那麼高度還是返回源高度

Bitmap mBitmap=BitmapFactory.decodeResource(getResources(), R.raw.bit); 
//獲取點陣圖預設密度mBitmap.getDensity()=480
int scaleHeight=mBitmap.getScaledHeight(160)  //524160密度縮放後高度
int height=mBitmap.getHeight() //1572  源點陣圖高度
對照上面的原始碼:160/480 *1527=524
  • boolean hasAlpha() 返回true表示當前點陣圖是包含alpha通道的
  • setHasAlpha(boolean hasAlpha) 如果點陣圖不包含alpha通道,此方法忽略
  • final boolean isMutable() 表示點陣圖是否可變

某些方法操作點陣圖時(如修改、複製點陣圖)需要知道當前點陣圖是否可變。

  • boolean isRecycled() 判斷點陣圖是否被回收了(true :回收了)
  • public void recycle() 回收一個位圖
  • reconfigure(int width, int height, Bitmap.Config config)

通過此方法給點陣圖定義新的寬、高、conifg等引數,獲得新的點陣圖,這種方式優點是不會新生成一個位圖

  • boolean sameAs(Bitmap other) 比較2個位圖是否一樣
  • isPremultiplied()、setPremultiplied(boolean)

此為圖的畫素是否被預乘 意思就是alpha通道混合到r、g、b各個通道
比如一個顏色50%的半透明紅色(128,255,0,0)所述預乘的形式是(128,128,0,0) 未被預乘的點陣圖不能繪製到Canvas
否則會丟擲執行時異常

  • prepareToDraw()
  • hasMipMap()、setHasMipMap(boolean)

mipmap這個設計到很多其他知識,比如紋理、貼圖、渲染 這個大概意思就是減少記憶體使用,提高gpu渲染速度和我們android
mimap檔案同理 總結:雖然bitmap提供了很多方法,但總結下來就幾種
1:建立圖片,2:修改圖片,通過修改圖片的畫素值、畫素色彩值,密度等 2:獲取圖片的相關引數、如寬、高、密度、畫素顏色值相關