Android關於Color你所知道的和不知道的一切
零、前言
1.做安卓的大多應該對顏色不太敏感,畢竟咱是敲程式碼的,顏色有設計師呢。
2.不過作為一名在大學被顏色薰(陶)過四年的人,對顏色多少還是挺親切的(雖然當時挺討厭的)
3.紀念也好,記錄也罷,為它寫篇總結也理所應當
4.如果你覺得並不需要了解關於顏色的知識,那你可以將本文當做一篇科普文(出去跟人家吹吹牛還是夠用的)
一、顏色知識科普:
這一切都要從光開始:
有個叫牛頓的人拿一塊三稜鏡將太陽光折射出了彩色產生色散現象: ----色散現象說明光在介質中的速度v=c/n(或折射率n)隨光的頻率f而變,從而:證明了光具有波動性
光的色散 | 光的色散圖示 |
---|---|
![]() timg.jpg |
![]() 光的色散.jpg |
關於黑與白
問:如果把所有非黑的顏料混合,會得到什麼?----感覺一團糟,應該是黑色吧 問:如果把所有非白的光混合,會得到什麼?----感覺越來越亮,應該是白色吧
為何光的疊加和顏料的疊加會產生相反的效果?
從一開始,這個問題就困擾著我,也將一直困擾這我... 如果說[肉眼所見到的光線,是由波長範圍很窄的電磁波產生的,不同波長的電磁波表現為不同的顏色], 那光的疊加也就是波的疊加,貌似有個[波動方程]吧...只能這樣說服自己: 光色疊加是減色模式:會越來約"淡" 顏料色疊加是加色模式:會越來約"濃"

原色.png
色彩模式
1.RGB:裝置相關的顏色模型
安卓敲程式碼的多少都用過 #ffff00
表示黃色吧,這是RGB的一種表現形式
你也可以用"R:255,G:255,B:0"來表示黃色,其實兩者是一個意思,只不過是10進位制和16進位制的轉化

RGB.png
RGB的位數:
RGB還有位數的區別,也就是一個顏色佔幾位,一般是8位, 也就是用1個位元組表示一種顏色(一個位元組8位) 1個位元組(8位)每種顏色有0~255共256種中顏色,三色共表達:256*256*256=16,777,216種顏色 所以RGB並不能代表所有顏色,它只是一個子集,自然界的顏色是無窮的。人類只能模擬 16位是2個位元組代表一種顏色,每種顏色有0~65535共65536中顏色, 三色共表達:65536*65536*65536=279213318656種顏色
2.ARGB:裝置相關的顏色模型(加透明度)
即在RGB的基礎上新增新增透明度
顏色通道的概念(自己的理解,僅供參考):
大學那會學ps,動不動紅色通道,Alpha通道的,搞得雲裡霧裡,現在想想 拿ARGB_8888(八位)來說,就相當有四道牆,每道牆上有256扇門分別標上0~255數字 第一道牆叫Alpha(透明度)牆,第二道牆叫R(紅)牆,第二道牆叫G(綠)牆,第二道牆叫B(藍)牆 現在你要從這四道牆的門走到終點,每次進門拿著門牌號,當你走到終點時,門牌號加起來就是顏色 那門,就是通道,如果進紅色的0門(俗稱:紅色通道關閉),從表現上來看最終顏色不帶紅色,如下下圖

通道.png

不開紅色通道.png
為什麼程式設計裡用 int
作為顏色?
眾所周知:一個int佔4個位元組,也就是4*8個位,剛好用來盛放ARGB_8888。

int表示顏色.png
3.CMYK:C(青)M(品紅)Y(黃)K(黑色)
作為程式員對CMYK瞭解估計不多,畢竟都在螢幕上,是ARGB的的天下 對於列印使用CMYK,符號是%,以不同顏色的百分比調色,理論上只應該有CMY就行了 但實際黑色(K)的要比其他三色更容易生產,用三張一百塊合成為一張十塊錢估計沒人會這麼做 至於為什麼叫K...也許是RGB先出來的,然後不能叫B,只能叫K了
4.HSV:
看到值就能想到大概的顏色
顏色有三個維度屬性:色相、明度和飽和度 HSV模型對應於: 圓柱座標系中的一個圓錐形子集,圓錐的頂面對應於V=1。 它包含RGB模型中的R=1,G=1,B=1三個面,所代表的顏色較亮。 色彩H由繞V軸的旋轉角給定。紅色對應於角度0°,綠色對應於角度120°,藍色對應於角度240°。 在HSV顏色模型中,每一種顏色和它的補色相差180°。飽和度S取值從0到1,所以圓錐頂面的半徑為1。

