Android Bitmap像素级操作详解

 更新时间:2021年4月10日 00:00  点击:1967

一:什么是Bitmap像素级的操作

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

二:载入与像素读写

在Android SDK中,图像的像素读写能够通过getPixel与setPixel两个Bitmap的API实现。

2.1 getPixel读取像素

Bitmap API读取像素的代码例如以下:

int pixel = bitmap.getPixel(col, row);// ARGB
int red = Color.red(pixel); // same as (pixel >> 16) &0xff
int green = Color.green(pixel); // same as (pixel >> 8) &0xff
int blue = Color.blue(pixel); // same as (pixel & 0xff)
int alpha = Color.alpha(pixel); // same as (pixel >>> 24)

得到像素pixel是32位的整数,四个字节分别相应透明通道、红色、绿色、蓝色通道。

getPixel参数说明:

  • col 表示的是第几行,下标从0开始。
  • row 表示的是第几列,下标从0开始。

比如获取第一个元素,也就是第一行第一列。

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

获取所有的颜色就是如下这样:

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);
     }
 }

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

2.2 setPixel写入像素

在讲写入像素前,我们先看一下如何创建一个bitmap,创建bitmap代码如下:

比如我们创建一个2*2的ARGB_8888图片:颜色分别是黑(0,0)、白(0,1)、红(1,0)、蓝(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行2列的bitmap。通过setPixel(col, row,color)方法为对应的点写入像素。

你是否会好奇,这样写入是否成功,那简单,通过上面我们知道getPixel可以读取像素点,我们验证一下。

首先,我们把创建的bitmap保存下来:

    /**
     * 保存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();
    }

然后,对保存的bitmap做像素的读取工作,代码如下:

int pixel_0_0 = bitmap.getPixel(0, 0);
LogUtils.i("getPixel","pixel_0_0  is:"+pixel_0_0)
int pixel_1_0 = bitmap.getPixel(1, 0);
LogUtils.i("getPixel","pixel_1_0 is:"+pixel_1_0 )
int pixel_0_1 = bitmap.getPixel(0, 1);
LogUtils.i("getPixel","pixel_0_1 is:"+pixel_0_1 )
int pixel_1_1 = bitmap.getPixel(1, 1);
LogUtils.i("getPixel","pixel_1_1 is:"+pixel_1_1)

你会发现控制台的输出结果如下:

pixel_0_0 is:-16777216   

pixel_1_0 is:-65536   

pixel_0_1 is:-1   

看了这些你可能比较模糊,不懂这么长一串是什么意思,都是负数,其实那就是颜色值。Color类中有几个方法可以方便获取argb分别对应的值,下面测试一下你就明白了。其实就是将int进行了位运算,分离出argb四个通道的值这里我简单转换一下:

praseColor("pixel_0_0", pixel_0_0);//黑:a:255, r:0, g:0, b:0
praseColor("pixel_1_0", pixel_1_0);//红:a:255, r:255, g:0, b:0
praseColor("pixel_0_1", pixel_0_1);//白:a:255, r:255, g:255, b:255
praseColor("pixel_1_1", pixel_1_1);//蓝:a:255, r:0, g:0, b:255
 
 
/**
 * 将颜色从int 拆分成argb,并打印出来
 * @param msg
 * @param color
 */
private void praseColor(String msg, int color) {
    int a = Color.alpha(color);
    int r = Color.red(color);
    int g = Color.green(color);
    int b = Color.blue(color);
    LogUtils.i(msg + "----a:" + a + ", r:" + r + ", g:" + g + ", b:" + b + L.l());
}

通过上面的列子,你会发现,通过setPixel(col, row)可以写入像素,当然,写入像素同时也是可以指定透明度的,代码例如以下:

bm.setPixel(col, row, Color.argb(alpha, red, green, blue));

通过Color.argb又一次组装成一个int的像素值。

使用BitmapFactory.decodeFile或者decodeResource等方法实现载入图像的Bitmap对象时。这些方法就会为要构建的Bitmap对象分配合适大小的内存。假设原始的图像文件数据非常大,就会导致DVM不能分配请求的内存大小。从而导致OOM(out of memory)问题。而通过配置BitmapFactory.Option预先读取图像高度与宽带,图像进行适当的下採样,就能够避免OOM问题的发生。预先仅仅获取图像高度与宽带的代码例如以下:

   // 获取Bitmap图像大小与类型属性
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), 
                                        R.drawable.test, options);
        int height = options.outHeight;
        int width = options.outWidth;
        String imageType = options.outMimeType;

