2

I have a small JNI method to retrieve a pixel from the screen. The method receives a ByteBuffer from Java and is written to in C++ according to this question how to write and read from bytebuffer passing from java to jni.

While this does work, I noticed the first byte I write is not correct, but the others are:

Input length is 3 at start 000000000239F238

Color is 202, 97, 79

values read in java: -54, 97, 79

That is the output of my program.

The C++ code:

JNIEXPORT void JNICALL Java_capturePixel(JNIEnv * env, jobject clz, jobject buffer)
{
    jbyte* bufferStart = static_cast<jbyte*>(env->GetDirectBufferAddress(buffer));
    jlong inputLength = env->GetDirectBufferCapacity(buffer);
    

    HDC hScreenDC = GetDC(nullptr);

    std::cout << "Input length is " << inputLength << " at start " << &bufferStart << std::endl;

    COLORREF pixel = GetPixel(hScreenDC, 100, 20);

    int r = GetRValue(pixel);
    int g = GetGValue(pixel);
    int b = GetBValue(pixel);

    std::cout << "Color is " << r << ", " << g << ", " << b << std::endl;

    bufferStart[0] = r;
    bufferStart[1] = g;
    bufferStart[2] = b;
}

My Java code (Actually Kotlin):

val r = buffer.get()
val g = buffer.get()
val b = buffer.get()
println("values read in java: $r, $g, $b")

Why is the first byte wrong? I am assuming it has to do with its sign? But then again, why are the others not wrong? I'm not sure why this is happening.

Community
  • 1
  • 1
Limnic
  • 1,826
  • 1
  • 20
  • 45

1 Answers1

2

As Richard in the comments mentioned, it has to do with the sign of the byte. It's a coincidence that the two other values are correct, because they do not go above 127; RGB is unsigned and ranges from 0-255 while a signed byte ranges from -128 to 127.

The bytes are correct in the buffer, but the java buffer.get() reads it signed.

A way to solve this is to call Byte.toUnsignedInt(...) (Java API) with the byte read.

Alternatively, you can create an extension method in Kotlin:

fun Byte.toUnsigned(): Int = this.toInt() and 0xff

which you can then use to call the get() on the buffer like this:

val r = buffer.get().toUnsigned()
val g = buffer.get().toUnsigned()
val b = buffer.get().toUnsigned()
Limnic
  • 1,826
  • 1
  • 20
  • 45