It is currently Fri Jun 05, 2026 5:46 pm


All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 11 posts ] 
Author Message
 Post subject: new March 2026 - GEGL Bubble Text (download below)
PostPosted: Mon Mar 23, 2026 7:01 pm  (#1) 
Offline
GimpChat Member
User avatar

Joined: Oct 31, 2020
Posts: 2010
This is a potential plugin I plan on releasing soon. It is not released or even a .c file yet, but a useful graph I made today that looks like a bubble.

I will name it lb:bubble if released


Image
Image

It requires my plugin lb:glassovertext to work but you can test it static here

Graph to test soon to come plugin in static form

id=0
color-overlay value=white

cubism tile-size=2.0 seed=3423
gaussian-blur std-dev-x=2 std-dev-y=2 abyss-policy=none

median-blur radius=2 alpha-percentile=70 abyss-policy=none
id=x
color-overlay value=#98e1ff
bevel type=bump radius=7 elevation=34 depth=11
edge
gray

color-to-alpha color=black
gaussian-blur std-dev-x=1 std-dev-y=1 abyss-policy=none
id=y

dst-over aux=[ ref=y gaussian-blur std-dev-x=7 std-dev-y=7  abyss-policy=none  opacity value=0.11  ]
dst-over aux=[ ref=y color-overlay value=black gaussian-blur std-dev-x=2 std-dev-y=2  abyss-policy=none 
opacity value=1.1 ]

id=gm gimp:layer-mode layer-mode=grain-merge blend-space=rgb-linear opacity=0.45 aux=[  ref=gm
alien-map
color-model=rgb
cpn-1-frequency=3.641
cpn-2-frequency=2.9489999999999998
cpn-3-frequency=7.5529999732971191
cpn-1-phaseshift=0
cpn-2-phaseshift=0
cpn-3-phaseshift=0
cpn-1-keep=no
cpn-2-keep=no
cpn-3-keep=no
]

noise-reduction iterations=4

over aux=[ ref=0 lb:glassovertext azimuth=94 hyperopacity=0.3 ]
dst-over aux=[ ref=0 color-overlay value=#0c82bb gaussian-blur std-dev-x=7 std-dev-y=7  abyss-policy=none opacity value=0.78  ]


Any good, or is this one lousy? Should this transform into a plugin? I think so. Feel free to share feed back both positive and negative. :jumpclap


Last edited by contrast_ on Wed Mar 25, 2026 2:56 am, edited 1 time in total.

Share on Facebook Share on Twitter Share on Orkut Share on Digg Share on MySpace Share on Delicious Share on Technorati
Top
 Post subject: Re: GEGL Bubble Text (COMING SOON)
PostPosted: Mon Mar 23, 2026 9:35 pm  (#2) 
Offline
Global Moderator
User avatar

Joined: May 16, 2010
Posts: 16010
I like it. I like the way it shines kind of purple and pink like a real bubble. Just seems to need to be a little puffier perhaps.Maybe some outer round bevel added possibly?

_________________
Image


Top
 Post subject: Re: GEGL Bubble Text (COMING SOON)
PostPosted: Mon Mar 23, 2026 11:36 pm  (#3) 
Offline
GimpChat Member
User avatar

Joined: Oct 31, 2020
Posts: 2010
I added a gaussian blur before to make it puffer.



Image

if you have port:gegl and lb:glassovertext installed. This is all you need to use it non-destructive as a graph

Image

gaussian-blur std-dev-x=1 std-dev-y=1 abyss-policy=none clip-extent=false
id=0
color-overlay value=white

cubism tile-size=2.0 seed=3423
gaussian-blur std-dev-x=2 std-dev-y=2 abyss-policy=none

median-blur radius=2 alpha-percentile=70 abyss-policy=none
id=x
color-overlay value=#98e1ff
bevel type=bump  blendmode=multiply radius=8 elevation=44 depth=11
edge
gray

color-to-alpha color=black
gaussian-blur std-dev-x=1 std-dev-y=1 abyss-policy=none  clip-extent=false
id=y

dst-over aux=[ ref=y gaussian-blur std-dev-x=7 std-dev-y=7  abyss-policy=none clip-extent=false  opacity value=0.11  ]
dst-over aux=[ ref=y color-overlay value=black gaussian-blur std-dev-x=2 std-dev-y=2  abyss-policy=none   clip-extent=false
opacity value=1.1 ]

id=gm gimp:layer-mode layer-mode=grain-merge blend-space=rgb-linear opacity=0.45 aux=[  ref=gm
alien-map
color-model=rgb
cpn-1-frequency=3.641
cpn-2-frequency=2.9489999999999998
cpn-3-frequency=7.5529999732971191
cpn-1-phaseshift=0
cpn-2-phaseshift=0
cpn-3-phaseshift=0
cpn-1-keep=no
cpn-2-keep=no
cpn-3-keep=no
]

noise-reduction iterations=4

over aux=[ ref=0 lb:glassovertext azimuth=94 hyperopacity=0.3 ]
dst-over aux=[ ref=0 color-overlay value=#0c82bb gaussian-blur std-dev-x=7 std-dev-y=7  abyss-policy=none clip-extent=false opacity value=0.78  ]


GEGL bubble still has not been started as .c code but based on everything I know it might take a day or two or much longer because I'm lazy. Based on previous experience this is a technical GEGL graph but not extremely technical. For example wood text was orders of magnitude more complex then this, where filters like metal text were mid level difficulty, this plugin seems slightly more complicated then metal text but nothing insane like wood text.

Image


Top
 Post subject: Re: GEGL Bubble Text (COMING SOON)
PostPosted: Tue Mar 24, 2026 12:05 am  (#4) 
Offline
GimpChat Member
User avatar

Joined: Oct 31, 2020
Posts: 2010
The plugin has not even started but here is its blank compilable template with a graph reference. I wonder if anyone knows where to begin. I certainly do

/* This file is an image processing operation for GEGL
*
* GEGL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* GEGL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
*
* Credit to Øyvind Kolås (pippin) for major GEGL contributions
* 2026, beaver, Bubble text

requires lb:glassovertext (which it ships with)

gaussian-blur std-dev-x=1 std-dev-y=1 abyss-policy=none clip-extent=false
id=0
color-overlay value=white

cubism tile-size=2.0 seed=3423
gaussian-blur std-dev-x=2 std-dev-y=2 abyss-policy=none

median-blur radius=2 alpha-percentile=70 abyss-policy=none
id=x
color-overlay value=#98e1ff
bevel type=bump  blendmode=multiply radius=8 elevation=44 depth=11
edge
gray

color-to-alpha color=black
gaussian-blur std-dev-x=1 std-dev-y=1 abyss-policy=none  clip-extent=false
id=y

dst-over aux=[ ref=y gaussian-blur std-dev-x=7 std-dev-y=7  abyss-policy=none clip-extent=false  opacity value=0.11  ]
dst-over aux=[ ref=y color-overlay value=black gaussian-blur std-dev-x=2 std-dev-y=2  abyss-policy=none   clip-extent=false
opacity value=1.1 ]

id=gm gimp:layer-mode layer-mode=grain-merge blend-space=rgb-linear opacity=0.45 aux=[  ref=gm
alien-map
color-model=rgb
cpn-1-frequency=3.641
cpn-2-frequency=2.9489999999999998
cpn-3-frequency=7.5529999732971191
cpn-1-phaseshift=0
cpn-2-phaseshift=0
cpn-3-phaseshift=0
cpn-1-keep=no
cpn-2-keep=no
cpn-3-keep=no
]

noise-reduction iterations=4

over aux=[ ref=0 lb:glassovertext azimuth=94 hyperopacity=0.3 ]
dst-over aux=[ ref=0 color-overlay value=#0c82bb gaussian-blur std-dev-x=7 std-dev-y=7  abyss-policy=none clip-extent=false opacity value=0.78  ]

]

end of syntax
*/

#include "config.h"
#include <glib/gi18n-lib.h>

#ifdef GEGL_PROPERTIES

/*Properties go here*/

#else

#define GEGL_OP_META
#define GEGL_OP_NAME     bubblebuddy
#define GEGL_OP_C_SOURCE bubblebuddy.c

#include "gegl-op.h"

/*starred nodes go inside typedef struct */

typedef struct
{
GeglNode *input;
GeglNode *output;
}State;

static void attach (GeglOperation *operation)
{
  GeglNode *gegl = operation->node;
  GeglProperties *o = GEGL_PROPERTIES (operation);


  State *state = o->user_data = g_malloc0 (sizeof (State));

/*new child node list is here, this is where starred nodes get defined

state->newchildname = gegl_node_new_child (gegl, "operation", "lb:name", NULL);*/
  state->input    = gegl_node_get_input_proxy (gegl, "input");
  state->output   = gegl_node_get_output_proxy (gegl, "output");

/*meta redirect property to new child orders go here

gegl_operation_meta_redirect (operation, "propertyname", state->newchildname,  "originalpropertyname");
*/

}

static void
update_graph (GeglOperation *operation)
{
  GeglProperties *o = GEGL_PROPERTIES (operation);
  State *state = o->user_data;
  if (!state) return;

  gegl_node_link_many (state->input,  state->output,  NULL);

/*optional connect from and too is here
  gegl_node_connect (state->blendmode, "aux", state->lastnodeinlist, "output"); */

}

static void
gegl_op_class_init (GeglOpClass *klass)
{
  GeglOperationClass *operation_class;
GeglOperationMetaClass *operation_meta_class = GEGL_OPERATION_META_CLASS (klass);
  operation_class = GEGL_OPERATION_CLASS (klass);

  operation_class->attach = attach;
  operation_meta_class->update = update_graph;

  gegl_operation_class_set_keys (operation_class,
    "name",        "lb:bubble",
    "title",       _("Bubble Text"),
    "reference-hash", "bubblebuddywhodontigetasay",
    "description", _("Make text that looks like soapy bubbles"),
/*<Image>/Colors <Image>/Filters are top level menus in GIMP*/
    "gimp:menu-path", "<Image>/Filters>Text Styling",
    "gimp:menu-label", _("Bubble text..."),
    NULL);
}

#endif


Top
 Post subject: Re: GEGL Bubble Text (COMING SOON)
PostPosted: Tue Mar 24, 2026 7:54 pm  (#5) 
Offline
GimpChat Member
User avatar

Joined: Oct 31, 2020
Posts: 2010
The plugin is 99% done. Compile this yourself and you can use it early.

Image


/* This file is an image processing operation for GEGL
*
* GEGL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* GEGL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
*
* Credit to Øyvind Kolås (pippin) for major GEGL contributions
* 2026, beaver, Bubble text

requires lb:glassovertext (which it ships with)

start of syntax


gaussian-blur std-dev-x=1 std-dev-y=1 abyss-policy=none clip-extent=false
id=0
color-overlay value=white

cubism tile-size=2.0 seed=3423
gaussian-blur std-dev-x=2 std-dev-y=2 abyss-policy=none

median-blur radius=2 alpha-percentile=70 abyss-policy=none
color-overlay value=#98e1ff
bevel type=bump  blendmode=multiply radius=8 elevation=44 depth=11
edge
gray

color-to-alpha color=black
gaussian-blur std-dev-x=1 std-dev-y=1 abyss-policy=none  clip-extent=false
id=y

dst-over aux=[ ref=y gaussian-blur std-dev-x=7 std-dev-y=7  abyss-policy=none clip-extent=false  opacity value=0.11  ]
dst-over aux=[ ref=y color-overlay value=black gaussian-blur std-dev-x=2 std-dev-y=2  abyss-policy=none   clip-extent=false
opacity value=1.1 ]

id=gm gimp:layer-mode layer-mode=grain-merge blend-space=rgb-linear opacity=0.45 aux=[  ref=gm
alien-map
color-model=rgb
cpn-1-frequency=3.641
cpn-2-frequency=2.9489999999999998
cpn-3-frequency=7.5529999732971191
cpn-1-phaseshift=0
cpn-2-phaseshift=0
cpn-3-phaseshift=0
cpn-1-keep=no
cpn-2-keep=no
cpn-3-keep=no
]

noise-reduction iterations=4

over aux=[ ref=0 lb:glassovertext azimuth=94 hyperopacity=0.3 ]
dst-over aux=[ ref=0 color-overlay value=#0c82bb gaussian-blur std-dev-x=7 std-dev-y=7  abyss-policy=none clip-extent=false opacity value=0.78  ]

]

end of syntax
*/

#include "config.h"
#include <glib/gi18n-lib.h>

#ifdef GEGL_PROPERTIES

/*Properties go here*/



enum_start (puff_puffpuff322928)
  enum_value (GEGL_BEVEL_HIGHPUFF, "highpuff",
              N_("High Puff"))
  enum_value (GEGL_BEVEL_LOWPUFF,      "lowpuff",
              N_("Low Puff"))
  enum_value (GEGL_BEVEL_LOWPUFF2,      "lowpuff2",
              N_("Low Puff 2"))
enum_end (puffpuffpuff322928)

property_enum (puffblend, _("Internal Blend/Puff"),
    puffpuffpuff322928, puff_puffpuff322928,
    GEGL_BEVEL_LOWPUFF2)
    description (_("There is no such thing as a perfect bubble text, only trade offs and options that are convenient in some places and bad in others. So try different internal bevel blend modes to have more or less puff"))





property_double (base_blur, _("Base blur"), 2.0)
  value_range (0.0, 4.0)
  ui_range (0.0, 4.0)
  ui_gamma (1.5)
    description (_("Blur at the start of the graph to enhance the bubble"))
  ui_steps      (0.5, 0.50)

property_double (tile_size, _("Base distortion"), 2.5)
    description (_("Base distortion of the bubble"))
    value_range (2.5, 4.0)
    ui_meta     ("unit", "pixel-distance")



property_seed (seed, _("Base distortion seed"), rand)
    description (_("Seed of the base distortion"))



property_double (grow, _("Grow"), 70.0)
  value_range (60.0, 100.0)
  ui_range (1.0, 8.0)
  ui_gamma (1.5)
    description (_("Grow radius of the bubble"))
  ui_steps      (0.5, 0.50)


property_double (elevation, _("Elevation"), 44.0)
  value_range (40.0, 60.0)
  ui_range (40.0, 60.0)
  ui_gamma (1.5)
    description (_("Elevation of the internal bump bevel"))
  ui_steps      (0.5, 0.50)




property_double (azimuth, _("Azimuth"), 94.0)
    description (_("Light angle (degrees)"))
    value_range (0, 360)
    ui_meta ("unit", "degree")
    ui_meta ("direction", "ccw")
  ui_steps      (0.5, 0.50)




property_double (cpn_1_frequency, _("Thin film control 1"), 3.6)
  value_range (3.0, 4.0)
    description (_("To modify the bubble's pseudo thin film interference iridescent properties"))


property_double  (cpn_2_frequency, _("Thin film control 2"), 4)
  value_range (2, 4)
    description (_("To modify the bubble's pseudo thin film interference iridescent properties"))


property_double  (cpn_3_frequency, _("Thin film control 3"), 7.5)
  value_range (7.0, 8.0)
    description (_("To modify the bubble's pseudo thin film interference iridescent properties"))


property_double (bg_opacity, _("Background opacity"), 0.78)
  value_range (0.0, 1.0)
  ui_range (0.0, 1.0)
  ui_gamma (1.5)
    description (_("Background opacity"))
  ui_steps      (0.5, 0.50)


#else

#define GEGL_OP_META
#define GEGL_OP_NAME     bubblebuddy
#define GEGL_OP_C_SOURCE bubblebuddy.c

#include "gegl-op.h"

/*starred nodes go inside typedef struct */

typedef struct
{
GeglNode *input;
GeglNode *gaussian1;
GeglNode *gaussian2;
GeglNode *gaussian3;
GeglNode *makewhite;
GeglNode *coloroverlay1;
GeglNode *coloroverlay2;
GeglNode *cubism;
GeglNode *median;
GeglNode *bevellow;
GeglNode *bevellow2;
GeglNode *bevelhigh;
GeglNode *edge;
GeglNode *gray;
GeglNode *normal;
GeglNode *glass;
GeglNode *nr;
GeglNode *behind;
GeglNode *behind2;
GeglNode *alienmap;
GeglNode *grainmerge;
GeglNode *nopy;
GeglNode *nopgm;
GeglNode *nopconform;
GeglNode *nop0;
GeglNode *graphcapture;
GeglNode *output;
}State;

static void attach (GeglOperation *operation)
{
  GeglNode *gegl = operation->node;
  GeglProperties *o = GEGL_PROPERTIES (operation);
  GeglColor *white = gegl_color_new ("#ffffff");
  GeglColor *secondcolor = gegl_color_new ("#98e1ff");
  GeglColor *thirdcolor = gegl_color_new ("#0c82bb");


  State *state = o->user_data = g_malloc0 (sizeof (State));

/*new child node list is here, this is where starred nodes get defined

state->newchildname = gegl_node_new_child (gegl, "operation", "lb:name", NULL);*/
  state->input    = gegl_node_get_input_proxy (gegl, "input");
  state->output   = gegl_node_get_output_proxy (gegl, "output");



#define largepart \
" edge gray color-to-alpha color=black gaussian-blur std-dev-x=1 std-dev-y=1 abyss-policy=none clip-extent=false id=y dst-over aux=[ ref=y gaussian-blur std-dev-x=7 std-dev-y=7 abyss-policy=none clip-extent=false opacity value=0.11 ] dst-over aux=[ ref=y color-overlay value=black gaussian-blur std-dev-x=2 std-dev-y=2 abyss-policy=none clip-extent=false opacity value=1.1 ] "\

state->nr = gegl_node_new_child (gegl, "operation", "gegl:noise-reduction", "iterations", 4, NULL);

state->graphcapture = gegl_node_new_child (gegl, "operation", "gegl:gegl", "string", largepart, NULL);

state->bevelhigh = gegl_node_new_child (gegl, "operation", "gegl:bevel", "type", 1, "blendmode", 2, "radius", 8.0, "elevation", 44.0, "depth", 11,   NULL);

state->bevellow = gegl_node_new_child (gegl, "operation", "gegl:bevel", "type", 1, "blendmode", 4, "radius", 8.0, "elevation", 44.0, "depth", 11,   NULL);

state->bevellow2 = gegl_node_new_child (gegl, "operation", "gegl:bevel", "type", 1, "blendmode", 1, "radius", 8.0, "elevation", 44.0, "depth", 11,   NULL);

state->cubism = gegl_node_new_child (gegl, "operation", "gegl:cubism", "tile-size", 2.0,  NULL);

state->nopgm = gegl_node_new_child (gegl, "operation", "gegl:nop", NULL);

state->nop0 = gegl_node_new_child (gegl, "operation", "gegl:nop", NULL);

state->nopconform = gegl_node_new_child (gegl, "operation", "gegl:nop", NULL);

state->nopy = gegl_node_new_child (gegl, "operation", "gegl:nop", NULL);

state->nr = gegl_node_new_child (gegl, "operation", "gegl:noise-reduction", "iterations", 4, NULL);

state->gaussian1 = gegl_node_new_child (gegl, "operation", "gegl:gaussian-blur", "std-dev-x", 2.0,  "std-dev-y", 2.0, "clip-extent", FALSE, "abyss-policy", 0,  NULL);

state->gaussian2 = gegl_node_new_child (gegl, "operation", "gegl:gaussian-blur", "std-dev-x", 3.5,  "std-dev-y", 3.5, "clip-extent", FALSE, "abyss-policy", 0,  NULL);

state->gaussian3 = gegl_node_new_child (gegl, "operation", "gegl:gaussian-blur", "std-dev-x", 3.5,  "std-dev-y", 3.5, "clip-extent", FALSE, "abyss-policy", 0,  NULL);

  state->grainmerge    = gegl_node_new_child (gegl,
              "operation", "gimp:layer-mode", "layer-mode", 47, "composite-mode", 0, "blend-space", 2, "opacity", 0.45,
              NULL);

  state->behind    = gegl_node_new_child (gegl,
              "operation", "gimp:layer-mode", "layer-mode", 29, "composite-mode", 0, "opacity", 0.78,
              NULL);

state->median = gegl_node_new_child (gegl, "operation", "gegl:median-blur", "radius", 2, "alpha-percentile", 70.0, "abyss-policy", 0, NULL);

state->normal = gegl_node_new_child (gegl, "operation", "gegl:over", NULL);

state->makewhite = gegl_node_new_child (gegl, "operation", "gegl:color-overlay", "value", white, NULL);

state->coloroverlay1 = gegl_node_new_child (gegl, "operation", "gegl:color-overlay", "value", secondcolor, NULL);

state->coloroverlay2 = gegl_node_new_child (gegl, "operation", "gegl:color-overlay", "value", thirdcolor, NULL);

state->glass = gegl_node_new_child (gegl, "operation", "lb:glassovertext", "azimuth", 94.0, "hyperopacity", 0.3, "elevation", 45.0, NULL);
   

state->alienmap = gegl_node_new_child (gegl, "operation", "gegl:alien-map", "cpn-1-frequency", 3.6, "cpn-2-frequency", 2.9, "cpn-3-frequency", 7.5,  NULL);



/*meta redirect property to new child orders go here

gegl_operation_meta_redirect (operation, "propertyname", state->newchildname,  "originalpropertyname");
*/

gegl_operation_meta_redirect (operation, "cpn_1_frequency", state->alienmap,  "cpn_1_frequency");
gegl_operation_meta_redirect (operation, "cpn_2_frequency", state->alienmap,  "cpn_2_frequency");
gegl_operation_meta_redirect (operation, "cpn_3_frequency", state->alienmap,  "cpn_3_frequency");
gegl_operation_meta_redirect (operation, "azimuth", state->glass,  "azimuth");
gegl_operation_meta_redirect (operation, "seed", state->cubism,  "seed");
gegl_operation_meta_redirect (operation, "tile-size", state->cubism,  "tile-size");
gegl_operation_meta_redirect (operation, "elevation", state->bevelhigh,  "elevation");
gegl_operation_meta_redirect (operation, "elevation", state->bevellow,  "elevation");
gegl_operation_meta_redirect (operation, "elevation", state->bevellow2,  "elevation");
gegl_operation_meta_redirect (operation, "base-blur", state->gaussian1,  "std-dev-x");
gegl_operation_meta_redirect (operation, "base-blur", state->gaussian1,  "std-dev-y");
gegl_operation_meta_redirect (operation, "grow", state->median,  "alpha-percentile");
gegl_operation_meta_redirect (operation, "bg_opacity", state->behind,  "opacity");
}

static void
update_graph (GeglOperation *operation)
{
  GeglProperties *o = GEGL_PROPERTIES (operation);
  State *state = o->user_data;
  if (!state) return;
  GeglNode *bevel = state->bevellow2;

  switch (o->puffblend) {
    case GEGL_BEVEL_HIGHPUFF: bevel = state->bevelhigh; break;
    case GEGL_BEVEL_LOWPUFF: bevel = state->bevellow; break;
    case GEGL_BEVEL_LOWPUFF2: bevel = state->bevellow2; break;
default: bevel = state->bevellow2;

}

  gegl_node_link_many (state->input, state->gaussian1, state->nop0, state->makewhite, state->cubism, state->median,  state->nopconform,  state->gaussian2,  state->coloroverlay1, bevel, state->graphcapture, state->nopgm, state->grainmerge, state->nr, state->normal, state->behind, state->output,  NULL);

  gegl_node_link_many (state->nopgm,  state->alienmap, NULL);
  gegl_node_connect (state->grainmerge, "aux", state->alienmap, "output");

  gegl_node_link_many (state->nop0,  state->glass, NULL);
  gegl_node_connect (state->normal, "aux", state->glass, "output");
  gegl_node_connect (state->behind, "aux", state->gaussian3, "output");
  gegl_node_link_many (state->nopconform, state->coloroverlay2, state->gaussian3, NULL);
}



static void
gegl_op_class_init (GeglOpClass *klass)
{
  GeglOperationClass *operation_class;
GeglOperationMetaClass *operation_meta_class = GEGL_OPERATION_META_CLASS (klass);
  operation_class = GEGL_OPERATION_CLASS (klass);

  operation_class->attach = attach;
  operation_meta_class->update = update_graph;

  gegl_operation_class_set_keys (operation_class,
    "name",        "lb:bubble",
    "title",       _("Bubble Text"),
    "reference-hash", "bubblebuddyheydontigetasayinthis",
    "description", _("Make text that looks like soapy bubbles"),
/*<Image>/Colors <Image>/Filters are top level menus in GIMP*/
    "gimp:menu-path", "<Image>/Filters/Text Styling",
    "gimp:menu-label", _("Bubble text..."),
    NULL);
}

#endif




and

/* This file is an image processing operation for GEGL
*
* GEGL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* GEGL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
*
* Credit to Øyvind Kolås (pippin) for major GEGL contributions
* Glass over Text 2023 Beaver
*/

/*GEGL Glass Over Text was once just a stand alone plugin, but now it is also part of GEGL Effects. The stand alone version still exist and does more then the GEGL Effects implementation of it. */


/*
GEGL Graph Below
*/

/*
color-overlay  value=#ffffff
gaussian-blur std-dev-x=1 std-dev-y=1
emboss azimuth=44 depth=29
id=1
gimp:layer-mode layer-mode=erase composite-mode=auto aux=[ ref=1 dst-over aux=[ color value=#000000  ]  crop  color-to-alpha opacity-threshold=0.14 color-overlay value=#000000 ]  ]
color-overlay value=#ffffff
gaussian-blur std-dev-x=0 std-dev-y=0
*/


/*GEGL Graph here. NOTE FOR DEVS replacing"gimp:layer-mode layer-mode=erase with gegl:xor does not work proper. I tried and you can try it too and watch it fail. GEGL blend modes are not as good as
Gimp blend modes  */

#include "config.h"
#include <glib/gi18n-lib.h>

#ifdef GEGL_PROPERTIES

/*This is the enum list of glass text's blend mode switcher.*/
enum_start (gegl_blend_mode_type_glasstext)
  enum_value (GEGL_BLEND_MODE_TYPE_GLASSOVER,      "Replace",
              N_("Replace"))
  enum_value (GEGL_BLEND_MODE_TYPE_GRAINMERGEGLASS,      "GrainMerge",
              N_("GrainMerge"))
  enum_value (GEGL_BLEND_MODE_TYPE_LINEARLIGHTGLASS,      "LinearLight",
              N_("LinearLight"))
  enum_value (GEGL_BLEND_MODE_TYPE_SUBTRACTGLASS,      "Subtract",
              N_("Subtract"))
  enum_value (GEGL_BLEND_MODE_TYPE_OVERLAYGLASS, "Overlay",
              N_("Overlay"))
  enum_value (GEGL_BLEND_MODE_TYPE_NORMALGLASS, "Normal",
              N_("Normal"))
enum_end (geglblendmodetypeglasstext)



property_enum (glassover, _("Blend Mode of Glass on Text"),
geglblendmodetypeglasstext, gegl_blend_mode_type_glasstext,
    GEGL_BLEND_MODE_TYPE_GLASSOVER)
    ui_meta     ("role", "output-extent")


property_boolean (enableglasstext, _("Enable Glass on Text for GEGL Effects"), TRUE)
  description    (_("This option is only for GEGL Effects"))
    ui_meta     ("role", "output-extent")
/*     ui_meta     ("role", "output-extent") hides this from showing up in the GUI */





#define TUTORIALGLASSTEXT \
" id=1 gimp:layer-mode layer-mode=erase composite-mode=auto aux=[ ref=1 dst-over aux=[ color value=#000000  ]   color-to-alpha opacity-threshold=0.14 color-overlay value=#000000 ]  ]  "\

property_double (azimuth, _("Azimuth"), 30.0)
    description (_("Light angle (degrees)"))
    value_range (0, 360)
    ui_meta ("unit", "degree")
    ui_meta ("direction", "ccw")
  ui_steps      (0.5, 0.50)


property_int (depth, _("Depth"), 20.0)
    description (_("Filter width"))
    value_range (10, 100)

property_double (elevation, _("Elevation"), 45.0)
    description (_("Elevation angle (degrees)"))
    value_range (40, 46)
    ui_meta ("unit", "degree")
  ui_steps      (0.1, 0.50)


property_double (std_dev, _("Retract Shine"), 3.0)
   description (_("Add a Gaussian Blur"))
   value_range (1, 3)
   ui_range    (1, 3)
   ui_gamma    (3.0)
   ui_meta     ("unit", "pixel-distance")
  ui_steps      (0.1, 0.50)

property_double (blur, _("Blur Shine"), 0.5)
   description (_("Add a Gaussian Blur"))
   value_range (0.5, 1)
   ui_range    (0.5, 1)
   ui_gamma    (3.0)
  ui_steps      (0.1, 0.50)
   ui_meta     ("unit", "pixel-distance")

property_color (color, _("Color"), "#ffffff")
    description (_("The color to paint over the input"))

property_double (hyperopacity, _("Hyper Opacity"), 1.0)
    description (_("Opacity Above 100 to make effect more noticable"))
    value_range (0.3, 1.5)
    ui_range    (1.0, 1.5)
  ui_steps      (0.1, 0.50)


#else

#define GEGL_OP_META
#define GEGL_OP_NAME     glassovertext
#define GEGL_OP_C_SOURCE glassovertext.c

#include "gegl-op.h"

typedef struct
{
  GeglNode *input;
  GeglNode *emboss;
  GeglNode *color;
  GeglNode *white;
  GeglNode *retract;
  GeglNode *gaussian;
  GeglNode *hyperopacity;
  GeglNode *string;
  GeglNode *normal;
  GeglNode *glassover;
  GeglNode *glassgrainmerge;
  GeglNode *glasslinearlight;
  GeglNode *glasssubtract;
  GeglNode *glassoverlay;
  GeglNode *output;
}State;



static void
update_graph (GeglOperation *operation)
{
  GeglProperties *o = GEGL_PROPERTIES (operation);
  State *state = o->user_data;
  if (!state) return;

  GeglNode *blendglass = state->glassover; /* the default */
  switch (o->glassover) {
    case GEGL_BLEND_MODE_TYPE_GLASSOVER: blendglass = state->glassover; break;
    case GEGL_BLEND_MODE_TYPE_GRAINMERGEGLASS: blendglass = state->glassgrainmerge; break;
    case GEGL_BLEND_MODE_TYPE_LINEARLIGHTGLASS: blendglass = state->glasslinearlight; break;
    case GEGL_BLEND_MODE_TYPE_SUBTRACTGLASS: blendglass = state->glasssubtract; break;
    case GEGL_BLEND_MODE_TYPE_OVERLAYGLASS: blendglass = state->glassoverlay; break;
    case GEGL_BLEND_MODE_TYPE_NORMALGLASS: blendglass = state->normal; break;
default: blendglass = state->glassover;

}
  if (o->enableglasstext)
  {
  gegl_node_link_many (state->input, blendglass, state->output,  NULL);
  gegl_node_link_many (state->input, state->white, state->retract, state->emboss, state->string, state->color, state->hyperopacity, state->gaussian, NULL);
  gegl_node_connect (blendglass, "aux", state->gaussian, "output");
  }
else
  gegl_node_link_many (state->input, state->output,  NULL);
}


static void attach (GeglOperation *operation)
{
GeglProperties *o = GEGL_PROPERTIES (operation);
  GeglNode *gegl = operation->node;
  GeglNode *input, *output, *emboss, *color, *glassover, *normal,  *glassgrainmerge, *glasslinearlight, *glasssubtract, *glassoverlay, *white, *retract, *gaussian, *hyperopacity, *string;
  GeglColor *white_color = gegl_color_new ("rgb(1.1,1.1,1.1)");


  input    = gegl_node_get_input_proxy (gegl, "input");
  output   = gegl_node_get_output_proxy (gegl, "output");

  normal = gegl_node_new_child (gegl,
                              "operation", "gimp:layer-mode", "layer-mode", 28,  NULL);


  emboss = gegl_node_new_child (gegl,
                                  "operation", "gegl:emboss",
                                  NULL);

  string = gegl_node_new_child (gegl,
                                  "operation", "gegl:gegl", "string", TUTORIALGLASSTEXT,
                                  NULL);

  hyperopacity = gegl_node_new_child (gegl,
                                  "operation", "gegl:opacity",
                                  NULL);

  retract    = gegl_node_new_child (gegl,
                                  "operation", "gegl:gaussian-blur", "clip-extent", FALSE,  "abyss-policy", 0,
                                  NULL);

  gaussian    = gegl_node_new_child (gegl,
                                  "operation", "gegl:gaussian-blur", "clip-extent", FALSE,  "abyss-policy", 0,
                                  NULL);


  color    = gegl_node_new_child (gegl,
                                  "operation", "gegl:color-overlay",
                                  NULL);


  white     = gegl_node_new_child (gegl, "operation", "gegl:color-overlay",
                                   "value", white_color,
                                  NULL);


  /* Glass Over Text blend modes for GEGL Effects */

  glassover = gegl_node_new_child (gegl,
                                  "operation", "gegl:src-in",
                                  NULL);

glasslinearlight = gegl_node_new_child (gegl,
                              "operation", "gimp:layer-mode", "layer-mode", 50, "composite-mode", 0, "blend-space", 3, NULL);

glassgrainmerge = gegl_node_new_child (gegl,
                              "operation", "gimp:layer-mode", "layer-mode", 47, "composite-mode", 0, "blend-space", 3, NULL);

glasssubtract = gegl_node_new_child (gegl,
                              "operation", "gimp:layer-mode", "layer-mode", 34, "composite-mode", 0, NULL);

glassoverlay = gegl_node_new_child (gegl,
                              "operation", "gimp:layer-mode", "layer-mode", 23, "composite-mode", 0, NULL);



  gegl_operation_meta_redirect (operation, "std_dev", retract, "std-dev-x");
  gegl_operation_meta_redirect (operation, "std_dev", retract, "std-dev-y");
  gegl_operation_meta_redirect (operation, "blur", gaussian, "std-dev-x");
  gegl_operation_meta_redirect (operation, "blur", gaussian, "std-dev-y");
  gegl_operation_meta_redirect (operation, "azimuth", emboss, "azimuth");
  gegl_operation_meta_redirect (operation, "elevation", emboss, "elevation");
  gegl_operation_meta_redirect (operation, "depth", emboss, "depth");
  gegl_operation_meta_redirect (operation, "color", color, "value");
  gegl_operation_meta_redirect (operation, "hyperopacity", hyperopacity, "value");


  /* now save references to the gegl nodes so we can use them
   * later, when update_graph() is called
   */
  State *state = g_malloc0 (sizeof (State));
  state->input = input;
  state->white = white;
  state->retract = retract;
  state->emboss = emboss;
  state->string = string;
  state->normal = normal;
  state->color = color;
  state->hyperopacity = hyperopacity;
  state->gaussian = gaussian;
  state->glassover = glassover;
  state->glassgrainmerge = glassgrainmerge;
  state->glasslinearlight = glasslinearlight;
  state->glasssubtract = glasssubtract;
  state->glassoverlay = glassoverlay;
  state->output = output;

  o->user_data = state;



}

static void
gegl_op_class_init (GeglOpClass *klass)
{
  GeglOperationClass *operation_class;
GeglOperationMetaClass *operation_meta_class = GEGL_OPERATION_META_CLASS (klass);
  operation_class = GEGL_OPERATION_CLASS (klass);

  operation_class->attach = attach;
  operation_meta_class->update = update_graph;

  gegl_operation_class_set_keys (operation_class,
    "name",        "lb:glassovertext",
    "title",       _("Glass Over Text"),
    "reference-hash", "stglass238a5125700fc254001b2ac",
    "description", _("Glass Over Text Effect - Filter expects user to place effect on layer above or use the normal blend mode. It may even be a good idea to experiment with other blend modes."),
    "gimp:menu-path", "<Image>/Filters/Text Styling",
    "gimp:menu-label", _("Glass over text (to blend)..."),
    NULL);
}

#endif


Top
 Post subject: Re: GEGL Bubble Text (COMING SOON)
PostPosted: Tue Mar 24, 2026 11:22 pm  (#6) 
Offline
GimpChat Member
User avatar

Joined: Oct 31, 2020
Posts: 2010
Image

Image


Image


updated code, it works even better


/* This file is an image processing operation for GEGL
*
* GEGL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* GEGL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
*
* Credit to Øyvind Kolås (pippin) for major GEGL contributions
* 2026, beaver, Bubble text

requires lb:glassovertext (which it ships with)

start of syntax


gaussian-blur std-dev-x=1 std-dev-y=1 abyss-policy=none clip-extent=false
id=0
color-overlay value=white

cubism tile-size=2.0 seed=3423
gaussian-blur std-dev-x=2 std-dev-y=2 abyss-policy=none

median-blur radius=2 alpha-percentile=70 abyss-policy=none
color-overlay value=#98e1ff
bevel type=bump  blendmode=multiply radius=8 elevation=44 depth=11
edge
gray

color-to-alpha color=black
gaussian-blur std-dev-x=1 std-dev-y=1 abyss-policy=none  clip-extent=false
id=y

dst-over aux=[ ref=y gaussian-blur std-dev-x=7 std-dev-y=7  abyss-policy=none clip-extent=false  opacity value=0.11  ]
dst-over aux=[ ref=y color-overlay value=black gaussian-blur std-dev-x=2 std-dev-y=2  abyss-policy=none   clip-extent=false
opacity value=1.1 ]

id=gm gimp:layer-mode layer-mode=grain-merge blend-space=rgb-linear opacity=0.45 aux=[  ref=gm
alien-map
color-model=rgb
cpn-1-frequency=3.641
cpn-2-frequency=2.9489999999999998
cpn-3-frequency=7.5529999732971191
cpn-1-phaseshift=0
cpn-2-phaseshift=0
cpn-3-phaseshift=0
cpn-1-keep=no
cpn-2-keep=no
cpn-3-keep=no
]

noise-reduction iterations=4

over aux=[ ref=0 lb:glassovertext azimuth=94 hyperopacity=0.3 ]
dst-over aux=[ ref=0 color-overlay value=#0c82bb gaussian-blur std-dev-x=7 std-dev-y=7  abyss-policy=none clip-extent=false opacity value=0.78  ]

]

end of syntax
*/

#include "config.h"
#include <glib/gi18n-lib.h>

#ifdef GEGL_PROPERTIES

/*Properties go here*/



enum_start (puff_puffpuff322928)
  enum_value (GEGL_BEVEL_HIGHPUFF, "highpuff",
         N_("High Puff"))
  enum_value (GEGL_BEVEL_LOWPUFF,      "lowpuff",
         N_("Low Puff"))
  enum_value (GEGL_BEVEL_LOWPUFF2,      "lowpuff2",
         N_("Low Puff 2"))
enum_end (puffpuffpuff322928)

property_enum (puffblend, _("Internal Blend/Puff"),
    puffpuffpuff322928, puff_puffpuff322928,
    GEGL_BEVEL_HIGHPUFF)
    description (_("There is no such thing as a perfect bubble text, only trade offs and options that are convenient in some places and bad in others. So try different internal bevel blend modes to have more or less puff"))





property_double (base_blur, _("Base blur"), 2.0)
  value_range (0.0, 4.0)
  ui_range (0.0, 4.0)
  ui_gamma (1.5)
    description (_("Blur at the start of the graph to enhance the bubble"))
  ui_steps      (0.5, 0.50)

property_double (tile_size, _("Base distortion"), 2.5)
    description (_("Base distortion of the bubble"))
    value_range (2.5, 4.0)
    ui_meta     ("unit", "pixel-distance")



property_seed (seed, _("Base distortion seed"), rand)
    description (_("Seed of the base distortion"))



property_double (grow, _("Grow"), 80.0)
  value_range (70.0, 100.0)
  ui_range (1.0, 8.0)
  ui_gamma (1.5)
    description (_("Grow radius of the bubble"))
  ui_steps      (0.5, 0.50)


property_double (elevation, _("Elevation"), 44.0)
  value_range (40.0, 60.0)
  ui_range (40.0, 60.0)
  ui_gamma (1.5)
    description (_("Elevation of the internal bump bevel"))
  ui_steps      (0.5, 0.50)




property_double (azimuth, _("Azimuth"), 94.0)
    description (_("Light angle (degrees)"))
    value_range (0, 360)
    ui_meta ("unit", "degree")
    ui_meta ("direction", "ccw")
  ui_steps      (0.5, 0.50)




property_double (cpn_1_frequency, _("Thin film control 1"), 3.6)
  value_range (3.0, 30.0)
    description (_("To modify the bubble's pseudo thin film interference iridescent properties"))


property_double  (cpn_2_frequency, _("Thin film control 2"), 4.0)
  value_range (2.0, 25.0)
    description (_("To modify the bubble's pseudo thin film interference iridescent properties"))


property_double  (cpn_3_frequency, _("Thin film control 3"), 7.5)
  value_range (3.0, 42.0)
    description (_("To modify the bubble's pseudo thin film interference iridescent properties"))

property_double (bg_glow, _("Background blur"), 3.5)
  value_range (0.0, 7.0)
  ui_range (0.0, 7.0)
  ui_gamma (1.0)
    description (_("Background blur"))
  ui_steps      (0.5, 0.50)

property_double (bg_opacity, _("Background opacity"), 0.78)
  value_range (0.0, 1.0)
  ui_range (0.0, 1.0)
  ui_gamma (1.0)
    description (_("Background opacity"))
  ui_steps      (0.5, 0.50)


#else

#define GEGL_OP_META
#define GEGL_OP_NAME     bubblebuddy
#define GEGL_OP_C_SOURCE bubblebuddy.c

#include "gegl-op.h"

/*starred nodes go inside typedef struct */

typedef struct
{
GeglNode *input;
GeglNode *gaussian1;
GeglNode *gaussian2;
GeglNode *gaussian3;
GeglNode *makewhite;
GeglNode *coloroverlay1;
GeglNode *coloroverlay2;
GeglNode *cubism;
GeglNode *median;
GeglNode *bevellow;
GeglNode *bevellow2;
GeglNode *bevelhigh;
GeglNode *edge;
GeglNode *gray;
GeglNode *normal;
GeglNode *glass;
GeglNode *nr;
GeglNode *behind;
GeglNode *behind2;
GeglNode *alienmap;
GeglNode *grainmerge;
GeglNode *nopy;
GeglNode *nopgm;
GeglNode *nopconform;
GeglNode *nop0;
GeglNode *graphcapture;
GeglNode *output;
}State;

static void attach (GeglOperation *operation)
{
  GeglNode *gegl = operation->node;
  GeglProperties *o = GEGL_PROPERTIES (operation);
  GeglColor *white = gegl_color_new ("#ffffff");
  GeglColor *secondcolor = gegl_color_new ("#98e1ff");
  GeglColor *thirdcolor = gegl_color_new ("#0c82bb");


  State *state = o->user_data = g_malloc0 (sizeof (State));

/*new child node list is here, this is where starred nodes get defined

state->newchildname = gegl_node_new_child (gegl, "operation", "lb:name", NULL);*/
  state->input    = gegl_node_get_input_proxy (gegl, "input");
  state->output   = gegl_node_get_output_proxy (gegl, "output");



#define largepart \
" edge gray color-to-alpha color=black gaussian-blur std-dev-x=1 std-dev-y=1 abyss-policy=none clip-extent=false id=y dst-over aux=[ ref=y gaussian-blur std-dev-x=7 std-dev-y=7 abyss-policy=none clip-extent=false opacity value=0.11 ] dst-over aux=[ ref=y color-overlay value=black gaussian-blur std-dev-x=2 std-dev-y=2 abyss-policy=none clip-extent=false opacity value=1.1 ] "\

state->nr = gegl_node_new_child (gegl, "operation", "gegl:noise-reduction", "iterations", 4, NULL);

state->graphcapture = gegl_node_new_child (gegl, "operation", "gegl:gegl", "string", largepart, NULL);

state->bevelhigh = gegl_node_new_child (gegl, "operation", "gegl:bevel", "type", 1, "blendmode", 2, "radius", 8.0, "elevation", 44.0, "depth", 11,   NULL);

state->bevellow = gegl_node_new_child (gegl, "operation", "gegl:bevel", "type", 1, "blendmode", 4, "radius", 8.0, "elevation", 44.0, "depth", 11,   NULL);

state->bevellow2 = gegl_node_new_child (gegl, "operation", "gegl:bevel", "type", 1, "blendmode", 1, "radius", 8.0, "elevation", 44.0, "depth", 11,   NULL);

state->cubism = gegl_node_new_child (gegl, "operation", "gegl:cubism", "tile-size", 2.0,  NULL);

state->nopgm = gegl_node_new_child (gegl, "operation", "gegl:nop", NULL);

state->nop0 = gegl_node_new_child (gegl, "operation", "gegl:nop", NULL);

state->nopconform = gegl_node_new_child (gegl, "operation", "gegl:nop", NULL);

state->nopy = gegl_node_new_child (gegl, "operation", "gegl:nop", NULL);

state->nr = gegl_node_new_child (gegl, "operation", "gegl:noise-reduction", "iterations", 4, NULL);

state->gaussian1 = gegl_node_new_child (gegl, "operation", "gegl:gaussian-blur", "std-dev-x", 2.0,  "std-dev-y", 2.0, "clip-extent", FALSE, "abyss-policy", 0,  NULL);

state->gaussian2 = gegl_node_new_child (gegl, "operation", "gegl:gaussian-blur", "std-dev-x", 3.0,  "std-dev-y", 3.0, "clip-extent", FALSE, "abyss-policy", 0,  NULL);

state->gaussian3 = gegl_node_new_child (gegl, "operation", "gegl:gaussian-blur", "std-dev-x", 3.0,  "std-dev-y", 3.0, "clip-extent", FALSE, "abyss-policy", 0,  NULL);

  state->grainmerge    = gegl_node_new_child (gegl,
              "operation", "gimp:layer-mode", "layer-mode", 47, "composite-mode", 0, "blend-space", 2, "opacity", 0.45,
              NULL);

  state->behind    = gegl_node_new_child (gegl,
              "operation", "gimp:layer-mode", "layer-mode", 29, "composite-mode", 0, "opacity", 0.78,
              NULL);

state->median = gegl_node_new_child (gegl, "operation", "gegl:median-blur", "radius", 2, "alpha-percentile", 80.0, "abyss-policy", 0, NULL);

state->normal = gegl_node_new_child (gegl, "operation", "gegl:over", NULL);

state->makewhite = gegl_node_new_child (gegl, "operation", "gegl:color-overlay", "value", white, NULL);

state->coloroverlay1 = gegl_node_new_child (gegl, "operation", "gegl:color-overlay", "value", secondcolor, NULL);

state->coloroverlay2 = gegl_node_new_child (gegl, "operation", "gegl:color-overlay", "value", thirdcolor, NULL);

state->glass = gegl_node_new_child (gegl, "operation", "lb:glassovertext", "azimuth", 94.0, "hyperopacity", 0.3, "elevation", 45.0, NULL);
   

state->alienmap = gegl_node_new_child (gegl, "operation", "gegl:alien-map", "cpn-1-frequency", 3.6, "cpn-2-frequency", 2.9, "cpn-3-frequency", 7.5,  NULL);



/*meta redirect property to new child orders go here

gegl_operation_meta_redirect (operation, "propertyname", state->newchildname,  "originalpropertyname");
*/

gegl_operation_meta_redirect (operation, "cpn_1_frequency", state->alienmap,  "cpn_1_frequency");
gegl_operation_meta_redirect (operation, "cpn_2_frequency", state->alienmap,  "cpn_2_frequency");
gegl_operation_meta_redirect (operation, "cpn_3_frequency", state->alienmap,  "cpn_3_frequency");
gegl_operation_meta_redirect (operation, "azimuth", state->glass,  "azimuth");
gegl_operation_meta_redirect (operation, "seed", state->cubism,  "seed");
gegl_operation_meta_redirect (operation, "tile-size", state->cubism,  "tile-size");
gegl_operation_meta_redirect (operation, "elevation", state->bevelhigh,  "elevation");
gegl_operation_meta_redirect (operation, "elevation", state->bevellow,  "elevation");
gegl_operation_meta_redirect (operation, "elevation", state->bevellow2,  "elevation");
gegl_operation_meta_redirect (operation, "base-blur", state->gaussian1,  "std-dev-x");
gegl_operation_meta_redirect (operation, "base-blur", state->gaussian1,  "std-dev-y");
gegl_operation_meta_redirect (operation, "grow", state->median,  "alpha-percentile");
gegl_operation_meta_redirect (operation, "bg_opacity", state->behind,  "opacity");
gegl_operation_meta_redirect (operation, "bg_glow", state->gaussian2,  "std-dev-x");
gegl_operation_meta_redirect (operation, "bg_glow", state->gaussian2,  "std-dev-y");
gegl_operation_meta_redirect (operation, "bg_glow", state->gaussian3,  "std-dev-x");
gegl_operation_meta_redirect (operation, "bg_glow", state->gaussian3,  "std-dev-y");

}

static void
update_graph (GeglOperation *operation)
{
  GeglProperties *o = GEGL_PROPERTIES (operation);
  State *state = o->user_data;
  if (!state) return;
  GeglNode *bevel = state->bevelhigh;

  switch (o->puffblend) {
    case GEGL_BEVEL_HIGHPUFF: bevel = state->bevelhigh; break;
    case GEGL_BEVEL_LOWPUFF: bevel = state->bevellow; break;
    case GEGL_BEVEL_LOWPUFF2: bevel = state->bevellow2; break;
default: bevel = state->bevellow2;

}

  gegl_node_link_many (state->input, state->gaussian1, state->nop0, state->makewhite, state->cubism, state->median,  state->nopconform,  state->gaussian2,  state->coloroverlay1, bevel, state->graphcapture, state->nopgm, state->grainmerge, state->nr, state->normal, state->behind, state->output,  NULL);

  gegl_node_link_many (state->nopgm,  state->alienmap, NULL);
  gegl_node_connect (state->grainmerge, "aux", state->alienmap, "output");

  gegl_node_link_many (state->nop0,  state->glass, NULL);
  gegl_node_connect (state->normal, "aux", state->glass, "output");
  gegl_node_connect (state->behind, "aux", state->gaussian3, "output");
  gegl_node_link_many (state->nopconform, state->coloroverlay2, state->gaussian3, NULL);
}



static void
gegl_op_class_init (GeglOpClass *klass)
{
  GeglOperationClass *operation_class;
GeglOperationMetaClass *operation_meta_class = GEGL_OPERATION_META_CLASS (klass);
  operation_class = GEGL_OPERATION_CLASS (klass);

  operation_class->attach = attach;
  operation_meta_class->update = update_graph;

  gegl_operation_class_set_keys (operation_class,
    "name",        "lb:bubble",
    "title",       _("Bubble Text"),
    "reference-hash", "bubblebuddyheydontigetasayinthis",
    "description", _("Make text that looks like soapy bubbles"),
/*<Image>/Colors <Image>/Filters are top level menus in GIMP*/
    "gimp:menu-path", "<Image>/Filters/Text Styling",
    "gimp:menu-label", _("Bubble text..."),
    NULL);
}

#endif



Top
 Post subject: Re: GEGL Bubble Text (COMING SOON)
PostPosted: Tue Mar 24, 2026 11:54 pm  (#7) 
Offline
Global Moderator
User avatar

Joined: May 16, 2010
Posts: 16010
This looks really good now. :)

_________________
Image


Top
 Post subject: Re: GEGL Bubble Text (COMING SOON)
PostPosted: Wed Mar 25, 2026 2:51 am  (#8) 
Offline
GimpChat Member
User avatar

Joined: Oct 31, 2020
Posts: 2010
This can be compiled at anytime. The latest code is here, i udpated it so the base distortion slider works better.

I'm hoping someone compiles it before I release

/* This file is an image processing operation for GEGL
*
* GEGL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* GEGL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
*
* Credit to Øyvind Kolås (pippin) for major GEGL contributions
* 2026, beaver, Bubble text

requires lb:glassovertext (which it ships with)

start of syntax


gaussian-blur std-dev-x=1 std-dev-y=1 abyss-policy=none clip-extent=false
id=0
color-overlay value=white

cubism tile-size=2.0 seed=3423
gaussian-blur std-dev-x=2 std-dev-y=2 abyss-policy=none

median-blur radius=2 alpha-percentile=70 abyss-policy=none
color-overlay value=#98e1ff
bevel type=bump  blendmode=multiply radius=8 elevation=44 depth=11
edge
gray

color-to-alpha color=black
gaussian-blur std-dev-x=1 std-dev-y=1 abyss-policy=none  clip-extent=false
id=y

dst-over aux=[ ref=y gaussian-blur std-dev-x=7 std-dev-y=7  abyss-policy=none clip-extent=false  opacity value=0.11  ]
dst-over aux=[ ref=y color-overlay value=black gaussian-blur std-dev-x=2 std-dev-y=2  abyss-policy=none   clip-extent=false
opacity value=1.1 ]

id=gm gimp:layer-mode layer-mode=grain-merge blend-space=rgb-linear opacity=0.45 aux=[  ref=gm
alien-map
color-model=rgb
cpn-1-frequency=3.641
cpn-2-frequency=2.9489999999999998
cpn-3-frequency=7.5529999732971191
cpn-1-phaseshift=0
cpn-2-phaseshift=0
cpn-3-phaseshift=0
cpn-1-keep=no
cpn-2-keep=no
cpn-3-keep=no
]

noise-reduction iterations=4

over aux=[ ref=0 lb:glassovertext azimuth=94 hyperopacity=0.3 ]
dst-over aux=[ ref=0 color-overlay value=#0c82bb gaussian-blur std-dev-x=7 std-dev-y=7  abyss-policy=none clip-extent=false opacity value=0.78  ]

]

end of syntax
*/

#include "config.h"
#include <glib/gi18n-lib.h>

#ifdef GEGL_PROPERTIES

/*Properties go here*/



enum_start (puff_puffpuff322928)
  enum_value (GEGL_BEVEL_HIGHPUFF, "highpuff",
         N_("High Puff"))
  enum_value (GEGL_BEVEL_LOWPUFF,      "lowpuff",
         N_("Low Puff"))
  enum_value (GEGL_BEVEL_LOWPUFF2,      "lowpuff2",
         N_("Low Puff 2"))
enum_end (puffpuffpuff322928)

property_enum (puffblend, _("Internal Blend/Puff"),
    puffpuffpuff322928, puff_puffpuff322928,
    GEGL_BEVEL_HIGHPUFF)
    description (_("There is no such thing as a perfect bubble text, only trade offs and options that are convenient in some places and bad in others. So try different internal bevel blend modes to have more or less puff"))





property_double (base_blur, _("Base blur"), 2.0)
  value_range (0.0, 4.0)
  ui_range (0.0, 4.0)
  ui_gamma (1.5)
    description (_("Blur at the start of the graph to enhance the bubble"))
  ui_steps      (0.5, 0.50)

property_double (tile_size, _("Base distortion"), 2.5)
    description (_("Base distortion of the bubble"))
    value_range (2.5, 10.0)
    ui_meta     ("unit", "pixel-distance")



property_seed (seed, _("Base distortion seed"), rand)
    description (_("Seed of the base distortion"))



property_double (grow, _("Grow"), 80.0)
  value_range (70.0, 100.0)
  ui_range (1.0, 8.0)
  ui_gamma (1.5)
    description (_("Grow radius of the bubble"))
  ui_steps      (0.5, 0.50)


property_double (elevation, _("Elevation"), 44.0)
  value_range (40.0, 60.0)
  ui_range (40.0, 60.0)
  ui_gamma (1.5)
    description (_("Elevation of the internal bump bevel"))
  ui_steps      (0.5, 0.50)




property_double (azimuth, _("Azimuth"), 94.0)
    description (_("Light angle (degrees)"))
    value_range (0, 360)
    ui_meta ("unit", "degree")
    ui_meta ("direction", "ccw")
  ui_steps      (0.5, 0.50)



property_double (tc_opacity, _("Thin film opacity"), 0.45)
    description (_("Opacity of the pseudo thin film interference iridescent effect"))
    value_range (0.0, 1.0)
    ui_meta ("unit", "degree")
  ui_steps      (0.5, 0.20)




property_double (cpn_1_frequency, _("Thin film control 1"), 3.6)
  value_range (3.0, 30.0)
    description (_("To modify the bubble's pseudo thin film interference iridescent properties"))


property_double  (cpn_2_frequency, _("Thin film control 2"), 4.0)
  value_range (2.0, 25.0)
    description (_("To modify the bubble's pseudo thin film interference iridescent properties"))


property_double  (cpn_3_frequency, _("Thin film control 3"), 7.5)
  value_range (3.0, 42.0)
    description (_("To modify the bubble's pseudo thin film interference iridescent properties"))

property_double (bg_glow, _("Background blur"), 3.5)
  value_range (0.0, 7.0)
  ui_range (0.0, 7.0)
  ui_gamma (1.0)
    description (_("Background blur"))
  ui_steps      (0.5, 0.50)

property_double (bg_opacity, _("Background opacity"), 0.78)
  value_range (0.0, 1.0)
  ui_range (0.0, 1.0)
  ui_gamma (1.0)
    description (_("Background opacity"))
  ui_steps      (0.5, 0.50)


#else

#define GEGL_OP_META
#define GEGL_OP_NAME     bubblebuddy
#define GEGL_OP_C_SOURCE bubblebuddy.c

#include "gegl-op.h"

/*starred nodes go inside typedef struct */

typedef struct
{
GeglNode *input;
GeglNode *gaussian1;
GeglNode *gaussian2;
GeglNode *gaussian3;
GeglNode *makewhite;
GeglNode *coloroverlay1;
GeglNode *coloroverlay2;
GeglNode *cubism;
GeglNode *median;
GeglNode *bevellow;
GeglNode *bevellow2;
GeglNode *bevelhigh;
GeglNode *edge;
GeglNode *gray;
GeglNode *normal;
GeglNode *glass;
GeglNode *nr;
GeglNode *opacity;
GeglNode *behind;
GeglNode *behind2;
GeglNode *alienmap;
GeglNode *grainmerge;
GeglNode *nopy;
GeglNode *nopgm;
GeglNode *nopconform;
GeglNode *nop0;
GeglNode *graphcapture;
GeglNode *output;
}State;

static void attach (GeglOperation *operation)
{
  GeglNode *gegl = operation->node;
  GeglProperties *o = GEGL_PROPERTIES (operation);
  GeglColor *white = gegl_color_new ("#ffffff");
  GeglColor *secondcolor = gegl_color_new ("#98e1ff");
  GeglColor *thirdcolor = gegl_color_new ("#0c82bb");


  State *state = o->user_data = g_malloc0 (sizeof (State));

/*new child node list is here, this is where starred nodes get defined

state->newchildname = gegl_node_new_child (gegl, "operation", "lb:name", NULL);*/
  state->input    = gegl_node_get_input_proxy (gegl, "input");
  state->output   = gegl_node_get_output_proxy (gegl, "output");



#define largepart \
" edge gray color-to-alpha color=black gaussian-blur std-dev-x=1 std-dev-y=1 abyss-policy=none clip-extent=false id=y dst-over aux=[ ref=y gaussian-blur std-dev-x=7 std-dev-y=7 abyss-policy=none clip-extent=false opacity value=0.11 ] dst-over aux=[ ref=y color-overlay value=black gaussian-blur std-dev-x=2 std-dev-y=2 abyss-policy=none clip-extent=false opacity value=1.1 ] "\

state->nr = gegl_node_new_child (gegl, "operation", "gegl:noise-reduction", "iterations", 4, NULL);

state->graphcapture = gegl_node_new_child (gegl, "operation", "gegl:gegl", "string", largepart, NULL);

state->bevelhigh = gegl_node_new_child (gegl, "operation", "gegl:bevel", "type", 1, "blendmode", 2, "radius", 8.0, "elevation", 44.0, "depth", 11,   NULL);

state->bevellow = gegl_node_new_child (gegl, "operation", "gegl:bevel", "type", 1, "blendmode", 4, "radius", 8.0, "elevation", 44.0, "depth", 11,   NULL);

state->bevellow2 = gegl_node_new_child (gegl, "operation", "gegl:bevel", "type", 1, "blendmode", 1, "radius", 8.0, "elevation", 44.0, "depth", 11,   NULL);

state->cubism = gegl_node_new_child (gegl, "operation", "gegl:cubism", "tile-size", 2.0,  NULL);

state->nopgm = gegl_node_new_child (gegl, "operation", "gegl:nop", NULL);

state->nop0 = gegl_node_new_child (gegl, "operation", "gegl:nop", NULL);

state->nopconform = gegl_node_new_child (gegl, "operation", "gegl:nop", NULL);

state->opacity = gegl_node_new_child (gegl, "operation", "gegl:opacity", "value", 0.7, NULL);


state->nopy = gegl_node_new_child (gegl, "operation", "gegl:nop", NULL);

state->nr = gegl_node_new_child (gegl, "operation", "gegl:noise-reduction", "iterations", 4, NULL);

state->gaussian1 = gegl_node_new_child (gegl, "operation", "gegl:gaussian-blur", "std-dev-x", 2.0,  "std-dev-y", 2.0, "clip-extent", FALSE, "abyss-policy", 0,  NULL);

state->gaussian2 = gegl_node_new_child (gegl, "operation", "gegl:gaussian-blur", "std-dev-x", 3.0,  "std-dev-y", 3.0, "clip-extent", FALSE, "abyss-policy", 0,  NULL);

state->gaussian3 = gegl_node_new_child (gegl, "operation", "gegl:gaussian-blur", "std-dev-x", 3.0,  "std-dev-y", 3.0, "clip-extent", FALSE, "abyss-policy", 0,  NULL);

  state->grainmerge    = gegl_node_new_child (gegl,
              "operation", "gimp:layer-mode", "layer-mode", 47, "composite-mode", 0, "blend-space", 2, "opacity", 0.45,
              NULL);

  state->behind    = gegl_node_new_child (gegl,
              "operation", "gimp:layer-mode", "layer-mode", 29, "composite-mode", 0, "opacity", 0.78,
              NULL);

state->median = gegl_node_new_child (gegl, "operation", "gegl:median-blur", "radius", 2, "alpha-percentile", 80.0, "abyss-policy", 0, NULL);

state->normal = gegl_node_new_child (gegl, "operation", "gegl:over", NULL);

state->makewhite = gegl_node_new_child (gegl, "operation", "gegl:color-overlay", "value", white, NULL);

state->coloroverlay1 = gegl_node_new_child (gegl, "operation", "gegl:color-overlay", "value", secondcolor, NULL);

state->coloroverlay2 = gegl_node_new_child (gegl, "operation", "gegl:color-overlay", "value", thirdcolor, NULL);

state->glass = gegl_node_new_child (gegl, "operation", "lb:glassovertext", "azimuth", 94.0, "hyperopacity", 0.3, "elevation", 45.0, NULL);
   

state->alienmap = gegl_node_new_child (gegl, "operation", "gegl:alien-map", "cpn-1-frequency", 3.6, "cpn-2-frequency", 2.9, "cpn-3-frequency", 7.5,  NULL);



/*meta redirect property to new child orders go here

gegl_operation_meta_redirect (operation, "propertyname", state->newchildname,  "originalpropertyname");
*/

gegl_operation_meta_redirect (operation, "cpn_1_frequency", state->alienmap,  "cpn_1_frequency");
gegl_operation_meta_redirect (operation, "cpn_2_frequency", state->alienmap,  "cpn_2_frequency");
gegl_operation_meta_redirect (operation, "cpn_3_frequency", state->alienmap,  "cpn_3_frequency");
gegl_operation_meta_redirect (operation, "azimuth", state->glass,  "azimuth");
gegl_operation_meta_redirect (operation, "seed", state->cubism,  "seed");
gegl_operation_meta_redirect (operation, "tile-size", state->cubism,  "tile-size");
gegl_operation_meta_redirect (operation, "elevation", state->bevelhigh,  "elevation");
gegl_operation_meta_redirect (operation, "elevation", state->bevellow,  "elevation");
gegl_operation_meta_redirect (operation, "elevation", state->bevellow2,  "elevation");
gegl_operation_meta_redirect (operation, "base-blur", state->gaussian1,  "std-dev-x");
gegl_operation_meta_redirect (operation, "base-blur", state->gaussian1,  "std-dev-y");
gegl_operation_meta_redirect (operation, "grow", state->median,  "alpha-percentile");
gegl_operation_meta_redirect (operation, "bg_opacity", state->behind,  "opacity");
gegl_operation_meta_redirect (operation, "bg_glow", state->gaussian2,  "std-dev-x");
gegl_operation_meta_redirect (operation, "bg_glow", state->gaussian2,  "std-dev-y");
gegl_operation_meta_redirect (operation, "bg_glow", state->gaussian3,  "std-dev-x");
gegl_operation_meta_redirect (operation, "bg_glow", state->gaussian3,  "std-dev-y");
gegl_operation_meta_redirect (operation, "tc_opacity", state->grainmerge,  "opacity");

}

static void
update_graph (GeglOperation *operation)
{
  GeglProperties *o = GEGL_PROPERTIES (operation);
  State *state = o->user_data;
  if (!state) return;
  GeglNode *bevel = state->bevelhigh;

  switch (o->puffblend) {
    case GEGL_BEVEL_HIGHPUFF: bevel = state->bevelhigh; break;
    case GEGL_BEVEL_LOWPUFF: bevel = state->bevellow; break;
    case GEGL_BEVEL_LOWPUFF2: bevel = state->bevellow2; break;
default: bevel = state->bevellow2;

}

  gegl_node_link_many (state->input, state->gaussian1, state->nop0, state->makewhite, state->cubism, state->median,  state->nopconform,  state->gaussian2,  state->coloroverlay1, bevel, state->graphcapture, state->nopgm, state->grainmerge, state->nr, state->normal, state->behind, state->output,  NULL);

  gegl_node_link_many (state->nopgm,  state->alienmap, state->opacity, NULL);
  gegl_node_connect (state->grainmerge, "aux", state->opacity, "output");

  gegl_node_link_many (state->nop0,  state->glass, NULL);
  gegl_node_connect (state->normal, "aux", state->glass, "output");
  gegl_node_connect (state->behind, "aux", state->gaussian3, "output");
  gegl_node_link_many (state->nopconform, state->coloroverlay2, state->gaussian3, NULL);
}



static void
gegl_op_class_init (GeglOpClass *klass)
{
  GeglOperationClass *operation_class;
GeglOperationMetaClass *operation_meta_class = GEGL_OPERATION_META_CLASS (klass);
  operation_class = GEGL_OPERATION_CLASS (klass);

  operation_class->attach = attach;
  operation_meta_class->update = update_graph;

  gegl_operation_class_set_keys (operation_class,
    "name",        "lb:bubble",
    "title",       _("Bubble Text"),
    "reference-hash", "bubblebuddyheydontigetasayinthis",
    "description", _("Make text that looks like soapy bubbles"),
/*<Image>/Colors <Image>/Filters are top level menus in GIMP*/
    "gimp:menu-path", "<Image>/Filters/Text Styling",
    "gimp:menu-label", _("Bubble text..."),
    NULL);
}

#endif



Top
 Post subject: Re: GEGL Bubble Text (COMING SOON)
PostPosted: Wed Mar 25, 2026 2:56 am  (#9) 
Offline
GimpChat Member
User avatar

Joined: Oct 31, 2020
Posts: 2010
Here, see the new sliders like Thin Film Opacity and how base distortion goes up higher,

Image

Attachment:
windows_binaries_gegl_plugin.zip [2.07 MiB]
Downloaded 38 times


Attachment:
linux_binaries_gegl_plugin.zip [17.44 KiB]
Downloaded 31 times


Attachment:
SourceCode.zip [11.63 KiB]
Downloaded 28 times


Source compile folder for Windows that needs to be in a directory above

https://github.com/LinuxBeaver/LinuxBea ... ettext.zip



LOCATION TO PUT GEGL PLUGIN BINARIES

Windows

C:\Users\(USERNAME)\AppData\Local\gegl-0.4\plug-ins


Linux
~/.local/share/gegl-0.4/plug-ins


Linux(Flatpak includes Chromebook)

~/.var/app/org.gimp.GIMP/data/gegl-0.4/plug-ins


then restart GIMP and go to "GEGL operation" to find the new plugins

It's in Filters Text Styling

Image


Top
 Post subject: Re: new March 2026 - GEGL Bubble Text (download below)
PostPosted: Thu Mar 26, 2026 1:02 am  (#10) 
Offline
GimpChat Member
User avatar

Joined: Oct 31, 2020
Posts: 2010
it updated again with a hue chroma slider

Attachment:
windows_binaries_gegl_plugin.zip [2.07 MiB]
Downloaded 28 times


Attachment:
linux_binaries_gegl_plugin.zip [18.12 KiB]
Downloaded 32 times


Attachment:
SourceCode.zip [11.89 KiB]
Downloaded 27 times


Top
 Post subject: Re: new March 2026 - GEGL Bubble Text (download below)
PostPosted: Thu Mar 26, 2026 8:41 pm  (#11) 
Offline
Global Moderator
User avatar

Joined: May 16, 2010
Posts: 16010
Pretty sweet contrast_ :bigthup

Image

_________________
Image


Top
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC - 5 hours [ DST ]



* Login  



Powered by phpBB3 © phpBB Group