天天看點

進階UI之Paint(濾鏡,顔色通道,矩陣運算)

前言

在之前的幾次課當中我們已經詳細了解到整個android程式,從啟動再到繪制的整體流程,從這中間我們又牽扯出了Canvas繪制圖形的畫闆和我們的Paint控制色彩樣式的畫筆,那麼之前基礎篇我們就不進行詳細的解釋,那些API在之前的基礎篇已經公布出來,我也注釋的非常詳細,今天我門來了解Paint進階篇真正需要了解的濾鏡

濾鏡

1.濾鏡效果

進階UI之Paint(濾鏡,顔色通道,矩陣運算)

image.png

從上圖我們可以看到 四張濾鏡效果圖像,其色彩的顯示效果各不一樣,也就是說所謂濾鏡其實隻不過是對于原本圖像色彩進行調整,那麼需要對圖像的色彩進行調整操作,我們會需要知道幾個概念,那就是我們的圖像構成,顔色通道,顔色模式以及顔色舉證

2.圖像構成

在具體講濾鏡之前,今天我們先來系統化真正認識一下在我們計算機當中我們的圖像到底是什麼,我們都知道在計算機體系當中我們的圖像有各種各樣的格式,比如jpg,png,gif等等...

那麼我們同樣也知道我們計算機當中的圖像檔案其實實際也就是一個二進制的位元組碼檔案,那麼這個圖像本質上來說是一個二進制檔案,然後我們的cpg,gpu對二進制檔案進行識别再顯示到我們的螢幕上,那麼我們現在需要關注的是,這些檔案當中他到底儲存的是什麼?

其實一個圖像檔案當中,他儲存的資料總體分為兩塊

  1. 圖像的資訊
  2. 圖像的資料

後者好了解,我們可以了解為圖像具體的那些像素點的資料,那麼前者其實我們可以了解為,是一組資訊,這組資訊的作用是讓我們的cpg,gpu在顯示圖像的時候,基于我門設定的這組資訊的規則不同,那麼顯示的效果不一樣, 以一種格式(PNG)為例,我在網上扒出了對于這個圖像的結構的解釋(粗略看一下就好)

PNG的檔案結構

    對于一個PNG檔案來說,其檔案頭總是由位固定的位元組來描述的:

    十進制數 137 80 78 71 13 10 26 10
    十六進制數 89 50 4E 47 0D 0A 1A 0A
    其中第一個位元組0x89超出了ASCII字元的範圍,這是為了避免某些軟體将                PNG檔案當做文本檔案來處理。檔案中剩餘的部分由3個以上的PNG的資料塊(Chunk)按照特定的順序組成,是以,一個标準的PNG檔案結構應該如下:

    PNG檔案标志 PNG資料塊 …… PNG資料塊
    PNG資料塊(Chunk)

    PNG定義了兩種類型的資料塊,一種是稱為關鍵資料塊(criticalchunk),這是标準的資料塊,另一種叫做輔助資料塊(ancillarychunks),這是可選的資料塊。關鍵資料塊定義了4個标準資料塊,每個PNG檔案都必須包含它們,PNG讀寫軟體也都必須要支援這些資料塊。雖然PNG檔案規範沒有要求PNG編譯碼器對可選資料塊進行編碼和譯碼,但規範提倡支援可選資料塊。

    下表就是PNG中資料塊的類别,其中,關鍵資料塊部分我們使用深色背景加以區分。
      
為了簡單起見,我們假設在我們使用的PNG檔案中,這4個資料塊按以上先後順序進行存儲,并且都隻出現一次。

    資料塊結構

    PNG檔案中,每個資料塊由4個部分組成,如下:

    名稱 位元組數 說明
    Length (長度) 4位元組 指定資料塊中資料域的長度,其長度不超過(231-1)位元組
    Chunk Type Code (資料塊類型碼) 4位元組 資料塊類型碼由ASCII字母(A-Z和a-z)組成
    Chunk Data (資料塊資料) 可變長度 存儲按照Chunk Type Code指定的資料
    CRC (循環備援檢測) 4位元組 存儲用來檢測是否有錯誤的循環備援碼
      

從上面這一段解釋當中我門可以看出,其實所謂的各個圖像格式隻不過是釋出的标準不一樣,那麼們cpu解析的規則也不一緻,同時一張圖檔裡面包含了多個資料塊,如下圖

