自定义View之Paint

来源:互联网 发布:淘宝商家怎么查看粉丝 编辑:程序博客网 时间:2024/05/19 15:43

ColorFilter

ColorFilter就是颜色过滤器,与Paint一起使用,用于修改Paint绘画的每一个像素的颜色。

这里写图片描述

从ColorFilter类的树结构中,可以清楚的看到它有3个子类:ColorMatrixColorFilter, LightingColorFilter, PorterDuffColorFilter。接下来我们就看这种3个子类,因为自从API 26以后,官方不再推荐使用其构造函数创建实例,而是由子类创建。

ColorMatrixColorFilter

ColorMatrixColorFilter直译过来就是颜色矩阵过滤器。什么是颜色矩阵(ColorMatrix)呢?我们来看一下ColorMatrix这个类,其内有一个数组,其实其保存的是一个4x5颜色矩阵:

而在Android中,每个像素的RGBA值则存储在一个5*1的颜色分量矩阵C中,由颜色分量矩阵C可以控制图像的颜色效果。

M和C两矩阵相乘的结果为:

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;

在一张图片中,每一个像素的颜色由R、G、B、A的值来决定其呈现的效果。也就是说,可以通过颜色矩阵修改颜色分量矩阵的值,从而改变每个像素的颜色的呈现效果。

  • 第一行决定了ARGB值的R
  • 第二行决定了ARGB值的G
  • 第三行决定了ARGB值的B
  • 第四行决定了ARGB值的A
  • 第五列是颜色的偏移量

在ColorMatrix有一个大小为20且不可改变长度的数组,用来存放颜色矩阵。

private final float[] mArray = new float[20];

每当调用reset方法时,ColorMatrix都会重置这个数组:

/** * [ 1 0 0 0 0   - red *   0 1 0 0 0   - green  *   0 0 1 0 0   - blue  *   0 0 0 1 0 ] - alpha  */public void reset() {    final float[] a = mArray;    Arrays.fill(a, 0);    a[0] = a[6] = a[12] = a[18] = 1;}


值得注意的是,R、G、B、A的值应在0-255范围内,在创建颜色矩阵时尤其要小心计算后的值是否在0-255范围内。

那么,现在我们自定义View用来显示处理后的图片显示:

class ColorMatrixFilterView : View {    private val mColorArray = floatArrayOf(            1f, 0f, 0f, 0f, 50f,            0f, 1f, 0f, 0f, 50f,            0f, 0f, 1f, 0f, 50f,            0f, 0f, 0f, 1f, 0f)    lateinit var mBitMap: Bitmap    lateinit var mPaintFilter: Paint    lateinit var mColorMatrix: ColorMatrix    constructor(context: Context) : super(context) {        initData()    }    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {        initData()    }    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {        initDatata()    }    private fun initData() {        // 创建画笔        mPaintFilter = Paint()        // 创建BitMap        mBitMap = BitmapFactory.decodeResource(resources, R.mipmap.ic_sea)        // 新建颜色矩阵对象        mColorMatrix = ColorMatrix()        // 设置颜色矩阵的值        mColorMatrix.set(mColorArray)    }    override fun onDraw(canvas: Canvas?) {        super.onDraw(canvas)        // 设置画笔颜色过滤器        mPaintFilter.colorFilter = ColorMatrixColorFilter(mColorMatrix)        // 绘制处理后的图片        canvas?.drawBitmap(mBitMap, 0f, 0f, mPaintFilter)    }}

看上面的例子,从本地资源加载图片并创建了一个BitMap,然后创建了一个ColorMatrix对象,将一个4*5的数组设置为颜色矩阵。接下来,创建了一个ColorMatrixColorFilter对象,并传递刚才创建的ColorMatrix对象。然后调用setColorFilter方法将ColorMatrixColorFilter对象传入.

运行后的效果如下:

这里写图片描述

来修改下,刚才的示例中并没有添加透明度,把透明度的偏移量添加上:

private val mColorArray = floatArrayOf(        1f, 0f, 0f, 0f, 50f,        0f, 1f, 0f, 0f, 50f,        0f, 0f, 1f, 0f, 50f,        0f, 0f, 0f, 1f, 120f)

看下效果

这里写图片描述

瞬间有股高大尚的感觉,再拍照时,各种复古模样的照片应该也是采用这种方式,改变了图片本身的颜色矩阵…

ColorMatrix常用API

以下示例中,所用到的ColorMatrix对象矩阵为:

val colorArray = floatArrayOf(        1f, 0f, 0f, 0f, 50f,        0f, 1f, 0f, 0f, 50f,        0f, 0f, 1f, 0f, 50f,        0f, 0f, 0f, 1f, 0f)cm = ColorMatrix(colorArray)
  • reset():该方法将ColorMatrix对象中的颜色矩阵重置为矩阵恒等式:

