From a188a8db9354b75a25acb788ea3a2332fc987cec Mon Sep 17 00:00:00 2001 From: woynert Date: Tue, 8 Apr 2025 21:51:51 -0500 Subject: [PATCH] app,libgimp: paint blend mode 'overwrite' --- app/operations/gimp-operations.c | 2 + app/operations/layer-modes/gimp-layer-modes.c | 16 ++ .../layer-modes/gimpoperationoverwrite.c | 250 ++++++++++++++++++ .../layer-modes/gimpoperationoverwrite.h | 53 ++++ app/operations/layer-modes/meson.build | 1 + app/operations/operations-enums.c | 2 + app/operations/operations-enums.h | 3 + libgimp/gimpenums.h | 4 +- pdb/enums.pl | 5 +- 9 files changed, 333 insertions(+), 3 deletions(-) create mode 100644 app/operations/layer-modes/gimpoperationoverwrite.c create mode 100644 app/operations/layer-modes/gimpoperationoverwrite.h diff --git a/app/operations/gimp-operations.c b/app/operations/gimp-operations.c index d109a60708..6a8c2a93b4 100644 --- a/app/operations/gimp-operations.c +++ b/app/operations/gimp-operations.c @@ -94,6 +94,7 @@ #include "layer-modes/gimpoperationmerge.h" #include "layer-modes/gimpoperationnormal.h" #include "layer-modes/gimpoperationpassthrough.h" +#include "layer-modes/gimpoperationoverwrite.h" #include "layer-modes/gimpoperationreplace.h" #include "layer-modes/gimpoperationsplit.h" @@ -180,6 +181,7 @@ gimp_operations_init (Gimp *gimp) g_type_class_ref (GIMP_TYPE_OPERATION_MERGE); g_type_class_ref (GIMP_TYPE_OPERATION_SPLIT); g_type_class_ref (GIMP_TYPE_OPERATION_PASS_THROUGH); + g_type_class_ref (GIMP_TYPE_OPERATION_OVERWRITE); g_type_class_ref (GIMP_TYPE_OPERATION_REPLACE); g_type_class_ref (GIMP_TYPE_OPERATION_ANTI_ERASE); diff --git a/app/operations/layer-modes/gimp-layer-modes.c b/app/operations/layer-modes/gimp-layer-modes.c index e6f08d58d0..c3b78f835b 100644 --- a/app/operations/layer-modes/gimp-layer-modes.c +++ b/app/operations/layer-modes/gimp-layer-modes.c @@ -832,6 +832,17 @@ static const GimpLayerModeInfo layer_mode_infos[] = .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR }, + { GIMP_LAYER_MODE_OVERWRITE, + + .op_name = "gimp:overwrite", + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_TRIVIAL, + .context = GIMP_LAYER_MODE_CONTEXT_PAINT, + .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_mode = GIMP_LAYER_COMPOSITE_UNION, + .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR + }, + { GIMP_LAYER_MODE_ANTI_ERASE, .op_name = "gimp:anti-erase", @@ -861,6 +872,7 @@ static const GimpLayerMode layer_mode_group_default[] = GIMP_LAYER_MODE_ANTI_ERASE, GIMP_LAYER_MODE_MERGE, GIMP_LAYER_MODE_SPLIT, + GIMP_LAYER_MODE_OVERWRITE, GIMP_LAYER_MODE_SEPARATOR, @@ -1105,6 +1117,10 @@ static const GimpLayerMode layer_mode_groups[][2] = [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 }, + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_OVERWRITE, + [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 + }, + { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_ANTI_ERASE, [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1 } diff --git a/app/operations/layer-modes/gimpoperationoverwrite.c b/app/operations/layer-modes/gimpoperationoverwrite.c new file mode 100644 index 0000000000..8d4a7a20c8 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationoverwrite.c @@ -0,0 +1,250 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationoverwrite.c + * Copyright (C) 2025 Woynert + * + * 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 . + */ + +#include "config.h" + +#include + +#include "../operations-types.h" + +#include "gimpoperationoverwrite.h" + + +static gboolean +gimp_operation_overwrite_process (GeglOperation *op, + void *in, + void *layer, + void *mask, + void *out, + glong samples, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationOverwrite, gimp_operation_overwrite, + GIMP_TYPE_OPERATION_LAYER_MODE) + + +static void +gimp_operation_overwrite_class_init (GimpOperationOverwriteClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass); + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:overwrite", + "description", "GIMP overwrite mode operation", + NULL); + + layer_mode_class->process = gimp_operation_overwrite_process; +} + +static void +gimp_operation_overwrite_init (GimpOperationOverwrite *self) +{ +} + +static gboolean +gimp_operation_overwrite_process (GeglOperation *op, + void *in_p, + void *layer_p, + void *mask_p, + void *out_p, + glong samples, + const GeglRectangle *roi, + gint level) +{ + GimpOperationLayerMode *layer_mode = (gpointer) op; + gfloat *in = in_p; + gfloat *out = out_p; + gfloat *layer = layer_p; + gfloat *mask = mask_p; + gfloat opacity = layer_mode->opacity; + const gboolean has_mask = mask != NULL; + + switch (layer_mode->composite_mode) + { + case GIMP_LAYER_COMPOSITE_UNION: + case GIMP_LAYER_COMPOSITE_AUTO: + while (samples--) + { + if (has_mask) + { + out[ALPHA] = in[ALPHA] + (*mask) * (opacity - in[ALPHA]); + + if (opacity + in[ALPHA] > -1e-5) + { + gfloat C; + gfloat lerp; + gint b; + + /* RGB interpolation */ + + C = in[ALPHA] / ( opacity + in[ALPHA] ); + + if (*mask < C) + lerp = *mask * ((1 - C) / C); + else + lerp = (*mask - C) * (C / (1 - C)) + 1 - C; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b] + (lerp) * (layer[b] - in[b]); + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + mask++; + } + + in += 4; + layer += 4; + out += 4; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP: + while (samples--) + { + gfloat layer_alpha; + + layer_alpha = layer[ALPHA] * opacity; + if (has_mask) + layer_alpha *= *mask; + + out[ALPHA] = in[ALPHA]; + + if (out[ALPHA]) + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b] + (layer[b] - in[b]) * layer_alpha; + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER: + while (samples--) + { + gfloat layer_alpha; + + layer_alpha = layer[ALPHA] * opacity; + if (has_mask) + layer_alpha *= *mask; + + out[ALPHA] = layer_alpha; + + if (out[ALPHA]) + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = layer[b]; + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + + case GIMP_LAYER_COMPOSITE_INTERSECTION: + while (samples--) + { + gfloat layer_alpha; + + layer_alpha = layer[ALPHA] * opacity; + if (has_mask) + layer_alpha *= *mask; + + out[ALPHA] = in[ALPHA] * layer_alpha; + + if (out[ALPHA]) + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = layer[b]; + } + } + else + { + gint b; + + for (b = RED; b < ALPHA; b++) + { + out[b] = in[b]; + } + } + + in += 4; + layer += 4; + out += 4; + + if (has_mask) + mask++; + } + break; + } + + return TRUE; +} diff --git a/app/operations/layer-modes/gimpoperationoverwrite.h b/app/operations/layer-modes/gimpoperationoverwrite.h new file mode 100644 index 0000000000..2ac63264d3 --- /dev/null +++ b/app/operations/layer-modes/gimpoperationoverwrite.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationoverwrite.h + * Copyright (C) 2025 Woynert + * + * 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 . + */ + +#ifndef __GIMP_OPERATION_OVERWRITE_H__ +#define __GIMP_OPERATION_OVERWRITE_H__ + + +#include "gimpoperationlayermode.h" + + +#define GIMP_TYPE_OPERATION_OVERWRITE (gimp_operation_overwrite_get_type ()) +#define GIMP_OPERATION_OVERWRITE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_OVERWRITE, GimpOperationOverwrite)) +#define GIMP_OPERATION_OVERWRITE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_OVERWRITE, GimpOperationOverwriteClass)) +#define GIMP_IS_OPERATION_OVERWRITE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_OVERWRITE)) +#define GIMP_IS_OPERATION_OVERWRITE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_OVERWRITE)) +#define GIMP_OPERATION_OVERWRITE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_OVERWRITE, GimpOperationOverwriteClass)) + + +typedef struct _GimpOperationOverwrite GimpOperationOverwrite; +typedef struct _GimpOperationOverwriteClass GimpOperationOverwriteClass; + +struct _GimpOperationOverwrite +{ + GimpOperationLayerMode parent_instance; +}; + +struct _GimpOperationOverwriteClass +{ + GimpOperationLayerModeClass parent_class; +}; + + +GType gimp_operation_overwrite_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_OVERWRITE_MODE_H__ */ diff --git a/app/operations/layer-modes/meson.build b/app/operations/layer-modes/meson.build index 0cf03134d7..76c78db38f 100644 --- a/app/operations/layer-modes/meson.build +++ b/app/operations/layer-modes/meson.build @@ -33,6 +33,7 @@ libapplayermodes_sources = files( 'gimpoperationmerge.c', 'gimpoperationnormal.c', 'gimpoperationpassthrough.c', + 'gimpoperationoverwrite.c', 'gimpoperationreplace.c', 'gimpoperationsplit.c', ) diff --git a/app/operations/operations-enums.c b/app/operations/operations-enums.c index 3430e2080d..3b865a1d65 100644 --- a/app/operations/operations-enums.c +++ b/app/operations/operations-enums.c @@ -147,6 +147,7 @@ gimp_layer_mode_get_type (void) { GIMP_LAYER_MODE_SPLIT, "GIMP_LAYER_MODE_SPLIT", "split" }, { GIMP_LAYER_MODE_PASS_THROUGH, "GIMP_LAYER_MODE_PASS_THROUGH", "pass-through" }, { GIMP_LAYER_MODE_REPLACE, "GIMP_LAYER_MODE_REPLACE", "replace" }, + { GIMP_LAYER_MODE_OVERWRITE, "GIMP_LAYER_MODE_OVERWRITE", "overwrite" }, { GIMP_LAYER_MODE_ANTI_ERASE, "GIMP_LAYER_MODE_ANTI_ERASE", "anti-erase" }, { 0, NULL, NULL } }; @@ -288,6 +289,7 @@ gimp_layer_mode_get_type (void) { GIMP_LAYER_MODE_SPLIT, NC_("layer-mode", "Split"), NULL }, { GIMP_LAYER_MODE_PASS_THROUGH, NC_("layer-mode", "Pass through"), NULL }, { GIMP_LAYER_MODE_REPLACE, NC_("layer-mode", "Replace"), NULL }, + { GIMP_LAYER_MODE_OVERWRITE, NC_("layer-mode", "Overwrite"), NULL }, { GIMP_LAYER_MODE_ANTI_ERASE, NC_("layer-mode", "Anti erase"), NULL }, { 0, NULL, NULL } }; diff --git a/app/operations/operations-enums.h b/app/operations/operations-enums.h index c6af2816bb..c9f586f758 100644 --- a/app/operations/operations-enums.h +++ b/app/operations/operations-enums.h @@ -129,6 +129,9 @@ typedef enum /* Mode only used for drawable filters. */ GIMP_LAYER_MODE_REPLACE, /*< desc="Replace" >*/ + /* Since 3.0 (paint mode only) */ + GIMP_LAYER_MODE_OVERWRITE, /*< desc="Overwrite" >*/ + /* Internal modes, not available to the PDB, must be kept at the end */ GIMP_LAYER_MODE_ANTI_ERASE, /*< pdb-skip, desc="Anti erase" >*/ diff --git a/libgimp/gimpenums.h b/libgimp/gimpenums.h index fcab197794..00dc96d1b2 100644 --- a/libgimp/gimpenums.h +++ b/libgimp/gimpenums.h @@ -207,6 +207,7 @@ GType gimp_layer_mode_get_type (void) G_GNUC_CONST; * @GIMP_LAYER_MODE_SPLIT: GIMP_LAYER_MODE_SPLIT * @GIMP_LAYER_MODE_PASS_THROUGH: GIMP_LAYER_MODE_PASS_THROUGH * @GIMP_LAYER_MODE_REPLACE: GIMP_LAYER_MODE_REPLACE + * @GIMP_LAYER_MODE_OVERWRITE: GIMP_LAYER_MODE_OVERWRITE * * Extracted from app/operations/operations-enums.h **/ @@ -274,7 +275,8 @@ typedef enum GIMP_LAYER_MODE_MERGE, GIMP_LAYER_MODE_SPLIT, GIMP_LAYER_MODE_PASS_THROUGH, - GIMP_LAYER_MODE_REPLACE + GIMP_LAYER_MODE_REPLACE, + GIMP_LAYER_MODE_OVERWRITE } GimpLayerMode; diff --git a/pdb/enums.pl b/pdb/enums.pl index 8c4259522e..e34077c828 100644 --- a/pdb/enums.pl +++ b/pdb/enums.pl @@ -772,7 +772,7 @@ package Gimp::CodeGen::enums; GIMP_LAYER_MODE_COLOR_ERASE GIMP_LAYER_MODE_ERASE GIMP_LAYER_MODE_MERGE GIMP_LAYER_MODE_SPLIT GIMP_LAYER_MODE_PASS_THROUGH - GIMP_LAYER_MODE_REPLACE) ], + GIMP_LAYER_MODE_REPLACE GIMP_LAYER_MODE_OVERWRITE) ], mapping => { GIMP_LAYER_MODE_NORMAL_LEGACY => '0', GIMP_LAYER_MODE_DISSOLVE => '1', GIMP_LAYER_MODE_BEHIND_LEGACY => '2', @@ -835,7 +835,8 @@ package Gimp::CodeGen::enums; GIMP_LAYER_MODE_MERGE => '59', GIMP_LAYER_MODE_SPLIT => '60', GIMP_LAYER_MODE_PASS_THROUGH => '61', - GIMP_LAYER_MODE_REPLACE => '62' } + GIMP_LAYER_MODE_REPLACE => '62', + GIMP_LAYER_MODE_OVERWRITE => '63' } }, GimpConvertDitherType => { contig => 1,