安卓自定義view-Paint 畫筆

咱們在學畫畫的時候,首先須要買畫筆,而後再購買畫板,就能夠愉快的創做(瞎畫😄)啦!在安卓中也是這麼設計的,畢竟設計也是要符合實際生活規律的。OK,那麼本篇就是從畫筆的基礎開始講起。android

畫筆基礎 api

  1. 構造方法
// 建立出一隻畫筆🖌️
Paint paint = new Paint()
複製代碼
  1. 畫筆的顏色,選擇喜歡的顏色做畫, 姿式不少,選擇喜歡的便可
mPaint.setColor(Color.parseColor("#ff00ff"));

mPaint.setColor(Color.argb(200, 200, 200,200));

// 或 setARGB 的方式
mPaint.setARGB(200, 200, 200, 200);

複製代碼
  1. 畫筆的尺寸大小,粗細程度, 好比我就喜歡粗的筆
mPaint.setStrokeWidth(30);
複製代碼
  1. 畫筆的樣式,比實際動手畫畫方便一些,好比咱們畫一個圓要填充整個圓,是否是得一點點的塗。可是手機上畫能夠一下搞定。這裏有三個模式: STROKE(畫線)、FILL(填充)、FILL_AND_STROKE (輪廓和填充)
// 有三種模式: FILL、STROKE、FILL_AND_STROKE
mPaint.setStyle(Paint.Style.STROKE);
複製代碼
  1. 抗鋸齒 在計算機圖形學中產生圖形鋸齒是由於計算機屏幕中的點是離散的有大小的像素點來表示的,當咱們用計算機畫連續的曲線的時候因爲屏幕像素沒有辦法表示小數座標,而後用四捨五入後整數來表示,放大來看,就會出現鋸齒。通常解決辦法就是找一些次要的像素填充或者透明度填充(經過計算後的到邊緣的顏色值)
mPaint.setAntiAlias(true);
複製代碼

來吧!看看效果左邊未設置抗鋸齒,右邊設置了抗鋸齒。git

抗鋸齒圖片對比

你是否是想說,這不同嗎?這麼看確實看不出區別。畢竟是人的肉眼,確定沒辦法觀察到很細節的東西。那麼咱們經過放大後再觀察一下:github

抗鋸齒放大後

這下你看到了吧!未開啓抗鋸齒算法優化的化,能夠看到邊緣有鋸齒形狀。而右側的就更加平順一些。那咱們有沒有必要開啓抗鋸齒呢? 其實大部分狀況下是能夠不開啓的,由於如今的手機分辨越來約高,只要不是對精細度追求極致的化,是能夠不用開啓的。算法

  1. 圖像抖動 因爲如今的手機分辨率愈來愈高,而且色彩深度默認爲 32 位,效果也足夠清晰了。抖動」是印刷行業和出版業中經常使用的一種工藝,老式的針式打印機只能打印出來黑點和白點,但是黑白圖片是有灰度級的,那麼如何打印出來圖片呢?「抖動」由此而生,抖動試圖經過在白色背景上生成黑色的二值圖像來給出色調變化的直觀印象,能夠假想一下,黑點越密,那麼遠距離觀察就越黑,如何控制黑點的分佈就是「抖動」算法的核心。

若是顆粒越小,是否是就會看起來是灰色啦,欺騙人的肉眼。 canvas

  1. 圖形端的形狀 固然這是本身的術語描述,應該叫什麼不清楚。共有三種:ROUND、BUTT、SQUARE, 第一種好理解圓的形狀,第三種也好理解矩形形狀,第二種是什麼鬼。咱們看下效果圖。
mPaint.setStrokeCap(Paint.Cap.ROUND);
複製代碼

  1. 線段之間拐角形狀

線段與線段之間的形狀,可讓線段的鏈接看來更加圓潤仍是棱角分明。它也有三種: MITER、ROUND、BEVEL。從單詞的的翻譯來看,彷佛只知道 ROUND,直接來看效果圖。api

mPaint.setStrokeJoin(Paint.Join.ROUND);

複製代碼