hsv.png
5.看一下黃色的幾種表達方式:

黃色.png
RGB:R:255 G:255 B:0#ffff00 CMYK:C:10% M:0 Y:83% K:0 HSV:H:60° S:100% V:100%
好了,科普結束,下面進入正題
一、Android中的Color
顏色使用場景:
1.基本使用:背景、陰影、文字顏色
2.基於Color建立的Bitmap以及疊合模式:Xfermode
3.paint中的著色、顏色過濾器
4.ColorMatrix的使用
1.常量:

Color中的預設色常量.png
2.建構函式
可見只有無引數構造可以用

Color建構函式.png
3.常用方法:
int blue = Color.BLUE;//第一種獲取藍色方法 blue = Color.parseColor("#0000FF");//第二種獲取藍色方法 blue = Color.rgb(0, 0, 255);//第三種獲取藍色方法 blue = Color.argb(255, 0, 0, 255);//第四種獲取藍色方法 blue = Color.HSVToColor(new float[]{240.0f, 1.0f, 1.0f});//第五種獲取藍色方法 blue = Color.HSVToColor(0xff0000FF);//第六種獲取藍色方法 //(吐槽:怎麼有種孔乙己說茴香豆的茴字有多少種寫法一樣...,看哪個順手就用哪個吧) float[] hsv = {0, 0, 0};//hsv陣列 Color.RGBToHSV(0, 0, 255, hsv);//將RGB轉為hsv Log.e(TAG, "onDraw: " + hsv[0]+","+hsv[1]+","+hsv[2]); //onDraw: 240.0,1.01.0
其實Color的本身並沒有太多的知識點,畢竟就是一個int而已,難點在於顏色的拼合與變換
二、Android點陣圖封裝類:Bitmap
什麼是點陣圖,前面講過顏色是按位儲存的,ARGB_8888每種顏色佔8位
相信大家都知道一張jpg或png放大後會是一個個小格子,稱為一個畫素(px),而且一個小格子是 一種顏色
也就是一張jpg或png圖片就是很多顏色的合集,而這些合集資訊都被封裝到了Bitmap類中
你可以使用Bitmap獲取任意畫素點,並修改它,對與某畫素點而言,顏色資訊是其主要的部分

