Home > Enterprise >  How to draw using the SKIA C API on a Java Canvas object on Android?
How to draw using the SKIA C API on a Java Canvas object on Android?

Time:06-17

I am experimenting with native drawing using the Java Canvas API on Android. As far as I can understand, Canvas is built as an abstraction on top of SKCanvas from the Skia graphics engine. I'm willing to pass the Canvas object obtained in the OnDraw() method of a view to native code and draw using C and the Skia API.

What would be the best way to gain access to the SkCanvas pointer after passing the java Canvas object to native code using JNI?

Here is the information I have been able to assimilate, which ultimately boils down to accessing a private member from the instance a native platform object.

Source code of Canvas.java from https://cs.android.com/android/platform/superproject/ /master:frameworks/base/graphics/java/android/graphics/Canvas.java;l=59 shows that it has private variable named mNativeCanvasWrapper (defined in frameworks/base/graphics/java/android/graphics/BaseCanvas.java) which can be accessed via reflection through the function declared below.

/** @hide */
@UnsupportedAppUsage
public long getNativeCanvasWrapper() {
    return mNativeCanvasWrapper;
}

mNativeCanvasWrapper, among other things, is passed to all functions responsible for drawing. Following the trail of one such function (getWidth()), I can see it's JNI registration in https://cs.android.com/android/platform/superproject/ /master:frameworks/base/libs/hwui/jni/android_graphics_Canvas.cpp;l=692

{"nGetWidth","(J)I", (void*) CanvasJNI::getWidth},

Definition of CanvasJNI::getWidth()

static jint getWidth(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
    return static_cast<jint>(get_canvas(canvasHandle)->width());
}

Definition of get_canvas(canvasHandle)

static Canvas* get_canvas(jlong canvasHandle) {
    return reinterpret_cast<Canvas*>(canvasHandle);
}

The above Canvas* pointer actually points to a class called SkiaCanvas, which inherits from the Canvas base class. Definition for the class can be found at frameworks/base/libs/hwui/SkiaCanvas.cpp. This is the class that contains the actual pointer to the SKCanvas object that I need (marked private), as can be seen at https://cs.android.com/android/platform/superproject/ /master:frameworks/base/libs/hwui/SkiaCanvas.h;l=219.

How can I obtain the SKCanvas pointer from a SkiaCanvas pointer, given that it's marked private and the only getter is marked protected?

CodePudding user response:

The answer is in two parts.
First, getting the Canvas* out of the JNI object:

jclass cls_Canvas = env->GetObjectClass(obj);
jmethodID mid_Canvas_getNativeCanvasWrapper = env->getMethodID("getNativeCanvasWrapper", "()J");
jlong canvas_ptr = env->CallLongMethod(obj, mid_Canvas_getNativeCanvasWrapper);

The second part goes from there:

class MyCanvas : public SkiaCanvas { public: SkCanvas* getCanvas() { return asSkCanvas(); };
MyCanvas* skc = reinterpret_cast<MyCanvas*>(canvas_ptr);
SkCanvas* s = myc->getCanvas();
  • Related