前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android粒子篇之Bitmap像素级操作

Android粒子篇之Bitmap像素级操作

作者头像
张风捷特烈
发布2018-12-14 17:01:45
1.7K0
发布2018-12-14 17:01:45
举报
零、前言

1.粒子效果的核心有三个点:收集粒子、更改粒子、显示粒子 2.Bitmap的可以获取像素,从而得到每个像素的颜色值 3.可以通过粒子拼合一张图片,并对粒子操作完成很多意想不到的效果 4.本项目源码见文尾捷文规范第一条,文件为BitmapSplitView.java


一、初识
1.什么是Bitmap像素级的操作:

相信大家都知道一张jpg或png放大后会是一个个小格子,称为一个像素(px),而且一个小格子是一种颜色 也就是一张jpg或png图片就是很多颜色的合集,而这些合集信息都被封装到了Bitmap类中 你可以使用Bitmap获取任意像素点,并修改它,对与某像素点而言,颜色信息是其主要的部分

像素.png


2.获取第一个像素
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.iv_200x200);
int color_0_0 = bitmap.getPixel(0, 0);//获取第1行,第1个像素颜色

使用该颜色画一个圆测试一下:

第一点.png


3.获取所有点像素颜色值

这里i代表列数,j代表行数,mColArri代表是一个图片第i列,第j行的像素颜色值

 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.iv_200x200);
 mColArr = new int[bitmap.getWidth()][bitmap.getHeight()];
 for (int i = 0; i < bitmap.getWidth(); i++) {
     for (int j = 0; j < bitmap.getHeight(); j++) {
         mColArr[i][j] = bitmap.getPixel(i, j);
     }
 }

二、分析:
1.首先看一下如何创建一个Bitmap对象

新建一个2*2的ARGB_8888图片:颜色分别是黑(0,0)、红(1,0)、白(0,1)、蓝(1,1)

Bitmap bitmap = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888);
bitmap.setPixel(0, 0, Color.BLACK);
bitmap.setPixel(1, 0, Color.RED);
bitmap.setPixel(0, 1, Color.WHITE);
bitmap.setPixel(1, 1, Color.BLUE);

2.保存bitmap成为图片到本地

保存bitmap就是压缩到一个输出流里,写成文件

