PaintCanvasMatrix使用讲解(一Paint)

Posted GAStudio

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PaintCanvasMatrix使用讲解(一Paint)相关的知识,希望对你有一定的参考价值。

我正在参加 CSDN 2015博客之星评选 感恩分享活动,如果觉得文章还不错,请投个票鼓励下吧:http://vote.blog.csdn.net/blogstar2015/candidate?username=tianjian4592


好了,前面主要讲了Animation,Animator 的使用,以及桌面火箭效果和水波纹效果,分别使用android框架和自己绘制实现,俗话说,工欲善其事,必先利其器,接下来几篇文章主要讲绘制中我们需要常使用的一些利器;

Paint:画笔

Canvas:画布

Matrix:变换矩阵

绘制动效确实就像拿着笔在画布上面画画一样,而Paint就是我们拿着的笔,Canvas就是使用的画布;


一、Paint(画笔)

根据我们要画的类型,我们可以选择不同的笔,比如大气磅礴的山水画,我们可以选择大头的毛笔;细腻入微的肖像画我们可以选择尖头的铅笔。并且根据我们想要的效果,我们在绘画的时候,还会选择不同的颜料或不同颜色的笔;

那么在程序中,Paint 就足以满足以上所有的需要,我们可以根据我们自己的需要去自行设置我们画笔的属性,首先来看看都能设置哪些属性:


Paint 有三个构造函数,分别是:

Paint()创建一个画笔对象;

Paint(int flags):在构造的时候可以传入一些定义好的属性,eg:Paint.ANTI_ALIAS_FLAG  --用于绘制时抗锯齿

Paint(Paint paint):使用构造函数中Paint的属性生成一个新的Paint

private void initPaint() 
        // 构建Paint时直接加上去锯齿属性
        mColorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        // 直接使用mColorPaint的属性构建mBitmapPaint
        mBitmapPaint = new Paint(mColorPaint);
    


方法常用的主要有以下一些:

setARGB (int a, int r, int g, int b):用于设置画笔颜色,A 代表 alpha(透明度),R 代表Red (红色),G 代表 Green(绿色),B 代表 Blue(蓝色)

色值采用16进制,取值在 0 - 255 之间 ,0(0x00) 即 完全没有 ,255(0xff) 代表满值 ;

setAlpha(int a): 用于设置Paint 的透明度;

setColor(int color):同样设置颜色,如果是常用色,可以使用Color 类中定义好的一些色值 ,eg:Color.WHITE

setColorFilter(ColorFilter filter):设置颜色过滤器,可以通过颜色过滤器过滤掉对应的色值,比如去掉照片颜色,生成老照片效果;

ColorFilter有以下几个子类可用:

ColorMatrixColorFilter

LightingColorFilter

PorterDuffColorFilter


1.ColorMatrixColorFilter:通过颜色矩阵(ColorMatrix)对图像中的色值进行改变

在Android中,图片是以一个个 RGBA 的像素点的形式加载到内存中的,所以如果需要改变图片的颜色,就需要针对这一个个像素点的RGBA的值进行修改,其实主要是RGB,A是透明度;

修改图片 RGBA 的值需要ColorMatrix类的支持,它定义了一个 4*5 的float[]类型的矩阵,矩阵中每一行表示 RGBA 中的一个参数。

颜色矩阵M是以一维数组m=[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t]的方式进行存储的;

颜色矩阵


而对于一张图像来说,展示的颜色效果取决于图像的RGBA(红色、绿色、蓝色、透明度)值。而图像的 RGBA 值则存储在一个5*1的颜色分量矩阵C中,由颜色分量矩阵C可以控制图像的颜色效果。颜色分量矩阵如图所示: 

所以为了改变图像的显示效果,只需要改变 4*5 的颜色矩阵ColorMatrix,然后通过

即可得到新的图像显示矩阵;

