3

On Android, I use JNI to send a string from C++ to Java then draw it to a bitmap then return the byte array back to C++ to draw it to OpenGL, I get a black rectangle .

First I create the bitmap in Java by this way:

public static byte[] DrawString(String text) {
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    paint.setColor(Color.rgb(110, 110, 110));
    float scale = ActivityGame.getCurrentActivity().getResources().getDisplayMetrics().density;
    paint.setTextSize((int) (24 * scale));
    Rect bounds = new Rect();
    paint.getTextBounds(text, 0, text.length(), bounds);

    Bitmap bitmap = Bitmap.createBitmap((int) (bounds.width() * scale), (int) (bounds.height() * scale), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    int x = (bitmap.getWidth() - bounds.width()) / 6;
    int y = (bitmap.getHeight() + bounds.height()) / 5;
    canvas.drawText(text, x * scale, y * scale, paint);

    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
    byte[] byteArray = stream.toByteArray();
    bitmap.recycle();
    return byteArray;
}

then in C++ I call the previous function by JNI by this way:

static unsigned char* DrawString(std::string text,int* length){
    JNIEnv *env= nullptr;
    JNIUtils::JVM->AttachCurrentThread(&env, NULL);
    jclass jniUtilsCls = env->FindClass("com/moussa/mightypolygons/JNIUtils");
    jmethodID drawStringMethodId = env->GetStaticMethodID(jniUtilsCls, "DrawString", "(Ljava/lang/String;)[B");
    jstring _text=env->NewStringUTF(text.c_str());
    jbyteArray bmpArr=(jbyteArray)env->CallStaticObjectMethod(jniUtilsCls,drawStringMethodId,_text);
    jsize num_bytes = env->GetArrayLength( bmpArr);
    jbyte* elements = env->GetByteArrayElements(bmpArr, NULL);
    unsigned char* bmp=new unsigned char[num_bytes];
    if (elements) {
        for(int i = 0; i < num_bytes; i++) {
            bmp[i] = elements[i];
        }
        *length=(int)num_bytes;
        return bmp;
    }
    return NULL;
}

Finally I get the char array and the length now I create a texture in OpenGL:

Texture::Texture(unsigned char *bmp,int len, TextureTypes type) {
image = stbi_load_from_memory(bmp,len, &width, &height, &nrComponents, 4);
glGenTextures(1, &textureObj);
if (image) {
    GLenum format;
    if (nrComponents == 1)
        format = GL_RED;
    else if (nrComponents == 3)
        format = GL_RGB;
    else if (nrComponents == 4)
        format = GL_RGBA;
    else
        format = GL_RGBA;
    glBindTexture(GL_TEXTURE_2D, textureObj);
    glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
                    format == GL_RGBA ? GL_CLAMP_TO_EDGE : GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
                    format == GL_RGBA ? GL_CLAMP_TO_EDGE : GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    stbi_image_free(image);
} else {
    stbi_image_free(image);
}
}

I get a black rectangle with same width and height of the bitmap.

Edit:

I saved the bitmap as PNG from Java side then reloaded it from C++ it was rendered perfect with same code, so is the problem is in bytes array or GL parameters or something else?

Edit 2:

I noticed in Java the ByteArray contains signed bytes with negative values when it is cast to unsigned char* in C++ it value changed, for example the first byte in the bytes array in Java is -119 it becomes 137 if cast to unsigned char

  • You have almost zero error-checking in this code. So the first step would be to add proper checking of every return value. And maybe also dump the uncompressed bitmap and the PNG to the filesystem and verify that they look as expected. – Michael Sep 17 '19 at 08:39
  • Does the last step produce a white rectangle if you fill your buffer with 0xFF and lock the format to RGBA? – Botje Sep 17 '19 at 08:43
  • @Botje No, just black – Mohamed Moussa Sep 17 '19 at 11:40
  • Then you need to debug the drawing path first. Can you share that code? – Botje Sep 17 '19 at 11:40
  • @Michael the Bitmap created in Java as expected and the C++ receive the byte array which has the correct bytes I guess – Mohamed Moussa Sep 17 '19 at 11:42
  • @Botje the GL Drawing call you mean? – Mohamed Moussa Sep 17 '19 at 11:48
  • The drawing call, shaders used, and vertex attributes for the rectangle. – Botje Sep 17 '19 at 11:49
  • I saved the bitmap as PNG from Java side then reload it from C++ it rendered perfect with same code, so is the problem is in bytes array or GL parameters or something else? – Mohamed Moussa Sep 17 '19 at 15:54
  • I see no reason why saving to a file would work and passing an in-memory buffer would not. Note that you passed '4' as `desired_channels` parameter, so `image` will always contain RGBA. – Botje Sep 18 '19 at 07:24
  • I noticed in Java the byteArray contains signed bytes with negative values when it is cast to unsigned char in C++ it value changed, for example the first byte in the bytes array in Java is -119 it becomes 137 if cast to unsigned char – Mohamed Moussa Sep 18 '19 at 23:47

1 Answers1

1

I used a different method to send the Bitmap object as it is from Java and read its pixels using android/bitmap.h

AndroidBitmapInfo androidBitmapInfo ;
void* pixels;
AndroidBitmap_getInfo(env, bitmap, &androidBitmapInfo);
AndroidBitmap_lockPixels(env, bitmap, &pixels);
unsigned char* pixelsChar = (unsigned char*) pixels;
AndroidBitmap_unlockPixels(env, bitmap);

it works fine now.