仔細看的化能看出在四個頂點都有些不一樣,MITER 更加尖一些, ROUND 則圓潤一些,BEVEL 好像是切了一個角而來。ROUND 很好理解,說一些 MITER 和 BEVEl。它們二者都有類似之處,就是線段的末尾進行延長相交,對於 MITER 來講若是線段之間的夾角越小,那麼頂點就會越尖。再來看一張圖,這張圖就比剛纔矩形更加直觀了。數組

  1. set(src)

這個很簡單,就是從某一個建立好的畫筆複製一份使用bash

  1. setFlags
等同於分別設置 setAntiAlias(true) 和 setDither(true)
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
複製代碼

高階部分 api

設置顏色

  1. setShader(Shader shader) 設置 Shader

翻譯過來稱爲着色器,其實也是用來設置畫筆的顏色的。它有 5 個子類,依次是: LinearGradient、SweepGradient、RadialGradient、BitmapShader、ComposeShaderide

. LinearGradient 從名字能夠看出是線性漸變的意思,直接上效果圖優化

LinearGradient linearGradient = new LinearGradient(
                300,
                300,
                700,
                700,
                Color.RED,
                Color.YELLOW,
                Shader.TileMode.CLAMP);
        mPaint.setShader(linearGradient);

複製代碼

能夠看到接近最後一個點的地方設置成了藍色,這幾個參數的意思是: 起點的 x, y 左邊,終點的 x,y 座標,起點的顏色,終點的顏色, Shader.TileMode.CLAMP 在兩個端點以外延用端點的顏色。

再來看看其餘的用法,穩住別慌,堅持住。

LinearGradient linearGradient = new LinearGradient(
                300,
                300,
                900,
                900,
                new int[] {
                        Color.RED,
                        Color.BLUE
                },
                new float[] {
                  0.5f,
                  0.5f
                },
                Shader.TileMode.CLAMP);
        mPaint.setShader(linearGradient);

複製代碼

這回發現多了兩個參數,其實並無,只是用int[] 添加顏色,float[] 數組用來指定端點到某一個區域的填充的顏色,須要和前面的 int[] 數組長度一直,不然會 gg。

繼續來看 TileMode 的三種區別

MIRROR:

REPEAT

. SweepGradient 漸變,扇形漸變,也就是說某一個角度掃過的區域的漸變效果,能夠看到跟線性漸變的使用方式差很少。

SweepGradient sweepGradient = new SweepGradient(300, 300,
                new int[]{Color.RED, Color.GREEN, Color.BLUE},
                new float[]{
                     0.1f, 0.3f, 1.0f
                });

        mPaint.setShader(sweepGradient);
複製代碼

. RadialGradient 徑向漸變,根據圓的半徑進行漸變色

RadialGradient radialGradient = new RadialGradient(700, 700, 300,
                new int[]{Color.RED, Color.GREEN, Color.BLUE},
                new float[]{0.2f, 0.5f, 1.0f},
                Shader.TileMode.CLAMP);

        mPaint.setShader(radialGradient);
複製代碼

效果圖

它也有三種使用模式,其實跟線性漸變同樣。

RadialGradient radialGradient = new RadialGradient(700, 700, 100,
                new int[]{Color.RED, Color.GREEN, Color.BLUE},
                new float[]{0.2f, 0.5f, 1.0f},
                Shader.TileMode.CLAMP); // 更換模式.
複製代碼

REPEAT:

RadialGradient radialGradient = new RadialGradient(700, 700, 150,
                Color.RED, Color.BLUE, Shader.TileMode.MIRROR);

        mPaint.setShader(radialGradient);
複製代碼

MIRROR:

. BitmapShader. Bitmap 是圖片,喲,圖片也能夠處理的哦!

BitmapShader bitmapShader = new BitmapShader(mBitmap,
                Shader.TileMode.REPEAT,
                Shader.TileMode.MIRROR);
        mPaint.setShader(bitmapShader);
        
      
// 繪製一個矩形,填充 view 的寬高.
canvas.drawRect(0, 0, mWidth, mHeight, mPaint);
複製代碼

CLAMP: 以圖片的 x 軸的最後一個像素填充 x 軸和 y 軸

REPEAT:以相同的圖片進行填充.

MIRROR: 鏡像的方式填充,比如照鏡子同樣,原像和鏡像恰好相反

