Android как использовать библиотеку libjpeg-turbo
Наконец -то мне удалось собрать статическую библиотеку libjpeg-turbo благодаря этому libjpeg-turbo для Android теперь у меня есть libjpeg.a и libsimd.a, сгенерированные ndk-build
но я не смог найти информацию о том, что делать дальше? Я декодирую JPEG из буфера (из сокета) в растровое изображение, используя сборку в BitmapFactory, которая прекрасно работает
byte[] jpgBits = new byte[jpgBitsLen];
dis.readFully(jpgBits);
Bitmap bmp = BitmapFactory.decodeByteArray(jpgBits, 0, jpgBitsLen);
как заменить BitmapFactory.decodeByteArray на libjpeg-turbo?
я кодирую поток на моем ПК, используя этот
tjhandle rmfdJpegCompressor = tjInitCompress();
tjCompress2(rmfdJpegCompressor, (unsigned char *)s_rmfdPixels, MFD_WH, 0, MFD_WH, TJPF_BGRX,
&rmfdBits, &rmfdBitsLen, TJSAMP_420, RMFD_JPEG_QUALITY,
0);
tjDestroy(rmfdJpegCompressor);
которая работает нормально, так что я думаю, что должен быть эквивалент Android?
Я прочитал это https://wiki.linaro.org/BenjaminGaignard/libjpeg-turboAndSkia Означает ли это, что единственный способ использовать его - это восстановить исходные коды Android, чтобы он использовал libjpeg-turbo? я где-то читал, что есть совместимость API и нативный API для libjpeg-turbo, и я рад использовать любой API, который проще всего, так как мне не нравится перестраивать Android
я попробовал следующее: под моим проектом root я создал папки jni / include и поместил туда turbojpeg.h под моим проектом root я создал папки jni / prebuilt и поместил туда libjpeg.a
в моем коде я положил
private native int tjInitDecompress();
в MainActivity и в onCreate я добавляю
int i = tjInitDecompress();
Log.d("MainActivity", "i="+i);
он собирается и работает, но вылетает в tjInitDecompress
в журнале написано: не найдена реализация для собственного Lcom/example.jpegtest/MainActivity;.tjInitDecompress ()I
Спасибо
1 ответ
Ну, это была гора работы, но я наконец-то получил кое-что работающее, поэтому я хочу, чтобы любой, кто заинтересовался, знал, как я это сделал.
сначала я создал демо hello-jin, как описано здесь https://developer.android.com/tools/sdk/ndk/index.html
Затем я создал новый проект, скопировал jni и изменил имена функций c, чтобы они соответствовали новому пакету и имени класса. не используйте - и _ в имени вашего пакета, иначе у вас будут проблемы. просто а-х0-9 лучше.
Затем я скопировал все файлы libjpeg-turbo и dirs в jni и проверил, что ndk-build все еще работает
затем я создал jni-оболочку для веселья libjpg, как этот tjpegini-arm.c
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <jni.h>
#include "turbojpeg.h"
/*
* Class: libjpegturbo_jniapi
* Method: tjInitDecompress
* Signature: ()I
*/
//package com.design2112.fbmslpit
//public class MainActivity
jint JNICALL Java_com_design2112_fbmslpit_MainActivity_tjInitDecompress
(JNIEnv *env, jobject thisObj)
{
return (int)tjInitDecompress();
}
/*
* Class: libjpegturbo_jniapi
* Method: tjDecompressHeader2
* Signature: (I[BI)I
*/
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;
//jsize length = (*env)->GetArrayLength(env, real_jpegBuf);
/*for (i = 0; i < length; i++) {
sum += inCArray[i];
}*/
int width, height, jpegSubsamp;
int ret = tjDecompressHeader2((tjhandle)handle,
(unsigned char *)real_jpegBuf, (unsigned long)jpegSize, &width, &height,
&jpegSubsamp);
if(ret!=0) {
return 0;
}
// ok, so pack width and height together
return width<<16 | height;
}
/*
* Class: libjpegturbo_jniapi
* Method: tjDecompress2
* Signature: (I[BI[IIIIII)V
*/
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);
}
/*
* Class: libjpegturbo_jniapi
* Method: tjDestroy
* Signature: (I)V
*/
void JNICALL Java_com_design2112_fbmslpit_MainActivity_tjDestroy
(JNIEnv *env, jobject thisObj, jint handle)
{
tjDestroy((tjhandle)handle);
}
ВАЖНО, вам придется переименовать com_design2112_fbmslpit_MainActivity в ваш пакет и класс, чтобы это работало
добавьте tjpegini-arm.c в make-файл Android.mk, затем запустите ndk-build в директории jni
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
тогда в моем MainAvtivity.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();
//Get the text file
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) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
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/*pitch*/, height, 2 /*TJPF_RGBX*/, 0);
tjDestroy(jpegDec);
Bitmap bmp = Bitmap.createBitmap(buffer, width, height, Bitmap.Config.ARGB_8888);
}
это в основном это. Вы можете отобразить BMP, как вы хотите.
Это слишком много работы, чтобы понять, что у меня нет никакого опыта. если кто-то найдет это полезным, напишите мне пиво.
ОБНОВЛЕНИЕ, вот шокирующая новость, для декодирования изображения 450x450 требуется 20 мс. встроенный BitmapFactory.decodeByteArray делает это примерно так же!
если кто-то попробует это и получит другие результаты, пожалуйста, запишите