作者VVll (信)
看板AndroidDev
标题[分享] 图片的点运算
时间Fri Jun 8 14:15:09 2012
假如想对一张图的 像素点做操作运算的话
在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
1F:推 gpc:基本上直接上opengl就可以了.. 06/08 14:16
2F:→ VVll:opengl 我不懂阿XD 06/08 15:07
3F:推 lovelycateye:不考虑一下cocos2d-x吗? 06/08 22:52