3

I am using Bitmap Operation native library in android studio but compile verry well but runtime throw Linker command fail with exit-code 1(use -v to see invocation)

I am put following the c code.

#include <jni.h>
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <android/bitmap.h>
#include <cstring>
#include <unistd.h>

#define  LOG_TAG    "DEBUG"
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

extern "C"
{
//store
JNIEXPORT jobject JNICALL Java_photoeditor_com_myapplication_MainActivity_jniStoreBitmapData(JNIEnv * env, jobject obj, jobject bitmap);
//get
JNIEXPORT jobject JNICALL Java_photoeditor_com_myapplication_MainActivity_jniGetBitmapFromStoredBitmapData(JNIEnv * env, jobject obj, jobject handle);
//free
JNIEXPORT void JNICALL Java_photoeditor_com_myapplication_MainActivity_jniFreeBitmapData(JNIEnv * env, jobject obj, jobject handle);
//rotate 90 degrees CCW
JNIEXPORT void JNICALL Java_photoeditor_com_myapplication_MainActivity_jniRotateBitmapCcw90(JNIEnv * env, jobject obj, jobject handle);
//rotate 90 degrees CW
JNIEXPORT void JNICALL Java_photoeditor_com_myapplication_MainActivity_jniRotateBitmapCw90(JNIEnv * env, jobject obj, jobject handle);
//crop
JNIEXPORT void JNICALL Java_photoeditor_com_myapplication_MainActivity_jniCropBitmap(JNIEnv * env, jobject obj, jobject handle, uint32_t left, uint32_t top, uint32_t right, uint32_t bottom);
//scale using nearest neighbor
JNIEXPORT void JNICALL Java_photoeditor_com_myapplication_MainActivity_jniScaleNNBitmap(JNIEnv * env, jobject obj, jobject handle, uint32_t newWidth, uint32_t newHeight);
}

class JniBitmap
{
public:
    uint32_t* _storedBitmapPixels;
    AndroidBitmapInfo _bitmapInfo;
    JniBitmap()
    {
        _storedBitmapPixels = NULL;
    }
};

/**crops the bitmap within to be smaller. note that no validations are done*/ //
JNIEXPORT void JNICALL Java_photoeditor_com_myapplication_MainActivity_jniCropBitmap(JNIEnv * env, jobject obj, jobject handle, uint32_t left, uint32_t top, uint32_t right, uint32_t bottom)
{
    JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
    if (jniBitmap->_storedBitmapPixels == NULL)
        return;
    uint32_t* previousData = jniBitmap->_storedBitmapPixels;
    uint32_t oldWidth = jniBitmap->_bitmapInfo.width;
    uint32_t newWidth = right - left, newHeight = bottom - top;
    uint32_t* newBitmapPixels = new uint32_t[newWidth * newHeight];
    uint32_t* whereToGet = previousData + left + top * oldWidth;
    uint32_t* whereToPut = newBitmapPixels;
    for (int y = top; y < bottom; ++y)
    {
        memcpy(whereToPut, whereToGet, sizeof(uint32_t) * newWidth);
        whereToGet += oldWidth;
        whereToPut += newWidth;
    }
    //done copying , so replace old data with new one
    delete[] previousData;
    jniBitmap->_storedBitmapPixels = newBitmapPixels;
    jniBitmap->_bitmapInfo.width = newWidth;
    jniBitmap->_bitmapInfo.height = newHeight;
}

/**rotates the inner bitmap data by 90 degress counter clock wise*/ //
JNIEXPORT void JNICALL Java_photoeditor_com_myapplication_MainActivity_jniRotateBitmapCcw90(JNIEnv * env, jobject obj, jobject handle)
{
    JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
    if (jniBitmap->_storedBitmapPixels == NULL)
        return;
    uint32_t* previousData = jniBitmap->_storedBitmapPixels;
    AndroidBitmapInfo bitmapInfo = jniBitmap->_bitmapInfo;
    uint32_t* newBitmapPixels = new uint32_t[bitmapInfo.height * bitmapInfo.width];
    int whereToGet = 0;
    // XY. ... ... ..X
    // ...>Y..>...>..Y
    // ... X.. .YX ...
    for (int x = 0; x < bitmapInfo.width; ++x)
        for (int y = bitmapInfo.height - 1; y >= 0; --y)
        {
            //take from each row (up to bottom), from left to right
            uint32_t pixel = previousData[whereToGet++];
            newBitmapPixels[bitmapInfo.width * y + x] = pixel;
        }
    delete[] previousData;
    jniBitmap->_storedBitmapPixels = newBitmapPixels;
    uint32_t temp = bitmapInfo.width;
    bitmapInfo.width = bitmapInfo.height;
    bitmapInfo.height = temp;
}