由此可见,通过颜色矩阵 ColorMatrix 修改了原图像的 RGBA 值,从而达到了改变图片颜色效果的目的。并且,通过如上图所示的运算可知,颜色矩阵 ColorMatrix 的第一行参数abcde决定了图像的红色成分,第二行参数fghij决定了图像的绿色成分,第三行参数klmno决定了图像的蓝色成分,第四行参数pqrst决定了图像的透明度,第五列参数ejot是颜色的偏移量。

  通常,改变颜色分量时可以通过修改第5列的颜色偏移量来实现,如上面所示的颜色矩阵,通过计算后可以得知该颜色矩阵的作用是使图像的红色分量和绿色分量均增加100,这样的效果就是图片泛黄(因为红色与绿色混合后得到黄色):


除此之外,也可以通过直接对颜色值乘以某一系数而达到改变颜色分量的目的。如下图所示的颜色矩阵,将绿色分量放大了2倍,这样的效果就是图片泛绿色:


基于此,我们利用ColorFilter 和 ColorMatrixColorFilter类 和 Paint 的setColorFilter 就可以改变图片的展示效果(颜色,饱和度,对比度等),从而得到类似市面上图像软件中的黑白老照片、泛黄旧照片、羞涩的青春... ...特效;


     

                                  (原图效果)                         (调节效果)


    // 通过外层传入的 A、R、G、B值生成对应的ColorMatrix ,然后重新绘制图像
    public void setArgb(float alpha, float red, float green, float blue) 
        mRedFilter = red;
        mGreenFilter = green;
        mBlueFilter = blue;
        mAlphaFilter = alpha;
        mColorMatrix = new ColorMatrix(new float[] 
                mRedFilter, 0, 0, 0, 0,
                0, mGreenFilter, 0, 0, 0,
                0, 0, mBlueFilter, 0, 0,
                0, 0, 0, mAlphaFilter, 0,
        );
        mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));

        postInvalidate();
    


在Activity中拖动seekbar时,动态改变对应的色值参数,然后设置给里面的View:

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) 
        float filter = (float) progress / 100;
        if (seekBar == mRedSeekBar) 
            mRedSeek = filter;
         else if (seekBar == mGreenSeekBar) 
            mGreenSeek = filter;
         else if (seekBar == mBlueSeekBar) 
            mBlueSeek = filter;
         else if (seekBar == mAlphaSeekBar) 
            mAlphaSeek = filter;
        
        mColorFilterView.setArgb(mAlphaSeek, mRedSeek, mGreenSeek, mBlueSeek);
    

我们再专门把绿色位置扩大为两倍,看下效果:

    private static final float[] mMatrixFloats = new float[] 
            1, 0, 0, 0, 0,
            0, 2, 0, 0, 0,
            0, 0, 1, 0, 0,
            0, 0, 0, 1, 0
    ;

可以看到这个图片绿的敞亮

我们继续修改影响绿色的这一排矩阵值,看会发生什么变化,这次我们只是把2改大一点,改为10看看:

    private static final float[] mMatrixFloats = new float[] 
            1, 0, 0, 0, 0,
            0, 10, 0, 0, 0,
            0, 0, 1, 0, 0,
            0, 0, 0, 1, 0
    ;
这次我们已经觉得图片绿的过头了
我们再将影响绿色的矩阵值改小,大家已经可以猜测下图片的效果了,那就是绿色会成倍的减弱,减弱到一定的层度就会显得发灰; 而最后面的颜色增量也是一样,可以根据数值的大小,正负一定程度上增强和减弱对应的色值;

上面的demo只是通过动态的改变上面颜色矩阵里的a、g、s、m 值,进而改变图像对应的色值分量,达到改变图片显示效果的目的,我们还可以通过改变矩阵的其他数据来调节出各种有意思的效果:

修改影响红色的这一排矩阵:

    private static final float[] mMatrixFloats = new float[] 
            2, 0.5f, 0.5f, 0.5f, 20,
            0, 1, 0, 0, 0,
            0, 0, 1, 0, 0,
            0, 0, 0, 1, 0
    ;
