赞
踩
目录
像画画一样,需要画板(canvas)和画笔(paint)
至于画板画什么,和用什么样的画笔,参考:https://blog.csdn.net/qq_37321098/article/details/84959425
安卓中用画板canvas画画,还需要传入bitmap,将bitmap和canvas绑定在一起(装载画布过程)。
(官方也不推荐使用无参方法构造canvas,需调用setBitmap函数为其设置一个Bitmap),看看源码验证一下:
- //bitmap被限定非非空了
- public Canvas(@NonNull Bitmap bitmap) {
- if (!bitmap.isMutable()) {
- throw new IllegalStateException("Immutable bitmap passed to Canvas constructor");
- }
- ...
- }
bitmap作用是用来存储所有绘制在canvas上的像素信息,所以通过这种方式创建的canvas,后面调用的Canvas.drawXX都是发生在这个bitmap上面。
换句话说:我们调用了许多Canvas.drawXX,但是其实并没有将图形直接绘制在canvas上,而是通过改变bitmap,让view重绘,从而显示改变后的bitmap。
问题:view的ondraw方法,为什么没看见bitmap绑定canvas过程?
这个系统传递给我们的canvas来自于ViewRootImpl的Surface,在绘图时系统将会SkBitmap设置到SkCanvas中并返回与之对应Canvas。所以,在onDraw()中也是有一个Bitmap的,只是这个Bitmap是由系统创建的罢了。
- @Override
- protected void onDraw(Canvas canvas) {
- //控件自身绘制前,如果是textview,则是绘制文字操作前
- super.onDraw(canvas);
- //控件自身绘制后,如果是textview,则是绘制文字操作后
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawColor(Color.GREEN);
- //初始文字
- Paint paint = new Paint();
- paint.setColor(Color.BLACK);
- paint.setTextSize(60);
- canvas.drawText("画布初始位置",100,100,paint);
- //平移后画的文字
- canvas.translate(100,100);
- paint.setColor(Color.RED);
- canvas.drawText("画布变化后位置",100,100,paint);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawColor(Color.GREEN);
- //初始文字
- Paint paint = new Paint();
- paint.setColor(Color.BLACK);
- paint.setTextSize(60);
- canvas.drawText("画布初始位置",100,100,paint);
- //旋转后画的文字
- canvas.rotate(15);
- paint.setColor(Color.RED);
- canvas.drawText("画布变化后位置",100,100,paint);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawColor(Color.GREEN);
- //初始文字
- Paint paint = new Paint();
- paint.setColor(Color.BLACK);
- paint.setTextSize(60);
- canvas.drawText("画布初始位置",100,100,paint);
- //裁剪后画的文字
- Rect rect = new Rect(10,200,600,500);
- canvas.clipRect(rect);
- canvas.drawColor(Color.BLUE);
- paint.setColor(Color.RED);
- canvas.drawText("画布变化后位置",10,250,paint);
- }
注意:
1.调用了canvas.clipRect( )后,再继续画图那么所绘的图只会在所剪裁的范围内体现
2.除了按照矩形剪裁以外,还可以有别的剪裁方式,比如:canvas.clipPath( )和canvas.clipRegion( )
通过canvas.save锁定画布,将已经所绘的图形锁定,之后的绘图就不会影响到原来画好的图形。
之后的操作又发生在哪里呢?会生成一个新的图层(Layer),并且这个图层是透明的。此时,所有draw的方法都是在这个图层上进行,所以不会对之前画好的图形造成任何影响。
通过canvas.restore将canvas.save( )时会生成一个新的图层(Layer)与底下原本的画好的图像相结合形成一个新的图像。
因此,save( )和restore( )最好配对使用,若restore( )的调用次数比save( )多可能会造成异常绘制。
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawColor(Color.GREEN);
- //初始文字
- Paint paint = new Paint();
- paint.setColor(Color.BLACK);
- paint.setTextSize(60);
- canvas.drawText("画布初始位置",100,100,paint);
- canvas.save();
- //save+旋转后画的图
- canvas.rotate(15);
- Bitmap bit= BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
- canvas.drawBitmap(bit, 100, 100, paint);
- canvas.restore();
- //合成后画字
- paint.setColor(Color.RED);
- canvas.drawText("画布变化后位置",250,250,paint);
- }
绘制初始文字和图片的2个图层合成之后,canvas的旋转属性也消失了,通过最后画的文字的旋转角度可知。
可以通过saveLayout()或者saveLayoutAlpha()去创建图层,创建的新图层遵循'先入后出'的概念,即新创建的图层永远在最上层,因此,我们画的内容也是绘制在最上层。
同样通过restore和restoreTocount()将图层出栈,出栈后,绘制的内容也是绘制在最上层的Layout图层上。
PorterDuffXfermode可以给画笔加上一些高级属性,可以设置两个图层交集区域的显示方式。
下面看看PorterDuffXfermode的16个定义常量对绘图效果的最终影响:
解释如下:
- //绘制内容
- private Bitmap mBitmap;
- //占位bitmap
- private Bitmap mOutBitmap;
-
- public void init() {
- mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.draw_img);
- mOutBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(),
- Bitmap.Config.ARGB_8888);
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);//关掉硬件加速
- }
-
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- Paint paint = new Paint();
- paint.setAntiAlias(true);
- canvas.drawRoundRect(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), 30, 30, paint);
- //交集只显示上层
- paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- canvas.drawBitmap(mBitmap,0,0,paint);
- }
原图:
处理后:
图像描述由3部分组成:
而在android中,系统用一个4×5的颜色矩阵ColorMatrix来处理图像。
初始颜色矩阵,不对原有的颜色值进行任何变化
分别可以通过改变系数 或者 偏移量去修改原有颜色值
a.修改色调
- ColorMatrix colorMatrix1 = new ColorMatrix();
- //第一个参数表示处理哪种颜色:0代表红 1绿色 2蓝色
- //第二个参数表示处理的值
- colorMatrix1.setRotate(0,100);
b.修改饱和度
- ColorMatrix colorMatrix2 = new ColorMatrix();
- //第一个参数表示处理的值
- colorMatrix2.setSaturation(0);
c.修改亮度
- //利用原理:三原色已相同比例混合,会显示白色
- int value = 1;
- ColorMatrix colorMatrix3 = new ColorMatrix();
- //4个参数分别为R-G-B-A要处理的值
- colorMatrix3.setScale(value,value,value,1);
d.组合a、b、c三种效果
- ColorMatrix colorMatrix = new ColorMatrix();
- colorMatrix.postConcat(colorMatrix1);
- colorMatrix.postConcat(colorMatrix2);
- colorMatrix.postConcat(colorMatrix3);
图像反转矩阵:
- //绘制内容
- private Bitmap mBitmap;
- //占位bitmap
- private Bitmap mOutBitmap;
- //各种算法矩阵,可自行百度实验
- //图像反转矩阵
- // -1,0,0,1,1,
- // 0,-1,0,1,1,
- // 0,0,-1,1,1,
- // 0,0,0,1,0
- float value[] = {-1,0,0,1,1,0,-1,0,1,1,0,0,-1,1,1,0,0,0,1,0};
-
- public void init() {
- mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.draw_img);
- mOutBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(),
- Bitmap.Config.ARGB_8888);
- //关掉硬件加速
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- }
-
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- //传入颜色矩阵值
- android.graphics.ColorMatrix colorMa = new android.graphics.ColorMatrix();
- colorMa.set(value);
- //绘图
- Paint paint = new Paint();
- paint.setColorFilter(new ColorMatrixColorFilter(colorMa));
- canvas.drawBitmap(mBitmap,0,0,paint);
- }
原图:
变化后:
不会改变原图,而是拿到原图像素点,并生成新图来修改。
先拿到原图像素点,且像素点是用数组存储,函数如下:
Biamap.getPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height)
参数:
pixels:接收位图颜色值的数组
offset:写入到pixels[]中的第一个像素索引值
stride:pixels[]中的行间距个数值(必须大于等于位图宽度)。不能为负数
x:从位图中读取的第一个像素的x坐标值。
y:从位图中读取的第一个像素的y坐标值
width:从每一行中读取的像素宽度
height:读取的行数
参考:https://www.cnblogs.com/fordreamxin/p/4605693.html
当我们拿到像素点数组oldPx后,还可以拿到每个像素点的RGBA值,如下:
int r=Color.red(oldPx[0]);
拿到rgba后,就可以调用一些图像处理的算法,去改变RGBA值,从而将改变后的RGBA值合成新的像素点,如下:
newPx[0]=Color.argb(a1,r1,g1,b1);
最终将新像素点数组,设置给bitmap,达到修改的目的,如下:
bitmap.setPixels(newPx,0,width,0,0,width,height);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。