Commit 564f45ba authored by Jon Nordby's avatar Jon Nordby

Merge remote-tracking branch 'origin/meta-json'

parents 539c231c aed68655
......@@ -494,7 +494,7 @@ AC_CHECK_FUNCS(fsync)
PKG_CHECK_MODULES(BABL, babl >= babl_required_version)
GLIB_PACKAGES="gobject-2.0 gmodule-2.0 gio-2.0"
GLIB_PACKAGES="gobject-2.0 gmodule-2.0 gio-2.0 json-glib-1.0"
# select platform-specific GIO
case "$os_win32" in
yes)
......
......@@ -28,6 +28,7 @@ liboperation_public_HEADERS = \
gegl-operation-context.h \
gegl-operation-filter.h \
gegl-operation-meta.h \
gegl-operation-meta-json.h \
gegl-operation-point-composer.h \
gegl-operation-point-composer3.h \
gegl-operation-point-filter.h \
......@@ -46,6 +47,7 @@ liboperation_sources = \
gegl-operation-composer3.c \
gegl-operation-filter.c \
gegl-operation-meta.c \
gegl-operation-meta-json.c \
gegl-operation-point-composer.c \
gegl-operation-point-composer3.c \
gegl-operation-point-filter.c \
......
/* This file is part of GEGL
*
* GEGL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* GEGL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2014 Jon Nordby <jononor@gmail.com>
*/
#include "config.h"
#include <glib-object.h>
#include <string.h>
#include "gegl.h"
#include "gegl-operation-meta-json.h"
static void finalize (GObject *self_object);
G_DEFINE_TYPE (GeglOperationMetaJson, gegl_operation_meta_json, GEGL_TYPE_OPERATION_META)
static void
gegl_operation_meta_json_class_init (GeglOperationMetaJsonClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
// TODO: override attach()
object_class->finalize = finalize;
}
static void
gegl_operation_meta_json_init (GeglOperationMetaJson *self)
{
}
static void
finalize (GObject *gobject)
{
GeglOperationMetaJson *self = GEGL_OPERATION_META_JSON (gobject);
G_OBJECT_CLASS (gegl_operation_meta_json_parent_class)->finalize (gobject);
}
/* This file is part of GEGL
*
* GEGL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* GEGL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2014 Jon Nordby <jononor@gmail.com>
*/
/* GeglOperationMetaJsn
* Used for GEGL operations that are implemented as a sub-graph in .json
*/
#ifndef __GEGL_OPERATION_META_JSON_H__
#define __GEGL_OPERATION_META_JSON_H__
#include "gegl-operation-meta.h"
G_BEGIN_DECLS
#define GEGL_TYPE_OPERATION_META_JSON (gegl_operation_meta_get_type ())
#define GEGL_OPERATION_META_JSON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_OPERATION_META_JSON, GeglOperationMetaJson))
#define GEGL_OPERATION_META_JSON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GEGL_TYPE_OPERATION_META_JSON, GeglOperationMetaJsonClass))
#define GEGL_IS_OPERATION_META_JSON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_OPERATION_META_JSON))
#define GEGL_IS_OPERATION_META_JSON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GEGL_TYPE_OPERATION_META_JSON))
#define GEGL_OPERATION_META_JSON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GEGL_TYPE_OPERATION_META_JSON, GeglOperationMetaJsonClass))
typedef struct _GeglOperationMetaJson GeglOperationMetaJson;
struct _GeglOperationMetaJson
{
GeglOperationMeta parent_instance;
gpointer pad[4];
};
typedef struct _GeglOperationMetaJsonClass GeglOperationMetaJsonClass;
struct _GeglOperationMetaJsonClass
{
GeglOperationMetaClass parent_class;
gpointer pad[4];
};
GType gegl_operation_meta_json_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif
......@@ -13,4 +13,5 @@ op_LTLIBRARIES = \
cast-format.la \
convert-format.la \
crop.la \
json.la \
nop.la
/* This file is part of GEGL
*
* GEGL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* GEGL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2014 Jon Nordby <jononor@gmail.com>
*/
/*
GeglOperationMetaJson : GeglOperationMeta
Base class for all ops, paramatrizable to the specific json data
Keeps the parsed json structure in memory as class_data
on class_init() registers GOBject properties for exposed ports
on attach() instantiates the nodes in subgraph, and connects them
GeglModuleJson :
on register(), walks directories in GEGL_PATH
for each .json file found, registers
Internal operations
operations/json/something.json
Installed to, and loaded at runtime from
$(GEGL_PATH)/myop.json
dropshadow a good initial testcase?
*/
#include <json-glib/json-glib.h>
#include <gegl-plugin.h>
// For module paths
#include <gegl-init-private.h>
#include <gegldatafiles.h>
/* JsonOp: Meta-operation base class for ops defined by .json file */
#include <operation/gegl-operation-meta-json.h>
typedef struct _JsonOp
{
GeglOperationMetaJson parent_instance;
JsonObject *json_root;
GHashTable *nodes; // gchar* -> GeglNode *, owned by parent node
} JsonOp;
typedef struct
{
GeglOperationMetaJsonClass parent_class;
JsonObject *json_root;
GHashTable *properties; // guint property_id -> PropertyTarget
} JsonOpClass;
typedef struct
{
gchar *node;
gchar *port;
} PropertyTarget;
static PropertyTarget *
property_target_new(gchar *node, gchar *port)
{
PropertyTarget *self = g_new(PropertyTarget, 1);
self->node = node;
self->port = port;
return self;
}
static void
property_target_free(PropertyTarget *self)
{
g_free(self->node);
g_free(self->port);
g_free(self);
}
static gchar *
replace_char_inline(gchar *str, gchar from, gchar to) {
for (int i=0; i<strlen(str); i++) {
str[i] = (str[i] == from) ? to : str[i];
}
return str;
}
static gchar *
component2geglop(const gchar *name) {
if (!name) {
return NULL;
}
gchar *dup = g_strdup(name);
replace_char_inline(dup, '/', ':');
g_ascii_strdown(dup, -1);
return dup;
}
static gchar *
component2gtypename(const gchar *name) {
if (!name) {
return NULL;
}
gchar *dup = g_strdup(name);
replace_char_inline(dup, '/', '_');
g_ascii_strdown(dup, -1);
return dup;
}
static gboolean
gvalue_from_string(GValue *value, GType target_type, GValue *dest_value) {
// Custom conversion from string
if (!G_VALUE_HOLDS_STRING(value)) {
return FALSE;
}
const gchar *iip = g_value_get_string(value);
if (g_type_is_a(target_type, G_TYPE_DOUBLE)) {
gdouble d = g_ascii_strtod(iip, NULL);
g_value_set_double(dest_value, d);
} else if (g_type_is_a(target_type, G_TYPE_INT)) {
gint i = g_ascii_strtoll(iip, NULL, 10);
g_value_set_int(dest_value, i);
} else if (g_type_is_a(target_type, G_TYPE_INT64)) {
gint64 i = g_ascii_strtoll(iip, NULL, 10);
g_value_set_int64(dest_value, i);
} else if (g_type_is_a(target_type, GEGL_TYPE_COLOR)) {
GeglColor *color = gegl_color_new(iip);
if (!color || !GEGL_IS_COLOR(color)) {
return FALSE;
}
g_value_set_object(dest_value, color);
} else if (g_type_is_a(target_type, G_TYPE_ENUM)) {
GEnumClass *klass = (GEnumClass *)g_type_class_ref(target_type);
GEnumValue *val = g_enum_get_value_by_nick(klass, iip);
g_return_val_if_fail(val, FALSE);
g_value_set_enum(dest_value, val->value);
g_type_class_unref((gpointer)klass);
} else if (g_type_is_a(target_type, G_TYPE_BOOLEAN)) {
gboolean b = g_ascii_strcasecmp("true", iip) == 0;
g_value_set_boolean(dest_value, b);
} else {
return FALSE;
}
return TRUE;
}
static gboolean
set_prop(GeglNode *t, const gchar *port, GParamSpec *paramspec, GValue *value) {
GType target_type = G_PARAM_SPEC_VALUE_TYPE(paramspec);
GValue dest_value = {0,};
g_value_init(&dest_value, target_type);
gboolean success = g_param_value_convert(paramspec, value, &dest_value, FALSE);
if (success) {
gegl_node_set_property(t, port, &dest_value);
return TRUE;
}
if (gvalue_from_string(value, target_type, &dest_value)) {
g_param_value_validate(paramspec, &dest_value);
gegl_node_set_property(t, port, &dest_value);
return TRUE;
}
return FALSE;
}
static GParamSpec *
copy_param_spec(GParamSpec *in, const gchar *name) {
const gchar * blurb = g_param_spec_get_blurb(in);
GParamSpec *out = NULL;
GParamFlags flags = G_PARAM_READWRITE;
// TODO: handle more things
if (G_IS_PARAM_SPEC_FLOAT(in)) {
GParamSpecFloat *f = G_PARAM_SPEC_FLOAT(in);
out = g_param_spec_double(name, name, blurb, f->minimum, f->maximum, f->default_value, flags);
} else if (G_IS_PARAM_SPEC_DOUBLE(in)) {
GParamSpecDouble *d = G_PARAM_SPEC_DOUBLE(in);
out = g_param_spec_double(name, name, blurb, d->minimum, d->maximum, d->default_value, flags);
} else if (G_IS_PARAM_SPEC_INT(in)) {
GParamSpecInt *i = G_PARAM_SPEC_INT(in);
out = g_param_spec_int(name, name, blurb, i->minimum, i->maximum, i->default_value, flags);
} else if (G_IS_PARAM_SPEC_UINT(in)) {
GParamSpecUInt *u = G_PARAM_SPEC_UINT(in);
out = g_param_spec_int(name, name, blurb, u->minimum, u->maximum, u->default_value, flags);
} else if (G_IS_PARAM_SPEC_LONG(in)) {
GParamSpecLong *l = G_PARAM_SPEC_LONG(in);
out = g_param_spec_int(name, name, blurb, l->minimum, l->maximum, l->default_value, flags);
} else if (GEGL_IS_PARAM_SPEC_COLOR(in)) {
GeglColor *default_value = gegl_param_spec_color_get_default(in);
out = gegl_param_spec_color(name, name, blurb, default_value, flags);
} else {
g_critical("json: Unknown param spec type for property %s", g_param_spec_get_nick(in));
}
return out;
}
static guint
install_properties(JsonOpClass *json_op_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (json_op_class);
JsonObject *root = json_op_class->json_root;
guint prop = 1;
// Exported ports
if (json_object_has_member(root, "inports")) {
JsonObject *inports = json_object_get_object_member(root, "inports");
GList *inport_names = json_object_get_members(inports);
for (int i=0; i<g_list_length(inport_names); i++) {
const gchar *name = g_list_nth_data(inport_names, i);
JsonObject *conn = json_object_get_object_member(inports, name);
const gchar *proc = json_object_get_string_member(conn, "process");
const gchar *port = json_object_get_string_member(conn, "port");
JsonObject *processes = json_object_get_object_member(root, "processes");
JsonObject *p = json_object_get_object_member(processes, proc);
const gchar *component = json_object_get_string_member(p, "component");
{
GParamSpec *target_spec = NULL;
gchar *opname = component2geglop(component);
// HACK: should avoid instantiating node to determine prop
GeglNode *n = gegl_node_new();
g_assert(n);
gegl_node_set(n, "operation", opname, NULL);
target_spec = gegl_node_find_property(n, port);
if (target_spec) {
GParamSpec *spec = copy_param_spec(target_spec, name);
PropertyTarget *t = property_target_new(g_strdup(proc), g_strdup(port));
g_hash_table_insert(json_op_class->properties, GINT_TO_POINTER(prop), t);
g_object_class_install_property (object_class, prop, spec);
prop++;
}
g_object_unref(n);
g_free(opname);
}
}
}
/*
if (json_object_has_member(root, "outports")) {
JsonObject *outports = json_object_get_object_member(root, "outports");
GList *outport_names = json_object_get_members(outports);
for (int i=0; i<g_list_length(outport_names); i++) {
const gchar *name = g_list_nth_data(outport_names, i);
JsonObject *conn = json_object_get_object_member(outports, name);
const gchar *proc = json_object_get_string_member(conn, "process");
const gchar *port = json_object_get_string_member(conn, "port");
graph_add_port(self, GraphOutPort, name, proc, port);
}
}
*/
return prop-1;
}
static GObject *
constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
gpointer klass = g_type_class_peek(GEGL_TYPE_OPERATION_META_JSON);
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
GObject *obj = gobject_class->constructor(type, n_construct_properties, construct_properties);
JsonOpClass *json_op_class = G_TYPE_INSTANCE_GET_CLASS(obj, type, JsonOpClass);
JsonOp *json_op = (JsonOp *)obj;
json_op->json_root = json_op_class->json_root; // to avoid looking up via class/gtype
return obj;
}
static void
get_property (GObject *gobject,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
JsonOpClass * json_op_class = (JsonOpClass *)G_OBJECT_GET_CLASS(gobject);
JsonOp * self = (JsonOp *)(gobject);
PropertyTarget *target = g_hash_table_lookup(json_op_class->properties, GINT_TO_POINTER(property_id));
if (!target) {
G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
return;
}
GeglNode *node = g_hash_table_lookup(self->nodes, target->node);
if (!node) {
G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
return;
}
gegl_node_get_property(node, target->port, value);
}
static void
set_property (GObject *gobject,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
JsonOpClass * json_op_class = (JsonOpClass *)G_OBJECT_GET_CLASS(gobject);
JsonOp * self = (JsonOp *)(gobject);
g_assert(self);
PropertyTarget *target = g_hash_table_lookup(json_op_class->properties, GINT_TO_POINTER(property_id));
if (!target) {
G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
return;
}
GeglNode *node = g_hash_table_lookup(self->nodes, target->node);
if (!node) {
G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
return;
}
gegl_node_set_property(node, target->port, value);
}
static void
attach (GeglOperation *operation)
{
JsonOp *self = (JsonOp *)operation;
GeglNode *gegl = operation->node;
// Processes
JsonObject *root = self->json_root;
JsonObject *processes = json_object_get_object_member(root, "processes");
GList *process_names = json_object_get_members(processes);
for (int i=0; i<g_list_length(process_names); i++) {
const gchar *name = g_list_nth_data(process_names, i);
JsonObject *proc = json_object_get_object_member(processes, name);
const gchar *component = json_object_get_string_member(proc, "component");
gchar *opname = component2geglop(component);
GeglNode *node = gegl_node_new_child (gegl, "operation", opname, NULL);
gegl_operation_meta_watch_node (operation, node);
g_assert(node);
g_hash_table_insert(self->nodes, (gpointer)g_strdup(name), (gpointer)node);
g_free(opname);
}
g_list_free(process_names);
// Connections
JsonArray *connections = json_object_get_array_member(root, "connections");
g_assert(connections);
for (int i=0; i<json_array_get_length(connections); i++) {
JsonObject *conn = json_array_get_object_element(connections, i);
JsonObject *tgt = json_object_get_object_member(conn, "tgt");
const gchar *tgt_proc = json_object_get_string_member(tgt, "process");
const gchar *tgt_port = json_object_get_string_member(tgt, "port");
GeglNode *tgt_node = g_hash_table_lookup(self->nodes, tgt_proc);
g_assert(tgt_node);
JsonNode *srcnode = json_object_get_member(conn, "src");
if (srcnode) {
// Connection
JsonObject *src = json_object_get_object_member(conn, "src");
const gchar *src_proc = json_object_get_string_member(src, "process");
const gchar *src_port = json_object_get_string_member(src, "port");
GeglNode *src_node = g_hash_table_lookup(self->nodes, src_proc);
g_assert(src_node);
gegl_node_connect_to (src_node, src_port, tgt_node, tgt_port);
} else {
// IIP
JsonNode *datanode = json_object_get_member(conn, "data");
GValue value = G_VALUE_INIT;
g_assert(JSON_NODE_HOLDS_VALUE(datanode));
json_node_get_value(datanode, &value);
GParamSpec *paramspec = gegl_node_find_property(tgt_node, tgt_port);
set_prop(tgt_node, tgt_port, paramspec, &value);
g_value_unset(&value);
}
}
// Exported ports
if (json_object_has_member(root, "inports")) {
JsonObject *inports = json_object_get_object_member(root, "inports");
GList *inport_names = json_object_get_members(inports);
for (int i=0; i<g_list_length(inport_names); i++) {
const gchar *name = g_list_nth_data(inport_names, i);
JsonObject *conn = json_object_get_object_member(inports, name);
const gchar *proc = json_object_get_string_member(conn, "process");
const gchar *port = json_object_get_string_member(conn, "port");
GeglNode *node = g_hash_table_lookup(self->nodes, proc);
g_assert(node);
if (g_strcmp0(name, "input") == 0) {
GeglNode *input = gegl_node_get_input_proxy (gegl, "input");
gegl_node_connect_to (input, "output", node, "input");
} else {
gegl_operation_meta_redirect (operation, name, node, port);
}
}
}
if (json_object_has_member(root, "outports")) {
JsonObject *outports = json_object_get_object_member(root, "outports");
GList *outport_names = json_object_get_members(outports);
for (int i=0; i<g_list_length(outport_names); i++) {
const gchar *name = g_list_nth_data(outport_names, i);
JsonObject *conn = json_object_get_object_member(outports, name);
const gchar *proc = json_object_get_string_member(conn, "process");
const gchar *port = json_object_get_string_member(conn, "port");
GeglNode *node = g_hash_table_lookup(self->nodes, proc);
g_assert(node);
if (g_strcmp0(name, "output") == 0) {
GeglNode *proxy = gegl_node_get_output_proxy (gegl, "output");
gegl_node_connect_to (node, port, proxy, "input");
} else {
g_warning("Unsupported output '%s' exported in .json file", name);
}
}
}
}
static void
json_op_init (JsonOp *self)
{
self->nodes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
}
static void
finalize (GObject *gobject)
{
JsonOp *self = (JsonOp *)(gobject);
// JsonOpClass *json_op_class = (JsonOpClass *)G_OBJECT_GET_CLASS(gobject);
g_hash_table_unref (self->nodes);
// FIXME: causes infinite loop GEGL_OPERATION_CLASS(json_op_class)->finalize(gobject);
}
/* json_op_class */
static const gchar *
metadata_get_property(JsonObject *root, const gchar *prop) {
if (json_object_has_member(root, "properties")) {
JsonObject *properties = json_object_get_object_member(root, "properties");
if (json_object_has_member(properties, prop)) {
return json_object_get_string_member(properties, prop);
}
}
return NULL;
}
static void
json_op_class_init (gpointer klass, gpointer class_data)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
JsonOpClass *json_op_class = (JsonOpClass *) (klass);
json_op_class->json_root = (JsonObject *) (class_data);
object_class->set_property = set_property;
object_class->get_property = get_property;
object_class->constructor = constructor;
object_class->finalize = finalize;
operation_class->attach = attach;
json_op_class->properties = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify)property_target_free);
install_properties(json_op_class);
const gchar *description = metadata_get_property(json_op_class->json_root, "description");
gchar *name = component2geglop(metadata_get_property(json_op_class->json_root, "name"));
gegl_operation_class_set_keys (operation_class,
"name", (name) ? name : g_strdup_printf("json:%s", G_OBJECT_CLASS_NAME(object_class)),
"categories", "meta:json",
"description", (description) ? description : "",
NULL);
}
static void
json_op_class_finalize (JsonOpClass *self)
{
g_hash_table_unref(self->properties);
}
static GType
json_op_register_type (GTypeModule *type_module, const gchar *name, gpointer klass_data)
{
gint flags = 0;
const GTypeInfo g_define_type_info =
{
sizeof (JsonOpClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) json_op_class_init,
(GClassFinalizeFunc) json_op_class_finalize,
klass_data,
sizeof (JsonOp),
0, /* n_preallocs */
(GInstanceInitFunc) json_op_init,
NULL /* value_table */
};
return g_type_module_register_type (type_module, GEGL_TYPE_OPERATION_META_JSON, name,
&g_define_type_info, (GTypeFlags) flags);
}
static GType
json_op_register_type_for_file (GTypeModule *type_module, const gchar *filepath)
{
GType ret = 0;
GError *error = NULL;
JsonParser *parser = json_parser_new();
const gboolean success = json_parser_load_from_file(parser, filepath, &error);
if (success) {
JsonNode *root_node = json_node_copy (json_parser_get_root (parser));
JsonObject *root = json_node_get_object (root_node);
g_assert(root_node);
const gchar *name = metadata_get_property(root, "name");
gchar *type_name = (name) ? component2gtypename(name) : component2gtypename(filepath);
ret = json_op_register_type(type_module, type_name, root);
g_free(type_name);
}
// g_object_unref(parser);
return ret;
}
/* JSON operation enumeration */
static void
load_file(const GeglDatafileData *file_data, gpointer user_data)
{
GTypeModule *module = (GTypeModule *)user_data;
if (!g_str_has_suffix(file_data->filename, ".json")) {
return;
}
json_op_register_type_for_file(module, file_data->filename);
}
static void
load_path(gchar *path, gpointer user_data)
{
gegl_datafiles_read_directories (path, G_FILE_TEST_EXISTS, load_file, user_data);
}
static void
json_register_operations(GTypeModule *module)
{
GSList *paths = gegl_get_default_module_paths();
g_slist_foreach(paths, (GFunc)load_path, module);
g_slist_free_full(paths, g_free);
}
/*** Module registration ***/
static const GeglModuleInfo modinfo =
{
GEGL_MODULE_ABI_VERSION
};
/* prototypes added to silence warnings from gcc for -Wmissing-prototypes*/
gboolean gegl_module_register (GTypeModule *module);
const GeglModuleInfo * gegl_module_query (GTypeModule *module);
G_MODULE_EXPORT const GeglModuleInfo *
gegl_module_query (GTypeModule *module)
{
return &modinfo;
}
G_MODULE_EXPORT gboolean