    [ 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
  • setConcat(ColorMatrix matA, ColorMatrix matB):将颜色矩阵matA和matB复合,相当与对图片进行matA矩阵处理再进行矩阵matB处理。
  • postContact(ColorMatrix prematrix):若matA.postConcat(postmatrix)等价与 setConcat(postmatrix,matA)
  • preConcat(ColorMatrix prematrix):若matA.preConcat(prematrix)等价与 setConcat(matA,prematrix)。
  • setScale(float rScale, float gScale, float bScale, float aScale):设置R、G、B、A对应变量值转到对应的倍数:

假如调用setCsrcale(1f, 2f, 0.5f, 1f),所得的结果为:

    Log.i("123", "转换前")    Log.i("123", "cm: ${cm.array.asList()}")    Log.i("123", "cmA: ${cmA.array.asList()}")    cm.postConcat(cmA)    Log.i("123", "转换前")    Log.i("123", "cm: ${cm.array.asList()}")    Log.i("123", "cmA: ${cmA.array.asList()}")    cmfv.setColorMatrix(cm)    //Log    转换前:    cm: [1.0, 0.0, 0.0, 0.0, 50.0,          0.0, 1.0, 0.0, 0.0, 50.0,         0.0, 0.0, 1.0, 0.0, 50.0,          0.0, 0.0, 0.0, 1.0, 0.0]    转换后:    cm: [1.0, 0.0, 0.0, 0.0, 0.0,         0.0, 2.0, 0.0, 0.0, 0.0,         0.0, 0.0, 0.5, 0.0, 0.0,          0.0, 0.0, 0.0, 1.0, 0.0]看这个函数的结果,<font color='red'>实际对应设置的是颜色矩阵对角线上的值,而颜色的偏移量均设置为0,这是为毛呢?</font>.我们来看看setScale函数的源码:![这里写图片描述](http://img.blog.csdn.net/20171011141236883?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSU9fRmllbGQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

- setRotate(int axis, float degrees):设置颜色分量旋转:axis为0时,旋转红色;axis为1时,旋转绿色;axis为2时,旋转蓝色.如何对颜色矩阵赋值。

![这里写图片描述](http://img.blog.csdn.net/20171011143211466?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSU9fRmllbGQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)值得注意的是,首先是通过调用reset函数将原颜色矩阵初始化。在该类中的很多函数调用,这样处理,可以看该类源码了解。
  • setSaturation(float Sat):通过改变矩阵的值设置图像的饱和度。参数0映射为对应的灰色图像,则没有改变。

    这里写图片描述

    实际效果就是将彩色图片转换为了黑白图片显示了。

LightingColorFilter

LightingColorFilter是一个模拟照明效果的过滤器,有两个目的:一是将原图的颜色过滤,二是将添加色映射到图片上。在其构造函数中接受两个参数:

LightingColorFilter(int mul, int add)

其中,mul用于与源颜色相乘(称为colormultiply),而add用于添加到源颜色上(称为colorAdd),这两个值都是16进制的色彩值0xAARRGGBB。需要注意的是:Alpha通道是原封不动的彩色滤光片。如果给定一个颜色值,可以这样计算出过滤后的颜色:

R' = R * colorMultiply.R + colorAdd.RG' = G * colorMultiply.G + colorAdd.GB' = B * colorMultiply.B + colorAdd.B

如果想将图片中的绿色过滤掉,可以修改G通道,将colorMultiply.G和colorAdd.G赋值为00,那么G的值应为00,也就意味着将图片中的绿色过滤掉。由于其他通道是不变的,colorMultiply.R=colorMultiply.B=FF,colorAdd.R=colorAdd.B=0。A通道是可以忽略的不做考虑。所以可以这么做:

// 设置画笔颜色过滤器mPaintFilter.colorFilter = LightingColorFilter(0xFFFF00FF.toInt(), 0x00000000)// 绘制处理后的图片canvas?.drawBitmap(mBitMap, 0f, 0f, mPaintFilter)

效果图:

这里写图片描述

处理后的图片中已经没有绿色,原来绿色的部分变成了蓝色。这只是将原图中的绿色过滤掉了,又想贪心的将红色映射到图片上,那只能改代码:

// 设置画笔颜色过滤器mPaintFilter.colorFilter = LightingColorFilter(0xFFFF00FF.toInt(), 0x00FF0000)// 绘制处理后的图片canvas?.drawBitmap(mBitMap, 0f, 0f, mPaintFilter)

效果图:

这里写图片描述

PorterDuffColorFilter

PorterDuffColorFilter是一种混合模式的色彩过滤器,它就是一种特殊的图形折叠处理模式,用于将单一的颜色和指定的Porter-Duff模式对源像素的色调进行过滤。至于Porter-Duff中所定义的那些模式,后续介绍,先简单做个例子看下效果:

// 设置画笔颜色过滤器mPaintFilter.colorFilter = PorterDuffColorFilter(0xFFFF0000.toInt(), PorterDuff.Mode.LIGHTEN)// 绘制处理后的图片canvas?.drawBitmap(mBitMap, 0f, 0f, mPaintFilter)

效果图:

这里写图片描述




若想了解更多Paint相关的内容,请跳入: 自定义View系列文章目录




如果觉得我的文章对您有用,请随意点赞、评论。您的支持将鼓励我继续创作!