androidandroid-ndklibjpeg-turbo

How to use jpeglib-turbo with android ndk?


I am trying to use jpeglib-turbo with android ndk to get the pixel rgb values of a jpeg image, and i am new to both C++,C and android NDK, till now i've tried using the solutions provided in Examples or tutorials of using libjpeg-turbo's TurboJPEG but i am unable to fix the issue at hand, Currently using https://github.com/openstf/android-libjpeg-turbo to build libjpeg-turbo.

Current code is

#include <turbojpeg.h>
#include <jni.h>
#include <android/log.h>
#include <syslog.h>



JNIEXPORT jbyteArray
Java_com_serelay_jpegturbo_MainActivity_getImagePixelData( JNIEnv*  env,
                                jobject  this,
                                jbyteArray data)
{
long unsigned int _jpegSize=2464742; //!< _jpegSize from above
unsigned char *_compressedImage; //!< _compressedImage from above

_compressedImage = data;

int width, height, jpegSubSamp;
// int jpegSubSamp = TJSAMP_444;


tjhandle _jpegDecompressor = tjInitDecompress();

tjDecompressHeader2(_jpegDecompressor, _compressedImage, _jpegSize, &width, 
&height, &jpegSubSamp);

unsigned long len = width * height * 4;
unsigned long pitch = width * 4;
syslog(LOG_CRIT, "====================");
syslog(LOG_CRIT, "jpegSize %lu", _jpegSize);

syslog(LOG_CRIT, "width %d", width);
syslog(LOG_CRIT, "height %d", height);
syslog(LOG_CRIT, "total length %lu", len);

syslog(LOG_CRIT, "====================");



unsigned char buffer[len]; //!< will contain the decompressed image

tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, buffer, width, pitch, height, TJPF_RGBA, 0);

tjDestroy(_jpegDecompressor);


// char array to byte array 

jbyteArray jbytes = buffer;
//jbyteArray jbytes = _compressedImage;

return jbytes;
}

To call it from java i am using public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Example of a call to a native method
    TextView tv = (TextView) findViewById(R.id.sample_text);
    System.loadLibrary("twolib-second");
    InputStream inputStream= getResources().openRawResource(R.raw.image1);
    byte[] bytes;
    try {
        bytes = new byte[inputStream.available()];
        inputStream.read(bytes);

        byte[] array = getImagePixelData(bytes);
        tv.setText(String.valueOf(getImagePixelData(bytes).length));

    } catch (IOException e) {
        e.printStackTrace();
    }

//        tv.setText(getImagePixelData(5));
}

/**
 * A native method that is implemented by the 'native-lib' native library,
 * which is packaged with this application.
 */
public native byte[] getImagePixelData(byte[] x);
}

I've been successfully calling from the code from java with the help of so files generated by ndk, but the issue i am faced was exceptions in the code which said
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xeaa6cc30

on further inspection i logged values of some variables which felt a bit bad considering the image being passed is of size 3840*2160, upon logging the variables i get

03-07 20:26:05.180 13613-13613/com.serelay.jpegturbo E/com.serelay.jpegturbo: 
====================
03-07 20:26:05.180 13613-13613/com.serelay.jpegturbo E/com.serelay.jpegturbo: 
jpegSize 2464742
03-07 20:26:05.180 13613-13613/com.serelay.jpegturbo E/com.serelay.jpegturbo: 
width 4
03-07 20:26:05.180 13613-13613/com.serelay.jpegturbo E/com.serelay.jpegturbo: 
height -1386946560
03-07 20:26:05.180 13613-13613/com.serelay.jpegturbo E/com.serelay.jpegturbo: 
total length 3578658816
03-07 20:26:05.180 13613-13613/com.serelay.jpegturbo E/com.serelay.jpegturbo: 
====================

i can see the width and height values are very wrong but i can't figure out why and how to fix it. It would be great if any help or directions can be provided to fix this issue, am stuck with this since last 2 days :) thanks.


Solution

  • Changes was required in tjDecompressHeader2 and we needed to use tjDecompressHeader3 in the library version, also we need to convert jbyteArray to unsigned char * and then back to jbyteArray in order to return a byte array to java, both of them can be observed in the code below.

    #include <turbojpeg.h>
    #include <jni.h>
    #include <android/log.h>
    #include <syslog.h>
    
    
    
    JNIEXPORT jbyteArray
    Java_com_serelay_jpegturbo_MainActivity_getImagePixelData( JNIEnv*  env,
                                    jobject  this,
                                    jbyteArray data,
                                    jint dataLength)
    {
    long unsigned int _jpegSize=dataLength; //!< _jpegSize from above
    unsigned char *_compressedImage; //!< _compressedImage from above
    jboolean isCopy;
    
    _compressedImage = (unsigned char*)(*env)->GetByteArrayElements(env, data, 
    &isCopy);
    int width, height, jpegSubSamp, jpegColorSpace;
    // int jpegSubSamp = TJSAMP_444;
    
    
    tjhandle _jpegDecompressor = tjInitDecompress();
    
    tjDecompressHeader3(_jpegDecompressor, _compressedImage, _jpegSize, &width, 
    &height, &jpegSubSamp, &jpegColorSpace);
    
    long len = width * height * 4;
    long pitch = width * 4;
    syslog(LOG_CRIT, "====================");
    syslog(LOG_CRIT, "jpegSize %lu", _jpegSize);
    
    syslog(LOG_CRIT, "width %d", width);
    syslog(LOG_CRIT, "height %d", height);
    syslog(LOG_CRIT, "subsampl %d", jpegSubSamp);
    syslog(LOG_CRIT, "colorSpace %d", jpegColorSpace);
        syslog(LOG_CRIT, "len %lu", len);
        syslog(LOG_CRIT, "pitch %lu", pitch);
    
    
    syslog(LOG_CRIT, "====================");
    syslog(LOG_CRIT, "===================0");
    
    
    
    unsigned char* mumfer = (unsigned char*)malloc(len); //!< will contain the 
    decompressed image
    syslog(LOG_CRIT, "===================1");
    
    tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, mumfer, width, 
    0, height, TJPF_RGBA, TJFLAG_FASTDCT);
    syslog(LOG_CRIT, "===================2");
    
    tjDestroy(_jpegDecompressor);
    syslog(LOG_CRIT, "===================3");
    
    
    // char array to byte array 
    
    jbyteArray array = (*env)->NewByteArray(env, len);
    syslog(LOG_CRIT, "===================4");
    
    //HERE I GET THE ERROR, I HAVE BEEN TRYING WITH len/2 and WORKS , PROBABLY 
    SOME BYTS ARE GETTING LOST.
    (*env)->SetByteArrayRegion (env, array, 0, len, (jbyte*)(mumfer));
    syslog(LOG_CRIT, "===================5");
    return array;
    }
    

    Also in java we need to convert the byte[] to int[] in order to get the exact pixel color values which can be done with the help of the following function

    public static int byteToColorInt(byte b) {
        return b & 0xFF;
    }