Android Bitmap像素級(jí)操作詳解
一:什么是Bitmap像素級(jí)的操作
相信大家都知道一張jpg或png放大后會(huì)是一個(gè)個(gè)小格子,稱為一個(gè)像素(px),而且一個(gè)小格子是一種顏色,也就是一張jpg或png圖片就是很多顏色的合集,而這些合集信息都被封裝到了Bitmap類中。你可以使用Bitmap獲取任意像素點(diǎn),并修改它,對(duì)與某像素點(diǎn)而言,顏色信息是其主要的部分。所以像素級(jí)操作就是對(duì)一個(gè)個(gè)點(diǎn)的顏色超過。
二:載入與像素讀寫
在Android SDK中,圖像的像素讀寫能夠通過getPixel與setPixel兩個(gè)Bitmap的API實(shí)現(xiàn)。
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位的整數(shù),四個(gè)字節(jié)分別相應(yīng)透明通道、紅色、綠色、藍(lán)色通道。
getPixel參數(shù)說(shuō)明:
- col 表示的是第幾行,下標(biāo)從0開始。
- row 表示的是第幾列,下標(biāo)從0開始。
比如獲取第一個(gè)元素,也就是第一行第一列。
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.iv_test); int color_0_0 = bitmap.getPixel(0, 0);//獲取第1行,第1個(gè)像素顏色
獲取所有的顏色就是如下這樣:
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代表列數(shù),j代表行數(shù),mColArr[i][j]代表是一個(gè)圖片第i列,第j行的像素顏色值。
2.2 setPixel寫入像素
在講寫入像素前,我們先看一下如何創(chuàng)建一個(gè)bitmap,創(chuàng)建bitmap代碼如下:
比如我們創(chuàng)建一個(gè)2*2的ARGB_8888圖片:顏色分別是黑(0,0)、白(0,1)、紅(1,0)、藍(lán)(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);
如此,我們就創(chuàng)建了一個(gè)2行2列的bitmap。通過setPixel(col, row,color)方法為對(duì)應(yīng)的點(diǎn)寫入像素。
你是否會(huì)好奇,這樣寫入是否成功,那簡(jiǎn)單,通過上面我們知道getPixel可以讀取像素點(diǎn),我們驗(yàn)證一下。
首先,我們把創(chuàng)建的bitmap保存下來(lái):
/**
* 保存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();
}
然后,對(duì)保存的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)
你會(huì)發(fā)現(xiàn)控制臺(tái)的輸出結(jié)果如下:
pixel_0_0 is:-16777216
pixel_1_0 is:-65536
pixel_0_1 is:-1
看了這些你可能比較模糊,不懂這么長(zhǎng)一串是什么意思,都是負(fù)數(shù),其實(shí)那就是顏色值。Color類中有幾個(gè)方法可以方便獲取argb分別對(duì)應(yīng)的值,下面測(cè)試一下你就明白了。其實(shí)就是將int進(jìn)行了位運(yùn)算,分離出argb四個(gè)通道的值這里我簡(jiǎn)單轉(zhuǎn)換一下:
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);//藍(lán):a:255, r:0, g:0, b:255
/**
* 將顏色從int 拆分成argb,并打印出來(lái)
* @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());
}
通過上面的列子,你會(huì)發(fā)現(xiàn),通過setPixel(col, row)可以寫入像素,當(dāng)然,寫入像素同時(shí)也是可以指定透明度的,代碼例如以下:
bm.setPixel(col, row, Color.argb(alpha, red, green, blue));
通過Color.argb又一次組裝成一個(gè)int的像素值。
使用BitmapFactory.decodeFile或者decodeResource等方法實(shí)現(xiàn)載入圖像的Bitmap對(duì)象時(shí)。這些方法就會(huì)為要構(gòu)建的Bitmap對(duì)象分配合適大小的內(nèi)存。假設(shè)原始的圖像文件數(shù)據(jù)非常大,就會(huì)導(dǎo)致DVM不能分配請(qǐng)求的內(nèi)存大小。從而導(dǎo)致OOM(out of memory)問題。而通過配置BitmapFactory.Option預(yù)先讀取圖像高度與寬帶,圖像進(jìn)行適當(dāng)?shù)南聮?cǎi)樣,就能夠避免OOM問題的發(fā)生。預(yù)先僅僅獲取圖像高度與寬帶的代碼例如以下:
// 獲取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;
基于下採(cǎi)樣載入超大Bitmap圖像的縮小版本號(hào):
// 下採(cǎi)樣
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;
}
}
// 獲取採(cǎi)樣后的圖像顯示。避免OOM問題
options.inJustDecodeBounds = false;
srcImage = BitmapFactory.decodeResource(getResources(), R.drawable.test, options);
三:像素操作
通過上面的講解,我們知道如何獲取設(shè)寫入像素顏色信息,拿到像素顏色信息,不就等于天下就是我的了嗎?下面來(lái)電高級(jí)一點(diǎn)的像素操作。首先先講一下android彩色圖像灰度化。
3.1:android彩色圖像灰度化
android彩色圖像灰度化的三個(gè)簡(jiǎn)單方法:
- 灰度化方法一:灰度值GRAY = (max(red, green, blue) + min(red, green, blue))/2
- 灰度化方法二:灰度值GRAY = (red + green + blue)/3
- 灰度化方法三:灰度值GRAY = red0.3 + green0.59 + blue*0.11
代碼實(shí)現(xiàn)例如以下:
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圖像鏡像映射與亮度調(diào)整的代碼實(shí)現(xiàn)例如以下:
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的花樣復(fù)刻
bitmap的花樣復(fù)刻大致分如下幾步實(shí)現(xiàn):
1.bitmap復(fù)刻的粒子載體
public class Ball implements Cloneable {
public float aX;//加速度
public float aY;//加速度Y
public float vX;//速度X
public float vY;//速度Y
public float x;//點(diǎn)位X
public float y;//點(diǎn)位Y
public int color;//顏色
public float r;//半徑
public long born;//誕生時(shí)間
public Ball clone() {
Ball clone = null;
try {
clone = (Ball) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
2.初始化粒子:
private int d = 50;//復(fù)刻的像素邊長(zhǎng)
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.正方形粒子的繪制(原圖復(fù)刻)
@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.復(fù)刻其他圖片資源文件
只要是bitmap就行了,我們可以decode圖片變成Bitmap。注意:此處只是演示,畫一張bitmap還是用原生的好,像素級(jí)的粒子化也有它的優(yōu)勢(shì),比如運(yùn)動(dòng)。具體更具實(shí)際情況而定。
//加載圖片數(shù)組
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.圓形復(fù)刻
注意: 在第四步復(fù)刻其他圖片資源文件時(shí),圖片已經(jīng)不是bitmap了,而是由一個(gè)個(gè)小正方形堆積而成并且擁有自己的顏色。我們可以利用它實(shí)現(xiàn)很多有趣的定西,比如畫正方形,畫個(gè)圓…?,F(xiàn)在你明白像素級(jí)操作什么了吧。也許你會(huì)感嘆,還能有更好玩的嗎,當(dāng)然有,后面還有更驚嘆的呢。
for (Ball ball : mBalls) {
mPaint.setColor(ball.color);
canvas.drawCircle(ball.x, ball.y, d/2, mPaint);
}
6.其他形狀復(fù)刻
更具像素單元,你可以用任意的圖形更換粒子單元,或者各種形狀的粒子混合適用,畫出你喜歡的形狀。比如我們繪制一個(gè)五角星:
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();
}
四:顏色操作
關(guān)于顏色操作,可以使用ColorFilter和ColorMatrix改變圖片顏色,也可以采用“旁門左道”方式,畢竟通過上面的操作我們已經(jīng)貨可以獲取像素點(diǎn)的顏色。
那就先將ColorFilter和ColorMatrix改變圖片顏色,然后再講采用“旁門左道”方式。
4.1 使用ColorFilter和ColorMatrix改變圖片顏色
1.ColorMatrix改變圖片顏色
顏色矩陣M是一個(gè)5*4的矩陣,在Android中,顏色矩陣M是以一維數(shù)組m=[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t]的方式進(jìn)行存儲(chǔ)的。如下圖所示:

他通過RGBA四個(gè)通道來(lái)直接操作對(duì)應(yīng)顏色,達(dá)到修改圖像的效果。
第一行決定紅色 R
第二行決定綠色 G
第三行決定藍(lán)色 B
第四行決定了透明度 A
第五列是顏色的偏移量
原圖的RGBA的ColorMatrix顏色矩陣數(shù)組為:
[ 1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0
]
在一張圖片中,圖像的RGBA(紅色、綠色、藍(lán)色、透明度)值決定了該圖片所呈現(xiàn)出來(lái)的顏色效果。而圖像的RGBA值則存儲(chǔ)在一個(gè)5*1的顏色分量矩陣C中,由顏色分量矩陣C可以控制圖像的顏色效果。顏色分量矩陣C如下所示:

要想改變一張圖片的顏色效果,只需要改變圖像的顏色分量矩陣即可。通過顏色矩陣可以很方便的修改圖像的顏色分量矩陣。假設(shè)修改后的圖像顏色分量矩陣為C1,則有如圖3所示的顏色分量矩陣計(jì)算公式。

通常,改變顏色分量時(shí)可以通過修改第5列的顏色偏移量來(lái)實(shí)現(xiàn),如下圖所示的顏色矩陣M1,通過計(jì)算后可以得知該顏色矩陣的作用是使圖像的紅色分量和綠色分量均增加100,這樣的效果就是圖片泛黃(因?yàn)榧t色與綠色混合后得到黃色)。

除此之外,也可以通過直接對(duì)顏色值乘以某一系數(shù)而達(dá)到改變顏色分量的目的。如下圖所示的顏色矩陣M2,將綠色分量放大了2倍,這樣的效果就是圖片泛綠色。

講了這么多,下面直入正題,聊聊如何使用ColorMatrix改變圖片顏色。
使用ColorMatrix改變圖片顏色的步驟大致如下:
- 通過Bitmap.createBitmap()方法獲得一個(gè)空白的Bitmap對(duì)象。
- 使用Bitmap對(duì)象創(chuàng)建畫布Canvas, 然后創(chuàng)建畫筆Paint。
- 定義ColorMatrix,并指定RGBA矩陣。
- 使用ColorMatrix創(chuàng)建一個(gè)ColorMatrixColorFilter對(duì)象, 作為畫筆的濾鏡 paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix))。
- 使用Canvas.drawBitmap()方法把原圖使用定義的Paint畫到空白的Bitmap對(duì)象上即可獲得改變RGBA值后的圖像。
最后更具如上步驟附上簡(jiǎn)單的代碼如下:
布局代碼:
<?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.獲取一個(gè)與baseBitmap大小一致的可編輯的空?qǐng)D片
afterBitmap = Bitmap.createBitmap(baseBitmap.getWidth(),
baseBitmap.getHeight(), baseBitmap.getConfig());
// 2.使用Bitmap對(duì)象創(chuàng)建畫布Canvas, 然后創(chuàng)建畫筆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();
// 根據(jù)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創(chuàng)建一個(gè)ColorMatrixColorFilter對(duì)象, 作為畫筆的濾鏡, 設(shè)置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 改變圖片顏色非常簡(jiǎn)單,使用如下:
//定義選中的顏色 int checkColor = context.getResources().getColor(R.color.theme_red); //當(dāng)選中該項(xiàng)時(shí),顯示選中顏色,否則顯示未選中顏色 viewHolder.icon.setColorFilter(selectPosition==position? checkColor :Color.TRANSPARENT);
這樣的的兩行代碼就可以改變顏色了。下面主要介紹一下的setColorFilter的第二個(gè)參數(shù)PorterDuff.Mode。
PorterDuff,一個(gè)陌生的單詞,百度翻譯和谷歌翻譯都查無(wú)來(lái)處,原因在于它是一個(gè)組合詞匯,來(lái)源于 Tomas Proter(托馬斯波特)和 Tom Duff(湯姆達(dá))兩個(gè)名字。這倆人是在圖形混合方面的大神級(jí)人物,他們?cè)?1984 年發(fā)表了論文,第一次提出了圖形混合的概念,也是取了兩人的名字命名。
Android PorterDuff.Mode 便是提供了圖片的各種混合模式,可以分為兩類:
- Alpha compositing modes(由倆大神的定義,包含 alpha 通道因素)
- Blending modes(不是倆大神的作品,不包含 alpha 通道因素)
具體的可以看官方文檔對(duì) PorterDuff.Mode的介紹,我這里只說(shuō)涉及到的 SRC_ATOP。
既然混合,兩個(gè)圖片,源圖片和目標(biāo)圖片,如下:

SRC_ATOP 混合模式效果如下圖,只保留源圖片和目標(biāo)圖片的相交部分,其他部分舍棄:

總結(jié):
合理使用 SetColorFilter() ,可以為 UI 好搭檔節(jié)省了不少切圖工作量,而且,同樣能縮小了 APK 的體積。
3.其他方式
簡(jiǎn)單的使用旁門左道的方式實(shí)現(xiàn)圖片的顏色改變。
圖片黑白化的算法
以前在JavaScript用過,在Python用過,現(xiàn)在終于用到j(luò)ava上了,不免感慨,語(yǔ)言無(wú)界限,真理永恒。就像1+1=2是永遠(yuǎn)不變的。所以大家最好收集一下相關(guān)的真理,不管何時(shí)都不會(huì)過時(shí),也不會(huì)錯(cuò)誤。
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) {//此處過濾掉其他顏色,避免全部產(chǎn)生粒子
Ball ball = new Ball();//產(chǎn)生粒子---每個(gè)粒子擁有隨機(jī)的一些屬性信息
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;
//獲取像素點(diǎn)的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);
}
灰色處理函數(shù)
/**
* 配湊灰顏色
*/
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);
}
顏色反轉(zhuǎn)
//顏色反轉(zhuǎn)
private int reverse(int a, int r, int g, int b) {
//拼湊出新的顏色
return Color.argb(a, 255-r, 255-g, 255-b);
}
五:粒子運(yùn)動(dòng)
4.1 將一個(gè)圖片粒子化的方法
這里速度x方向是正負(fù)等概率隨機(jī)數(shù)值,所以粒子會(huì)呈現(xiàn)左右運(yùn)動(dòng)趨勢(shì)。有一定的y方向速度,但加速度aY向下,導(dǎo)致粒子向下運(yùn)動(dòng),綜合效果就是兩邊四散加墜落。要改變粒子的運(yùn)動(dòng)方式,只要改變粒子的這些參數(shù)就行了。
/**
* 根像素初始化粒子
*
* @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();//產(chǎn)生粒子---每個(gè)粒子擁有隨機(jī)的一些屬性信息
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初始化時(shí)間流:ValueAnimator
//初始化時(shí)間流ValueAnimator
mAnimator = ValueAnimator.ofFloat(0, 1);
mAnimator.setRepeatCount(-1);
mAnimator.setDuration(2000);
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.addUpdateListener(animation -> {
updateBall();//更新小球位置
invalidate();
});
4.4點(diǎn)擊開啟ValueAnimator
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mRunTime = System.currentTimeMillis();//記錄點(diǎn)擊時(shí)間
mAnimator.start();
break;
}
return true;
}
總結(jié)
好了,到了這里關(guān)于bitmap的像素級(jí)超過基本介紹完畢,但你會(huì)像素級(jí)操作后,你發(fā)做成更多炫酷有趣的東西,比如各種美顏濾鏡變量算法等。
以上就是android Bitmap像素級(jí)操作詳解的詳細(xì)內(nèi)容,更多關(guān)于android Bitmap詳解的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 詳解Android Bitmap的使用
- android獲取圖片尺寸的兩種方式及bitmap的縮放操作
- Android 實(shí)現(xiàn)把bitmap圖片的某一部分的顏色改成其他顏色
- Android 實(shí)現(xiàn)將Bitmap 保存到本地
- Android中的Bitmap序列化失敗的解決方法
- Android BitmapUtils工具類使用詳解
- Android Bitmap的加載與緩存
- 詳解Android Bitmap的常用壓縮方式
- Android中的Bitmap的詳細(xì)介紹
- Android圖片處理工具類BitmapUtils
- Android開發(fā)中Bitmap高效加載使用詳解
- Android中Glide獲取圖片Path、Bitmap用法詳解
相關(guān)文章
詳解Android ViewPager2中的緩存和復(fù)用機(jī)制
最近接觸到豎向整頁(yè)滑動(dòng)的需求,發(fā)現(xiàn)了viewpager2,viewpager2支持fragment,保留了viewpager的特性,下面這篇文章主要給大家介紹了關(guān)于ViewPager2中的緩存和復(fù)用機(jī)制的相關(guān)資料,需要的朋友可以參考下2021-11-11
XListView實(shí)現(xiàn)下拉刷新和上拉加載原理解析
這篇文章主要為大家解析了XListView實(shí)現(xiàn)下拉刷新和上拉加載原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android的Launcher啟動(dòng)器中添加快捷方式及小部件實(shí)例
這篇文章主要介紹了在Android的Launcher啟動(dòng)器中添加快捷方式及窗口小部件的方法,包括在自己的應(yīng)用程序中添加窗口小部件AppWidget的例子,需要的朋友可以參考下2016-02-02
如何給Flutter界面切換實(shí)現(xiàn)點(diǎn)特效
這篇文章主要給大家介紹了關(guān)于如何給Flutter界面切換實(shí)現(xiàn)點(diǎn)特效的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Flutter具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
Android開發(fā)之ListView、GridView 詳解及示例代碼
本文主要介紹Android開發(fā)之ListView、GridView,這里整理了相關(guān)資料及簡(jiǎn)單示例代碼,幫助大家學(xué)習(xí)參考,有需要的小伙伴可以參考下2016-08-08
Android自定義View實(shí)現(xiàn)圓弧進(jìn)度的效果
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)圓弧進(jìn)度的效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01
android廣角相機(jī)畸變校正算法和實(shí)現(xiàn)示例
今天小編就為大家分享一篇android廣角相機(jī)畸變校正算法和實(shí)現(xiàn)示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2018-08-08
XListView實(shí)現(xiàn)網(wǎng)絡(luò)加載圖片和下拉刷新
這篇文章主要為大家詳細(xì)介紹了XListView實(shí)現(xiàn)網(wǎng)絡(luò)加載圖片和下拉刷新,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11
Android 指紋識(shí)別詳解及實(shí)現(xiàn)方法
本文主要介紹Android 指紋識(shí)別的知識(shí),這里整理了詳細(xì)的資料和簡(jiǎn)單實(shí)現(xiàn)代碼,有開發(fā)這部分的朋友可以參考下2016-09-09

