add support for custom Java Drawables

This is made possible, by adding a second Canvas implementation which
can be used to render directly to GdkSnapshot objects

For now the only implemented method is drawBitmap(), this is already
enough to make VectorDrawableCompat functional
This commit is contained in:
Julian Winkler 2024-03-24 21:01:47 +01:00
parent 0b78cbcc55
commit ad266c7821
15 changed files with 182 additions and 30 deletions

View file

@ -97,6 +97,7 @@ libtranslationlayer_so = shared_library('translation_layer_main', [
'src/api-impl-jni/graphics/NinePatchPaintable.c', 'src/api-impl-jni/graphics/NinePatchPaintable.c',
'src/api-impl-jni/graphics/NinePatchPaintable.c', 'src/api-impl-jni/graphics/NinePatchPaintable.c',
'src/api-impl-jni/graphics/android_graphics_BitmapFactory.c', 'src/api-impl-jni/graphics/android_graphics_BitmapFactory.c',
'src/api-impl-jni/graphics/android_graphics_GskCanvas.c',
'src/api-impl-jni/graphics/android_graphics_Matrix.c', 'src/api-impl-jni/graphics/android_graphics_Matrix.c',
'src/api-impl-jni/graphics/android_graphics_Path.c', 'src/api-impl-jni/graphics/android_graphics_Path.c',
'src/api-impl-jni/graphics/android_graphics_Typeface.c', 'src/api-impl-jni/graphics/android_graphics_Typeface.c',

View file

@ -0,0 +1,21 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class android_graphics_GskCanvas */
#ifndef _Included_android_graphics_GskCanvas
#define _Included_android_graphics_GskCanvas
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: android_graphics_GskCanvas
* Method: native_drawBitmap
* Signature: (JJIIII)V
*/
JNIEXPORT void JNICALL Java_android_graphics_GskCanvas_native_1drawBitmap
(JNIEnv *, jobject, jlong, jlong, jint, jint, jint, jint);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -15,6 +15,14 @@ extern "C" {
JNIEXPORT jlong JNICALL Java_android_graphics_drawable_Drawable_native_1paintable_1from_1path JNIEXPORT jlong JNICALL Java_android_graphics_drawable_Drawable_native_1paintable_1from_1path
(JNIEnv *, jclass, jstring); (JNIEnv *, jclass, jstring);
/*
* Class: android_graphics_drawable_Drawable
* Method: native_constructor
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_android_graphics_drawable_Drawable_native_1constructor
(JNIEnv *, jobject);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -0,0 +1,16 @@
#include <gtk/gtk.h>
#include <graphene.h>
#include "../defines.h"
#include "../util.h"
#include "../generated_headers/android_graphics_GskCanvas.h"
JNIEXPORT void JNICALL Java_android_graphics_GskCanvas_native_1drawBitmap(JNIEnv *env, jclass this_class, jlong snapshot_ptr, jlong pixbuf_ptr, jint x, jint y, jint width, jint height)
{
GdkSnapshot *snapshot = (GdkSnapshot *)_PTR(snapshot_ptr);
GdkPixbuf *pixbuf = (GdkPixbuf *)_PTR(pixbuf_ptr);
GdkTexture *texture = gdk_texture_new_for_pixbuf(pixbuf);
gtk_snapshot_append_texture(snapshot, texture, &GRAPHENE_RECT_INIT(x, y, width, height));
g_object_unref(texture);
}

View file

@ -2,6 +2,7 @@
#include <string.h> #include <string.h>
#include "../defines.h" #include "../defines.h"
#include "../util.h"
#include "NinePatchPaintable.h" #include "NinePatchPaintable.h"
#include "../generated_headers/android_graphics_drawable_Drawable.h" #include "../generated_headers/android_graphics_drawable_Drawable.h"
@ -20,3 +21,49 @@ JNIEXPORT jlong JNICALL Java_android_graphics_drawable_Drawable_native_1paintabl
(*env)->ReleaseStringUTFChars(env, pathStr, path); (*env)->ReleaseStringUTFChars(env, pathStr, path);
return _INTPTR(paintable); return _INTPTR(paintable);
} }
struct _JavaPaintable {
GObject parent_instance;
jobject drawable;
};
G_DECLARE_FINAL_TYPE(JavaPaintable, java_paintable, JAVA, PAINTABLE, GObject)
static void java_paintable_snapshot(GdkPaintable *gdk_paintable, GdkSnapshot *snapshot, double width, double height)
{
JNIEnv *env = get_jni_env();
JavaPaintable *paintable = JAVA_PAINTABLE(gdk_paintable);
jclass canvas_class = (*env)->FindClass(env, "android/graphics/GskCanvas");
jmethodID canvas_constructor = _METHOD(canvas_class, "<init>", "(J)V");
jobject canvas = (*env)->NewObject(env, canvas_class, canvas_constructor, _INTPTR(snapshot));
(*env)->CallVoidMethod(env, paintable->drawable, handle_cache.drawable.setBounds, 0, 0, (int)width, (int)height);
(*env)->CallVoidMethod(env, paintable->drawable, handle_cache.drawable.draw, canvas);
if ((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(env);
(*env)->DeleteLocalRef(env, canvas);
(*env)->DeleteLocalRef(env, canvas_class);
}
static void java_paintable_init(JavaPaintable *java_paintable)
{
}
static void java_paintable_paintable_init(GdkPaintableInterface *iface)
{
iface->snapshot = java_paintable_snapshot;
}
static void java_paintable_class_init(JavaPaintableClass *class)
{
}
G_DEFINE_TYPE_WITH_CODE(JavaPaintable, java_paintable, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE(GDK_TYPE_PAINTABLE, java_paintable_paintable_init))
JNIEXPORT jlong JNICALL Java_android_graphics_drawable_Drawable_native_1constructor(JNIEnv *env, jobject this) {
JavaPaintable *paintable = NULL;
if (handle_cache.drawable.draw != _METHOD(_CLASS(this), "draw", "(Landroid/graphics/Canvas;)V")) {
paintable = g_object_new(java_paintable_get_type(), NULL);
paintable->drawable = _REF(this);
}
return _INTPTR(paintable);
}

View file

@ -144,6 +144,10 @@ void set_up_handle_cache(JNIEnv *env)
handle_cache.key_event.class = _REF((*env)->FindClass(env, "android/view/KeyEvent")); handle_cache.key_event.class = _REF((*env)->FindClass(env, "android/view/KeyEvent"));
handle_cache.key_event.constructor = _METHOD(handle_cache.key_event.class, "<init>", "(II)V"); handle_cache.key_event.constructor = _METHOD(handle_cache.key_event.class, "<init>", "(II)V");
handle_cache.drawable.class = _REF((*env)->FindClass(env, "android/graphics/drawable/Drawable"));
handle_cache.drawable.draw = _METHOD(handle_cache.drawable.class, "draw", "(Landroid/graphics/Canvas;)V");
handle_cache.drawable.setBounds = _METHOD(handle_cache.drawable.class, "setBounds", "(IIII)V");
} }
void extract_from_apk(const char *path, const char *target) { void extract_from_apk(const char *path, const char *target) {

View file

@ -100,6 +100,11 @@ struct handle_cache {
jclass class; jclass class;
jmethodID constructor; jmethodID constructor;
} key_event; } key_event;
struct {
jclass class;
jmethodID draw;
jmethodID setBounds;
} drawable;
}; };
extern struct handle_cache handle_cache; extern struct handle_cache handle_cache;

View file

@ -81,7 +81,7 @@ public class Canvas {
* @param px The x-coord for the pivot point (unchanged by the rotation) * @param px The x-coord for the pivot point (unchanged by the rotation)
* @param py The y-coord for the pivot point (unchanged by the rotation) * @param py The y-coord for the pivot point (unchanged by the rotation)
*/ */
public final void rotate(float degrees, float px, float py) { public void rotate(float degrees, float px, float py) {
native_rotate_and_translate(skia_canvas, widget, degrees, px, py); native_rotate_and_translate(skia_canvas, widget, degrees, px, py);
} }
// --- // ---

View file

@ -0,0 +1,74 @@
package android.graphics;
/**
* GskCanvas:
* - implements Canvas for onscreen rendering inside GTKs snapshot function
*/
public class GskCanvas extends Canvas {
private long snapshot;
public GskCanvas(long snapshot) {
System.out.println("GskCanvas(" + snapshot + ")");
this.snapshot = snapshot;
}
@Override
public int save() {
System.out.println("GskCanvas.save()");
return -1;
}
@Override
public void restore() {
System.out.println("GskCanvas.restore()");
}
@Override
public void translate(float dx, float dy) {
System.out.println("GskCanvas.translate(" + dx + ", " + dy + ")");
}
@Override
public void rotate(float degrees) {
System.out.println("GskCanvas.rotate(" + degrees + ")");
}
@Override
public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
native_drawBitmap(snapshot, bitmap.pixbuf, dst.left, dst.top, dst.width(), dst.height());
}
@Override
public void drawPath(Path path, Paint paint) {
System.out.println("GskCanvas.drawPath(" + path + ", " + paint + ")");
}
@Override
public void drawRect(float left, float top, float right, float bottom, Paint paint) {
System.out.println("GskCanvas.drawRect(" + left + ", " + top + ", " + right + ", " + bottom + ", " + paint + ")");
}
@Override
public void rotate(float degrees, float px, float py) {
System.out.println("GskCanvas.rotate(" + degrees + ", " + px + ", " + py + ")");
}
@Override
public void drawText(String text, float x, float y, Paint paint) {
System.out.println("GskCanvas.drawText(" + text + ", " + x + ", " + y + ", " + paint + ")");
}
@Override
public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
System.out.println("GskCanvas.drawLine(" + startX + ", " + startY + ", " + stopX + ", " + stopY + ", " + paint + ")");
}
@Override
public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
Rect dst = new Rect((int)left, (int)top, (int)left + bitmap.getWidth(), (int)top + bitmap.getHeight());
drawBitmap(bitmap, src, dst, paint);
}
protected native void native_drawBitmap(long snapshot, long pixbuf, int x, int y, int width, int height);
}