继续改:

   private static final float[] mMatrixFloats = new float[] 
            0.22f, 0.5f, 0.1f, 0, 0,
            0.22f, 0.5f, 0.1f, 0, 0,
            0.22f, 0.5f, 0.1f, 0, 0,
            0, 0, 0, 1, 0
    ;
这下图片变灰了
再改:
    private static final float[] mMatrixFloats = new float[] 
            -1, 0, 0, 1, 0,
            0, -1, 0, 1, 0,
            0, 0, -1, 1, 0,
            0, 0, 0, 1, 0
    ;

将RGB均反相,但如果只这样会变成黑色,然后再增强一下; 再改:
    private static final float[] mMatrixFloats = new float[] 
            0, 0, 1, 0, 0,
            1, 0, 0, 0, 0,
            0, 2, 0, 0, 0,
            0, 0, 0, 1, 0
    ;


根据上面的矩阵,我们相当于去除了图片原先的RGB,然后原先的蓝色转变为红色,原先的红色转变为绿色,原先色绿色转变为蓝色,并变为2倍,经过以上变化,则形成了上面最终的效果;

所以通过修改颜色矩阵值,可以调出各种图片展示效果,如:高对比度、低对比度、高饱和度、低饱和度、负片等等,大家可以自己体验体验;


2.LightingColorFilter:

    /**
     * Create a colorfilter that multiplies the RGB channels by one color, and then adds a second color,
     * pinning the result for each component to [0..255]. The alpha components of the mul and add arguments
     * are ignored.
     */
    public LightingColorFilter(int mul, int add) 
        native_instance = native_CreateLightingFilter(mul, add);
        nativeColorFilter = nCreateLightingFilter(native_instance, mul, add);
    


从源码上看,LightingColorFilter 只有一个构造方法,然后传入 mul 和 add ,这两个 分别是16进制色值,定义如下:

    private static final int MUL_COLOR = 0xff00ffff;
    private static final int ADD_COLOR = 0x000000ff;

最终颜色的计算方式为:(MUL_COLOR * 原色值 + ADD_COLOR )% 255

所以上面则能够过滤掉红色色值,并增强蓝色色值,另外从源码的注释上也可以看到LightingColorFilter 是不计算透明度的;

3.PorterDuffColorFilter:可以类比于PS里的 图层混合模式

    /**
     * Create a colorfilter that uses the specified color and porter-duff mode.
     *
     * @param srcColor       The source color used with the specified
     *                       porter-duff mode
     * @param mode           The porter-duff mode that is applied
     */
    public PorterDuffColorFilter(int srcColor, PorterDuff.Mode mode) 
        native_instance = native_CreatePorterDuffFilter(srcColor, mode.nativeInt);
        nativeColorFilter = nCreatePorterDuffFilter(native_instance, srcColor, mode.nativeInt);
    

其实就是用一个色值和要绘制的图片进行叠加,然后可以选择叠加的模式(PorterDuff.Mode),PorterDuff.Mode 以枚举的形式定义,共有如下一些:

    // these value must match their native equivalents. See SkPorterDuff.h
    public enum Mode 
        /** [0, 0] */
        CLEAR       (0),
        /** [Sa, Sc] */
        SRC         (1),
        /** [Da, Dc] */
        DST         (2),
        /** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */
        SRC_OVER    (3),
        /** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */
        DST_OVER    (4),
        /** [Sa * Da, Sc * Da] */
        SRC_IN      (5),
        /** [Sa * Da, Sa * Dc] */
        DST_IN      (6),
        /** [Sa * (1 - Da), Sc * (1 - Da)] */
        SRC_OUT     (7),
        /** [Da * (1 - Sa), Dc * (1 - Sa)] */
        DST_OUT     (8),
        /** [Da, Sc * Da + (1 - Sa) * Dc] */
        SRC_ATOP    (9),
        /** [Sa, Sa * Dc + Sc * (1 - Da)] */
        DST_ATOP    (10),
        /** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */
        XOR         (11),
        /** [Sa + Da - Sa*Da,
             Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */
        DARKEN      (12),
        /** [Sa + Da - Sa*Da,
             Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */
        LIGHTEN     (13),
        /** [Sa * Da, Sc * Dc] */
        MULTIPLY    (14),
        /** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */
        SCREEN      (15),
        /** Saturate(S + D) */
        ADD         (16),
        OVERLAY     (17);

