Android Bitmap像素級操作詳解

一:什麼是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詳解的資料請關註WalkonNet其它相關文章!

推薦閱讀:

    None Found