畫素.png
1.重新認識Bitmap
我們一般使用Bitmap是都是用BitmapFactory來decode資源,所以並未設計太多Bitmap的操作,以致認為Bitmap=圖片
Bitmap實際是一個封裝圖片畫素資訊的類,它能顯示出來是因為View及手機的硬體
1).建立一個Bitmap:
注意區別bitmapCanvas和View中OnDraw中Canvas的區別:
這裡:bitmapCanvas是負責在點陣圖(Bitmap)上繪製,讓點陣圖記錄畫素點位資訊的
OnDraw中Canvas是用來在View上繪製,顯示在螢幕上的。
打個不恰當的比方:
你是bitmapCanvas,負責畫一張圖(Bitmap),你畫完後不能直接交給印刷人員(View)去印
需要交給審稿員(OnDraw中canvas),經過他允許才能給印刷人員
/** * 建立一個Bitmap * * @param color 背景色 * @return bitmap */ private Bitmap createBitmap(int color) { //建立一個ARGB_8888,寬高200的bitmap Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); //使用Bitmap建立一個canvas畫板,畫板上的一切都會保留在bitmap上 Canvas bitmapCanvas = new Canvas(bitmap); //接下來就是在畫板上操作 Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setColor(color); Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); bitmapCanvas.drawRect(rect, p); p.setColor(Color.GRAY); p.setStrokeWidth(3); bitmapCanvas.drawLine(0, 0, 200, 200, p); bitmapCanvas.drawLine(200, 0, 0, 200, p); return bitmap; }
OnDraw中使用Bitmap,使用Bitmap,使用Bitmap...
//審稿人統一,印刷到View上 canvas.drawBitmap(mBitmap, 100, 100, mMainPaint);

繪製bitmap.png
三、Xfermode:圖片疊合時的處理方式

Xfermode.png
終於寫到這裡了,總算與Xfermode相遇了,最喜歡分析很多的情況,這裡有18種模式,想想都激動...。
做開發的,我們應該知道src和dst吧src是源,dst是目標,在react裡就有src的原始檔,和dest的輸出檔案
圖片疊合顧名思義,必須有兩個圖片才行,這裡原圖src用藍色正方形,目標dst用綠色圓形
1.src和dst圖片

源與目標.png
/** * 建立源圖片 * * @return bitmap */ private Bitmap createSrcBitmap() { //建立一個ARGB_8888,寬高200的bitmap Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); //使用Bitmap建立一個canvas畫板,畫板上的一切都會保留在bitmap上 Canvas bitmapCanvas = new Canvas(bitmap); //接下來就是在畫板上操作 Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setColor(0x882045F3); Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); bitmapCanvas.drawRect(rect, p); return bitmap; } /** * 建立目標 * * @return bitmap */ private Bitmap createDstBitmap() { //建立一個ARGB_8888,寬高200的bitmap Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); //使用Bitmap建立一個canvas畫板,畫板上的一切都會保留在bitmap上 Canvas bitmapCanvas = new Canvas(bitmap); //接下來就是在畫板上操作 Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setColor(0xff43F41D); bitmapCanvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getHeight() / 2,p); return bitmap; }
2.疊合模式18種:android.graphics.PorterDuff.Mode
別怕,別怕,一幅圖展示一下:
public enum Mode { CLEAR(0), SRC(1), DST(2), SRC_OVER(3), DST_OVER(4), SRC_IN(5), DST_IN(6), SRC_OUT(7), DST_OUT(8), SRC_ATOP(9), DST_ATOP(10), XOR(11), DARKEN(16), LIGHTEN(17), MULTIPLY(13), SCREEN(14), ADD(12), OVERLAY(15); Mode(int nativeInt) { this.nativeInt = nativeInt; } public final int nativeInt; }
3.如何優雅地繪製下面一幅圖:
注意:測試了一下,開不開硬體加速對這東西有影響,下面在無有硬體加速: android:hardwareAccelerated="false"
mMainPaint.setXfermode(XXX);放置的順序也很重要,在下面的是疊合的源
網上有一組圖,不過沒有透明度,我對源(藍色)加了88的透明度,顯示的更清楚些
注意:看正方形框裡的內容,看正方形框裡的內容,看正方形框裡的內容!因為它是被疊合的物件

Screenshot_2018-11-10-11-17-14-46.png
private void init() { mMainPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mMainPaint.setStyle(Paint.Style.FILL); mMainPaint.setStrokeCap(Paint.Cap.ROUND); src = createSrcBitmap(); dst = createDstBitmap(); //背景圖層的筆 mLayerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mLayerPaint.setStyle(Paint.Style.FILL); mLayerPaint.setFilterBitmap(false); //文字的筆 mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setTextSize(45); Typeface typeface = Typeface.create(Typeface.MONOSPACE, Typeface.BOLD); mTextPaint.setTypeface(typeface); mTextPaint.setColor(0xffF98D1F); //虛線畫筆 mDashPaint = new Paint(); mDashPaint.setStrokeWidth(3); mDashPaint.setColor(Color.RED); mDashPaint.setStyle(Paint.Style.STROKE); //設定虛線效果new float[]{可見長度, 不可見長度},偏移值 mDashPaint.setPathEffect(new DashPathEffect(new float[]{10, 5}, 0)); mModes = new PorterDuffXfermode[]{ new PorterDuffXfermode(PorterDuff.Mode.CLEAR),//0 new PorterDuffXfermode(PorterDuff.Mode.SRC),//1 new PorterDuffXfermode(PorterDuff.Mode.DST),//2 new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),//3 new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),//4 new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),//5 new PorterDuffXfermode(PorterDuff.Mode.DST_IN),//6 new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),//7 new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),//8 new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),//9 new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),//10 new PorterDuffXfermode(PorterDuff.Mode.XOR),//11 new PorterDuffXfermode(PorterDuff.Mode.DARKEN),//12 new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),//13 new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),//14 new PorterDuffXfermode(PorterDuff.Mode.SCREEN),//15 new PorterDuffXfermode(PorterDuff.Mode.ADD),//16 new PorterDuffXfermode(PorterDuff.Mode.OVERLAY),//17 }; mModeText = new String[]{"CLEAR", "SRC", "DST", "SRC_OVER", "DST_OVER", "SRC_IN", "DST_IN", "SRC_OUT", "DST_OUT", "SRC_ATOP", "DST_ATOP", "XOR", "DARKEN", "LIGHTEN", "MULTIPLY", "SCREEN", "ADD", "OVERLAY" }; }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //建立一個圖層,在圖層上演示圖形混合後的效果 int sc = 0; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { sc = canvas.saveLayer(new RectF(0, 0, 2500, 2500), mLayerPaint); } for (int i = 0; i < 18; i++) { int line = i % 6; int row = i / 6; canvas.drawBitmap(dst, 350 * line, row * 350, mMainPaint);//目標圖象 mMainPaint.setXfermode(mModes[i]);//設定對源的疊合模式 canvas.drawBitmap(src, 100 + 350 * line, 100 + row * 350, mMainPaint); //輔助資訊 canvas.drawText(mModeText[i],100 + 350 * line, 300 + row * 350,mTextPaint); canvas.drawCircle(100 + 350 * line, 100 + row * 350, 100, mDashPaint); canvas.drawRect(100 + 350 * line, 100 + row * 350, 100 + 200 + 350 * line, 100 + 200 + row * 350, mDashPaint); } canvas.restoreToCount(sc); }
四、著色器:Shader(本節在Paint篇也有,為保全Color篇的完整性,這裡cv了一下)
一個很簡單的類,有5個子類:

Shader.png
1.線性漸變:
1).new LinearGradient(漸變起點x,y,漸變終點x,y,漸變色1,漸變色2,漸變模式)
漸變模式: Shader.TileMode.
[ MIRROR
| CLAMP
| REPEAT
] (圖中很形象,就不解釋了)
int colorStart = Color.parseColor("#84F125"); int colorEnd = Color.parseColor("#5825F1"); canvas.save(); canvas.translate(mCoo.x, mCoo.y); mRedPaint.setStyle(Paint.Style.FILL); mRedPaint.setShader( new LinearGradient( -200, 0, 200, 0, colorStart, colorEnd, Shader.TileMode.MIRROR )); canvas.drawRect(-400,-200,400,-100,mRedPaint); canvas.translate(0, 150); mRedPaint.setShader( new LinearGradient( -100, 0, 100, 0, colorStart, colorEnd, Shader.TileMode.CLAMP )); canvas.drawRect(-400,-200,400,-100,mRedPaint); canvas.translate(0, 150); mRedPaint.setShader( new LinearGradient( -100, 0, 100, 0, colorStart, colorEnd, Shader.TileMode.REPEAT )); canvas.drawRect(-400,-200,400,-100,mRedPaint);

線性漸變.png
2).多色多點漸變:LinearGradient(漸變起點x,y,漸變終點x,y,顏色陣列,位置百分點陣列0~1,漸變模式)

多色漸變.png
int[] colors = new int[]{ Color.parseColor("#F60C0C"),//紅 Color.parseColor("#F3B913"),//橙 Color.parseColor("#E7F716"),//黃 Color.parseColor("#3DF30B"),//綠 Color.parseColor("#0DF6EF"),//青 Color.parseColor("#0829FB"),//藍 Color.parseColor("#B709F4"),//紫 }; float[] pos = new float[]{ 1.f / 7, 2.f / 7, 3.f / 7, 4.f / 7, 5.f / 7, 6.f / 7, 1 }; canvas.translate(0, 150); mRedPaint.setShader( new LinearGradient( -300, 0, 300, 0, colors, pos, Shader.TileMode.CLAMP )); canvas.drawRect(-400, -200, 400, -100, mRedPaint);
2.徑向漸變:RadialGradient
1).兩色漸變:RadialGradient(漸變中心,漸變半徑,顏色1,顏色2,漸變模式)
canvas.translate(mCoo.x, mCoo.y); int colorStart = Color.parseColor("#84F125"); int colorEnd = Color.parseColor("#5825F1"); mRedPaint.setStyle(Paint.Style.FILL); mRedPaint.setShader( new RadialGradient( 0,0,50, colorStart, colorEnd, Shader.TileMode.MIRROR )); canvas.drawCircle(0, 0, 150, mRedPaint); canvas.translate(350, 0); mRedPaint.setShader( new RadialGradient( 0,0,50, colorStart, colorEnd, Shader.TileMode.CLAMP )); canvas.drawCircle(0, 0, 150, mRedPaint); canvas.translate(350, 0); mRedPaint.setShader( new RadialGradient( 0,0,50, colorStart, colorEnd, Shader.TileMode.REPEAT )); canvas.drawCircle(0, 0, 150, mRedPaint);

徑像漸變.png
2).多色多點徑向漸變:
RadialGradient(漸變中心,漸變半徑,漸變模式,顏色陣列,位置百分點陣列0~1,漸變模式)

