自定義控件繪制(Paint之ColorMatrix與濾鏡)篇六

內(nèi)容均來(lái)自

  1. https://blog.csdn.net/harvic880925/article/details/51187277

矩陣的概念

略,參考源博客

色彩矩陣

對(duì)于色彩的存儲(chǔ)弄屡,Bitmap類(lèi)使用一個(gè)32位的數(shù)值來(lái)保存屋彪。紅、綠允粤、藍(lán)及透明度各占8位,每一個(gè)色彩分量的取值范圍是0-255。透明度為0表示完全透明嚷硫,為255時(shí),色彩完全可見(jiàn)始鱼。

色彩的矩陣表示

四階矩形

一個(gè)色彩信息包含R仔掸、G、B医清、Alpha信息起暮,所以,必然要使用一個(gè)4階色彩變換矩陣來(lái)修改色彩的每一個(gè)分量值(如果不想修改会烙,對(duì)應(yīng)的數(shù)值給0):

圖片來(lái)自源博客

上圖中负懦,每個(gè)顏色占據(jù)一行,即占據(jù)一個(gè)維度柏腻,除了本分量值外纸厉,其他為0;
矩陣計(jì)算時(shí)葫盼,每行中的各個(gè)數(shù)字與另一矩陣中的一列中的各個(gè)數(shù)字分別相乘残腌,然后相加為新值;

注意:對(duì)于色彩變換矩陣贫导,這里的色彩順序是R抛猫、G、B孩灯、A而不是A闺金、R、G峰档、B败匹!
將色彩(0,255讥巡,0掀亩,255)更改為半透明時(shí),可以使用下面的的矩陣運(yùn)算來(lái)表示:

來(lái)自源博客(修改A所在行分量)

五階矩陣

使用四階矩陣完全可以改變圖片的RGBA值了欢顷,但考慮一種情況槽棍,如果我們只想在原有的R色上增加一些分量呢?
這時(shí),我們就得再多加一階來(lái)表示平移變換炼七。
一個(gè)既包含線性變換(乘法)缆巧,又包含平移變換(加法)的組合變換,稱(chēng)為仿射變換豌拙。
使用四階的色彩變換矩陣來(lái)修改色彩陕悬,只能夠?qū)ι实拿恳粋€(gè)分量值進(jìn)行乘(除)運(yùn)算,如果要對(duì)這些分量值進(jìn)行加減法的運(yùn)算(平移變換)按傅,只能通過(guò)五階矩陣來(lái)完成捉超。

比如:
1、紅色分量值更改為原來(lái)的2倍(線性)唯绍;
2狂秦、綠色分量增加100(平移);
則使用4階矩陣的乘法無(wú)法實(shí)現(xiàn)推捐,所以,應(yīng)該在四階色彩變換矩陣上增加一個(gè)“啞元坐標(biāo)”侧啼,來(lái)實(shí)現(xiàn)所列的矩陣運(yùn)算:

來(lái)自源博客

Android中的色彩矩陣

色彩變換矩陣的表示形式牛柒,是五階的,在默認(rèn)情況下痊乾,色彩變換矩陣的形式:


copy from 源博客

Android中的色彩矩陣是用ColorMatrix類(lèi)來(lái)表示的皮壁,使用如下:

// 生成色彩矩陣    
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(單個(gè)顏色通道輸出)

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)

// 生成色彩矩陣(把紅色和藍(lán)色都去掉,僅顯示綠色值)
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)
效果-過(guò)濾了其他顏色哪审,保留綠色

示例代碼2(圖片多顏色單個(gè)綠色通道輸出)

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)
    // 生成色彩矩陣(把紅色和藍(lán)色都去掉蛾魄,僅顯示綠色值)
    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)
}
效果圖

色彩的幾種運(yùn)算方式

色彩的平移運(yùn)算

有了上面的鋪墊,這個(gè)理解起來(lái)就簡(jiǎn)單了湿滓;
色彩的平移運(yùn)算滴须,實(shí)際上就是色彩的加法運(yùn)算。就是在色彩變換矩陣的最后一行加上某個(gè)值叽奥;這樣可以增加特定色彩的飽和度扔水;