基于下採样载入超大Bitmap图像的缩小版本号:

 // 下採样
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value 
            // that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }
        // 获取採样后的图像显示。避免OOM问题
        options.inJustDecodeBounds = false;
        srcImage = BitmapFactory.decodeResource(getResources(), R.drawable.test, options);

三:像素操作

通过上面的讲解,我们知道如何获取设写入像素颜色信息,拿到像素颜色信息,不就等于天下就是我的了吗?下面来电高级一点的像素操作。首先先讲一下android彩色图像灰度化。

3.1:android彩色图像灰度化

android彩色图像灰度化的三个简单方法:

  1. 灰度化方法一:灰度值GRAY = (max(red, green, blue) + min(red, green, blue))/2
  2. 灰度化方法二:灰度值GRAY = (red + green + blue)/3
  3. 灰度化方法三:灰度值GRAY = red0.3 + green0.59 + blue*0.11

代码实现例如以下:

public Bitmap gray(Bitmap bitmap, int schema)
{
    Bitmap bm = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), bitmap.getConfig());
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    for(int row=0; row<height; row++){
       for(int col=0; col<width; col++){
          int pixel = bitmap.getPixel(col, row);// ARGB
          int red = Color.red(pixel); // same as (pixel >> 16) &0xff
          int green = Color.green(pixel); // same as (pixel >> 8) &0xff
          int blue = Color.blue(pixel); // same as (pixel & 0xff)
          int alpha = Color.alpha(pixel); // same as (pixel >>> 24)
          int gray = 0;
          if(schema == 0)
          {
              gray = (Math.max(blue, Math.max(red, green)) + 
                          Math.min(blue, Math.min(red, green))) / 2;
          }
          else if(schema == 1)
          {
              gray = (red + green + blue) / 3;
          }
          else if(schema == 2)
          {
              gray = (int)(0.3 * red + 0.59 * green + 0.11 * blue);
          }
          bm.setPixel(col, row, Color.argb(alpha, gray, gray, gray));
       }
    }
    return bm;
}

Bitmap图像镜像映射与亮度调整的代码实现例如以下:

public Bitmap brightness(Bitmap bitmap, double depth)
{
    Bitmap bm = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), bitmap.getConfig());
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    for(int row=0; row<height; row++){
       for(int col=0; col<width; col++){
          int pixel = bitmap.getPixel(col, row);// ARGB
          int red = Color.red(pixel); // same as (pixel >> 16) &0xff
          int green = Color.green(pixel); // same as (pixel >> 8) &0xff
          int blue = Color.blue(pixel); // same as (pixel & 0xff)
          int alpha = Color.alpha(pixel); // same as (pixel >>> 24)
          double gray = (0.3 * red + 0.59 * green + 0.11 * blue);
          red += (depth * gray);
          if(red > 255) { red = 255; }

          green += (depth * gray);
          if(green > 255) { green = 255; }

          blue += (depth * gray);
          if(blue > 255) { blue = 255; }
          bm.setPixel(col, row, Color.argb(alpha, red, green, blue));
       }
    }
    return bm;
}

public Bitmap flip(Bitmap bitmap)
{
    Bitmap bm = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), bitmap.getConfig());
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    for(int row=0; row<height; row++){
       for(int col=0; col<width; col++){
          int pixel = bitmap.getPixel(col, row);// ARGB
          int red = Color.red(pixel); // same as (pixel >> 16) &0xff
          int green = Color.green(pixel); // same as (pixel >> 8) &0xff
          int blue = Color.blue(pixel); // same as (pixel & 0xff)
          int alpha = Color.alpha(pixel); // same as (pixel >>> 24)
          int ncol = width - col - 1;
          bm.setPixel(ncol, row, Color.argb(alpha, red, green, blue));
       }
    }
    return bm;
}

3.2:bitmap的花样复刻

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.初始化粒子:

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.正方形粒子的绘制(原图复刻)

@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);