當前你也能夠混合,好比在 x 軸方向重複填充, y 軸方向鏡像填充

還能夠這樣:將圖片填充到圓裏

. ComposeShader 使用組合的方式,將前面的着色器配合來使用.

// 關閉硬件加速
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.beauty);

        mHeartBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.heart);

        BitmapShader bitmapShader1 = new BitmapShader(mBitmap,
                Shader.TileMode.CLAMP,
                Shader.TileMode.CLAMP);

        BitmapShader bitmapShader2 = new BitmapShader(mHeartBitmap,
                Shader.TileMode.CLAMP,
                Shader.TileMode.CLAMP);

        ComposeShader composeShader = new ComposeShader(bitmapShader1, bitmapShader2, PorterDuff.Mode.SRC_OUT);
        mPaint.setShader(composeShader);

        
   canvas.drawCircle(500, 500, 300, mPaint);

複製代碼
  1. setColorFilter(ColorFilter colorFilter)

從名字就能夠看出,主要是對顏色進行過濾。就是在繪製以前先過濾點指定的顏色再進行顯示。也有三個子類供咱們使用: LightingColorFilter、PorterDuffColorFilter、ColorMatrixColorFilter

. LightingColorFilter 光照過濾,就比如咱們帶着太陽鏡過濾了太陽光中對眼睛有傷害的紫外線,而後眼睛看到了就是比較柔的。學到老活到老, 來看使用

LightingColorFilter colorFilter = new LightingColorFilter(
                Color.parseColor("#ffffff"),
                Color.parseColor("#000000"));
        mPaint.setColorFilter(colorFilter);
複製代碼

經過看源碼,能夠看到它的計算規則是:

R' = R * colorMultiply.R + colorAdd.R G' = G * colorMultiply.G + colorAdd.G
 B' = B * colorMultiply.B + colorAdd.B 複製代碼

不難發現,若是採起上面的方式設置 mul: #ffffff, add: #000000, 那麼計算以下:

R' = R * 0xff/0xff + 0x00 G' = G * 0xff/0xff + 0x00
B' = B * 0xff/0xff + 0x00 結果和原圖同樣. 複製代碼

如今若是要去掉原圖中紅色,只要將 mul 改成 0x00ffff, add 不變

LightingColorFilter colorFilter = new LightingColorFilter(
                Color.parseColor("#00ffff"),
                Color.parseColor("#000000"));
        mPaint.setColorFilter(colorFilter);
複製代碼

加強紅色:mul 不變, add: 0x660000

LightingColorFilter colorFilter = new LightingColorFilter(
                Color.parseColor("#ffffff"),
                Color.parseColor("#660000"));
        mPaint.setColorFilter(colorFilter);
複製代碼

. PorterDuffColorFilter

指定一種顏色爲源,採用圖形混合模式進行處理。

// 指定白色爲源,混合模式爲目標顯示 DST_OVER
 PorterDuffColorFilter porterDuffColorFilter = new PorterDuffColorFilter(
                Color.parseColor("#ffffff"),
                PorterDuff.Mode.DST_OVER);
        mPaint.setColorFilter(porterDuffColorFilter);
複製代碼

更改原圖顏色,並更改混合模式: SRC_OVER

PorterDuffColorFilter porterDuffColorFilter = new PorterDuffColorFilter(
                Color.parseColor("#ED29AB"),
                PorterDuff.Mode.SRC_OVER);
        mPaint.setColorFilter(porterDuffColorFilter);

複製代碼

. ColorMatrixColorFilter

這個就比較兇殘了,由於是在是很是強大,內部是一個 4x5 的矩陣,經過變化矩陣中各通道的值,能夠實現各類效果。

[ a, b, c, d, e,
  f, g, h, i, j,
  k, l, m, n, o,
  p, q, r, s, t ]

複製代碼

經過代碼搞搞事情,看看能玩出什麼花招出來。

float[] colorMatrix = new float[] {
                1, 0, 0, 0, 0,
                0, 1, 0, 0, 0,
                0, 0, 1, 0, 0,
                0, 0, 0, 1, 0
        };
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
mPaint.setColorFilter(colorMatrixColorFilter);