進階UI之Paint(濾鏡,顔色通道,矩陣運算)

這是我打開的一張jpg圖的資料,這時我門看到的是16進制的資料,盡管我們不知道這些資料是什麼,那麼此時我們結合上述所說,假定第一行的資料是這個jpg的标志,第2-8行可能記錄的是解析規則等等資訊類似,後面的為資料,那麼這樣去看待一張圖形,我們就能大緻明白一個意思, 标志+圖像資訊+資料--》最終構成一張完整的圖像,圖像根據資料和所為的解析規則計算顯示出來的。那麼對于今天的濾鏡,我門需要了解到圖像當中比較重要的兩個資訊顔色通道,顔色模式

3.顔色通道,顔色模式

顔色通道: 儲存圖像顔色資訊的通道稱為顔色通道。這句話是顔色通道的基本定義,我門可以了解為記錄色彩資訊的那一段資料,每個圖像都有一個或者多個顔色通道,圖像中預設的顔色通道資訊取決于顔色模式

顔色模式: ​​​​顔色模式我門可以了解為将某種顔色表現為數字形式的模型,或者說是一種記錄圖像顔色的方式。分為:RGB模式、CMYK模式、HSB模式、Lab顔色模式、位圖模式、灰階模式、索引顔色模式、雙色調模式和多通道模式等。

其實實際上我們就認為,現在我要顯示的色彩這個時候是用數字表示,最經典的RGB模式我們可以了解為R(255) G(0) B(0)目前這個像素,顯示,在識别的時候為紅色,他由紅 綠 藍 三種色彩進行混合,顯示出我們要的顔色其程度數值是0-255的範圍

總結: 也就是說,其實圖像的顯示,每個點都是由模式所決定的色彩數值混合形成我們想要的顔色,那麼我們的濾鏡效果實作,其實實際上就是去對于顔色通道進行過濾操作,在其原本的模式數值上面進行操作,達到更改圖像色彩效果的目的,這就是我們所為的濾鏡

4.顔色矩陣

在android當中,他所采用的顔色模式是RGBA模式,也就是在紅綠藍的基礎上加入了Alpha透明度的概念,那麼也就是他現在是一個四通道的模式。在Android當中當android将圖像資訊擷取出來時候,目前圖像的顔色通道資訊他用的是一個矩陣在進行儲存。用一個數組來表示的話就是

進階UI之Paint(濾鏡,顔色通道,矩陣運算)
進階UI之Paint(濾鏡,顔色通道,矩陣運算)

上面這裡看到的是一個四通道形式的表示方式,我門會發現在

1-1 2-2 3-3 4-4

的位置都是一個1的數值,那麼幾個數值我們把他了解為顔色的系數,1為原本數值不動,若0.5那麼目前對應的rgba四個選項按比例處理。如果想要更改為半透明的,那麼,目前a的值改為0.5就OK了紅色翻一倍就改為2。但是如果在這種情況下

進階UI之Paint(濾鏡,顔色通道,矩陣運算)

這個四介矩陣做不到,在android當中真正的表現方式他應用了一個4*5的矩陣

進階UI之Paint(濾鏡,顔色通道,矩陣運算)

最後方加入一列,作為所為的亞元坐标,其實也就是分量值,那麼下面這個顔色的矩陣如果想要将我們的顔色值達到上訴效果,第二行的100表示在之前的顔色基礎上增加100個綠色數值,那麼這種是我們最為簡單的了解,而真正的他當中的實作實際上是采用矩陣的計算

4.矩陣運算

進階UI之Paint(濾鏡,顔色通道,矩陣運算)
進階UI之Paint(濾鏡,顔色通道,矩陣運算)

矩陣的運算規則是矩陣A的一行乘以矩陣C的一列作為矩陣R的一行,C矩陣是圖檔中包含的ARGB資訊,R矩陣是用顔色矩陣應用于C之後的新的顔色分量,運算結果如下:

R' = aR + bG + cB + dA + e;

G' = fR + gG + hB + iA + j;

B' = kR + lG + mB + nA + o;

A' = pR + qG + rB + sA + t;

