Android:视图绘制(六) ------Paint进阶之ColorMatrix(一)
来源:互联网 发布:自定义票据打印软件 编辑:程序博客网 时间:2024/05/29 12:18
本文主要讲ColorMatrix(色彩矩阵),Android中的图像颜色变换(色值,色相,饱和度,亮度,滤镜等一系列效果)均可用其实现。
写本文的时候距离这系列文章的上一篇已经有一段时间了,最近公司我负责的app有一次重大的改版,时间有点紧,加上本文有点难度,我需要一段连续的时间来让自己有一个完整的思路,这样写的东西才不会让人摸不着头脑,所以,拖了好久,迟迟不敢动笔。
步入正题:
Paint,我们知道所有的绘制都是基于Paint的,Paint中提供了一系列的方法来达到不同的绘制效果。其中用来设置颜色的方法是 Paint.setColorFilter(),其源码如下:
/** * Set or clear the paint's colorfilter, returning the parameter. * * @param filter May be null. The new filter to be installed in the paint * @return filter */ public ColorFilter setColorFilter(ColorFilter filter) { int filterNative = 0; if (filter != null) filterNative = filter.native_instance; native_setColorFilter(mNativePaint, filterNative); mColorFilter = filter; return filter; }
其中作为参数的 ColorFilter 派生了三个子类 ColorMatrixColorFilter,LightingColorFilter,PorterDuffColorFilter 来达到不同的效果。
这里,主要了解下 ColorMatrixColorFilter ,我们通过看他的源码,发现其内部都是一些native的方法,我们所能做的主要操作都是通过其参数 ColorMatrix 来实现的,这也是我们这里主要讲的。
ColorMatrix 是android中用于处理颜色的类。从名字来看是颜色矩阵的意思,因为Android中颜色都是通过矩阵的形式来表示的。
所以想要透彻的理解 ColorMatrix,我们需要先了解一下矩阵。如果有过大学的教育经历的话,应该知道矩阵是高等数学中的内容(现在知道不好好学习的后果了吧,,)。
矩阵概述
定义 (百度百科)
由 m × n 个数aij排成的m行n列的数表称为m行n列的矩阵,简称m × n矩阵。记作:
这m×n 个数称为矩阵A的元素,简称为元,数aij位于矩阵A的第i行第j列,称为矩阵A的(i,j)元,以数 aij为(i,j)元的矩阵可记为(aij)或(aij)m × n,m×n矩阵A也记作Amn。
乘法
矩阵有各种运算,但是Android中颜色的变换都是基于矩阵的乘法,所以这里只介绍乘法。
两个矩阵的乘法仅当第一个矩阵A的列数和另一个矩阵B的行数相等时才能定义。如A是m×n矩阵和B是n×p矩阵,它们的乘积C是一个m×p矩阵 ,它的一个元素:
这里有几点需要特别注意:
1.两个矩阵相乘,只有前一个矩阵的行等于后一个矩阵的列,才可以进行。例:3X2矩阵 乘以 2X3矩阵 其结果为 3X3矩阵。
2.矩阵的乘法,通俗来讲就是用前一个矩阵的每一行和后一个矩阵的每一列对应的数做乘积在相加。
3.矩阵的乘法不满足交换律,所以矩阵A 乘以 矩阵B 和 矩阵B 乘以 矩阵A 是不同的。
色彩矩阵
先介绍一些基本概念:
在Android中最常用来表示图片的数据结构是位图 —- Bitmap。Bitmap中包含了一张图片的所有数据,其包括表示位置的点阵和表示图像的颜色值。所谓点阵就是一个包含像素的矩阵,每一个元素对应着图片的一个像素;而颜色值——ARGB,分别对应透明度、红、绿、蓝这四个通道分量,它们共同决定了每个像素点显示的颜色。每个通道用8比特定义,所以一个颜色值就是一个int整型,可以表示256*256*256种颜色值。
如果你曾经操作Bitmap做过图形处理,你一定见过这几个常量。ARGB_8888、ARGB_4444、RGB_565。这几个常量其实就是告诉系统如何对图片的颜色值进行处理,例如 ARGB_8888 是告诉系统透明度、R、G、B在颜色值中分别用8bit表示,这时颜色值为32bit,这样的定义能够表示最多的颜色值,图片质量也是最好的;ARGB_4444 则是每个通道用4bit表示,这样颜色值只用16bit,节省了空间,但是却只能表示16*16*16种颜色,也就是说图片会失去很多彩色信息;RGB_565 类型的颜色值同样是16bit,但是它丢弃了透明度信息,可以表示32*64*32种颜色值。
4X4 矩阵表示
由于一个色彩信息包含R、G、B、Alpha信息,所以,我们必然要使用一个4阶色彩变换矩阵来修改色彩的每一个分量值:
注意:对于色彩变换矩阵,这里的色彩顺序是R、G、B、A而不是A、R、G、B!!!
如果想将色彩(0,255,0,255)更改为半透明时,可以使用下面的的矩阵运算来表示:
4X5 矩阵表示
上面使用四阶矩阵完全可以改变图片的RGBA值了,但考虑一种情况,如果我们只想在原有的R色上增加一些分量呢?
这时,我们就得再多加一阶来表示平移变换。所以,一个既包含线性变换,又包含平移变换的组合变换,称为仿射变换。使用四阶的色彩变换矩阵来修改色彩,只能够对色彩的每一个分量值进行乘(除)运算,如果要对这些分量值进行加减法的运算(平移变换),只能通过五阶矩阵来完成。
考虑下面这个变换:
1、红色分量值更改为原来的2倍;
2、绿色分量增加100;
则使用4阶矩阵的乘法无法实现,所以,应该在四阶色彩变换矩阵上增加一个“哑元坐标”,来实现所列的矩阵运算:
这个矩阵中,分量值用的是100。
所以Android的颜色矩阵 ColorMatrix 是一个 4X5 的矩阵,以一维数组 m=[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t] 的形式进行存储。
而对于图像中每一个像素的颜色是以一个 5X1 的颜色分量矩阵表示的。
在处理图像时,使用矩阵乘法运算MC来处理颜色分量矩阵:
所以每一个颜色通道的值如下:
R1 = a*R + b*G + c*B + d*A + e;
G1 = f*R + g*G + h*B + i*A + j;
B1 = k*R + l*G + m*B + n*A + o;
A1 = p*R + q*G + r*B + s*A + t;
可以看到这样我们就可以通过色彩变幻矩阵来调整图像的颜色值,来达到不同的效果。
实例
在上面的所有讲解之后,大家也应该看出来了,色彩变换矩阵的表示形式,肯定是五阶的那种,所以大家看一下,在默认情况下,色彩变换矩阵的形式:
Android中的色彩矩阵是用ColorMatrics类来表示的。使用ColorMatrix的方法如下:
// 生成色彩矩阵 ColorMatrix colorMatrix = new ColorMatrix(new float[]{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, }); mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
1.通道
通过上面的讲解,我们知道,当颜色变换矩阵为时,图像的ARGB都不改变。但是当我们把RGB其中的两个变成0,另一个保持不变,会出现什么效果?在PS中,这个叫做通道。
先看效果图:(请注意按钮的水波纹效果,GIF可能不太清晰,其实每次都是通过按钮点击触发事件的)
上面每一个通道,都是把其对应的颜色值置为1,其余的置为0。复原是把三个颜色都设置成1,即图像原始色彩。
以蓝色举例,蓝色通道:
// 蓝色通道色彩矩阵 ColorMatrix colorMatrix = new ColorMatrix(new float[]{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, }); mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
2.饱和度
所谓的饱和度,指的其实是色彩的纯度,纯度越高,表现越鲜明,纯度较低,表现则较黯淡。(出自百度百科)饱和度,又称彩度或色纯度,意思就是“色彩的鲜艳程度”。讲得有学问一点,就是指一种颜色与相同亮度的消色(即白、灰、黑色)之间差别的程度。当某种色彩与消色对比,当色彩成分越高,该色的饱和度越高,颜色也就越鲜艳;消色成分越高,该色彩饱和度越低,颜色也就越不鲜艳,显得越灰。我们用黑白摄影的概念来想就很清楚:当色光投射到黑白底片上时,会依照其亮度被转换为相应的黑、白、灰色,也就是完全消色了。
通俗来讲,就是每种颜色的分量越多,这种颜色的饱和度就越高。上效果图(请原谅我的清晰度,CSDN最大就只能传2M,刚才第一个GIF做出来5M多,直接把CSDN干崩了):
最后的时候,当RGB三种色值都加到最大的时候,图片变成了白色,其实不难理解,单三种色值都最大的时候为 255,255,255。这不就是白色吗。
下面以红色饱和度为例:
ColorMatrix colorMatrix = new ColorMatrix(new float[]{ 1, 0, 0, 0, R, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, });
以更改R的值来改变红色的饱和度。
3.反相
每种颜色相对于255的补值,叫做他的补色。一幅图像上有很多颜色,每个颜色都转成各自的补色,叫做反相。ps中也有反相功能。反相的颜色矩阵固定,其值如下:
// 颜色反转矩阵 private float[] reversalMatrix = new float[]{ -1, 0, 0, 0, 255, 0, -1, 0, 0, 255, 0, 0, -1, 0, 255, 0, 0, 0, 1, 0 };
效果图如下:
4.亮度
现在我们知道,通过 ColorMatrix 我们可以放大或是缩小每种颜色的值,当对RGBA同时进行缩放的时候,改变的就是图像的亮度。
// 颜色矩阵 float[] matrix = new float[]{ brightness, 0, 0, 0, 0, 0, brightness, 0, 0, 0, 0, 0, brightness, 0, 0, 0, 0, 0, brightness, 0};
brightness 为缩放的倍数。
效果图如下:
5.色相
色相,色彩可呈现出来的质的面貌。色相是由原色、间色和复色来构成的。(百度百科)
Android中的色相是以一种原色为基准,另两种原色做正余弦变换来改变色值的。这块有点难度。
以红色为基准的颜色变换矩阵:
float[] matrix = new float[]{ 1, 0, 0, 0, 0, 0, (float) Math.cos(brightness), (float) Math.sin(brightness), 0, 0, 0, -(float) Math.sin(brightness), (float) Math.cos(brightness), 0, 0, 0, 0, 0, 1, 0};
以绿色为基准的颜色变换矩阵:
float[] matrix = new float[]{ (float) Math.cos(brightness), 0, -(float) Math.sin(brightness), 0, 0, 0, 1, 0, 0, 0, (float) Math.sin(brightness), 0, (float) Math.cos(brightness), 0, 0, 0, 0, 0, 1, 0};
以蓝色为基准的颜色变换矩阵:
float[] matrix = new float[]{ (float) Math.cos(brightness), (float) Math.sin(brightness), 0, 0, 0, -(float) Math.sin(brightness), (float) Math.cos(brightness), 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0};
此处用了Math类来求正余弦而没有直接贴公式,因为我认为这样对我们coder来说,更有直观性。至于具体怎么导出来的,有一点难度,本人也没有实际导过,而是直接从源码中拿出来的。其原理并没有太过深入的理解。
效果图如下:
上面讲了一大堆利用色彩矩阵的来做一些运算,但这些都是需要特定的色彩设计基础的,所以适合想要深入了解的人,如果只是想知道怎么用的话,其实Android中ColorMatrix自带了一些函数来帮我们完成一些调整饱和度、色彩旋转等操作的函数。还有一些滤镜效果,我们下一篇在详细介绍。
下面贴出本文完整的代码:
布局文件:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.yogee.photoshopapp.MainActivity"> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android:id="@+id/image" android:layout_width="match_parent" android:layout_height="300dp" android:scaleType="fitCenter" android:src="@drawable/bg" /> <LinearLayout android:id="@+id/progress" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/image" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="R" /> <SeekBar android:id="@+id/sb_red" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="255" android:progress="0" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="G" /> <SeekBar android:id="@+id/sb_green" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="255" android:progress="0" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="B" /> <SeekBar android:id="@+id/sb_blue" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="255" android:progress="0" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="亮度" /> <SeekBar android:id="@+id/sb_brightness" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="20" android:progress="10" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="蓝色色相" /> <SeekBar android:id="@+id/sb_hueB" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="360" android:progress="0"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="红色色相"/> <SeekBar android:id="@+id/sb_hueR" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="360" android:progress="0" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="绿色色相" /> <SeekBar android:id="@+id/sb_hueG" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="360" android:progress="0" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/progress" android:layout_marginTop="10dp" android:gravity="center" android:orientation="horizontal"> <Button android:id="@+id/restore" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="3dp" android:text="复原" /> <Button android:id="@+id/blue" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="3dp" android:text="蓝色通道" /> <Button android:id="@+id/red" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="3dp" android:text="红色通道" /> <Button android:id="@+id/green" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="3dp" android:text="绿色通道" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/progress" android:layout_marginTop="10dp" android:gravity="center" android:orientation="horizontal"> <Button android:id="@+id/reversal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="3dp" android:text="反相" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="3dp" android:text="蓝色通道" android:visibility="invisible" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="3dp" android:text="红色通道" android:visibility="invisible" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="3dp" android:text="绿色通道" android:visibility="invisible" /> </LinearLayout> </LinearLayout> </ScrollView></LinearLayout>
Activity代码:
/** * colorMatrix 示例代码 * * @author 阿东_Luck * @date 2016-11-2 19:26:30 */public class MainActivity extends Activity implements View.OnClickListener { // 资源图片 private ImageView image; // 蓝色通道 private Button blue; // 红色通道 private Button red; // 绿色通道 private Button green; // 复原 private Button restore; // 颜色反转 private Button reversal; // 资源Bitmap private Bitmap bg; // 生成Bitmap private Bitmap changeBitmap; // 画布 private Canvas mCanvas; // 画笔 private Paint mPaint; // 蓝色分量值 private int colorB = 1; // 红色分量值 private int colorR = 1; // 绿色分量值 private int colorG = 1; private SeekBar sb_red, sb_green, sb_blue, sb_brightness, sb_hueR, sb_hueG,sb_hueB; // 红色增量值 private float progressR; // 绿色增量值 private float progressG; // 蓝色增量值 private float progressB; // 颜色反转矩阵 private float[] reversalMatrix = new float[]{ -1, 0, 0, 0, 255, 0, -1, 0, 0, 255, 0, 0, -1, 0, 255, 0, 0, 0, 1, 0 }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData(); } private void initView() { image = (ImageView) findViewById(R.id.image); blue = (Button) findViewById(R.id.blue); blue.setOnClickListener(this); red = (Button) findViewById(R.id.red); red.setOnClickListener(this); green = (Button) findViewById(R.id.green); green.setOnClickListener(this); restore = (Button) findViewById(R.id.restore); restore.setOnClickListener(this); reversal = (Button) findViewById(R.id.reversal); reversal.setOnClickListener(this); sb_red = (SeekBar) findViewById(R.id.sb_red); sb_green = (SeekBar) findViewById(R.id.sb_green); sb_blue = (SeekBar) findViewById(R.id.sb_blue); sb_brightness = (SeekBar) findViewById(R.id.sb_brightness); sb_hueR = (SeekBar) findViewById(R.id.sb_hueR); sb_hueG = (SeekBar) findViewById(R.id.sb_hueG); sb_hueB = (SeekBar) findViewById(R.id.sb_hueB); sb_red.setOnSeekBarChangeListener(seekBarChange); sb_green.setOnSeekBarChangeListener(seekBarChange); sb_blue.setOnSeekBarChangeListener(seekBarChange); sb_brightness.setOnSeekBarChangeListener(brightnessSeekBarChange); sb_hueR.setOnSeekBarChangeListener(hueSeekBarChangeR); sb_hueG.setOnSeekBarChangeListener(hueSeekBarChangeG); sb_hueB.setOnSeekBarChangeListener(hueSeekBarChangeB); } private SeekBar.OnSeekBarChangeListener seekBarChange = new SeekBar.OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { // 获取每个SeekBar当前的值 progressR = sb_red.getProgress(); progressG = sb_green.getProgress(); progressB = sb_blue.getProgress(); Log.i("main", "R:G:B=" + progressR + ":" + progressG + ":" + progressB); changeColor(); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } }; private SeekBar.OnSeekBarChangeListener brightnessSeekBarChange = new SeekBar.OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { float brightness = seekBar.getProgress() / 10f; // 颜色矩阵 float[] matrix = new float[]{ colorR * brightness, 0, 0, 0, progressR, 0, colorG * brightness, 0, 0, progressG, 0, 0, colorB * brightness, 0, progressB, 0, 0, 0, brightness, 0}; changeColor(matrix); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } }; private SeekBar.OnSeekBarChangeListener hueSeekBarChangeB = new SeekBar.OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { Double brightness = Double.valueOf(seekBar.getProgress()); float[] matrix = new float[]{ (float) Math.cos(brightness), (float) Math.sin(brightness), 0, 0, 0, -(float) Math.sin(brightness), (float) Math.cos(brightness), 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0 }; changeColor(matrix); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } }; private SeekBar.OnSeekBarChangeListener hueSeekBarChangeR = new SeekBar.OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { Double brightness = Double.valueOf(seekBar.getProgress()); float[] matrix = new float[]{ 1, 0, 0, 0, 0, 0, (float) Math.cos(brightness), (float) Math.sin(brightness), 0, 0, 0, -(float) Math.sin(brightness), (float) Math.cos(brightness), 0, 0, 0, 0, 0, 1, 0 }; changeColor(matrix); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } }; private SeekBar.OnSeekBarChangeListener hueSeekBarChangeG = new SeekBar.OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { Double brightness = Double.valueOf(seekBar.getProgress()); float[] matrix = new float[]{ (float) Math.cos(brightness), 0, -(float) Math.sin(brightness), 0, 0, 0, 1, 0, 0, 0, (float) Math.sin(brightness), 0, (float) Math.cos(brightness), 0, 0, 0, 0, 0, 1, 0 }; changeColor(matrix); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } }; private void initData() { // 从资源文件中获取图片 bg = BitmapFactory.decodeResource(getResources(), R.drawable.bg); // 获取一个与baseBitmap大小一致的可编辑的空图片 changeBitmap = Bitmap.createBitmap(bg.getWidth(), bg.getHeight(), bg.getConfig()); mCanvas = new Canvas(changeBitmap); mPaint = new Paint(); } private void changeColor() { // 颜色矩阵 float[] matrix = new float[]{ colorR, 0, 0, 0, progressR, 0, colorG, 0, 0, progressG, 0, 0, colorB, 0, progressB, 0, 0, 0, 1, 0}; changeColor(matrix); } private void changeColor(float[] matrix) { // 定义ColorMatrix,并指定RGBA矩阵 ColorMatrix colorMatrix = new ColorMatrix(); colorMatrix.set(matrix); // 设置Paint的颜色 mPaint.setColorFilter(new ColorMatrixColorFilter(matrix)); // 通过指定了RGBA矩阵的Paint把原图画到空白图片上 mCanvas.drawBitmap(bg, new Matrix(), mPaint); image.setImageBitmap(changeBitmap); } private void resertData() { colorR = 1; colorG = 1; colorB = 1; sb_red.setProgress(0); sb_green.setProgress(0); sb_blue.setProgress(0); sb_brightness.setProgress(sb_brightness.getMax() / 2); progressR = 0; progressG = 0; progressB = 0; } @Override public void onClick(View v) { switch (v.getId()) { case R.id.reversal: resertData(); changeColor(reversalMatrix); break; case R.id.blue: colorR = 0; colorG = 0; colorB = 1; changeColor(); break; case R.id.red: colorR = 1; colorG = 0; colorB = 0; changeColor(); break; case R.id.green: colorR = 0; colorG = 1; colorB = 0; changeColor(); break; case R.id.restore: resertData(); changeColor(); break; default: break; } }}
- Android:视图绘制(六) ------Paint进阶之ColorMatrix(一)
- Android:视图绘制(五) ------Paint进阶之PathEffect
- Android:视图绘制(二) ------Paint进阶
- android自留地(一)ColorMatrix
- Android:视图绘制(一) ------基本的绘图操作Paint和Canvas
- 我的Android进阶之旅------>Android图片处理(Matrix,ColorMatrix)
- android 用户界面和视图绘制(一)
- Android:视图绘制(四) ------Path进阶
- 自定义控件三部曲之绘图篇(八)——Paint之ColorMatrix与滤镜效果
- 自定义控件三部曲之绘图篇(八)——Paint之ColorMatrix与滤镜效果
- 自定义控件之绘图篇(八) —— Paint之ColorMatrix与滤镜效果
- Android|图形图像之ColorMatrix
- Paint之setColorFilter(一)
- Android学习之Paint图形图像处理(一)
- Android视图绘制流程完全解析(一)
- Android Paint 之 获取绘制的 Path 文本的Path
- 学习有关Paint绘制效果总结(一)
- Android Canvas Paint绘制文本
- 学习设计模式(1)——面向接口编程
- POJ 2394 Dijkstra
- mybatis if标签test 判断数字遇到的问题
- mac os webStorm 完全卸载
- Android 爬坑之旅:软键盘挡住输入框问题的终极解决方案
- Android:视图绘制(六) ------Paint进阶之ColorMatrix(一)
- C++程序栈与尾递归优化
- lighttpd及pcre的交叉编译
- JVM性能调优监控工具jps、jstack、jmap、jhat、jstat、hprof使用详解
- cmd - 常用命令
- http错误码大全
- delete/newoperator operatornew/delete operatornew[]/delete[] placementnew/delete
- 位图索引bitmap(三):位图索引技术集合FastBit
- cpp的STL值vector和deque