View file

@ -28,7 +28,9 @@ public class Drawable {
private int[] mStateSet = new int[0]; private int[] mStateSet = new int[0];
public long paintable; public long paintable;
public Drawable() {} public Drawable() {
this.paintable = native_constructor();
}
public Drawable(long paintable) { public Drawable(long paintable) {
this.paintable = paintable; this.paintable = paintable;
@ -180,4 +182,5 @@ public class Drawable {
} }
protected static native long native_paintable_from_path(String path); protected static native long native_paintable_from_path(String path);
protected native long native_constructor();
} }

View file

@ -1,15 +1,7 @@
package android.graphics.drawable; package android.graphics.drawable;
import android.graphics.Canvas;
public class GradientDrawable extends Drawable { public class GradientDrawable extends Drawable {
@Override
public void draw(Canvas canvas) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'draw'");
}
public void setColor(int color) {} public void setColor(int color) {}
public void setCornerRadius(float cornerRadius) {} public void setCornerRadius(float cornerRadius) {}

View file

@ -1,6 +1,5 @@
package android.graphics.drawable; package android.graphics.drawable;
import android.graphics.Canvas;
import android.graphics.Rect; import android.graphics.Rect;
public class InsetDrawable extends Drawable { public class InsetDrawable extends Drawable {
@ -9,12 +8,6 @@ public class InsetDrawable extends Drawable {
super(); super();
} }
@Override
public void draw(Canvas canvas) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'draw'");
}
public boolean getPadding(Rect padding) { return false; } public boolean getPadding(Rect padding) { return false; }
} }