複製代碼

是否是沒啥變化,由於上面矩陣定義的是白色的矩陣。因此沒啥變化。再來看下面的矩陣會有什麼變化。

float[] colorMatrix = new float[] {
                0.33F, 0.59F, 0.11F, 0, 0,
                0.33F, 0.59F, 0.11F, 0, 0,
                0.33F, 0.59F, 0.11F, 0, 0,
                0, 0, 0, 1, 0,
        };
        ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
        mPaint.setColorFilter(colorMatrixColorFilter);
        
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(TECHNICOLOR);
mPaint.setColorFilter(colorMatrixColorFilter);
複製代碼

是否是發現變灰色了。繼續修改

private static final float[] INVERT = new float[] {
            -1, 0, 0, 0, 255,
            0, -1, 0, 0, 255,
            0, 0, -1, 0, 255,
            0, 0, 0, 1, 0,
    };
    
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(TECHNICOLOR);
mPaint.setColorFilter(colorMatrixColorFilter);
複製代碼

是否是有點想膠捲的的圖像顏色啦😄,繼續修改。

private static final float[] RGB_TO_BGR = new float[] {
            0, 0, 1, 0, 0,
            0, 1, 0, 0, 0,
            1, 0, 0, 0, 0,
            0, 0, 0, 1, 0,
    };
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(TECHNICOLOR);
mPaint.setColorFilter(colorMatrixColorFilter);
複製代碼

能夠看到這裏將 RGB 轉爲 BGR 的方式顯示效果。

在看,如何設置稱棕褐色。

private static final float[] SEPIA = new float[] {
            0.393F, 0.769F, 0.189F, 0, 0,
            0.349F, 0.686F, 0.168F, 0, 0,
            0.272F, 0.534F, 0.131F, 0, 0,
            0, 0, 0, 1, 0,
    };
    
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(TECHNICOLOR);
mPaint.setColorFilter(colorMatrixColorFilter);
複製代碼

黑白照片

private static final float[] BLACK_AND_WHITE = new float[] {
            1.5F, 1.5F, 1.5F, 0, -255,
            1.5F, 1.5F, 1.5F, 0, -255,
            1.5F, 1.5F, 1.5F, 0, -255,
            0, 0, 0, 1, 0,
    };
    
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(TECHNICOLOR);
mPaint.setColorFilter(colorMatrixColorFilter);

複製代碼

彩色照片

private static final float[] TECHNICOLOR = new float[] {
            1.9125277891456083F, -0.8545344976951645F, -0.09155508482755585F, 0, 11.793603434377337F,
            -0.3087833385928097F, 1.7658908555458428F, -0.10601743074722245F, 0, -70.35205161461398F,
            -0.231103377548616F, -0.7501899197440212F, 1.847597816108189F, 0, 30.950940869491138F,
            0, 0, 0, 1, 0
    };
    
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(TECHNICOLOR);
mPaint.setColorFilter(colorMatrixColorFilter);
複製代碼

經過不一樣的矩陣,能夠獲得不一樣轉換後的效果。

接下來討論一下濾鏡的原理,安卓系統使用一個顏色矩陣 ColorMatrix(下圖 A 表示) 來處理圖像的顏色,對於每個像素點都有一個顏色份量矩陣來保存的 RGBA 值(下圖 C)。

來看官方文檔的描述 ColorMatrix, 在安卓系統中經過維護一個 4 * 5 的覺得數組來表示。那對一個顏色的運算使用矩陣乘法以下:

// 運算的結果爲 R'G'B'A'
   R’ = a*R + b*G + c*B + d*A + e;
   G’ = f*R + g*G + h*B + i*A + j;
   B’ = k*R + l*G + m*B + n*A + o;
   A’ = p*R + q*G + r*B + s*A + t;
複製代碼

這個公式告訴咱們,第一行 abcde 決定紅色,第二行 fghij 決定綠色,第三行 klmno 決定藍色,最後一行 pqrst 決定透明度。

再根據文檔中可看出 reset 方法的顏色矩陣爲:

[ 1 0 0 0 0   - red vector
   0 1 0 0 0   - green vector
   0 0 1 0 0   - blue vector
   0 0 0 1 0 ] - alpha vector