/**
 * 根像素初始化粒子
 * @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;
}

5.圆形复刻

注意: 在第四步复刻其他图片资源文件时,图片已经不是bitmap了,而是由一个个小正方形堆积而成并且拥有自己的颜色。我们可以利用它实现很多有趣的定西,比如画正方形,画个圆…。现在你明白像素级操作什么了吧。也许你会感叹,还能有更好玩的吗,当然有,后面还有更惊叹的呢。

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

6.其他形状复刻
更具像素单元,你可以用任意的图形更换粒子单元,或者各种形状的粒子混合适用,画出你喜欢的形状。比如我们绘制一个五角星:

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();
}

四:颜色操作

关于颜色操作,可以使用ColorFilter和ColorMatrix改变图片颜色,也可以采用“旁门左道”方式,毕竟通过上面的操作我们已经货可以获取像素点的颜色。

那就先将ColorFilter和ColorMatrix改变图片颜色,然后再讲采用“旁门左道”方式。

4.1 使用ColorFilter和ColorMatrix改变图片颜色

1.ColorMatrix改变图片颜色
颜色矩阵M是一个5*4的矩阵,在Android中,颜色矩阵M是以一维数组m=[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t]的方式进行存储的。如下图所示:

他通过RGBA四个通道来直接操作对应颜色,达到修改图像的效果。

第一行决定红色 R

第二行决定绿色 G

第三行决定蓝色 B

第四行决定了透明度 A

第五列是颜色的偏移量

原图的RGBA的ColorMatrix颜色矩阵数组为:

[ 1, 0, 0, 0, 0,  

0, 1, 0, 0, 0,  

0, 0, 1, 0, 0, 

 0, 0, 0, 1, 0

]

在一张图片中,图像的RGBA(红色、绿色、蓝色、透明度)值决定了该图片所呈现出来的颜色效果。而图像的RGBA值则存储在一个5*1的颜色分量矩阵C中,由颜色分量矩阵C可以控制图像的颜色效果。颜色分量矩阵C如下所示:

要想改变一张图片的颜色效果,只需要改变图像的颜色分量矩阵即可。通过颜色矩阵可以很方便的修改图像的颜色分量矩阵。假设修改后的图像颜色分量矩阵为C1,则有如图3所示的颜色分量矩阵计算公式。

通常,改变颜色分量时可以通过修改第5列的颜色偏移量来实现,如下图所示的颜色矩阵M1,通过计算后可以得知该颜色矩阵的作用是使图像的红色分量和绿色分量均增加100,这样的效果就是图片泛黄(因为红色与绿色混合后得到黄色)。

除此之外,也可以通过直接对颜色值乘以某一系数而达到改变颜色分量的目的。如下图所示的颜色矩阵M2,将绿色分量放大了2倍,这样的效果就是图片泛绿色。

讲了这么多,下面直入正题,聊聊如何使用ColorMatrix改变图片颜色。

使用ColorMatrix改变图片颜色的步骤大致如下:

  • 通过Bitmap.createBitmap()方法获得一个空白的Bitmap对象。
  • 使用Bitmap对象创建画布Canvas, 然后创建画笔Paint。
  • 定义ColorMatrix,并指定RGBA矩阵。
  • 使用ColorMatrix创建一个ColorMatrixColorFilter对象, 作为画笔的滤镜 paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix))。
  • 使用Canvas.drawBitmap()方法把原图使用定义的Paint画到空白的Bitmap对象上即可获得改变RGBA值后的图像。

最后更具如上步骤附上简单的代码如下:

布局代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    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="A" />

    <SeekBar
        android:id="@+id/sb_alpha"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="255"
        android:progress="255" />

    <ImageView
        android:id="@+id/iv_color_show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:layout_gravity="center_horizontal"
        android:src="@drawable/btn_pause" />

</LinearLayout>

控制逻辑代码:

public class ColorMatrixActivity extends ActionBarActivity {

    private SeekBar sb_red, sb_green, sb_blue, sb_alpha;
    private ImageView iv_show;
    private Bitmap afterBitmap;
    private Paint paint;
    private Canvas canvas;
    private Bitmap baseBitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.colormatrix_activity);
        initView();
    }

    private void initView() {
        iv_show = (ImageView) findViewById(R.id.iv_color_show);
        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_alpha = (SeekBar) findViewById(R.id.sb_alpha);

        sb_red.setOnSeekBarChangeListener(seekBarChange);
        sb_green.setOnSeekBarChangeListener(seekBarChange);
        sb_blue.setOnSeekBarChangeListener(seekBarChange);
        sb_alpha.setOnSeekBarChangeListener(seekBarChange);

        baseBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.btn_pause);
        // 1.获取一个与baseBitmap大小一致的可编辑的空图片
        afterBitmap = Bitmap.createBitmap(baseBitmap.getWidth(),
                baseBitmap.getHeight(), baseBitmap.getConfig());
        // 2.使用Bitmap对象创建画布Canvas, 然后创建画笔Paint。
        canvas = new Canvas(afterBitmap);
        paint = new Paint();
    }

    private SeekBar.OnSeekBarChangeListener seekBarChange = new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            if(seekBar.getId() == R.id.sb_alpha){
                iv_show.getDrawable().setAlpha(sb_alpha.getProgress());

            }else{
                float progressR = sb_red.getProgress();
                float progressG = sb_green.getProgress();
                float progressB = sb_blue.getProgress();
                // 根据SeekBar定义RGBA的矩阵, 通过修改矩阵第五列颜色的偏移量改变图片的颜色
                float[] src = new float[]{
                        1, 0, 0, 0, progressR,
                        0, 1, 0, 0, progressG,
                        0, 0, 1, 0, progressB,
                        0, 0, 0, 1, 0};

                // 3.定义ColorMatrix,并指定RGBA矩阵
                ColorMatrix colorMatrix = new ColorMatrix();
                colorMatrix.set(src);
                // 4.使用ColorMatrix创建一个ColorMatrixColorFilter对象, 作为画笔的滤镜, 设置Paint的颜色
                paint.setColorFilter(new ColorMatrixColorFilter(src));
                // 5.通过指定了RGBA矩阵的Paint把原图画到空白图片上
                canvas.drawBitmap(baseBitmap, new Matrix(), paint);
                iv_show.setImageBitmap(afterBitmap);
            }
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {

        }

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) {
        }
    };
}

2.ColorFilter 改变图片颜色
ColorFilter 改变图片颜色非常简单,使用如下:

//定义选中的颜色
int checkColor = context.getResources().getColor(R.color.theme_red);

//当选中该项时,显示选中颜色,否则显示未选中颜色
viewHolder.icon.setColorFilter(selectPosition==position? checkColor :Color.TRANSPARENT);

这样的的两行代码就可以改变颜色了。下面主要介绍一下的setColorFilter的第二个参数PorterDuff.Mode。

PorterDuff,一个陌生的单词,百度翻译和谷歌翻译都查无来处,原因在于它是一个组合词汇,来源于 Tomas Proter(托马斯波特)和 Tom Duff(汤姆达)两个名字。这俩人是在图形混合方面的大神级人物,他们在 1984 年发表了论文,第一次提出了图形混合的概念,也是取了两人的名字命名。

Android PorterDuff.Mode 便是提供了图片的各种混合模式,可以分为两类:

  • Alpha compositing modes(由俩大神的定义,包含 alpha 通道因素)
  • Blending modes(不是俩大神的作品,不包含 alpha 通道因素)

具体的可以看官方文档对 PorterDuff.Mode的介绍,我这里只说涉及到的 SRC_ATOP。

既然混合,两个图片,源图片和目标图片,如下:

SRC_ATOP 混合模式效果如下图,只保留源图片和目标图片的相交部分,其他部分舍弃:

总结:

合理使用 SetColorFilter() ,可以为 UI 好搭档节省了不少切图工作量,而且,同样能缩小了 APK 的体积。

3.其他方式
简单的使用旁门左道的方式实现图片的颜色改变。

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

private void BitmapToBall(Bitmap bitmap){
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int[] colorPixels = new int[width*height];
        bitmap.getPixels(colorPixels,0,width,0,0,width,height);
        for (int i = 0; i < bitmap.getWidth(); i++) {
            for (int j = 0; j < bitmap.getHeight(); j++) {
                int pixel = colorPixels[i+width*j];
                if (pixel < 0) {//此处过滤掉其他颜色,避免全部产生粒子
                    Ball ball = new Ball();//产生粒子---每个粒子拥有随机的一些属性信息
                    ball.vX = (float) (Math.pow(-1, Math.ceil(Math.random() * 1000)) * 20 * Math.random());
                    ball.vY = rangeInt(-15, 35);
                    ball.aY = 0.98f;
                    ball.x = i ;
                    ball.y = j ;
                    ball.color = pixel;
                    ball.born = System.currentTimeMillis();
                    mBalls.add(ball);
                }
            }
        }
 
    }
 
/**
 * 根像素初始化粒子
 *
 * @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);
}

灰色处理函数

/**
 * 配凑灰颜色
 */
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);
}

