[分享] 圖片的點運算 JDK+NDK

看板AndroidDev作者 (信)時間13年前 (2012/06/08 14:15), 編輯推噓2(201)
留言3則, 3人參與, 最新討論串1/1
假如想對一張圖的 像素點做操作運算的話 在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
基本上直接上opengl就可以了..
06/08 14:16, 1F

06/08 15:07, , 2F
opengl 我不懂阿XD
06/08 15:07, 2F

06/08 22:52, , 3F
不考慮一下cocos2d-x嗎?
06/08 22:52, 3F
文章代碼(AID): #1FqPXngu (AndroidDev)