以上公式中,Sa 代表 Source alpha (源透明度 -- 传入颜色的透明度),Da 代表 Destination alpha (目标 alpha),Sc 代表 Source Color (源颜色),Dc 代表 Destination Color (目标色);每一种模式的计算方法分别如上;

我们简单测试下,还是用前面例子的图:


mBitDuffPaint.setColorFilter(new PorterDuffColorFilter(DUFF_COLOR, PorterDuff.Mode.DARKEN));  ----  变暗
我们看一下效果:


这时得出的图片效果和在PS里对两个图层使用变暗的叠加效果是一样的,PS里的效果如下:



我们继续调几个模式看看效果,变亮效果:

mBitDuffPaint.setColorFilter(new PorterDuffColorFilter(DUFF_COLOR, PorterDuff.Mode.LIGHTEN));
onDraw 中:

canvas.drawBitmap(mBitmap, mSrcRect, mDestRect, mBitDuffPaint);

程序 和 PS 展示效果分别为:

    
我上面只是使用蓝色 和图片进行了混合,大家可以使用其他颜色再调的玩玩,其中具体的叠加规律大家也可以自己稍加总结; 好了,颜色过滤器就讲这么多;
setDither(boolean dither):防抖动,这个属性的需求场景主要出现在绘制渐变色彩或含渐变的图片时,android对不含alpha通道的图片会进行一个转化,

成为RGB565 格式的,这种格式占用内存小,但因为如此,就会出现讨厌的“色带”情景,让人感觉过渡的不是那么柔和,针对这个问题,android提出了

防抖动,它会将原始颜色的过渡处根据两边的色值进行一些改变,从而让颜色过渡更加的柔和,让人觉得是平滑的过渡;

setFilterBitmap(boolean filter):如果该项设置为true,则图像在动画进行中会滤掉对Bitmap图像的优化操作,加快显示速度

setFlags(int flags):可以用来给Paint设置里面定义好的一些属性,如抗锯齿,防抖动等;

setMaskFilter(MaskFilter maskFilter):设置绘制时图片边缘效果,可以有模糊和浮雕;

MaskFilter类可以为Paint分配边缘效果。
        对MaskFilter的扩展可以对一个Paint边缘的alpha通道应用转换。Android包含了下面几种MaskFilter:

        BlurMaskFilter   指定了一个模糊的样式和半径来处理Paint的边缘;
        EmbossMaskFilter  指定了光源的方向和环境光强度来添加浮雕效果;

setPathEffect(PathEffect effect):是用来控制绘制轮廓(线条)的方式:

这个类本身并没有做什么特殊的处理,只是继承了Object,通常我们使用的是它的几个子类,就是上面图片中所显示的。在使用的时候,通常是
PathEffect pe = new 一个具体的子类;
然后使用Paint的setPathEffect(PathEffect pe)方法即可。

CornerPathEffect:

这个类的作用就是将Path的各个连接线段之间的夹角用一种更平滑的方式连接,类似于圆弧与切线的效果;
一般的,通过CornerPathEffect(float radius)指定一个具体的圆弧半径来实例化一个CornerPathEffect;

DashPathEffect:

这个类的作用就是将Path的线段虚线化:
构造函数为DashPathEffect(float[] intervals, float phase),其中intervals为虚线的ON和OFF数组,该数组的length必须大于等于2,phase为绘制时的偏移量。