颜色反转

//颜色反转
private int reverse(int a, int r, int g, int b) {
    //拼凑出新的颜色
    return Color.argb(a, 255-r, 255-g, 255-b);
}

五:粒子运动

4.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);
        }
    }
}

4.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;
    }
}

4.3初始化时间流:ValueAnimator

//初始化时间流ValueAnimator
mAnimator = ValueAnimator.ofFloat(0, 1);
mAnimator.setRepeatCount(-1);
mAnimator.setDuration(2000);
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.addUpdateListener(animation -> {
    updateBall();//更新小球位置
    invalidate();
});

4.4点击开启ValueAnimator

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mRunTime = System.currentTimeMillis();//记录点击时间
            mAnimator.start();
            break;
    }
    return true;
}

总结

好了,到了这里关于bitmap的像素级超过基本介绍完毕,但你会像素级操作后,你发做成更多炫酷有趣的东西,比如各种美颜滤镜变量算法等。

以上就是android Bitmap像素级操作详解的详细内容,更多关于android Bitmap详解的资料请关注猪先飞其它相关文章!

[!--infotagslink--]

相关文章

  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • 夜神android模拟器设置代理的方法

    夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • Android WebView加载html5页面实例教程

    如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
  • Android自定义WebView网络视频播放控件例子

    下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
  • Android用MemoryFile文件类读写进行性能优化

    java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
  • Android设置TextView竖着显示实例

    TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
  • android.os.BinderProxy cannot be cast to com解决办法

    本文章来给大家介绍关于android.os.BinderProxy cannot be cast to com解决办法,希望此文章对各位有帮助呀。 Android在绑定服务的时候出现java.lang.ClassCastExc...2016-09-20
  • Android 实现钉钉自动打卡功能

    这篇文章主要介绍了Android 实现钉钉自动打卡功能的步骤,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下...2021-03-15
  • Android 开发之布局细节对比:RTL模式

    下面我们来看一篇关于Android 开发之布局细节对比:RTL模式 ,希望这篇文章对各位同学会带来帮助,具体的细节如下介绍。 前言 讲真,好久没写博客了,2016都过了一半了,赶紧...2016-10-02
  • Android中使用SDcard进行文件的读取方法

    首先如果要在程序中使用sdcard进行存储,我们必须要在AndroidManifset.xml文件进行下面的权限设置: 在AndroidManifest.xml中加入访问SDCard的权限如下: <!--...2016-09-20
  • Android开发之PhoneGap打包及错误解决办法

    下面来给各位简单的介绍一下关于Android开发之PhoneGap打包及错误解决办法,希望碰到此类问题的同学可进入参考一下哦。 在我安装、配置好PhoneGap项目的所有依赖...2016-09-20
  • 用Intel HAXM给Android模拟器Emulator加速

    Android 模拟器 Emulator 速度真心不给力,, 现在我们来介绍使用 Intel HAXM 技术为 Android 模拟器加速,使模拟器运行度与真机比肩。 周末试玩了一下在Eclipse中使...2016-09-20
  • Android判断当前屏幕是全屏还是非全屏

    在安卓开发时我碰到一个问题就是需要实现全屏,但又需要我们来判断出用户是使用了全屏或非全屏了,下面我分别找了两段代码,大家可参考。 先来看一个android屏幕全屏实...2016-09-20
  • Android开发中布局中的onClick简单完成多控件时的监听的利与弊

    本文章来为各位介绍一篇关于Android开发中布局中的onClick简单完成多控件时的监听的利与弊的例子,希望这个例子能够帮助到各位朋友. 首先在一个控件加上这么一句:and...2016-09-20
  • Ubuntu 系统下安装Android开发环境 Android Studio 1.0 步骤

    Android Studio 是一个Android开发环境,基于IntelliJ IDEA. 类似 Eclipse ADT,Android Studio 提供了集成的 Android 开发工具用于开发和调试,可以在Linux,Mac OS X,Window...2016-09-20
  • Android实现简单用户注册案例

    这篇文章主要为大家详细介绍了Android实现简单用户注册案例,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-05-26