Android, как использовать библиотеку libjpeg-turbo

i, наконец, удалось создать библиотеку libjpeg-turbo static благодаря этому 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?

i закодировать поток на моем ПК, используя этот

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);

которые работают нормально, поэтому я думаю, что должен быть андроид-эквивалент?

Я прочитал это https://wiki.linaro.org/BenjaminGaignard/libjpeg-turboAndSkia означает ли это, что единственный способ использовать его - перестроить источники Android, чтобы он использовал libjpeg-turbo? я читаю где-то есть api совместимости и собственный api для libjpeg-turbo, и я рад использовать все, что api проще всего, так как я не хочу перестраивать android

Я пробовал следующее:  под моим проектом root я создал папки jni/include и ставил turbojpeg.h там  под моим корнем проекта я создал папки jni/prebuilt и поместил libjpeg.a туда

в моем java-коде, я ставлю

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, чтобы соответствовать новому пакету и имени класса. не используйте - и _ в имени вашего пакета или у вас будут проблемы. лучше всего использовать a-x0-9.

то я скопировал весь файл libipeg-turbo и dirs в jni и протестировал, что ndk-build все еще работает

тогда я создал jni-обертку для libfly funs вроде этого 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 в makefile 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 так, как хотите.

Это тоже я дерьмовая работа, чтобы понять, что у меня нет опыта jni ndk. если кто-то найдет это полезное, напишите мне пиво.

ОБНОВЛЕНИЕ, вот шокирующая новость, ее принятие 20 мс для декодирования изображения 450x450. встроенный BitmapFactory.decodeByteArray делает это примерно так же!

Если кто-то еще пытается это сделать и получает разные результаты, сделайте заметку