View file

@ -1,15 +1,9 @@
package android.graphics.drawable; package android.graphics.drawable;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.Canvas;
public class RippleDrawable extends Drawable { public class RippleDrawable extends Drawable {
public RippleDrawable(ColorStateList colorStateList, Drawable drawable, Drawable drawable2) {} public RippleDrawable(ColorStateList colorStateList, Drawable drawable, Drawable drawable2) {}
@Override
public void draw(Canvas canvas) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'draw'");
}
} }

View file

@ -1,6 +1,5 @@
package android.graphics.drawable; package android.graphics.drawable;
import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.drawable.shapes.Shape; import android.graphics.drawable.shapes.Shape;
@ -9,11 +8,5 @@ public class ShapeDrawable extends Drawable {
public ShapeDrawable(Shape shape) {} public ShapeDrawable(Shape shape) {}
public Paint getPaint() {return new Paint();} public Paint getPaint() {return new Paint();}
@Override
public void draw(Canvas canvas) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'draw'");
}
} }

View file

@ -161,6 +161,7 @@ hax_jar = jar('hax', [
'android/graphics/Canvas.java', 'android/graphics/Canvas.java',
'android/graphics/Color.java', 'android/graphics/Color.java',
'android/graphics/ColorFilter.java', 'android/graphics/ColorFilter.java',
'android/graphics/GskCanvas.java',
'android/graphics/Matrix.java', 'android/graphics/Matrix.java',
'android/graphics/Paint.java', 'android/graphics/Paint.java',
'android/graphics/Path.java', 'android/graphics/Path.java',