输出结果.png

    /**
     * 保存bitmap到本地
     *
     * @param path    路径
     * @param mBitmap 图片
     * @return 路径
     */
    public static String saveBitmap(String path, Bitmap mBitmap) {

        File filePic = FileHelper.get().createFile(path + ".png");
        try {
            FileOutputStream fos = new FileOutputStream(filePic);
            mBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
            fos.flush();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return filePic.getAbsolutePath();
    }

3.bitmap.getPixel到底说了什么?
int pixel_0_0 = bitmap.getPixel(0, 0);
int pixel_1_0 = bitmap.getPixel(1, 0);
int pixel_0_1 = bitmap.getPixel(0, 1);
int pixel_1_1 = bitmap.getPixel(1, 1);

黑:pixel_0_0:-16777216   
红:pixel_1_0:-65536   
白:pixel_0_1:-1   
蓝:pixel_1_1:-16776961   

都是负数,感觉不怎么好懂:其实那就是颜色值 Color类中有几个方法可以方便获取argb分别对应的值,下面测试一下你就明白了 其实就是将int进行了位运算,分离出argb四个通道的值

printColor("pixel_0_0", pixel_0_0);//黑:a:255, r:0, g:0, b:0
printColor("pixel_1_0", pixel_1_0);//红:a:255, r:255, g:0, b:0
printColor("pixel_0_1", pixel_0_1);//白:a:255, r:255, g:255, b:255
printColor("pixel_1_1", pixel_1_1);//蓝:a:255, r:0, g:0, b:255


/**
 * 将颜色从int 拆分成argb,并打印出来
 * @param msg
 * @param color
 */
private void printColor(String msg, int color) {
    int a = Color.alpha(color);
    int r = Color.red(color);
    int g = Color.green(color);
    int b = Color.blue(color);
    L.d(msg + "----a:" + a + ", r:" + r + ", g:" + g + ", b:" + b + L.l());
}

二、bitmap的花样复刻:

既然像素颜色信息拿到手了,不就等于天下我有了吗?

1.bitmap复刻的粒子载体
public class Ball implements Cloneable {
    public float aX;//加速度
    public float aY;//加速度Y
    public float vX;//速度X
    public float vY;//速度Y
    public float x;//点位X
    public float y;//点位Y
    public int color;//颜色
    public float r;//半径
    public long born;//诞生时间

    public Ball clone() {
        Ball clone = null;
        try {
            clone = (Ball) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
}

2.初始化粒子:

坐标分析.png

private int d = 50;//复刻的像素边长
private List<Ball> mBalls = new ArrayList<>();//粒子集合
/**
 * 根像素初始化粒子
 * @param bitmap
 * @return
 */
private List<Ball> initBall(Bitmap bitmap) {
    for (int i = 0; i < bitmap.getHeight(); i++) {
        for (int j = 0; j < bitmap.getWidth(); j++) {
            Ball ball = new Ball();
            ball.x = i * d + d / 2;
            ball.y = j * d + d / 2;
            ball.color = bitmap.getPixel(i, j);
            mBalls.add(ball);
        }
    }
    return mBalls;
}

3.正方形粒子的绘制(原图复刻):

原图复刻.png

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.save();
    canvas.translate(mCoo.x, mCoo.y);
    for (Ball ball : mBalls) {
        mPaint.setColor(ball.color);
        canvas.drawRect(
                ball.x - d / 2, ball.y - d / 2, ball.x + d / 2, ball.y + d / 2, mPaint);
    }
    canvas.restore();
    HelpDraw.draw(canvas, mGridPicture, mCooPicture);
}

4.复刻其他图片资源文件

只要是bitmap就行了,我们可以decode图片变成Bitmap 注意:此处只是演示,画一张bitmap还是用原生的好,像素级的粒子化由它的优势,比如运动

//加载图片数组
mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.iv_200x200);
initBall(mBitmap);

原图复刻图片资源.png


5.圆形复刻

这里的4中的图片已经不是bitmap了,而是由一个个小正方形堆积成的东西,这些小正方形拥有自己的颜色 然而我们可以利用它实现一些好玩的东西,比如不画正方形,画个圆怎么样? 现在你明白像素级操作什么了吧。也许你会感叹,还能怎么玩,先把下巴收回去,后面还有更惊叹的呢。

圆形复刻图片资源.png

 for (Ball ball : mBalls) {
     mPaint.setColor(ball.color);
     canvas.drawCircle(ball.x, ball.y, d/2, mPaint);
 }

6.其他形状复刻

你可以用任意的图形更换粒子单元,或者各种形状的粒子混合适用,全凭你的操作 像素单元都在你的手上了,还有什么不能做。

五角星复刻图片资源.png

for (int i = 0; i < mBalls.size(); i++) {
    canvas.save();
    int line = i % mBitmap.getHeight();
    int row = i / mBitmap.getWidth();
    canvas.translate(row * 2 * d, line * 2 * d);
    mPaint.setColor(mBalls.get(i).color);
    canvas.drawPath(CommonPath.nStarPath(5, d, d / 2), mPaint);
    canvas.restore();
}

三、关于颜色操作:

Color篇中详细介绍了使用ColorFilterColorMatrix改变图片颜色 这里既然拿到了小球颜色,那么统一改变一下小球的颜色则何如? 注:以下属于改变颜色的旁门左道,主要为了演示。正规还是用ColorFilterColorMatrix

颜色处理.png

1.下面是图片黑白化的算法

以前在JavaScript用过,在Python用过,现在终于用到java上了,不免感慨,语言无界限,真理永恒 所以大家最好收集一下相关的真理,不管何时都不会过时,也不会错误,就像1+1=2

/**
 * 根像素初始化粒子
 *
 * @param bitmap
 * @return
 */
private List<Ball> initBall(Bitmap bitmap) {
    for (int i = 0; i < bitmap.getHeight(); i++) {
        for (int j = 0; j < bitmap.getWidth(); j++) {
            Ball ball = new Ball();
            ball.x = i * d + d / 2;
            ball.y = j * d + d / 2;
            //获取像素点的a、r、g、b
            int color_i_j = bitmap.getPixel(i, j);
            int a = Color.alpha(color_i_j);
            int r = Color.red(color_i_j);
            int g = Color.green(color_i_j);
            int b = Color.blue(color_i_j);
        
            ball.color = blackAndWhite(a, r, g, b);
            mBalls.add(ball);
        }
    }
    return mBalls;
}

/**
 * 配凑黑白颜色
 */
private int blackAndWhite(int a, int r, int g, int b) {
    //拼凑出新的颜色
    int grey = (int) (r * 0.3 + g * 0.59 + b * 0.11);
    if (grey > 255 / 2) {
        grey = 255;
    } else {
        grey = 0;
    }
    return Color.argb(a, grey, grey, grey);
}
2.灰色处理函数
/**
 * 配凑灰颜色
 */
private int grey(int a, int r, int g, int b) {
    //拼凑出新的颜色
    int grey = (int) (r * 0.3 + g * 0.59 + b * 0.11);
    return Color.argb(a, grey, grey, grey);
}
3.颜色反转
//颜色反转
private int reverse(int a, int r, int g, int b) {
    //拼凑出新的颜色
    return Color.argb(a, 255-r, 255-g, 255-b);
}

四、粒子运动

到了最重要的地方了,让小球动起来的核心分析见Android原生绘图之让你了解View的运动:

重力扩散.gif

1.将一个图片粒子化的方法

这里速度x方向是正负等概率随机数值,所以粒子会呈现左右运动趋势 有一定的y方向速度,但加速度aY向下,导致粒子向下运动,综合效果就是两边四散加坠落 要改变粒子的运动方式,只要改变粒子的这些参数就行了。

/**
 * 根像素初始化粒子
 *
 * @param bitmap
 * @return
 */
private void initBall(Bitmap bitmap) {
    for (int i = 0; i < bitmap.getHeight(); i++) {
        for (int j = 0; j < bitmap.getWidth(); j++) {
            Ball ball = new Ball();//产生粒子---每个粒子拥有随机的一些属性信息
            ball.x = i * d + d / 2;
            ball.y = j * d + d / 2;
            ball.vX = (float) (Math.pow(-1, Math.ceil(Math.random() * 1000)) * 20 * Math.random());
            ball.vY = rangeInt(-15, 35);
            ball.aY = 0.98f;
            ball.color = bitmap.getPixel(i, j);
            ball.born = System.currentTimeMillis();
            mBalls.add(ball);
        }
    }
}
2.更新小球
/**
 * 更新小球
 */
private void updateBall() {
    for (int i = 0; i < mBalls.size(); i++) {
        Ball ball = mBalls.get(i);
        if (System.currentTimeMillis() - mRunTime > 2000) {
            mBalls.remove(i);
        }
        ball.x += ball.vX;
        ball.y += ball.vY;
        ball.vY += ball.aY;
        ball.vX += ball.aX;
    }
}
3.初始化时间流:ValueAnimator
//初始化时间流ValueAnimator
mAnimator = ValueAnimator.ofFloat(0, 1);
mAnimator.setRepeatCount(-1);
mAnimator.setDuration(2000);
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.addUpdateListener(animation -> {
    updateBall();//更新小球位置
    invalidate();
});
4.点击开启ValueAnimator
@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mRunTime = System.currentTimeMillis();//记录点击时间
            mAnimator.start();
            break;
    }
    return true;
}

好了,本篇就到这里,你是不是更清楚Bitmap是什么了? 希望本文可以给你一点灵感,你能做出更酷炫的东西。


本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.11.18 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 零、前言
  • 一、初识
    • 1.什么是Bitmap像素级的操作:
      • 2.获取第一个像素
        • 3.获取所有点像素颜色值
        • 二、分析:
          • 1.首先看一下如何创建一个Bitmap对象
            • 2.保存bitmap成为图片到本地
              • 3.bitmap.getPixel到底说了什么?
              • 二、bitmap的花样复刻:
                • 1.bitmap复刻的粒子载体
                  • 2.初始化粒子:
                    • 3.正方形粒子的绘制(原图复刻):
                      • 4.复刻其他图片资源文件
                        • 5.圆形复刻
                          • 6.其他形状复刻
                          • 三、关于颜色操作:
                            • 1.下面是图片黑白化的算法
                              • 2.灰色处理函数
                                • 3.颜色反转
                                • 四、粒子运动
                                  • 1.将一个图片粒子化的方法
                                    • 2.更新小球
                                      • 3.初始化时间流:ValueAnimator
                                        • 4.点击开启ValueAnimator
                                        领券
                                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档