DiscretePathEffect:

这个类的作用是打散Path的线段,使得在原来路径的基础上发生打散效果。
一般的,通过构造DiscretePathEffect(float segmentLength,float deviation)来构造一个实例,其中,segmentLength指定最大的段长,deviation指定偏离量。

PathDashPathEffect:

这个类的作用是使用Path图形来填充当前的路径,其构造函数为PathDashPathEffect (Path shape, float advance, float phase,PathDashPathEffect.Stylestyle)。
shape则是指填充图形,advance指每个图形间的间距,phase为绘制时的偏移量,style为该类自由的枚举值,有三种情况:Style.ROTATE、Style.MORPH和Style.TRANSLATE;

其中ROTATE的情况下,线段连接处的图形转换以旋转到与下一段移动方向相一致的角度进行转转,MORPH时图形会以发生拉伸或压缩等变形的情况与下一段相连接,TRANSLATE时,图形会以位置平移的方式与下一段相连接。

ComposePathEffect:

组合效果,这个类需要两个PathEffect参数来构造一个实例,ComposePathEffect (PathEffect outerpe,PathEffect innerpe),表现时,会首先将innerpe表现出来,然后再在innerpe的基础上去增加outerpe的效果;

SumPathEffect:

叠加效果,这个类也需要两个PathEffect作为参数SumPathEffect(PathEffect first,PathEffect second),但与ComposePathEffect不同的是,在表现时,会分别对两个参数的效果各自独立进行表现,然后将两个效果简单的重叠在一起显示出来;

关于参数phase

在存在phase参数的两个类里,如果phase参数的值不停发生改变,那么所绘制的图形也会随着偏移量而不断的发生变动,这个时候,看起来这条线就像动起来了一样:

举个栗子:

public class PathEffectView extends View 

    private float mPhase;
    private PathEffect[] mEffects = new PathEffect[7];
    private int[] mColors;
    private Paint mPaint;
    private Path mPath;

    public PathEffectView(Context context) 
        super(context);
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(4);
        // 创建,并初始化Path
        mPath = new Path();
        mPath.moveTo(0, 0);
        for (int i = 1; i <= 15; i++)
        
            // 生成15个点,随机生成它们的坐标,并将它们连成一条Path
            mPath.lineTo(i * 20, (float) Math.random() * 60);
        
        // 初始化七个颜色
        mColors = new int[] 
                Color.BLACK, Color.BLUE, Color.CYAN,
                Color.GREEN, Color.MAGENTA, Color.RED, Color.YELLOW
        ;
    

    protected void onDraw(Canvas canvas)
    
        // 将背景填充成白色
        canvas.drawColor(Color.WHITE);
        // -------下面开始初始化7种路径的效果
        // 使用路径效果--原始效果
        mEffects[0] = null;
        // 使用CornerPathEffect路径效果--参数为圆角半径
        mEffects[1] = new CornerPathEffect(10);
        // 初始化DiscretePathEffect -- segmentLength指定最大的段长,deviation指定偏离量
        mEffects[2] = new DiscretePathEffect(3.0f, 5.0f);
        // 初始化DashPathEffect --intervals为虚线的ON和OFF数组,offset为绘制时的偏移量
        mEffects[3] = new DashPathEffect(new float[] 
                20, 10, 5, 10
        , mPhase);
        // 初始化PathDashPathEffect
        Path p = new Path();
        p.addRect(0, 0, 8, 8, Path.Direction.CCW);
        // shape则是指填充图形,advance指每个图形间的间距,phase为绘制时的偏移量,style为该类自由的枚举值
        mEffects[4] = new PathDashPathEffect(p, 12, mPhase, PathDashPathEffect.Style.ROTATE);
        // 组合效果
        mEffects[5] = new ComposePathEffect(mEffects[2], mEffects[4]);
        // 叠加效果
        mEffects[6] = new SumPathEffect(mEffects[4], mEffects[3]);
        // 将画布移到8,8处开始绘制
        canvas.translate(8, 8);
        // 依次使用7种不同路径效果,7种不同的颜色来绘制路径
        for (int i = 0; i < mEffects.length; i++)
        
            mPaint.setPathEffect(mEffects[i]);
            mPaint.setColor(mColors[i]);
            canvas.drawPath(mPath, mPaint);
            canvas.translate(0, 60);
        
        // 改变phase值,形成动画效果
        mPhase += 1;
        invalidate();
    


