內(nèi)容均來自
矩陣的概念
略,參考源博客
色彩矩陣
對于色彩的存儲,Bitmap類使用一個32位的數(shù)值來保存。紅、綠、藍及透明度各占8位,每一個色彩分量的取值范圍是0-255。透明度為0表示完全透明,為255時,色彩完全可見。
色彩的矩陣表示
四階矩形
一個色彩信息包含R、G、B、Alpha信息,所以,必然要使用一個4階色彩變換矩陣來修改色彩的每一個分量值(如果不想修改,對應(yīng)的數(shù)值給0):

上圖中,每個顏色占據(jù)一行,即占據(jù)一個維度,除了本分量值外,其他為0;
矩陣計算時,每行中的各個數(shù)字與另一矩陣中的一列中的各個數(shù)字分別相乘,然后相加為新值;
注意:對于色彩變換矩陣,這里的色彩順序是R、G、B、A而不是A、R、G、B!
將色彩(0,255,0,255)更改為半透明時,可以使用下面的的矩陣運算來表示:

五階矩陣
使用四階矩陣完全可以改變圖片的RGBA值了,但考慮一種情況,如果我們只想在原有的R色上增加一些分量呢?
這時,我們就得再多加一階來表示平移變換。
一個既包含線性變換(乘法),又包含平移變換(加法)的組合變換,稱為仿射變換。
使用四階的色彩變換矩陣來修改色彩,只能夠?qū)ι实拿恳粋€分量值進行乘(除)運算,如果要對這些分量值進行加減法的運算(平移變換),只能通過五階矩陣來完成。
比如:
1、紅色分量值更改為原來的2倍(線性);
2、綠色分量增加100(平移);
則使用4階矩陣的乘法無法實現(xiàn),所以,應(yīng)該在四階色彩變換矩陣上增加一個“啞元坐標”,來實現(xiàn)所列的矩陣運算:

Android中的色彩矩陣
色彩變換矩陣的表示形式,是五階的,在默認情況下,色彩變換矩陣的形式:

Android中的色彩矩陣是用ColorMatrix類來表示的,使用如下:
// 生成色彩矩陣
ColorMatrix colorMatrix = new ColorMatrix(new float[]{
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 0.5, 0,
});
mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
示例代碼1(單個顏色通道輸出)
val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
setARGB(255, 200, 100, 100)
}
// 繪制原始位圖
canvas.drawRect(0f, 0f, width / 2.toFloat(), 600f, paint)
canvas.translate(width / 2.toFloat(), 0f)
// 生成色彩矩陣(把紅色和藍色都去掉,僅顯示綠色值)
val colorMatrix = ColorMatrix(floatArrayOf(
0f, 0f, 0f, 0f, 0f, // RED
0f, 1f, 0f, 0f, 0f, // GREEN
0f, 0f, 0f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA
paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
canvas.drawRect(0f, 0f, width / 2.toFloat(), 600f, paint)

示例代碼2(圖片多顏色單個綠色通道輸出)
private var bitmap: Bitmap
private var paint: Paint
init {
bitmap = BitmapFactory.decodeResource(context.resources, R.mipmap.chrome)
paint = Paint(Paint.ANTI_ALIAS_FLAG)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// 繪制原始位圖
canvas.drawBitmap(bitmap, null, Rect(0, 0, 250, 250 * bitmap.height / bitmap.width), paint)
canvas.translate(0f, 260f)
// 生成色彩矩陣(把紅色和藍色都去掉,僅顯示綠色值)
val colorMatrix = ColorMatrix(floatArrayOf(
0f, 0f, 0f, 0f, 0f, // RED
0f, 1f, 0f, 0f, 0f, // GREEN
0f, 0f, 0f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA
paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
canvas.drawBitmap(bitmap, null, Rect(0, 0, 250, 250 * bitmap.height / bitmap.width), paint)
}

色彩的幾種運算方式
色彩的平移運算
有了上面的鋪墊,這個理解起來就簡單了;
色彩的平移運算,實際上就是色彩的加法運算。就是在色彩變換矩陣的最后一行加上某個值;這樣可以增加特定色彩的飽和度;

示例:為上圖增大綠色飽和度
// 飽和度綠色
val colorMatrix = ColorMatrix(floatArrayOf(
1f, 0f, 0f, 0f, 0f, // RED
0f, 1f, 0f, 0f, 50f, // GREEN
0f, 0f, 1f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA

注意:由于圖片是由一個個像素組成的,所以用每個像素所對應(yīng)的色彩數(shù)組,來乘轉(zhuǎn)換矩陣,結(jié)果就是轉(zhuǎn)換后的當前點的顏色值;所以,在應(yīng)用ColorMatrics后,圖片中每個像素的綠色值都增加了50;
色彩平移除了增加指定顏色飽和度以外,另一個應(yīng)用就是色彩反轉(zhuǎn):
// 色彩反相
val colorMatrix = ColorMatrix(floatArrayOf(
-1f, 0f, 0f, 0f, 255f, // RED
0f, -1f, 0f, 0f, 255f, // GREEN
0f, 0f, -1f, 0f, 255f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA

色彩的縮放
色彩的縮放運算其實就是色彩的乘法運算。在色彩矩陣對角線上的分別代表R、G、B、A的幾個值,將其分別乘以指定的值。這就是所謂的縮放變換。

可以針對某一個顏色值進行放大縮小運算,但當對R、G、B、A同時進行放大縮小時,就是對亮度進行調(diào)節(jié)!
// 色彩縮放,(全部為增加亮度)
val colorMatrix = ColorMatrix(floatArrayOf(
1.2f, 0f, 0f, 0f, 0f, // RED
0f, 1.2f, 0f, 0f, 0f, // GREEN
0f, 0f, 1.2f, 0f, 0f, // BLUE
0f, 0f, 0f, 1.2f, 0f)) // ALPHA

縮放變換的特殊應(yīng)用(通道輸出)
上面我們輸出過綠色通道;
由于在色彩變換矩陣中,對角線上的數(shù)的取值范圍是從0-1的,所以當取0時,這個色彩就完全不顯示,所以當我們R、G都取0,而獨有B取1時,就只顯示了藍色,所形成的圖像也就是我們通常說的藍色通道;

色彩的旋轉(zhuǎn)運算
RGB色是如何旋轉(zhuǎn)的呢,首先用R、G、B三色建立立體坐標系:

我們可以把一個色彩值看成三維空間里的一個點,色彩值的三個分量可以看成該點的坐標(三維坐標)。我們先不考慮,在三個維度綜合情況下是怎么旋轉(zhuǎn)的,我們先看看,在某個軸做為Z軸,在另兩個軸形成的平面上旋轉(zhuǎn)的情況,下圖分析了,在將藍色軸做為Z軸,僅在紅—綠平面上旋轉(zhuǎn)a度的情況:

在圖中,我們可以看到,在旋轉(zhuǎn)后,原R在R軸的分量變?yōu)?原R*cosa(投影);
但原G分量在旋轉(zhuǎn)后,在R軸上也有了分量,但分量落在了負軸上,所以我們要減去這部分分量,所以最終的結(jié)果是最終的R=原R*cosa-原G*sina;
下面就看下關(guān)于幾種旋轉(zhuǎn)計算及結(jié)果矩陣,(注意:這幾個圖只標記了原X軸色彩分量的旋轉(zhuǎn),沒有把Y軸色彩分量的旋轉(zhuǎn)標記出來)
繞藍色軸旋轉(zhuǎn)a°

對應(yīng)的色彩變換矩陣是:

繞紅色軸旋轉(zhuǎn)a度

對應(yīng)的色彩變換矩陣是:

繞綠色軸旋轉(zhuǎn)a度

對應(yīng)的色彩變換矩陣是:

當圍繞紅色軸進行色彩旋轉(zhuǎn)時,由于當前紅色軸的色彩是不變的,而僅利用三角函數(shù)來動態(tài)的變更綠色和藍色的顏色值。這種改變就叫做色相調(diào)節(jié)!當圍繞紅色軸旋轉(zhuǎn)時,是對圖片就行紅色色相的調(diào)節(jié);同理,當圍繞藍色顏色軸旋轉(zhuǎn)時,就是對圖片就行藍色色相調(diào)節(jié);當然,當圍繞綠色軸旋轉(zhuǎn)時,就是對圖片進行綠色色相的調(diào)節(jié).
色彩的投射運算
色彩矩陣運算的公式:

紅色運算給單獨拉了出來,紅色標記的那幾個元素a12,a13,a14,在運算中,是利用G、B、A的顏色值的分量來增加紅色值的。
比如:

注意:最終結(jié)果的220=0.2*100+1*200,可見綠色分量在原有綠色分量的基礎(chǔ)上,增加了紅色分量值的0.2倍;利用其它色彩分量的倍數(shù)來更改自己色彩分量的值,這種運算就叫投射運算。
下圖陰影部分;對這些值進行修改時,修改所使用的增加值來自于其它色彩分量的信息。

色彩投射的應(yīng)用之 - 黑白照片
// 黑白
colorMatrix = ColorMatrix(floatArrayOf(
0.213f, 0.715f, 0.072f, 0f, 0f, // RED
0.213f, 0.715f, 0.072f, 0f, 0f, // GREEN
0.213f, 0.715f, 0.072f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA

彩色轉(zhuǎn)黑白原理:
- 只要把RGB三通道的色彩信息設(shè)置成一樣;即:R=G=B,那么圖像就變成了灰色;
- 為了保證圖像亮度不變,同一個通道中的R+G+B=1:如:0.213+0.715+0.072=1;
三個數(shù)字的由來:0.213, 0.715, 0.072;
按理說應(yīng)該把RGB平分,都是0.3333333,可以試試0.33,原因還是看源博客吧;
投射運算之色彩反色
利用色彩矩陣將兩個顏色反轉(zhuǎn),這種操作就叫做色彩反色 (如下:紅綠色反轉(zhuǎn))
colorMatrix = ColorMatrix(floatArrayOf(
0f, 1f, 0f, 0f, 0f, // RED, 由綠色替換
1f, 0f, 0f, 0f, 0f, // GREEN,由紅色替換
0f, 0f, 1f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA

投射運算之顏色變舊
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函數(shù)
Android中ColorMatrix自帶了一些函數(shù)來幫我們完成一些調(diào)整飽和度、色彩旋轉(zhuǎn)等操作;
ColorMatrix的構(gòu)造函數(shù):
ColorMatrix() // new 一個新的
ColorMatrix(float[] src) // 指定創(chuàng)建
ColorMatrix(ColorMatrix src) // 以src為源copy 一個
setSaturation - 設(shè)置飽和度
通過色彩的平移運算,可單獨增加R、G、B的飽和度,但如果整體呢,就需要用到 setSaturation函數(shù)了;
// 整體增強顏色飽和度,即同時增強R,G,B的色彩飽和度
public void setSaturation(float sat)
參數(shù)float sat:表示把當前色彩飽和度放大的倍數(shù)。取值
- 0表示完全無色彩,即灰度圖像(黑白圖像;
- 1時,表示色彩不變動;
- 大于1時,顯示色彩過度飽和,飽和度放大多少倍;

setScale - 色彩縮放
// 分別對應(yīng)R,G,B,A顏色值的縮放倍數(shù)
public void setScale(float rScale, float gScale, float bScale,float aScale)
效果如下:

setRotate-色彩旋轉(zhuǎn)
參考源博客,上面介紹了色彩旋轉(zhuǎn)的原理,涉及到正余弦函數(shù),同時Android封裝好了色彩旋轉(zhuǎn)的函數(shù):
/**
* 將旋轉(zhuǎn)圍繞某一個顏色軸旋轉(zhuǎn) degrees 度
* axis=0 圍繞紅色軸旋轉(zhuǎn)
* axis=1 圍繞綠色軸旋轉(zhuǎn)
* axis=2 圍繞藍色軸旋轉(zhuǎn)
*/
public void setRotate(int axis, float degrees);
示例代碼
seekBar {
id = android.R.id.text1
max = 360 // 整個旋轉(zhuǎn)度數(shù)的范圍是-180到180,360為一個周期
progress = 180
onSeekBarChangeListener {
onProgressChanged { _, progress, _ ->
view7.colorMatrix.setRotate(0,progress-180*.10f)
view7.postInvalidate()
}
}
}.lparams(width = ViewGroup.LayoutParams.MATCH_PARENT)

ColorMatrix相乘
涉及到矩陣相乘;ColorMatrix矩陣相乘涉及到3個方法:
// matA*matB,然后將結(jié)果做為當前ColorMatrix的值(覆蓋當前)
public void setConcat(ColorMatrix matA, ColorMatrix matB)
// 就是將當前的矩陣乘以 prematrix
public void preConcat(ColorMatrix prematrix)
// postmatrix*當前矩陣,上面為前乘,這里為后乘
public void postConcat(ColorMatrix postmatrix)
矩陣的前乘 與 后乘 的結(jié)果是不一樣的;
setConcat()
下面兩個顏色矩陣相乘:

因為矩陣相乘的原則是:第一個矩陣的列數(shù)需等于第2個矩陣的行數(shù),明顯這里不是這樣的,那如何相乘呢?
Android提供了一個方案,讓這兩個矩陣相乘,就是把第一個矩陣的最后一列單獨拿出來,另外加到結(jié)果上;

參考源博客