src/api-impl: use skia instead of cairo

Using the C API provided by SkiaSharp's skia fork instead of using cairo
significantly improves performance. The API is also closer to the android
Canvas API, which makes the implementation more straightforward.
This commit is contained in:
Mis012
2023-08-28 20:03:32 +02:00
parent 096919ec37
commit 1e47824a79
47 changed files with 3184 additions and 159 deletions

View File

@@ -4,46 +4,10 @@
#include "../util.h"
#include "../drawables/ninepatch.h"
#include "../sk_area/sk_area.h"
#include "WrapperWidget.h"
/**
* gdk_texture_new_for_surface:
* @surface: a cairo image surface
*
* Creates a new texture object representing the surface.
*
* @surface must be an image surface with format CAIRO_FORMAT_ARGB32.
*
* Returns: a new `GdkTexture`
*/
GdkTexture * gdk_texture_new_for_surface (cairo_surface_t *surface)
{
GdkTexture *texture;
GBytes *bytes;
g_return_val_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE, NULL);
g_return_val_if_fail (cairo_image_surface_get_width (surface) > 0, NULL);
g_return_val_if_fail (cairo_image_surface_get_height (surface) > 0, NULL);
bytes = g_bytes_new_with_free_func (cairo_image_surface_get_data (surface),
cairo_image_surface_get_height (surface)
* cairo_image_surface_get_stride (surface),
(GDestroyNotify) cairo_surface_destroy,
cairo_surface_reference (surface));
texture = gdk_memory_texture_new (cairo_image_surface_get_width (surface),
cairo_image_surface_get_height (surface),
GDK_MEMORY_DEFAULT,
bytes,
cairo_image_surface_get_stride (surface));
g_bytes_unref (bytes);
return texture;
}
// ---
G_DEFINE_TYPE(WrapperWidget, wrapper_widget, GTK_TYPE_WIDGET)
static void wrapper_widget_init (WrapperWidget *wrapper_widget)
@@ -66,54 +30,27 @@ static void wrapper_widget_dispose(GObject *wrapper_widget)
G_OBJECT_CLASS (wrapper_widget_parent_class)->dispose (wrapper_widget);
}
void wrapper_snapshot(GtkWidget* widget, GtkSnapshot* snapshot)
void skia_draw_func(SKArea *sk_area, sk_canvas_t *canvas, WrapperWidget *wrapper_widget)
{
struct ninepatch_t *ninepatch = g_object_get_data(G_OBJECT(widget), "background_ninepatch");
if(ninepatch && 0) {
GtkAllocation alloc;
gtk_widget_get_allocation(widget, &alloc);
ninepatch_stretch(ninepatch, alloc.width, alloc.height);
cairo_surface_t *surface = ninepatch_to_surface(ninepatch);
// GdkPixbuf *pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, ninepatch->__w, ninepatch->__h);
// GdkTexture *texture = gdk_texture_new_for_pixbuf(pixbuf);
GdkTexture *texture = gdk_texture_new_for_surface(surface);
gtk_snapshot_append_texture(snapshot, texture, &GRAPHENE_RECT_INIT(0, 0, ninepatch->__w, ninepatch->__h));
cairo_surface_destroy(surface);
// g_object_unref(pixbuf);
g_object_unref(texture);
JNIEnv *env;
(*wrapper_widget->jvm)->GetEnv(wrapper_widget->jvm, (void**)&env, JNI_VERSION_1_6);
if(wrapper_widget->canvas == NULL) {
wrapper_widget->canvas = _REF((*env)->NewObject(env, handle_cache.canvas.class, handle_cache.canvas.constructor, _INTPTR(canvas), 0));
} else {
_SET_LONG_FIELD(wrapper_widget->canvas, "skia_canvas", _INTPTR(canvas));
}
gtk_widget_snapshot_child(widget, gtk_widget_get_first_child(widget), snapshot);
// if onDraw method is overwritten call it now
WrapperWidget *wrapper_widget = WRAPPER_WIDGET(widget);
GtkAllocation alloc;
gtk_widget_get_allocation(widget, &alloc);
if (wrapper_widget->draw_method) {
cairo_t *cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT(0, 0, alloc.width, alloc.height));
(*env)->CallVoidMethod(env, wrapper_widget->jobj, wrapper_widget->draw_method, wrapper_widget->canvas);
JNIEnv *env;
(*wrapper_widget->jvm)->GetEnv(wrapper_widget->jvm, (void**)&env, JNI_VERSION_1_6);
if(wrapper_widget->canvas == NULL) {
wrapper_widget->canvas = _REF((*env)->NewObject(env, handle_cache.canvas.class, handle_cache.canvas.constructor, _INTPTR(cr), 0));
} else {
_SET_LONG_FIELD(wrapper_widget->canvas, "cairo_context", _INTPTR(cr));
}
(*env)->CallVoidMethod(env, wrapper_widget->jobj, wrapper_widget->draw_method, wrapper_widget->canvas);
if((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(env);
cairo_destroy (cr);
}
if((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(env);
}
/*void wrapper_snapshot(GtkWidget* widget, GtkSnapshot* snapshot)
{
gtk_widget_snapshot_child(widget, gtk_widget_get_first_child(widget), snapshot);
}*/
static void wrapper_widget_class_init(WrapperWidgetClass *class)
{
@@ -122,7 +59,7 @@ static void wrapper_widget_class_init(WrapperWidgetClass *class)
object_class->dispose = wrapper_widget_dispose;
widget_class->snapshot = wrapper_snapshot;
// widget_class->snapshot = wrapper_snapshot;
gtk_widget_class_set_layout_manager_type(widget_class, GTK_TYPE_BIN_LAYOUT);
}
@@ -156,6 +93,14 @@ static void on_mapped(GtkWidget* self, gpointer data)
}
}
// FIXME: this is used in one other place as well, should probably go in util.c or gtk_util.c?
gboolean tick_callback(GtkWidget* widget, GdkFrameClock* frame_clock, gpointer user_data)
{
gtk_widget_queue_draw(widget);
gtk_widget_queue_draw(gtk_widget_get_parent(widget));
return G_SOURCE_CONTINUE;
}
void wrapper_widget_set_jobject(WrapperWidget *wrapper, JNIEnv *env, jobject jobj)
{
JavaVM *jvm;
@@ -163,9 +108,15 @@ void wrapper_widget_set_jobject(WrapperWidget *wrapper, JNIEnv *env, jobject job
wrapper->jvm = jvm;
wrapper->jobj = _REF(jobj);
jmethodID draw_method = _METHOD(_CLASS(jobj), "onDraw", "(Landroid/graphics/Canvas;)V");
if (draw_method != handle_cache.view.onDraw)
if (draw_method != handle_cache.view.onDraw) {
wrapper->draw_method = draw_method;
GtkWidget *sk_area = sk_area_new();
sk_area_set_draw_func(SK_AREA_WIDGET(sk_area), skia_draw_func, wrapper);
gtk_widget_insert_before(sk_area, GTK_WIDGET(wrapper), NULL);
gtk_widget_add_tick_callback(sk_area, tick_callback, NULL, NULL);
}
jmethodID measure_method = _METHOD(_CLASS(jobj), "onMeasure", "(II)V");
if (measure_method != handle_cache.view.onMeasure) {
wrapper->measure_method = measure_method;