效果为:



setShader(Shader shader)


设置图像效果,使用Shader可以绘制出各种渐变效果;

Shader下面有五个子类可用:

BitmapShader :位图图像渲染

LinearGradient:线性渲染

RadialGradient:环形渲染

SweepGradient:扫描渐变渲染/梯度渲染

ComposeGradient:组合渲染,可以和其他几个子类组合起来使用

这几个类中LinearGradient、RadialGradient、SweepGradient均是可以将颜色进行处理,形成柔和的过渡,也可以称为渐变,而BitmapShader 则是直接使用位图进行渲染,就类似于贴图,在贴图的过程中根据需要自然就可以选择相应的模式,有三种模式可供选择,分别是:

枚举: 
emun Shader.TileMode 

定义了平铺的3种模式: 
static final Shader.TileMode CLAMP: 边缘拉伸,即使用边缘的最后一个像素进行延展;

static final Shader.TileMode MIRROR:在水平方向和垂直方向交替景象, 两个相邻图像间没有缝隙,从名称上看就像照镜子一样;

Static final Shader.TillMode REPETA:在水平方向和垂直方向重复摆放,两个相邻图像间有缝隙缝隙;


Shader在应用中也是非常的广泛,不少大产品中的亮点设计也与之相关,接下来我们分别看个小例子:


1.LinearGradient(线性渲染--也叫线性渐变)


我们在PS上如果要拉出一条线性渐变,只需要先把渐变模式选为线性,然后拉出一条渐变线,同理,在android中,只需要一个起始点和一个终点即可确定一条渐变线,那么颜色如何渐变过去呢,往下看,LinearGradient 有两个构造方法:

        /**	Create a shader that draws a linear gradient along a line.
        @param x0       The x-coordinate for the start of the gradient line
        @param y0       The y-coordinate for the start of the gradient line
        @param x1       The x-coordinate for the end of the gradient line
        @param y1       The y-coordinate for the end of the gradient line
        @param  color0  The color at the start of the gradient line.
        @param  color1  The color at the end of the gradient line.
        @param  tile    The Shader tiling mode
	*/
	public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,
            TileMode tile) --省略--


这个构造方法就是传入起点和终点作为渐变线,然后color0作为起始点的颜色值,color1作为终点的颜色值,然后生成的效果就是从起始点到终点从color0均匀的渐变到color1,注意是均匀:

我们来看下效果:


    // 定义几种初始颜色
    private static final int COLOR_BLUE = 0xff0000ff;
    private static final int COLOR_GREEN = 0xff00ff00;
    private static final int COLOR_RED = 0xffff0000;

    // 定义起始和最终颜色
    private int mStartColor = COLOR_BLUE;
    private int mToColor = COLOR_RED;


然后创建一个从屏幕上中心点到屏幕1/3高度的下中心点的渐变:


new LinearGradient(mHalfWidth, 0, mHalfWidth, mTotalHeight/3, mStartColor, mToColor, Shader.TileMode.CLAMP)


绘制一个全屏矩形:


canvas.drawRect(0, 0, mTotalWidth, mTotalHeight,
                mGradientPaint);

看下三种模式下的效果:

                                                                      
      (Shader.TileMode.CLAMP )             (Shader.TileMode.MIRROR)                (Shader.TileMode.REPEAT)


