diff options
Diffstat (limited to 'app/operations/gimpoperationmaskcomponents.cc')
-rw-r--r-- | app/operations/gimpoperationmaskcomponents.cc | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/app/operations/gimpoperationmaskcomponents.cc b/app/operations/gimpoperationmaskcomponents.cc new file mode 100644 index 0000000..779dfac --- /dev/null +++ b/app/operations/gimpoperationmaskcomponents.cc @@ -0,0 +1,586 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationmaskcomponents.c + * Copyright (C) 2012 Michael Natterer <mitch@gimp.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <gegl.h> + +extern "C" +{ + +#include "operations-types.h" + +#include "gimpoperationmaskcomponents.h" + +} /* extern "C" */ + + +enum +{ + PROP_0, + PROP_MASK, + PROP_ALPHA +}; + + +static void gimp_operation_mask_components_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_operation_mask_components_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gimp_operation_mask_components_prepare (GeglOperation *operation); +static GeglRectangle gimp_operation_mask_components_get_bounding_box (GeglOperation *operation); +static gboolean gimp_operation_mask_components_parent_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_prop, + const GeglRectangle *result, + gint level); + +static gboolean gimp_operation_mask_components_process (GeglOperation *operation, + void *in_buf, + void *aux_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationMaskComponents, gimp_operation_mask_components, + GEGL_TYPE_OPERATION_POINT_COMPOSER) + +#define parent_class gimp_operation_mask_components_parent_class + + +static void +gimp_operation_mask_components_class_init (GimpOperationMaskComponentsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationPointComposerClass *point_class = GEGL_OPERATION_POINT_COMPOSER_CLASS (klass); + + object_class->set_property = gimp_operation_mask_components_set_property; + object_class->get_property = gimp_operation_mask_components_get_property; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:mask-components", + "categories", "gimp", + "description", "Selectively pick components from src or aux", + NULL); + + operation_class->prepare = gimp_operation_mask_components_prepare; + operation_class->get_bounding_box = gimp_operation_mask_components_get_bounding_box; + operation_class->process = gimp_operation_mask_components_parent_process; + + point_class->process = gimp_operation_mask_components_process; + + g_object_class_install_property (object_class, PROP_MASK, + g_param_spec_flags ("mask", + "Mask", + "The component mask", + GIMP_TYPE_COMPONENT_MASK, + GIMP_COMPONENT_MASK_ALL, + (GParamFlags) ( + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT))); + + g_object_class_install_property (object_class, PROP_ALPHA, + g_param_spec_double ("alpha", + "Alpha", + "The masked-in alpha value when there's no aux input", + -G_MAXDOUBLE, + G_MAXDOUBLE, + 0.0, + (GParamFlags) ( + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT))); +} + +static void +gimp_operation_mask_components_init (GimpOperationMaskComponents *self) +{ +} + +static void +gimp_operation_mask_components_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpOperationMaskComponents *self = GIMP_OPERATION_MASK_COMPONENTS (object); + + switch (property_id) + { + case PROP_MASK: + g_value_set_flags (value, self->mask); + break; + + case PROP_ALPHA: + g_value_set_double (value, self->alpha); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_operation_mask_components_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpOperationMaskComponents *self = GIMP_OPERATION_MASK_COMPONENTS (object); + + switch (property_id) + { + case PROP_MASK: + self->mask = (GimpComponentMask) g_value_get_flags (value); + break; + + case PROP_ALPHA: + self->alpha = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static guint32 +get_alpha_value (const Babl *format, + gfloat alpha) +{ + switch (babl_format_get_bytes_per_pixel (format)) + { + #define DEF_CASE(bpp, type) \ + case bpp: \ + { \ + type alpha_value; \ + \ + babl_process ( \ + babl_fish (babl_format_n (babl_type ("float"), 1), \ + babl_format_n (babl_format_get_type (format, 0), 1)), \ + &alpha, &alpha_value, 1); \ + \ + return alpha_value; \ + } + + DEF_CASE ( 4, guint8) + DEF_CASE ( 8, guint16) + DEF_CASE (16, guint32) + + #undef DEF_CASE + + default: + g_return_val_if_reached (0); + } +} + +template <class T> +struct ProcessGeneric +{ + static void + process (gconstpointer in_buf, + gconstpointer aux_buf, + gpointer out_buf, + gint n, + GimpComponentMask mask, + T alpha_value) + { + T *out = (T *) out_buf; + gint i; + gint c; + + if (aux_buf) + { + const T *in[4]; + + for (c = 0; c < 4; c++) + { + if (mask & (1 << c)) + in[c] = (const T *) aux_buf + c; + else + in[c] = (const T *) in_buf + c; + } + + for (i = 0; i < n; i++) + { + for (c = 0; c < 4; c++) + { + out[c] = *in[c]; + + in[c] += 4; + } + + out += 4; + } + } + else + { + const T *in = (const T*) in_buf; + + for (i = 0; i < n; i++) + { + for (c = 0; c < 3; c++) + { + if (mask & (1 << c)) + out[c] = 0; + else + out[c] = in[c]; + } + + if (mask & (1 << 3)) + out[3] = alpha_value; + else + out[3] = in[3]; + + in += 4; + out += 4; + } + } + } +}; + +template <class T> +struct Process : ProcessGeneric<T> +{ +}; + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + +template <> +struct Process<guint8> +{ + static void + process (gconstpointer in_buf, + gconstpointer aux_buf, + gpointer out_buf, + gint n, + GimpComponentMask mask, + guint8 alpha_value) + { + const guint32 *in; + guint32 *out; + guint32 in_mask = 0; + gint i; + gint c; + + if (((guintptr) in_buf | (guintptr) aux_buf | (guintptr) out_buf) % 4) + { + ProcessGeneric<guint8>::process (in_buf, aux_buf, out_buf, n, + mask, alpha_value); + + return; + } + + in = (const guint32 *) in_buf; + out = (guint32 *) out_buf; + + for (c = 0; c < 4; c++) + { + if (! (mask & (1 << c))) + in_mask |= 0xff << (8 * c); + } + + if (aux_buf) + { + const guint32 *aux = (const guint32 *) aux_buf; + guint32 aux_mask = ~in_mask; + + for (i = 0; i < n; i++) + { + *out = (*in & in_mask) | (*aux & aux_mask); + + in++; + aux++; + out++; + } + } + else + { + if (! (mask & GIMP_COMPONENT_MASK_ALPHA) || ! alpha_value) + { + for (i = 0; i < n; i++) + { + *out = *in & in_mask; + + in++; + out++; + } + } + else + { + guint32 alpha_mask = alpha_value << 24; + + for (i = 0; i < n; i++) + { + *out = (*in & in_mask) | alpha_mask; + + in++; + out++; + } + } + } + } +}; + +#endif /* G_BYTE_ORDER == G_LITTLE_ENDIAN */ + +template <class T> +static gboolean +gimp_operation_mask_components_process (GimpOperationMaskComponents *self, + void *in_buf, + void *aux_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + Process<T>::process (in_buf, aux_buf, out_buf, samples, + self->mask, self->alpha_value); + + return TRUE; +} + +static void +gimp_operation_mask_components_prepare (GeglOperation *operation) +{ + GimpOperationMaskComponents *self = GIMP_OPERATION_MASK_COMPONENTS (operation); + const Babl *format; + + format = gimp_operation_mask_components_get_format ( + gegl_operation_get_source_format (operation, "input")); + + gegl_operation_set_format (operation, "input", format); + gegl_operation_set_format (operation, "aux", format); + gegl_operation_set_format (operation, "output", format); + + if (format != self->format) + { + self->format = format; + + self->alpha_value = get_alpha_value (format, self->alpha); + + switch (babl_format_get_bytes_per_pixel (format)) + { + case 4: + self->process = (gpointer) + gimp_operation_mask_components_process<guint8>; + break; + + case 8: + self->process = (gpointer) + gimp_operation_mask_components_process<guint16>; + break; + + case 16: + self->process = (gpointer) + gimp_operation_mask_components_process<guint32>; + break; + + default: + g_return_if_reached (); + } + } +} + +static GeglRectangle +gimp_operation_mask_components_get_bounding_box (GeglOperation *operation) +{ + GimpOperationMaskComponents *self = GIMP_OPERATION_MASK_COMPONENTS (operation); + GeglRectangle *in_rect; + GeglRectangle *aux_rect; + GeglRectangle result = {}; + + in_rect = gegl_operation_source_get_bounding_box (operation, "input"); + aux_rect = gegl_operation_source_get_bounding_box (operation, "aux"); + + if (self->mask == 0) + { + if (in_rect) + return *in_rect; + } + else if (self->mask == GIMP_COMPONENT_MASK_ALL) + { + if (aux_rect) + return *aux_rect; + } + + if (in_rect) + gegl_rectangle_bounding_box (&result, &result, in_rect); + + if (aux_rect) + gegl_rectangle_bounding_box (&result, &result, aux_rect); + + return result; +} + +static gboolean +gimp_operation_mask_components_parent_process (GeglOperation *operation, + GeglOperationContext *context, + const gchar *output_prop, + const GeglRectangle *result, + gint level) +{ + GimpOperationMaskComponents *self = GIMP_OPERATION_MASK_COMPONENTS (operation); + + if (self->mask == 0) + { + GObject *input = gegl_operation_context_get_object (context, "input"); + + gegl_operation_context_set_object (context, "output", input); + + return TRUE; + } + else if (self->mask == GIMP_COMPONENT_MASK_ALL) + { + GObject *aux = gegl_operation_context_get_object (context, "aux"); + + /* when there's no aux and the alpha component is masked-in, we set the + * result's alpha component to the value of the "alpha" property; if it + * doesn't equal 0, we can't forward an empty aux. + */ + if (aux || ! self->alpha_value) + { + gegl_operation_context_set_object (context, "output", aux); + + return TRUE; + } + } + + return GEGL_OPERATION_CLASS (parent_class)->process (operation, context, + output_prop, result, + level); +} + +static gboolean +gimp_operation_mask_components_process (GeglOperation *operation, + void *in_buf, + void *aux_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level) +{ + typedef gboolean (* ProcessFunc) (GimpOperationMaskComponents *self, + void *in_buf, + void *aux_buf, + void *out_buf, + glong samples, + const GeglRectangle *roi, + gint level); + + GimpOperationMaskComponents *self = (GimpOperationMaskComponents *) operation; + + return ((ProcessFunc) self->process) (self, + in_buf, aux_buf, out_buf, samples, + roi, level); +} + +const Babl * +gimp_operation_mask_components_get_format (const Babl *input_format) +{ + const Babl *format = NULL; + + if (input_format) + { + const Babl *model = babl_format_get_model (input_format); + const gchar *model_name = babl_get_name (model); + const Babl *type = babl_format_get_type (input_format, 0); + const gchar *type_name = babl_get_name (type); + + if (! strcmp (model_name, "Y") || + ! strcmp (model_name, "YA") || + ! strcmp (model_name, "RGB") || + ! strcmp (model_name, "RGBA")) + { + if (! strcmp (type_name, "u8")) + format = babl_format ("RGBA u8"); + else if (! strcmp (type_name, "u16")) + format = babl_format ("RGBA u16"); + else if (! strcmp (type_name, "u32")) + format = babl_format ("RGBA u32"); + else if (! strcmp (type_name, "half")) + format = babl_format ("RGBA half"); + else if (! strcmp (type_name, "float")) + format = babl_format ("RGBA float"); + } + else if (! strcmp (model_name, "Y'") || + ! strcmp (model_name, "Y'A") || + ! strcmp (model_name, "R'G'B'") || + ! strcmp (model_name, "R'G'B'A") || + babl_format_is_palette (input_format)) + { + if (! strcmp (type_name, "u8")) + format = babl_format ("R'G'B'A u8"); + else if (! strcmp (type_name, "u16")) + format = babl_format ("R'G'B'A u16"); + else if (! strcmp (type_name, "u32")) + format = babl_format ("R'G'B'A u32"); + else if (! strcmp (type_name, "half")) + format = babl_format ("R'G'B'A half"); + else if (! strcmp (type_name, "float")) + format = babl_format ("R'G'B'A float"); + } + } + + if (! format) + format = babl_format ("RGBA float"); + + return format; +} + +void +gimp_operation_mask_components_process (const Babl *format, + gconstpointer in, + gconstpointer aux, + gpointer out, + gint n, + GimpComponentMask mask) +{ + g_return_if_fail (format != NULL); + g_return_if_fail (in != NULL); + g_return_if_fail (out != NULL); + g_return_if_fail (n >= 0); + + switch (babl_format_get_bytes_per_pixel (format)) + { + case 4: + Process<guint8>::process (in, aux, out, n, mask, 0); + break; + + case 8: + Process<guint16>::process (in, aux, out, n, mask, 0); + break; + + case 16: + Process<guint32>::process (in, aux, out, n, mask, 0); + break; + + default: + g_return_if_reached (); + } +} |