plug-ins: wavelet-decompose improvements

Add an option to keep the decomposition in a layer group.

Add an option to add layer mask to each scales layers.

Do not use 'new from visible' because it produces unexpected result when
- image is already composed by several layers
- target layer has not the same size as the image canvas
Replaced by succession of layer copy and merge down.
This commit is contained in:
Thomas Manni
2017-09-26 13:19:13 +01:00
parent 92e58c4caf
commit 41a2a3634c

View File

@ -36,6 +36,8 @@
typedef struct
{
gint scales;
gint create_group;
gint create_masks;
} WaveletDecomposeParams;
@ -50,13 +52,17 @@ static void run (const gchar *name,
static void wavelet_blur (gint32 drawable_id,
gint radius);
static gboolean wavelet_decompose_dialog (void);
/* create a few globals, set default values */
static WaveletDecomposeParams wavelet_params =
{
5 /* default scales */
5, /* default scales */
1, /* create group */
0 /* do not add mask by default */
};
const GimpPlugInInfo PLUG_IN_INFO =
@ -76,10 +82,15 @@ query (void)
{
static const GimpParamDef args[] =
{
{ GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
{ GIMP_PDB_IMAGE, "image", "Input image (unused)" },
{ GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
{ GIMP_PDB_INT32, "scales", "Number of scales (1-7)" }
{ GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), "
"RUN-NONINTERACTIVE (1) }" },
{ GIMP_PDB_IMAGE, "image", "Input image (unused)" },
{ GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
{ GIMP_PDB_INT32, "scales", "Number of scales (1-7)" },
{ GIMP_PDB_INT32, "create-group", "Create a layer group to store the "
"decomposition" },
{ GIMP_PDB_INT32, "create-masks", "Add a layer mask to each scales "
"layers" }
};
gimp_install_procedure (PLUG_IN_PROC,
@ -134,13 +145,15 @@ run (const gchar *name,
break;
case GIMP_RUN_NONINTERACTIVE:
if (nparams != 4)
if (nparams != 6)
{
status = GIMP_PDB_CALLING_ERROR;
}
else
{
wavelet_params.scales = param[3].data.d_int32;
wavelet_params.scales = param[3].data.d_int32;
wavelet_params.create_group = param[4].data.d_int32;
wavelet_params.create_masks = param[5].data.d_int32;
}
break;
@ -155,58 +168,89 @@ run (const gchar *name,
if (status == GIMP_PDB_SUCCESS)
{
gint32 *scale_ids;
gint32 original_id;
gint32 new_scale_id;
gint32 parent_id;
gint id;
gimp_progress_init (_("Wavelet-Decompose"));
gimp_image_undo_group_start (image_id);
if (wavelet_params.create_group)
{
gint32 group_id = gimp_layer_group_new (image_id);
gimp_item_set_name (group_id, _("Decomposition"));
gimp_item_set_visible (group_id, FALSE);
gimp_image_insert_layer (image_id, group_id,
gimp_item_get_parent (drawable_id),
gimp_image_get_item_position (image_id,
drawable_id));
parent_id = group_id;
}
else
parent_id = -1;
scale_ids = g_new (gint32, wavelet_params.scales);
original_id = gimp_layer_copy (drawable_id);
gimp_image_insert_layer (image_id, original_id, -1, 0);
new_scale_id = gimp_layer_copy (drawable_id);
gimp_image_insert_layer (image_id, new_scale_id, parent_id,
gimp_image_get_item_position (image_id,
drawable_id));
for (id = 0 ; id < wavelet_params.scales; ++id)
{
gint32 blur_id;
gint32 blur_id, tmp_id;
gchar scale_name[20];
gimp_progress_update ((gdouble) id / (gdouble) wavelet_params.scales);
/* blur */
blur_id = gimp_layer_copy (original_id);
gimp_image_insert_layer (image_id, blur_id, 0,
gimp_image_get_item_position (image_id,
original_id));
wavelet_blur (blur_id, 1 << id);
scale_ids[id] = new_scale_id;
/* grain extract */
gimp_layer_set_mode (blur_id, GIMP_LAYER_MODE_GRAIN_EXTRACT);
/* new from visible */
g_snprintf (scale_name, sizeof (scale_name), "Scale %d", id + 1);
scale_ids[id] = gimp_layer_new_from_visible (image_id, image_id,
scale_name);
gimp_item_set_name (new_scale_id, scale_name);
gimp_image_insert_layer (image_id, scale_ids[id], 0,
tmp_id = gimp_layer_copy (new_scale_id);
gimp_image_insert_layer (image_id, tmp_id, parent_id,
gimp_image_get_item_position (image_id,
blur_id));
gimp_layer_set_mode (scale_ids[id], GIMP_LAYER_MODE_GRAIN_MERGE);
gimp_layer_set_mode (blur_id, GIMP_LAYER_MODE_NORMAL);
gimp_item_set_visible (scale_ids[id], FALSE);
new_scale_id));
wavelet_blur (tmp_id, pow(2.0, id));
gimp_image_remove_layer (image_id, original_id);
blur_id = gimp_layer_copy (tmp_id);
gimp_image_insert_layer (image_id, blur_id, parent_id,
gimp_image_get_item_position (image_id,
tmp_id));
original_id = blur_id;
gimp_layer_set_mode (tmp_id, GIMP_LAYER_MODE_GRAIN_EXTRACT);
new_scale_id = gimp_image_merge_down (image_id, tmp_id,
GIMP_EXPAND_AS_NECESSARY);
scale_ids[id] = new_scale_id;
gimp_item_set_visible (new_scale_id, FALSE);
new_scale_id = blur_id;
}
gimp_item_set_name (original_id, "Residual");
gimp_item_set_name (new_scale_id, "residual");
for (id = 0 ; id < wavelet_params.scales; ++id)
for (id = 0; id < wavelet_params.scales; id++)
{
gimp_image_reorder_item (image_id, scale_ids[id], parent_id,
gimp_image_get_item_position (image_id,
new_scale_id));
gimp_layer_set_mode (scale_ids[id], GIMP_LAYER_MODE_GRAIN_MERGE);
if (wavelet_params.create_masks)
{
gint32 mask_id = gimp_layer_create_mask (scale_ids[id],
GIMP_ADD_MASK_WHITE);
gimp_layer_add_mask (scale_ids[id], mask_id);
}
gimp_item_set_visible (scale_ids[id], TRUE);
}
if (wavelet_params.create_group)
gimp_item_set_visible (parent_id, TRUE);
g_free (scale_ids);
gimp_image_undo_group_end (image_id);
@ -233,23 +277,19 @@ wavelet_blur (gint32 drawable_id,
if (gimp_drawable_mask_intersect (drawable_id, &x, &y, &width, &height))
{
GeglBuffer *buffer;
GeglBuffer *shadow_buffer;
GeglBuffer *buffer = gimp_drawable_get_buffer (drawable_id);
GeglBuffer *shadow = gimp_drawable_get_shadow_buffer (drawable_id);
buffer = gimp_drawable_get_buffer (drawable_id);
shadow_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
gegl_render_op (buffer, shadow_buffer,
gegl_render_op (buffer, shadow,
"gegl:wavelet-blur",
"radius", (gdouble) radius,
NULL);
g_object_unref (shadow_buffer); /* flushes the shadow blur */
g_object_unref (buffer);
gimp_drawable_merge_shadow (drawable_id, TRUE);
gegl_buffer_flush (shadow);
gimp_drawable_merge_shadow (drawable_id, FALSE);
gimp_drawable_update (drawable_id, x, y, width, height);
gimp_displays_flush ();
g_object_unref (buffer);
g_object_unref (shadow);
}
}
@ -259,6 +299,7 @@ wavelet_decompose_dialog (void)
GtkWidget *dialog;
GtkWidget *main_vbox;
GtkWidget *table;
GtkWidget *button;
GtkObject *adj;
gboolean run;
@ -292,6 +333,8 @@ wavelet_decompose_dialog (void)
gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
gtk_widget_show (table);
/* scales */
adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
_("Scales:"), SCALE_WIDTH, ENTRY_WIDTH,
wavelet_params.scales,
@ -303,6 +346,30 @@ wavelet_decompose_dialog (void)
G_CALLBACK (gimp_int_adjustment_update),
&wavelet_params.scales);
/* create group layer */
button = gtk_check_button_new_with_mnemonic (_("Create a layer group to store the decomposition"));
gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
wavelet_params.create_group);
gtk_widget_show (button);
g_signal_connect (button, "toggled",
G_CALLBACK (gimp_toggle_button_update),
&wavelet_params.create_group);
/* create layer masks */
button = gtk_check_button_new_with_mnemonic (_("Add a layer mask to each scales layers"));
gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
wavelet_params.create_masks);
gtk_widget_show (button);
g_signal_connect (button, "toggled",
G_CALLBACK (gimp_toggle_button_update),
&wavelet_params.create_masks);
gtk_widget_show (dialog);
run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);