android - studio - ndk r13
Android cómo usar la biblioteca libjpeg-turbo (1)
finalmente logré construir biblioteca estática libjpeg-turbo gracias a este libjpeg-turbo para android ahora tengo un libjpeg.a y un libsimd.a generado por ndk-build
pero no he podido encontrar ninguna información sobre qué hacer a continuación? Estoy decodificando un jpeg desde un buffer (desde un socket) a un mapa de bits usando la construcción en BitmapFactory que funciona bien
byte[] jpgBits = new byte[jpgBitsLen];
dis.readFully(jpgBits);
Bitmap bmp = BitmapFactory.decodeByteArray(jpgBits, 0, jpgBitsLen);
¿Cómo reemplazo BitmapFactory.decodeByteArray con libjpeg-turbo?
codifico la transmisión en mi PC usando esto
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);
que funcionan bien, entonces creo que debe haber un equivalente de Android?
Leí esto https://wiki.linaro.org/BenjaminGaignard/libjpeg-turboAndSkia ¿Esto significa que la única forma de usarlo es reconstruir las fuentes de Android para que use libjpeg-turbo? He leído en alguna parte que hay una API de compatibilidad y una API nativa para libjpeg-turbo y estoy contento de usar cualquier API que sea más fácil ya que no me gusta reconstruir Android.
He intentado lo siguiente: bajo la raíz de mi proyecto, creé las carpetas jni / include y puse turbojpeg.h allí bajo la raíz del proyecto, creé las carpetas jni / prebuilt y puse libjpeg.a ahí
en mi código de Java, puse
private native int tjInitDecompress();
en MainActivity y en onCreate agrego
int i = tjInitDecompress();
Log.d("MainActivity", "i="+i);
se construye y se ejecuta, pero se bloquea en tjInitDecompress
en el registro dice: No se encontró ninguna implementación para Lcom / example.jpegtest / MainActivity nativa; .tjInitDecompress () I
Gracias
Bueno, ha sido una montaña de trabajo, pero finalmente conseguí algo que funciona, así que quiero que cualquiera que esté interesado sepa cómo lo hice.
Primero construí la demostración de hello-jin como se describe aquí https://developer.android.com/tools/sdk/ndk/index.html
luego creé un nuevo proyecto, copié el jni y cambié los nombres del c func para que coincida con el nuevo paquete y el nombre de clase. no use - y _ en el nombre de su paquete o tendrá problemas. solo a-x0-9 es lo mejor.
luego copié todos los archivos libjpeg-turbo y dirs en jni y probé que ndk-build aún funcionaba
luego creé un jni wrapper para las diversiones libjpg como este 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);
}
IMPORTANTE, tendrá que cambiar el nombre de com_design2112_fbmslpit_MainActivity a su paquete y clase para que esto funcione
agregue tjpegini-arm.c al archivo make de Android.mk luego ejecute ndk-build en el jni dir
ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk obj/local/armeabi/libjpeg.so LOCAL_ARM_MODE=arm
y copie el .so al nombre y lugar correctos
cp obj/local/armeabi/libjpeg.so ../libs/armeabi/libtjpegjni-arm.so
luego en mi 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);
}
eso es básicamente eso. puede mostrar el bmp de la manera que desee.
esto también me da una gran cantidad de trabajo para descubrir que no tengo ninguna experiencia jni ndk en absoluto. si alguien encuentra esto útil, envíeme una cerveza por correo electrónico.
ACTUALIZACIÓN, aquí están las noticias impactantes, está tomando 20ms para decodificar una imagen de 450x450. el construido en BitmapFactory.decodeByteArray lo hace en casi lo mismo!
si alguien más lo intenta y obtiene resultados diferentes, por favor, tome nota