多色徑向漸變.png
int[] colors = new int[]{ Color.parseColor("#F60C0C"),//紅 Color.parseColor("#F3B913"),//橙 Color.parseColor("#E7F716"),//黃 Color.parseColor("#3DF30B"),//綠 Color.parseColor("#0DF6EF"),//青 Color.parseColor("#0829FB"),//藍 Color.parseColor("#B709F4"),//紫 }; float[] pos = new float[]{ 1.f / 7, 2.f / 7, 3.f / 7, 4.f / 7, 5.f / 7, 6.f / 7, 1 }; mRedPaint.setStyle(Paint.Style.FILL); mRedPaint.setShader( new RadialGradient( 0, 0, 200, colors, pos, Shader.TileMode.CLAMP )); canvas.drawCircle(0, 0, 250, mRedPaint);
3.掃描漸變:SweepGradient
這個要比上面的簡單一點,沒有漸變的模式
雙色掃描漸變:SweepGradient(中心點x,y,顏色1,顏色2)
多色掃描漸變:SweepGradient(中心點x,y,顏色陣列,位置百分點陣列0~1)

掃描漸變.png
int colorStart = Color.parseColor("#84F125"); int colorEnd = Color.parseColor("#5825F1"); mRedPaint.setStyle(Paint.Style.FILL); mRedPaint.setShader( new SweepGradient(0, 0, colorStart, colorEnd)); canvas.drawCircle(0, 0, 150, mRedPaint); canvas.translate(400, 0); int[] colors = new int[]{ Color.parseColor("#F60C0C"),//紅 Color.parseColor("#F3B913"),//橙 Color.parseColor("#E7F716"),//黃 Color.parseColor("#3DF30B"),//綠 Color.parseColor("#0DF6EF"),//青 Color.parseColor("#0829FB"),//藍 Color.parseColor("#B709F4"),//紫 }; float[] pos = new float[]{ 1.f / 7, 2.f / 7, 3.f / 7, 4.f / 7, 5.f / 7, 6.f / 7, 1 }; mRedPaint.setShader( new SweepGradient(0, 0, colors, pos)); canvas.drawCircle(0, 0, 150, mRedPaint);
4.圖片著色器:BitmapShader(圖片,著色模式x,著色模式y)
用圖片的所有畫素點作為畫筆的顏色
1).文字的圖片底色:
//載入圖片,生成圖片著色器 Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.menu_bg); BitmapShader bs = new BitmapShader(bitmap, Shader.TileMode.CLAMP,Shader.TileMode.CLAMP); mRedPaint.setShader(bs); mRedPaint.setTextSize(150); mRedPaint.setStrokeWidth(10); mRedPaint.setStyle(Paint.Style.FILL_AND_STROKE); canvas.drawText("張風捷特烈", 0, 500, mRedPaint);

圖片著色.png
2)路徑+圖片著色器實現裁剪圖片:路徑Path相關知識見上一篇:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.menu_bg); BitmapShader bs = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mRedPaint.setShader(bs); mRedPaint.setStyle(Paint.Style.FILL); Path path = CommonPath.nStarPath(8, 500, 250); canvas.drawPath(path, mRedPaint);

