summaryrefslogtreecommitdiff
path: root/src/enhancer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/enhancer.c')
-rw-r--r--src/enhancer.c191
1 files changed, 137 insertions, 54 deletions
diff --git a/src/enhancer.c b/src/enhancer.c
index aea40f9..26e4ca3 100644
--- a/src/enhancer.c
+++ b/src/enhancer.c
@@ -67,40 +67,53 @@ enhancer_get_presets(Enhancer *e) {
return e ? e->p_presets : NULL;
}
+/* Create the GEGL op node for a built-in preset (in p_graph), or NULL if the
+ * name is unknown. */
+static GeglNode *
+_op_for_builtin(GeglNode *p_graph, const char *c_name) {
+ if (g_str_equal(c_name, "Auto-fix")) {
+ return (gegl_node_new_child(p_graph, "operation", "gegl:stretch-contrast",
+ NULL));
+ }
+ if (g_str_equal(c_name, "Brightness")) {
+ return (gegl_node_new_child(p_graph, "operation", "gegl:exposure",
+ "exposure", 0.5, NULL));
+ }
+ if (g_str_equal(c_name, "Contrast")) {
+ return (gegl_node_new_child(p_graph, "operation",
+ "gegl:brightness-contrast", "contrast", 1.3,
+ NULL));
+ }
+ if (g_str_equal(c_name, "Saturation")) {
+ return (gegl_node_new_child(p_graph, "operation", "gegl:saturation",
+ "scale", 1.4, NULL));
+ }
+ if (g_str_equal(c_name, "Warm")) {
+ return (
+ gegl_node_new_child(p_graph, "operation", "gegl:color-enhance", NULL));
+ }
+ if (g_str_equal(c_name, "Cool")) {
+ return (gegl_node_new_child(p_graph, "operation", "gegl:exposure",
+ "exposure", -0.3, NULL));
+ }
+ if (g_str_equal(c_name, "Sharpen")) {
+ return (
+ gegl_node_new_child(p_graph, "operation", "gegl:unsharp-mask", NULL));
+ }
+ if (g_str_equal(c_name, "Denoise")) {
+ return (gegl_node_new_child(p_graph, "operation", "gegl:noise-reduction",
+ NULL));
+ }
+ return (NULL);
+}
+
/* Build and apply a GEGL graph for a built-in preset. */
static GeglBuffer *
_apply_builtin(GeglBuffer *p_in, const char *c_name, GError **p_err) {
GeglNode *p_graph = gegl_node_new();
GeglNode *p_src = gegl_node_new_child(
p_graph, "operation", "gegl:buffer-source", "buffer", p_in, NULL);
- GeglNode *p_op = NULL;
-
- if (g_str_equal(c_name, "Auto-fix")) {
- p_op = gegl_node_new_child(p_graph, "operation", "gegl:stretch-contrast",
- NULL);
- } else if (g_str_equal(c_name, "Brightness")) {
- p_op = gegl_node_new_child(p_graph, "operation", "gegl:exposure",
- "exposure", 0.5, NULL);
- } else if (g_str_equal(c_name, "Contrast")) {
- p_op =
- gegl_node_new_child(p_graph, "operation", "gegl:brightness-contrast",
- "contrast", 1.3, NULL);
- } else if (g_str_equal(c_name, "Saturation")) {
- p_op = gegl_node_new_child(p_graph, "operation", "gegl:saturation",
- "scale", 1.4, NULL);
- } else if (g_str_equal(c_name, "Warm")) {
- p_op =
- gegl_node_new_child(p_graph, "operation", "gegl:color-enhance", NULL);
- } else if (g_str_equal(c_name, "Cool")) {
- p_op = gegl_node_new_child(p_graph, "operation", "gegl:exposure",
- "exposure", -0.3, NULL);
- } else if (g_str_equal(c_name, "Sharpen")) {
- p_op =
- gegl_node_new_child(p_graph, "operation", "gegl:unsharp-mask", NULL);
- } else if (g_str_equal(c_name, "Denoise")) {
- p_op = gegl_node_new_child(p_graph, "operation", "gegl:noise-reduction",
- NULL);
- }
+ GeglNode *p_op = _op_for_builtin(p_graph, c_name);
if (p_op == NULL) {
g_object_unref(p_graph);
@@ -121,6 +134,59 @@ _apply_builtin(GeglBuffer *p_in, const char *c_name, GError **p_err) {
return p_out;
}
+/* Apply a chain of the enabled built-in presets (bit i of u_mask -> preset i)
+ * in array order, composing them. Returns a new buffer, or NULL if no preset
+ * is enabled or any op is unavailable. */
+GeglBuffer *
+enhancer_apply_chain(Enhancer *e, GeglBuffer *p_in, const GPtrArray *p_presets,
+ guint8 u_mask, GError **p_err) {
+ (void)e;
+ g_return_val_if_fail(p_in != NULL, NULL);
+ g_return_val_if_fail(p_presets != NULL, NULL);
+ GeglNode *p_graph = gegl_node_new();
+ GeglNode *p_src = gegl_node_new_child(
+ p_graph, "operation", "gegl:buffer-source", "buffer", p_in, NULL);
+ GeglNode *p_prev = p_src;
+ gboolean b_any = FALSE;
+ for (guint i = 0; i < p_presets->len && i < 8; i++) {
+ if ((u_mask & (guint8)(1u << i)) == 0) {
+ continue;
+ }
+ const EnhancerPreset *p_pr = g_ptr_array_index((GPtrArray *)p_presets, i);
+ if (!p_pr->i_builtin) {
+ g_object_unref(p_graph);
+ g_set_error(p_err, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "enhancer: user graph presets not chainable");
+ return NULL;
+ }
+ GeglNode *p_op = _op_for_builtin(p_graph, p_pr->c_name);
+ if (p_op == NULL) {
+ g_object_unref(p_graph);
+ g_set_error(p_err, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "enhancer: unknown preset '%s'", p_pr->c_name);
+ return NULL;
+ }
+ gegl_node_link(p_prev, p_op);
+ p_prev = p_op;
+ b_any = TRUE;
+ }
+ if (!b_any) {
+ g_object_unref(p_graph);
+ g_set_error(p_err, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "enhancer: no preset enabled");
+ return NULL;
+ }
+ GeglRectangle st_rect = {0, 0, gegl_buffer_get_width(p_in),
+ gegl_buffer_get_height(p_in)};
+ GeglBuffer *p_out = gegl_buffer_new(&st_rect, babl_format("RGBA float"));
+ GeglNode *p_sink = gegl_node_new_child(
+ p_graph, "operation", "gegl:buffer-sink", "buffer", &p_out, NULL);
+ gegl_node_link(p_prev, p_sink);
+ gegl_node_process(p_sink);
+ g_object_unref(p_graph);
+ return p_out;
+}
+
GeglBuffer *
enhancer_apply(Enhancer *e, GeglBuffer *p_in, const EnhancerPreset *p_preset,
GError **p_err) {
@@ -171,20 +237,18 @@ _saver_for_ext(GFile *p_out) {
return c_op;
}
-gboolean
-enhancer_export(Enhancer *e, GeglBuffer *p_in, const EnhancerPreset *p_preset,
- GFile *p_out, GError **p_err) {
- (void)e;
- g_return_val_if_fail(p_in != NULL, FALSE);
- g_return_val_if_fail(p_out != NULL, FALSE);
-
+/* Save p_buf to p_out with the format chosen by the output extension.
+ * ju0: pick the saver by extension (jpg q95 / png / webp). ku0: verify the
+ * save actually produced a non-empty, newer file instead of trusting a
+ * pre-existing path. Returns TRUE on a real write. */
+static gboolean
+_save_buffer(GeglBuffer *p_buf, GFile *p_out, GError **p_err) {
char *c_path = g_file_get_path(p_out);
if (c_path == NULL) {
g_set_error(p_err, G_IO_ERROR, G_IO_ERROR_FAILED,
"enhancer: non-local export path");
return FALSE;
}
-
const char *c_op = _saver_for_ext(p_out);
if (c_op == NULL) {
g_free(c_path);
@@ -192,21 +256,10 @@ enhancer_export(Enhancer *e, GeglBuffer *p_in, const EnhancerPreset *p_preset,
"enhancer: unsupported export extension");
return FALSE;
}
-
- /* Apply the preset first. */
- GeglBuffer *p_buf = enhancer_apply(NULL, p_in, p_preset, p_err);
- if (p_buf == NULL) {
- g_free(c_path);
- return FALSE;
- }
-
- /* ku0: capture the save's real success. Record the output's pre-state so
- * a pre-existing file can't masquerade as a successful save. */
- GStatBuf st_before;
- gboolean b_existed = (g_stat(c_path, &st_before) == 0);
-
- GeglNode *p_graph = gegl_node_new();
- GeglNode *p_src = gegl_node_new_child(
+ GStatBuf st_before;
+ gboolean b_existed = (g_stat(c_path, &st_before) == 0);
+ GeglNode *p_graph = gegl_node_new();
+ GeglNode *p_src = gegl_node_new_child(
p_graph, "operation", "gegl:buffer-source", "buffer", p_buf, NULL);
GeglNode *p_save;
if (g_str_equal(c_op, "gegl:jpg-save")) {
@@ -219,9 +272,6 @@ enhancer_export(Enhancer *e, GeglBuffer *p_in, const EnhancerPreset *p_preset,
gegl_node_link(p_src, p_save);
gegl_node_process(p_save);
g_object_unref(p_graph);
- g_object_unref(p_buf);
-
- /* Verify the save actually produced a non-empty file newer than before. */
GStatBuf st_after;
gboolean b_ok = FALSE;
if (g_stat(c_path, &st_after) == 0 && st_after.st_size > 0) {
@@ -239,6 +289,39 @@ enhancer_export(Enhancer *e, GeglBuffer *p_in, const EnhancerPreset *p_preset,
return TRUE;
}
+gboolean
+enhancer_export(Enhancer *e, GeglBuffer *p_in, const EnhancerPreset *p_preset,
+ GFile *p_out, GError **p_err) {
+ (void)e;
+ g_return_val_if_fail(p_in != NULL, FALSE);
+ g_return_val_if_fail(p_out != NULL, FALSE);
+ GeglBuffer *p_buf = enhancer_apply(NULL, p_in, p_preset, p_err);
+ if (p_buf == NULL) {
+ return FALSE;
+ }
+ gboolean b_ok = _save_buffer(p_buf, p_out, p_err);
+ g_object_unref(p_buf);
+ return b_ok;
+}
+
+/* Export p_in with the enabled-preset chain (u_mask) composed, to p_out. */
+gboolean
+enhancer_export_chain(Enhancer *e, GeglBuffer *p_in, const GPtrArray *p_presets,
+ guint8 u_mask, GFile *p_out, GError **p_err) {
+ (void)e;
+ g_return_val_if_fail(p_in != NULL, FALSE);
+ g_return_val_if_fail(p_out != NULL, FALSE);
+ g_return_val_if_fail(p_presets != NULL, FALSE);
+ GeglBuffer *p_buf =
+ enhancer_apply_chain(NULL, p_in, p_presets, u_mask, p_err);
+ if (p_buf == NULL) {
+ return FALSE;
+ }
+ gboolean b_ok = _save_buffer(p_buf, p_out, p_err);
+ g_object_unref(p_buf);
+ return b_ok;
+}
+
#if GGAZE_HAVE_GEGL
GeglBuffer *