Is there a way to programmatically attach filters to layers

… same as calling the filters from the UI with the Merge not checked out?

Maybe related to this: NDE: Filters without a dialog are not added as NDE effects (#10786) · Issues · GNOME / GIMP · GitLab

It’ll be part of the general filter API that Jehan is planning: ScriptFu scripts can't call GEGL filters since PDB compatibility procedures were obsoleted in rc1 (#12279) · Issues · GNOME / GIMP · GitLab

Update: @Jehan has a merge request with example Python API, if you’re interested in seeing what it will look like: Draft: New public API for GimpDrawableFilter (!2008) · Merge requests · GNOME / GIMP · GitLab

Note that:

  • I will also add an API to apply directly a filter (i.e. merging directly a file instead of attaching it) which will replace the various usage of hardcoded legacy filters which we removed (and then we’ll likely remove more/all of them).
  • For bindings, it will be slightly more verbose than for C (where we will be able to have a 1-liner variable arg function for common use cases) because GObject Introspection cannot transform vararg functions to say Python or other scripts. It would still be possible for people to create their own helper function though (I saw a few similar threads recently about such helper functions, I think). We won’t have any in Python officially, because I’d like to avoid per-language custom code (we used to have this in 2.x series for Python but it mostly ended up as unmaintained code for 10+ years).
  • It should eventually be more powerful than running GEGL directly from plug-in code with GEGL API, because it “sees” GIMP’s internal operations too (color balance, levels, etc.) which are not visible from the plug-in process. Right now though I realized that we are not able to configure these (because there is an indirection in core code: args are themselves in a config object which we don’t know how to pass through the protocol yet), but this can eventually be fixed.
1 Like

Currently I use something like this:

#
# Simple-minded GEGL helper for the time being
#
# Sample function to apply a GEGL operation on one drawable
def applyGeglOnBuffers(bufferIn,bufferOut,opName,**kwargs):
    graph=Gegl.Node()
    source=graph.create_child("gegl:buffer-source")
    source.set_property('buffer',bufferIn)
    sink=graph.create_child("gegl:write-buffer")
    sink.set_property('buffer',bufferOut)
    operation=graph.create_child(opName)
    # Iterate named arguments, converting names_with_underscores to names-with-dashes
    for name,value in kwargs.items():
        operation.set_property(name.replace('_','-'),value)
    source.link(operation)
    operation.link(sink)
    sink.process()
    bufferOut.flush()

Used as:

applyGeglOnBuffers(layer.get_buffer(),layer.get_shadow_buffer(),'gegl:gaussian-blur',std_dev_x=sizeX,std_dev_y=sizeY)

So it is quite simple. I’m also thinking about chaining directly GEGL ops if that means a significant gain in performance.

@Ofnuts Your code works fine but with the following limitations:

  • It only works for applying/merging a layer directly into a layer’s buffer. Our new API will allow this and will also allow to create a non-destructive filter attached to a layer (which, unless mistaken, was your initial question).
  • It will also eventually works for GIMP’s internal operations. For instance, your code won’t work with opName="gimp:color-balance" (because this op is not visible on plug-in side) whereas this will work with our API (once I’ll have handled the passing of the config file indirection for these internal ops).
  • You can also change the blending mode and opacity.

With our new API, you’d be able to transform your help function like this to make good use of the 2 first advantages at least (and adding more args, you could support blending mode/opacity too):

def applyGeglOnBuffers(drawable, merge, opName, **kwargs):
  # XXX: the last arg is the filter name; you could set custom names
  # helping making it clearer filters which came from a given script
  # in the effect list.
  filter = Gimp.DrawableFilter.new(drawable, opName, opName)
  config = filter.get_config ()
  for name, value in kwargs.items():
    config.set_property(name.replace('_','-'), value)
  filter.update()
  if merge:
    # Not yet implemented in the MR but will be by RC2:
    drawable.merge_filter(filter)
  else:
    # Non-destructive filter:
    drawable.append_filter(filter)

Of course, this is meant to work on a drawable. If you want to work on a separate buffer (not attached to a drawable) like with your current function, your current code is the right one.

1 Like

Your suggestion is fine. In the code above I left the buffers out because there could be cases where the input and the output aren’t on the same layer.

How would you handle the “render” GEGL ops, those that work without input, such as solid noise and plasma?

Ofnuts last question is one which is a major block to me at the moment trying to port some of my personal plugins that produce textures. (see Creating solid noise in Python plugin). However I have been able to use the gegl plasma function and rectangle function. The problem seems to be with the functions based on the PointRender operation.

This topic was automatically closed 45 days after the last reply. New replies are no longer allowed.