JNIEXPORT void JNICALL Java_photoeditor_com_myapplication_MainActivity_jniRotateBitmapCw90(JNIEnv * env, jobject obj, jobject handle)
{
    JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
    if (jniBitmap->_storedBitmapPixels == NULL)
        return;
    uint32_t* previousData = jniBitmap->_storedBitmapPixels;
    AndroidBitmapInfo bitmapInfo = jniBitmap->_bitmapInfo;
    uint32_t* newBitmapPixels = new uint32_t[bitmapInfo.height * bitmapInfo.width];
    int whereToGet = 0;
    // XY. ..X ... ...
    // ...>..Y>...>Y..
    // ... ... .YX X..
    for (int x = bitmapInfo.width - 1; x >= 0; --x)
        for (int y = 0; y < bitmapInfo.height; ++y)
        {
            //take from each row (up to bottom), from left to right
            uint32_t pixel = previousData[whereToGet++];
            newBitmapPixels[bitmapInfo.width * y + x] = pixel;
        }
    delete[] previousData;
    jniBitmap->_storedBitmapPixels = newBitmapPixels;
    uint32_t temp = bitmapInfo.width;
    bitmapInfo.width = bitmapInfo.height;
    bitmapInfo.height = temp;
}

/**free bitmap*/  //
JNIEXPORT void JNICALL Java_photoeditor_com_myapplication_MainActivity_jniFreeBitmapData(JNIEnv * env, jobject obj, jobject handle)
{
    JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
    if (jniBitmap->_storedBitmapPixels == NULL)
        return;
    delete[] jniBitmap->_storedBitmapPixels;
    jniBitmap->_storedBitmapPixels = NULL;
    delete jniBitmap;
}

/**restore java bitmap (from JNI data)*/  //
JNIEXPORT jobject JNICALL Java_photoeditor_com_myapplication_MainActivity_jniGetBitmapFromStoredBitmapData(JNIEnv * env, jobject obj, jobject handle)
{
    JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
    if (jniBitmap->_storedBitmapPixels == NULL)
    {
        LOGD("no bitmap data was stored. returning null...");
        return NULL;
    }
    //
    //creating a new bitmap to put the pixels into it - using Bitmap Bitmap.createBitmap (int width, int height, Bitmap.Config config) :
    //
    //LOGD("creating new bitmap...");
    jclass bitmapCls = env->FindClass("android/graphics/Bitmap");
    jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
    jstring configName = env->NewStringUTF("ARGB_8888");
    jclass bitmapConfigClass = env->FindClass("android/graphics/Bitmap$Config");
    jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(bitmapConfigClass, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;");
    jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass, valueOfBitmapConfigFunction, configName);
    jobject newBitmap = env->CallStaticObjectMethod(bitmapCls, createBitmapFunction, jniBitmap->_bitmapInfo.width, jniBitmap->_bitmapInfo.height, bitmapConfig);
    //
    // putting the pixels into the new bitmap:
    //
    int ret;
    void* bitmapPixels;
    if ((ret = AndroidBitmap_lockPixels(env, newBitmap, &bitmapPixels)) < 0)
    {
        LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
        return NULL;
    }
    uint32_t* newBitmapPixels = (uint32_t*) bitmapPixels;
    int pixelsCount = jniBitmap->_bitmapInfo.height * jniBitmap->_bitmapInfo.width;
    memcpy(newBitmapPixels, jniBitmap->_storedBitmapPixels, sizeof(uint32_t) * pixelsCount);
    AndroidBitmap_unlockPixels(env, newBitmap);
    //LOGD("returning the new bitmap");
    return newBitmap;
}