使用路徑裁剪圖片.png
還有一個ComposeShader比較複雜,以後有需求會專門寫一篇
七、顏色過濾器:(Paint篇有,但本篇更加深入)
ColorFilter只有三個子類

顏色過濾器.png
1.LightingColorFilter(顏色1,顏色2):

LightingColorFilter測試.png
Bitmap mainBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.menu_bg); mRedPaint.setStyle(Paint.Style.FILL); mRedPaint.setColorFilter(new LightingColorFilter( Color.parseColor("#F00000"),//紅 Color.parseColor("#0000ff")//藍 )); canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint); canvas.translate(350, 0); mRedPaint.setColorFilter(new LightingColorFilter( Color.parseColor("#FF0000"),//紅 Color.parseColor("#00ff00")//綠 )); canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint); canvas.translate(350, 0); mRedPaint.setColorFilter(new LightingColorFilter( Color.parseColor("#FF0000"),//紅 Color.parseColor("#000000")//黑 )); canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint); canvas.restore();
下面分析一下紅藍配的結果:開啟LightingColorFilter原始碼:
/** * Create a colorfilter that multiplies the RGB channels by one color, * and then adds a second color. The alpha components of the mul and add * arguments are ignored. 建立一個顏色過濾器:用mul顏色乘以RGB通道的顏色,再加上add顏色,mul和add的透明度將被忽略 */ public LightingColorFilter(@ColorInt int mul, @ColorInt int add) { mMul = mul; mAdd = add; } //看了沒什麼感覺,又是native的方法,往上一看,有註釋 * Given a source color RGB, the resulting R'G'B' color is computed thusly: * R' = R * colorMultiply.R + colorAdd.R * G' = G * colorMultiply.G + colorAdd.G * B' = B * colorMultiply.B + colorAdd.B
這下明白了,就是顏色變換嘛----草稿紙準備好,要演算了:
注意:當相乘數大於255時,便會溢位,相當於8位容不下那麼多數,後面再進來,前面的就被推出來了
這裡為了區別,特意用#F00000來測試,結果有一點點偏差,畢竟兩次選點的點位可能有偏差

顏色運算.png
活了這麼大,第一次對顏色進行乘法和加法,對於一張圖片,加上綠色就是對每個畫素點進行這樣的運算
2.PorterDuffColorFilter(顏色,模式--PorterDuff.Mode):
PorterDuff.Mode是不是很熟悉,看上面的疊加模式吧

PorterDuffColorFilter測試.png
Bitmap mainBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.menu_bg); mRedPaint.setStyle(Paint.Style.FILL); mRedPaint.setColorFilter(new PorterDuffColorFilter( Color.parseColor("#0000ff"), PorterDuff.Mode.DARKEN)); canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint); canvas.translate(350, 0); mRedPaint.setColorFilter(new PorterDuffColorFilter( Color.parseColor("#0000ff"),PorterDuff.Mode.LIGHTEN )); canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint); canvas.translate(350, 0); mRedPaint.setColorFilter(new PorterDuffColorFilter( Color.parseColor("#0000ff"),PorterDuff.Mode.SCREEN )); canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint); canvas.translate(350, 0); mRedPaint.setColorFilter(new PorterDuffColorFilter( Color.parseColor("#0000ff"),PorterDuff.Mode.OVERLAY )); canvas.drawBitmap(mainBitmap, 0, 0, mRedPaint);
3.ColorMatrixColorFilter(顏色變換矩陣或20個float數)
本文的重中之重便是ColorMatrix:
它是有一個5*4的矩陣對某個顏色進行運算,是不是有種眾星捧月的但覺,沒錯,20個數,是不是很開心

