Android 图片特效处理:drawBitmapMesh——图像扭曲+动感

Android 专栏收录该内容
188 篇文章 0 订阅

  最近学了好多绘图啊,图片处理啊之类的,想着学全面一点,就又研究了一下drawBitmapMesh,drawBitmapMesh也是在Canvas画布上进行使用的,它具有能够使图片扭曲变形的效果,感觉有点像哈哈镜,只不过哈哈镜是直接照出来的效果,而drawBitmapMesh是通过改变它所有点的坐标位置改变图像的。

drawBitmapMesh核心原理

  我们使用drawBitmapMesh是通过这行代码实现的 canvas.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset, paint)
,可以看出来参数一是一张Bitmap图像,参数二、三分别是扭曲图像分割的点数(继续看你就会理解这里的意思),verts是变化的坐标点,vertOffset是偏移量。我们只能通过改变verts来改变图片的形状。
下面来看下drawBitmapMesh实现的核心原理,看下面一张图片,我们将图片进行分割成了许许多多的小方块,形成网格,网格的交叉点就是我们所要获得的坐标点。通过将一张图片分割,获取坐标点,之后改变坐标点,图片就会呈现不同的形状了(这里可能不太好理解,如果有学过ps可能就比较好理解了)。
这里写图片描述
注:上面的图片我们分成了5*5的网格,这样就会产生6*6个坐标点

实例:揉动的图片

这里写图片描述
思路:
1、确定划分网格数
2、通过划分的网格数,确定变化数组float[] verts与原始数组float[] origs存放坐标点,使用一个数组存xy坐标(偶数位为X坐标,奇数位为Y坐标)。
3、通过for循环获得所有坐标点
4、监听手势,改变verts[]数组值,刷新界面
5、在canvas上绘制改变后的Bitmap

public class MydrawBitmapMesh extends View{
    private Bitmap mbitmap;
    //将图片划分成200*200个小格
    private static final int WIDTH=200;
    private static final int HEIGHT=200;
    //小格相交的总的点数
    private int COUNT=(WIDTH+1)*(HEIGHT+1);
    private float[] verts=new float[COUNT*2];
    private float[] origs=new float[COUNT*2];
    private float k;
    public MydrawBitmapMesh(Context context) {
        super(context);
        init();
    }
    public MydrawBitmapMesh(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();

    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);       
        canvas.drawBitmapMesh(mbitmap, WIDTH, HEIGHT, verts, 0, null, 0, null); 
        canvas.drawBitmap(mbitmap, 100, 300, null);
        invalidate();
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        touchwrap(event.getX(),event.getY());
        return super.onTouchEvent(event);
    }
    private void touchwrap(float x, float y) {
        for(int i=0;i<COUNT*2;i+=2){
            //x/y轴每个点坐标与当前x/y坐标的距离
            float dx=x-origs[i+0];
            float dy=y-origs[i+1];
            float dd=dx*dx+dy*dy;
            //计算每个坐标点与当前点(x、y)之间的距离     
            float d=(float) Math.sqrt(dd);
            //计算扭曲度,距离当前点越远的点扭曲度越小
            float pull=80000/((float)(dd*d));
            //对verts重新赋值
            if(pull>=1){
                verts[i+0]=x;
                verts[i+1]=y;
            }else{
                verts[i+0]=origs[i+0]+dx*pull;
                verts[i+1]=origs[i+1]+dy*pull;
            }

        }
        invalidate();

    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {     
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    public void init(){
        int index=0;
        mbitmap=BitmapFactory.decodeResource(getResources(),R.drawable.dog);
        float bitmapwidth=mbitmap.getWidth();
        float bitmapheight=mbitmap.getHeight();
        for(int i=0;i<HEIGHT+1;i++){
            float fy=bitmapwidth/HEIGHT*i;
            for(int j=0;j<WIDTH+1;j++){
                float fx=bitmapheight/WIDTH*j;
                //偶数位记录x坐标  奇数位记录Y坐标
                origs[index*2+0]=verts[index*2+0]=fx;
                origs[index*2+1]=verts[index*2+1]=fy;
                index++;
            }
        }

    }
}

实例:飘动的图片

思路与上面实例相同,只是verts的改变形式不同而已。
这里写图片描述

public class MydrawBitmapMesh extends View{
    private Bitmap mbitmap;
    //将图片划分成200个小格
    private static final int WIDTH=200;
    private static final int HEIGHT=200;
    //小格相交的总的点数
    private int COUNT=(WIDTH+1)*(HEIGHT+1);
    private float[] verts=new float[COUNT*2];
    private float[] origs=new float[COUNT*2];
    private float k;
    public MydrawBitmapMesh(Context context) {
        super(context);
        init();
    }
    public MydrawBitmapMesh(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();

    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        for(int i=0;i<HEIGHT+1;i++){
            for(int j=0;j<WIDTH+1;j++){
                //x坐标不变
                verts[(i*(WIDTH+1)+j)*2+0]+=0;
            //增加k值是为了让相位产生移动,从而可以飘动起来
                float offset=(float)Math.sin((float)j/WIDTH*2*Math.PI+k);
                //y坐标改变,呈现正弦曲线
                verts[(i*(WIDTH+1)+j)*2+1]=origs[(i*(WIDTH+1)+j)*2+1]+offset*50;
            }
        }
        k+=0.4f;  

        canvas.drawBitmapMesh(mbitmap, WIDTH, HEIGHT, verts, 0, null, 0, null); 
        canvas.drawBitmap(mbitmap, 100, 300, null);
        invalidate();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {     
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    public void init(){
        int index=0;
        mbitmap=BitmapFactory.decodeResource(getResources(),R.drawable.dog);
        float bitmapwidth=mbitmap.getWidth();
        float bitmapheight=mbitmap.getHeight();
        for(int i=0;i<HEIGHT+1;i++){
            float fy=bitmapwidth/HEIGHT*i;
            for(int j=0;j<WIDTH+1;j++){
                float fx=bitmapheight/WIDTH*j;
                //偶数位记录x坐标  奇数位记录Y坐标
                origs[index*2+0]=verts[index*2+0]=fx;
                origs[index*2+1]=verts[index*2+1]=fy;
                index++;
            }
        }

    }
}
  • 1
    点赞
  • 2
    评论
  • 5
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值