/**store java bitmap as JNI data*/  //
JNIEXPORT jobject JNICALL Java_photoeditor_com_myapplication_MainActivity_jniStoreBitmapData(JNIEnv * env, jobject obj, jobject bitmap)
{
    AndroidBitmapInfo bitmapInfo;
    uint32_t* storedBitmapPixels = NULL;
    //LOGD("reading bitmap info...");
    int ret;
    if ((ret = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo)) < 0)
    {
        LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
        return NULL;
    }
    //LOGD("width:%d height:%d stride:%d", bitmapInfo.width, bitmapInfo.height, bitmapInfo.stride);
    if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888)
    {
        LOGE("Bitmap format is not RGBA_8888!");
        return NULL;
    }
    //
    //read pixels of bitmap into native memory :
    //
    //LOGD("reading bitmap pixels...");
    void* bitmapPixels;
    if ((ret = AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels)) < 0)
    {
        LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
        return NULL;
    }
    uint32_t* src = (uint32_t*) bitmapPixels;
    storedBitmapPixels = new uint32_t[bitmapInfo.height * bitmapInfo.width];
    int pixelsCount = bitmapInfo.height * bitmapInfo.width;
    memcpy(storedBitmapPixels, src, sizeof(uint32_t) * pixelsCount);
    AndroidBitmap_unlockPixels(env, bitmap);
    JniBitmap *jniBitmap = new JniBitmap();
    jniBitmap->_bitmapInfo = bitmapInfo;
    jniBitmap->_storedBitmapPixels = storedBitmapPixels;
    return env->NewDirectByteBuffer(jniBitmap, 0);
}

/**scales the image using the fastest, simplest algorithm called "nearest neighbor" */ //
JNIEXPORT void JNICALL Java_photoeditor_com_myapplication_MainActivity_jniScaleNNBitmap(JNIEnv * env, jobject obj, jobject handle, uint32_t newWidth, uint32_t newHeight)
{
    JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
    if (jniBitmap->_storedBitmapPixels == NULL)
        return;
    uint32_t oldWidth = jniBitmap->_bitmapInfo.width;
    uint32_t oldHeight = jniBitmap->_bitmapInfo.height;
    uint32_t* previousData = jniBitmap->_storedBitmapPixels;
    uint32_t* newBitmapPixels = new uint32_t[newWidth * newHeight];
    int x2, y2;
    int whereToPut = 0;
    for (int y = 0; y < newHeight; ++y)
    {
        for (int x = 0; x < newWidth; ++x)
        {
            x2 = x * oldWidth / newWidth;
            if (x2 < 0)
                x2 = 0;
            else if (x2 >= oldWidth)
                x2 = oldWidth - 1;
            y2 = y * oldHeight / newHeight;
            if (y2 < 0)
                y2 = 0;
            else if (y2 >= oldHeight)
                y2 = oldHeight - 1;
            newBitmapPixels[whereToPut++] = previousData[(y2 * oldWidth) + x2];
            //same as : newBitmapPixels[(y * newWidth) + x] = previousData[(y2 * oldWidth) + x2];
        }
    }

    delete[] previousData;
    jniBitmap->_storedBitmapPixels = newBitmapPixels;
    jniBitmap->_bitmapInfo.width = newWidth;
    jniBitmap->_bitmapInfo.height = newHeight;
}

I am use stacktrace that time display following exception i am put it

enter image description here

Cmakelists.txt inside configration

cmake_minimum_required(VERSION 3.4.1)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")

add_library( # Sets the name of the library.
             JniBitmapOperations

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             # Associated headers in the same location as their source
             # file are automatically included.
             src/main/cpp/JniBitmapOperations.cpp )

find_library( # Sets the name of the path variable.
              log-lib
                    JniBitmapOperations
              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log
              )
target_link_libraries( # Specifies the target library.

                       JniBitmapOperations

                       # Links the target library to the log library
                       # included in the NDK.
                       )

Android.mk file content

LOCAL_PATH := $(call my-dir)

#bitmap operations module
include $(CLEAR_VARS)

LOCAL_MODULE    := JniBitmapOperations
LOCAL_SRC_FILES := JniBitmapOperations.cpp
LOCAL_LDLIBS := -llog
LOCAL_LDFLAGS += -ljnigraphics

include $(BUILD_SHARED_LIBRARY)
APP_OPTIM := debug
LOCAL_CFLAGS := -g

#if you need to add more module, do the same as the one we started with (the one with the CLEAR_VARS)

Add calling java jniBitmapHolder.java file and no more process only compile and build my project to throw this exception .

import android.graphics.Bitmap;
import android.util.Log;

import java.nio.ByteBuffer;