顏色矩陣.png
1.關閉RGB顏色通道(變為黑色)
顏色ARBG佔了int的四個位元組,所以不可能是負數,至於如何處理負數,要看ColorMatrix的處理
測試了一下,應該是0,ARGB都沒了

斜對角-1.jpg

test-1.png
設為1後,結果[-R,-G,-B,A],黑色,符合預期:

test1.png
2.關閉RGB顏色通道(變為黑色),後偏移紅色255
由於只有G、B不通,所以顯示是不同的紅色

紅色.jpg

只過濾出紅色.png
3.關閉RGB顏色通道(變為黑色),後偏移藍色255
-1,0,0,0,0 0,-1,0,0,0 0,0,-1,0,255 0,0,0,1,0

只過濾出藍色.png
4.關閉RGB顏色通道(變為黑色),後偏移三色255
-1,0,0,0,255 0,-1,0,0,255 0,0,-1,0,255 0,0,0,1,0

反色.png
5.調節亮度:增加RGB的偏移顏色(-255~255)

亮度調節

亮度調節.png
6.灰度
//只要把RGB三通道的色彩資訊設定成一樣:即:R=G=B, //為了保證影象亮度不變,同一個通道中的R+G+B=1 0.3086, 0.6094, 0.0820, 0, 0 0.3086, 0.6094, 0.0820, 0, 0 0.3086, 0.6094, 0.0820, 0, 0 0,0,0,1, 0