顔色矩陣并不是看上去那麼深奧,其實需要使用的參數很少,而且很有規律第一行決定紅色第二行決定綠色 第三行決定藍色,第四行決定了透明度,第五列是顔色的偏移量。下面是一個實際中使用的顔色矩陣。

進階UI之Paint(濾鏡,顔色通道,矩陣運算)

如果把這個矩陣作用于各顔色分量的話,R=A*C,計算後會發現,各個顔色分量實際上沒有任何的改變(R'=R G'=G B'=B A'=A)。

進階UI之Paint(濾鏡,顔色通道,矩陣運算)

圖1.5所示矩陣計算後會發現紅色分量增加100,綠色分量增加100,這樣的效果就是圖檔偏黃,因為紅色和綠色混合後得到黃色,黃色增加了100,圖檔當然就偏黃了。

進階UI之Paint(濾鏡,顔色通道,矩陣運算)

改變各顔色分量不僅可以通過修改第5列的顔色偏移量也可如上面矩陣所示将對應的顔色值乘以一個倍數,直接放大。上圖1.6是将綠色分量乘以2變為原來的2倍。至此已經明白了如何通過顔色矩陣來改變各顔色分量。

5.濾鏡實作

那麼在對于的實作上面android的使用非常簡單,隻需要依賴于一個API就好

// 顔色通道過濾
    /*ColorMatrix colorMartrix = new ColorMatrix(new float[]{
            1, 0,0,0,0,
            0,0,0,0,0,
            0,0,0,0,0,
            0,0,0,1,0,
    });
  paint.setColorFilter(new ColorMatrixColorFilter(colorMartrix));
      

那麼下面是我的demo代碼,大家可以去試試效果

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    setLayerType(View.LAYER_TYPE_SOFTWARE,null);
      
RectF rectF = new RectF(0,100,bitmap.getWidth(),bitmap.getHeight());
    paint.reset();
    paint.setColor(Color.RED);

    canvas.drawBitmap(bitmap,null, rectF,paint);
    // 平移運算---加法
    /*ColorMatrix colorMartrix = new ColorMatrix(new float[]{
            1, 0,0,0,0,
            0,1,0,0,100,
            0,0,1,0,0,
            0,0,0,1,0,
    });*/

    // 反相效果 -- 底片效果
   /* ColorMatrix colorMartrix = new ColorMatrix(new float[]{
            -1, 0,0,0,255,
            0,-1,0,0,255,
            0,0,-1,0,255,
            0,0,0,1,0,
    });*/
    // 縮放運算---乘法 -- 顔色增強
    /*ColorMatrix colorMartrix = new ColorMatrix(new float[]{
            1.2f, 0,0,0,0,
            0,1.2f,0,0,0,
            0,0,1.2f,0,0,
            0,0,0,1.2f,0,
    });*/

    // 黑白照片
    // 去色原理:隻要把R G B 三通道的色彩資訊設定成一樣,那麼圖像就會變成灰色,
    // 同時為了保證圖像亮度不變,同一個通道裡的R+G+B =1
    //
    /*ColorMatrix colorMartrix = new ColorMatrix(new float[]{
            0.213f, 0.715f,0.072f,0,0,
            0.213f, 0.715f,0.072f,0,0,
            0.213f, 0.715f,0.072f,0,0,
            0,0,0,1,0,
    });*/

    // 發色效果---(比如紅色和綠色交換)
    /*ColorMatrix colorMartrix = new ColorMatrix(new float[]{
            0,1,0,0,0,
            1, 0,0,0,0,
            0,0,1,0,0,
            0,0,0,1,0,
    });*/
    // 複古效果
    /*ColorMatrix colorMartrix = new ColorMatrix(new float[]{
            1/2f,1/2f,1/2f,0,0,
            1/3f, 1/3f,1/3f,0,0,
            1/4f,1/4f,1/4f,0,0,
            0,0,0,1,0,
    });*/
    // 顔色通道過濾
   ColorMatrix colorMartrix = new ColorMatrix(new float[]{
            1, 0,0,0,0,
            0,0,0,0,0,
            0,0,0,0,0,
            0,0,0,1,0,
    });

    RectF rectF2 = new RectF(600,100,600 + bitmap.getWidth(),bitmap.getHeight());
    paint.setColorFilter(new ColorMatrixColorFilter(colorMartrix));
        canvas.drawBitmap(bitmap,null, rectF2,paint);
    }