public class JniBitmapHolder
  {
  ByteBuffer _handler =null;
  static
    {
    System.loadLibrary("JniBitmapOperations");
    }

  private native ByteBuffer jniStoreBitmapData(Bitmap bitmap);

  private native Bitmap jniGetBitmapFromStoredBitmapData(ByteBuffer handler);

  private native void jniFreeBitmapData(ByteBuffer handler);

  private native void jniRotateBitmapCcw90(ByteBuffer handler);

  private native void jniRotateBitmapCw90(ByteBuffer handler);

  private native void jniCropBitmap(ByteBuffer handler,final int left,final int top,final int right,final int bottom);

  private native void jniScaleNNBitmap(ByteBuffer handler,final int newWidth,final int newHeight);

  public JniBitmapHolder()
    {}

  public JniBitmapHolder(final Bitmap bitmap)
    {
    storeBitmap(bitmap);
    }

  public void storeBitmap(final Bitmap bitmap)
    {
    if(_handler!=null)
      freeBitmap();
    _handler=jniStoreBitmapData(bitmap);
    }

  public void rotateBitmapCcw90()
    {
    if(_handler==null)
      return;
    jniRotateBitmapCcw90(_handler);
    }

  public void rotateBitmapCw90()
    {
    if(_handler==null)
      return;
    jniRotateBitmapCw90(_handler);
    }

  public void cropBitmap(final int left,final int top,final int right,final int bottom)
    {
    if(_handler==null)
      return;
    jniCropBitmap(_handler,left,top,right,bottom);
    }

  public Bitmap getBitmap()
    {
    if(_handler==null)
      return null;
    return jniGetBitmapFromStoredBitmapData(_handler);
    }

  public Bitmap getBitmapAndFree()
    {
    final Bitmap bitmap=getBitmap();
    freeBitmap();
    return bitmap;
    }

  public void scaleBitmap(final int newWidth,final int newHeight)
    {
    if(_handler==null)
      return;
    jniScaleNNBitmap(_handler,newWidth,newHeight);
    }

  public void freeBitmap()
    {
    if(_handler==null)
      return;
    jniFreeBitmapData(_handler);
    _handler=null;
    }

  @Override
  protected void finalize() throws Throwable
    {
    super.finalize();
    if(_handler==null)
      return;
    Log.w("DEBUG","JNI bitmap wasn't freed nicely.please rememeber to free the bitmap as soon as you can");
    freeBitmap();
    }
  }

build gradle file configration.

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    useLibrary 'org.apache.http.legacy'
    defaultConfig {
        applicationId "com.itflash.whatsappstickers"
        minSdkVersion 18
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        multiDexEnabled true

        ndk {
            moduleName "JniBitmapOperations"
            ldLibs "log",  "jnigraphics"
            //abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86'
        }

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cmake {
                    cppFlags "-frtti -fexceptions"
                    arguments '-DANDROID_PLATFORM=android-19', '-DANDROID_TOOLCHAIN=clang'
                }
            }
        }
    }
    packagingOptions {
        exclude 'META-INF/NOTICE' // will not include NOTICE file
        exclude 'META-INF/LICENSE' // will not include LICENSE file
        exclude 'META-INF/DEPENDENCIES'
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
    dexOptions {
        incremental true
        javaMaxHeapSize "1536m"
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.google.android.gms:play-services:10.2.0'
    compile 'com.android.support:appcompat-v7:25.2.0'
    testCompile 'junit:junit:4.12'
}
Kuldeep
  • 123
  • 1
  • 12

1 Answers1

1

The linker cannot find the functions you use, i.e. you must specify the library manually. Looks to me, as if that should be libjnigraphics.

Did you specify LOCAL_LDFLAGS += -ljnigraphics in Android.mk as shown in this answer?

Community
  • 1
  • 1
grasbueschel
  • 879
  • 2
  • 8
  • 24
  • Ok. The problem stays the same though, i.e. the line is not used or lib couldn't be found, etc. To help further, can you please post the invocation (the full command that results in exit code 1)? – grasbueschel Mar 14 '17 at 10:16
  • Sorry friend i am not send full code for you but send some more c details check it . – Kuldeep Mar 14 '17 at 11:41
  • I'm not asking for code, I ask for the full command that gets executed when linking the project and fails. – grasbueschel Mar 14 '17 at 12:30
  • i am build my project that time throw this exception to me . – Kuldeep Mar 14 '17 at 12:38
  • Yes, I got that. And building a project means issuing a couple of commands, one of them being the linker command that fails. If you don't understand that, or are unable to operate your IDE, I'm sorry, but I can't help you. – grasbueschel Mar 14 '17 at 13:02
  • Its Ok friend Thank you . – Kuldeep Mar 14 '17 at 13:34
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/138026/discussion-between-kuldeep-and-grasbueschel). – Kuldeep Mar 14 '17 at 14:56