diff --git a/plug-ins/script-fu/libscriptfu/scheme-marshal.c b/plug-ins/script-fu/libscriptfu/scheme-marshal.c index f5f5955d23..373311a705 100644 --- a/plug-ins/script-fu/libscriptfu/scheme-marshal.c +++ b/plug-ins/script-fu/libscriptfu/scheme-marshal.c @@ -260,9 +260,65 @@ get_item_from_ID_in_script (scheme *sc, return NULL; /* no error */ } -/* Caller owns the returned GeglColor. +/* Walk a Scheme list of numerics, clamping into a C array of bytes. + * Expects one to four numerics. + * Returns length of list, in range [0,4] + * Length zero denotes an error: not a numeric, or list is empty. + * A list longer than expected (>4) is not an error, but extra list is not used. + */ +static void +marshal_list_of_numeric_to_rgba (scheme *sc, + pointer color_list, + guchar (*rgba)[4], /* OUT */ + guint *length) /* OUT */ +{ + *length = 0; + for (guint i=0; i<4; i++) + { + if (sc->vptr->is_number (sc->vptr->pair_car (color_list))) + { + (*rgba)[i] = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), + 0, 255); + *length = *length + 1; + color_list = sc->vptr->pair_cdr (color_list); + } + else + { + /* Reached end of list or non-numeric. */ + return; + } + } + /* *length is in [0,4] and *rgba is filled in with same count. */ +} + +/* Walk a C array of bytes (rgba) creating Scheme list of numeric. + * Returns list whose length is in range [0,4]. + * The list is memory managed by scheme. + */ +static pointer +marshal_rgba_to_list_of_numeric (scheme *sc, + guchar rgba[4], + guint length) +{ + pointer result_list = sc->NIL; + + /* Walk rgba in reverse. */ + for (gint i=length-1; i>=0; i--) + { + /* Prepend to list, returning new list. */ + result_list = sc->vptr->cons (sc, + sc->vptr->mk_integer (sc, rgba[i]), + result_list); + } + + /* list is numerics from rgba and length (list) == length. */ + return result_list; +} + +/* Returns a GeglColor from a scheme list. + * Caller owns the returned GeglColor. * Returns NULL on failure: - * - list wrong length + * - list wrong length, not >1 * - list elements not numbers. */ GeglColor * @@ -270,73 +326,109 @@ marshal_component_list_to_color (scheme *sc, pointer color_list) { GeglColor *color_result; - guchar r = 0, g = 0, b = 0; + guchar rgba[4]; + guint list_length; - /* FIXME dispatch on list length and create different format colors */ - if (sc->vptr->list_length (sc, color_list) != 3) - return NULL; - if (sc->vptr->is_number (sc->vptr->pair_car (color_list))) - r = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), - 0, 255); + marshal_list_of_numeric_to_rgba (sc, color_list, &rgba, &list_length); + /* list_length is the count of numerics used. */ + + /* Dispatch on list length and create different format colors */ + if (list_length == 3 || list_length == 4) + { + /* RGBA */ + if (list_length == 3) + { + /* gegl has no three byte rgb. * ScriptFu sets alpha to 1.0 */ + rgba[3] = 255; + } + /* Assert rgba is full */ + color_result = gegl_color_new ("black"); + gegl_color_set_rgba_with_space (color_result, + (gdouble) rgba[0] / 255.0, + (gdouble) rgba[1] / 255.0, + (gdouble) rgba[2] / 255.0, + (gdouble) rgba[3] / 255.0, + NULL); /* NULL defaults to sRGB */ + } + else if (list_length == 1) + { + /* GRAY */ + GBytes *bytes = g_bytes_new (rgba, 1); + + color_result = gegl_color_new ("black"); + gegl_color_set_bytes (color_result, babl_format ("Y' u8"), bytes); + g_free (bytes); + } + else if (list_length == 2) + { + /* GRAYA */ + GBytes *bytes = g_bytes_new (rgba, 2); + + color_result = gegl_color_new ("black"); + gegl_color_set_bytes (color_result, babl_format ("Y'A u8"), bytes); + g_free (bytes); + } else - return NULL; + { + color_result = NULL; + } - color_list = sc->vptr->pair_cdr (color_list); - if (sc->vptr->is_number (sc->vptr->pair_car (color_list))) - g = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), - 0, 255); - else - return NULL; - - color_list = sc->vptr->pair_cdr (color_list); - if (sc->vptr->is_number (sc->vptr->pair_car (color_list))) - b = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), - 0, 255); - else - return NULL; - - color_result = gegl_color_new ("black"); - gegl_color_set_rgba_with_space (color_result, - (gdouble) r / 255.0, - (gdouble) g / 255.0, - (gdouble) b / 255.0, - 1.0, NULL); return color_result; } -/* Returns (0 0 0) if color is NULL. */ -/* FIXME this should return a list +/* Returns a Scheme list of integers or empty list, * the same length as the count of components in the color. - * E.G. gimp-drawable-get-pixel may return indexed, rgb, or rgba. + * List is length: + * 1 GRAY + * 2 GRAYA + * 3 RGB + * 4 RGBA + * Returns NIL when color is NULL. */ pointer marshal_color_to_component_list (scheme *sc, GeglColor *color) { - guchar rgb[3] = { 0 }; + pointer result; + guint count_components; + guchar rgba[4] = { 0 }; - /* Warn when color has different count of components than - * the 3 of the pixel we are converting to. - */ - if (babl_format_get_n_components (gegl_color_get_format (color)) != 3) + if (color == NULL) { - g_warning ("%s converting to pixel with loss/gain of components", G_STRFUNC); + result = sc->NIL; + } + else + { + count_components = babl_format_get_n_components (gegl_color_get_format (color)); + switch (count_components) + { + case 3: + gegl_color_get_pixel (color, babl_format ("R'G'B' u8"), rgba); + result = marshal_rgba_to_list_of_numeric (sc, rgba, 3); + break; + case 4: + gegl_color_get_pixel (color, babl_format ("R'G'B'A u8"), rgba); + result = marshal_rgba_to_list_of_numeric (sc, rgba, 4); + break; + case 1: + /* Grayscale with TRC from space */ + gegl_color_get_pixel (color, babl_format ("Y' u8"), rgba); + result = marshal_rgba_to_list_of_numeric (sc, rgba, 1); + break; + case 2: + /* Grayscale with TRC from space, separate alpha. */ + gegl_color_get_pixel (color, babl_format ("Y'A u8"), rgba); + result = marshal_rgba_to_list_of_numeric (sc, rgba, 2); + break; + default: + g_warning ("%s unhandled count of color components: %d", G_STRFUNC, count_components); + result = sc->NIL; + } } - if (color) - gegl_color_get_pixel (color, babl_format ("R'G'B' u8"), rgb); - /* else will return (0 0 0) */ - - return sc->vptr->cons ( - sc, - sc->vptr->mk_integer (sc, rgb[0]), - sc->vptr->cons (sc, - sc->vptr->mk_integer (sc, rgb[1]), - sc->vptr->cons (sc, - sc->vptr->mk_integer (sc, rgb[2]), - sc->NIL))); + return result; } /* ColorArray */ diff --git a/plug-ins/script-fu/libscriptfu/script-fu-color.c b/plug-ins/script-fu/libscriptfu/script-fu-color.c index fa2ec6614d..99692521ed 100644 --- a/plug-ins/script-fu/libscriptfu/script-fu-color.c +++ b/plug-ins/script-fu/libscriptfu/script-fu-color.c @@ -37,10 +37,11 @@ * and default values, only the declarations of defaults etc. * Since a GimpProcedureConfig carries the values * and GParamSpec carries the defaults. - * - ScriptFu might not support RGB triplet repr + * - ScriptFu might support other formats * * Complex: - * PDB and widgets traffic in GeglColor but SF dumbs it down to a Scheme list (r g b) + * PDB and widgets traffic in GeglColor but SF dumbs it down to a Scheme list + * e.g. (r g b) (r g b a) or (y) or (y a) * * More SF code deals with GeglColor: * see scheme_marshall.c we marshall from GeglColor to/from Scheme lists of numbers. @@ -49,19 +50,17 @@ /* Return the Scheme representation. * Caller owns returned string. + * + * An alias: knows SFColorType is GeglColor */ gchar* sf_color_get_repr (SFColorType arg_value) { - guchar rgb[3] = { 0 }; - - if (arg_value) - gegl_color_get_pixel (arg_value, babl_format ("R'G'B' u8"), rgb); - - return g_strdup_printf ("'(%d %d %d)", (gint) rgb[0], (gint) rgb[1], (gint) rgb[2]); + return sf_color_get_repr_from_gegl_color (arg_value); } -/* Returns GeglColor from SFColorType. +/* Returns GeglColor from arg of type SFColorType. + * When arg is NULL, returns GeglColor transparent. * * Returned GeglColor is owned by caller. */ @@ -78,19 +77,25 @@ void sf_color_set_from_gegl_color (SFColorType *arg_value, GeglColor *color) { - const Babl *format = gegl_color_get_format (color); - guint8 pixel[48]; - - gegl_color_get_pixel (color, format, pixel); if (*arg_value) - gegl_color_set_pixel (*arg_value, format, pixel); + { + /* Arg is already a GeglColor, change its color. */ + const Babl *format = gegl_color_get_format (color); + guint8 pixel[48]; + + gegl_color_get_pixel (color, format, pixel); + gegl_color_set_pixel (*arg_value, format, pixel); + } else - *arg_value = gegl_color_duplicate (color); + { + /* Arg is NULL. Duplicate given color. */ + *arg_value = gegl_color_duplicate (color); + } } /* Set the default for an arg of type SFColorType from a string name. * - * Keep default value to put in default of param spec when creating procedure. + * The default value is later put in default of param spec when creating procedure. * * Also, the old-style dialog resets from the kept default value. * Versus new-style dialog, using ProcedureConfig, which resets from a paramspec. @@ -115,12 +120,20 @@ sf_color_arg_set_default_by_name (SFArg *arg, } else { - /* ScriptFu does not let an author specify RGBA, only RGB. */ - gimp_color_set_alpha (color, 1.0); + /* css named colors are in sRGB. + * You cannot name a grayscale color. + * However, "transparent" is also a css named color. + */ + /* We don't override alpha: + * Not calling: gimp_color_set_alpha (color, 1.0); + */ - /* Copying a struct that is not allocated, not setting a pointer. */ - g_clear_object (&arg->default_value.sfa_color); - arg->default_value.sfa_color = color; + /* Expect the default is NULL, but call the setter anyway, + * which sets it when not already NULL. + */ + sf_color_set_from_gegl_color (&arg->default_value.sfa_color, color); + + g_object_unref (color); } return result; } @@ -146,7 +159,7 @@ GeglColor* sf_color_arg_get_default_color (SFArg *arg) { /* require the default was set earlier. - * No easy way to assert it was set, + * No easy way to assert it was set. */ return sf_color_get_gegl_color (arg->default_value.sfa_color); } @@ -154,22 +167,53 @@ sf_color_arg_get_default_color (SFArg *arg) /* Methods for conversion GeglColor to/from Scheme representation. */ -/* Caller owns returned string.*/ +/* Convert GeglColor to scheme representation as list of numeric. + * List is length in [0,4]. + * Caller owns returned string. + * Length depends on format: GRAY, GRAYA, RGB, or RGBA. + * Returns literal for empty list when color is NULL. + */ gchar* sf_color_get_repr_from_gegl_color (GeglColor *color) { - guchar rgb[3] = { 0 }; + gchar *result; - /* Warn when color has different count of components than - * the 3 of the pixel we are converting to. - */ - if (babl_format_get_n_components (gegl_color_get_format (color)) != 3) + if (color == NULL) { - g_warning ("%s converting to pixel with loss/gain of components", G_STRFUNC); + result = g_strdup_printf ("'()"); + } + else + { + guint count_components = babl_format_get_n_components (gegl_color_get_format (color)); + guchar rgba[4] = { 0 }; + + /* Dispatch on count of components. */ + switch (count_components) + { + case 1: + gegl_color_get_pixel (color, babl_format ("Y' u8"), rgba); + result = g_strdup_printf ("'(%d)", (gint) rgba[0]); + break; + case 2: + gegl_color_get_pixel (color, babl_format ("Y'A u8"), rgba); + result = g_strdup_printf ("'(%d %d)", (gint) rgba[0], (gint) rgba[1]); + break; + case 3: + gegl_color_get_pixel (color, babl_format ("R'G'B' u8"), rgba); + result = g_strdup_printf ("'(%d %d %d)", (gint) rgba[0], (gint) rgba[1], (gint) rgba[2]); + break; + case 4: + gegl_color_get_pixel (color, babl_format ("R'G'B'A u8"), rgba); + result = g_strdup_printf ("'(%d %d %d %d)", + (gint) rgba[0], (gint) rgba[1], (gint) rgba[2], (gint) rgba[3]); + break; + default: + g_warning ("%s unhandled count of color components", G_STRFUNC); + result = g_strdup_printf ("'()"); + } } - gegl_color_get_pixel (color, babl_format ("R'G'B' u8"), rgb); - return g_strdup_printf ("'(%d %d %d)", (gint) rgb[0], (gint) rgb[1], (gint) rgb[2]); + return result; } /* Caller owns the returned GeglColor.