Request for C code review: gimp-layer-combine-alpha (Script-Fu API helper)

Hi,

I added a new Script-Fu API command to simplify a frequently used operation that was too slow using the existing building blocks.

Previously, to intersect a layer’s alpha with a base layer’s alpha, I used Script-Fu:

;; Clone the base layers alpha to the painting layer
(when trim
(let ((layer-mask (add-mask-to-layer layer ADD-MASK-ALPHA-TRANSFER))
      (base-mask (add-mask-to-layer base-layer ADD-MASK-ALPHA-TRANSFER)))
  (gimp-channel-combine-masks layer-mask base-mask CHANNEL-OP-INTERSECT 0 0)
  (gimp-layer-remove-mask base-layer MASK-APPLY)
  (gimp-layer-remove-mask layer MASK-APPLY)))

Now I have a faster shortcut:

(when trim
  (gimp-layer-combine-alpha layer base-layer CHANNEL-OP-INTERSECT 0 0))

I hacked together the implementation by borrowing from the gimp-channel-combine-masks logic. It does what I need, but I suspect it’s inefficient. I briefly attempted a direct buffer-level solution but didn’t get far. I’d appreciate feedback.

This function is intended for repeatedly trimming excess paint by intersecting a painting layer’s alpha with another layer’s alpha. The constant creation and restoration of masks feels wasteful. Maybe it’s fine. Maybe it’s not.

Any thoughts?

void
gimp_layer_combine_alpha (GimpLayer      *dst_layer,
                          GimpLayer      *src_layer,
                          GimpChannelOps  op,
                          gint            offx,
                          gint            offy)
{
  GimpLayerMask          *dst_mask, *src_mask;
  GeglBuffer             *dst_buffer, *src_buffer;
  GimpChannelCombineData  data;

  g_return_if_fail (GIMP_IS_LAYER (dst_layer));
  g_return_if_fail (GIMP_IS_LAYER (src_layer));
  g_return_if_fail (gimp_drawable_has_alpha (GIMP_DRAWABLE (dst_layer)));
  g_return_if_fail (gimp_drawable_has_alpha (GIMP_DRAWABLE (src_layer)));

  // Create two masks from the layers alpha, alpha gets removed
  dst_mask = gimp_layer_create_mask (dst_layer, GIMP_ADD_MASK_ALPHA_TRANSFER, NULL);
  src_mask = gimp_layer_create_mask (src_layer, GIMP_ADD_MASK_ALPHA_TRANSFER, NULL);

  // Store as buffers for GEGL op
  dst_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (dst_mask));
  src_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (src_mask));

  if (gimp_alpha_combine_start (GIMP_CHANNEL (dst_mask), op,
      GEGL_RECTANGLE (
        offx + gegl_buffer_get_x (src_buffer),
        offy + gegl_buffer_get_y (src_buffer),
        gegl_buffer_get_width  (src_buffer),
        gegl_buffer_get_height (src_buffer)),
      FALSE, FALSE, &data))
    {
      gimp_gegl_mask_combine_buffer (dst_buffer, src_buffer, op, offx, offy);
      gimp_alpha_combine_end (GIMP_CHANNEL (dst_mask), &data);
    }

  // Restore alpha from masks
  gimp_layer_add_mask (dst_layer, dst_mask, FALSE, NULL);
  gimp_layer_apply_mask (dst_layer, GIMP_MASK_APPLY, FALSE);
  gimp_layer_add_mask (src_layer, src_mask, FALSE, NULL);
  gimp_layer_apply_mask (src_layer, GIMP_MASK_APPLY, FALSE);
}

Wouldn’t it be simpler to set the top layer’s Composite mode to Clip to backdrop?

Interesting, not aware of that. Backdrop? is that the layer directly below, or the lowest layer in the group?

Everything under the layer in the same group, in my example above, the backdrop is only the red dot, the Background layer isn’t part of it.

This is very powerful. For instance, in the image below, the cutout is done by the text layer, which remains a text layer, so I can later edit the text, or change the font, or adjust the kerning and see the result in real time.

1 Like

That is useful to know thanks, unfortunately I need to trim many layers to a single “backdrop” layer. So complexity and manual intersecting is still required. If the backdrop layer was only the last layer in a group, then this would be an ideal solution.