灰度.png
7.飽和度:(這裡我亂調的,可參考色彩方面的書)

飽和度測試.png
(R-1)*X + 1, G*X,B*X,0, 0, R*X,(G-1)*X + 1,B*X,0, 0, R*X,G*X,(B-1)*X + 1,0, 0, 0,0,0,1, 0 R=0.3086,G=0.6094,B=0.0820
/** * 飽和度調節 * @param R 紅色保留比 * @param G 綠色保留比 * @param B 藍色保留比 * @param X 值越小越飽和----0為原圖 * @return */ private float[] colorM(float R, float G, float B, float X) { float[] array=new float[]{ (R - 1) * X + 1, G * X, B * X, 0, 0, R * X, (G - 1) * X + 1, B * X, 0, 0, R * X, G * X, (B - 1) * X + 1, 0, 0, 0, 0, 0, 1, 0 }; return array; }
8.對比度:

對比度測試.png
X,0,0,0,128*(1-X)RX*R+128*(1-X) 0,X,0,0,128*(1-X)*GG*R+128*(1-X) 0,0,X,0,128*(1-X)B=B*R+128*(1-X) 0,0,0,1,0AA 1
private float[] colorM(float X) { float[] array = new float[]{ X, 0, 0, 0, 128 * (1 - X), 0, X, 0, 0, 128 * (1 - X), 0, 0, X, 0, 128 * (1 - X), 0, 0, 0, 1, 0 }; return array; }
安卓自帶的圖片處理API
public void setSaturation(float sat) { reset(); float[] m = mArray; final float invSat = 1 - sat; final float R = 0.213f * invSat; final float G = 0.715f * invSat; final float B = 0.072f * invSat; m[0] = R + sat; m[1] = G;m[2] = B; m[5] = R;m[6] = G + sat; m[7] = B; m[10] = R;m[11] = G;m[12] = B + sat; }
mCmx.setSaturation(folat );

色彩飽和度.gif
終於寫完了,完結散花。
後記:捷文規範
1.本文成長記錄及勘誤表
專案原始碼 | 日期 | 備註 |
---|---|---|
V0.1--無 | 2018-11-10 | ofollow,noindex">Android關於Color你所知道的和不知道的一切 |
2.更多關於我
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
我的github | 我的簡書 | 我的CSDN | 個人網站 |
3.宣告
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大程式設計愛好者共同交流
3----個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4----看到這裡,我在此感謝你的喜歡與支援