[分享] 圖片的點運算 JDK+NDK
假如想對一張圖的 像素點做操作運算的話
在JDK上的步驟 步驟如下
--附註:圖片演算法處理部分 可能不是很準確 僅供參考
沒有特別宣告出來的變數 請當成已經宣告並有設值 例如alpha
Bitmap b1 = ....//取得一張圖
for(int x=0;x<b1.getWidth();x++){
for(int y=0;y<b1.getHeight();y++){
argb = b1.getPixel(x,y);//取得原始色彩
argb = (alpha<<24) | (argb & 0x00FFFFFF);//做透明度變更
b1.setPixel(x,y, argb);//寫入新色彩
}
}
但以這述例子來說 每個點要操作3次 如果是400*300 大小的圖的話
總要要進行400*300*3 = 36萬次的運算操作
這僅只是一張圖而已 如果想做流暢的動畫圖片 那更可觀
-----------------------------------------------------------
而JDK有提供另一個方法去取得圖的像素點
int size = width * height;
int[] pixel = new int[size];//先宣告記憶體配置大小
b1.getPixels(pixel, 0, width, 0, 0, width, height);//取得像素點
for(int t=0;t<size;t++){
pixel[i] = (alpha<<24) | (pixel[i] & 0x00FFFFFF);//做透明度變更
}
b1.setPixels(pixel, 0, width, 0, 0, width, height);//將像素點寫入
以這種方法來看 確實比前例快了 因為迴圈每次只作一次操作
處理時間 = 12萬次 + getPixels + setPixels
但會發現 運算速度是瓶頸之一
因此必須藉由更快的方法去做點運算
------------------------------------------------------------
NDK 是android中 可以利用C語言去做運算處理的一個機制方法
可以解決大量運算在JAVA中緩慢的問題
以上述例子來說 我們把整個for回圈的運算部分 丟給C處理 最後再將結果回傳給JAVA
--c
JNIEXPORT jintArray compute( JNIEnv* env, jobject thiz,
jintArray ref, jint size )//從java丟了一個pixelArray跟size大小過來
{
//將array 寫入int指標中
jint *pixel = (*env)->GetIntArrayElements(env, ref, NULL);
int t;
for(t=0;t<size;t++){
pixel[i] = (alpha<<24) | (pixel[i] & 0x00FFFFFF);//做透明度變更
}
(*env)->SetIntArrayRegion(env, ref, 0, size, pixel);//寫回array
return ref;
}
--java
int size = width * height;
int[] pixel = new int[size];//先宣告記憶體配置大小
b1.getPixels(pixel, 0, width, 0, 0, width, height);//取得像素點
pixel = compute( pixel, size );//java call c then get return array
b1.setPixels(pixel, 0, width, 0, 0, width, height);//將像素點寫
如果需要做圖片運算處理的動畫 透過C去加快運算部分 會有很大的處理速度改善
----------------------------------------------------------------
但如果對這樣的速度還是很不滿意 因為如果做動畫處理
每次c算完 回到java都還要再做一次 setPixels這個操作 實在太花時間
還好ndk可以直接對bitmap做指標操作處理
--c
#include <android/bitmap.h>//必須要載入此類別
JNIEXPORT void compute( JNIEnv* env, jobject thiz,
jobject bmp ){//直接從java丟一張圖過來
jclass m_class;
jmethodID m_id;
m_class = (*env)->FindClass( env, "JavaClassName");
//取得java 類別的位置與名稱
m_id = (*env)->GetStaticMethodID( env, m_class , "update" , "()V" );
//透過此類別取得你要呼叫的java 方法
AndroidBitmapInfo info;
AndroidBitmap_getInfo(env, bmp, &info);//取得圖片資訊
int size = info.width * info.height;//透過info取得所要資訊
void* _bmp;
AndroidBitmap_lockPixels(env, bmp, &_bmp);//將圖片的記憶體參考到 宣告的指標
int* pixel = (int*)_bmp;//強制型別轉換 以便運算
int i;
for(i=0;i<size;i++){
pixel[i] = (alpha<<24) | (pixel[i] & 0x00FFFFFF);//做透明度變更
}
AndroidBitmap_unlockPixels(env, bmp);//解除參考
(*env)->CallStaticVoidMethod( env, m_class, m_id);
//事情做完了 c call java去更新畫面
}
--java
Bitmap b1 = ....//取得一張
start(){
compute(b1);
}
update(){
ImageView.setImageBitmap(b1);//透過類似方法將結果顯示在畫面上
}
-----------------------------------------------------------------
參考資料
JNI 基本觀念
http://cheng-min-i-taiwan.blogspot.com/2011/04/java-native-interface-jni.html
JNI 基本使用
http://cheng-min-i-taiwan.blogspot.com/2010/06/android-ndk-hellojni.html
c call java
http://changyy.pixnet.net/blog/post/29469121
-----------------------------------------------------------------
後記
以上是這幾個禮拜的研究心得整理 因為要在android上作2D動畫
所以必須盡可能找到越快的處理方法 但無奈即使是在JNI上
如果要做 乘法運算還是會有嚴重的處理時間
因此下一個研究方向 可能就是用opengl去算圖了
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 118.170.153.165
推
06/08 14:16, , 1F
06/08 14:16, 1F
→
06/08 15:07, , 2F
06/08 15:07, 2F
推
06/08 22:52, , 3F
06/08 22:52, 3F