接下来改下渐变起始点和终点为左上角和1/3屏幕高度的右下角:

new LinearGradient(0, 0, mTotalWidth, mTotalHeight/3, mStartColor, mToColor, Shader.TileMode.CLAMP)


还是绘制同样的矩形,看下三种模式下的效果:

                                                                  

  (Shader.TileMode.CLAMP)             (Shader.TileMode.MIRROR)                (Shader.TileMode.REPEAT)


仔细看三种模式下的渐变,可能有些人会觉得奇怪,因为感觉图一次的结尾处并不是1/3高度处,原因是啥呢?是因为渐变线是斜角,大家可以看REPEAT模式下的图,一次结尾处在屏幕右边的位置刚好是屏幕 1/3 处,看到这儿应该清楚了吧;

渐变ok了,但有时候我们的需求不止于此,还需要多种颜色渐变,并且还得控制渐变的大小,比如需要红绿蓝三种颜色渐变,并且还要 红色占比 10% ,绿色占比 60% ,蓝色占比 30%,这个时候该怎么做呢?我们来看LinearGradient 的第二个构造函数:

        /**	Create a shader that draws a linear gradient along a line.
        @param x0           The x-coordinate for the start of the gradient line
        @param y0           The y-coordinate for the start of the gradient line
        @param x1           The x-coordinate for the end of the gradient line
        @param y1           The y-coordinate for the end of the gradient line
        @param  colors      The colors to be distributed along the gradient line
        @param  positions   May be null. The relative positions [0..1] of
                            each corresponding color in the colors array. If this is null,
                            the the colors are distributed evenly along the gradient line.
        @param  tile        The Shader tiling mode
	*/
	public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],
            TileMode tile) ---省略---

  其中 colors 代表要使用的颜色色值数组,positions 代表与颜色对应的相应的位置数组,位置数组里的取值 从 0 -1,分别对应从开始到结束,这样里面的每一个值就对应了距离起始点的距离比例,并且使用该方式,则默认认为有多种颜色,所以颜色数组长度必须大于等于2,并且两数组长度必须一致,这么说可能还不是特清晰,我们来完成上面的需求-需要红黄蓝三种颜色渐变,并且还要 红色占比 10% ,黄色占比 60% ,蓝色占比 30%:

好,咱们按照上面的说法,把对应颜色的位置按照比例设置上,看下效果:

    // 颜色数组
    private int[] mColors = new int[] 
            COLOR_RED, COLOR_GREEN, COLOR_BLUE
    ;
    // 与颜色数组对应的位置数组
    private float[] mPositions = new float[] 
            0.1f, 0.7f, 1f
    ;

我们就以 REPEAT 模式为例,看看效果:


Shader.TileMode.REPEAT


我们发现根本和我们想要的比例不一样,并不是 红:绿:蓝 = 1 :6 :3 ,反而 接近于 4 :4.5 :1.5 ,为什么?

那是因为 positions 里面代表的是对应颜色的比例位置,红色在 10% 处,绿色在 70% 处 ,蓝色在 100% 处 ,那么 红色和 绿色之间则有 60%的位置用于渐变,那么平均下来红色和绿色分别可以占据 30% 的位置(当然中间是渐变的,只是估算大概位置),同理可以计算出  绿 和 蓝 渐变的过程中各占 15% 的位置,综合起来则是 4:4.5:1.5;

这么计算下来,我们可以根据

以上是关于PaintCanvasMatrix使用讲解(一Paint)的主要内容,如果未能解决你的问题,请参考以下文章

STM32F407串口调试总结

2020年6月23日第一次面试题(外派PA)

PA1=GPIOA->BSRR PA1=0x01 问题: PA1是一个引脚,为啥能把0x01赋给一个引脚,引脚不是一位的吗?

请高人讲解java ajax实现分页的思路

实验四:ESP8266WIFI通讯实验

实验四:ESP8266WIFI通讯实验