经过一系列的努力,我终于把一些东西搞定了,现在想让有兴趣的人知道我是如何做到的。
首先,我按照这里描述的方式构建了hello-jin演示版本
https://developer.android.com/tools/sdk/ndk/index.html
然后我创建了一个新项目,复制了jni文件夹,并将c函数的名称更改为匹配新包和类名。不要在您的包名称中使用“-”和“_”,否则会出现问题,最好只使用字母和数字。
接下来,我复制了所有的libjpeg-turbo文件和文件夹到jni目录中,并测试了ndk-build是否仍然工作。
最后,我创建了一个jni封装器,用于处理libjpg函数,如下所示
tjpegini-arm.c
#include <jni.h>
#include "turbojpeg.h"
jint JNICALL Java_com_design2112_fbmslpit_MainActivity_tjInitDecompress
(JNIEnv *env, jobject thisObj)
{
return (int)tjInitDecompress();
}
jint JNICALL Java_com_design2112_fbmslpit_MainActivity_tjDecompressHeader2
(JNIEnv *env, jobject thisObj, jint handle, jbyteArray jpegBuf, jint jpegSize)
{
jbyte *real_jpegBuf = (*env)->GetByteArrayElements(env, jpegBuf, 0);
if (!real_jpegBuf) return -1;
int width, height, jpegSubsamp;
int ret = tjDecompressHeader2((tjhandle)handle,
(unsigned char *)real_jpegBuf, (unsigned long)jpegSize, &width, &height,
&jpegSubsamp);
if(ret!=0) {
return 0;
}
return width<<16 | height;
}
void JNICALL Java_com_design2112_fbmslpit_MainActivity_tjDecompress2
(JNIEnv *env, jobject thisObj, jint handle, jbyteArray jpegBuf, jint jpegSize, jintArray dstBuf,
jint width, jint pitch, jint height, jint pixelFormat, jint flags)
{
jbyte *real_jpegBuf = (*env)->GetByteArrayElements(env, jpegBuf, 0);
if (!real_jpegBuf) return;
jint *real_dstBuf = (*env)->GetIntArrayElements(env, dstBuf, 0);
if (!real_dstBuf) return;
jsize length = (*env)->GetArrayLength(env, jpegBuf);
tjDecompress2((tjhandle)handle,
(unsigned char *)real_jpegBuf, (unsigned long)jpegSize, (unsigned char *)real_dstBuf,
width, pitch, height, pixelFormat, flags);
}
void JNICALL Java_com_design2112_fbmslpit_MainActivity_tjDestroy
(JNIEnv *env, jobject thisObj, jint handle)
{
tjDestroy((tjhandle)handle);
}
重要提示:为了使此功能正常工作,您需要将com_design2112_fbmslpit_MainActivity重命名为您的包和类
将tjpegini-arm.c添加到Android.mk makefile中,然后在jni目录下运行ndk-build
ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk obj/local/armeabi/libjpeg.so LOCAL_ARM_MODE=arm
将.so 文件复制到正确的名称和位置。
cp obj/local/armeabi/libjpeg.so ../libs/armeabi/libtjpegjni-arm.so
然后在我的MainActivity.java文件中。
public class MainActivity extends Activity {
public native int tjInitDecompress();
public native int tjDecompressHeader2(int handle, byte[] jpegBits, int jpegBitsLen);
public native void tjDecompress2(int handle, byte[] jpegBits,
int jpegBitsLen, int[] outbuffer, int width, int pitch, int height,
int pixelFormat, int flags);
public native void tjDestroy(int handle);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
File sdcard = Environment.getExternalStorageDirectory();
File file = new File(sdcard,"/Download/test.jpg");
int jpegBitsLen = (int) file.length();
byte[] jpegBits = new byte[jpegBitsLen];
DataInputStream dis;
try {
dis = new DataInputStream(new FileInputStream(file));
dis.readFully(jpegBits);
dis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.loadLibrary("tjpegjni-arm");
int jpegDec = tjInitDecompress();
int wh = tjDecompressHeader2(jpegDec, jpegBits, jpegBitsLen);
int width = wh>>16;
int height = wh&0x7fff;
int[] buffer = new int[width*height];
tjDecompress2(jpegDec, jpegBits, jpegBitsLen, buffer, width, 0, height, 2 , 0);
tjDestroy(jpegDec);
Bitmap bmp = Bitmap.createBitmap(buffer, width, height, Bitmap.Config.ARGB_8888);
}
基本上就是这样了。您可以以任何方式显示bmp。
对我来说,没有jni ndk经验,这需要我花费大量的时间去研究。如果有人发现这很有用,请给我发送一封邮件,谢谢!
更新,令人震惊的消息是,解码一个450x450的图像需要20毫秒。
内置的BitmapFactory.decodeByteArray也需要差不多同样的时间!
如果其他人尝试并得到不同的结果,请做个记录。
libjpeg-turbo
无法击败内置的Jpeg解码器。特别是对于小图像而言。这个库有什么用处呢? 用于创建大型(10兆字节及以上)的Jpeg图像;用于操作大型Jpeg图像(例如旋转和裁剪)。请注意,许多设备都具有硬件支持的Jpeg编码/解码功能,这比任何NEON优化都要快得多。再次强调,这只对大型图像有意义。 - Alex CohnTJCompressor
替换内置的Bitmap.compress(CompressFormat.JPEG)
,我的图像确实非常小(它们是大约40x40的更大图像的瓦片)。我现在每秒只能压缩约10个完整帧的瓦片(对于我正在处理的图像大约256个瓦片),因此大约需要2560次小压缩,我原本期望通过使用libjpeg-turbo
将其提高到约20-30 fps。如果这不可行,有没有更好的Android领域中的JPEG压缩建议? - Roberto Andrade