複製代碼

這個顏色也稱爲初始顏色,運用這個顏色矩陣做用在圖片的話,將會保持原來的顏色不變。下面來一個簡單的變化,矩陣取反,其實前面已經看到過效果了。

[ -1 0 0 0 255   - red vector
   0 1- 0 0 255   - green vector
   0 0 -1 0 255   - blue vector
   0 0 0 -1 0 ] - alpha vector

複製代碼

這裏的最後一列,表示的是顏色的偏移量,下面的意思就是紅色和綠色偏移100。

[1,0,0,0,100,
 0,1,0,0,100,
 0,0,1,0,100,
 0,0,0,1,0
]

複製代碼

下面是我寫的一個能夠經過設置參數來調節圖片的顏色過濾,代碼很簡單安卓碎片知識

若是開發中是這樣設置的話,我相信可能會累死。安卓 ColorMatrix 顏色矩陣中封裝了一些 API 來快速調整上面這三個顏色參數,能夠方便的使用。

. 色調

對色彩進行旋轉運算,至於如何旋轉的這個我也沒有研究。感興趣的能夠上網找找資料看看。

// 第一個參數: 0 表示繞紅色的旋轉, 1 表示繞綠色的旋轉, 2表示繞藍色的旋轉
// 至於第二個參數:能夠經過源碼大概能夠知道。它應該在 -180 ~ 180 之間。
colorMatrix.setRotate(0, 180);
colorMatrix.setRotate(1, 180);
colorMatrix.setRotate(2, 180);

複製代碼
/**
     * Set the rotation on a color axis by the specified values.
     * <p>
     * <code>axis=0</code> correspond to a rotation around the RED color
     * <code>axis=1</code> correspond to a rotation around the GREEN color
     * <code>axis=2</code> correspond to a rotation around the BLUE color
     * </p>
     */
    public void setRotate(int axis, float degrees) {
        reset();
        // 週期是在 -180 到 180 之間
        double radians = degrees * Math.PI / 180d;
        float cosine = (float) Math.cos(radians);
        float sine = (float) Math.sin(radians);
        switch (axis) {
        // Rotation around the red color
        case 0:
            mArray[6] = mArray[12] = cosine;
            mArray[7] = sine;
            mArray[11] = -sine;
            break;
        // Rotation around the green color
        case 1:
            mArray[0] = mArray[12] = cosine;
            mArray[2] = -sine;
            mArray[10] = sine;
            break;
        // Rotation around the blue color
        case 2:
            mArray[0] = mArray[6] = cosine;
            mArray[1] = sine;
            mArray[5] = -sine;
            break;
        default:
            throw new RuntimeException();
        }
    }
複製代碼

. 飽和度 放大色彩飽和度,大於 1 色彩開始飽和,等於 1 不變,小於 1 過分到沒有色彩飽和,等於 0 爲黑白圖像。

colorMatrix1.setSaturation(3.0f);

複製代碼

就是將 RBGA 加上咱們指定的 sat。

/**
     * Set the matrix to affect the saturation of colors.
     *
     * @param sat A value of 0 maps the color to gray-scale. 1 is identity.
     */
    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;
    }
複製代碼

. 亮度 以不一樣的顏色比列進行混合, 若是所有爲 0 爲黑色。所有爲 1 則爲圖片原有顏色。若是看源碼的話,能夠看到它其實設置的是 a[0],a[6],a[12], a[18] 的值,而後其餘的都置爲0。

colorMatrix1.setScale(0f, 0f, 0f,1.0f);

複製代碼
/**
     * Set this colormatrix to scale by the specified values.
     */
    public void setScale(float rScale, float gScale, float bScale,
                         float aScale) {
        final float[] a = mArray;

        for (int i = 19; i > 0; --i) {
            a[i] = 0;
        }
        a[0] = rScale;
        a[6] = gScale;
        a[12] = bScale;
        a[18] = aScale;
    }

複製代碼

好吧! 寫到這兒就結束了。可是 Paint 的尚未說完,剩下的還有跟文字相關的、其餘的圖層混合模式。

相關文章
相關標籤/搜索