Skip to content

New public API for GimpDrawableFilter

Jehan requested to merge wip/Jehan/run-gegl-op into master

This is the MR WIP to handle #12279 (closed) and #11653.

Now:

  • we will have a GimpDrawableFilter class on libgimp side too;
  • plug-ins can have access to all filters currently set as NDE on a layer with: gimp_drawable_get_filters();
  • filter's information can be queried with gimp_drawable_filter_get_name|operation_name|visible|blend_mode|opacity();
  • visible can be changed with gimp_drawable_filter_set_visible() and it's immediately propagated to core;
  • A filter can be deleted with gimp_drawable_filter_delete() (immediate core change too);
  • Blend mode and opacity can be changed with gimp_drawable_filter_set_blend_mode|opacity() but this change is not immediately propagated; instead you must call gimp_drawable_filter_update() which will sync all these changes, along with GEGL op arguments (see below) in one block, hence avoiding multiple useless intermediate re-rendering when changing several settings;
  • Node/GEGL operation settings are copied in a new type GimpDrawableFilterConfig which you can get with gimp_drawable_filter_get_config(); you may update the arguments but it will only be synced with core at next gimp_drawable_filter_update() call;
  • furthermore a special trick is applied to GeglParamEnum arguments: these are transformed into GimpChoice arguments and therefore settable as semantic strings, since the internal op enum type and values are not visible by calling code, making these args much friendlier to use.

Some example in Python:

>>> img = Gimp.get_images()[0]
>>> l = img.get_layers()[0]
# This layer has 3 filters on it.
>>> l.get_filters()
[<Gimp.DrawableFilter object at 0x7f49cdc27a00 (GimpDrawableFilter at 0x55a2cef9b0e0)>, <Gimp.DrawableFilter object at 0x7f49cd2fdf40 (GimpDrawableFilter at 0x55a2cf56aca0)>, <Gimp.DrawableFilter object at 0x7f49cd2fddc0 (GimpDrawableFilter at 0x55a2cf58bf10)>]
# Lets check the bottom filter
>>> f = l.get_filters()[2]
>>> f.get_name()
'Median Blur'
>>> f.get_operation_name()
'gegl:median-blur'
>>> c = f.get_config()
# 'neighborhood' property is meant to be a GeglMedianBlurNeighborhood but this enum type is not visible
# so libgimp transforms it into a GimpChoice
>>> c.get_property('neighborhood')
'circle'
# Oups typo! Not allowed value.
>>> c.set_property('neighborhood', 'diamand')
<input>:1: Warning: value ""diamand"" of type 'gchararray' is invalid or out of range for property 'neighborhood' of type 'gchararray'
>>> c.set_property('neighborhood', 'diamond')
>>> f.set_opacity(0.5)
# None of the config args nor opacity changes are synced at this point in time, until we update:
>>> f.update()
# Now let's add a new filter (note how it's possible to set a filter's name, which is currently not possible to do through GUI):
>>> f2 = Gimp.DrawableFilter.new(l, "gegl:gaussian-blur", "plop")
>>> l.append_filter(f2)
True

So what I still want to do:

  • We also need a call to merge a filter directly, probably gimp_drawable_merge_filter(f)
  • Right now gimp_drawable_filter_get_config() constructs a config with default values, I must instead set it to current values.
  • For C, we'll want a variable-arg one-liner function, something like maybe gimp_drawable_append_new_filter ('gegl:gaussian-blur', 'std-dev-x', 3.0, 'abyss-policy', 'clamp', NULL) which will create a config, set config args immediately through varargs (unset args left to default), append it and return the new filter object.
  • Like a gimp_drawable_merge_new_filter() too, which is the var-args one-liner for destructive effects.
  • Script-fu should have a special binding of its own (which will likely use the new var-args functions).
  • Add support for setting drawables as aux input to a GimpDrawableFilter in PDB.
  • Make sure that aux input is usable in Script-Fu too.
  • The rest of compat PDB procs usage (cf. #11653) should be replaced by these new script-fu or libgimp functions.
  • As result of previous point, all compat procs can be deleted, hence closing #11653.
  • Delete the plug-in-rotate compat procedure if it has equivalent non-legacy ones?
  • Decide what to do with the 2 autocrop compat procedures. -> #11653
  • Fix the bug mentioned below. -> #12567
  • We should probably support the special config files of GIMP's operations. For instance, trying to get the config of "gimp:color-balance" gets us this WARNING: -> #12568

app/gimp-3.0: Gimp-Plug-In-WARNING: _gimp_param_spec_to_gp_param_def: GParamSpecObject for unsupported type 'GParamObject:GimpColorBalanceConfig'

Maybe we should special-case these by passing through the protocol the args of the config objects instead?


@cmyk.student If you could help me, I have a small issue with current code. Follow these steps:

  1. Open an image;
  2. Make a selection;
  3. Open the Python Console;
  4. Run:
img = Gimp.get_images()[0]; l = img.get_layers()[0]
f = Gimp.DrawableFilter.new(l, "gegl:gaussian-blur", "plop")
l.append_filter(f)
  1. The new filter is added and only applied to your selection. So far so good.
  2. You may remove the selection;
  3. In layer effects list, double-click the new "plop" effect;
  4. Change whatever value (so far looks good in preview);
  5. Click OK.

Result: after editing, the layer effect is still present in the list, but the effect is not visible anymore. You may try editing again the effect's args. Each time the edit dialog is opened, the effect looks like it's working again. But exiting the filter with OK or Cancel, render is broken again. If you save, reload the XCF, it now works fine though.


What I'll probably not do now:

  • We may want to have a gimp_drawable_prepend_filter(), gimp_drawable_insert_filter() (insert at any position) and gimp_drawable_reorder_filter(). TODO later.
  • I wanted to make a function to edit the mask of a filter from a plug-in but I realized that there is a question to handle: what should be the origin of a filter's mask? (0, 0) should be at the layer's (0, 0) rather than the image's origin, right? Can a filter mask be bigger than the layer even (because filter output can grow out of input bounds)? What about the discussions on input vs. output masks (right now only one is used for both use case), which is related to the clipping issue we have on another report? And so on. Before we put these into API which will make them much harder to change, let's take a pause to breath and think how best to handle these concepts. For the time being, filters will just use selection on creation.
Edited by Jehan

Merge request reports

Loading