圖片來(lái)自源博客

示例:為上圖增大綠色飽和度

// 飽和度綠色
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
增加綠色飽和度

注意:由于圖片是由一個(gè)個(gè)像素組成的,所以用每個(gè)像素所對(duì)應(yīng)的色彩數(shù)組朝氓,來(lái)乘轉(zhuǎn)換矩陣魔市,結(jié)果就是轉(zhuǎn)換后的當(dāng)前點(diǎn)的顏色值;所以赵哲,在應(yīng)用ColorMatrics后待德,圖片中每個(gè)像素的綠色值都增加了50;

色彩平移除了增加指定顏色飽和度以外枫夺,另一個(gè)應(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
反相對(duì)比

色彩的縮放

色彩的縮放運(yùn)算其實(shí)就是色彩的乘法運(yùn)算将宪。在色彩矩陣對(duì)角線上的分別代表R、G、B涧偷、A的幾個(gè)值簸喂,將其分別乘以指定的值。這就是所謂的縮放變換燎潮。


圖片來(lái)自源博客

可以針對(duì)某一個(gè)顏色值進(jìn)行放大縮小運(yùn)算喻鳄,但當(dāng)對(duì)R、G确封、B除呵、A同時(shí)進(jìn)行放大縮小時(shí),就是對(duì)亮度進(jìn)行調(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
色彩縮放-亮度放大1.2倍

縮放變換的特殊應(yīng)用(通道輸出)

上面我們輸出過(guò)綠色通道;
由于在色彩變換矩陣中秉剑,對(duì)角線上的數(shù)的取值范圍是從0-1的泛豪,所以當(dāng)取0時(shí),這個(gè)色彩就完全不顯示侦鹏,所以當(dāng)我們R诡曙、G都取0,而獨(dú)有B取1時(shí)略水,就只顯示了藍(lán)色价卤,所形成的圖像也就是我們通常說(shuō)的藍(lán)色通道;

各顏色通道下的效果

色彩的旋轉(zhuǎn)運(yùn)算

RGB色是如何旋轉(zhuǎn)的呢渊涝,首先用R慎璧、G、B三色建立立體坐標(biāo)系:

圖片來(lái)自源博客

我們可以把一個(gè)色彩值看成三維空間里的一個(gè)點(diǎn)跨释,色彩值的三個(gè)分量可以看成該點(diǎn)的坐標(biāo)(三維坐標(biāo))胸私。我們先不考慮,在三個(gè)維度綜合情況下是怎么旋轉(zhuǎn)的鳖谈,我們先看看盖文,在某個(gè)軸做為Z軸,在另兩個(gè)軸形成的平面上旋轉(zhuǎn)的情況蚯姆,下圖分析了五续,在將藍(lán)色軸做為Z軸,僅在紅—綠平面上旋轉(zhuǎn)a度的情況:

圖片來(lái)自源博客

在圖中龄恋,我們可以看到疙驾,在旋轉(zhuǎn)后,原R在R軸的分量變?yōu)?原R*cosa(投影);
但原G分量在旋轉(zhuǎn)后郭毕,在R軸上也有了分量它碎,但分量落在了負(fù)軸上,所以我們要減去這部分分量,所以最終的結(jié)果是最終的R=原R*cosa-原G*sina;

下面就看下關(guān)于幾種旋轉(zhuǎn)計(jì)算及結(jié)果矩陣扳肛,(注意:這幾個(gè)圖只標(biāo)記了原X軸色彩分量的旋轉(zhuǎn)傻挂,沒(méi)有把Y軸色彩分量的旋轉(zhuǎn)標(biāo)記出來(lái))

繞藍(lán)色軸旋轉(zhuǎn)a°

圖片來(lái)自源博客

對(duì)應(yīng)的色彩變換矩陣是:
圖片來(lái)自源博客

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

image.png

對(duì)應(yīng)的色彩變換矩陣是:
image.png

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

image.png

對(duì)應(yīng)的色彩變換矩陣是:
image.png

當(dāng)圍繞紅色軸進(jìn)行色彩旋轉(zhuǎn)時(shí),由于當(dāng)前紅色軸的色彩是不變的挖息,而僅利用三角函數(shù)來(lái)動(dòng)態(tài)的變更綠色和藍(lán)色的顏色值金拒。這種改變就叫做色相調(diào)節(jié)!當(dāng)圍繞紅色軸旋轉(zhuǎn)時(shí)套腹,是對(duì)圖片就行紅色色相的調(diào)節(jié)绪抛;同理,當(dāng)圍繞藍(lán)色顏色軸旋轉(zhuǎn)時(shí)电禀,就是對(duì)圖片就行藍(lán)色色相調(diào)節(jié)幢码;當(dāng)然,當(dāng)圍繞綠色軸旋轉(zhuǎn)時(shí)尖飞,就是對(duì)圖片進(jìn)行綠色色相的調(diào)節(jié).

色彩的投射運(yùn)算

色彩矩陣運(yùn)算的公式:

源博客中的圖片

紅色運(yùn)算給單獨(dú)拉了出來(lái)症副,紅色標(biāo)記的那幾個(gè)元素a12,a13,a14,在運(yùn)算中,是利用G政基、B瓦糕、A的顏色值的分量來(lái)增加紅色值的。

比如:


源博客中的圖片

注意:最終結(jié)果的220=0.2*100+1*200,可見(jiàn)綠色分量在原有綠色分量的基礎(chǔ)上腋么,增加了紅色分量值的0.2倍;利用其它色彩分量的倍數(shù)來(lái)更改自己色彩分量的值亥揖,這種運(yùn)算就叫投射運(yùn)算珊擂。

下圖陰影部分;對(duì)這些值進(jìn)行修改時(shí)费变,修改所使用的增加值來(lái)自于其它色彩分量的信息摧扇。

源博客中的圖片

色彩投射的應(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)黑白原理:

  1. 只要把RGB三通道的色彩信息設(shè)置成一樣;即:R=G=B挚歧,那么圖像就變成了灰色扛稽;
  2. 為了保證圖像亮度不變,同一個(gè)通道中的R+G+B=1:如:0.213+0.715+0.072=1滑负;

三個(gè)數(shù)字的由來(lái):0.213, 0.715, 0.072在张;
按理說(shuō)應(yīng)該把RGB平分,都是0.3333333矮慕,可以試試0.33帮匾,原因還是看源博客吧;

投射運(yùn)算之色彩反色
利用色彩矩陣將兩個(gè)顏色反轉(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
紅綠色反轉(zhuǎn)-左為原圖

投射運(yùn)算之顏色變舊

        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ù)來(lái)幫我們完成一些調(diào)整飽和度、色彩旋轉(zhuǎn)等操作;

ColorMatrix的構(gòu)造函數(shù):

ColorMatrix()  // new 一個(gè)新的
ColorMatrix(float[] src)   // 指定創(chuàng)建
ColorMatrix(ColorMatrix src)   // 以src為源copy 一個(gè)

setSaturation - 設(shè)置飽和度

通過(guò)色彩的平移運(yùn)算,可單獨(dú)增加R螺句、G虽惭、B的飽和度,但如果整體呢蛇尚,就需要用到 setSaturation函數(shù)了芽唇;

// 整體增強(qiáng)顏色飽和度,即同時(shí)增強(qiáng)R,G,B的色彩飽和度  
public void setSaturation(float sat) 

參數(shù)float sat:表示把當(dāng)前色彩飽和度放大的倍數(shù)佣蓉。取值

  • 0表示完全無(wú)色彩披摄,即灰度圖像(黑白圖像;
  • 1時(shí)勇凭,表示色彩不變動(dòng)疚膊;
  • 大于1時(shí),顯示色彩過(guò)度飽和虾标,飽和度放大多少倍寓盗;
色彩飽和過(guò)渡效果

setScale - 色彩縮放

// 分別對(duì)應(yīng)R,G,B,A顏色值的縮放倍數(shù)
public void setScale(float rScale, float gScale, float bScale,float aScale)  

效果如下:


色彩縮放

setRotate-色彩旋轉(zhuǎn)

參考源博客,上面介紹了色彩旋轉(zhuǎn)的原理璧函,涉及到正余弦函數(shù)傀蚌,同時(shí)Android封裝好了色彩旋轉(zhuǎn)的函數(shù):

/** 
 * 將旋轉(zhuǎn)圍繞某一個(gè)顏色軸旋轉(zhuǎn) degrees 度
 * axis=0 圍繞紅色軸旋轉(zhuǎn) 
 * axis=1 圍繞綠色軸旋轉(zhuǎn) 
 * axis=2 圍繞藍(lán)色軸旋轉(zhuǎn) 
 */  
public void setRotate(int axis, float degrees);  

示例代碼

seekBar {
      id = android.R.id.text1
      max = 360   // 整個(gè)旋轉(zhuǎn)度數(shù)的范圍是-180到180,360為一個(gè)周期
      progress = 180
      onSeekBarChangeListener {
              onProgressChanged { _, progress, _ ->
                      view7.colorMatrix.setRotate(0,progress-180*.10f)
                      view7.postInvalidate()
              }
     }
}.lparams(width = ViewGroup.LayoutParams.MATCH_PARENT)
色彩rotate

ColorMatrix相乘

涉及到矩陣相乘蘸吓;ColorMatrix矩陣相乘涉及到3個(gè)方法:

// matA*matB善炫,然后將結(jié)果做為當(dāng)前ColorMatrix的值(覆蓋當(dāng)前)
public void setConcat(ColorMatrix matA, ColorMatrix matB)  

// 就是將當(dāng)前的矩陣乘以 prematrix
public void preConcat(ColorMatrix prematrix)

// postmatrix*當(dāng)前矩陣,上面為前乘库继,這里為后乘
public void postConcat(ColorMatrix postmatrix)

矩陣的前乘 與 后乘 的結(jié)果是不一樣的箩艺;

setConcat()

下面兩個(gè)顏色矩陣相乘:


圖片來(lái)自源博客

因?yàn)榫仃囅喑说脑瓌t是:第一個(gè)矩陣的列數(shù)需等于第2個(gè)矩陣的行數(shù),明顯這里不是這樣的宪萄,那如何相乘呢艺谆?
Android提供了一個(gè)方案,讓這兩個(gè)矩陣相乘拜英,就是把第一個(gè)矩陣的最后一列單獨(dú)拿出來(lái)静汤,另外加到結(jié)果上;


圖片來(lái)自源博客

參考源博客

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末居凶,一起剝皮案震驚了整個(gè)濱河市虫给,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌侠碧,老刑警劉巖狰右,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異舆床,居然都是意外死亡棋蚌,警方通過(guò)查閱死者的電腦和手機(jī)嫁佳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)谷暮,“玉大人蒿往,你說(shuō)我怎么就攤上這事∈遥” “怎么了瓤漏?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)颊埃。 經(jīng)常有香客問(wèn)我蔬充,道長(zhǎng),這世上最難降的妖魔是什么班利? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任饥漫,我火速辦了婚禮,結(jié)果婚禮上罗标,老公的妹妹穿的比我還像新娘庸队。我一直安慰自己,他們只是感情好闯割,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布彻消。 她就那樣靜靜地躺著,像睡著了一般宙拉。 火紅的嫁衣襯著肌膚如雪宾尚。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天谢澈,我揣著相機(jī)與錄音煌贴,去河邊找鬼。 笑死澳化,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的稳吮。 我是一名探鬼主播缎谷,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼灶似!你這毒婦竟也來(lái)了列林?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤酪惭,失蹤者是張志新(化名)和其女友劉穎希痴,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體春感,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡砌创,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年虏缸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫩实。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡刽辙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出甲献,到底是詐尸還是另有隱情宰缤,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布晃洒,位于F島的核電站慨灭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏球及。R本人自食惡果不足惜氧骤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望桶略。 院中可真熱鬧语淘,春花似錦、人聲如沸际歼。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鹅心。三九已至吕粗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旭愧,已是汗流浹背颅筋。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留输枯,地道東北人议泵。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像桃熄,于是被迫代替她去往敵國(guó)和親先口。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容