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();