summaryrefslogtreecommitdiffstats
path: root/app/operations
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 03:13:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 03:13:10 +0000
commit3c57dd931145d43f2b0aef96c4d178135956bf91 (patch)
tree3de698981e9f0cc2c4f9569b19a5f3595e741f6b /app/operations
parentInitial commit. (diff)
downloadgimp-3c57dd931145d43f2b0aef96c4d178135956bf91.tar.xz
gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.zip
Adding upstream version 2.10.36.upstream/2.10.36
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'app/operations')
-rw-r--r--app/operations/Makefile.am140
-rw-r--r--app/operations/Makefile.in1376
-rw-r--r--app/operations/gimp-operation-config.c827
-rw-r--r--app/operations/gimp-operation-config.h53
-rw-r--r--app/operations/gimp-operations.c225
-rw-r--r--app/operations/gimp-operations.h27
-rw-r--r--app/operations/gimpbrightnesscontrastconfig.c248
-rw-r--r--app/operations/gimpbrightnesscontrastconfig.h58
-rw-r--r--app/operations/gimpcageconfig.c832
-rw-r--r--app/operations/gimpcageconfig.h108
-rw-r--r--app/operations/gimpcolorbalanceconfig.c382
-rw-r--r--app/operations/gimpcolorbalanceconfig.h62
-rw-r--r--app/operations/gimpcurvesconfig.c695
-rw-r--r--app/operations/gimpcurvesconfig.h81
-rw-r--r--app/operations/gimphuesaturationconfig.c367
-rw-r--r--app/operations/gimphuesaturationconfig.h62
-rw-r--r--app/operations/gimplevelsconfig.c964
-rw-r--r--app/operations/gimplevelsconfig.h92
-rw-r--r--app/operations/gimpoperationborder.c748
-rw-r--r--app/operations/gimpoperationborder.h58
-rw-r--r--app/operations/gimpoperationbrightnesscontrast.c140
-rw-r--r--app/operations/gimpoperationbrightnesscontrast.h53
-rw-r--r--app/operations/gimpoperationbuffersourcevalidate.c307
-rw-r--r--app/operations/gimpoperationbuffersourcevalidate.h52
-rw-r--r--app/operations/gimpoperationcagecoefcalc.c294
-rw-r--r--app/operations/gimpoperationcagecoefcalc.h62
-rw-r--r--app/operations/gimpoperationcagetransform.c603
-rw-r--r--app/operations/gimpoperationcagetransform.h58
-rw-r--r--app/operations/gimpoperationcolorbalance.c198
-rw-r--r--app/operations/gimpoperationcolorbalance.h53
-rw-r--r--app/operations/gimpoperationcolorize.c274
-rw-r--r--app/operations/gimpoperationcolorize.h57
-rw-r--r--app/operations/gimpoperationcomposecrop.c329
-rw-r--r--app/operations/gimpoperationcomposecrop.h55
-rw-r--r--app/operations/gimpoperationcurves.c118
-rw-r--r--app/operations/gimpoperationcurves.h53
-rw-r--r--app/operations/gimpoperationdesaturate.c257
-rw-r--r--app/operations/gimpoperationdesaturate.h55
-rw-r--r--app/operations/gimpoperationequalize.c248
-rw-r--r--app/operations/gimpoperationequalize.h57
-rw-r--r--app/operations/gimpoperationfillsource.c254
-rw-r--r--app/operations/gimpoperationfillsource.h55
-rw-r--r--app/operations/gimpoperationflood.c1104
-rw-r--r--app/operations/gimpoperationflood.h53
-rw-r--r--app/operations/gimpoperationgradient.c1283
-rw-r--r--app/operations/gimpoperationgradient.h73
-rw-r--r--app/operations/gimpoperationgrow.c391
-rw-r--r--app/operations/gimpoperationgrow.h56
-rw-r--r--app/operations/gimpoperationhistogramsink.c244
-rw-r--r--app/operations/gimpoperationhistogramsink.h56
-rw-r--r--app/operations/gimpoperationhuesaturation.c302
-rw-r--r--app/operations/gimpoperationhuesaturation.h58
-rw-r--r--app/operations/gimpoperationlevels.c208
-rw-r--r--app/operations/gimpoperationlevels.h57
-rw-r--r--app/operations/gimpoperationmaskcomponents.cc586
-rw-r--r--app/operations/gimpoperationmaskcomponents.h68
-rw-r--r--app/operations/gimpoperationoffset.c497
-rw-r--r--app/operations/gimpoperationoffset.h55
-rw-r--r--app/operations/gimpoperationpointfilter.c131
-rw-r--r--app/operations/gimpoperationpointfilter.h73
-rw-r--r--app/operations/gimpoperationposterize.c165
-rw-r--r--app/operations/gimpoperationposterize.h55
-rw-r--r--app/operations/gimpoperationprofiletransform.c307
-rw-r--r--app/operations/gimpoperationprofiletransform.h65
-rw-r--r--app/operations/gimpoperationscalarmultiply.c189
-rw-r--r--app/operations/gimpoperationscalarmultiply.h56
-rw-r--r--app/operations/gimpoperationsemiflatten.c191
-rw-r--r--app/operations/gimpoperationsemiflatten.h55
-rw-r--r--app/operations/gimpoperationsetalpha.c188
-rw-r--r--app/operations/gimpoperationsetalpha.h55
-rw-r--r--app/operations/gimpoperationsettings.c320
-rw-r--r--app/operations/gimpoperationsettings.h75
-rw-r--r--app/operations/gimpoperationshrink.c443
-rw-r--r--app/operations/gimpoperationshrink.h57
-rw-r--r--app/operations/gimpoperationthreshold.c232
-rw-r--r--app/operations/gimpoperationthreshold.h57
-rw-r--r--app/operations/gimpoperationthresholdalpha.c178
-rw-r--r--app/operations/gimpoperationthresholdalpha.h56
-rw-r--r--app/operations/layer-modes-legacy/Makefile.am54
-rw-r--r--app/operations/layer-modes-legacy/Makefile.in1038
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationadditionlegacy.c126
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationadditionlegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationburnlegacy.c128
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationburnlegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.c124
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.c126
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationdividelegacy.c127
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationdividelegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationdodgelegacy.c127
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationdodgelegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.c125
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.c125
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.c135
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.c141
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.c146
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.c140
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.c140
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.c124
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.c124
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationscreenlegacy.c124
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationscreenlegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.c157
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.h53
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.c125
-rw-r--r--app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.h53
-rw-r--r--app/operations/layer-modes/Makefile.am79
-rw-r--r--app/operations/layer-modes/Makefile.in1115
-rw-r--r--app/operations/layer-modes/gimp-layer-modes.c1522
-rw-r--r--app/operations/layer-modes/gimp-layer-modes.h73
-rw-r--r--app/operations/layer-modes/gimpoperationantierase.c188
-rw-r--r--app/operations/layer-modes/gimpoperationantierase.h53
-rw-r--r--app/operations/layer-modes/gimpoperationbehind.c236
-rw-r--r--app/operations/layer-modes/gimpoperationbehind.h53
-rw-r--r--app/operations/layer-modes/gimpoperationdissolve.c175
-rw-r--r--app/operations/layer-modes/gimpoperationdissolve.h53
-rw-r--r--app/operations/layer-modes/gimpoperationerase.c214
-rw-r--r--app/operations/layer-modes/gimpoperationerase.h53
-rw-r--r--app/operations/layer-modes/gimpoperationlayermode-blend.c1215
-rw-r--r--app/operations/layer-modes/gimpoperationlayermode-blend.h200
-rw-r--r--app/operations/layer-modes/gimpoperationlayermode-composite-sse2.c105
-rw-r--r--app/operations/layer-modes/gimpoperationlayermode-composite.c434
-rw-r--r--app/operations/layer-modes/gimpoperationlayermode-composite.h98
-rw-r--r--app/operations/layer-modes/gimpoperationlayermode.c914
-rw-r--r--app/operations/layer-modes/gimpoperationlayermode.h89
-rw-r--r--app/operations/layer-modes/gimpoperationmerge.c243
-rw-r--r--app/operations/layer-modes/gimpoperationmerge.h54
-rw-r--r--app/operations/layer-modes/gimpoperationnormal-sse2.c264
-rw-r--r--app/operations/layer-modes/gimpoperationnormal-sse4.c260
-rw-r--r--app/operations/layer-modes/gimpoperationnormal.c266
-rw-r--r--app/operations/layer-modes/gimpoperationnormal.h91
-rw-r--r--app/operations/layer-modes/gimpoperationpassthrough.c55
-rw-r--r--app/operations/layer-modes/gimpoperationpassthrough.h54
-rw-r--r--app/operations/layer-modes/gimpoperationreplace.c347
-rw-r--r--app/operations/layer-modes/gimpoperationreplace.h53
-rw-r--r--app/operations/layer-modes/gimpoperationsplit.c213
-rw-r--r--app/operations/layer-modes/gimpoperationsplit.h54
-rw-r--r--app/operations/operations-enums.c370
-rw-r--r--app/operations/operations-enums.h190
-rw-r--r--app/operations/operations-types.h76
-rw-r--r--app/operations/tests/Makefile.am71
-rw-r--r--app/operations/tests/Makefile.in815
152 files changed, 33831 insertions, 0 deletions
diff --git a/app/operations/Makefile.am b/app/operations/Makefile.am
new file mode 100644
index 0000000..24bfe6d
--- /dev/null
+++ b/app/operations/Makefile.am
@@ -0,0 +1,140 @@
+## Process this file with automake to produce Makefile.in
+
+SUBDIRS = \
+ layer-modes \
+ layer-modes-legacy \
+ tests
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"Gimp-Operations\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/app \
+ -I$(top_srcdir)/app \
+ $(CAIRO_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ -I$(includedir)
+
+noinst_LIBRARIES = \
+ libappoperations.a
+
+libappoperations_a_sources = \
+ operations-types.h \
+ operations-enums.h \
+ gimp-operations.c \
+ gimp-operations.h \
+ \
+ gimp-operation-config.c \
+ gimp-operation-config.h \
+ gimpoperationsettings.c \
+ gimpoperationsettings.h \
+ gimpbrightnesscontrastconfig.c \
+ gimpbrightnesscontrastconfig.h \
+ gimpcageconfig.c \
+ gimpcageconfig.h \
+ gimpcolorbalanceconfig.c \
+ gimpcolorbalanceconfig.h \
+ gimpcurvesconfig.c \
+ gimpcurvesconfig.h \
+ gimphuesaturationconfig.c \
+ gimphuesaturationconfig.h \
+ gimplevelsconfig.c \
+ gimplevelsconfig.h \
+ \
+ gimpoperationborder.c \
+ gimpoperationborder.h \
+ gimpoperationbuffersourcevalidate.c \
+ gimpoperationbuffersourcevalidate.h \
+ gimpoperationcagecoefcalc.c \
+ gimpoperationcagecoefcalc.h \
+ gimpoperationcagetransform.c \
+ gimpoperationcagetransform.h \
+ gimpoperationcomposecrop.c \
+ gimpoperationcomposecrop.h \
+ gimpoperationequalize.c \
+ gimpoperationequalize.h \
+ gimpoperationfillsource.c \
+ gimpoperationfillsource.h \
+ gimpoperationflood.c \
+ gimpoperationflood.h \
+ gimpoperationgradient.c \
+ gimpoperationgradient.h \
+ gimpoperationgrow.c \
+ gimpoperationgrow.h \
+ gimpoperationhistogramsink.c \
+ gimpoperationhistogramsink.h \
+ gimpoperationmaskcomponents.cc \
+ gimpoperationmaskcomponents.h \
+ gimpoperationoffset.c \
+ gimpoperationoffset.h \
+ gimpoperationprofiletransform.c \
+ gimpoperationprofiletransform.h \
+ gimpoperationscalarmultiply.c \
+ gimpoperationscalarmultiply.h \
+ gimpoperationsemiflatten.c \
+ gimpoperationsemiflatten.h \
+ gimpoperationsetalpha.c \
+ gimpoperationsetalpha.h \
+ gimpoperationshrink.c \
+ gimpoperationshrink.h \
+ gimpoperationthresholdalpha.c \
+ gimpoperationthresholdalpha.h \
+ \
+ gimpoperationpointfilter.c \
+ gimpoperationpointfilter.h \
+ gimpoperationbrightnesscontrast.c \
+ gimpoperationbrightnesscontrast.h \
+ gimpoperationcolorbalance.c \
+ gimpoperationcolorbalance.h \
+ gimpoperationcolorize.c \
+ gimpoperationcolorize.h \
+ gimpoperationcurves.c \
+ gimpoperationcurves.h \
+ gimpoperationdesaturate.c \
+ gimpoperationdesaturate.h \
+ gimpoperationhuesaturation.c \
+ gimpoperationhuesaturation.h \
+ gimpoperationlevels.c \
+ gimpoperationlevels.h \
+ gimpoperationposterize.c \
+ gimpoperationposterize.h \
+ gimpoperationthreshold.c \
+ gimpoperationthreshold.h
+
+libappoperations_a_built_sources = operations-enums.c
+
+libappoperations_a_SOURCES = \
+ $(libappoperations_a_built_sources) \
+ $(libappoperations_a_sources)
+
+#
+# rules to generate built sources
+#
+# setup autogeneration dependencies
+gen_sources = xgen-oec
+CLEANFILES = $(gen_sources)
+
+xgen-oec: $(srcdir)/operations-enums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <gio/gio.h>\n#include \"libgimpbase/gimpbase.h\"\n#include \"operations-enums.h\"\n#include \"gimp-intl.h\"" \
+ --fprod "\n/* enumerations from \"@basename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > $@
+
+# copy the generated enum file back to the source directory only if it's
+# changed; otherwise, only update its timestamp, so that the recipe isn't
+# executed again on the next build, however, allow this to (harmlessly) fail,
+# to support building from a read-only source tree.
+$(srcdir)/operations-enums.c: xgen-oec
+ $(AM_V_GEN) if ! cmp -s $< $@; then \
+ cp $< $@; \
+ else \
+ touch $@ 2> /dev/null \
+ || true; \
+ fi
diff --git a/app/operations/Makefile.in b/app/operations/Makefile.in
new file mode 100644
index 0000000..e960ea3
--- /dev/null
+++ b/app/operations/Makefile.in
@@ -0,0 +1,1376 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = app/operations
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_@AM_V@)
+am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
+am__v_AR_0 = @echo " AR " $@;
+am__v_AR_1 =
+libappoperations_a_AR = $(AR) $(ARFLAGS)
+libappoperations_a_LIBADD =
+am__objects_1 = operations-enums.$(OBJEXT)
+am__objects_2 = gimp-operations.$(OBJEXT) \
+ gimp-operation-config.$(OBJEXT) \
+ gimpoperationsettings.$(OBJEXT) \
+ gimpbrightnesscontrastconfig.$(OBJEXT) \
+ gimpcageconfig.$(OBJEXT) gimpcolorbalanceconfig.$(OBJEXT) \
+ gimpcurvesconfig.$(OBJEXT) gimphuesaturationconfig.$(OBJEXT) \
+ gimplevelsconfig.$(OBJEXT) gimpoperationborder.$(OBJEXT) \
+ gimpoperationbuffersourcevalidate.$(OBJEXT) \
+ gimpoperationcagecoefcalc.$(OBJEXT) \
+ gimpoperationcagetransform.$(OBJEXT) \
+ gimpoperationcomposecrop.$(OBJEXT) \
+ gimpoperationequalize.$(OBJEXT) \
+ gimpoperationfillsource.$(OBJEXT) gimpoperationflood.$(OBJEXT) \
+ gimpoperationgradient.$(OBJEXT) gimpoperationgrow.$(OBJEXT) \
+ gimpoperationhistogramsink.$(OBJEXT) \
+ gimpoperationmaskcomponents.$(OBJEXT) \
+ gimpoperationoffset.$(OBJEXT) \
+ gimpoperationprofiletransform.$(OBJEXT) \
+ gimpoperationscalarmultiply.$(OBJEXT) \
+ gimpoperationsemiflatten.$(OBJEXT) \
+ gimpoperationsetalpha.$(OBJEXT) gimpoperationshrink.$(OBJEXT) \
+ gimpoperationthresholdalpha.$(OBJEXT) \
+ gimpoperationpointfilter.$(OBJEXT) \
+ gimpoperationbrightnesscontrast.$(OBJEXT) \
+ gimpoperationcolorbalance.$(OBJEXT) \
+ gimpoperationcolorize.$(OBJEXT) gimpoperationcurves.$(OBJEXT) \
+ gimpoperationdesaturate.$(OBJEXT) \
+ gimpoperationhuesaturation.$(OBJEXT) \
+ gimpoperationlevels.$(OBJEXT) gimpoperationposterize.$(OBJEXT) \
+ gimpoperationthreshold.$(OBJEXT)
+am_libappoperations_a_OBJECTS = $(am__objects_1) $(am__objects_2)
+libappoperations_a_OBJECTS = $(am_libappoperations_a_OBJECTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimp-operation-config.Po \
+ ./$(DEPDIR)/gimp-operations.Po \
+ ./$(DEPDIR)/gimpbrightnesscontrastconfig.Po \
+ ./$(DEPDIR)/gimpcageconfig.Po \
+ ./$(DEPDIR)/gimpcolorbalanceconfig.Po \
+ ./$(DEPDIR)/gimpcurvesconfig.Po \
+ ./$(DEPDIR)/gimphuesaturationconfig.Po \
+ ./$(DEPDIR)/gimplevelsconfig.Po \
+ ./$(DEPDIR)/gimpoperationborder.Po \
+ ./$(DEPDIR)/gimpoperationbrightnesscontrast.Po \
+ ./$(DEPDIR)/gimpoperationbuffersourcevalidate.Po \
+ ./$(DEPDIR)/gimpoperationcagecoefcalc.Po \
+ ./$(DEPDIR)/gimpoperationcagetransform.Po \
+ ./$(DEPDIR)/gimpoperationcolorbalance.Po \
+ ./$(DEPDIR)/gimpoperationcolorize.Po \
+ ./$(DEPDIR)/gimpoperationcomposecrop.Po \
+ ./$(DEPDIR)/gimpoperationcurves.Po \
+ ./$(DEPDIR)/gimpoperationdesaturate.Po \
+ ./$(DEPDIR)/gimpoperationequalize.Po \
+ ./$(DEPDIR)/gimpoperationfillsource.Po \
+ ./$(DEPDIR)/gimpoperationflood.Po \
+ ./$(DEPDIR)/gimpoperationgradient.Po \
+ ./$(DEPDIR)/gimpoperationgrow.Po \
+ ./$(DEPDIR)/gimpoperationhistogramsink.Po \
+ ./$(DEPDIR)/gimpoperationhuesaturation.Po \
+ ./$(DEPDIR)/gimpoperationlevels.Po \
+ ./$(DEPDIR)/gimpoperationmaskcomponents.Po \
+ ./$(DEPDIR)/gimpoperationoffset.Po \
+ ./$(DEPDIR)/gimpoperationpointfilter.Po \
+ ./$(DEPDIR)/gimpoperationposterize.Po \
+ ./$(DEPDIR)/gimpoperationprofiletransform.Po \
+ ./$(DEPDIR)/gimpoperationscalarmultiply.Po \
+ ./$(DEPDIR)/gimpoperationsemiflatten.Po \
+ ./$(DEPDIR)/gimpoperationsetalpha.Po \
+ ./$(DEPDIR)/gimpoperationsettings.Po \
+ ./$(DEPDIR)/gimpoperationshrink.Po \
+ ./$(DEPDIR)/gimpoperationthreshold.Po \
+ ./$(DEPDIR)/gimpoperationthresholdalpha.Po \
+ ./$(DEPDIR)/operations-enums.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+SOURCES = $(libappoperations_a_SOURCES)
+DIST_SOURCES = $(libappoperations_a_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = \
+ layer-modes \
+ layer-modes-legacy \
+ tests
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"Gimp-Operations\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/app \
+ -I$(top_srcdir)/app \
+ $(CAIRO_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ -I$(includedir)
+
+noinst_LIBRARIES = \
+ libappoperations.a
+
+libappoperations_a_sources = \
+ operations-types.h \
+ operations-enums.h \
+ gimp-operations.c \
+ gimp-operations.h \
+ \
+ gimp-operation-config.c \
+ gimp-operation-config.h \
+ gimpoperationsettings.c \
+ gimpoperationsettings.h \
+ gimpbrightnesscontrastconfig.c \
+ gimpbrightnesscontrastconfig.h \
+ gimpcageconfig.c \
+ gimpcageconfig.h \
+ gimpcolorbalanceconfig.c \
+ gimpcolorbalanceconfig.h \
+ gimpcurvesconfig.c \
+ gimpcurvesconfig.h \
+ gimphuesaturationconfig.c \
+ gimphuesaturationconfig.h \
+ gimplevelsconfig.c \
+ gimplevelsconfig.h \
+ \
+ gimpoperationborder.c \
+ gimpoperationborder.h \
+ gimpoperationbuffersourcevalidate.c \
+ gimpoperationbuffersourcevalidate.h \
+ gimpoperationcagecoefcalc.c \
+ gimpoperationcagecoefcalc.h \
+ gimpoperationcagetransform.c \
+ gimpoperationcagetransform.h \
+ gimpoperationcomposecrop.c \
+ gimpoperationcomposecrop.h \
+ gimpoperationequalize.c \
+ gimpoperationequalize.h \
+ gimpoperationfillsource.c \
+ gimpoperationfillsource.h \
+ gimpoperationflood.c \
+ gimpoperationflood.h \
+ gimpoperationgradient.c \
+ gimpoperationgradient.h \
+ gimpoperationgrow.c \
+ gimpoperationgrow.h \
+ gimpoperationhistogramsink.c \
+ gimpoperationhistogramsink.h \
+ gimpoperationmaskcomponents.cc \
+ gimpoperationmaskcomponents.h \
+ gimpoperationoffset.c \
+ gimpoperationoffset.h \
+ gimpoperationprofiletransform.c \
+ gimpoperationprofiletransform.h \
+ gimpoperationscalarmultiply.c \
+ gimpoperationscalarmultiply.h \
+ gimpoperationsemiflatten.c \
+ gimpoperationsemiflatten.h \
+ gimpoperationsetalpha.c \
+ gimpoperationsetalpha.h \
+ gimpoperationshrink.c \
+ gimpoperationshrink.h \
+ gimpoperationthresholdalpha.c \
+ gimpoperationthresholdalpha.h \
+ \
+ gimpoperationpointfilter.c \
+ gimpoperationpointfilter.h \
+ gimpoperationbrightnesscontrast.c \
+ gimpoperationbrightnesscontrast.h \
+ gimpoperationcolorbalance.c \
+ gimpoperationcolorbalance.h \
+ gimpoperationcolorize.c \
+ gimpoperationcolorize.h \
+ gimpoperationcurves.c \
+ gimpoperationcurves.h \
+ gimpoperationdesaturate.c \
+ gimpoperationdesaturate.h \
+ gimpoperationhuesaturation.c \
+ gimpoperationhuesaturation.h \
+ gimpoperationlevels.c \
+ gimpoperationlevels.h \
+ gimpoperationposterize.c \
+ gimpoperationposterize.h \
+ gimpoperationthreshold.c \
+ gimpoperationthreshold.h
+
+libappoperations_a_built_sources = operations-enums.c
+libappoperations_a_SOURCES = \
+ $(libappoperations_a_built_sources) \
+ $(libappoperations_a_sources)
+
+
+#
+# rules to generate built sources
+#
+# setup autogeneration dependencies
+gen_sources = xgen-oec
+CLEANFILES = $(gen_sources)
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .cc .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu app/operations/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu app/operations/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+
+libappoperations.a: $(libappoperations_a_OBJECTS) $(libappoperations_a_DEPENDENCIES) $(EXTRA_libappoperations_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libappoperations.a
+ $(AM_V_AR)$(libappoperations_a_AR) libappoperations.a $(libappoperations_a_OBJECTS) $(libappoperations_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libappoperations.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-operation-config.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-operations.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrightnesscontrastconfig.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcageconfig.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorbalanceconfig.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcurvesconfig.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimphuesaturationconfig.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplevelsconfig.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationborder.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationbrightnesscontrast.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationbuffersourcevalidate.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationcagecoefcalc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationcagetransform.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationcolorbalance.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationcolorize.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationcomposecrop.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationcurves.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationdesaturate.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationequalize.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationfillsource.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationflood.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationgradient.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationgrow.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationhistogramsink.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationhuesaturation.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationlevels.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationmaskcomponents.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationoffset.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationpointfilter.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationposterize.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationprofiletransform.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationscalarmultiply.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationsemiflatten.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationsetalpha.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationsettings.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationshrink.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationthreshold.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationthresholdalpha.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/operations-enums.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+.cc.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(LIBRARIES)
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/gimp-operation-config.Po
+ -rm -f ./$(DEPDIR)/gimp-operations.Po
+ -rm -f ./$(DEPDIR)/gimpbrightnesscontrastconfig.Po
+ -rm -f ./$(DEPDIR)/gimpcageconfig.Po
+ -rm -f ./$(DEPDIR)/gimpcolorbalanceconfig.Po
+ -rm -f ./$(DEPDIR)/gimpcurvesconfig.Po
+ -rm -f ./$(DEPDIR)/gimphuesaturationconfig.Po
+ -rm -f ./$(DEPDIR)/gimplevelsconfig.Po
+ -rm -f ./$(DEPDIR)/gimpoperationborder.Po
+ -rm -f ./$(DEPDIR)/gimpoperationbrightnesscontrast.Po
+ -rm -f ./$(DEPDIR)/gimpoperationbuffersourcevalidate.Po
+ -rm -f ./$(DEPDIR)/gimpoperationcagecoefcalc.Po
+ -rm -f ./$(DEPDIR)/gimpoperationcagetransform.Po
+ -rm -f ./$(DEPDIR)/gimpoperationcolorbalance.Po
+ -rm -f ./$(DEPDIR)/gimpoperationcolorize.Po
+ -rm -f ./$(DEPDIR)/gimpoperationcomposecrop.Po
+ -rm -f ./$(DEPDIR)/gimpoperationcurves.Po
+ -rm -f ./$(DEPDIR)/gimpoperationdesaturate.Po
+ -rm -f ./$(DEPDIR)/gimpoperationequalize.Po
+ -rm -f ./$(DEPDIR)/gimpoperationfillsource.Po
+ -rm -f ./$(DEPDIR)/gimpoperationflood.Po
+ -rm -f ./$(DEPDIR)/gimpoperationgradient.Po
+ -rm -f ./$(DEPDIR)/gimpoperationgrow.Po
+ -rm -f ./$(DEPDIR)/gimpoperationhistogramsink.Po
+ -rm -f ./$(DEPDIR)/gimpoperationhuesaturation.Po
+ -rm -f ./$(DEPDIR)/gimpoperationlevels.Po
+ -rm -f ./$(DEPDIR)/gimpoperationmaskcomponents.Po
+ -rm -f ./$(DEPDIR)/gimpoperationoffset.Po
+ -rm -f ./$(DEPDIR)/gimpoperationpointfilter.Po
+ -rm -f ./$(DEPDIR)/gimpoperationposterize.Po
+ -rm -f ./$(DEPDIR)/gimpoperationprofiletransform.Po
+ -rm -f ./$(DEPDIR)/gimpoperationscalarmultiply.Po
+ -rm -f ./$(DEPDIR)/gimpoperationsemiflatten.Po
+ -rm -f ./$(DEPDIR)/gimpoperationsetalpha.Po
+ -rm -f ./$(DEPDIR)/gimpoperationsettings.Po
+ -rm -f ./$(DEPDIR)/gimpoperationshrink.Po
+ -rm -f ./$(DEPDIR)/gimpoperationthreshold.Po
+ -rm -f ./$(DEPDIR)/gimpoperationthresholdalpha.Po
+ -rm -f ./$(DEPDIR)/operations-enums.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/gimp-operation-config.Po
+ -rm -f ./$(DEPDIR)/gimp-operations.Po
+ -rm -f ./$(DEPDIR)/gimpbrightnesscontrastconfig.Po
+ -rm -f ./$(DEPDIR)/gimpcageconfig.Po
+ -rm -f ./$(DEPDIR)/gimpcolorbalanceconfig.Po
+ -rm -f ./$(DEPDIR)/gimpcurvesconfig.Po
+ -rm -f ./$(DEPDIR)/gimphuesaturationconfig.Po
+ -rm -f ./$(DEPDIR)/gimplevelsconfig.Po
+ -rm -f ./$(DEPDIR)/gimpoperationborder.Po
+ -rm -f ./$(DEPDIR)/gimpoperationbrightnesscontrast.Po
+ -rm -f ./$(DEPDIR)/gimpoperationbuffersourcevalidate.Po
+ -rm -f ./$(DEPDIR)/gimpoperationcagecoefcalc.Po
+ -rm -f ./$(DEPDIR)/gimpoperationcagetransform.Po
+ -rm -f ./$(DEPDIR)/gimpoperationcolorbalance.Po
+ -rm -f ./$(DEPDIR)/gimpoperationcolorize.Po
+ -rm -f ./$(DEPDIR)/gimpoperationcomposecrop.Po
+ -rm -f ./$(DEPDIR)/gimpoperationcurves.Po
+ -rm -f ./$(DEPDIR)/gimpoperationdesaturate.Po
+ -rm -f ./$(DEPDIR)/gimpoperationequalize.Po
+ -rm -f ./$(DEPDIR)/gimpoperationfillsource.Po
+ -rm -f ./$(DEPDIR)/gimpoperationflood.Po
+ -rm -f ./$(DEPDIR)/gimpoperationgradient.Po
+ -rm -f ./$(DEPDIR)/gimpoperationgrow.Po
+ -rm -f ./$(DEPDIR)/gimpoperationhistogramsink.Po
+ -rm -f ./$(DEPDIR)/gimpoperationhuesaturation.Po
+ -rm -f ./$(DEPDIR)/gimpoperationlevels.Po
+ -rm -f ./$(DEPDIR)/gimpoperationmaskcomponents.Po
+ -rm -f ./$(DEPDIR)/gimpoperationoffset.Po
+ -rm -f ./$(DEPDIR)/gimpoperationpointfilter.Po
+ -rm -f ./$(DEPDIR)/gimpoperationposterize.Po
+ -rm -f ./$(DEPDIR)/gimpoperationprofiletransform.Po
+ -rm -f ./$(DEPDIR)/gimpoperationscalarmultiply.Po
+ -rm -f ./$(DEPDIR)/gimpoperationsemiflatten.Po
+ -rm -f ./$(DEPDIR)/gimpoperationsetalpha.Po
+ -rm -f ./$(DEPDIR)/gimpoperationsettings.Po
+ -rm -f ./$(DEPDIR)/gimpoperationshrink.Po
+ -rm -f ./$(DEPDIR)/gimpoperationthreshold.Po
+ -rm -f ./$(DEPDIR)/gimpoperationthresholdalpha.Po
+ -rm -f ./$(DEPDIR)/operations-enums.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic clean-libtool \
+ clean-noinstLIBRARIES cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am
+
+.PRECIOUS: Makefile
+
+
+xgen-oec: $(srcdir)/operations-enums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <gio/gio.h>\n#include \"libgimpbase/gimpbase.h\"\n#include \"operations-enums.h\"\n#include \"gimp-intl.h\"" \
+ --fprod "\n/* enumerations from \"@basename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > $@
+
+# copy the generated enum file back to the source directory only if it's
+# changed; otherwise, only update its timestamp, so that the recipe isn't
+# executed again on the next build, however, allow this to (harmlessly) fail,
+# to support building from a read-only source tree.
+$(srcdir)/operations-enums.c: xgen-oec
+ $(AM_V_GEN) if ! cmp -s $< $@; then \
+ cp $< $@; \
+ else \
+ touch $@ 2> /dev/null \
+ || true; \
+ fi
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/app/operations/gimp-operation-config.c b/app/operations/gimp-operation-config.c
new file mode 100644
index 0000000..492cefd
--- /dev/null
+++ b/app/operations/gimp-operation-config.c
@@ -0,0 +1,827 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 <string.h>
+
+#include <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "operations-types.h"
+
+#include "core/gimp.h"
+
+#include "core/gimplist.h"
+#include "core/gimpparamspecs-duplicate.h"
+#include "core/gimpviewable.h"
+
+#include "gegl/gimp-gegl-utils.h"
+
+#include "gimp-operation-config.h"
+#include "gimpoperationsettings.h"
+
+
+/* local function prototypes */
+
+static void gimp_operation_config_config_sync (GObject *config,
+ const GParamSpec *gimp_pspec,
+ GeglNode *node);
+static void gimp_operation_config_config_notify (GObject *config,
+ const GParamSpec *gimp_pspec,
+ GeglNode *node);
+static void gimp_operation_config_node_notify (GeglNode *node,
+ const GParamSpec *gegl_pspec,
+ GObject *config);
+
+static GFile * gimp_operation_config_get_file (GType config_type);
+static void gimp_operation_config_add_sep (GimpContainer *container);
+static void gimp_operation_config_remove_sep (GimpContainer *container);
+
+
+/* public functions */
+
+static GHashTable *
+gimp_operation_config_get_type_table (Gimp *gimp)
+{
+ static GHashTable *config_types = NULL;
+
+ if (! config_types)
+ config_types = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ NULL);
+
+ return config_types;
+}
+
+static GHashTable *
+gimp_operation_config_get_container_table (Gimp *gimp)
+{
+ static GHashTable *config_containers = NULL;
+
+ if (! config_containers)
+ config_containers = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify) g_object_unref);
+
+ return config_containers;
+}
+
+static GValue *
+gimp_operation_config_value_new (GParamSpec *pspec)
+{
+ GValue *value = g_slice_new0 (GValue);
+
+ g_value_init (value, pspec->value_type);
+ g_param_value_set_default (pspec, value);
+
+ return value;
+}
+
+static void
+gimp_operation_config_value_free (GValue *value)
+{
+ g_value_unset (value);
+ g_slice_free (GValue, value);
+}
+
+static GHashTable *
+gimp_operation_config_get_properties (GObject *object)
+{
+ GHashTable *properties = g_object_get_data (object, "properties");
+
+ if (! properties)
+ {
+ properties = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) gimp_operation_config_value_free);
+
+ g_object_set_data_full (object, "properties", properties,
+ (GDestroyNotify) g_hash_table_unref);
+ }
+
+ return properties;
+}
+
+static GValue *
+gimp_operation_config_value_get (GObject *object,
+ GParamSpec *pspec)
+{
+ GHashTable *properties = gimp_operation_config_get_properties (object);
+ GValue *value;
+
+ value = g_hash_table_lookup (properties, pspec->name);
+
+ if (! value)
+ {
+ value = gimp_operation_config_value_new (pspec);
+ g_hash_table_insert (properties, g_strdup (pspec->name), value);
+ }
+
+ return value;
+}
+
+static void
+gimp_operation_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GValue *val = gimp_operation_config_value_get (object, pspec);
+
+ g_value_copy (value, val);
+}
+
+static void
+gimp_operation_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GValue *val = gimp_operation_config_value_get (object, pspec);
+
+ g_value_copy (val, value);
+}
+
+static void
+gimp_operation_config_class_init (GObjectClass *klass,
+ const gchar *operation)
+{
+ GParamSpec **pspecs;
+ guint n_pspecs;
+ gint i;
+
+ klass->set_property = gimp_operation_config_set_property;
+ klass->get_property = gimp_operation_config_get_property;
+
+ pspecs = gegl_operation_list_properties (operation, &n_pspecs);
+
+ for (i = 0; i < n_pspecs; i++)
+ {
+ GParamSpec *pspec = pspecs[i];
+
+ if ((pspec->flags & G_PARAM_READABLE) &&
+ (pspec->flags & G_PARAM_WRITABLE) &&
+ strcmp (pspec->name, "input") &&
+ strcmp (pspec->name, "output"))
+ {
+ GParamSpec *copy = gimp_param_spec_duplicate (pspec);
+
+ if (copy)
+ {
+ g_object_class_install_property (klass, i + 1, copy);
+ }
+ }
+ }
+
+ g_free (pspecs);
+}
+
+static gboolean
+gimp_operation_config_equal (GimpConfig *a,
+ GimpConfig *b)
+{
+ GList *diff;
+ gboolean equal = TRUE;
+
+ diff = gimp_config_diff (G_OBJECT (a), G_OBJECT (b),
+ GIMP_CONFIG_PARAM_SERIALIZE);
+
+ if (G_TYPE_FROM_INSTANCE (a) == G_TYPE_FROM_INSTANCE (b))
+ {
+ GList *list;
+
+ for (list = diff; list; list = g_list_next (list))
+ {
+ GParamSpec *pspec = list->data;
+
+ if (g_type_is_a (pspec->owner_type, GIMP_TYPE_OPERATION_SETTINGS))
+ {
+ equal = FALSE;
+ break;
+ }
+ }
+ }
+ else if (diff)
+ {
+ equal = FALSE;
+ }
+
+ g_list_free (diff);
+
+ return equal;
+}
+
+static void
+gimp_operation_config_config_iface_init (GimpConfigInterface *iface)
+{
+ iface->equal = gimp_operation_config_equal;
+}
+
+
+/* public functions */
+
+void
+gimp_operation_config_register (Gimp *gimp,
+ const gchar *operation,
+ GType config_type)
+{
+ GHashTable *config_types;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+ g_return_if_fail (operation != NULL);
+ g_return_if_fail (g_type_is_a (config_type, GIMP_TYPE_OBJECT));
+
+ config_types = gimp_operation_config_get_type_table (gimp);
+
+ g_hash_table_insert (config_types,
+ g_strdup (operation),
+ (gpointer) config_type);
+ }
+
+GType
+gimp_operation_config_get_type (Gimp *gimp,
+ const gchar *operation,
+ const gchar *icon_name,
+ GType parent_type)
+{
+ GHashTable *config_types;
+ GType config_type;
+
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), G_TYPE_NONE);
+ g_return_val_if_fail (operation != NULL, G_TYPE_NONE);
+
+ config_types = gimp_operation_config_get_type_table (gimp);
+
+ config_type = (GType) g_hash_table_lookup (config_types, operation);
+
+ if (! config_type)
+ {
+ GTypeQuery query;
+
+ g_return_val_if_fail (g_type_is_a (parent_type, GIMP_TYPE_OBJECT),
+ G_TYPE_NONE);
+
+ g_type_query (parent_type, &query);
+
+ {
+ GTypeInfo info =
+ {
+ query.class_size,
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gimp_operation_config_class_init,
+ NULL, /* class_finalize */
+ operation,
+ query.instance_size,
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL,
+ };
+
+ const GInterfaceInfo config_info =
+ {
+ (GInterfaceInitFunc) gimp_operation_config_config_iface_init,
+ NULL, /* interface_finalize */
+ NULL /* interface_data */
+ };
+
+ gchar *type_name = g_strdup_printf ("GimpGegl-%s-config",
+ operation);
+
+ g_strcanon (type_name,
+ G_CSET_DIGITS "-" G_CSET_a_2_z G_CSET_A_2_Z, '-');
+
+ config_type = g_type_register_static (parent_type, type_name,
+ &info, 0);
+
+ g_free (type_name);
+
+ g_type_add_interface_static (config_type, GIMP_TYPE_CONFIG,
+ &config_info);
+
+ if (icon_name && g_type_is_a (config_type, GIMP_TYPE_VIEWABLE))
+ {
+ GimpViewableClass *viewable_class = g_type_class_ref (config_type);
+
+ viewable_class->default_icon_name = g_strdup (icon_name);
+
+ g_type_class_unref (viewable_class);
+ }
+
+ gimp_operation_config_register (gimp, operation, config_type);
+ }
+ }
+
+ return config_type;
+}
+
+GimpContainer *
+gimp_operation_config_get_container (Gimp *gimp,
+ GType config_type,
+ GCompareFunc sort_func)
+{
+ GHashTable *config_containers;
+ GimpContainer *container;
+
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
+ g_return_val_if_fail (g_type_is_a (config_type, GIMP_TYPE_OBJECT), NULL);
+
+ config_containers = gimp_operation_config_get_container_table (gimp);
+
+ container = g_hash_table_lookup (config_containers, (gpointer) config_type);
+
+ if (! container)
+ {
+ container = gimp_list_new (config_type, TRUE);
+ gimp_list_set_sort_func (GIMP_LIST (container), sort_func);
+
+ g_hash_table_insert (config_containers,
+ (gpointer) config_type, container);
+
+ gimp_operation_config_deserialize (gimp, container, NULL);
+
+ if (gimp_container_get_n_children (container) == 0)
+ {
+ GFile *file = gimp_operation_config_get_file (config_type);
+
+ if (! g_file_query_exists (file, NULL))
+ {
+ GQuark quark = g_quark_from_static_string ("compat-file");
+ GFile *compat_file;
+
+ compat_file = g_type_get_qdata (config_type, quark);
+
+ if (compat_file)
+ {
+ if (! g_file_move (compat_file, file, 0,
+ NULL, NULL, NULL, NULL))
+ {
+ gimp_operation_config_deserialize (gimp, container,
+ compat_file);
+ }
+ else
+ {
+ gimp_operation_config_deserialize (gimp, container, NULL);
+ }
+ }
+ }
+
+ g_object_unref (file);
+ }
+
+ gimp_operation_config_add_sep (container);
+ }
+
+ return container;
+}
+
+void
+gimp_operation_config_serialize (Gimp *gimp,
+ GimpContainer *container,
+ GFile *file)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+ g_return_if_fail (GIMP_IS_CONTAINER (container));
+ g_return_if_fail (file == NULL || G_IS_FILE (file));
+
+ if (file)
+ {
+ g_object_ref (file);
+ }
+ else
+ {
+ GType config_type = gimp_container_get_children_type (container);
+
+ file = gimp_operation_config_get_file (config_type);
+ }
+
+ if (gimp->be_verbose)
+ g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file));
+
+ gimp_operation_config_remove_sep (container);
+
+ if (! gimp_config_serialize_to_gfile (GIMP_CONFIG (container),
+ file,
+ "settings",
+ "end of settings",
+ NULL, &error))
+ {
+ gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR,
+ error->message);
+ g_clear_error (&error);
+ }
+
+ gimp_operation_config_add_sep (container);
+
+ g_object_unref (file);
+}
+
+void
+gimp_operation_config_deserialize (Gimp *gimp,
+ GimpContainer *container,
+ GFile *file)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+ g_return_if_fail (GIMP_IS_CONTAINER (container));
+ g_return_if_fail (file == NULL || G_IS_FILE (file));
+
+ if (file)
+ {
+ g_object_ref (file);
+ }
+ else
+ {
+ GType config_type = gimp_container_get_children_type (container);
+
+ file = gimp_operation_config_get_file (config_type);
+ }
+
+ if (gimp->be_verbose)
+ g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file));
+
+ if (! gimp_config_deserialize_gfile (GIMP_CONFIG (container),
+ file,
+ NULL, &error))
+ {
+ if (error->code != GIMP_CONFIG_ERROR_OPEN_ENOENT)
+ gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR,
+ error->message);
+
+ g_clear_error (&error);
+ }
+
+ g_object_unref (file);
+}
+
+void
+gimp_operation_config_sync_node (GObject *config,
+ GeglNode *node)
+{
+ GParamSpec **pspecs;
+ gchar *operation;
+ guint n_pspecs;
+ gint i;
+
+ g_return_if_fail (G_IS_OBJECT (config));
+ g_return_if_fail (GEGL_IS_NODE (node));
+
+ gegl_node_get (node,
+ "operation", &operation,
+ NULL);
+
+ g_return_if_fail (operation != NULL);
+
+ pspecs = gegl_operation_list_properties (operation, &n_pspecs);
+ g_free (operation);
+
+ for (i = 0; i < n_pspecs; i++)
+ {
+ GParamSpec *gegl_pspec = pspecs[i];
+ GParamSpec *gimp_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
+ gegl_pspec->name);
+
+ /* if the operation has an object property of the config's
+ * type, use the config object directly
+ */
+ if (G_IS_PARAM_SPEC_OBJECT (gegl_pspec) &&
+ gegl_pspec->value_type == G_TYPE_FROM_INSTANCE (config))
+ {
+ gegl_node_set (node,
+ gegl_pspec->name, config,
+ NULL);
+ }
+ else if (gimp_pspec)
+ {
+ GValue value = G_VALUE_INIT;
+
+ g_value_init (&value, gimp_pspec->value_type);
+
+ g_object_get_property (G_OBJECT (config), gimp_pspec->name,
+ &value);
+
+ if (GEGL_IS_PARAM_SPEC_COLOR (gegl_pspec))
+ {
+ GimpRGB gimp_color;
+ GeglColor *gegl_color;
+
+ gimp_value_get_rgb (&value, &gimp_color);
+ g_value_unset (&value);
+
+ gegl_color = gimp_gegl_color_new (&gimp_color);
+
+ g_value_init (&value, gegl_pspec->value_type);
+ g_value_take_object (&value, gegl_color);
+ }
+
+ gegl_node_set_property (node, gegl_pspec->name,
+ &value);
+
+ g_value_unset (&value);
+ }
+ }
+
+ g_free (pspecs);
+}
+
+void
+gimp_operation_config_connect_node (GObject *config,
+ GeglNode *node)
+{
+ GParamSpec **pspecs;
+ gchar *operation;
+ guint n_pspecs;
+ gint i;
+
+ g_return_if_fail (G_IS_OBJECT (config));
+ g_return_if_fail (GEGL_IS_NODE (node));
+
+ gegl_node_get (node,
+ "operation", &operation,
+ NULL);
+
+ g_return_if_fail (operation != NULL);
+
+ pspecs = gegl_operation_list_properties (operation, &n_pspecs);
+ g_free (operation);
+
+ for (i = 0; i < n_pspecs; i++)
+ {
+ GParamSpec *pspec = pspecs[i];
+
+ /* if the operation has an object property of the config's
+ * type, connect it to a special callback and done
+ */
+ if (G_IS_PARAM_SPEC_OBJECT (pspec) &&
+ pspec->value_type == G_TYPE_FROM_INSTANCE (config))
+ {
+ g_signal_connect_object (config, "notify",
+ G_CALLBACK (gimp_operation_config_config_sync),
+ node, 0);
+ g_free (pspecs);
+ return;
+ }
+ }
+
+ for (i = 0; i < n_pspecs; i++)
+ {
+ GParamSpec *gegl_pspec = pspecs[i];
+ GParamSpec *gimp_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
+ gegl_pspec->name);
+
+ if (gimp_pspec)
+ {
+ gchar *notify_name = g_strconcat ("notify::", gimp_pspec->name, NULL);
+
+ g_signal_connect_object (config, notify_name,
+ G_CALLBACK (gimp_operation_config_config_notify),
+ node, 0);
+
+ g_signal_connect_object (node, notify_name,
+ G_CALLBACK (gimp_operation_config_node_notify),
+ config, 0);
+
+ g_free (notify_name);
+ }
+ }
+
+ g_free (pspecs);
+}
+
+GParamSpec **
+gimp_operation_config_list_properties (GObject *config,
+ GType owner_type,
+ GParamFlags flags,
+ guint *n_pspecs)
+{
+ GParamSpec **param_specs;
+ guint n_param_specs;
+ gint i, j;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+
+ param_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (config),
+ &n_param_specs);
+
+ for (i = 0, j = 0; i < n_param_specs; i++)
+ {
+ GParamSpec *pspec = param_specs[i];
+
+ /* ignore properties of parent classes of owner_type */
+ if (! g_type_is_a (pspec->owner_type, owner_type))
+ continue;
+
+ if (flags && ((pspec->flags & flags) != flags))
+ continue;
+
+ if (gimp_gegl_param_spec_has_key (pspec, "role", "output-extent"))
+ continue;
+
+ param_specs[j] = param_specs[i];
+ j++;
+ }
+
+ if (n_pspecs)
+ *n_pspecs = j;
+
+ if (j == 0)
+ {
+ g_free (param_specs);
+ param_specs = NULL;
+ }
+
+ return param_specs;
+}
+
+
+/* private functions */
+
+static void
+gimp_operation_config_config_sync (GObject *config,
+ const GParamSpec *gimp_pspec,
+ GeglNode *node)
+{
+ gimp_operation_config_sync_node (config, node);
+}
+
+static void
+gimp_operation_config_config_notify (GObject *config,
+ const GParamSpec *gimp_pspec,
+ GeglNode *node)
+{
+ GParamSpec *gegl_pspec = gegl_node_find_property (node, gimp_pspec->name);
+
+ if (gegl_pspec)
+ {
+ GValue value = G_VALUE_INIT;
+ gulong handler;
+
+ g_value_init (&value, gimp_pspec->value_type);
+ g_object_get_property (config, gimp_pspec->name, &value);
+
+ if (GEGL_IS_PARAM_SPEC_COLOR (gegl_pspec))
+ {
+ GimpRGB gimp_color;
+ GeglColor *gegl_color;
+
+ gimp_value_get_rgb (&value, &gimp_color);
+ g_value_unset (&value);
+
+ gegl_color = gimp_gegl_color_new (&gimp_color);
+
+ g_value_init (&value, gegl_pspec->value_type);
+ g_value_take_object (&value, gegl_color);
+ }
+
+ handler = g_signal_handler_find (node,
+ G_SIGNAL_MATCH_DETAIL |
+ G_SIGNAL_MATCH_FUNC |
+ G_SIGNAL_MATCH_DATA,
+ 0,
+ g_quark_from_string (gegl_pspec->name),
+ NULL,
+ gimp_operation_config_node_notify,
+ config);
+
+ if (handler)
+ g_signal_handler_block (node, handler);
+
+ gegl_node_set_property (node, gegl_pspec->name, &value);
+ g_value_unset (&value);
+
+ if (handler)
+ g_signal_handler_unblock (node, handler);
+
+ }
+}
+
+static void
+gimp_operation_config_node_notify (GeglNode *node,
+ const GParamSpec *gegl_pspec,
+ GObject *config)
+{
+ GParamSpec *gimp_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
+ gegl_pspec->name);
+
+ if (gimp_pspec)
+ {
+ GValue value = G_VALUE_INIT;
+ gulong handler;
+
+ g_value_init (&value, gegl_pspec->value_type);
+ gegl_node_get_property (node, gegl_pspec->name, &value);
+
+ if (GEGL_IS_PARAM_SPEC_COLOR (gegl_pspec))
+ {
+ GeglColor *gegl_color;
+ GimpRGB gimp_color;
+
+ gegl_color = g_value_dup_object (&value);
+ g_value_unset (&value);
+
+ if (gegl_color)
+ {
+ gegl_color_get_rgba (gegl_color,
+ &gimp_color.r,
+ &gimp_color.g,
+ &gimp_color.b,
+ &gimp_color.a);
+ g_object_unref (gegl_color);
+ }
+ else
+ {
+ gimp_rgba_set (&gimp_color, 0.0, 0.0, 0.0, 1.0);
+ }
+
+ g_value_init (&value, gimp_pspec->value_type);
+ gimp_value_set_rgb (&value, &gimp_color);
+ }
+
+ handler = g_signal_handler_find (config,
+ G_SIGNAL_MATCH_DETAIL |
+ G_SIGNAL_MATCH_FUNC |
+ G_SIGNAL_MATCH_DATA,
+ 0,
+ g_quark_from_string (gimp_pspec->name),
+ NULL,
+ gimp_operation_config_config_notify,
+ node);
+
+ if (handler)
+ g_signal_handler_block (config, handler);
+
+ g_object_set_property (config, gimp_pspec->name, &value);
+ g_value_unset (&value);
+
+ if (handler)
+ g_signal_handler_unblock (config, handler);
+ }
+}
+
+static GFile *
+gimp_operation_config_get_file (GType config_type)
+{
+ GFile *file;
+ gchar *basename;
+
+ basename = g_strconcat (g_type_name (config_type), ".settings", NULL);
+ file = gimp_directory_file ("filters", basename, NULL);
+ g_free (basename);
+
+ return file;
+}
+
+static void
+gimp_operation_config_add_sep (GimpContainer *container)
+{
+ GimpObject *sep = g_object_get_data (G_OBJECT (container), "separator");
+
+ if (! sep)
+ {
+ sep = g_object_new (gimp_container_get_children_type (container),
+ NULL);
+
+ gimp_container_add (container, sep);
+ g_object_unref (sep);
+
+ g_object_set_data (G_OBJECT (container), "separator", sep);
+ }
+}
+
+static void
+gimp_operation_config_remove_sep (GimpContainer *container)
+{
+ GimpObject *sep = g_object_get_data (G_OBJECT (container), "separator");
+
+ if (sep)
+ {
+ gimp_container_remove (container, sep);
+
+ g_object_set_data (G_OBJECT (container), "separator", NULL);
+ }
+}
diff --git a/app/operations/gimp-operation-config.h b/app/operations/gimp-operation-config.h
new file mode 100644
index 0000000..5f920e0
--- /dev/null
+++ b/app/operations/gimp-operation-config.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_CONFIG_H__
+#define __GIMP_OPERATION_CONFIG_H__
+
+
+void gimp_operation_config_register (Gimp *gimp,
+ const gchar *operation,
+ GType config_type);
+
+GType gimp_operation_config_get_type (Gimp *gimp,
+ const gchar *operation,
+ const gchar *icon_name,
+ GType parent_type);
+
+GimpContainer * gimp_operation_config_get_container (Gimp *gimp,
+ GType config_type,
+ GCompareFunc sort_func);
+
+void gimp_operation_config_serialize (Gimp *gimp,
+ GimpContainer *container,
+ GFile *file);
+void gimp_operation_config_deserialize (Gimp *gimp,
+ GimpContainer *container,
+ GFile *file);
+
+void gimp_operation_config_sync_node (GObject *config,
+ GeglNode *node);
+void gimp_operation_config_connect_node (GObject *config,
+ GeglNode *node);
+
+GParamSpec ** gimp_operation_config_list_properties (GObject *config,
+ GType owner_type,
+ GParamFlags flags,
+ guint *n_pspecs);
+
+
+#endif /* __GIMP_OPERATION_CONFIG_H__ */
diff --git a/app/operations/gimp-operations.c b/app/operations/gimp-operations.c
new file mode 100644
index 0000000..7cb30fd
--- /dev/null
+++ b/app/operations/gimp-operations.c
@@ -0,0 +1,225 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimp-operations.c
+ *
+ * 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 <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "operations-types.h"
+
+#include "core/gimp.h"
+
+#include "gimp-operations.h"
+
+#include "gimpoperationborder.h"
+#include "gimpoperationbuffersourcevalidate.h"
+#include "gimpoperationcagecoefcalc.h"
+#include "gimpoperationcagetransform.h"
+#include "gimpoperationcomposecrop.h"
+#include "gimpoperationequalize.h"
+#include "gimpoperationfillsource.h"
+#include "gimpoperationflood.h"
+#include "gimpoperationgradient.h"
+#include "gimpoperationgrow.h"
+#include "gimpoperationhistogramsink.h"
+#include "gimpoperationmaskcomponents.h"
+#include "gimpoperationoffset.h"
+#include "gimpoperationprofiletransform.h"
+#include "gimpoperationscalarmultiply.h"
+#include "gimpoperationsemiflatten.h"
+#include "gimpoperationsetalpha.h"
+#include "gimpoperationshrink.h"
+#include "gimpoperationthresholdalpha.h"
+
+#include "gimpoperationbrightnesscontrast.h"
+#include "gimpoperationcolorbalance.h"
+#include "gimpoperationcolorize.h"
+#include "gimpoperationcurves.h"
+#include "gimpoperationdesaturate.h"
+#include "gimpoperationhuesaturation.h"
+#include "gimpoperationlevels.h"
+#include "gimpoperationposterize.h"
+#include "gimpoperationthreshold.h"
+
+#include "gimp-operation-config.h"
+#include "gimpbrightnesscontrastconfig.h"
+#include "gimpcolorbalanceconfig.h"
+#include "gimpcurvesconfig.h"
+#include "gimphuesaturationconfig.h"
+#include "gimplevelsconfig.h"
+
+#include "layer-modes-legacy/gimpoperationadditionlegacy.h"
+#include "layer-modes-legacy/gimpoperationburnlegacy.h"
+#include "layer-modes-legacy/gimpoperationdarkenonlylegacy.h"
+#include "layer-modes-legacy/gimpoperationdifferencelegacy.h"
+#include "layer-modes-legacy/gimpoperationdividelegacy.h"
+#include "layer-modes-legacy/gimpoperationdodgelegacy.h"
+#include "layer-modes-legacy/gimpoperationgrainextractlegacy.h"
+#include "layer-modes-legacy/gimpoperationgrainmergelegacy.h"
+#include "layer-modes-legacy/gimpoperationhardlightlegacy.h"
+#include "layer-modes-legacy/gimpoperationhslcolorlegacy.h"
+#include "layer-modes-legacy/gimpoperationhsvhuelegacy.h"
+#include "layer-modes-legacy/gimpoperationhsvsaturationlegacy.h"
+#include "layer-modes-legacy/gimpoperationhsvvaluelegacy.h"
+#include "layer-modes-legacy/gimpoperationlightenonlylegacy.h"
+#include "layer-modes-legacy/gimpoperationmultiplylegacy.h"
+#include "layer-modes-legacy/gimpoperationscreenlegacy.h"
+#include "layer-modes-legacy/gimpoperationsoftlightlegacy.h"
+#include "layer-modes-legacy/gimpoperationsubtractlegacy.h"
+
+#include "layer-modes/gimp-layer-modes.h"
+#include "layer-modes/gimpoperationantierase.h"
+#include "layer-modes/gimpoperationbehind.h"
+#include "layer-modes/gimpoperationdissolve.h"
+#include "layer-modes/gimpoperationerase.h"
+#include "layer-modes/gimpoperationmerge.h"
+#include "layer-modes/gimpoperationnormal.h"
+#include "layer-modes/gimpoperationpassthrough.h"
+#include "layer-modes/gimpoperationreplace.h"
+#include "layer-modes/gimpoperationsplit.h"
+
+
+static void
+set_compat_file (GType type,
+ const gchar *basename)
+{
+ GFile *file = gimp_directory_file ("tool-options", basename, NULL);
+ GQuark quark = g_quark_from_static_string ("compat-file");
+
+ g_type_set_qdata (type, quark, file);
+}
+
+static void
+set_settings_folder (GType type,
+ const gchar *basename)
+{
+ GFile *file = gimp_directory_file (basename, NULL);
+ GQuark quark = g_quark_from_static_string ("settings-folder");
+
+ g_type_set_qdata (type, quark, file);
+}
+
+void
+gimp_operations_init (Gimp *gimp)
+{
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+ gimp_layer_modes_init ();
+
+ g_type_class_ref (GIMP_TYPE_OPERATION_BORDER);
+ g_type_class_ref (GIMP_TYPE_OPERATION_BUFFER_SOURCE_VALIDATE);
+ g_type_class_ref (GIMP_TYPE_OPERATION_CAGE_COEF_CALC);
+ g_type_class_ref (GIMP_TYPE_OPERATION_CAGE_TRANSFORM);
+ g_type_class_ref (GIMP_TYPE_OPERATION_COMPOSE_CROP);
+ g_type_class_ref (GIMP_TYPE_OPERATION_EQUALIZE);
+ g_type_class_ref (GIMP_TYPE_OPERATION_FILL_SOURCE);
+ g_type_class_ref (GIMP_TYPE_OPERATION_FLOOD);
+ g_type_class_ref (GIMP_TYPE_OPERATION_GRADIENT);
+ g_type_class_ref (GIMP_TYPE_OPERATION_GROW);
+ g_type_class_ref (GIMP_TYPE_OPERATION_HISTOGRAM_SINK);
+ g_type_class_ref (GIMP_TYPE_OPERATION_MASK_COMPONENTS);
+ g_type_class_ref (GIMP_TYPE_OPERATION_OFFSET);
+ g_type_class_ref (GIMP_TYPE_OPERATION_PROFILE_TRANSFORM);
+ g_type_class_ref (GIMP_TYPE_OPERATION_SCALAR_MULTIPLY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_SEMI_FLATTEN);
+ g_type_class_ref (GIMP_TYPE_OPERATION_SET_ALPHA);
+ g_type_class_ref (GIMP_TYPE_OPERATION_SHRINK);
+ g_type_class_ref (GIMP_TYPE_OPERATION_THRESHOLD_ALPHA);
+
+ g_type_class_ref (GIMP_TYPE_OPERATION_BRIGHTNESS_CONTRAST);
+ g_type_class_ref (GIMP_TYPE_OPERATION_COLOR_BALANCE);
+ g_type_class_ref (GIMP_TYPE_OPERATION_COLORIZE);
+ g_type_class_ref (GIMP_TYPE_OPERATION_CURVES);
+ g_type_class_ref (GIMP_TYPE_OPERATION_DESATURATE);
+ g_type_class_ref (GIMP_TYPE_OPERATION_HUE_SATURATION);
+ g_type_class_ref (GIMP_TYPE_OPERATION_LEVELS);
+ g_type_class_ref (GIMP_TYPE_OPERATION_POSTERIZE);
+ g_type_class_ref (GIMP_TYPE_OPERATION_THRESHOLD);
+
+ g_type_class_ref (GIMP_TYPE_OPERATION_NORMAL);
+ g_type_class_ref (GIMP_TYPE_OPERATION_DISSOLVE);
+ g_type_class_ref (GIMP_TYPE_OPERATION_BEHIND);
+ g_type_class_ref (GIMP_TYPE_OPERATION_MULTIPLY_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_SCREEN_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_ADDITION_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_SUBTRACT_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_DARKEN_ONLY_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_HSV_HUE_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_HSL_COLOR_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_DIVIDE_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_DODGE_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_BURN_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY);
+ g_type_class_ref (GIMP_TYPE_OPERATION_ERASE);
+ 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_REPLACE);
+ g_type_class_ref (GIMP_TYPE_OPERATION_ANTI_ERASE);
+
+ gimp_operation_config_register (gimp,
+ "gimp:brightness-contrast",
+ GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG);
+ set_compat_file (GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG,
+ "gimp-brightness-contrast-tool.settings");
+ set_settings_folder (GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG,
+ "brightness-contrast");
+
+ gimp_operation_config_register (gimp,
+ "gimp:color-balance",
+ GIMP_TYPE_COLOR_BALANCE_CONFIG);
+ set_compat_file (GIMP_TYPE_COLOR_BALANCE_CONFIG,
+ "gimp-color-balance-tool.settings");
+ set_settings_folder (GIMP_TYPE_COLOR_BALANCE_CONFIG,
+ "color-balance");
+
+ gimp_operation_config_register (gimp,
+ "gimp:curves",
+ GIMP_TYPE_CURVES_CONFIG);
+ set_compat_file (GIMP_TYPE_CURVES_CONFIG,
+ "gimp-curves-tool.settings");
+ set_settings_folder (GIMP_TYPE_CURVES_CONFIG,
+ "curves");
+
+ gimp_operation_config_register (gimp,
+ "gimp:hue-saturation",
+ GIMP_TYPE_HUE_SATURATION_CONFIG);
+ set_compat_file (GIMP_TYPE_HUE_SATURATION_CONFIG,
+ "gimp-hue-saturation-tool.settings");
+ set_settings_folder (GIMP_TYPE_HUE_SATURATION_CONFIG,
+ "hue-saturation");
+
+ gimp_operation_config_register (gimp,
+ "gimp:levels",
+ GIMP_TYPE_LEVELS_CONFIG);
+ set_compat_file (GIMP_TYPE_LEVELS_CONFIG,
+ "gimp-levels-tool.settings");
+ set_settings_folder (GIMP_TYPE_LEVELS_CONFIG,
+ "levels");
+}
diff --git a/app/operations/gimp-operations.h b/app/operations/gimp-operations.h
new file mode 100644
index 0000000..6f2b222
--- /dev/null
+++ b/app/operations/gimp-operations.h
@@ -0,0 +1,27 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimp-operations.h
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATIONS_H__
+#define __GIMP_OPERATIONS_H__
+
+
+void gimp_operations_init (Gimp *gimp);
+
+
+#endif /* __GIMP_OPERATIONS_H__ */
diff --git a/app/operations/gimpbrightnesscontrastconfig.c b/app/operations/gimpbrightnesscontrastconfig.c
new file mode 100644
index 0000000..75ee36a
--- /dev/null
+++ b/app/operations/gimpbrightnesscontrastconfig.c
@@ -0,0 +1,248 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpbrightnesscontrastconfig.c
+ * Copyright (C) 2007 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 <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpmath/gimpmath.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "operations-types.h"
+
+#include "gimpbrightnesscontrastconfig.h"
+#include "gimplevelsconfig.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_BRIGHTNESS,
+ PROP_CONTRAST
+};
+
+
+static void gimp_brightness_contrast_config_iface_init (GimpConfigInterface *iface);
+
+static void gimp_brightness_contrast_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_brightness_contrast_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static gboolean gimp_brightness_contrast_config_equal (GimpConfig *a,
+ GimpConfig *b);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpBrightnessContrastConfig,
+ gimp_brightness_contrast_config,
+ GIMP_TYPE_OPERATION_SETTINGS,
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
+ gimp_brightness_contrast_config_iface_init))
+
+#define parent_class gimp_brightness_contrast_config_parent_class
+
+
+static void
+gimp_brightness_contrast_config_class_init (GimpBrightnessContrastConfigClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
+
+ object_class->set_property = gimp_brightness_contrast_config_set_property;
+ object_class->get_property = gimp_brightness_contrast_config_get_property;
+
+ viewable_class->default_icon_name = "gimp-tool-brightness-contrast";
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_BRIGHTNESS,
+ "brightness",
+ _("Brightness"),
+ _("Brightness"),
+ -1.0, 1.0, 0.0, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_CONTRAST,
+ "contrast",
+ _("Contrast"),
+ _("Contrast"),
+ -1.0, 1.0, 0.0, 0);
+}
+
+static void
+gimp_brightness_contrast_config_iface_init (GimpConfigInterface *iface)
+{
+ iface->equal = gimp_brightness_contrast_config_equal;
+}
+
+static void
+gimp_brightness_contrast_config_init (GimpBrightnessContrastConfig *self)
+{
+}
+
+static void
+gimp_brightness_contrast_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpBrightnessContrastConfig *self = GIMP_BRIGHTNESS_CONTRAST_CONFIG (object);
+
+ switch (property_id)
+ {
+ case PROP_BRIGHTNESS:
+ g_value_set_double (value, self->brightness);
+ break;
+
+ case PROP_CONTRAST:
+ g_value_set_double (value, self->contrast);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_brightness_contrast_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpBrightnessContrastConfig *self = GIMP_BRIGHTNESS_CONTRAST_CONFIG (object);
+
+ switch (property_id)
+ {
+ case PROP_BRIGHTNESS:
+ self->brightness = g_value_get_double (value);
+ break;
+
+ case PROP_CONTRAST:
+ self->contrast = g_value_get_double (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gimp_brightness_contrast_config_equal (GimpConfig *a,
+ GimpConfig *b)
+{
+ GimpBrightnessContrastConfig *config_a = GIMP_BRIGHTNESS_CONTRAST_CONFIG (a);
+ GimpBrightnessContrastConfig *config_b = GIMP_BRIGHTNESS_CONTRAST_CONFIG (b);
+
+ if (! gimp_operation_settings_config_equal_base (a, b) ||
+ config_a->brightness != config_b->brightness ||
+ config_a->contrast != config_b->contrast)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/* public functions */
+
+GimpLevelsConfig *
+gimp_brightness_contrast_config_to_levels_config (GimpBrightnessContrastConfig *config)
+{
+ GimpLevelsConfig *levels;
+ gdouble brightness;
+ gdouble slant;
+ gdouble value;
+
+ g_return_val_if_fail (GIMP_IS_BRIGHTNESS_CONTRAST_CONFIG (config), NULL);
+
+ levels = g_object_new (GIMP_TYPE_LEVELS_CONFIG, NULL);
+
+ gimp_operation_settings_config_copy_base (GIMP_CONFIG (config),
+ GIMP_CONFIG (levels),
+ 0);
+
+ brightness = config->brightness / 2.0;
+ slant = tan ((config->contrast + 1) * G_PI_4);
+
+ if (config->brightness >= 0)
+ {
+ value = -0.5 * slant + brightness * slant + 0.5;
+
+ if (value < 0.0)
+ {
+ value = 0.0;
+
+ /* this slightly convoluted math follows by inverting the
+ * calculation of the brightness/contrast LUT in base/lut-funcs.h */
+
+ levels->low_input[GIMP_HISTOGRAM_VALUE] =
+ (- brightness * slant + 0.5 * slant - 0.5) / (slant - brightness * slant);
+ }
+
+ levels->low_output[GIMP_HISTOGRAM_VALUE] = value;
+
+ value = 0.5 * slant + 0.5;
+
+ if (value > 1.0)
+ {
+ value = 1.0;
+
+ levels->high_input[GIMP_HISTOGRAM_VALUE] =
+ (- brightness * slant + 0.5 * slant + 0.5) / (slant - brightness * slant);
+ }
+
+ levels->high_output[GIMP_HISTOGRAM_VALUE] = value;
+ }
+ else
+ {
+ value = 0.5 - 0.5 * slant;
+
+ if (value < 0.0)
+ {
+ value = 0.0;
+
+ levels->low_input[GIMP_HISTOGRAM_VALUE] =
+ (0.5 * slant - 0.5) / (slant + brightness * slant);
+ }
+
+ levels->low_output[GIMP_HISTOGRAM_VALUE] = value;
+
+ value = slant * brightness + slant * 0.5 + 0.5;
+
+ if (value > 1.0)
+ {
+ value = 1.0;
+
+ levels->high_input[GIMP_HISTOGRAM_VALUE] =
+ (0.5 * slant + 0.5) / (slant + brightness * slant);
+ }
+
+ levels->high_output[GIMP_HISTOGRAM_VALUE] = value;
+ }
+
+ return levels;
+}
diff --git a/app/operations/gimpbrightnesscontrastconfig.h b/app/operations/gimpbrightnesscontrastconfig.h
new file mode 100644
index 0000000..bab1a03
--- /dev/null
+++ b/app/operations/gimpbrightnesscontrastconfig.h
@@ -0,0 +1,58 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpbrightnesscontrastconfig.h
+ * Copyright (C) 2007 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/>.
+ */
+
+#ifndef __GIMP_BRIGHTNESS_CONTRAST_CONFIG_H__
+#define __GIMP_BRIGHTNESS_CONTRAST_CONFIG_H__
+
+
+#include "gimpoperationsettings.h"
+
+
+#define GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG (gimp_brightness_contrast_config_get_type ())
+#define GIMP_BRIGHTNESS_CONTRAST_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG, GimpBrightnessContrastConfig))
+#define GIMP_BRIGHTNESS_CONTRAST_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG, GimpBrightnessContrastConfigClass))
+#define GIMP_IS_BRIGHTNESS_CONTRAST_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG))
+#define GIMP_IS_BRIGHTNESS_CONTRAST_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG))
+#define GIMP_BRIGHTNESS_CONTRAST_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG, GimpBrightnessContrastConfigClass))
+
+
+typedef struct _GimpBrightnessContrastConfigClass GimpBrightnessContrastConfigClass;
+
+struct _GimpBrightnessContrastConfig
+{
+ GimpOperationSettings parent_instance;
+
+ gdouble brightness;
+ gdouble contrast;
+};
+
+struct _GimpBrightnessContrastConfigClass
+{
+ GimpOperationSettingsClass parent_class;
+};
+
+
+GType gimp_brightness_contrast_config_get_type (void) G_GNUC_CONST;
+
+GimpLevelsConfig *
+gimp_brightness_contrast_config_to_levels_config (GimpBrightnessContrastConfig *config);
+
+
+#endif /* __GIMP_BRIGHTNESS_CONTRAST_CONFIG_H__ */
diff --git a/app/operations/gimpcageconfig.c b/app/operations/gimpcageconfig.c
new file mode 100644
index 0000000..17548cf
--- /dev/null
+++ b/app/operations/gimpcageconfig.c
@@ -0,0 +1,832 @@
+/* GIMP - The GNU Image Manipulation Program
+ *
+ * gimpcageconfig.c
+ * Copyright (C) 2010 Michael Muré <batolettre@gmail.com>
+ *
+ * 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 <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpbase/gimpbase.h"
+
+#include "operations-types.h"
+
+#include "gimpcageconfig.h"
+
+
+/*#define DEBUG_CAGE */
+
+/* This DELTA is aimed to not have handle on exact pixel during computation,
+ * to avoid particular case. It shouldn't be so useful, but it's a double
+ * safety. */
+#define DELTA 0.010309278351
+
+
+static void gimp_cage_config_finalize (GObject *object);
+static void gimp_cage_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_cage_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_cage_config_compute_scaling_factor (GimpCageConfig *gcc);
+static void gimp_cage_config_compute_edges_normal (GimpCageConfig *gcc);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpCageConfig, gimp_cage_config,
+ GIMP_TYPE_OPERATION_SETTINGS,
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
+ NULL))
+
+#define parent_class gimp_cage_config_parent_class
+
+#ifdef DEBUG_CAGE
+static void
+print_cage (GimpCageConfig *gcc)
+{
+ gint i;
+ GeglRectangle bounding_box;
+ GimpCagePoint *point;
+
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+
+ bounding_box = gimp_cage_config_get_bounding_box (gcc);
+
+ for (i = 0; i < gcc->cage_points->len; i++)
+ {
+ point = &g_array_index (gcc->cage_points, GimpCagePoint, i);
+ g_printerr ("cgx: %.0f cgy: %.0f cvdx: %.0f cvdy: %.0f sf: %.2f normx: %.2f normy: %.2f %s\n",
+ point->src_point.x + ((gcc->cage_mode==GIMP_CAGE_MODE_CAGE_CHANGE)?gcc->displacement_x:0),
+ point->src_point.y + ((gcc->cage_mode==GIMP_CAGE_MODE_CAGE_CHANGE)?gcc->displacement_y:0),
+ point->dest_point.x + ((gcc->cage_mode==GIMP_CAGE_MODE_DEFORM)?gcc->displacement_x:0),
+ point->dest_point.y + ((gcc->cage_mode==GIMP_CAGE_MODE_DEFORM)?gcc->displacement_y:0),
+ point->edge_scaling_factor,
+ point->edge_normal.x,
+ point->edge_normal.y,
+ ((point->selected) ? "S" : "NS"));
+ }
+
+ g_printerr ("bounding box: x: %d y: %d width: %d height: %d\n", bounding_box.x, bounding_box.y, bounding_box.width, bounding_box.height);
+ g_printerr ("disp x: %f disp y: %f\n", gcc->displacement_x, gcc->displacement_y);
+ g_printerr ("done\n");
+}
+#endif
+
+static void
+gimp_cage_config_class_init (GimpCageConfigClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = gimp_cage_config_set_property;
+ object_class->get_property = gimp_cage_config_get_property;
+
+ object_class->finalize = gimp_cage_config_finalize;
+}
+
+static void
+gimp_cage_config_init (GimpCageConfig *self)
+{
+ /*pre-allocation for 50 vertices for the cage.*/
+ self->cage_points = g_array_sized_new (FALSE, FALSE, sizeof(GimpCagePoint), 50);
+}
+
+static void
+gimp_cage_config_finalize (GObject *object)
+{
+ GimpCageConfig *gcc = GIMP_CAGE_CONFIG (object);
+
+ g_array_free (gcc->cage_points, TRUE);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_cage_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_cage_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gimp_cage_config_get_n_points:
+ * @gcc: the cage config
+ *
+ * Returns: the number of points of the cage
+ */
+guint
+gimp_cage_config_get_n_points (GimpCageConfig *gcc)
+{
+ return gcc->cage_points->len;
+}
+
+/**
+ * gimp_cage_config_add_cage_point:
+ * @gcc: the cage config
+ * @x: x value of the new point
+ * @y: y value of the new point
+ *
+ * Add a new point in the last index of the polygon of the cage.
+ * Point is added in both source and destination cage
+ */
+void
+gimp_cage_config_add_cage_point (GimpCageConfig *gcc,
+ gdouble x,
+ gdouble y)
+{
+ gimp_cage_config_insert_cage_point (gcc, gcc->cage_points->len, x, y);
+}
+
+/**
+ * gimp_cage_config_insert_cage_point:
+ * @gcc: the cage config
+ * @point_number: index where the point will be inserted
+ * @x: x value of the new point
+ * @y: y value of the new point
+ *
+ * Insert a new point in the polygon of the cage at the given index.
+ * Point is added in both source and destination cage
+ */
+void
+gimp_cage_config_insert_cage_point (GimpCageConfig *gcc,
+ gint point_number,
+ gdouble x,
+ gdouble y)
+{
+ GimpCagePoint point;
+
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+ g_return_if_fail (point_number <= gcc->cage_points->len);
+ g_return_if_fail (point_number >= 0);
+
+ point.src_point.x = x + DELTA;
+ point.src_point.y = y + DELTA;
+
+ point.dest_point.x = x + DELTA;
+ point.dest_point.y = y + DELTA;
+
+ g_array_insert_val (gcc->cage_points, point_number, point);
+
+ gimp_cage_config_compute_scaling_factor (gcc);
+ gimp_cage_config_compute_edges_normal (gcc);
+}
+
+/**
+ * gimp_cage_config_remove_last_cage_point:
+ * @gcc: the cage config
+ *
+ * Remove the last point of the cage, in both source and destination cage
+ */
+void
+gimp_cage_config_remove_last_cage_point (GimpCageConfig *gcc)
+{
+ gimp_cage_config_remove_cage_point (gcc, gcc->cage_points->len - 1);
+}
+
+/**
+ * gimp_cage_config_remove_cage_point:
+ * @gcc: the cage config
+ * @point_number: the index of the point to remove
+ *
+ * Remove the given point from the cage
+ */
+void
+gimp_cage_config_remove_cage_point (GimpCageConfig *gcc,
+ gint point_number)
+{
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+ g_return_if_fail (point_number < gcc->cage_points->len);
+ g_return_if_fail (point_number >= 0);
+
+ if (gcc->cage_points->len > 0)
+ g_array_remove_index (gcc->cage_points, gcc->cage_points->len - 1);
+
+ gimp_cage_config_compute_scaling_factor (gcc);
+ gimp_cage_config_compute_edges_normal (gcc);
+}
+
+/**
+ * gimp_cage_config_remove_selected_points:
+ * @gcc: the cage config
+ *
+ * Remove all the selected points from the cage
+ */
+void
+gimp_cage_config_remove_selected_points (GimpCageConfig *gcc)
+{
+ gint i;
+ GimpCagePoint *point;
+
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+
+ for (i = 0; i < gcc->cage_points->len; i++)
+ {
+ point = &g_array_index (gcc->cage_points, GimpCagePoint, i);
+
+ if (point->selected)
+ {
+ g_array_remove_index (gcc->cage_points, i);
+ i--;
+ }
+ }
+
+ gimp_cage_config_compute_scaling_factor (gcc);
+ gimp_cage_config_compute_edges_normal (gcc);
+}
+
+/**
+ * gimp_cage_config_get_point_coordinate:
+ * @gcc: the cage config
+ * @mode: the actual mode of the cage, GIMP_CAGE_MODE_CAGE_CHANGE or GIMP_CAGE_MODE_DEFORM
+ * @point_number: the index of the point to return
+ *
+ * Returns: the real position of the given point, as a GimpVector2
+ */
+GimpVector2
+gimp_cage_config_get_point_coordinate (GimpCageConfig *gcc,
+ GimpCageMode mode,
+ gint point_number)
+{
+ GimpVector2 result = { 0.0, 0.0 };
+ GimpCagePoint *point;
+
+ g_return_val_if_fail (GIMP_IS_CAGE_CONFIG (gcc), result);
+ g_return_val_if_fail (point_number < gcc->cage_points->len, result);
+ g_return_val_if_fail (point_number >= 0, result);
+
+ point = &g_array_index (gcc->cage_points, GimpCagePoint, point_number);
+
+ if (point->selected)
+ {
+ if (mode == GIMP_CAGE_MODE_CAGE_CHANGE)
+ {
+ result.x = point->src_point.x + gcc->displacement_x;
+ result.y = point->src_point.y + gcc->displacement_y;
+ }
+ else
+ {
+ result.x = point->dest_point.x + gcc->displacement_x;
+ result.y = point->dest_point.y + gcc->displacement_y;
+ }
+ }
+ else
+ {
+ if (mode == GIMP_CAGE_MODE_CAGE_CHANGE)
+ {
+ result.x = point->src_point.x;
+ result.y = point->src_point.y;
+ }
+ else
+ {
+ result.x = point->dest_point.x;
+ result.y = point->dest_point.y;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * gimp_cage_config_add_displacement:
+ * @gcc: the cage config
+ * @mode: the actual mode of the cage, GIMP_CAGE_MODE_CAGE_CHANGE or GIMP_CAGE_MODE_DEFORM
+ * @point_number: the point of the cage to move
+ * @x: x displacement value
+ * @y: y displacement value
+ *
+ * Add a displacement for all selected points of the cage.
+ * This displacement need to be committed to become effective.
+ */
+void
+gimp_cage_config_add_displacement (GimpCageConfig *gcc,
+ GimpCageMode mode,
+ gdouble x,
+ gdouble y)
+{
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+
+ gcc->cage_mode = mode;
+ gcc->displacement_x = x;
+ gcc->displacement_y = y;
+
+ #ifdef DEBUG_CAGE
+ print_cage (gcc);
+ #endif
+}
+
+/**
+ * gimp_cage_config_commit_displacement:
+ * @gcc: the cage config
+ *
+ * Apply the displacement to the cage
+ */
+void
+gimp_cage_config_commit_displacement (GimpCageConfig *gcc)
+{
+ gint i;
+
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+
+ for (i = 0; i < gcc->cage_points->len; i++)
+ {
+ GimpCagePoint *point;
+ point = &g_array_index (gcc->cage_points, GimpCagePoint, i);
+
+ if (point->selected)
+ {
+ if (gcc->cage_mode == GIMP_CAGE_MODE_CAGE_CHANGE)
+ {
+ point->src_point.x += gcc->displacement_x;
+ point->src_point.y += gcc->displacement_y;
+ point->dest_point.x += gcc->displacement_x;
+ point->dest_point.y += gcc->displacement_y;
+ }
+ else
+ {
+ point->dest_point.x += gcc->displacement_x;
+ point->dest_point.y += gcc->displacement_y;
+ }
+ }
+ }
+
+ gimp_cage_config_compute_scaling_factor (gcc);
+ gimp_cage_config_compute_edges_normal (gcc);
+ gimp_cage_config_reset_displacement (gcc);
+}
+
+/**
+ * gimp_cage_config_reset_displacement:
+ * @gcc: the cage config
+ *
+ * Set the displacement to zero.
+ */
+void
+gimp_cage_config_reset_displacement (GimpCageConfig *gcc)
+{
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+
+ gcc->displacement_x = 0.0;
+ gcc->displacement_y = 0.0;
+}
+
+/**
+ * gimp_cage_config_get_bounding_box:
+ * @gcc: the cage config
+ *
+ * Compute the bounding box of the source cage
+ *
+ * Returns: the bounding box of the source cage, as a GeglRectangle
+ */
+GeglRectangle
+gimp_cage_config_get_bounding_box (GimpCageConfig *gcc)
+{
+ GeglRectangle bounding_box = { 0, 0, 0, 0};
+ gint i;
+ GimpCagePoint *point;
+
+ g_return_val_if_fail (GIMP_IS_CAGE_CONFIG (gcc), bounding_box);
+
+ if (gcc->cage_points->len == 0)
+ return bounding_box;
+
+ point = &g_array_index (gcc->cage_points, GimpCagePoint, 0);
+
+ if (point->selected)
+ {
+ bounding_box.x = point->src_point.x + gcc->displacement_x;
+ bounding_box.y = point->src_point.y + gcc->displacement_y;
+ }
+ else
+ {
+ bounding_box.x = point->src_point.x;
+ bounding_box.y = point->src_point.y;
+ }
+
+ for (i = 1; i < gcc->cage_points->len; i++)
+ {
+ gdouble x,y;
+ point = &g_array_index (gcc->cage_points, GimpCagePoint, i);
+
+ if (point->selected)
+ {
+ x = point->src_point.x + gcc->displacement_x;
+ y = point->src_point.y + gcc->displacement_y;
+ }
+ else
+ {
+ x = point->src_point.x;
+ y = point->src_point.y;
+ }
+
+ if (x < bounding_box.x)
+ {
+ bounding_box.width += bounding_box.x - x;
+ bounding_box.x = x;
+ }
+
+ if (y < bounding_box.y)
+ {
+ bounding_box.height += bounding_box.y - y;
+ bounding_box.y = y;
+ }
+
+ if (x > bounding_box.x + bounding_box.width)
+ {
+ bounding_box.width = x - bounding_box.x;
+ }
+
+ if (y > bounding_box.y + bounding_box.height)
+ {
+ bounding_box.height = y - bounding_box.y;
+ }
+ }
+
+ return bounding_box;
+}
+
+/**
+ * gimp_cage_config_reverse_cage:
+ * @gcc: the cage config
+ *
+ * When using non-simple cage (like a cage in 8), user may want to
+ * manually inverse inside and outside of the cage. This function
+ * reverse the cage
+ */
+void
+gimp_cage_config_reverse_cage (GimpCageConfig *gcc)
+{
+ GimpCagePoint temp;
+ gint i;
+
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+
+ for (i = 0; i < gcc->cage_points->len / 2; i++)
+ {
+ temp = g_array_index (gcc->cage_points, GimpCagePoint, i);
+
+ g_array_index (gcc->cage_points, GimpCagePoint, i) =
+ g_array_index (gcc->cage_points, GimpCagePoint, gcc->cage_points->len - i - 1);
+
+ g_array_index (gcc->cage_points, GimpCagePoint, gcc->cage_points->len - i - 1) = temp;
+ }
+
+ gimp_cage_config_compute_scaling_factor (gcc);
+ gimp_cage_config_compute_edges_normal (gcc);
+}
+
+/**
+ * gimp_cage_config_reverse_cage_if_needed:
+ * @gcc: the cage config
+ *
+ * Since the cage need to be defined counter-clockwise to have the
+ * topological inside in the actual 'physical' inside of the cage,
+ * this function compute if the cage is clockwise or not, and reverse
+ * the cage if needed.
+ *
+ * This function does not take into account an eventual displacement
+ */
+void
+gimp_cage_config_reverse_cage_if_needed (GimpCageConfig *gcc)
+{
+ gint i;
+ gdouble sum;
+
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+
+ sum = 0.0;
+
+ /* this is a bit crappy, but should works most of the case */
+ /* we do the sum of the projection of each point to the previous
+ segment, and see the final sign */
+ for (i = 0; i < gcc->cage_points->len ; i++)
+ {
+ GimpVector2 P1, P2, P3;
+ gdouble z;
+
+ P1 = (g_array_index (gcc->cage_points, GimpCagePoint, i)).src_point;
+ P2 = (g_array_index (gcc->cage_points, GimpCagePoint, (i+1) % gcc->cage_points->len)).src_point;
+ P3 = (g_array_index (gcc->cage_points, GimpCagePoint, (i+2) % gcc->cage_points->len)).src_point;
+
+ z = P1.x * (P2.y - P3.y) + P2.x * (P3.y - P1.y) + P3.x * (P1.y - P2.y);
+
+ sum += z;
+ }
+
+ /* sum > 0 mean a cage defined counter-clockwise, so we reverse it */
+ if (sum > 0)
+ {
+ gimp_cage_config_reverse_cage (gcc);
+ }
+}
+
+/**
+ * gimp_cage_config_compute_scaling_factor:
+ * @gcc: the cage config
+ *
+ * Update Green Coordinate scaling factor for the destination cage.
+ * This function does not take into account an eventual displacement.
+ */
+static void
+gimp_cage_config_compute_scaling_factor (GimpCageConfig *gcc)
+{
+ GimpVector2 edge;
+ gdouble length, length_d;
+ gint i;
+ GimpCagePoint *current, *last;
+
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+ if (gcc->cage_points->len < 2)
+ return;
+
+ last = &g_array_index (gcc->cage_points, GimpCagePoint, 0);
+
+ for (i = 1; i <= gcc->cage_points->len; i++)
+ {
+ current = &g_array_index (gcc->cage_points, GimpCagePoint, i % gcc->cage_points->len);
+
+ gimp_vector2_sub (&edge,
+ &(last->src_point),
+ &(current->src_point));
+ length = gimp_vector2_length (&edge);
+
+ gimp_vector2_sub (&edge,
+ &(last->dest_point),
+ &(current->dest_point));
+ length_d = gimp_vector2_length (&edge);
+
+ last->edge_scaling_factor = length_d / length;
+ last = current;
+ }
+}
+
+/**
+ * gimp_cage_config_compute_edges_normal:
+ * @gcc: the cage config
+ *
+ * Update edges normal for the destination cage.
+ * This function does not take into account an eventual displacement.
+ */
+static void
+gimp_cage_config_compute_edges_normal (GimpCageConfig *gcc)
+{
+ GimpVector2 normal;
+ gint i;
+ GimpCagePoint *current, *last;
+
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+
+ last = &g_array_index (gcc->cage_points, GimpCagePoint, 0);
+
+ for (i = 1; i <= gcc->cage_points->len; i++)
+ {
+ current = &g_array_index (gcc->cage_points, GimpCagePoint, i % gcc->cage_points->len);
+
+ gimp_vector2_sub (&normal,
+ &(current->dest_point),
+ &(last->dest_point));
+
+ last->edge_normal = gimp_vector2_normal (&normal);
+ last = current;
+ }
+}
+
+/**
+ * gimp_cage_config_point_inside:
+ * @gcc: the cage config
+ * @x: x coordinate of the point to test
+ * @y: y coordinate of the point to test
+ *
+ * Check if the given point is inside the cage. This test is done in
+ * the regard of the topological inside of the source cage.
+ *
+ * Returns: TRUE if the point is inside, FALSE if not.
+ * This function does not take into account an eventual displacement.
+ */
+gboolean
+gimp_cage_config_point_inside (GimpCageConfig *gcc,
+ gfloat x,
+ gfloat y)
+{
+ GimpVector2 *last, *current;
+ gboolean inside = FALSE;
+ gint i;
+
+ g_return_val_if_fail (GIMP_IS_CAGE_CONFIG (gcc), FALSE);
+
+ last = &((g_array_index (gcc->cage_points, GimpCagePoint, gcc->cage_points->len - 1)).src_point);
+
+ for (i = 0; i < gcc->cage_points->len; i++)
+ {
+ current = &((g_array_index (gcc->cage_points, GimpCagePoint, i)).src_point);
+
+ if ((((current->y <= y) && (y < last->y))
+ || ((last->y <= y) && (y < current->y)))
+ && (x < (last->x - current->x) * (y - current->y) / (last->y - current->y) + current->x))
+ {
+ inside = !inside;
+ }
+
+ last = current;
+ }
+
+ return inside;
+}
+
+/**
+ * gimp_cage_config_select_point:
+ * @gcc: the cage config
+ * @point_number: the index of the point to select
+ *
+ * Select the given point of the cage, and deselect the others.
+ */
+void
+gimp_cage_config_select_point (GimpCageConfig *gcc,
+ gint point_number)
+{
+ gint i;
+ GimpCagePoint *point;
+
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+ g_return_if_fail (point_number < gcc->cage_points->len);
+ g_return_if_fail (point_number >= 0);
+
+ for (i = 0; i < gcc->cage_points->len; i++)
+ {
+ point = &g_array_index (gcc->cage_points, GimpCagePoint, i);
+
+ if (i == point_number)
+ {
+ point->selected = TRUE;
+ }
+ else
+ {
+ point->selected = FALSE;
+ }
+ }
+}
+
+/**
+ * gimp_cage_config_select_area:
+ * @gcc: the cage config
+ * @mode: the actual mode of the cage, GIMP_CAGE_MODE_CAGE_CHANGE or GIMP_CAGE_MODE_DEFORM
+ * @area: the area to select
+ *
+ * Select cage's point inside the given area and deselect others
+ */
+void
+gimp_cage_config_select_area (GimpCageConfig *gcc,
+ GimpCageMode mode,
+ GeglRectangle area)
+{
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+
+ gimp_cage_config_deselect_points (gcc);
+ gimp_cage_config_select_add_area (gcc, mode, area);
+}
+
+/**
+ * gimp_cage_config_select_add_area:
+ * @gcc: the cage config
+ * @mode: the actual mode of the cage, GIMP_CAGE_MODE_CAGE_CHANGE or GIMP_CAGE_MODE_DEFORM
+ * @area: the area to select
+ *
+ * Select cage's point inside the given area. Already selected point stay selected.
+ */
+void
+gimp_cage_config_select_add_area (GimpCageConfig *gcc,
+ GimpCageMode mode,
+ GeglRectangle area)
+{
+ gint i;
+ GimpCagePoint *point;
+
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+
+ for (i = 0; i < gcc->cage_points->len; i++)
+ {
+ point = &g_array_index (gcc->cage_points, GimpCagePoint, i);
+
+ if (mode == GIMP_CAGE_MODE_CAGE_CHANGE)
+ {
+ if (point->src_point.x >= area.x &&
+ point->src_point.x <= area.x + area.width &&
+ point->src_point.y >= area.y &&
+ point->src_point.y <= area.y + area.height)
+ {
+ point->selected = TRUE;
+ }
+ }
+ else
+ {
+ if (point->dest_point.x >= area.x &&
+ point->dest_point.x <= area.x + area.width &&
+ point->dest_point.y >= area.y &&
+ point->dest_point.y <= area.y + area.height)
+ {
+ point->selected = TRUE;
+ }
+ }
+ }
+}
+
+/**
+ * gimp_cage_config_toggle_point_selection:
+ * @gcc: the cage config
+ * @point_number: the index of the point to toggle selection
+ *
+ * Toggle the selection of the given cage point
+ */
+void
+gimp_cage_config_toggle_point_selection (GimpCageConfig *gcc,
+ gint point_number)
+{
+ GimpCagePoint *point;
+
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+ g_return_if_fail (point_number < gcc->cage_points->len);
+ g_return_if_fail (point_number >= 0);
+
+ point = &g_array_index (gcc->cage_points, GimpCagePoint, point_number);
+ point->selected = ! point->selected;
+}
+
+/**
+ * gimp_cage_deselect_points:
+ * @gcc: the cage config
+ *
+ * Deselect all cage points.
+ */
+void
+gimp_cage_config_deselect_points (GimpCageConfig *gcc)
+{
+ gint i;
+
+ g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+
+ for (i = 0; i < gcc->cage_points->len; i++)
+ {
+ (g_array_index (gcc->cage_points, GimpCagePoint, i)).selected = FALSE;
+ }
+}
+
+/**
+ * gimp_cage_config_point_is_selected:
+ * @gcc: the cage config
+ * @point_number: the index of the point to test
+ *
+ * Returns: TRUE if the point is selected, FALSE otherwise.
+ */
+gboolean
+gimp_cage_config_point_is_selected (GimpCageConfig *gcc,
+ gint point_number)
+{
+ GimpCagePoint *point;
+
+ g_return_val_if_fail (GIMP_IS_CAGE_CONFIG (gcc), FALSE);
+ g_return_val_if_fail (point_number < gcc->cage_points->len, FALSE);
+ g_return_val_if_fail (point_number >= 0, FALSE);
+
+ point = &(g_array_index (gcc->cage_points, GimpCagePoint, point_number));
+
+ return point->selected;
+}
diff --git a/app/operations/gimpcageconfig.h b/app/operations/gimpcageconfig.h
new file mode 100644
index 0000000..8106d52
--- /dev/null
+++ b/app/operations/gimpcageconfig.h
@@ -0,0 +1,108 @@
+/* GIMP - The GNU Image Manipulation Program
+ *
+ * gimpcageconfig.h
+ * Copyright (C) 2010 Michael Muré <batolettre@gmail.com>
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_CAGE_CONFIG_H__
+#define __GIMP_CAGE_CONFIG_H__
+
+
+#include "gimpoperationsettings.h"
+
+
+struct _GimpCagePoint
+{
+ GimpVector2 src_point;
+ GimpVector2 dest_point;
+ GimpVector2 edge_normal;
+ gdouble edge_scaling_factor;
+ gboolean selected;
+};
+
+
+#define GIMP_TYPE_CAGE_CONFIG (gimp_cage_config_get_type ())
+#define GIMP_CAGE_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CAGE_CONFIG, GimpCageConfig))
+#define GIMP_CAGE_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CAGE_CONFIG, GimpCageConfigClass))
+#define GIMP_IS_CAGE_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CAGE_CONFIG))
+#define GIMP_IS_CAGE_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CAGE_CONFIG))
+#define GIMP_CAGE_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CAGE_CONFIG, GimpCageConfigClass))
+
+
+typedef struct _GimpCageConfigClass GimpCageConfigClass;
+
+struct _GimpCageConfig
+{
+ GimpOperationSettings parent_instance;
+
+ GArray *cage_points;
+
+ gdouble displacement_x;
+ gdouble displacement_y;
+ GimpCageMode cage_mode; /* Cage mode, used to commit displacement */
+};
+
+struct _GimpCageConfigClass
+{
+ GimpOperationSettingsClass parent_class;
+};
+
+
+GType gimp_cage_config_get_type (void) G_GNUC_CONST;
+
+guint gimp_cage_config_get_n_points (GimpCageConfig *gcc);
+void gimp_cage_config_add_cage_point (GimpCageConfig *gcc,
+ gdouble x,
+ gdouble y);
+void gimp_cage_config_insert_cage_point (GimpCageConfig *gcc,
+ gint point_number,
+ gdouble x,
+ gdouble y);
+void gimp_cage_config_remove_last_cage_point (GimpCageConfig *gcc);
+void gimp_cage_config_remove_cage_point (GimpCageConfig *gcc,
+ gint point_number);
+void gimp_cage_config_remove_selected_points (GimpCageConfig *gcc);
+GimpVector2 gimp_cage_config_get_point_coordinate (GimpCageConfig *gcc,
+ GimpCageMode mode,
+ gint point_number);
+void gimp_cage_config_add_displacement (GimpCageConfig *gcc,
+ GimpCageMode mode,
+ gdouble x,
+ gdouble y);
+void gimp_cage_config_commit_displacement (GimpCageConfig *gcc);
+void gimp_cage_config_reset_displacement (GimpCageConfig *gcc);
+GeglRectangle gimp_cage_config_get_bounding_box (GimpCageConfig *gcc);
+void gimp_cage_config_reverse_cage_if_needed (GimpCageConfig *gcc);
+void gimp_cage_config_reverse_cage (GimpCageConfig *gcc);
+gboolean gimp_cage_config_point_inside (GimpCageConfig *gcc,
+ gfloat x,
+ gfloat y);
+void gimp_cage_config_select_point (GimpCageConfig *gcc,
+ gint point_number);
+void gimp_cage_config_select_area (GimpCageConfig *gcc,
+ GimpCageMode mode,
+ GeglRectangle area);
+void gimp_cage_config_select_add_area (GimpCageConfig *gcc,
+ GimpCageMode mode,
+ GeglRectangle area);
+void gimp_cage_config_toggle_point_selection (GimpCageConfig *gcc,
+ gint point_number);
+void gimp_cage_config_deselect_points (GimpCageConfig *gcc);
+gboolean gimp_cage_config_point_is_selected (GimpCageConfig *gcc,
+ gint point_number);
+
+
+#endif /* __GIMP_CAGE_CONFIG_H__ */
diff --git a/app/operations/gimpcolorbalanceconfig.c b/app/operations/gimpcolorbalanceconfig.c
new file mode 100644
index 0000000..7011a86
--- /dev/null
+++ b/app/operations/gimpcolorbalanceconfig.c
@@ -0,0 +1,382 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpcolorbalanceconfig.c
+ * Copyright (C) 2007 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 <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "operations-types.h"
+
+#include "gimpcolorbalanceconfig.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_RANGE,
+ PROP_CYAN_RED,
+ PROP_MAGENTA_GREEN,
+ PROP_YELLOW_BLUE,
+ PROP_PRESERVE_LUMINOSITY
+};
+
+
+static void gimp_color_balance_config_iface_init (GimpConfigInterface *iface);
+
+static void gimp_color_balance_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_color_balance_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static gboolean gimp_color_balance_config_serialize (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data);
+static gboolean gimp_color_balance_config_deserialize (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data);
+static gboolean gimp_color_balance_config_equal (GimpConfig *a,
+ GimpConfig *b);
+static void gimp_color_balance_config_reset (GimpConfig *config);
+static gboolean gimp_color_balance_config_copy (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpColorBalanceConfig, gimp_color_balance_config,
+ GIMP_TYPE_OPERATION_SETTINGS,
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
+ gimp_color_balance_config_iface_init))
+
+#define parent_class gimp_color_balance_config_parent_class
+
+
+static void
+gimp_color_balance_config_class_init (GimpColorBalanceConfigClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
+
+ object_class->set_property = gimp_color_balance_config_set_property;
+ object_class->get_property = gimp_color_balance_config_get_property;
+
+ viewable_class->default_icon_name = "gimp-tool-color-balance";
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_RANGE,
+ "range",
+ _("Range"),
+ _("The affected range"),
+ GIMP_TYPE_TRANSFER_MODE,
+ GIMP_TRANSFER_MIDTONES, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_CYAN_RED,
+ "cyan-red",
+ _("Cyan-Red"),
+ _("Cyan-Red"),
+ -1.0, 1.0, 0.0, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_MAGENTA_GREEN,
+ "magenta-green",
+ _("Magenta-Green"),
+ _("Magenta-Green"),
+ -1.0, 1.0, 0.0, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_YELLOW_BLUE,
+ "yellow-blue",
+ _("Yellow-Blue"),
+ _("Yellow-Blue"),
+ -1.0, 1.0, 0.0, 0);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_PRESERVE_LUMINOSITY,
+ "preserve-luminosity",
+ _("Preserve Luminosity"),
+ _("Preserve Luminosity"),
+ TRUE, 0);
+}
+
+static void
+gimp_color_balance_config_iface_init (GimpConfigInterface *iface)
+{
+ iface->serialize = gimp_color_balance_config_serialize;
+ iface->deserialize = gimp_color_balance_config_deserialize;
+ iface->equal = gimp_color_balance_config_equal;
+ iface->reset = gimp_color_balance_config_reset;
+ iface->copy = gimp_color_balance_config_copy;
+}
+
+static void
+gimp_color_balance_config_init (GimpColorBalanceConfig *self)
+{
+ gimp_config_reset (GIMP_CONFIG (self));
+}
+
+static void
+gimp_color_balance_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorBalanceConfig *self = GIMP_COLOR_BALANCE_CONFIG (object);
+
+ switch (property_id)
+ {
+ case PROP_RANGE:
+ g_value_set_enum (value, self->range);
+ break;
+
+ case PROP_CYAN_RED:
+ g_value_set_double (value, self->cyan_red[self->range]);
+ break;
+
+ case PROP_MAGENTA_GREEN:
+ g_value_set_double (value, self->magenta_green[self->range]);
+ break;
+
+ case PROP_YELLOW_BLUE:
+ g_value_set_double (value, self->yellow_blue[self->range]);
+ break;
+
+ case PROP_PRESERVE_LUMINOSITY:
+ g_value_set_boolean (value, self->preserve_luminosity);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_color_balance_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorBalanceConfig *self = GIMP_COLOR_BALANCE_CONFIG (object);
+
+ switch (property_id)
+ {
+ case PROP_RANGE:
+ self->range = g_value_get_enum (value);
+ g_object_notify (object, "cyan-red");
+ g_object_notify (object, "magenta-green");
+ g_object_notify (object, "yellow-blue");
+ break;
+
+ case PROP_CYAN_RED:
+ self->cyan_red[self->range] = g_value_get_double (value);
+ break;
+
+ case PROP_MAGENTA_GREEN:
+ self->magenta_green[self->range] = g_value_get_double (value);
+ break;
+
+ case PROP_YELLOW_BLUE:
+ self->yellow_blue[self->range] = g_value_get_double (value);
+ break;
+
+ case PROP_PRESERVE_LUMINOSITY:
+ self->preserve_luminosity = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gimp_color_balance_config_serialize (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data)
+{
+ GimpColorBalanceConfig *bc_config = GIMP_COLOR_BALANCE_CONFIG (config);
+ GimpTransferMode range;
+ GimpTransferMode old_range;
+ gboolean success = TRUE;
+
+ if (! gimp_operation_settings_config_serialize_base (config, writer, data))
+ return FALSE;
+
+ old_range = bc_config->range;
+
+ for (range = GIMP_TRANSFER_SHADOWS;
+ range <= GIMP_TRANSFER_HIGHLIGHTS;
+ range++)
+ {
+ bc_config->range = range;
+
+ success = (gimp_config_serialize_property_by_name (config,
+ "range",
+ writer) &&
+ gimp_config_serialize_property_by_name (config,
+ "cyan-red",
+ writer) &&
+ gimp_config_serialize_property_by_name (config,
+ "magenta-green",
+ writer) &&
+ gimp_config_serialize_property_by_name (config,
+ "yellow-blue",
+ writer));
+
+ if (! success)
+ break;
+ }
+
+ if (success)
+ success = gimp_config_serialize_property_by_name (config,
+ "preserve-luminosity",
+ writer);
+
+ bc_config->range = old_range;
+
+ return success;
+}
+
+static gboolean
+gimp_color_balance_config_deserialize (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data)
+{
+ GimpColorBalanceConfig *cb_config = GIMP_COLOR_BALANCE_CONFIG (config);
+ GimpTransferMode old_range;
+ gboolean success = TRUE;
+
+ old_range = cb_config->range;
+
+ success = gimp_config_deserialize_properties (config, scanner, nest_level);
+
+ g_object_set (config, "range", old_range, NULL);
+
+ return success;
+}
+
+static gboolean
+gimp_color_balance_config_equal (GimpConfig *a,
+ GimpConfig *b)
+{
+ GimpColorBalanceConfig *config_a = GIMP_COLOR_BALANCE_CONFIG (a);
+ GimpColorBalanceConfig *config_b = GIMP_COLOR_BALANCE_CONFIG (b);
+ GimpTransferMode range;
+
+ if (! gimp_operation_settings_config_equal_base (a, b))
+ return FALSE;
+
+ for (range = GIMP_TRANSFER_SHADOWS;
+ range <= GIMP_TRANSFER_HIGHLIGHTS;
+ range++)
+ {
+ if (config_a->cyan_red[range] != config_b->cyan_red[range] ||
+ config_a->magenta_green[range] != config_b->magenta_green[range] ||
+ config_a->yellow_blue[range] != config_b->yellow_blue[range])
+ return FALSE;
+ }
+
+ /* don't compare "range" */
+
+ if (config_a->preserve_luminosity != config_b->preserve_luminosity)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+gimp_color_balance_config_reset (GimpConfig *config)
+{
+ GimpColorBalanceConfig *cb_config = GIMP_COLOR_BALANCE_CONFIG (config);
+ GimpTransferMode range;
+
+ gimp_operation_settings_config_reset_base (config);
+
+ for (range = GIMP_TRANSFER_SHADOWS;
+ range <= GIMP_TRANSFER_HIGHLIGHTS;
+ range++)
+ {
+ cb_config->range = range;
+ gimp_color_balance_config_reset_range (cb_config);
+ }
+
+ gimp_config_reset_property (G_OBJECT (config), "range");
+ gimp_config_reset_property (G_OBJECT (config), "preserve-luminosity");
+}
+
+static gboolean
+gimp_color_balance_config_copy (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags)
+{
+ GimpColorBalanceConfig *src_config = GIMP_COLOR_BALANCE_CONFIG (src);
+ GimpColorBalanceConfig *dest_config = GIMP_COLOR_BALANCE_CONFIG (dest);
+ GimpTransferMode range;
+
+ if (! gimp_operation_settings_config_copy_base (src, dest, flags))
+ return FALSE;
+
+ for (range = GIMP_TRANSFER_SHADOWS;
+ range <= GIMP_TRANSFER_HIGHLIGHTS;
+ range++)
+ {
+ dest_config->cyan_red[range] = src_config->cyan_red[range];
+ dest_config->magenta_green[range] = src_config->magenta_green[range];
+ dest_config->yellow_blue[range] = src_config->yellow_blue[range];
+ }
+
+ g_object_notify (G_OBJECT (dest), "cyan-red");
+ g_object_notify (G_OBJECT (dest), "magenta-green");
+ g_object_notify (G_OBJECT (dest), "yellow-blue");
+
+ dest_config->range = src_config->range;
+ dest_config->preserve_luminosity = src_config->preserve_luminosity;
+
+ g_object_notify (G_OBJECT (dest), "range");
+ g_object_notify (G_OBJECT (dest), "preserve-luminosity");
+
+ return TRUE;
+}
+
+
+/* public functions */
+
+void
+gimp_color_balance_config_reset_range (GimpColorBalanceConfig *config)
+{
+ g_return_if_fail (GIMP_IS_COLOR_BALANCE_CONFIG (config));
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ gimp_config_reset_property (G_OBJECT (config), "cyan-red");
+ gimp_config_reset_property (G_OBJECT (config), "magenta-green");
+ gimp_config_reset_property (G_OBJECT (config), "yellow-blue");
+
+ g_object_thaw_notify (G_OBJECT (config));
+}
diff --git a/app/operations/gimpcolorbalanceconfig.h b/app/operations/gimpcolorbalanceconfig.h
new file mode 100644
index 0000000..4c58cea
--- /dev/null
+++ b/app/operations/gimpcolorbalanceconfig.h
@@ -0,0 +1,62 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpcolorbalanceconfig.h
+ * Copyright (C) 2007 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/>.
+ */
+
+#ifndef __GIMP_COLOR_BALANCE_CONFIG_H__
+#define __GIMP_COLOR_BALANCE_CONFIG_H__
+
+
+#include "gimpoperationsettings.h"
+
+
+#define GIMP_TYPE_COLOR_BALANCE_CONFIG (gimp_color_balance_config_get_type ())
+#define GIMP_COLOR_BALANCE_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_BALANCE_CONFIG, GimpColorBalanceConfig))
+#define GIMP_COLOR_BALANCE_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_BALANCE_CONFIG, GimpColorBalanceConfigClass))
+#define GIMP_IS_COLOR_BALANCE_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_BALANCE_CONFIG))
+#define GIMP_IS_COLOR_BALANCE_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_BALANCE_CONFIG))
+#define GIMP_COLOR_BALANCE_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_BALANCE_CONFIG, GimpColorBalanceConfigClass))
+
+
+typedef struct _GimpColorBalanceConfigClass GimpColorBalanceConfigClass;
+
+struct _GimpColorBalanceConfig
+{
+ GimpOperationSettings parent_instance;
+
+ GimpTransferMode range;
+
+ gdouble cyan_red[3];
+ gdouble magenta_green[3];
+ gdouble yellow_blue[3];
+
+ gboolean preserve_luminosity;
+};
+
+struct _GimpColorBalanceConfigClass
+{
+ GimpOperationSettingsClass parent_class;
+};
+
+
+GType gimp_color_balance_config_get_type (void) G_GNUC_CONST;
+
+void gimp_color_balance_config_reset_range (GimpColorBalanceConfig *config);
+
+
+#endif /* __GIMP_COLOR_BALANCE_CONFIG_H__ */
diff --git a/app/operations/gimpcurvesconfig.c b/app/operations/gimpcurvesconfig.c
new file mode 100644
index 0000000..b63bcf3
--- /dev/null
+++ b/app/operations/gimpcurvesconfig.c
@@ -0,0 +1,695 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpcurvesconfig.c
+ * Copyright (C) 2007 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 <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "operations-types.h"
+
+#include "core/gimp-utils.h"
+#include "core/gimpcurve.h"
+#include "core/gimphistogram.h"
+
+#include "gimpcurvesconfig.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_LINEAR,
+ PROP_CHANNEL,
+ PROP_CURVE
+};
+
+
+static void gimp_curves_config_iface_init (GimpConfigInterface *iface);
+
+static void gimp_curves_config_finalize (GObject *object);
+static void gimp_curves_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_curves_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static gboolean gimp_curves_config_serialize (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data);
+static gboolean gimp_curves_config_deserialize (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data);
+static gboolean gimp_curves_config_equal (GimpConfig *a,
+ GimpConfig *b);
+static void gimp_curves_config_reset (GimpConfig *config);
+static gboolean gimp_curves_config_copy (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags);
+
+static void gimp_curves_config_curve_dirty (GimpCurve *curve,
+ GimpCurvesConfig *config);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpCurvesConfig, gimp_curves_config,
+ GIMP_TYPE_OPERATION_SETTINGS,
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
+ gimp_curves_config_iface_init))
+
+#define parent_class gimp_curves_config_parent_class
+
+
+static void
+gimp_curves_config_class_init (GimpCurvesConfigClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
+
+ object_class->finalize = gimp_curves_config_finalize;
+ object_class->set_property = gimp_curves_config_set_property;
+ object_class->get_property = gimp_curves_config_get_property;
+
+ viewable_class->default_icon_name = "gimp-tool-curves";
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_LINEAR,
+ "linear",
+ _("Linear"),
+ _("Work on linear RGB"),
+ FALSE, 0);
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_CHANNEL,
+ "channel",
+ _("Channel"),
+ _("The affected channel"),
+ GIMP_TYPE_HISTOGRAM_CHANNEL,
+ GIMP_HISTOGRAM_VALUE, 0);
+
+ GIMP_CONFIG_PROP_OBJECT (object_class, PROP_CURVE,
+ "curve",
+ _("Curve"),
+ _("Curve"),
+ GIMP_TYPE_CURVE,
+ GIMP_CONFIG_PARAM_AGGREGATE);
+}
+
+static void
+gimp_curves_config_iface_init (GimpConfigInterface *iface)
+{
+ iface->serialize = gimp_curves_config_serialize;
+ iface->deserialize = gimp_curves_config_deserialize;
+ iface->equal = gimp_curves_config_equal;
+ iface->reset = gimp_curves_config_reset;
+ iface->copy = gimp_curves_config_copy;
+}
+
+static void
+gimp_curves_config_init (GimpCurvesConfig *self)
+{
+ GimpHistogramChannel channel;
+
+ for (channel = GIMP_HISTOGRAM_VALUE;
+ channel <= GIMP_HISTOGRAM_ALPHA;
+ channel++)
+ {
+ self->curve[channel] = GIMP_CURVE (gimp_curve_new ("curves config"));
+
+ g_signal_connect_object (self->curve[channel], "dirty",
+ G_CALLBACK (gimp_curves_config_curve_dirty),
+ self, 0);
+ }
+
+ gimp_config_reset (GIMP_CONFIG (self));
+}
+
+static void
+gimp_curves_config_finalize (GObject *object)
+{
+ GimpCurvesConfig *self = GIMP_CURVES_CONFIG (object);
+ GimpHistogramChannel channel;
+
+ for (channel = GIMP_HISTOGRAM_VALUE;
+ channel <= GIMP_HISTOGRAM_ALPHA;
+ channel++)
+ {
+ g_object_unref (self->curve[channel]);
+ self->curve[channel] = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_curves_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpCurvesConfig *self = GIMP_CURVES_CONFIG (object);
+
+ switch (property_id)
+ {
+ case PROP_LINEAR:
+ g_value_set_boolean (value, self->linear);
+ break;
+
+ case PROP_CHANNEL:
+ g_value_set_enum (value, self->channel);
+ break;
+
+ case PROP_CURVE:
+ g_value_set_object (value, self->curve[self->channel]);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_curves_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpCurvesConfig *self = GIMP_CURVES_CONFIG (object);
+
+ switch (property_id)
+ {
+ case PROP_LINEAR:
+ self->linear = g_value_get_boolean (value);
+ break;
+
+ case PROP_CHANNEL:
+ self->channel = g_value_get_enum (value);
+ g_object_notify (object, "curve");
+ break;
+
+ case PROP_CURVE:
+ {
+ GimpCurve *src_curve = g_value_get_object (value);
+ GimpCurve *dest_curve = self->curve[self->channel];
+
+ if (src_curve && dest_curve)
+ {
+ gimp_config_copy (GIMP_CONFIG (src_curve),
+ GIMP_CONFIG (dest_curve), 0);
+ }
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gimp_curves_config_serialize (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data)
+{
+ GimpCurvesConfig *c_config = GIMP_CURVES_CONFIG (config);
+ GimpHistogramChannel channel;
+ GimpHistogramChannel old_channel;
+ gboolean success = TRUE;
+
+ if (! gimp_operation_settings_config_serialize_base (config, writer, data) ||
+ ! gimp_config_serialize_property_by_name (config, "linear", writer))
+ return FALSE;
+
+ old_channel = c_config->channel;
+
+ for (channel = GIMP_HISTOGRAM_VALUE;
+ channel <= GIMP_HISTOGRAM_ALPHA;
+ channel++)
+ {
+ c_config->channel = channel;
+
+ /* serialize the channel properties manually (not using
+ * gimp_config_serialize_properties()), so the parent class'
+ * properties don't end up in the config file one per channel.
+ * See bug #700653.
+ */
+ success =
+ (gimp_config_serialize_property_by_name (config, "channel", writer) &&
+ gimp_config_serialize_property_by_name (config, "curve", writer));
+
+ if (! success)
+ break;
+ }
+
+ c_config->channel = old_channel;
+
+ return success;
+}
+
+static gboolean
+gimp_curves_config_deserialize (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data)
+{
+ GimpCurvesConfig *c_config = GIMP_CURVES_CONFIG (config);
+ GimpHistogramChannel old_channel;
+ gboolean success = TRUE;
+
+ old_channel = c_config->channel;
+
+ success = gimp_config_deserialize_properties (config, scanner, nest_level);
+
+ g_object_set (config, "channel", old_channel, NULL);
+
+ return success;
+}
+
+static gboolean
+gimp_curves_config_equal (GimpConfig *a,
+ GimpConfig *b)
+{
+ GimpCurvesConfig *config_a = GIMP_CURVES_CONFIG (a);
+ GimpCurvesConfig *config_b = GIMP_CURVES_CONFIG (b);
+ GimpHistogramChannel channel;
+
+ if (! gimp_operation_settings_config_equal_base (a, b) ||
+ config_a->linear != config_b->linear)
+ return FALSE;
+
+ for (channel = GIMP_HISTOGRAM_VALUE;
+ channel <= GIMP_HISTOGRAM_ALPHA;
+ channel++)
+ {
+ GimpCurve *curve_a = config_a->curve[channel];
+ GimpCurve *curve_b = config_b->curve[channel];
+
+ if (curve_a && curve_b)
+ {
+ if (! gimp_config_is_equal_to (GIMP_CONFIG (curve_a),
+ GIMP_CONFIG (curve_b)))
+ return FALSE;
+ }
+ else if (curve_a || curve_b)
+ {
+ return FALSE;
+ }
+ }
+
+ /* don't compare "channel" */
+
+ return TRUE;
+}
+
+static void
+gimp_curves_config_reset (GimpConfig *config)
+{
+ GimpCurvesConfig *c_config = GIMP_CURVES_CONFIG (config);
+ GimpHistogramChannel channel;
+
+ gimp_operation_settings_config_reset_base (config);
+
+ for (channel = GIMP_HISTOGRAM_VALUE;
+ channel <= GIMP_HISTOGRAM_ALPHA;
+ channel++)
+ {
+ c_config->channel = channel;
+ gimp_curves_config_reset_channel (c_config);
+ }
+
+ gimp_config_reset_property (G_OBJECT (config), "linear");
+ gimp_config_reset_property (G_OBJECT (config), "channel");
+}
+
+static gboolean
+gimp_curves_config_copy (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags)
+{
+ GimpCurvesConfig *src_config = GIMP_CURVES_CONFIG (src);
+ GimpCurvesConfig *dest_config = GIMP_CURVES_CONFIG (dest);
+ GimpHistogramChannel channel;
+
+ if (! gimp_operation_settings_config_copy_base (src, dest, flags))
+ return FALSE;
+
+ for (channel = GIMP_HISTOGRAM_VALUE;
+ channel <= GIMP_HISTOGRAM_ALPHA;
+ channel++)
+ {
+ gimp_config_copy (GIMP_CONFIG (src_config->curve[channel]),
+ GIMP_CONFIG (dest_config->curve[channel]),
+ flags);
+ }
+
+ dest_config->linear = src_config->linear;
+ dest_config->channel = src_config->channel;
+
+ g_object_notify (G_OBJECT (dest), "linear");
+ g_object_notify (G_OBJECT (dest), "channel");
+
+ return TRUE;
+}
+
+static void
+gimp_curves_config_curve_dirty (GimpCurve *curve,
+ GimpCurvesConfig *config)
+{
+ g_object_notify (G_OBJECT (config), "curve");
+}
+
+
+/* public functions */
+
+GObject *
+gimp_curves_config_new_spline (gint32 channel,
+ const gdouble *points,
+ gint n_points)
+{
+ GimpCurvesConfig *config;
+ GimpCurve *curve;
+ gint i;
+
+ g_return_val_if_fail (channel >= GIMP_HISTOGRAM_VALUE &&
+ channel <= GIMP_HISTOGRAM_ALPHA, NULL);
+ g_return_val_if_fail (points != NULL, NULL);
+ g_return_val_if_fail (n_points >= 2 && n_points <= 1024, NULL);
+
+ config = g_object_new (GIMP_TYPE_CURVES_CONFIG, NULL);
+
+ curve = config->curve[channel];
+
+ gimp_data_freeze (GIMP_DATA (curve));
+
+ gimp_curve_set_curve_type (curve, GIMP_CURVE_SMOOTH);
+ gimp_curve_clear_points (curve);
+
+ for (i = 0; i < n_points; i++)
+ gimp_curve_add_point (curve,
+ (gdouble) points[i * 2],
+ (gdouble) points[i * 2 + 1]);
+
+ gimp_data_thaw (GIMP_DATA (curve));
+
+ return G_OBJECT (config);
+}
+
+GObject *
+gimp_curves_config_new_explicit (gint32 channel,
+ const gdouble *samples,
+ gint n_samples)
+{
+ GimpCurvesConfig *config;
+ GimpCurve *curve;
+ gint i;
+
+ g_return_val_if_fail (channel >= GIMP_HISTOGRAM_VALUE &&
+ channel <= GIMP_HISTOGRAM_ALPHA, NULL);
+ g_return_val_if_fail (samples != NULL, NULL);
+ g_return_val_if_fail (n_samples >= 2 && n_samples <= 4096, NULL);
+
+ config = g_object_new (GIMP_TYPE_CURVES_CONFIG, NULL);
+
+ curve = config->curve[channel];
+
+ gimp_data_freeze (GIMP_DATA (curve));
+
+ gimp_curve_set_curve_type (curve, GIMP_CURVE_FREE);
+ gimp_curve_set_n_samples (curve, n_samples);
+
+ for (i = 0; i < n_samples; i++)
+ gimp_curve_set_curve (curve,
+ (gdouble) i / (gdouble) (n_samples - 1),
+ (gdouble) samples[i]);
+
+ gimp_data_thaw (GIMP_DATA (curve));
+
+ return G_OBJECT (config);
+}
+
+GObject *
+gimp_curves_config_new_spline_cruft (gint32 channel,
+ const guint8 *points,
+ gint n_points)
+{
+ GObject *config;
+ gdouble *d_points;
+ gint i;
+
+ g_return_val_if_fail (channel >= GIMP_HISTOGRAM_VALUE &&
+ channel <= GIMP_HISTOGRAM_ALPHA, NULL);
+ g_return_val_if_fail (points != NULL, NULL);
+ g_return_val_if_fail (n_points >= 2 && n_points <= 1024, NULL);
+
+ d_points = g_new (gdouble, 2 * n_points);
+
+ for (i = 0; i < n_points; i++)
+ {
+ d_points[i * 2] = (gdouble) points[i * 2] / 255.0;
+ d_points[i * 2 + 1] = (gdouble) points[i * 2 + 1] / 255.0;
+ }
+
+ config = gimp_curves_config_new_spline (channel, d_points, n_points);
+
+ g_free (d_points);
+
+ return config;
+}
+
+GObject *
+gimp_curves_config_new_explicit_cruft (gint32 channel,
+ const guint8 *samples,
+ gint n_samples)
+{
+ GObject *config;
+ gdouble *d_samples;
+ gint i;
+
+ g_return_val_if_fail (channel >= GIMP_HISTOGRAM_VALUE &&
+ channel <= GIMP_HISTOGRAM_ALPHA, NULL);
+ g_return_val_if_fail (samples != NULL, NULL);
+ g_return_val_if_fail (n_samples >= 2 && n_samples <= 4096, NULL);
+
+ d_samples = g_new (gdouble, n_samples);
+
+ for (i = 0; i < n_samples; i++)
+ {
+ d_samples[i] = (gdouble) samples[i] / 255.0;
+ }
+
+ config = gimp_curves_config_new_explicit (channel, d_samples, n_samples);
+
+ g_free (d_samples);
+
+ return config;
+}
+
+void
+gimp_curves_config_reset_channel (GimpCurvesConfig *config)
+{
+ g_return_if_fail (GIMP_IS_CURVES_CONFIG (config));
+
+ gimp_config_reset (GIMP_CONFIG (config->curve[config->channel]));
+}
+
+#define GIMP_CURVE_N_CRUFT_POINTS 17
+
+gboolean
+gimp_curves_config_load_cruft (GimpCurvesConfig *config,
+ GInputStream *input,
+ GError **error)
+{
+ GDataInputStream *data_input;
+ gint index[5][GIMP_CURVE_N_CRUFT_POINTS];
+ gint value[5][GIMP_CURVE_N_CRUFT_POINTS];
+ gchar *line;
+ gsize line_len;
+ gint i, j;
+
+ g_return_val_if_fail (GIMP_IS_CURVES_CONFIG (config), FALSE);
+ g_return_val_if_fail (G_IS_INPUT_STREAM (input), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ data_input = g_data_input_stream_new (input);
+
+ line_len = 64;
+ line = gimp_data_input_stream_read_line_always (data_input, &line_len,
+ NULL, error);
+ if (! line)
+ return FALSE;
+
+ if (strcmp (line, "# GIMP Curves File") != 0)
+ {
+ g_set_error_literal (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
+ _("not a GIMP Curves file"));
+ g_object_unref (data_input);
+ g_free (line);
+ return FALSE;
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ for (j = 0; j < GIMP_CURVE_N_CRUFT_POINTS; j++)
+ {
+ gchar *x_str = NULL;
+ gchar *y_str = NULL;
+
+ if (! (x_str = g_data_input_stream_read_upto (data_input, " ", -1,
+ NULL, NULL, error)) ||
+ ! g_data_input_stream_read_byte (data_input, NULL, error) ||
+ ! (y_str = g_data_input_stream_read_upto (data_input, " ", -1,
+ NULL, NULL, error)) ||
+ ! g_data_input_stream_read_byte (data_input, NULL, error))
+ {
+ g_free (x_str);
+ g_free (y_str);
+ g_object_unref (data_input);
+ return FALSE;
+ }
+
+ if (sscanf (x_str, "%d", &index[i][j]) != 1 ||
+ sscanf (y_str, "%d", &value[i][j]) != 1)
+ {
+ g_set_error_literal (error,
+ GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
+ _("Parse error, didn't find 2 integers"));
+ g_free (x_str);
+ g_free (y_str);
+ g_object_unref (data_input);
+ return FALSE;
+ }
+
+ g_free (x_str);
+ g_free (y_str);
+ }
+ }
+
+ g_object_unref (data_input);
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ for (i = 0; i < 5; i++)
+ {
+ GimpCurve *curve = config->curve[i];
+
+ gimp_data_freeze (GIMP_DATA (curve));
+
+ gimp_curve_set_curve_type (curve, GIMP_CURVE_SMOOTH);
+ gimp_curve_clear_points (curve);
+
+ for (j = 0; j < GIMP_CURVE_N_CRUFT_POINTS; j++)
+ {
+ gdouble x;
+ gdouble y;
+
+ x = (gdouble) index[i][j] / 255.0;
+ y = (gdouble) value[i][j] / 255.0;
+
+ if (x >= 0.0)
+ gimp_curve_add_point (curve, x, y);
+ }
+
+ gimp_data_thaw (GIMP_DATA (curve));
+ }
+
+ config->linear = FALSE;
+
+ g_object_notify (G_OBJECT (config), "linear");
+
+ g_object_thaw_notify (G_OBJECT (config));
+
+ return TRUE;
+}
+
+gboolean
+gimp_curves_config_save_cruft (GimpCurvesConfig *config,
+ GOutputStream *output,
+ GError **error)
+{
+ GString *string;
+ gint i;
+
+ g_return_val_if_fail (GIMP_IS_CURVES_CONFIG (config), FALSE);
+ g_return_val_if_fail (G_IS_OUTPUT_STREAM (output), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ string = g_string_new ("# GIMP Curves File\n");
+
+ for (i = 0; i < 5; i++)
+ {
+ GimpCurve *curve = config->curve[i];
+ gint j;
+
+ if (curve->curve_type == GIMP_CURVE_SMOOTH)
+ {
+ g_object_ref (curve);
+ }
+ else
+ {
+ curve = GIMP_CURVE (gimp_data_duplicate (GIMP_DATA (curve)));
+
+ gimp_curve_set_curve_type (curve, GIMP_CURVE_SMOOTH);
+ }
+
+ for (j = 0; j < GIMP_CURVE_N_CRUFT_POINTS; j++)
+ {
+ gint x = -1;
+ gint y = -1;
+
+ if (j < gimp_curve_get_n_points (curve))
+ {
+ gdouble point_x;
+ gdouble point_y;
+
+ gimp_curve_get_point (curve, j, &point_x, &point_y);
+
+ x = floor (point_x * 255.999);
+ y = floor (point_y * 255.999);
+ }
+
+ g_string_append_printf (string, "%d %d ", x, y);
+ }
+
+ g_string_append_printf (string, "\n");
+
+ g_object_unref (curve);
+ }
+
+ if (! g_output_stream_write_all (output, string->str, string->len,
+ NULL, NULL, error))
+ {
+ g_prefix_error (error, _("Writing curves file failed: "));
+ g_string_free (string, TRUE);
+ return FALSE;
+ }
+
+ g_string_free (string, TRUE);
+
+ return TRUE;
+}
diff --git a/app/operations/gimpcurvesconfig.h b/app/operations/gimpcurvesconfig.h
new file mode 100644
index 0000000..c6241fe
--- /dev/null
+++ b/app/operations/gimpcurvesconfig.h
@@ -0,0 +1,81 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpcurvesconfig.h
+ * Copyright (C) 2007 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/>.
+ */
+
+#ifndef __GIMP_CURVES_CONFIG_H__
+#define __GIMP_CURVES_CONFIG_H__
+
+
+#include "gimpoperationsettings.h"
+
+
+#define GIMP_TYPE_CURVES_CONFIG (gimp_curves_config_get_type ())
+#define GIMP_CURVES_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CURVES_CONFIG, GimpCurvesConfig))
+#define GIMP_CURVES_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CURVES_CONFIG, GimpCurvesConfigClass))
+#define GIMP_IS_CURVES_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CURVES_CONFIG))
+#define GIMP_IS_CURVES_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CURVES_CONFIG))
+#define GIMP_CURVES_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CURVES_CONFIG, GimpCurvesConfigClass))
+
+
+typedef struct _GimpCurvesConfigClass GimpCurvesConfigClass;
+
+struct _GimpCurvesConfig
+{
+ GimpOperationSettings parent_instance;
+
+ gboolean linear;
+
+ GimpHistogramChannel channel;
+
+ GimpCurve *curve[5];
+};
+
+struct _GimpCurvesConfigClass
+{
+ GimpOperationSettingsClass parent_class;
+};
+
+
+GType gimp_curves_config_get_type (void) G_GNUC_CONST;
+
+GObject * gimp_curves_config_new_spline (gint32 channel,
+ const gdouble *points,
+ gint n_points);
+GObject * gimp_curves_config_new_explicit (gint32 channel,
+ const gdouble *samples,
+ gint n_samples);
+
+GObject * gimp_curves_config_new_spline_cruft (gint32 channel,
+ const guint8 *points,
+ gint n_points);
+GObject * gimp_curves_config_new_explicit_cruft (gint32 channel,
+ const guint8 *samples,
+ gint n_samples);
+
+void gimp_curves_config_reset_channel (GimpCurvesConfig *config);
+
+gboolean gimp_curves_config_load_cruft (GimpCurvesConfig *config,
+ GInputStream *input,
+ GError **error);
+gboolean gimp_curves_config_save_cruft (GimpCurvesConfig *config,
+ GOutputStream *output,
+ GError **error);
+
+
+#endif /* __GIMP_CURVES_CONFIG_H__ */
diff --git a/app/operations/gimphuesaturationconfig.c b/app/operations/gimphuesaturationconfig.c
new file mode 100644
index 0000000..4f0a397
--- /dev/null
+++ b/app/operations/gimphuesaturationconfig.c
@@ -0,0 +1,367 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimphuesaturationconfig.c
+ * Copyright (C) 2007 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 <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpconfig/gimpconfig.h"
+
+#include "operations-types.h"
+
+#include "gimphuesaturationconfig.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_RANGE,
+ PROP_HUE,
+ PROP_SATURATION,
+ PROP_LIGHTNESS,
+ PROP_OVERLAP
+};
+
+
+static void gimp_hue_saturation_config_iface_init (GimpConfigInterface *iface);
+
+static void gimp_hue_saturation_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_hue_saturation_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static gboolean gimp_hue_saturation_config_serialize (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data);
+static gboolean gimp_hue_saturation_config_deserialize (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data);
+static gboolean gimp_hue_saturation_config_equal (GimpConfig *a,
+ GimpConfig *b);
+static void gimp_hue_saturation_config_reset (GimpConfig *config);
+static gboolean gimp_hue_saturation_config_copy (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpHueSaturationConfig, gimp_hue_saturation_config,
+ GIMP_TYPE_OPERATION_SETTINGS,
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
+ gimp_hue_saturation_config_iface_init))
+
+#define parent_class gimp_hue_saturation_config_parent_class
+
+
+static void
+gimp_hue_saturation_config_class_init (GimpHueSaturationConfigClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
+
+ object_class->set_property = gimp_hue_saturation_config_set_property;
+ object_class->get_property = gimp_hue_saturation_config_get_property;
+
+ viewable_class->default_icon_name = "gimp-tool-hue-saturation";
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_RANGE,
+ "range",
+ _("Range"),
+ _("The affected range"),
+ GIMP_TYPE_HUE_RANGE,
+ GIMP_HUE_RANGE_ALL, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_HUE,
+ "hue",
+ _("Hue"),
+ _("Hue"),
+ -1.0, 1.0, 0.0, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_SATURATION,
+ "saturation",
+ _("Saturation"),
+ _("Saturation"),
+ -1.0, 1.0, 0.0, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LIGHTNESS,
+ "lightness",
+ _("Lightness"),
+ _("Lightness"),
+ -1.0, 1.0, 0.0, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OVERLAP,
+ "overlap",
+ _("Overlap"),
+ _("Overlap"),
+ 0.0, 1.0, 0.0, 0);
+}
+
+static void
+gimp_hue_saturation_config_iface_init (GimpConfigInterface *iface)
+{
+ iface->serialize = gimp_hue_saturation_config_serialize;
+ iface->deserialize = gimp_hue_saturation_config_deserialize;
+ iface->equal = gimp_hue_saturation_config_equal;
+ iface->reset = gimp_hue_saturation_config_reset;
+ iface->copy = gimp_hue_saturation_config_copy;
+}
+
+static void
+gimp_hue_saturation_config_init (GimpHueSaturationConfig *self)
+{
+ gimp_config_reset (GIMP_CONFIG (self));
+}
+
+static void
+gimp_hue_saturation_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpHueSaturationConfig *self = GIMP_HUE_SATURATION_CONFIG (object);
+
+ switch (property_id)
+ {
+ case PROP_RANGE:
+ g_value_set_enum (value, self->range);
+ break;
+
+ case PROP_HUE:
+ g_value_set_double (value, self->hue[self->range]);
+ break;
+
+ case PROP_SATURATION:
+ g_value_set_double (value, self->saturation[self->range]);
+ break;
+
+ case PROP_LIGHTNESS:
+ g_value_set_double (value, self->lightness[self->range]);
+ break;
+
+ case PROP_OVERLAP:
+ g_value_set_double (value, self->overlap);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_hue_saturation_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpHueSaturationConfig *self = GIMP_HUE_SATURATION_CONFIG (object);
+
+ switch (property_id)
+ {
+ case PROP_RANGE:
+ self->range = g_value_get_enum (value);
+ g_object_notify (object, "hue");
+ g_object_notify (object, "saturation");
+ g_object_notify (object, "lightness");
+ break;
+
+ case PROP_HUE:
+ self->hue[self->range] = g_value_get_double (value);
+ break;
+
+ case PROP_SATURATION:
+ self->saturation[self->range] = g_value_get_double (value);
+ break;
+
+ case PROP_LIGHTNESS:
+ self->lightness[self->range] = g_value_get_double (value);
+ break;
+
+ case PROP_OVERLAP:
+ self->overlap = g_value_get_double (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gimp_hue_saturation_config_serialize (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data)
+{
+ GimpHueSaturationConfig *hs_config = GIMP_HUE_SATURATION_CONFIG (config);
+ GimpHueRange range;
+ GimpHueRange old_range;
+ gboolean success = TRUE;
+
+ if (! gimp_operation_settings_config_serialize_base (config, writer, data))
+ return FALSE;
+
+ old_range = hs_config->range;
+
+ for (range = GIMP_HUE_RANGE_ALL; range <= GIMP_HUE_RANGE_MAGENTA; range++)
+ {
+ hs_config->range = range;
+
+ success = (gimp_config_serialize_property_by_name (config, "range",
+ writer) &&
+ gimp_config_serialize_property_by_name (config, "hue",
+ writer) &&
+ gimp_config_serialize_property_by_name (config, "saturation",
+ writer) &&
+ gimp_config_serialize_property_by_name (config, "lightness",
+ writer));
+
+ if (! success)
+ break;
+ }
+
+ if (success)
+ success = gimp_config_serialize_property_by_name (config, "overlap",
+ writer);
+
+ hs_config->range = old_range;
+
+ return success;
+}
+
+static gboolean
+gimp_hue_saturation_config_deserialize (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data)
+{
+ GimpHueSaturationConfig *hs_config = GIMP_HUE_SATURATION_CONFIG (config);
+ GimpHueRange old_range;
+ gboolean success = TRUE;
+
+ old_range = hs_config->range;
+
+ success = gimp_config_deserialize_properties (config, scanner, nest_level);
+
+ g_object_set (config, "range", old_range, NULL);
+
+ return success;
+}
+
+static gboolean
+gimp_hue_saturation_config_equal (GimpConfig *a,
+ GimpConfig *b)
+{
+ GimpHueSaturationConfig *config_a = GIMP_HUE_SATURATION_CONFIG (a);
+ GimpHueSaturationConfig *config_b = GIMP_HUE_SATURATION_CONFIG (b);
+ GimpHueRange range;
+
+ if (! gimp_operation_settings_config_equal_base (a, b))
+ return FALSE;
+
+ for (range = GIMP_HUE_RANGE_ALL; range <= GIMP_HUE_RANGE_MAGENTA; range++)
+ {
+ if (config_a->hue[range] != config_b->hue[range] ||
+ config_a->saturation[range] != config_b->saturation[range] ||
+ config_a->lightness[range] != config_b->lightness[range])
+ return FALSE;
+ }
+
+ /* don't compare "range" */
+
+ if (config_a->overlap != config_b->overlap)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+gimp_hue_saturation_config_reset (GimpConfig *config)
+{
+ GimpHueSaturationConfig *hs_config = GIMP_HUE_SATURATION_CONFIG (config);
+ GimpHueRange range;
+
+ gimp_operation_settings_config_reset_base (config);
+
+ for (range = GIMP_HUE_RANGE_ALL; range <= GIMP_HUE_RANGE_MAGENTA; range++)
+ {
+ hs_config->range = range;
+ gimp_hue_saturation_config_reset_range (hs_config);
+ }
+
+ gimp_config_reset_property (G_OBJECT (config), "range");
+ gimp_config_reset_property (G_OBJECT (config), "overlap");
+}
+
+static gboolean
+gimp_hue_saturation_config_copy (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags)
+{
+ GimpHueSaturationConfig *src_config = GIMP_HUE_SATURATION_CONFIG (src);
+ GimpHueSaturationConfig *dest_config = GIMP_HUE_SATURATION_CONFIG (dest);
+ GimpHueRange range;
+
+ if (! gimp_operation_settings_config_copy_base (src, dest, flags))
+ return FALSE;
+
+ for (range = GIMP_HUE_RANGE_ALL; range <= GIMP_HUE_RANGE_MAGENTA; range++)
+ {
+ dest_config->hue[range] = src_config->hue[range];
+ dest_config->saturation[range] = src_config->saturation[range];
+ dest_config->lightness[range] = src_config->lightness[range];
+ }
+
+ g_object_notify (G_OBJECT (dest), "hue");
+ g_object_notify (G_OBJECT (dest), "saturation");
+ g_object_notify (G_OBJECT (dest), "lightness");
+
+ dest_config->range = src_config->range;
+ dest_config->overlap = src_config->overlap;
+
+ g_object_notify (G_OBJECT (dest), "range");
+ g_object_notify (G_OBJECT (dest), "overlap");
+
+ return TRUE;
+}
+
+
+/* public functions */
+
+void
+gimp_hue_saturation_config_reset_range (GimpHueSaturationConfig *config)
+{
+ g_return_if_fail (GIMP_IS_HUE_SATURATION_CONFIG (config));
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ gimp_config_reset_property (G_OBJECT (config), "hue");
+ gimp_config_reset_property (G_OBJECT (config), "saturation");
+ gimp_config_reset_property (G_OBJECT (config), "lightness");
+
+ g_object_thaw_notify (G_OBJECT (config));
+}
diff --git a/app/operations/gimphuesaturationconfig.h b/app/operations/gimphuesaturationconfig.h
new file mode 100644
index 0000000..151ac05
--- /dev/null
+++ b/app/operations/gimphuesaturationconfig.h
@@ -0,0 +1,62 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimphuesaturationconfig.h
+ * Copyright (C) 2007 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/>.
+ */
+
+#ifndef __GIMP_HUE_SATURATION_CONFIG_H__
+#define __GIMP_HUE_SATURATION_CONFIG_H__
+
+
+#include "gimpoperationsettings.h"
+
+
+#define GIMP_TYPE_HUE_SATURATION_CONFIG (gimp_hue_saturation_config_get_type ())
+#define GIMP_HUE_SATURATION_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_HUE_SATURATION_CONFIG, GimpHueSaturationConfig))
+#define GIMP_HUE_SATURATION_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_HUE_SATURATION_CONFIG, GimpHueSaturationConfigClass))
+#define GIMP_IS_HUE_SATURATION_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_HUE_SATURATION_CONFIG))
+#define GIMP_IS_HUE_SATURATION_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_HUE_SATURATION_CONFIG))
+#define GIMP_HUE_SATURATION_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_HUE_SATURATION_CONFIG, GimpHueSaturationConfigClass))
+
+
+typedef struct _GimpHueSaturationConfigClass GimpHueSaturationConfigClass;
+
+struct _GimpHueSaturationConfig
+{
+ GimpOperationSettings parent_instance;
+
+ GimpHueRange range;
+
+ gdouble hue[7];
+ gdouble saturation[7];
+ gdouble lightness[7];
+
+ gdouble overlap;
+};
+
+struct _GimpHueSaturationConfigClass
+{
+ GimpOperationSettingsClass parent_class;
+};
+
+
+GType gimp_hue_saturation_config_get_type (void) G_GNUC_CONST;
+
+void gimp_hue_saturation_config_reset_range (GimpHueSaturationConfig *config);
+
+
+#endif /* __GIMP_HUE_SATURATION_CONFIG_H__ */
diff --git a/app/operations/gimplevelsconfig.c b/app/operations/gimplevelsconfig.c
new file mode 100644
index 0000000..df5d349
--- /dev/null
+++ b/app/operations/gimplevelsconfig.c
@@ -0,0 +1,964 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimplevelsconfig.c
+ * Copyright (C) 2007 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 <errno.h>
+
+#include <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "operations-types.h"
+
+#include "core/gimp-utils.h"
+#include "core/gimpcurve.h"
+#include "core/gimphistogram.h"
+
+#include "gimpcurvesconfig.h"
+#include "gimplevelsconfig.h"
+#include "gimpoperationlevels.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_LINEAR,
+ PROP_CHANNEL,
+ PROP_LOW_INPUT,
+ PROP_HIGH_INPUT,
+ PROP_CLAMP_INPUT,
+ PROP_GAMMA,
+ PROP_LOW_OUTPUT,
+ PROP_HIGH_OUTPUT,
+ PROP_CLAMP_OUTPUT
+};
+
+
+static void gimp_levels_config_iface_init (GimpConfigInterface *iface);
+
+static void gimp_levels_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_levels_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static gboolean gimp_levels_config_serialize (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data);
+static gboolean gimp_levels_config_deserialize (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data);
+static gboolean gimp_levels_config_equal (GimpConfig *a,
+ GimpConfig *b);
+static void gimp_levels_config_reset (GimpConfig *config);
+static gboolean gimp_levels_config_copy (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpLevelsConfig, gimp_levels_config,
+ GIMP_TYPE_OPERATION_SETTINGS,
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
+ gimp_levels_config_iface_init))
+
+#define parent_class gimp_levels_config_parent_class
+
+
+static void
+gimp_levels_config_class_init (GimpLevelsConfigClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
+
+ object_class->set_property = gimp_levels_config_set_property;
+ object_class->get_property = gimp_levels_config_get_property;
+
+ viewable_class->default_icon_name = "gimp-tool-levels";
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_LINEAR,
+ "linear",
+ _("Linear"),
+ _("Work on linear RGB"),
+ FALSE, 0);
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_CHANNEL,
+ "channel",
+ _("Channel"),
+ _("The affected channel"),
+ GIMP_TYPE_HISTOGRAM_CHANNEL,
+ GIMP_HISTOGRAM_VALUE, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LOW_INPUT,
+ "low-input",
+ _("Low Input"),
+ _("Low Input"),
+ 0.0, 1.0, 0.0, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_HIGH_INPUT,
+ "high-input",
+ _("High Input"),
+ _("High Input"),
+ 0.0, 1.0, 1.0, 0);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CLAMP_INPUT,
+ "clamp-input",
+ _("Clamp Input"),
+ _("Clamp input values before applying output mapping."),
+ FALSE, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_GAMMA,
+ "gamma",
+ _("Gamma"),
+ _("Gamma"),
+ 0.1, 10.0, 1.0, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LOW_OUTPUT,
+ "low-output",
+ _("Low Output"),
+ _("Low Output"),
+ 0.0, 1.0, 0.0, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_HIGH_OUTPUT,
+ "high-output",
+ _("High Output"),
+ _("High Output"),
+ 0.0, 1.0, 1.0, 0);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CLAMP_OUTPUT,
+ "clamp-output",
+ _("Clamp Output"),
+ _("Clamp final output values."),
+ FALSE, 0);
+}
+
+static void
+gimp_levels_config_iface_init (GimpConfigInterface *iface)
+{
+ iface->serialize = gimp_levels_config_serialize;
+ iface->deserialize = gimp_levels_config_deserialize;
+ iface->equal = gimp_levels_config_equal;
+ iface->reset = gimp_levels_config_reset;
+ iface->copy = gimp_levels_config_copy;
+}
+
+static void
+gimp_levels_config_init (GimpLevelsConfig *self)
+{
+ gimp_config_reset (GIMP_CONFIG (self));
+}
+
+static void
+gimp_levels_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpLevelsConfig *self = GIMP_LEVELS_CONFIG (object);
+
+ switch (property_id)
+ {
+ case PROP_LINEAR:
+ g_value_set_boolean (value, self->linear);
+ break;
+
+ case PROP_CHANNEL:
+ g_value_set_enum (value, self->channel);
+ break;
+
+ case PROP_LOW_INPUT:
+ g_value_set_double (value, self->low_input[self->channel]);
+ break;
+
+ case PROP_HIGH_INPUT:
+ g_value_set_double (value, self->high_input[self->channel]);
+ break;
+
+ case PROP_CLAMP_INPUT:
+ g_value_set_boolean (value, self->clamp_input);
+ break;
+
+ case PROP_GAMMA:
+ g_value_set_double (value, self->gamma[self->channel]);
+ break;
+
+ case PROP_LOW_OUTPUT:
+ g_value_set_double (value, self->low_output[self->channel]);
+ break;
+
+ case PROP_HIGH_OUTPUT:
+ g_value_set_double (value, self->high_output[self->channel]);
+ break;
+
+ case PROP_CLAMP_OUTPUT:
+ g_value_set_boolean (value, self->clamp_output);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_levels_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpLevelsConfig *self = GIMP_LEVELS_CONFIG (object);
+
+ switch (property_id)
+ {
+ case PROP_LINEAR:
+ self->linear = g_value_get_boolean (value);
+ break;
+
+ case PROP_CHANNEL:
+ self->channel = g_value_get_enum (value);
+ g_object_notify (object, "low-input");
+ g_object_notify (object, "high-input");
+ g_object_notify (object, "gamma");
+ g_object_notify (object, "low-output");
+ g_object_notify (object, "high-output");
+ break;
+
+ case PROP_LOW_INPUT:
+ self->low_input[self->channel] = g_value_get_double (value);
+ break;
+
+ case PROP_HIGH_INPUT:
+ self->high_input[self->channel] = g_value_get_double (value);
+ break;
+
+ case PROP_CLAMP_INPUT:
+ self->clamp_input = g_value_get_boolean (value);
+ break;
+
+ case PROP_GAMMA:
+ self->gamma[self->channel] = g_value_get_double (value);
+ break;
+
+ case PROP_LOW_OUTPUT:
+ self->low_output[self->channel] = g_value_get_double (value);
+ break;
+
+ case PROP_HIGH_OUTPUT:
+ self->high_output[self->channel] = g_value_get_double (value);
+ break;
+
+ case PROP_CLAMP_OUTPUT:
+ self->clamp_output = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gimp_levels_config_serialize (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data)
+{
+ GimpLevelsConfig *l_config = GIMP_LEVELS_CONFIG (config);
+ GimpHistogramChannel channel;
+ GimpHistogramChannel old_channel;
+ gboolean success = TRUE;
+
+ if (! gimp_operation_settings_config_serialize_base (config, writer, data) ||
+ ! gimp_config_serialize_property_by_name (config, "linear", writer) ||
+ ! gimp_config_serialize_property_by_name (config, "clamp-input", writer) ||
+ ! gimp_config_serialize_property_by_name (config, "clamp-output", writer))
+ return FALSE;
+
+ old_channel = l_config->channel;
+
+ for (channel = GIMP_HISTOGRAM_VALUE;
+ channel <= GIMP_HISTOGRAM_ALPHA;
+ channel++)
+ {
+ l_config->channel = channel;
+
+ /* serialize the channel properties manually (not using
+ * gimp_config_serialize_properties()), so the parent class'
+ * properties don't end up in the config file one per channel.
+ * See bug #700653.
+ */
+ success =
+ (gimp_config_serialize_property_by_name (config, "channel", writer) &&
+ gimp_config_serialize_property_by_name (config, "low-input", writer) &&
+ gimp_config_serialize_property_by_name (config, "high-input", writer) &&
+ gimp_config_serialize_property_by_name (config, "gamma", writer) &&
+ gimp_config_serialize_property_by_name (config, "low-output", writer) &&
+ gimp_config_serialize_property_by_name (config, "high-output", writer));
+
+ if (! success)
+ break;
+ }
+
+ l_config->channel = old_channel;
+
+ return success;
+}
+
+static gboolean
+gimp_levels_config_deserialize (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data)
+{
+ GimpLevelsConfig *l_config = GIMP_LEVELS_CONFIG (config);
+ GimpHistogramChannel old_channel;
+ gboolean success = TRUE;
+
+ old_channel = l_config->channel;
+
+ success = gimp_config_deserialize_properties (config, scanner, nest_level);
+
+ g_object_set (config, "channel", old_channel, NULL);
+
+ return success;
+}
+
+static gboolean
+gimp_levels_config_equal (GimpConfig *a,
+ GimpConfig *b)
+{
+ GimpLevelsConfig *config_a = GIMP_LEVELS_CONFIG (a);
+ GimpLevelsConfig *config_b = GIMP_LEVELS_CONFIG (b);
+ GimpHistogramChannel channel;
+
+ if (! gimp_operation_settings_config_equal_base (a, b) ||
+ config_a->linear != config_b->linear ||
+ config_a->clamp_input != config_b->clamp_input ||
+ config_a->clamp_output != config_b->clamp_output)
+ return FALSE;
+
+ for (channel = GIMP_HISTOGRAM_VALUE;
+ channel <= GIMP_HISTOGRAM_ALPHA;
+ channel++)
+ {
+ if (config_a->gamma[channel] != config_b->gamma[channel] ||
+ config_a->low_input[channel] != config_b->low_input[channel] ||
+ config_a->high_input[channel] != config_b->high_input[channel] ||
+ config_a->low_output[channel] != config_b->low_output[channel] ||
+ config_a->high_output[channel] != config_b->high_output[channel])
+ return FALSE;
+ }
+
+ /* don't compare "channel" */
+
+ return TRUE;
+}
+
+static void
+gimp_levels_config_reset (GimpConfig *config)
+{
+ GimpLevelsConfig *l_config = GIMP_LEVELS_CONFIG (config);
+ GimpHistogramChannel channel;
+
+ gimp_operation_settings_config_reset_base (config);
+
+ for (channel = GIMP_HISTOGRAM_VALUE;
+ channel <= GIMP_HISTOGRAM_ALPHA;
+ channel++)
+ {
+ l_config->channel = channel;
+ gimp_levels_config_reset_channel (l_config);
+ }
+
+ gimp_config_reset_property (G_OBJECT (config), "linear");
+ gimp_config_reset_property (G_OBJECT (config), "channel");
+ gimp_config_reset_property (G_OBJECT (config), "clamp-input");
+ gimp_config_reset_property (G_OBJECT (config), "clamp_output");
+}
+
+static gboolean
+gimp_levels_config_copy (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags)
+{
+ GimpLevelsConfig *src_config = GIMP_LEVELS_CONFIG (src);
+ GimpLevelsConfig *dest_config = GIMP_LEVELS_CONFIG (dest);
+ GimpHistogramChannel channel;
+
+ if (! gimp_operation_settings_config_copy_base (src, dest, flags))
+ return FALSE;
+
+ for (channel = GIMP_HISTOGRAM_VALUE;
+ channel <= GIMP_HISTOGRAM_ALPHA;
+ channel++)
+ {
+ dest_config->gamma[channel] = src_config->gamma[channel];
+ dest_config->low_input[channel] = src_config->low_input[channel];
+ dest_config->high_input[channel] = src_config->high_input[channel];
+ dest_config->low_output[channel] = src_config->low_output[channel];
+ dest_config->high_output[channel] = src_config->high_output[channel];
+ }
+
+ g_object_notify (G_OBJECT (dest), "gamma");
+ g_object_notify (G_OBJECT (dest), "low-input");
+ g_object_notify (G_OBJECT (dest), "high-input");
+ g_object_notify (G_OBJECT (dest), "low-output");
+ g_object_notify (G_OBJECT (dest), "high-output");
+
+ dest_config->linear = src_config->linear;
+ dest_config->channel = src_config->channel;
+ dest_config->clamp_input = src_config->clamp_input;
+ dest_config->clamp_output = src_config->clamp_output;
+
+ g_object_notify (G_OBJECT (dest), "linear");
+ g_object_notify (G_OBJECT (dest), "channel");
+ g_object_notify (G_OBJECT (dest), "clamp-input");
+ g_object_notify (G_OBJECT (dest), "clamp-output");
+
+ return TRUE;
+}
+
+
+/* public functions */
+
+void
+gimp_levels_config_reset_channel (GimpLevelsConfig *config)
+{
+ g_return_if_fail (GIMP_IS_LEVELS_CONFIG (config));
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ gimp_config_reset_property (G_OBJECT (config), "gamma");
+ gimp_config_reset_property (G_OBJECT (config), "low-input");
+ gimp_config_reset_property (G_OBJECT (config), "high-input");
+ gimp_config_reset_property (G_OBJECT (config), "low-output");
+ gimp_config_reset_property (G_OBJECT (config), "high-output");
+
+ g_object_thaw_notify (G_OBJECT (config));
+}
+
+void
+gimp_levels_config_stretch (GimpLevelsConfig *config,
+ GimpHistogram *histogram,
+ gboolean is_color)
+{
+ g_return_if_fail (GIMP_IS_LEVELS_CONFIG (config));
+ g_return_if_fail (histogram != NULL);
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ if (is_color)
+ {
+ GimpHistogramChannel channel;
+
+ /* Set the overall value to defaults */
+ channel = config->channel;
+ config->channel = GIMP_HISTOGRAM_VALUE;
+ gimp_levels_config_reset_channel (config);
+ config->channel = channel;
+
+ for (channel = GIMP_HISTOGRAM_RED;
+ channel <= GIMP_HISTOGRAM_BLUE;
+ channel++)
+ {
+ gimp_levels_config_stretch_channel (config, histogram, channel);
+ }
+ }
+ else
+ {
+ gimp_levels_config_stretch_channel (config, histogram,
+ GIMP_HISTOGRAM_VALUE);
+ }
+
+ g_object_thaw_notify (G_OBJECT (config));
+}
+
+void
+gimp_levels_config_stretch_channel (GimpLevelsConfig *config,
+ GimpHistogram *histogram,
+ GimpHistogramChannel channel)
+{
+ gdouble count;
+ gdouble bias = 0.006;
+ gint n_bins;
+ gint i;
+
+ g_return_if_fail (GIMP_IS_LEVELS_CONFIG (config));
+ g_return_if_fail (histogram != NULL);
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ config->gamma[channel] = 1.0;
+ config->low_output[channel] = 0.0;
+ config->high_output[channel] = 1.0;
+
+ n_bins = gimp_histogram_n_bins (histogram);
+ count = gimp_histogram_get_count (histogram, channel, 0, n_bins - 1);
+
+ if (count == 0.0)
+ {
+ config->low_input[channel] = 0.0;
+ config->high_input[channel] = 0.0;
+ }
+ else
+ {
+ gdouble new_count;
+ gdouble percentage;
+ gdouble next_percentage;
+
+ /* Set the low input */
+ new_count = 0.0;
+
+ for (i = 0; i < (n_bins - 1); i++)
+ {
+ new_count += gimp_histogram_get_value (histogram, channel, i);
+ percentage = new_count / count;
+ next_percentage = (new_count +
+ gimp_histogram_get_value (histogram,
+ channel,
+ i + 1)) / count;
+
+ if (fabs (percentage - bias) < fabs (next_percentage - bias))
+ {
+ config->low_input[channel] = (gdouble) (i + 1) / (n_bins - 1);
+ break;
+ }
+ }
+
+ /* Set the high input */
+ new_count = 0.0;
+
+ for (i = (n_bins - 1); i > 0; i--)
+ {
+ new_count += gimp_histogram_get_value (histogram, channel, i);
+ percentage = new_count / count;
+ next_percentage = (new_count +
+ gimp_histogram_get_value (histogram,
+ channel,
+ i - 1)) / count;
+
+ if (fabs (percentage - bias) < fabs (next_percentage - bias))
+ {
+ config->high_input[channel] = (gdouble) (i - 1) / (n_bins - 1);
+ break;
+ }
+ }
+ }
+
+ g_object_notify (G_OBJECT (config), "gamma");
+ g_object_notify (G_OBJECT (config), "low-input");
+ g_object_notify (G_OBJECT (config), "high-input");
+ g_object_notify (G_OBJECT (config), "low-output");
+ g_object_notify (G_OBJECT (config), "high-output");
+
+ g_object_thaw_notify (G_OBJECT (config));
+}
+
+static gdouble
+gimp_levels_config_input_from_color (GimpHistogramChannel channel,
+ const GimpRGB *color)
+{
+ switch (channel)
+ {
+ case GIMP_HISTOGRAM_VALUE:
+ return MAX (MAX (color->r, color->g), color->b);
+
+ case GIMP_HISTOGRAM_RED:
+ return color->r;
+
+ case GIMP_HISTOGRAM_GREEN:
+ return color->g;
+
+ case GIMP_HISTOGRAM_BLUE:
+ return color->b;
+
+ case GIMP_HISTOGRAM_ALPHA:
+ return color->a;
+
+ case GIMP_HISTOGRAM_RGB:
+ return MIN (MIN (color->r, color->g), color->b);
+
+ case GIMP_HISTOGRAM_LUMINANCE:
+ return GIMP_RGB_LUMINANCE (color->r, color->g, color->b);
+ }
+
+ return 0.0;
+}
+
+void
+gimp_levels_config_adjust_by_colors (GimpLevelsConfig *config,
+ GimpHistogramChannel channel,
+ const GimpRGB *black,
+ const GimpRGB *gray,
+ const GimpRGB *white)
+{
+ g_return_if_fail (GIMP_IS_LEVELS_CONFIG (config));
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ if (black)
+ {
+ config->low_input[channel] = gimp_levels_config_input_from_color (channel,
+ black);
+ g_object_notify (G_OBJECT (config), "low-input");
+ }
+
+
+ if (white)
+ {
+ config->high_input[channel] = gimp_levels_config_input_from_color (channel,
+ white);
+ g_object_notify (G_OBJECT (config), "high-input");
+ }
+
+ if (gray)
+ {
+ gdouble input;
+ gdouble range;
+ gdouble inten;
+ gdouble out_light;
+ gdouble lightness;
+
+ /* Calculate lightness value */
+ lightness = GIMP_RGB_LUMINANCE (gray->r, gray->g, gray->b);
+
+ input = gimp_levels_config_input_from_color (channel, gray);
+
+ range = config->high_input[channel] - config->low_input[channel];
+ if (range <= 0)
+ goto out;
+
+ input -= config->low_input[channel];
+ if (input < 0)
+ goto out;
+
+ /* Normalize input and lightness */
+ inten = input / range;
+ out_light = lightness / range;
+
+ /* See bug 622054: picking pure black or white as gamma doesn't
+ * work. But we cannot compare to 0.0 or 1.0 because cpus and
+ * compilers are shit. If you try to check out_light using
+ * printf() it will give exact 0.0 or 1.0 anyway, probably
+ * because the generated code is different and out_light doesn't
+ * live in a register. That must be why the cpu/compiler mafia
+ * invented epsilon and defined this shit to be the programmer's
+ * responsibility.
+ */
+ if (out_light <= 0.0001 || out_light >= 0.9999)
+ goto out;
+
+ /* Map selected color to corresponding lightness */
+ config->gamma[channel] = log (inten) / log (out_light);
+ config->gamma[channel] = CLAMP (config->gamma[channel], 0.1, 10.0);
+ g_object_notify (G_OBJECT (config), "gamma");
+ }
+
+ out:
+ g_object_thaw_notify (G_OBJECT (config));
+}
+
+GimpCurvesConfig *
+gimp_levels_config_to_curves_config (GimpLevelsConfig *config)
+{
+ GimpCurvesConfig *curves;
+ GimpHistogramChannel channel;
+
+ g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), NULL);
+
+ curves = g_object_new (GIMP_TYPE_CURVES_CONFIG, NULL);
+
+ gimp_operation_settings_config_copy_base (GIMP_CONFIG (config),
+ GIMP_CONFIG (curves),
+ 0);
+
+ curves->linear = config->linear;
+
+ for (channel = GIMP_HISTOGRAM_VALUE;
+ channel <= GIMP_HISTOGRAM_ALPHA;
+ channel++)
+ {
+ GimpCurve *curve = curves->curve[channel];
+ static const gint n = 8;
+ gdouble gamma = config->gamma[channel];
+ gdouble delta_in;
+ gdouble delta_out;
+ gdouble x, y;
+
+ /* clear the points set by default */
+ gimp_curve_clear_points (curve);
+
+ delta_in = config->high_input[channel] - config->low_input[channel];
+ delta_out = config->high_output[channel] - config->low_output[channel];
+
+ x = config->low_input[channel];
+ y = config->low_output[channel];
+
+ gimp_curve_add_point (curve, x, y);
+
+ if (delta_out != 0 && gamma != 1.0)
+ {
+ /* The Levels tool performs gamma adjustment, which is a
+ * power law, while the Curves tool uses cubic Bézier
+ * curves. Here we try to approximate this gamma adjustment
+ * with a Bézier curve with 5 control points. Two of them
+ * must be (low_input, low_output) and (high_input,
+ * high_output), so we need to add 3 more control points in
+ * the middle.
+ */
+ gint i;
+
+ if (gamma > 1)
+ {
+ /* Case no. 1: γ > 1
+ *
+ * The curve should look like a horizontal
+ * parabola. Since its curvature is greatest when x is
+ * small, we add more control points there, so the
+ * approximation is more accurate. I decided to set the
+ * length of the consecutive segments to x₀, γ⋅x₀, γ²⋅x₀
+ * and γ³⋅x₀ and I saw that the curves looked
+ * good. Still, this is completely arbitrary.
+ */
+ gdouble dx = 0;
+ gdouble x0;
+
+ for (i = 0; i < n; ++i)
+ dx = dx * gamma + 1;
+ x0 = delta_in / dx;
+
+ dx = 0;
+ for (i = 1; i < n; ++i)
+ {
+ dx = dx * gamma + x0;
+ x = config->low_input[channel] + dx;
+ y = config->low_output[channel] + delta_out *
+ gimp_operation_levels_map_input (config, channel, x);
+ gimp_curve_add_point (curve, x, y);
+ }
+ }
+ else
+ {
+ /* Case no. 2: γ < 1
+ *
+ * The curve is the same as the one in case no. 1,
+ * observed through a reflexion along the y = x axis. So
+ * if we invert γ and swap the x and y axes we can use
+ * the same method as in case no. 1.
+ */
+ GimpLevelsConfig *config_inv;
+ gdouble dy = 0;
+ gdouble y0;
+ const gdouble gamma_inv = 1 / gamma;
+
+ config_inv = gimp_config_duplicate (GIMP_CONFIG (config));
+
+ config_inv->gamma[channel] = gamma_inv;
+ config_inv->low_input[channel] = config->low_output[channel];
+ config_inv->low_output[channel] = config->low_input[channel];
+ config_inv->high_input[channel] = config->high_output[channel];
+ config_inv->high_output[channel] = config->high_input[channel];
+
+ for (i = 0; i < n; ++i)
+ dy = dy * gamma_inv + 1;
+ y0 = delta_out / dy;
+
+ dy = 0;
+ for (i = 1; i < n; ++i)
+ {
+ dy = dy * gamma_inv + y0;
+ y = config->low_output[channel] + dy;
+ x = config->low_input[channel] + delta_in *
+ gimp_operation_levels_map_input (config_inv, channel, y);
+ gimp_curve_add_point (curve, x, y);
+ }
+
+ g_object_unref (config_inv);
+ }
+ }
+
+ x = config->high_input[channel];
+ y = config->high_output[channel];
+
+ gimp_curve_add_point (curve, x, y);
+ }
+
+ return curves;
+}
+
+gboolean
+gimp_levels_config_load_cruft (GimpLevelsConfig *config,
+ GInputStream *input,
+ GError **error)
+{
+ GDataInputStream *data_input;
+ gint low_input[5];
+ gint high_input[5];
+ gint low_output[5];
+ gint high_output[5];
+ gdouble gamma[5];
+ gchar *line;
+ gsize line_len;
+ gint i;
+
+ g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), FALSE);
+ g_return_val_if_fail (G_IS_INPUT_STREAM (input), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ data_input = g_data_input_stream_new (input);
+
+ line_len = 64;
+ line = gimp_data_input_stream_read_line_always (data_input, &line_len,
+ NULL, error);
+ if (! line)
+ return FALSE;
+
+ if (strcmp (line, "# GIMP Levels File") != 0)
+ {
+ g_set_error_literal (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
+ _("not a GIMP Levels file"));
+ g_object_unref (data_input);
+ g_free (line);
+ return FALSE;
+ }
+
+ g_free (line);
+
+ for (i = 0; i < 5; i++)
+ {
+ gchar float_buf[32];
+ gchar *endp;
+ gint fields;
+
+ line_len = 64;
+ line = gimp_data_input_stream_read_line_always (data_input, &line_len,
+ NULL, error);
+ if (! line)
+ {
+ g_object_unref (data_input);
+ return FALSE;
+ }
+
+ fields = sscanf (line, "%d %d %d %d %31s",
+ &low_input[i],
+ &high_input[i],
+ &low_output[i],
+ &high_output[i],
+ float_buf);
+
+ g_free (line);
+
+ if (fields != 5)
+ goto error;
+
+ gamma[i] = g_ascii_strtod (float_buf, &endp);
+
+ if (endp == float_buf || errno == ERANGE)
+ goto error;
+ }
+
+ g_object_unref (data_input);
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ for (i = 0; i < 5; i++)
+ {
+ config->low_input[i] = low_input[i] / 255.0;
+ config->high_input[i] = high_input[i] / 255.0;
+ config->gamma[i] = gamma[i];
+ config->low_output[i] = low_output[i] / 255.0;
+ config->high_output[i] = high_output[i] / 255.0;
+ }
+
+ config->linear = FALSE;
+ config->clamp_input = TRUE;
+ config->clamp_output = TRUE;
+
+ g_object_notify (G_OBJECT (config), "linear");
+ g_object_notify (G_OBJECT (config), "low-input");
+ g_object_notify (G_OBJECT (config), "high-input");
+ g_object_notify (G_OBJECT (config), "clamp-input");
+ g_object_notify (G_OBJECT (config), "gamma");
+ g_object_notify (G_OBJECT (config), "low-output");
+ g_object_notify (G_OBJECT (config), "high-output");
+ g_object_notify (G_OBJECT (config), "clamp-output");
+
+ g_object_thaw_notify (G_OBJECT (config));
+
+ return TRUE;
+
+ error:
+ g_object_unref (data_input);
+
+ g_set_error_literal (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
+ _("parse error"));
+ return FALSE;
+}
+
+gboolean
+gimp_levels_config_save_cruft (GimpLevelsConfig *config,
+ GOutputStream *output,
+ GError **error)
+{
+ GString *string;
+ gint i;
+
+ g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), FALSE);
+ g_return_val_if_fail (G_IS_OUTPUT_STREAM (output), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ string = g_string_new ("# GIMP Levels File\n");
+
+ for (i = 0; i < 5; i++)
+ {
+ gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+ g_string_append_printf (string,
+ "%d %d %d %d %s\n",
+ (gint) (config->low_input[i] * 255.999),
+ (gint) (config->high_input[i] * 255.999),
+ (gint) (config->low_output[i] * 255.999),
+ (gint) (config->high_output[i] * 255.999),
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE,
+ config->gamma[i]));
+ }
+
+ if (! g_output_stream_write_all (output, string->str, string->len,
+ NULL, NULL, error))
+ {
+ g_prefix_error (error, _("Writing levels file failed: "));
+ g_string_free (string, TRUE);
+ return FALSE;
+ }
+
+ g_string_free (string, TRUE);
+
+ return TRUE;
+}
diff --git a/app/operations/gimplevelsconfig.h b/app/operations/gimplevelsconfig.h
new file mode 100644
index 0000000..2e7569c
--- /dev/null
+++ b/app/operations/gimplevelsconfig.h
@@ -0,0 +1,92 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimplevelsconfig.h
+ * Copyright (C) 2007 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/>.
+ */
+
+#ifndef __GIMP_LEVELS_CONFIG_H__
+#define __GIMP_LEVELS_CONFIG_H__
+
+
+#include "gimpoperationsettings.h"
+
+
+#define GIMP_TYPE_LEVELS_CONFIG (gimp_levels_config_get_type ())
+#define GIMP_LEVELS_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LEVELS_CONFIG, GimpLevelsConfig))
+#define GIMP_LEVELS_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_LEVELS_CONFIG, GimpLevelsConfigClass))
+#define GIMP_IS_LEVELS_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LEVELS_CONFIG))
+#define GIMP_IS_LEVELS_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_LEVELS_CONFIG))
+#define GIMP_LEVELS_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_LEVELS_CONFIG, GimpLevelsConfigClass))
+
+
+typedef struct _GimpLevelsConfigClass GimpLevelsConfigClass;
+
+struct _GimpLevelsConfig
+{
+ GimpOperationSettings parent_instance;
+
+ gboolean linear;
+
+ GimpHistogramChannel channel;
+
+ gdouble low_input[5];
+ gdouble high_input[5];
+
+ gboolean clamp_input;
+
+ gdouble gamma[5];
+
+ gdouble low_output[5];
+ gdouble high_output[5];
+
+ gboolean clamp_output;
+};
+
+struct _GimpLevelsConfigClass
+{
+ GimpOperationSettingsClass parent_class;
+};
+
+
+GType gimp_levels_config_get_type (void) G_GNUC_CONST;
+
+void gimp_levels_config_reset_channel (GimpLevelsConfig *config);
+
+void gimp_levels_config_stretch (GimpLevelsConfig *config,
+ GimpHistogram *histogram,
+ gboolean is_color);
+void gimp_levels_config_stretch_channel (GimpLevelsConfig *config,
+ GimpHistogram *histogram,
+ GimpHistogramChannel channel);
+void gimp_levels_config_adjust_by_colors (GimpLevelsConfig *config,
+ GimpHistogramChannel channel,
+ const GimpRGB *black,
+ const GimpRGB *gray,
+ const GimpRGB *white);
+
+GimpCurvesConfig *
+ gimp_levels_config_to_curves_config (GimpLevelsConfig *config);
+
+gboolean gimp_levels_config_load_cruft (GimpLevelsConfig *config,
+ GInputStream *input,
+ GError **error);
+gboolean gimp_levels_config_save_cruft (GimpLevelsConfig *config,
+ GOutputStream *output,
+ GError **error);
+
+
+#endif /* __GIMP_LEVELS_CONFIG_H__ */
diff --git a/app/operations/gimpoperationborder.c b/app/operations/gimpoperationborder.c
new file mode 100644
index 0000000..e08481c
--- /dev/null
+++ b/app/operations/gimpoperationborder.c
@@ -0,0 +1,748 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationborder.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 <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "operations-types.h"
+
+#include "gimpoperationborder.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_RADIUS_X,
+ PROP_RADIUS_Y,
+ PROP_FEATHER,
+ PROP_EDGE_LOCK
+};
+
+
+static void gimp_operation_border_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_border_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static GeglRectangle
+gimp_operation_border_get_required_for_output (GeglOperation *self,
+ const gchar *input_pad,
+ const GeglRectangle *roi);
+static GeglRectangle
+ gimp_operation_border_get_cached_region (GeglOperation *self,
+ const GeglRectangle *roi);
+static void gimp_operation_border_prepare (GeglOperation *operation);
+static gboolean gimp_operation_border_process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationBorder, gimp_operation_border,
+ GEGL_TYPE_OPERATION_FILTER)
+
+#define parent_class gimp_operation_border_parent_class
+
+
+static void
+gimp_operation_border_class_init (GimpOperationBorderClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
+
+ object_class->set_property = gimp_operation_border_set_property;
+ object_class->get_property = gimp_operation_border_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:border",
+ "categories", "gimp",
+ "description", "GIMP Border operation",
+ NULL);
+
+ operation_class->prepare = gimp_operation_border_prepare;
+ operation_class->get_required_for_output = gimp_operation_border_get_required_for_output;
+ operation_class->get_cached_region = gimp_operation_border_get_cached_region;
+ operation_class->threaded = FALSE;
+
+ filter_class->process = gimp_operation_border_process;
+
+ g_object_class_install_property (object_class, PROP_RADIUS_X,
+ g_param_spec_int ("radius-x",
+ "Radius X",
+ "Border radius in X diection",
+ 1, 2342, 1,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_RADIUS_Y,
+ g_param_spec_int ("radius-y",
+ "Radius Y",
+ "Border radius in Y diection",
+ 1, 2342, 1,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_FEATHER,
+ g_param_spec_boolean ("feather",
+ "Feather",
+ "Feather the border",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_EDGE_LOCK,
+ g_param_spec_boolean ("edge-lock",
+ "Edge Lock",
+ "Shrink from border",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_border_init (GimpOperationBorder *self)
+{
+}
+
+static void
+gimp_operation_border_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationBorder *self = GIMP_OPERATION_BORDER (object);
+
+ switch (property_id)
+ {
+ case PROP_RADIUS_X:
+ g_value_set_int (value, self->radius_x);
+ break;
+
+ case PROP_RADIUS_Y:
+ g_value_set_int (value, self->radius_y);
+ break;
+
+ case PROP_FEATHER:
+ g_value_set_boolean (value, self->feather);
+ break;
+
+ case PROP_EDGE_LOCK:
+ g_value_set_boolean (value, self->edge_lock);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_border_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationBorder *self = GIMP_OPERATION_BORDER (object);
+
+ switch (property_id)
+ {
+ case PROP_RADIUS_X:
+ self->radius_x = g_value_get_int (value);
+ break;
+
+ case PROP_RADIUS_Y:
+ self->radius_y = g_value_get_int (value);
+ break;
+
+ case PROP_FEATHER:
+ self->feather = g_value_get_boolean (value);
+ break;
+
+ case PROP_EDGE_LOCK:
+ self->edge_lock = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_border_prepare (GeglOperation *operation)
+{
+ const Babl *space = gegl_operation_get_source_space (operation, "input");
+ gegl_operation_set_format (operation, "input", babl_format_with_space ("Y float", space));
+ gegl_operation_set_format (operation, "output", babl_format_with_space ("Y float", space));
+}
+
+static GeglRectangle
+gimp_operation_border_get_required_for_output (GeglOperation *self,
+ const gchar *input_pad,
+ const GeglRectangle *roi)
+{
+ return *gegl_operation_source_get_bounding_box (self, "input");
+}
+
+static GeglRectangle
+gimp_operation_border_get_cached_region (GeglOperation *self,
+ const GeglRectangle *roi)
+{
+ return *gegl_operation_source_get_bounding_box (self, "input");
+}
+
+static inline void
+rotate_pointers (gfloat **p,
+ guint32 n)
+{
+ guint32 i;
+ gfloat *tmp;
+
+ tmp = p[0];
+
+ for (i = 0; i < n - 1; i++)
+ p[i] = p[i + 1];
+
+ p[i] = tmp;
+}
+
+/* Computes whether pixels in `buf[1]', if they are selected, have neighbouring
+ pixels that are unselected. Put result in `transition'. */
+static void
+compute_transition (gfloat *transition,
+ gfloat **buf,
+ gint32 width,
+ gboolean edge_lock)
+{
+ register gint32 x = 0;
+
+ if (width == 1)
+ {
+ if (buf[1][0] >= 0.5 && (buf[0][0] < 0.5 || buf[2][0] < 0.5))
+ transition[0] = 1.0;
+ else
+ transition[0] = 0.0;
+ return;
+ }
+
+ if (buf[1][0] >= 0.5 && edge_lock)
+ {
+ /* The pixel to the left (outside of the canvas) is considered selected,
+ so we check if there are any unselected pixels in neighbouring pixels
+ _on_ the canvas. */
+ if (buf[0][x] < 0.5 || buf[0][x + 1] < 0.5 ||
+ buf[1][x + 1] < 0.5 ||
+ buf[2][x] < 0.5 || buf[2][x + 1] < 0.5 )
+ {
+ transition[x] = 1.0;
+ }
+ else
+ {
+ transition[x] = 0.0;
+ }
+ }
+ else if (buf[1][0] >= 0.5 && !edge_lock)
+ {
+ /* We must not care about neighbouring pixels on the image canvas since
+ there always are unselected pixels to the left (which is outside of
+ the image canvas). */
+ transition[x] = 1.0;
+ }
+ else
+ {
+ transition[x] = 0.0;
+ }
+
+ for (x = 1; x < width - 1; x++)
+ {
+ if (buf[1][x] >= 0.5)
+ {
+ if (buf[0][x - 1] < 0.5 || buf[0][x] < 0.5 || buf[0][x + 1] < 0.5 ||
+ buf[1][x - 1] < 0.5 || buf[1][x + 1] < 0.5 ||
+ buf[2][x - 1] < 0.5 || buf[2][x] < 0.5 || buf[2][x + 1] < 0.5)
+ transition[x] = 1.0;
+ else
+ transition[x] = 0.0;
+ }
+ else
+ {
+ transition[x] = 0.0;
+ }
+ }
+
+ if (buf[1][width - 1] >= 0.5 && edge_lock)
+ {
+ /* The pixel to the right (outside of the canvas) is considered selected,
+ so we check if there are any unselected pixels in neighbouring pixels
+ _on_ the canvas. */
+ if ( buf[0][x - 1] < 0.5 || buf[0][x] < 0.5 ||
+ buf[1][x - 1] < 0.5 ||
+ buf[2][x - 1] < 0.5 || buf[2][x] < 0.5)
+ {
+ transition[width - 1] = 1.0;
+ }
+ else
+ {
+ transition[width - 1] = 0.0;
+ }
+ }
+ else if (buf[1][width - 1] >= 0.5 && !edge_lock)
+ {
+ /* We must not care about neighbouring pixels on the image canvas since
+ there always are unselected pixels to the right (which is outside of
+ the image canvas). */
+ transition[width - 1] = 1.0;
+ }
+ else
+ {
+ transition[width - 1] = 0.0;
+ }
+}
+
+static gboolean
+gimp_operation_border_process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *roi,
+ gint level)
+{
+ /* This function has no bugs, but if you imagine some you can blame
+ * them on jaycox@gimp.org
+ */
+ GimpOperationBorder *self = GIMP_OPERATION_BORDER (operation);
+ const Babl *input_format = gegl_operation_get_format (operation, "input");
+ const Babl *output_format = gegl_operation_get_format (operation, "output");
+
+ gint32 i, j, x, y;
+
+ /* A cache used in the algorithm as it works its way down. `buf[1]' is the
+ current row. Thus, at algorithm initialization, `buf[0]' represents the
+ row 'above' the first row of the region. */
+ gfloat *buf[3];
+
+ /* The resulting selection is calculated row by row, and this buffer holds the
+ output for each individual row, on each iteration. */
+ gfloat *out;
+
+ /* Keeps track of transitional pixels (pixels that are selected and have
+ unselected neighbouring pixels). */
+ gfloat **transition;
+
+ /* TODO: Figure out role clearly in algorithm. */
+ gint16 *max;
+
+ /* TODO: Figure out role clearly in algorithm. */
+ gfloat **density;
+
+ gint16 last_index;
+
+ /* optimize this case specifically */
+ if (self->radius_x == 1 && self->radius_y == 1)
+ {
+ gfloat *transition;
+ gfloat *source[3];
+
+ for (i = 0; i < 3; i++)
+ source[i] = g_new (gfloat, roi->width);
+
+ transition = g_new (gfloat, roi->width);
+
+ /* With `self->edge_lock', initialize row above image as
+ * selected, otherwise, initialize as unselected.
+ */
+ if (self->edge_lock)
+ {
+ for (i = 0; i < roi->width; i++)
+ source[0][i] = 1.0;
+ }
+ else
+ {
+ memset (source[0], 0, roi->width * sizeof (gfloat));
+ }
+
+ gegl_buffer_get (input,
+ GEGL_RECTANGLE (roi->x, roi->y + 0,
+ roi->width, 1),
+ 1.0, input_format, source[1],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ if (roi->height > 1)
+ gegl_buffer_get (input,
+ GEGL_RECTANGLE (roi->x, roi->y + 1,
+ roi->width, 1),
+ 1.0, input_format, source[2],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ else
+ memcpy (source[2], source[1], roi->width * sizeof (gfloat));
+
+ compute_transition (transition, source, roi->width, self->edge_lock);
+ gegl_buffer_set (output,
+ GEGL_RECTANGLE (roi->x, roi->y,
+ roi->width, 1),
+ 0, output_format, transition,
+ GEGL_AUTO_ROWSTRIDE);
+
+ for (y = 1; y < roi->height; y++)
+ {
+ rotate_pointers (source, 3);
+
+ if (y + 1 < roi->height)
+ {
+ gegl_buffer_get (input,
+ GEGL_RECTANGLE (roi->x, roi->y + y + 1,
+ roi->width, 1),
+ 1.0, input_format, source[2],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+ else
+ {
+ /* Depending on `self->edge_lock', set the row below the
+ * image as either selected or non-selected.
+ */
+ if (self->edge_lock)
+ {
+ for (i = 0; i < roi->width; i++)
+ source[2][i] = 1.0;
+ }
+ else
+ {
+ memset (source[2], 0, roi->width * sizeof (gfloat));
+ }
+ }
+
+ compute_transition (transition, source, roi->width, self->edge_lock);
+ gegl_buffer_set (output,
+ GEGL_RECTANGLE (roi->x, roi->y + y,
+ roi->width, 1),
+ 0, output_format, transition,
+ GEGL_AUTO_ROWSTRIDE);
+ }
+
+ for (i = 0; i < 3; i++)
+ g_free (source[i]);
+
+ g_free (transition);
+
+ /* Finished handling the radius = 1 special case, return here. */
+ return TRUE;
+ }
+
+ max = g_new (gint16, roi->width + 2 * self->radius_x);
+
+ for (i = 0; i < (roi->width + 2 * self->radius_x); i++)
+ max[i] = self->radius_y + 2;
+
+ max += self->radius_x;
+
+ for (i = 0; i < 3; i++)
+ buf[i] = g_new (gfloat, roi->width);
+
+ transition = g_new (gfloat *, self->radius_y + 1);
+
+ for (i = 0; i < self->radius_y + 1; i++)
+ {
+ transition[i] = g_new (gfloat, roi->width + 2 * self->radius_x);
+ memset (transition[i], 0,
+ (roi->width + 2 * self->radius_x) * sizeof (gfloat));
+ transition[i] += self->radius_x;
+ }
+
+ out = g_new (gfloat, roi->width);
+
+ density = g_new (gfloat *, 2 * self->radius_x + 1);
+ density += self->radius_x;
+
+ /* allocate density[][] */
+ for (x = 0; x < (self->radius_x + 1); x++)
+ {
+ density[ x] = g_new (gfloat, 2 * self->radius_y + 1);
+ density[ x] += self->radius_y;
+ density[-x] = density[x];
+ }
+
+ /* compute density[][] */
+ for (x = 0; x < (self->radius_x + 1); x++)
+ {
+ gdouble tmpx, tmpy, dist;
+ gfloat a;
+
+ if (x > 0)
+ tmpx = x - 0.5;
+ else if (x < 0)
+ tmpx = x + 0.5;
+ else
+ tmpx = 0.0;
+
+ for (y = 0; y < (self->radius_y + 1); y++)
+ {
+ if (y > 0)
+ tmpy = y - 0.5;
+ else if (y < 0)
+ tmpy = y + 0.5;
+ else
+ tmpy = 0.0;
+
+ dist = ((tmpy * tmpy) / (self->radius_y * self->radius_y) +
+ (tmpx * tmpx) / (self->radius_x * self->radius_x));
+
+ if (dist < 1.0)
+ {
+ if (self->feather)
+ a = 1.0 - sqrt (dist);
+ else
+ a = 1.0;
+ }
+ else
+ {
+ a = 0.0;
+ }
+
+ density[ x][ y] = a;
+ density[ x][-y] = a;
+ density[-x][ y] = a;
+ density[-x][-y] = a;
+ }
+ }
+
+ /* Since the algorithm considerers `buf[0]' to be 'over' the row
+ * currently calculated, we must start with `buf[0]' as non-selected
+ * if there is no `self->edge_lock. If there is an
+ * 'self->edge_lock', initialize the first row to 'selected'. Refer
+ * to bug #350009.
+ */
+ if (self->edge_lock)
+ {
+ for (i = 0; i < roi->width; i++)
+ buf[0][i] = 1.0;
+ }
+ else
+ {
+ memset (buf[0], 0, roi->width * sizeof (gfloat));
+ }
+
+ gegl_buffer_get (input,
+ GEGL_RECTANGLE (roi->x, roi->y + 0,
+ roi->width, 1),
+ 1.0, input_format, buf[1],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ if (roi->height > 1)
+ gegl_buffer_get (input,
+ GEGL_RECTANGLE (roi->x, roi->y + 1,
+ roi->width, 1),
+ 1.0, input_format, buf[2],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ else
+ memcpy (buf[2], buf[1], roi->width * sizeof (gfloat));
+
+ compute_transition (transition[1], buf, roi->width, self->edge_lock);
+
+ /* set up top of image */
+ for (y = 1; y < self->radius_y && y + 1 < roi->height; y++)
+ {
+ rotate_pointers (buf, 3);
+ gegl_buffer_get (input,
+ GEGL_RECTANGLE (roi->x, roi->y + y + 1,
+ roi->width, 1),
+ 1.0, input_format, buf[2],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ compute_transition (transition[y + 1], buf, roi->width, self->edge_lock);
+ }
+
+ /* set up max[] for top of image */
+ for (x = 0; x < roi->width; x++)
+ {
+ max[x] = -(self->radius_y + 7);
+
+ for (j = 1; j < self->radius_y + 1; j++)
+ if (transition[j][x])
+ {
+ max[x] = j;
+ break;
+ }
+ }
+
+ /* main calculation loop */
+ for (y = 0; y < roi->height; y++)
+ {
+ rotate_pointers (buf, 3);
+ rotate_pointers (transition, self->radius_y + 1);
+
+ if (y < roi->height - (self->radius_y + 1))
+ {
+ gegl_buffer_get (input,
+ GEGL_RECTANGLE (roi->x,
+ roi->y + y + self->radius_y + 1,
+ roi->width, 1),
+ 1.0, input_format, buf[2],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ compute_transition (transition[self->radius_y], buf, roi->width, self->edge_lock);
+ }
+ else
+ {
+ if (self->edge_lock)
+ {
+ memcpy (transition[self->radius_y], transition[self->radius_y - 1], roi->width * sizeof (gfloat));
+ }
+ else
+ {
+ /* No edge lock, set everything 'below canvas' as seen
+ * from the algorithm as unselected.
+ */
+ memset (buf[2], 0, roi->width * sizeof (gfloat));
+ compute_transition (transition[self->radius_y], buf, roi->width, self->edge_lock);
+ }
+ }
+
+ /* update max array */
+ for (x = 0; x < roi->width; x++)
+ {
+ if (max[x] < 1)
+ {
+ if (max[x] <= -self->radius_y)
+ {
+ if (transition[self->radius_y][x])
+ max[x] = self->radius_y;
+ else
+ max[x]--;
+ }
+ else
+ {
+ if (transition[-max[x]][x])
+ max[x] = -max[x];
+ else if (transition[-max[x] + 1][x])
+ max[x] = -max[x] + 1;
+ else
+ max[x]--;
+ }
+ }
+ else
+ {
+ max[x]--;
+ }
+
+ if (max[x] < -self->radius_y - 1)
+ max[x] = -self->radius_y - 1;
+ }
+
+ last_index = 1;
+
+ /* render scan line */
+ for (x = 0 ; x < roi->width; x++)
+ {
+ gfloat last_max;
+
+ last_index--;
+
+ if (last_index >= 0)
+ {
+ last_max = 0.0;
+
+ for (i = self->radius_x; i >= 0; i--)
+ if (max[x + i] <= self->radius_y && max[x + i] >= -self->radius_y &&
+ density[i][max[x+i]] > last_max)
+ {
+ last_max = density[i][max[x + i]];
+ last_index = i;
+ }
+
+ out[x] = last_max;
+ }
+ else
+ {
+ last_max = 0.0;
+
+ for (i = self->radius_x; i >= -self->radius_x; i--)
+ if (max[x + i] <= self->radius_y && max[x + i] >= -self->radius_y &&
+ density[i][max[x + i]] > last_max)
+ {
+ last_max = density[i][max[x + i]];
+ last_index = i;
+ }
+
+ out[x] = last_max;
+ }
+
+ if (last_max <= 0.0)
+ {
+ for (i = x + 1; i < roi->width; i++)
+ {
+ if (max[i] >= -self->radius_y)
+ break;
+ }
+
+ if (i - x > self->radius_x)
+ {
+ for (; x < i - self->radius_x; x++)
+ out[x] = 0;
+
+ x--;
+ }
+
+ last_index = self->radius_x;
+ }
+ }
+
+ gegl_buffer_set (output,
+ GEGL_RECTANGLE (roi->x, roi->y + y,
+ roi->width, 1),
+ 0, output_format, out,
+ GEGL_AUTO_ROWSTRIDE);
+ }
+
+ g_free (out);
+
+ for (i = 0; i < 3; i++)
+ g_free (buf[i]);
+
+ max -= self->radius_x;
+ g_free (max);
+
+ for (i = 0; i < self->radius_y + 1; i++)
+ {
+ transition[i] -= self->radius_x;
+ g_free (transition[i]);
+ }
+
+ g_free (transition);
+
+ for (i = 0; i < self->radius_x + 1 ; i++)
+ {
+ density[i] -= self->radius_y;
+ g_free (density[i]);
+ }
+
+ density -= self->radius_x;
+ g_free (density);
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationborder.h b/app/operations/gimpoperationborder.h
new file mode 100644
index 0000000..83eb9fa
--- /dev/null
+++ b/app/operations/gimpoperationborder.h
@@ -0,0 +1,58 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationborder.h
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_BORDER_H__
+#define __GIMP_OPERATION_BORDER_H__
+
+
+#include <gegl-plugin.h>
+
+
+#define GIMP_TYPE_OPERATION_BORDER (gimp_operation_border_get_type ())
+#define GIMP_OPERATION_BORDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_BORDER, GimpOperationBorder))
+#define GIMP_OPERATION_BORDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_BORDER, GimpOperationBorderClass))
+#define GIMP_IS_OPERATION_BORDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_BORDER))
+#define GIMP_IS_OPERATION_BORDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_BORDER))
+#define GIMP_OPERATION_BORDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_BORDER, GimpOperationBorderClass))
+
+
+typedef struct _GimpOperationBorder GimpOperationBorder;
+typedef struct _GimpOperationBorderClass GimpOperationBorderClass;
+
+struct _GimpOperationBorder
+{
+ GeglOperationFilter parent_instance;
+
+ gint radius_x;
+ gint radius_y;
+ gboolean feather;
+ gboolean edge_lock;
+};
+
+struct _GimpOperationBorderClass
+{
+ GeglOperationFilterClass parent_class;
+};
+
+
+GType gimp_operation_border_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_BORDER_H__ */
diff --git a/app/operations/gimpoperationbrightnesscontrast.c b/app/operations/gimpoperationbrightnesscontrast.c
new file mode 100644
index 0000000..1dd9186
--- /dev/null
+++ b/app/operations/gimpoperationbrightnesscontrast.c
@@ -0,0 +1,140 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationbrightnesscontrast.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 <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "operations-types.h"
+
+#include "gimpbrightnesscontrastconfig.h"
+#include "gimpoperationbrightnesscontrast.h"
+
+#include "gimp-intl.h"
+
+
+static gboolean gimp_operation_brightness_contrast_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationBrightnessContrast, gimp_operation_brightness_contrast,
+ GIMP_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_brightness_contrast_parent_class
+
+
+static void
+gimp_operation_brightness_contrast_class_init (GimpOperationBrightnessContrastClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+
+ object_class->set_property = gimp_operation_point_filter_set_property;
+ object_class->get_property = gimp_operation_point_filter_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:brightness-contrast",
+ "categories", "color",
+ "description", _("Adjust brightness and contrast"),
+ NULL);
+
+ point_class->process = gimp_operation_brightness_contrast_process;
+
+ g_object_class_install_property (object_class,
+ GIMP_OPERATION_POINT_FILTER_PROP_CONFIG,
+ g_param_spec_object ("config",
+ "Config",
+ "The config object",
+ GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_brightness_contrast_init (GimpOperationBrightnessContrast *self)
+{
+}
+
+static inline gfloat
+gimp_operation_brightness_contrast_map (gfloat value,
+ gdouble brightness,
+ gdouble slant)
+{
+ /* apply brightness */
+ if (brightness < 0.0)
+ value = value * (1.0 + brightness);
+ else
+ value = value + ((1.0 - value) * brightness);
+
+ value = (value - 0.5) * slant + 0.5;
+
+ return value;
+}
+
+static gboolean
+gimp_operation_brightness_contrast_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationPointFilter *point = GIMP_OPERATION_POINT_FILTER (operation);
+ GimpBrightnessContrastConfig *config = GIMP_BRIGHTNESS_CONTRAST_CONFIG (point->config);
+ gfloat *src = in_buf;
+ gfloat *dest = out_buf;
+ gdouble brightness;
+ gdouble slant;
+
+ if (! config)
+ return FALSE;
+
+ brightness = config->brightness / 2.0;
+ slant = tan ((config->contrast + 1) * G_PI_4);
+
+ while (samples--)
+ {
+ dest[RED] = gimp_operation_brightness_contrast_map (src[RED],
+ brightness,
+ slant);
+ dest[GREEN] = gimp_operation_brightness_contrast_map (src[GREEN],
+ brightness,
+ slant);
+ dest[BLUE] = gimp_operation_brightness_contrast_map (src[BLUE],
+ brightness,
+ slant);
+ dest[ALPHA] = src[ALPHA];
+
+ src += 4;
+ dest += 4;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationbrightnesscontrast.h b/app/operations/gimpoperationbrightnesscontrast.h
new file mode 100644
index 0000000..d93ff20
--- /dev/null
+++ b/app/operations/gimpoperationbrightnesscontrast.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationbrightnesscontrast.h
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_BRIGHTNESS_CONTRAST_H__
+#define __GIMP_OPERATION_BRIGHTNESS_CONTRAST_H__
+
+
+#include "gimpoperationpointfilter.h"
+
+
+#define GIMP_TYPE_OPERATION_BRIGHTNESS_CONTRAST (gimp_operation_brightness_contrast_get_type ())
+#define GIMP_OPERATION_BRIGHTNESS_CONTRAST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_BRIGHTNESS_CONTRAST, GimpOperationBrightnessContrast))
+#define GIMP_OPERATION_BRIGHTNESS_CONTRAST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_BRIGHTNESS_CONTRAST, GimpOperationBrightnessContrastClass))
+#define GIMP_IS_OPERATION_BRIGHTNESS_CONTRAST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_BRIGHTNESS_CONTRAST))
+#define GIMP_IS_OPERATION_BRIGHTNESS_CONTRAST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_BRIGHTNESS_CONTRAST))
+#define GIMP_OPERATION_BRIGHTNESS_CONTRAST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_BRIGHTNESS_CONTRAST, GimpOperationBrightnessContrastClass))
+
+
+typedef struct _GimpOperationBrightnessContrast GimpOperationBrightnessContrast;
+typedef struct _GimpOperationBrightnessContrastClass GimpOperationBrightnessContrastClass;
+
+struct _GimpOperationBrightnessContrast
+{
+ GimpOperationPointFilter parent_instance;
+};
+
+struct _GimpOperationBrightnessContrastClass
+{
+ GimpOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_brightness_contrast_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_BRIGHTNESS_CONTRAST_H__ */
diff --git a/app/operations/gimpoperationbuffersourcevalidate.c b/app/operations/gimpoperationbuffersourcevalidate.c
new file mode 100644
index 0000000..d277653
--- /dev/null
+++ b/app/operations/gimpoperationbuffersourcevalidate.c
@@ -0,0 +1,307 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationbuffersourcevalidate.c
+ * Copyright (C) 2017 Ell
+ *
+ * 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 <cairo.h>
+#include <gegl-plugin.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "operations-types.h"
+
+#include "gegl/gimptilehandlervalidate.h"
+
+#include "gimpoperationbuffersourcevalidate.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_BUFFER
+};
+
+
+static void gimp_operation_buffer_source_validate_dispose (GObject *object);
+static void gimp_operation_buffer_source_validate_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_buffer_source_validate_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static GeglRectangle gimp_operation_buffer_source_validate_get_bounding_box (GeglOperation *operation);
+static void gimp_operation_buffer_source_validate_prepare (GeglOperation *operation);
+static gboolean gimp_operation_buffer_source_validate_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_pad,
+ const GeglRectangle *result,
+ gint level);
+
+static void gimp_operation_buffer_source_validate_invalidate (gpointer object,
+ const GeglRectangle *rect,
+ GimpOperationBufferSourceValidate *buffer_source_validate);
+
+
+G_DEFINE_TYPE (GimpOperationBufferSourceValidate, gimp_operation_buffer_source_validate,
+ GEGL_TYPE_OPERATION_SOURCE)
+
+#define parent_class gimp_operation_buffer_source_validate_parent_class
+
+
+static void
+gimp_operation_buffer_source_validate_class_init (GimpOperationBufferSourceValidateClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+
+ object_class->dispose = gimp_operation_buffer_source_validate_dispose;
+ object_class->set_property = gimp_operation_buffer_source_validate_set_property;
+ object_class->get_property = gimp_operation_buffer_source_validate_get_property;
+
+ operation_class->get_bounding_box = gimp_operation_buffer_source_validate_get_bounding_box;
+ operation_class->prepare = gimp_operation_buffer_source_validate_prepare;
+ operation_class->process = gimp_operation_buffer_source_validate_process;
+
+ operation_class->threaded = FALSE;
+ operation_class->cache_policy = GEGL_CACHE_POLICY_NEVER;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:buffer-source-validate",
+ "categories", "gimp",
+ "description", "GIMP Buffer-Source Validate operation",
+ NULL);
+
+ g_object_class_install_property (object_class, PROP_BUFFER,
+ g_param_spec_object ("buffer",
+ "Buffer",
+ "Input buffer",
+ GEGL_TYPE_BUFFER,
+ G_PARAM_READWRITE));
+}
+
+static void
+gimp_operation_buffer_source_validate_init (GimpOperationBufferSourceValidate *self)
+{
+}
+
+static void
+gimp_operation_buffer_source_validate_dispose (GObject *object)
+{
+ GimpOperationBufferSourceValidate *buffer_source_validate = GIMP_OPERATION_BUFFER_SOURCE_VALIDATE (object);
+
+ if (buffer_source_validate->buffer)
+ {
+ GimpTileHandlerValidate *validate_handler;
+
+ validate_handler = gimp_tile_handler_validate_get_assigned (
+ buffer_source_validate->buffer);
+
+ if (validate_handler)
+ {
+ g_signal_connect (
+ validate_handler,
+ "invalidated",
+ G_CALLBACK (gimp_operation_buffer_source_validate_invalidate),
+ buffer_source_validate);
+ }
+
+ g_signal_handlers_disconnect_by_func (
+ buffer_source_validate->buffer,
+ gimp_operation_buffer_source_validate_invalidate,
+ buffer_source_validate);
+
+ g_clear_object (&buffer_source_validate->buffer);
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_operation_buffer_source_validate_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationBufferSourceValidate *buffer_source_validate = GIMP_OPERATION_BUFFER_SOURCE_VALIDATE (object);
+
+ switch (property_id)
+ {
+ case PROP_BUFFER:
+ g_value_set_object (value, buffer_source_validate->buffer);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_buffer_source_validate_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationBufferSourceValidate *buffer_source_validate = GIMP_OPERATION_BUFFER_SOURCE_VALIDATE (object);
+
+ switch (property_id)
+ {
+ case PROP_BUFFER:
+ {
+ if (buffer_source_validate->buffer)
+ {
+ GimpTileHandlerValidate *validate_handler;
+
+ validate_handler = gimp_tile_handler_validate_get_assigned (
+ buffer_source_validate->buffer);
+
+ gimp_operation_buffer_source_validate_invalidate (
+ buffer_source_validate->buffer,
+ gegl_buffer_get_extent (buffer_source_validate->buffer),
+ buffer_source_validate);
+
+ g_signal_handlers_disconnect_by_func (
+ buffer_source_validate->buffer,
+ gimp_operation_buffer_source_validate_invalidate,
+ buffer_source_validate);
+
+ if (validate_handler)
+ {
+ g_signal_handlers_disconnect_by_func (
+ validate_handler,
+ gimp_operation_buffer_source_validate_invalidate,
+ buffer_source_validate);
+ }
+
+ g_clear_object (&buffer_source_validate->buffer);
+ }
+
+ buffer_source_validate->buffer = g_value_dup_object (value);
+
+ if (buffer_source_validate->buffer)
+ {
+ GimpTileHandlerValidate *validate_handler;
+
+ validate_handler = gimp_tile_handler_validate_get_assigned (
+ buffer_source_validate->buffer);
+
+ if (validate_handler)
+ {
+ g_signal_connect (
+ validate_handler,
+ "invalidated",
+ G_CALLBACK (gimp_operation_buffer_source_validate_invalidate),
+ buffer_source_validate);
+ }
+
+ gegl_buffer_signal_connect (
+ buffer_source_validate->buffer,
+ "changed",
+ G_CALLBACK (gimp_operation_buffer_source_validate_invalidate),
+ buffer_source_validate);
+
+ gimp_operation_buffer_source_validate_invalidate (
+ buffer_source_validate->buffer,
+ gegl_buffer_get_extent (buffer_source_validate->buffer),
+ buffer_source_validate);
+ }
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static GeglRectangle
+gimp_operation_buffer_source_validate_get_bounding_box (GeglOperation *operation)
+{
+ GimpOperationBufferSourceValidate *buffer_source_validate = GIMP_OPERATION_BUFFER_SOURCE_VALIDATE (operation);
+
+ GeglRectangle result = {};
+
+ if (buffer_source_validate->buffer)
+ result = *gegl_buffer_get_extent (buffer_source_validate->buffer);
+
+ return result;
+}
+
+static void
+gimp_operation_buffer_source_validate_prepare (GeglOperation *operation)
+{
+ GimpOperationBufferSourceValidate *buffer_source_validate = GIMP_OPERATION_BUFFER_SOURCE_VALIDATE (operation);
+ const Babl *format = NULL;
+
+ if (buffer_source_validate->buffer)
+ format = gegl_buffer_get_format (buffer_source_validate->buffer);
+
+ gegl_operation_set_format (operation, "output", format);
+}
+
+static gboolean
+gimp_operation_buffer_source_validate_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_pad,
+ const GeglRectangle *result,
+ gint level)
+{
+ GimpOperationBufferSourceValidate *buffer_source_validate = GIMP_OPERATION_BUFFER_SOURCE_VALIDATE (operation);
+ GeglBuffer *buffer = buffer_source_validate->buffer;
+
+ if (buffer)
+ {
+ GimpTileHandlerValidate *validate_handler;
+
+ validate_handler = gimp_tile_handler_validate_get_assigned (buffer);
+
+ if (validate_handler)
+ {
+ GeglRectangle rect;
+
+ /* align the rectangle to the tile grid */
+ gegl_rectangle_align_to_buffer (
+ &rect, result, buffer_source_validate->buffer,
+ GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
+
+ gimp_tile_handler_validate_validate (validate_handler,
+ buffer_source_validate->buffer,
+ &rect,
+ TRUE, FALSE);
+ }
+
+ gegl_operation_context_set_object (context, "output", G_OBJECT (buffer));
+
+ gegl_object_set_has_forked (G_OBJECT (buffer));
+ }
+
+ return TRUE;
+}
+
+static void
+gimp_operation_buffer_source_validate_invalidate (gpointer object,
+ const GeglRectangle *rect,
+ GimpOperationBufferSourceValidate *buffer_source_validate)
+{
+ gegl_operation_invalidate (GEGL_OPERATION (buffer_source_validate),
+ rect, FALSE);
+}
diff --git a/app/operations/gimpoperationbuffersourcevalidate.h b/app/operations/gimpoperationbuffersourcevalidate.h
new file mode 100644
index 0000000..c47c333
--- /dev/null
+++ b/app/operations/gimpoperationbuffersourcevalidate.h
@@ -0,0 +1,52 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationbuffersourcevalidate.h
+ * Copyright (C) 2017 Ell
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_BUFFER_SOURCE_VALIDATE_H__
+#define __GIMP_OPERATION_BUFFER_SOURCE_VALIDATE_H__
+
+
+#define GIMP_TYPE_OPERATION_BUFFER_SOURCE_VALIDATE (gimp_operation_buffer_source_validate_get_type ())
+#define GIMP_OPERATION_BUFFER_SOURCE_VALIDATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_BUFFER_SOURCE_VALIDATE, GimpOperationBufferSourceValidate))
+#define GIMP_OPERATION_BUFFER_SOURCE_VALIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_BUFFER_SOURCE_VALIDATE, GimpOperationBufferSourceValidateClass))
+#define GIMP_IS_OPERATION_BUFFER_SOURCE_VALIDATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_BUFFER_SOURCE_VALIDATE))
+#define GIMP_IS_OPERATION_BUFFER_SOURCE_VALIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_BUFFER_SOURCE_VALIDATE))
+#define GIMP_OPERATION_BUFFER_SOURCE_VALIDATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_BUFFER_SOURCE_VALIDATE, GimpOperationBufferSourceValidateClass))
+
+
+typedef struct _GimpOperationBufferSourceValidate GimpOperationBufferSourceValidate;
+typedef struct _GimpOperationBufferSourceValidateClass GimpOperationBufferSourceValidateClass;
+
+struct _GimpOperationBufferSourceValidate
+{
+ GeglOperationSource parent_instance;
+
+ GeglBuffer *buffer;
+};
+
+struct _GimpOperationBufferSourceValidateClass
+{
+ GeglOperationSourceClass parent_class;
+};
+
+
+GType gimp_operation_buffer_source_validate_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_BUFFER_SOURCE_VALIDATE_H__ */
diff --git a/app/operations/gimpoperationcagecoefcalc.c b/app/operations/gimpoperationcagecoefcalc.c
new file mode 100644
index 0000000..9c78054
--- /dev/null
+++ b/app/operations/gimpoperationcagecoefcalc.c
@@ -0,0 +1,294 @@
+/* GIMP - The GNU Image Manipulation Program
+ *
+ * gimpoperationcagecoefcalc.c
+ * Copyright (C) 2010 Michael Muré <batolettre@gmail.com>
+ *
+ * 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 <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "operations-types.h"
+
+#include "gimpoperationcagecoefcalc.h"
+#include "gimpcageconfig.h"
+
+#include "gimp-intl.h"
+
+
+static void gimp_operation_cage_coef_calc_finalize (GObject *object);
+static void gimp_operation_cage_coef_calc_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_cage_coef_calc_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_operation_cage_coef_calc_prepare (GeglOperation *operation);
+static GeglRectangle gimp_operation_cage_coef_calc_get_bounding_box (GeglOperation *operation);
+static gboolean gimp_operation_cage_coef_calc_process (GeglOperation *operation,
+ GeglBuffer *output,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationCageCoefCalc, gimp_operation_cage_coef_calc,
+ GEGL_TYPE_OPERATION_SOURCE)
+
+#define parent_class gimp_operation_cage_coef_calc_parent_class
+
+
+static void
+gimp_operation_cage_coef_calc_class_init (GimpOperationCageCoefCalcClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationSourceClass *source_class = GEGL_OPERATION_SOURCE_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:cage-coef-calc",
+ "categories", "transform",
+ "description", _("Compute a set of coefficient buffer for the GIMP cage tool"),
+ NULL);
+
+ operation_class->prepare = gimp_operation_cage_coef_calc_prepare;
+ operation_class->get_bounding_box = gimp_operation_cage_coef_calc_get_bounding_box;
+ operation_class->cache_policy = GEGL_CACHE_POLICY_ALWAYS;
+ operation_class->get_cached_region = NULL;
+
+ source_class->process = gimp_operation_cage_coef_calc_process;
+
+ object_class->get_property = gimp_operation_cage_coef_calc_get_property;
+ object_class->set_property = gimp_operation_cage_coef_calc_set_property;
+ object_class->finalize = gimp_operation_cage_coef_calc_finalize;
+
+ g_object_class_install_property (object_class,
+ GIMP_OPERATION_CAGE_COEF_CALC_PROP_CONFIG,
+ g_param_spec_object ("config",
+ "Config",
+ "A GimpCageConfig object, that define the transformation",
+ GIMP_TYPE_CAGE_CONFIG,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_cage_coef_calc_init (GimpOperationCageCoefCalc *self)
+{
+}
+
+static void
+gimp_operation_cage_coef_calc_finalize (GObject *object)
+{
+ GimpOperationCageCoefCalc *self = GIMP_OPERATION_CAGE_COEF_CALC (object);
+
+ g_clear_object (&self->config);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_operation_cage_coef_calc_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationCageCoefCalc *self = GIMP_OPERATION_CAGE_COEF_CALC (object);
+
+ switch (property_id)
+ {
+ case GIMP_OPERATION_CAGE_COEF_CALC_PROP_CONFIG:
+ g_value_set_object (value, self->config);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_cage_coef_calc_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationCageCoefCalc *self = GIMP_OPERATION_CAGE_COEF_CALC (object);
+
+ switch (property_id)
+ {
+ case GIMP_OPERATION_CAGE_COEF_CALC_PROP_CONFIG:
+ if (self->config)
+ g_object_unref (self->config);
+ self->config = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gimp_operation_cage_coef_calc_is_on_straight (GimpVector2 *d1,
+ GimpVector2 *d2,
+ GimpVector2 *p)
+{
+ GimpVector2 v1, v2;
+ gfloat deter;
+
+ v1.x = p->x - d1->x;
+ v1.y = p->y - d1->y;
+ v2.x = d2->x - d1->x;
+ v2.y = d2->y - d1->y;
+
+ gimp_vector2_normalize (&v1);
+ gimp_vector2_normalize (&v2);
+
+ deter = v1.x * v2.y - v2.x * v1.y;
+
+ return (deter < 0.000000001) && (deter > -0.000000001);
+}
+
+static void
+gimp_operation_cage_coef_calc_prepare (GeglOperation *operation)
+{
+ GimpOperationCageCoefCalc *occc = GIMP_OPERATION_CAGE_COEF_CALC (operation);
+ GimpCageConfig *config = GIMP_CAGE_CONFIG (occc->config);
+
+ gegl_operation_set_format (operation,
+ "output",
+ babl_format_n (babl_type ("float"),
+ 2 * gimp_cage_config_get_n_points (config)));
+}
+
+static GeglRectangle
+gimp_operation_cage_coef_calc_get_bounding_box (GeglOperation *operation)
+{
+ GimpOperationCageCoefCalc *occc = GIMP_OPERATION_CAGE_COEF_CALC (operation);
+ GimpCageConfig *config = GIMP_CAGE_CONFIG (occc->config);
+
+ return gimp_cage_config_get_bounding_box (config);
+}
+
+static gboolean
+gimp_operation_cage_coef_calc_process (GeglOperation *operation,
+ GeglBuffer *output,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationCageCoefCalc *occc = GIMP_OPERATION_CAGE_COEF_CALC (operation);
+ GimpCageConfig *config = GIMP_CAGE_CONFIG (occc->config);
+
+ const Babl *format;
+
+ GeglBufferIterator *it;
+ guint n_cage_vertices;
+ GimpCagePoint *current, *last;
+
+ if (! config)
+ return FALSE;
+
+ format = babl_format_n (babl_type ("float"), 2 * gimp_cage_config_get_n_points (config));
+
+ n_cage_vertices = gimp_cage_config_get_n_points (config);
+
+ it = gegl_buffer_iterator_new (output, roi, 0, format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (it))
+ {
+ /* iterate inside the roi */
+ gfloat *coef = it->items[0].data;
+ gint n_pixels = it->length;
+ gint x = it->items[0].roi.x; /* initial x */
+ gint y = it->items[0].roi.y; /* and y coordinates */
+ gint j;
+
+ memset (coef, 0, sizeof * coef * n_pixels * 2 * n_cage_vertices);
+ while(n_pixels--)
+ {
+ if (gimp_cage_config_point_inside(config, x, y))
+ {
+ last = &(g_array_index (config->cage_points, GimpCagePoint, 0));
+
+ for( j = 0; j < n_cage_vertices; j++)
+ {
+ GimpVector2 v1,v2,a,b,p;
+ gdouble BA,SRT,L0,L1,A0,A1,A10,L10, Q,S,R, absa;
+
+ current = &(g_array_index (config->cage_points, GimpCagePoint, (j+1) % n_cage_vertices));
+ v1 = last->src_point;
+ v2 = current->src_point;
+ p.x = x;
+ p.y = y;
+ a.x = v2.x - v1.x;
+ a.y = v2.y - v1.y;
+ absa = gimp_vector2_length (&a);
+
+ b.x = v1.x - x;
+ b.y = v1.y - y;
+ Q = a.x * a.x + a.y * a.y;
+ S = b.x * b.x + b.y * b.y;
+ R = 2.0 * (a.x * b.x + a.y * b.y);
+ BA = b.x * a.y - b.y * a.x;
+ SRT = sqrt(4.0 * S * Q - R * R);
+
+ L0 = log(S);
+ L1 = log(S + Q + R);
+ A0 = atan2(R, SRT) / SRT;
+ A1 = atan2(2.0 * Q + R, SRT) / SRT;
+ A10 = A1 - A0;
+ L10 = L1 - L0;
+
+ /* edge coef */
+ coef[j + n_cage_vertices] = (-absa / (4.0 * G_PI)) * ((4.0*S-(R*R)/Q) * A10 + (R / (2.0 * Q)) * L10 + L1 - 2.0);
+
+ if (isnan(coef[j + n_cage_vertices]))
+ {
+ coef[j + n_cage_vertices] = 0.0;
+ }
+
+ /* vertice coef */
+ if (!gimp_operation_cage_coef_calc_is_on_straight (&v1, &v2, &p))
+ {
+ coef[j] += (BA / (2.0 * G_PI)) * (L10 /(2.0*Q) - A10 * (2.0 + R / Q));
+ coef[(j+1)%n_cage_vertices] -= (BA / (2.0 * G_PI)) * (L10 / (2.0 * Q) - A10 * (R / Q));
+ }
+
+ last = current;
+ }
+ }
+
+ coef += 2 * n_cage_vertices;
+
+ /* update x and y coordinates */
+ x++;
+ if (x >= (it->items[0].roi.x + it->items[0].roi.width))
+ {
+ x = it->items[0].roi.x;
+ y++;
+ }
+ }
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationcagecoefcalc.h b/app/operations/gimpoperationcagecoefcalc.h
new file mode 100644
index 0000000..e63be27
--- /dev/null
+++ b/app/operations/gimpoperationcagecoefcalc.h
@@ -0,0 +1,62 @@
+/* GIMP - The GNU Image Manipulation Program
+ *
+ * gimpoperationcagecoefcalc.h
+ * Copyright (C) 2010 Michael Muré <batolettre@gmail.com>
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_CAGE_COEF_CALC_H__
+#define __GIMP_OPERATION_CAGE_COEF_CALC_H__
+
+
+#include <gegl-plugin.h>
+#include <operation/gegl-operation-source.h>
+
+
+enum
+{
+ GIMP_OPERATION_CAGE_COEF_CALC_PROP_0,
+ GIMP_OPERATION_CAGE_COEF_CALC_PROP_CONFIG
+};
+
+
+#define GIMP_TYPE_OPERATION_CAGE_COEF_CALC (gimp_operation_cage_coef_calc_get_type ())
+#define GIMP_OPERATION_CAGE_COEF_CALC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_CAGE_COEF_CALC, GimpOperationCageCoefCalc))
+#define GIMP_OPERATION_CAGE_COEF_CALC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_CAGE_COEF_CALC, GimpOperationCageCoefCalcClass))
+#define GIMP_IS_OPERATION_CAGE_COEF_CALC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_CAGE_COEF_CALC))
+#define GIMP_IS_OPERATION_CAGE_COEF_CALC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_CAGE_COEF_CALC))
+#define GIMP_OPERATION_CAGE_COEF_CALC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_CAGE_COEF_CALC, GimpOperationCageCoefCalcClass))
+
+
+typedef struct _GimpOperationCageCoefCalc GimpOperationCageCoefCalc;
+typedef struct _GimpOperationCageCoefCalcClass GimpOperationCageCoefCalcClass;
+
+struct _GimpOperationCageCoefCalc
+{
+ GeglOperationSource parent_instance;
+
+ GimpCageConfig *config;
+};
+
+struct _GimpOperationCageCoefCalcClass
+{
+ GeglOperationSourceClass parent_class;
+};
+
+
+GType gimp_operation_cage_coef_calc_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_CAGE_COEF_CALC_H__ */
diff --git a/app/operations/gimpoperationcagetransform.c b/app/operations/gimpoperationcagetransform.c
new file mode 100644
index 0000000..d2f0450
--- /dev/null
+++ b/app/operations/gimpoperationcagetransform.c
@@ -0,0 +1,603 @@
+/* GIMP - The GNU Image Manipulation Program
+ *
+ * gimpoperationcage.c
+ * Copyright (C) 2010 Michael Muré <batolettre@gmail.com>
+ *
+ * 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 <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "operations-types.h"
+
+#include "gimpoperationcagetransform.h"
+#include "gimpcageconfig.h"
+
+#include "gimp-intl.h"
+
+enum
+{
+ PROP_0,
+ PROP_CONFIG,
+ PROP_FILL,
+};
+
+
+static void gimp_operation_cage_transform_finalize (GObject *object);
+static void gimp_operation_cage_transform_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_cage_transform_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_cage_transform_prepare (GeglOperation *operation);
+static gboolean gimp_operation_cage_transform_process (GeglOperation *operation,
+ GeglBuffer *in_buf,
+ GeglBuffer *aux_buf,
+ GeglBuffer *out_buf,
+ const GeglRectangle *roi,
+ gint level);
+static void gimp_operation_cage_transform_interpolate_source_coords_recurs
+ (GimpOperationCageTransform *oct,
+ GeglBuffer *out_buf,
+ const GeglRectangle *roi,
+ GimpVector2 p1_s,
+ GimpVector2 p1_d,
+ GimpVector2 p2_s,
+ GimpVector2 p2_d,
+ GimpVector2 p3_s,
+ GimpVector2 p3_d,
+ gint recursion_depth,
+ gfloat *coords);
+static GimpVector2 gimp_cage_transform_compute_destination (GimpCageConfig *config,
+ gfloat *coef,
+ GeglSampler *coef_sampler,
+ GimpVector2 coords);
+GeglRectangle gimp_operation_cage_transform_get_cached_region (GeglOperation *operation,
+ const GeglRectangle *roi);
+GeglRectangle gimp_operation_cage_transform_get_required_for_output (GeglOperation *operation,
+ const gchar *input_pad,
+ const GeglRectangle *roi);
+GeglRectangle gimp_operation_cage_transform_get_bounding_box (GeglOperation *operation);
+
+
+G_DEFINE_TYPE (GimpOperationCageTransform, gimp_operation_cage_transform,
+ GEGL_TYPE_OPERATION_COMPOSER)
+
+#define parent_class gimp_operation_cage_transform_parent_class
+
+
+static void
+gimp_operation_cage_transform_class_init (GimpOperationCageTransformClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationComposerClass *filter_class = GEGL_OPERATION_COMPOSER_CLASS (klass);
+
+ object_class->get_property = gimp_operation_cage_transform_get_property;
+ object_class->set_property = gimp_operation_cage_transform_set_property;
+ object_class->finalize = gimp_operation_cage_transform_finalize;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:cage-transform",
+ "categories", "transform",
+ "description", _("Convert a set of coefficient buffer to a coordinate buffer for the GIMP cage tool"),
+ NULL);
+
+ operation_class->prepare = gimp_operation_cage_transform_prepare;
+
+ operation_class->get_required_for_output = gimp_operation_cage_transform_get_required_for_output;
+ operation_class->get_cached_region = gimp_operation_cage_transform_get_cached_region;
+ operation_class->get_bounding_box = gimp_operation_cage_transform_get_bounding_box;
+ /* XXX Temporarily disable multi-threading on this operation because
+ * it is much faster when single-threaded. See bug 787663.
+ */
+ operation_class->threaded = FALSE;
+
+ filter_class->process = gimp_operation_cage_transform_process;
+
+ g_object_class_install_property (object_class, PROP_CONFIG,
+ g_param_spec_object ("config",
+ "Config",
+ "A GimpCageConfig object, that define the transformation",
+ GIMP_TYPE_CAGE_CONFIG,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_FILL,
+ g_param_spec_boolean ("fill-plain-color",
+ _("Fill with plain color"),
+ _("Fill the original position of the cage with a plain color"),
+ FALSE,
+ G_PARAM_READWRITE));
+}
+
+static void
+gimp_operation_cage_transform_init (GimpOperationCageTransform *self)
+{
+ self->format_coords = babl_format_n(babl_type("float"), 2);
+}
+
+static void
+gimp_operation_cage_transform_finalize (GObject *object)
+{
+ GimpOperationCageTransform *self = GIMP_OPERATION_CAGE_TRANSFORM (object);
+
+ g_clear_object (&self->config);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_operation_cage_transform_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationCageTransform *self = GIMP_OPERATION_CAGE_TRANSFORM (object);
+
+ switch (property_id)
+ {
+ case PROP_CONFIG:
+ g_value_set_object (value, self->config);
+ break;
+ case PROP_FILL:
+ g_value_set_boolean (value, self->fill_plain_color);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_cage_transform_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationCageTransform *self = GIMP_OPERATION_CAGE_TRANSFORM (object);
+
+ switch (property_id)
+ {
+ case PROP_CONFIG:
+ if (self->config)
+ g_object_unref (self->config);
+ self->config = g_value_dup_object (value);
+ break;
+ case PROP_FILL:
+ self->fill_plain_color = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_cage_transform_prepare (GeglOperation *operation)
+{
+ GimpOperationCageTransform *oct = GIMP_OPERATION_CAGE_TRANSFORM (operation);
+ GimpCageConfig *config = GIMP_CAGE_CONFIG (oct->config);
+
+ gegl_operation_set_format (operation, "input",
+ babl_format_n (babl_type ("float"),
+ 2 * gimp_cage_config_get_n_points (config)));
+ gegl_operation_set_format (operation, "output",
+ babl_format_n (babl_type ("float"), 2));
+}
+
+static gboolean
+gimp_operation_cage_transform_process (GeglOperation *operation,
+ GeglBuffer *in_buf,
+ GeglBuffer *aux_buf,
+ GeglBuffer *out_buf,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationCageTransform *oct = GIMP_OPERATION_CAGE_TRANSFORM (operation);
+ GimpCageConfig *config = GIMP_CAGE_CONFIG (oct->config);
+ GeglRectangle cage_bb;
+ gfloat *coords;
+ gfloat *coef;
+ const Babl *format_coef;
+ GeglSampler *coef_sampler;
+ GimpVector2 plain_color;
+ GeglBufferIterator *it;
+ gint x, y;
+ gboolean output_set;
+ GimpCagePoint *point;
+ guint n_cage_vertices;
+
+ /* pre-fill the out buffer with no-displacement coordinate */
+ it = gegl_buffer_iterator_new (out_buf, roi, 0, NULL,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
+ cage_bb = gimp_cage_config_get_bounding_box (config);
+
+ point = &(g_array_index (config->cage_points, GimpCagePoint, 0));
+ plain_color.x = (gint) point->src_point.x;
+ plain_color.y = (gint) point->src_point.y;
+
+ n_cage_vertices = gimp_cage_config_get_n_points (config);
+
+ while (gegl_buffer_iterator_next (it))
+ {
+ /* iterate inside the roi */
+ gint n_pixels = it->length;
+ gfloat *output = it->items[0].data;
+
+ x = it->items[0].roi.x; /* initial x */
+ y = it->items[0].roi.y; /* and y coordinates */
+
+ while (n_pixels--)
+ {
+ output_set = FALSE;
+ if (oct->fill_plain_color)
+ {
+ if (x > cage_bb.x &&
+ y > cage_bb.y &&
+ x < cage_bb.x + cage_bb.width &&
+ y < cage_bb.y + cage_bb.height)
+ {
+ if (gimp_cage_config_point_inside (config, x, y))
+ {
+ output[0] = plain_color.x;
+ output[1] = plain_color.y;
+ output_set = TRUE;
+ }
+ }
+ }
+ if (!output_set)
+ {
+ output[0] = x + 0.5;
+ output[1] = y + 0.5;
+ }
+
+ output += 2;
+
+ /* update x and y coordinates */
+ x++;
+ if (x >= (it->items[0].roi.x + it->items[0].roi.width))
+ {
+ x = it->items[0].roi.x;
+ y++;
+ }
+ }
+ }
+
+ if (! aux_buf)
+ return TRUE;
+
+ gegl_operation_progress (operation, 0.0, "");
+
+ /* pre-allocate memory outside of the loop */
+ coords = g_slice_alloc (2 * sizeof (gfloat));
+ coef = g_malloc (n_cage_vertices * 2 * sizeof (gfloat));
+ format_coef = babl_format_n (babl_type ("float"), 2 * n_cage_vertices);
+ coef_sampler = gegl_buffer_sampler_new (aux_buf,
+ format_coef, GEGL_SAMPLER_NEAREST);
+
+ /* compute, reverse and interpolate the transformation */
+ for (y = cage_bb.y; y < cage_bb.y + cage_bb.height - 1; y++)
+ {
+ GimpVector2 p1_d, p2_d, p3_d, p4_d;
+ GimpVector2 p1_s, p2_s, p3_s, p4_s;
+
+ p1_s.y = y;
+ p2_s.y = y+1;
+ p3_s.y = y+1;
+ p3_s.x = cage_bb.x;
+ p4_s.y = y;
+ p4_s.x = cage_bb.x;
+
+ p3_d = gimp_cage_transform_compute_destination (config, coef, coef_sampler, p3_s);
+ p4_d = gimp_cage_transform_compute_destination (config, coef, coef_sampler, p4_s);
+
+ for (x = cage_bb.x; x < cage_bb.x + cage_bb.width - 1; x++)
+ {
+ p1_s = p4_s;
+ p2_s = p3_s;
+ p3_s.x = x+1;
+ p4_s.x = x+1;
+
+ p1_d = p4_d;
+ p2_d = p3_d;
+ p3_d = gimp_cage_transform_compute_destination (config, coef, coef_sampler, p3_s);
+ p4_d = gimp_cage_transform_compute_destination (config, coef, coef_sampler, p4_s);
+
+ if (gimp_cage_config_point_inside (config, x, y))
+ {
+ gimp_operation_cage_transform_interpolate_source_coords_recurs (oct,
+ out_buf,
+ roi,
+ p1_s, p1_d,
+ p2_s, p2_d,
+ p3_s, p3_d,
+ 0,
+ coords);
+
+ gimp_operation_cage_transform_interpolate_source_coords_recurs (oct,
+ out_buf,
+ roi,
+ p1_s, p1_d,
+ p3_s, p3_d,
+ p4_s, p4_d,
+ 0,
+ coords);
+ }
+ }
+
+ if ((y - cage_bb.y) % 20 == 0)
+ {
+ gdouble fraction = ((gdouble) (y - cage_bb.y) /
+ (gdouble) (cage_bb.height));
+
+ /* 0.0 and 1.0 indicate progress start/end, so avoid them */
+ if (fraction > 0.0 && fraction < 1.0)
+ {
+ gegl_operation_progress (operation, fraction, "");
+ }
+ }
+ }
+
+ g_object_unref (coef_sampler);
+ g_free (coef);
+ g_slice_free1 (2 * sizeof (gfloat), coords);
+
+ gegl_operation_progress (operation, 1.0, "");
+
+ return TRUE;
+}
+
+
+static void
+gimp_operation_cage_transform_interpolate_source_coords_recurs (GimpOperationCageTransform *oct,
+ GeglBuffer *out_buf,
+ const GeglRectangle *roi,
+ GimpVector2 p1_s,
+ GimpVector2 p1_d,
+ GimpVector2 p2_s,
+ GimpVector2 p2_d,
+ GimpVector2 p3_s,
+ GimpVector2 p3_d,
+ gint recursion_depth,
+ gfloat *coords)
+{
+ gint xmin, xmax, ymin, ymax, x, y;
+
+ /* Stop recursion if all 3 vertices of the triangle are outside the
+ * ROI (left/right or above/below).
+ */
+ if (p1_d.x >= roi->x + roi->width &&
+ p2_d.x >= roi->x + roi->width &&
+ p3_d.x >= roi->x + roi->width) return;
+ if (p1_d.y >= roi->y + roi->height &&
+ p2_d.y >= roi->y + roi->height &&
+ p3_d.y >= roi->y + roi->height) return;
+
+ if (p1_d.x < roi->x &&
+ p2_d.x < roi->x &&
+ p3_d.x < roi->x) return;
+ if (p1_d.y < roi->y &&
+ p2_d.y < roi->y &&
+ p3_d.y < roi->y) return;
+
+ xmin = xmax = lrint (p1_d.x);
+ ymin = ymax = lrint (p1_d.y);
+
+ x = lrint (p2_d.x);
+ xmin = MIN (x, xmin);
+ xmax = MAX (x, xmax);
+
+ x = lrint (p3_d.x);
+ xmin = MIN (x, xmin);
+ xmax = MAX (x, xmax);
+
+ y = lrint (p2_d.y);
+ ymin = MIN (y, ymin);
+ ymax = MAX (y, ymax);
+
+ y = lrint (p3_d.y);
+ ymin = MIN (y, ymin);
+ ymax = MAX (y, ymax);
+
+ /* test if there is no more pixel in the triangle */
+ if (xmin == xmax || ymin == ymax)
+ return;
+
+ /* test if the triangle is implausibly large as manifested by too deep recursion */
+ if (recursion_depth > 5)
+ return;
+
+ /* test if the triangle is small enough.
+ *
+ * if yes, we compute the coefficient of the barycenter for the
+ * pixel (x,y) and see if a pixel is inside (ie the 3 coef have the
+ * same sign).
+ */
+ if (xmax - xmin == 1 && ymax - ymin == 1)
+ {
+ gdouble a, b, c, denom, x, y;
+
+ x = (gdouble) xmin + 0.5;
+ y = (gdouble) ymin + 0.5;
+
+ denom = (p2_d.x - p1_d.x) * p3_d.y + (p1_d.x - p3_d.x) * p2_d.y + (p3_d.x - p2_d.x) * p1_d.y;
+ a = ((p2_d.x - x) * p3_d.y + (x - p3_d.x) * p2_d.y + (p3_d.x - p2_d.x) * y) / denom;
+ b = - ((p1_d.x - x) * p3_d.y + (x - p3_d.x) * p1_d.y + (p3_d.x - p1_d.x) * y) / denom;
+ c = 1.0 - a - b;
+
+ /* if a pixel is inside, we compute its source coordinate and
+ * set it in the output buffer
+ */
+ if ((a > 0 && b > 0 && c > 0) || (a < 0 && b < 0 && c < 0))
+ {
+ GeglRectangle rect = { 0, 0, 1, 1 };
+ gfloat coords[2];
+
+ rect.x = xmin;
+ rect.y = ymin;
+
+ coords[0] = (a * p1_s.x + b * p2_s.x + c * p3_s.x);
+ coords[1] = (a * p1_s.y + b * p2_s.y + c * p3_s.y);
+
+ gegl_buffer_set (out_buf,
+ &rect,
+ 0,
+ oct->format_coords,
+ coords,
+ GEGL_AUTO_ROWSTRIDE);
+ }
+
+ return;
+ }
+ else
+ {
+ /* we cut the triangle in 4 sub-triangle and treat it recursively */
+ /*
+ * /\
+ * /__\
+ * /\ /\
+ * /__\/__\
+ *
+ */
+
+ GimpVector2 pm1_d, pm2_d, pm3_d;
+ GimpVector2 pm1_s, pm2_s, pm3_s;
+ gint next_depth = recursion_depth + 1;
+
+ pm1_d.x = (p1_d.x + p2_d.x) / 2.0;
+ pm1_d.y = (p1_d.y + p2_d.y) / 2.0;
+
+ pm2_d.x = (p2_d.x + p3_d.x) / 2.0;
+ pm2_d.y = (p2_d.y + p3_d.y) / 2.0;
+
+ pm3_d.x = (p3_d.x + p1_d.x) / 2.0;
+ pm3_d.y = (p3_d.y + p1_d.y) / 2.0;
+
+ pm1_s.x = (p1_s.x + p2_s.x) / 2.0;
+ pm1_s.y = (p1_s.y + p2_s.y) / 2.0;
+
+ pm2_s.x = (p2_s.x + p3_s.x) / 2.0;
+ pm2_s.y = (p2_s.y + p3_s.y) / 2.0;
+
+ pm3_s.x = (p3_s.x + p1_s.x) / 2.0;
+ pm3_s.y = (p3_s.y + p1_s.y) / 2.0;
+
+ gimp_operation_cage_transform_interpolate_source_coords_recurs (oct,
+ out_buf,
+ roi,
+ p1_s, p1_d,
+ pm1_s, pm1_d,
+ pm3_s, pm3_d,
+ next_depth,
+ coords);
+
+ gimp_operation_cage_transform_interpolate_source_coords_recurs (oct,
+ out_buf,
+ roi,
+ pm1_s, pm1_d,
+ p2_s, p2_d,
+ pm2_s, pm2_d,
+ next_depth,
+ coords);
+
+ gimp_operation_cage_transform_interpolate_source_coords_recurs (oct,
+ out_buf,
+ roi,
+ pm1_s, pm1_d,
+ pm2_s, pm2_d,
+ pm3_s, pm3_d,
+ next_depth,
+ coords);
+
+ gimp_operation_cage_transform_interpolate_source_coords_recurs (oct,
+ out_buf,
+ roi,
+ pm3_s, pm3_d,
+ pm2_s, pm2_d,
+ p3_s, p3_d,
+ next_depth,
+ coords);
+ }
+}
+
+static GimpVector2
+gimp_cage_transform_compute_destination (GimpCageConfig *config,
+ gfloat *coef,
+ GeglSampler *coef_sampler,
+ GimpVector2 coords)
+{
+ GimpVector2 result = {0, 0};
+ gint n_cage_vertices = gimp_cage_config_get_n_points (config);
+ gint i;
+ GimpCagePoint *point;
+
+ gegl_sampler_get (coef_sampler,
+ coords.x, coords.y, NULL, coef, GEGL_ABYSS_NONE);
+
+ for (i = 0; i < n_cage_vertices; i++)
+ {
+ point = &g_array_index (config->cage_points, GimpCagePoint, i);
+
+ result.x += coef[i] * point->dest_point.x;
+ result.y += coef[i] * point->dest_point.y;
+
+ result.x += coef[i + n_cage_vertices] * point->edge_scaling_factor * point->edge_normal.x;
+ result.y += coef[i + n_cage_vertices] * point->edge_scaling_factor * point->edge_normal.y;
+ }
+
+ return result;
+}
+
+GeglRectangle
+gimp_operation_cage_transform_get_cached_region (GeglOperation *operation,
+ const GeglRectangle *roi)
+{
+ GeglRectangle result = *gegl_operation_source_get_bounding_box (operation,
+ "input");
+
+ return result;
+}
+
+GeglRectangle
+gimp_operation_cage_transform_get_required_for_output (GeglOperation *operation,
+ const gchar *input_pad,
+ const GeglRectangle *roi)
+{
+ GeglRectangle result = *gegl_operation_source_get_bounding_box (operation,
+ "input");
+
+ return result;
+}
+
+GeglRectangle
+gimp_operation_cage_transform_get_bounding_box (GeglOperation *operation)
+{
+ GeglRectangle result = *gegl_operation_source_get_bounding_box (operation,
+ "input");
+
+ return result;
+}
diff --git a/app/operations/gimpoperationcagetransform.h b/app/operations/gimpoperationcagetransform.h
new file mode 100644
index 0000000..8ee9286
--- /dev/null
+++ b/app/operations/gimpoperationcagetransform.h
@@ -0,0 +1,58 @@
+/* GIMP - The GNU Image Manipulation Program
+ *
+ * gimpoperationcagetransform.h
+ * Copyright (C) 2010 Michael Muré <batolettre@gmail.com>
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_CAGE_TRANSFORM_H__
+#define __GIMP_OPERATION_CAGE_TRANSFORM_H__
+
+
+#include <gegl-plugin.h>
+#include <operation/gegl-operation-composer.h>
+
+
+#define GIMP_TYPE_OPERATION_CAGE_TRANSFORM (gimp_operation_cage_transform_get_type ())
+#define GIMP_OPERATION_CAGE_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_CAGE_TRANSFORM, GimpOperationCageTransform))
+#define GIMP_OPERATION_CAGE_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_CAGE_TRANSFORM, GimpOperationCageTransformClass))
+#define GIMP_IS_OPERATION_CAGE_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_CAGE_TRANSFORM))
+#define GIMP_IS_OPERATION_CAGE_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_CAGE_TRANSFORM))
+#define GIMP_OPERATION_CAGE_TRANSFORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_CAGE_TRANSFORM, GimpOperationCageTransformClass))
+
+
+typedef struct _GimpOperationCageTransform GimpOperationCageTransform;
+typedef struct _GimpOperationCageTransformClass GimpOperationCageTransformClass;
+
+struct _GimpOperationCageTransform
+{
+ GeglOperationComposer parent_instance;
+
+ GimpCageConfig *config;
+ gboolean fill_plain_color;
+
+ const Babl *format_coords;
+};
+
+struct _GimpOperationCageTransformClass
+{
+ GeglOperationComposerClass parent_class;
+};
+
+
+GType gimp_operation_cage_transform_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_CAGE_TRANSFORM_H__ */
diff --git a/app/operations/gimpoperationcolorbalance.c b/app/operations/gimpoperationcolorbalance.c
new file mode 100644
index 0000000..9988158
--- /dev/null
+++ b/app/operations/gimpoperationcolorbalance.c
@@ -0,0 +1,198 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationcolorbalance.c
+ * Copyright (C) 2007 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 <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "operations-types.h"
+
+#include "gimpcolorbalanceconfig.h"
+#include "gimpoperationcolorbalance.h"
+
+#include "gimp-intl.h"
+
+
+static gboolean gimp_operation_color_balance_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationColorBalance, gimp_operation_color_balance,
+ GIMP_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_color_balance_parent_class
+
+
+static void
+gimp_operation_color_balance_class_init (GimpOperationColorBalanceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+
+ object_class->set_property = gimp_operation_point_filter_set_property;
+ object_class->get_property = gimp_operation_point_filter_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:color-balance",
+ "categories", "color",
+ "description", _("Adjust color distribution"),
+ NULL);
+
+ point_class->process = gimp_operation_color_balance_process;
+
+ g_object_class_install_property (object_class,
+ GIMP_OPERATION_POINT_FILTER_PROP_CONFIG,
+ g_param_spec_object ("config",
+ "Config",
+ "The config object",
+ GIMP_TYPE_COLOR_BALANCE_CONFIG,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_color_balance_init (GimpOperationColorBalance *self)
+{
+}
+
+static inline gfloat
+gimp_operation_color_balance_map (gfloat value,
+ gdouble lightness,
+ gdouble shadows,
+ gdouble midtones,
+ gdouble highlights)
+{
+ /* Apply masks to the corrections for shadows, midtones and
+ * highlights so that each correction affects only one range.
+ * Those masks look like this:
+ * ‾\___
+ * _/‾\_
+ * ___/‾
+ * with ramps of width a at x = b and x = 1 - b.
+ *
+ * The sum of these masks equals 1 for x in 0..1, so applying the
+ * same correction in the shadows and in the midtones is equivalent
+ * to applying this correction on a virtual shadows_and_midtones
+ * range.
+ */
+ static const gdouble a = 0.25, b = 0.333, scale = 0.7;
+
+ shadows *= CLAMP ((lightness - b) / -a + 0.5, 0, 1) * scale;
+ midtones *= CLAMP ((lightness - b) / a + 0.5, 0, 1) *
+ CLAMP ((lightness + b - 1) / -a + 0.5, 0, 1) * scale;
+ highlights *= CLAMP ((lightness + b - 1) / a + 0.5, 0, 1) * scale;
+
+ value += shadows;
+ value += midtones;
+ value += highlights;
+ value = CLAMP (value, 0.0, 1.0);
+
+ return value;
+}
+
+static gboolean
+gimp_operation_color_balance_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationPointFilter *point = GIMP_OPERATION_POINT_FILTER (operation);
+ GimpColorBalanceConfig *config = GIMP_COLOR_BALANCE_CONFIG (point->config);
+ gfloat *src = in_buf;
+ gfloat *dest = out_buf;
+
+ if (! config)
+ return FALSE;
+
+ while (samples--)
+ {
+ gfloat r = src[RED];
+ gfloat g = src[GREEN];
+ gfloat b = src[BLUE];
+ gfloat r_n;
+ gfloat g_n;
+ gfloat b_n;
+
+ GimpRGB rgb = { r, g, b};
+ GimpHSL hsl;
+
+ gimp_rgb_to_hsl (&rgb, &hsl);
+
+ r_n = gimp_operation_color_balance_map (r, hsl.l,
+ config->cyan_red[GIMP_TRANSFER_SHADOWS],
+ config->cyan_red[GIMP_TRANSFER_MIDTONES],
+ config->cyan_red[GIMP_TRANSFER_HIGHLIGHTS]);
+
+ g_n = gimp_operation_color_balance_map (g, hsl.l,
+ config->magenta_green[GIMP_TRANSFER_SHADOWS],
+ config->magenta_green[GIMP_TRANSFER_MIDTONES],
+ config->magenta_green[GIMP_TRANSFER_HIGHLIGHTS]);
+
+ b_n = gimp_operation_color_balance_map (b, hsl.l,
+ config->yellow_blue[GIMP_TRANSFER_SHADOWS],
+ config->yellow_blue[GIMP_TRANSFER_MIDTONES],
+ config->yellow_blue[GIMP_TRANSFER_HIGHLIGHTS]);
+
+ if (config->preserve_luminosity)
+ {
+ GimpHSL hsl2;
+
+ rgb.r = r_n;
+ rgb.g = g_n;
+ rgb.b = b_n;
+ gimp_rgb_to_hsl (&rgb, &hsl);
+
+ rgb.r = r;
+ rgb.g = g;
+ rgb.b = b;
+ gimp_rgb_to_hsl (&rgb, &hsl2);
+
+ hsl.l = hsl2.l;
+
+ gimp_hsl_to_rgb (&hsl, &rgb);
+
+ r_n = rgb.r;
+ g_n = rgb.g;
+ b_n = rgb.b;
+ }
+
+ dest[RED] = r_n;
+ dest[GREEN] = g_n;
+ dest[BLUE] = b_n;
+ dest[ALPHA] = src[ALPHA];
+
+ src += 4;
+ dest += 4;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationcolorbalance.h b/app/operations/gimpoperationcolorbalance.h
new file mode 100644
index 0000000..a6ba386
--- /dev/null
+++ b/app/operations/gimpoperationcolorbalance.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationcolorbalance.h
+ * Copyright (C) 2007 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_COLOR_BALANCE_H__
+#define __GIMP_OPERATION_COLOR_BALANCE_H__
+
+
+#include "gimpoperationpointfilter.h"
+
+
+#define GIMP_TYPE_OPERATION_COLOR_BALANCE (gimp_operation_color_balance_get_type ())
+#define GIMP_OPERATION_COLOR_BALANCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_COLOR_BALANCE, GimpOperationColorBalance))
+#define GIMP_OPERATION_COLOR_BALANCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_COLOR_BALANCE, GimpOperationColorBalanceClass))
+#define GIMP_IS_OPERATION_COLOR_BALANCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_COLOR_BALANCE))
+#define GIMP_IS_OPERATION_COLOR_BALANCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_COLOR_BALANCE))
+#define GIMP_OPERATION_COLOR_BALANCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_COLOR_BALANCE, GimpOperationColorBalanceClass))
+
+
+typedef struct _GimpOperationColorBalance GimpOperationColorBalance;
+typedef struct _GimpOperationColorBalanceClass GimpOperationColorBalanceClass;
+
+struct _GimpOperationColorBalance
+{
+ GimpOperationPointFilter parent_instance;
+};
+
+struct _GimpOperationColorBalanceClass
+{
+ GimpOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_color_balance_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_COLOR_BALANCE_H__ */
diff --git a/app/operations/gimpoperationcolorize.c b/app/operations/gimpoperationcolorize.c
new file mode 100644
index 0000000..fd64840
--- /dev/null
+++ b/app/operations/gimpoperationcolorize.c
@@ -0,0 +1,274 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationcolorize.c
+ * Copyright (C) 2007 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 <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "operations-types.h"
+
+#include "gimpoperationcolorize.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_HUE,
+ PROP_SATURATION,
+ PROP_LIGHTNESS,
+ PROP_COLOR
+};
+
+
+static void gimp_operation_colorize_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_colorize_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static gboolean gimp_operation_colorize_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationColorize, gimp_operation_colorize,
+ GIMP_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_colorize_parent_class
+
+
+static void
+gimp_operation_colorize_class_init (GimpOperationColorizeClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+ GimpHSL hsl;
+ GimpRGB rgb;
+
+ object_class->set_property = gimp_operation_colorize_set_property;
+ object_class->get_property = gimp_operation_colorize_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:colorize",
+ "categories", "color",
+ "description", _("Colorize the image"),
+ NULL);
+
+ point_class->process = gimp_operation_colorize_process;
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_HUE,
+ "hue",
+ _("Hue"),
+ _("Hue"),
+ 0.0, 1.0, 0.5, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_SATURATION,
+ "saturation",
+ _("Saturation"),
+ _("Saturation"),
+ 0.0, 1.0, 0.5, 0);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LIGHTNESS,
+ "lightness",
+ _("Lightness"),
+ _("Lightness"),
+ -1.0, 1.0, 0.0, 0);
+
+ gimp_hsl_set (&hsl, 0.5, 0.5, 0.5);
+ gimp_hsl_set_alpha (&hsl, 1.0);
+ gimp_hsl_to_rgb (&hsl, &rgb);
+
+ g_object_class_install_property (object_class, PROP_COLOR,
+ gimp_param_spec_rgb ("color",
+ _("Color"),
+ _("Color"),
+ FALSE, &rgb,
+ G_PARAM_READWRITE));
+}
+
+static void
+gimp_operation_colorize_init (GimpOperationColorize *self)
+{
+}
+
+static void
+gimp_operation_colorize_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationColorize *self = GIMP_OPERATION_COLORIZE (object);
+
+ switch (property_id)
+ {
+ case PROP_HUE:
+ g_value_set_double (value, self->hue);
+ break;
+
+ case PROP_SATURATION:
+ g_value_set_double (value, self->saturation);
+ break;
+
+ case PROP_LIGHTNESS:
+ g_value_set_double (value, self->lightness);
+ break;
+
+ case PROP_COLOR:
+ {
+ GimpHSL hsl;
+ GimpRGB rgb;
+
+ gimp_hsl_set (&hsl,
+ self->hue,
+ self->saturation,
+ (self->lightness + 1.0) / 2.0);
+ gimp_hsl_set_alpha (&hsl, 1.0);
+ gimp_hsl_to_rgb (&hsl, &rgb);
+ gimp_value_set_rgb (value, &rgb);
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_colorize_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationColorize *self = GIMP_OPERATION_COLORIZE (object);
+
+ switch (property_id)
+ {
+ case PROP_HUE:
+ self->hue = g_value_get_double (value);
+ g_object_notify (object, "color");
+ break;
+
+ case PROP_SATURATION:
+ self->saturation = g_value_get_double (value);
+ g_object_notify (object, "color");
+ break;
+
+ case PROP_LIGHTNESS:
+ self->lightness = g_value_get_double (value);
+ g_object_notify (object, "color");
+ break;
+
+ case PROP_COLOR:
+ {
+ GimpRGB rgb;
+ GimpHSL hsl;
+
+ gimp_value_get_rgb (value, &rgb);
+ gimp_rgb_to_hsl (&rgb, &hsl);
+
+ if (hsl.h == -1)
+ hsl.h = self->hue;
+
+ if (hsl.l == 0.0 || hsl.l == 1.0)
+ hsl.s = self->saturation;
+
+ g_object_set (self,
+ "hue", hsl.h,
+ "saturation", hsl.s,
+ "lightness", hsl.l * 2.0 - 1.0,
+ NULL);
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gimp_operation_colorize_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationColorize *colorize = GIMP_OPERATION_COLORIZE (operation);
+ gfloat *src = in_buf;
+ gfloat *dest = out_buf;
+ GimpHSL hsl;
+
+ hsl.h = colorize->hue;
+ hsl.s = colorize->saturation;
+
+ while (samples--)
+ {
+ GimpRGB rgb;
+ gfloat lum = GIMP_RGB_LUMINANCE (src[RED],
+ src[GREEN],
+ src[BLUE]);
+
+ if (colorize->lightness > 0)
+ {
+ lum = lum * (1.0 - colorize->lightness);
+
+ lum += 1.0 - (1.0 - colorize->lightness);
+ }
+ else if (colorize->lightness < 0)
+ {
+ lum = lum * (colorize->lightness + 1.0);
+ }
+
+ hsl.l = lum;
+
+ gimp_hsl_to_rgb (&hsl, &rgb);
+
+ /* the code in base/colorize.c would multiply r,b,g with lum,
+ * but this is a bug since it should multiply with 255. We
+ * don't repeat this bug here (this is the reason why the gegl
+ * colorize is brighter than the legacy one).
+ */
+ dest[RED] = rgb.r; /* * lum */
+ dest[GREEN] = rgb.g; /* * lum */
+ dest[BLUE] = rgb.b; /* * lum */
+ dest[ALPHA] = src[ALPHA];
+
+ src += 4;
+ dest += 4;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationcolorize.h b/app/operations/gimpoperationcolorize.h
new file mode 100644
index 0000000..cfb5545
--- /dev/null
+++ b/app/operations/gimpoperationcolorize.h
@@ -0,0 +1,57 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationcolorize.h
+ * Copyright (C) 2007 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_COLORIZE_H__
+#define __GIMP_OPERATION_COLORIZE_H__
+
+
+#include "gimpoperationpointfilter.h"
+
+
+#define GIMP_TYPE_OPERATION_COLORIZE (gimp_operation_colorize_get_type ())
+#define GIMP_OPERATION_COLORIZE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_COLORIZE, GimpOperationColorize))
+#define GIMP_OPERATION_COLORIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_COLORIZE, GimpOperationColorizeClass))
+#define GIMP_IS_OPERATION_COLORIZE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_COLORIZE))
+#define GIMP_IS_OPERATION_COLORIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_COLORIZE))
+#define GIMP_OPERATION_COLORIZE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_COLORIZE, GimpOperationColorizeClass))
+
+
+typedef struct _GimpOperationColorize GimpOperationColorize;
+typedef struct _GimpOperationColorizeClass GimpOperationColorizeClass;
+
+struct _GimpOperationColorize
+{
+ GimpOperationPointFilter parent_instance;
+
+ gdouble hue;
+ gdouble saturation;
+ gdouble lightness;
+};
+
+struct _GimpOperationColorizeClass
+{
+ GimpOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_colorize_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_COLORIZE_H__ */
diff --git a/app/operations/gimpoperationcomposecrop.c b/app/operations/gimpoperationcomposecrop.c
new file mode 100644
index 0000000..25e6247
--- /dev/null
+++ b/app/operations/gimpoperationcomposecrop.c
@@ -0,0 +1,329 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationcomposecrop.c
+ * Copyright (C) 2012 Michael Natterer <mitch@gimp.org>
+ * Copyright (C) 2016 Massimo Valentini <mvalentini@src.gnome.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>
+
+#include "operations-types.h"
+
+#include "gimpoperationcomposecrop.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_X,
+ PROP_Y,
+ PROP_WIDTH,
+ PROP_HEIGHT
+};
+
+
+static void gimp_operation_compose_crop_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_compose_crop_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_operation_compose_crop_prepare (GeglOperation *operation);
+static GeglRectangle gimp_operation_compose_crop_get_required_for_output (GeglOperation *operation,
+ const gchar *input_pad,
+ const GeglRectangle *output_roi);
+static gboolean gimp_operation_compose_crop_parent_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_pad,
+ const GeglRectangle *roi,
+ gint level);
+
+static gboolean gimp_operation_compose_crop_process (GeglOperation *operation,
+ void *in_buf,
+ void *aux_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationComposeCrop, gimp_operation_compose_crop,
+ GEGL_TYPE_OPERATION_POINT_COMPOSER)
+
+#define parent_class gimp_operation_compose_crop_parent_class
+
+
+static void
+gimp_operation_compose_crop_class_init (GimpOperationComposeCropClass *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_compose_crop_set_property;
+ object_class->get_property = gimp_operation_compose_crop_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:compose-crop",
+ "categories", "gimp",
+ "description", "Selectively pick components from src or aux",
+ NULL);
+
+ operation_class->prepare = gimp_operation_compose_crop_prepare;
+ operation_class->get_invalidated_by_change = gimp_operation_compose_crop_get_required_for_output;
+ operation_class->get_required_for_output = gimp_operation_compose_crop_get_required_for_output;
+ operation_class->process = gimp_operation_compose_crop_parent_process;
+
+ point_class->process = gimp_operation_compose_crop_process;
+
+ g_object_class_install_property (object_class, PROP_X,
+ g_param_spec_int ("x",
+ "x",
+ "x",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class, PROP_Y,
+ g_param_spec_int ("y",
+ "y",
+ "y",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class, PROP_WIDTH,
+ g_param_spec_int ("width",
+ "width",
+ "width",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class, PROP_HEIGHT,
+ g_param_spec_int ("height",
+ "height",
+ "height",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_compose_crop_init (GimpOperationComposeCrop *self)
+{
+}
+
+static void
+gimp_operation_compose_crop_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationComposeCrop *self = GIMP_OPERATION_COMPOSE_CROP (object);
+
+ switch (property_id)
+ {
+ case PROP_X:
+ g_value_set_int (value, self->rect.x);
+ break;
+ case PROP_Y:
+ g_value_set_int (value, self->rect.y);
+ break;
+ case PROP_WIDTH:
+ g_value_set_int (value, self->rect.width);
+ break;
+ case PROP_HEIGHT:
+ g_value_set_int (value, self->rect.height);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_compose_crop_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationComposeCrop *self = GIMP_OPERATION_COMPOSE_CROP (object);
+
+ switch (property_id)
+ {
+ case PROP_X:
+ self->rect.x = g_value_get_int (value);
+ break;
+ case PROP_Y:
+ self->rect.y = g_value_get_int (value);
+ break;
+ case PROP_WIDTH:
+ self->rect.width = g_value_get_int (value);
+ break;
+ case PROP_HEIGHT:
+ self->rect.height = g_value_get_int (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_compose_crop_prepare (GeglOperation *operation)
+{
+ const Babl *input_format = gegl_operation_get_source_format (operation, "input");
+ const Babl *aux_format = gegl_operation_get_source_format (operation, "aux");
+ const Babl *format;
+
+ if (input_format)
+ {
+ if (input_format == aux_format)
+ {
+ format = input_format;
+ }
+ else
+ {
+ const Babl *model = babl_format_get_model (input_format);
+
+ if (model == babl_model ("R'G'B'A"))
+ format = babl_format_with_space ("R'G'B'A float", input_format);
+ else
+ format = babl_format_with_space ("RGBA float", input_format);
+ }
+ }
+ else
+ {
+ format = babl_format_with_space ("RGBA float", input_format);
+ }
+
+ gegl_operation_set_format (operation, "input", format);
+ gegl_operation_set_format (operation, "aux", format);
+ gegl_operation_set_format (operation, "output", format);
+}
+
+static GeglRectangle
+gimp_operation_compose_crop_get_required_for_output (GeglOperation *operation,
+ const gchar *input_pad,
+ const GeglRectangle *output_roi)
+{
+ GimpOperationComposeCrop *self = GIMP_OPERATION_COMPOSE_CROP (operation);
+ GeglRectangle result;
+
+ if (! strcmp (input_pad, "input"))
+ gegl_rectangle_intersect (&result, output_roi, &self->rect);
+ else if (! strcmp (input_pad, "aux"))
+ gegl_rectangle_subtract_bounding_box (&result, output_roi, &self->rect);
+ else
+ g_return_val_if_reached (*output_roi);
+
+ return result;
+}
+
+static gboolean
+gimp_operation_compose_crop_parent_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_pad,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationComposeCrop *self = GIMP_OPERATION_COMPOSE_CROP (operation);
+
+ if (gegl_rectangle_contains (&self->rect, roi))
+ {
+ GObject *input;
+
+ input = gegl_operation_context_get_object (context, "input");
+ gegl_operation_context_set_object (context, "output", input);
+
+ return TRUE;
+ }
+ else if (! gegl_rectangle_intersect (NULL, &self->rect, roi))
+ {
+ GObject *aux;
+
+ aux = gegl_operation_context_get_object (context, "aux");
+ gegl_operation_context_set_object (context, "output", aux);
+
+ return TRUE;
+ }
+
+ return GEGL_OPERATION_CLASS (parent_class)->process (operation, context,
+ output_pad, roi, level);
+}
+
+static gboolean
+gimp_operation_compose_crop_process (GeglOperation *operation,
+ void *in_buf,
+ void *aux_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationComposeCrop *self = GIMP_OPERATION_COMPOSE_CROP (operation);
+ const Babl *format = gegl_operation_get_format (operation, "output");
+ gint bpp = babl_format_get_bytes_per_pixel (format);
+ const guchar *in = in_buf;
+ const guchar *aux = aux_buf;
+ guchar *out = out_buf;
+ gint x0, x1;
+ gint y0, y1;
+ gint y;
+
+#define COPY(src, n) \
+ do \
+ { \
+ gint size = (n) * bpp; \
+ \
+ if (src) \
+ memcpy (out, (src), size); \
+ else \
+ memset (out, 0, size); \
+ \
+ in += size; \
+ if (aux) aux += size; \
+ out += size; \
+ } \
+ while (FALSE)
+
+ x0 = CLAMP (self->rect.x, roi->x, roi->x + roi->width);
+ x1 = CLAMP (self->rect.x + self->rect.width, roi->x, roi->x + roi->width);
+
+ y0 = CLAMP (self->rect.y, roi->y, roi->y + roi->height);
+ y1 = CLAMP (self->rect.y + self->rect.height, roi->y, roi->y + roi->height);
+
+ COPY (aux, (y0 - roi->y) * roi->width);
+
+ for (y = y0; y < y1; y++)
+ {
+ COPY (aux, x0 - roi->x);
+ COPY (in, x1 - x0);
+ COPY (aux, roi->x + roi->width - x1);
+ }
+
+ COPY (aux, (roi->y + roi->height - y1) * roi->width);
+
+#undef COPY
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationcomposecrop.h b/app/operations/gimpoperationcomposecrop.h
new file mode 100644
index 0000000..83b7613
--- /dev/null
+++ b/app/operations/gimpoperationcomposecrop.h
@@ -0,0 +1,55 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationcomposecrop.h
+ * Copyright (C) 2012 Michael Natterer <mitch@gimp.org>
+ * Copyright (C) 2016 Massimo Valentini <mvalentini@src.gnome.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/>.
+ */
+
+#ifndef __GIMP_OPERATION_COMPOSE_CROP_H__
+#define __GIMP_OPERATION_COMPOSE_CROP_H__
+
+#include <gegl-plugin.h>
+
+
+#define GIMP_TYPE_OPERATION_COMPOSE_CROP (gimp_operation_compose_crop_get_type ())
+#define GIMP_OPERATION_COMPOSE_CROP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_COMPOSE_CROP, GimpOperationComposeCrop))
+#define GIMP_OPERATION_COMPOSE_CROP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_COMPOSE_CROP, GimpOperationComposeCropClass))
+#define GIMP_IS_OPERATION_COMPOSE_CROP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_COMPOSE_CROP))
+#define GIMP_IS_OPERATION_COMPOSE_CROP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_COMPOSE_CROP))
+#define GIMP_OPERATION_COMPOSE_CROP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_COMPOSE_CROP, GimpOperationComposeCropClass))
+
+
+typedef struct _GimpOperationComposeCrop GimpOperationComposeCrop;
+typedef struct _GimpOperationComposeCropClass GimpOperationComposeCropClass;
+
+struct _GimpOperationComposeCrop
+{
+ GeglOperationPointComposer parent_instance;
+
+ GeglRectangle rect;
+};
+
+struct _GimpOperationComposeCropClass
+{
+ GeglOperationPointComposerClass parent_class;
+};
+
+
+GType gimp_operation_compose_crop_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_COMPOSE_CROP_H__ */
diff --git a/app/operations/gimpoperationcurves.c b/app/operations/gimpoperationcurves.c
new file mode 100644
index 0000000..e83b142
--- /dev/null
+++ b/app/operations/gimpoperationcurves.c
@@ -0,0 +1,118 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationcurves.c
+ * Copyright (C) 2007 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 <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "operations-types.h"
+
+#include "core/gimpcurve.h"
+#include "core/gimpcurve-map.h"
+
+#include "gimpcurvesconfig.h"
+#include "gimpoperationcurves.h"
+
+#include "gimp-intl.h"
+
+
+static gboolean gimp_operation_curves_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationCurves, gimp_operation_curves,
+ GIMP_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_curves_parent_class
+
+
+static void
+gimp_operation_curves_class_init (GimpOperationCurvesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+
+ object_class->set_property = gimp_operation_point_filter_set_property;
+ object_class->get_property = gimp_operation_point_filter_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:curves",
+ "categories", "color",
+ "description", _("Adjust color curves"),
+ NULL);
+
+ point_class->process = gimp_operation_curves_process;
+
+ g_object_class_install_property (object_class,
+ GIMP_OPERATION_POINT_FILTER_PROP_LINEAR,
+ g_param_spec_boolean ("linear",
+ "Linear",
+ "Whether to operate on linear RGB",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ GIMP_OPERATION_POINT_FILTER_PROP_CONFIG,
+ g_param_spec_object ("config",
+ "Config",
+ "The config object",
+ GIMP_TYPE_CURVES_CONFIG,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_curves_init (GimpOperationCurves *self)
+{
+}
+
+static gboolean
+gimp_operation_curves_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationPointFilter *point = GIMP_OPERATION_POINT_FILTER (operation);
+ GimpCurvesConfig *config = GIMP_CURVES_CONFIG (point->config);
+ gfloat *src = in_buf;
+ gfloat *dest = out_buf;
+
+ if (! config)
+ return FALSE;
+
+ gimp_curve_map_pixels (config->curve[0],
+ config->curve[1],
+ config->curve[2],
+ config->curve[3],
+ config->curve[4], src, dest, samples);
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationcurves.h b/app/operations/gimpoperationcurves.h
new file mode 100644
index 0000000..ab097b2
--- /dev/null
+++ b/app/operations/gimpoperationcurves.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationcurves.h
+ * Copyright (C) 2007 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_CURVES_H__
+#define __GIMP_OPERATION_CURVES_H__
+
+
+#include "gimpoperationpointfilter.h"
+
+
+#define GIMP_TYPE_OPERATION_CURVES (gimp_operation_curves_get_type ())
+#define GIMP_OPERATION_CURVES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_CURVES, GimpOperationCurves))
+#define GIMP_OPERATION_CURVES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_CURVES, GimpOperationCurvesClass))
+#define GIMP_IS_OPERATION_CURVES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_CURVES))
+#define GIMP_IS_OPERATION_CURVES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_CURVES))
+#define GIMP_OPERATION_CURVES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_CURVES, GimpOperationCurvesClass))
+
+
+typedef struct _GimpOperationCurves GimpOperationCurves;
+typedef struct _GimpOperationCurvesClass GimpOperationCurvesClass;
+
+struct _GimpOperationCurves
+{
+ GimpOperationPointFilter parent_instance;
+};
+
+struct _GimpOperationCurvesClass
+{
+ GimpOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_curves_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_CURVES_H__ */
diff --git a/app/operations/gimpoperationdesaturate.c b/app/operations/gimpoperationdesaturate.c
new file mode 100644
index 0000000..9f833db
--- /dev/null
+++ b/app/operations/gimpoperationdesaturate.c
@@ -0,0 +1,257 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationdesaturate.c
+ * Copyright (C) 2007 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 <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "operations-types.h"
+
+#include "gimpoperationdesaturate.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_MODE
+};
+
+
+static void gimp_operation_desaturate_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_desaturate_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_operation_desaturate_prepare (GeglOperation *operation);
+static gboolean gimp_operation_desaturate_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationDesaturate, gimp_operation_desaturate,
+ GIMP_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_desaturate_parent_class
+
+
+static void
+gimp_operation_desaturate_class_init (GimpOperationDesaturateClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+
+ object_class->set_property = gimp_operation_desaturate_set_property;
+ object_class->get_property = gimp_operation_desaturate_get_property;
+
+ operation_class->prepare = gimp_operation_desaturate_prepare;
+
+ point_class->process = gimp_operation_desaturate_process;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:desaturate",
+ "categories", "color",
+ "description", _("Turn colors into shades of gray"),
+ NULL);
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_MODE,
+ "mode",
+ _("Mode"),
+ _("Choose shade of gray based on"),
+ GIMP_TYPE_DESATURATE_MODE,
+ GIMP_DESATURATE_LUMINANCE,
+ GIMP_PARAM_STATIC_STRINGS);
+}
+
+static void
+gimp_operation_desaturate_init (GimpOperationDesaturate *self)
+{
+}
+
+static void
+gimp_operation_desaturate_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationDesaturate *desaturate = GIMP_OPERATION_DESATURATE (object);
+
+ switch (property_id)
+ {
+ case PROP_MODE:
+ g_value_set_enum (value, desaturate->mode);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_desaturate_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationDesaturate *desaturate = GIMP_OPERATION_DESATURATE (object);
+
+ switch (property_id)
+ {
+ case PROP_MODE:
+ desaturate->mode = g_value_get_enum (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_desaturate_prepare (GeglOperation *operation)
+{
+ GimpOperationDesaturate *desaturate = GIMP_OPERATION_DESATURATE (operation);
+ const Babl *format = gegl_operation_get_source_format (operation, "input");
+
+ if (desaturate->mode == GIMP_DESATURATE_LUMINANCE)
+ {
+ format = babl_format_with_space ("RGBA float", format);
+ }
+ else
+ {
+ format = babl_format_with_space ("R'G'B'A float", format);
+ }
+
+ gegl_operation_set_format (operation, "input", format);
+ gegl_operation_set_format (operation, "output", format);
+}
+
+static gboolean
+gimp_operation_desaturate_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationDesaturate *desaturate = GIMP_OPERATION_DESATURATE (operation);
+ gfloat *src = in_buf;
+ gfloat *dest = out_buf;
+
+ switch (desaturate->mode)
+ {
+ case GIMP_DESATURATE_LIGHTNESS:
+ /* This is the formula for Lightness in the HSL "bi-hexcone"
+ * model: https://en.wikipedia.org/wiki/HSL_and_HSV
+ */
+ while (samples--)
+ {
+ gfloat min, max, value;
+
+ max = MAX (src[0], src[1]);
+ max = MAX (max, src[2]);
+ min = MIN (src[0], src[1]);
+ min = MIN (min, src[2]);
+
+ value = (max + min) / 2;
+
+ dest[0] = value;
+ dest[1] = value;
+ dest[2] = value;
+ dest[3] = src[3];
+
+ src += 4;
+ dest += 4;
+ }
+ break;
+
+ case GIMP_DESATURATE_LUMA:
+ case GIMP_DESATURATE_LUMINANCE:
+ while (samples--)
+ {
+ gfloat value = GIMP_RGB_LUMINANCE (src[0], src[1], src[2]);
+
+ dest[0] = value;
+ dest[1] = value;
+ dest[2] = value;
+ dest[3] = src[3];
+
+ src += 4;
+ dest += 4;
+ }
+ break;
+
+ case GIMP_DESATURATE_AVERAGE:
+ /* This is the formula for Intensity in the HSI model:
+ * https://en.wikipedia.org/wiki/HSL_and_HSV
+ */
+ while (samples--)
+ {
+ gfloat value = (src[0] + src[1] + src[2]) / 3;
+
+ dest[0] = value;
+ dest[1] = value;
+ dest[2] = value;
+ dest[3] = src[3];
+
+ src += 4;
+ dest += 4;
+ }
+ break;
+
+ case GIMP_DESATURATE_VALUE:
+ /* This is the formula for Value in the HSV model:
+ * https://en.wikipedia.org/wiki/HSL_and_HSV
+ */
+ while (samples--)
+ {
+ gfloat value;
+
+ value = MAX (src[0], src[1]);
+ value = MAX (value, src[2]);
+
+ dest[0] = value;
+ dest[1] = value;
+ dest[2] = value;
+ dest[3] = src[3];
+
+ src += 4;
+ dest += 4;
+ }
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationdesaturate.h b/app/operations/gimpoperationdesaturate.h
new file mode 100644
index 0000000..3117a61
--- /dev/null
+++ b/app/operations/gimpoperationdesaturate.h
@@ -0,0 +1,55 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationdesaturate.h
+ * Copyright (C) 2007 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_DESATURATE_H__
+#define __GIMP_OPERATION_DESATURATE_H__
+
+
+#include "gimpoperationpointfilter.h"
+
+
+#define GIMP_TYPE_OPERATION_DESATURATE (gimp_operation_desaturate_get_type ())
+#define GIMP_OPERATION_DESATURATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_DESATURATE, GimpOperationDesaturate))
+#define GIMP_OPERATION_DESATURATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_DESATURATE, GimpOperationDesaturateClass))
+#define GIMP_IS_OPERATION_DESATURATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_DESATURATE))
+#define GIMP_IS_OPERATION_DESATURATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_DESATURATE))
+#define GIMP_OPERATION_DESATURATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_DESATURATE, GimpOperationDesaturateClass))
+
+
+typedef struct _GimpOperationDesaturate GimpOperationDesaturate;
+typedef struct _GimpOperationDesaturateClass GimpOperationDesaturateClass;
+
+struct _GimpOperationDesaturate
+{
+ GimpOperationPointFilter parent_instance;
+
+ GimpDesaturateMode mode;
+};
+
+struct _GimpOperationDesaturateClass
+{
+ GimpOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_desaturate_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_DESATURATE_H__ */
diff --git a/app/operations/gimpoperationequalize.c b/app/operations/gimpoperationequalize.c
new file mode 100644
index 0000000..119be23
--- /dev/null
+++ b/app/operations/gimpoperationequalize.c
@@ -0,0 +1,248 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationequalize.c
+ * Copyright (C) 2007 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 <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "operations-types.h"
+
+#include "core/gimphistogram.h"
+
+#include "gimpoperationequalize.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_HISTOGRAM
+};
+
+
+static void gimp_operation_equalize_finalize (GObject *object);
+static void gimp_operation_equalize_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_equalize_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static gboolean gimp_operation_equalize_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationEqualize, gimp_operation_equalize,
+ GIMP_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_equalize_parent_class
+
+
+static void
+gimp_operation_equalize_class_init (GimpOperationEqualizeClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+
+ object_class->finalize = gimp_operation_equalize_finalize;
+ object_class->set_property = gimp_operation_equalize_set_property;
+ object_class->get_property = gimp_operation_equalize_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:equalize",
+ "categories", "color",
+ "description", "GIMP Equalize operation",
+ NULL);
+
+ point_class->process = gimp_operation_equalize_process;
+
+ g_object_class_install_property (object_class, PROP_HISTOGRAM,
+ g_param_spec_object ("histogram",
+ "Histogram",
+ "The histogram",
+ GIMP_TYPE_HISTOGRAM,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gimp_operation_equalize_init (GimpOperationEqualize *self)
+{
+ self->values = NULL;
+ self->n_bins = 0;
+}
+
+static void
+gimp_operation_equalize_finalize (GObject *object)
+{
+ GimpOperationEqualize *self = GIMP_OPERATION_EQUALIZE (object);
+
+ g_clear_pointer (&self->values, g_free);
+ g_clear_object (&self->histogram);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_operation_equalize_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationEqualize *self = GIMP_OPERATION_EQUALIZE (object);
+
+ switch (property_id)
+ {
+ case PROP_HISTOGRAM:
+ g_value_set_pointer (value, self->histogram);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_equalize_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationEqualize *self = GIMP_OPERATION_EQUALIZE (object);
+
+ switch (property_id)
+ {
+ case PROP_HISTOGRAM:
+ if (self->histogram)
+ g_object_unref (self->histogram);
+
+ self->histogram = g_value_dup_object (value);
+
+ if (self->histogram)
+ {
+ gdouble pixels;
+ gint n_bins;
+ gint max;
+ gint k;
+
+ n_bins = gimp_histogram_n_bins (self->histogram);
+
+ if ((self->values != NULL) && (self->n_bins != n_bins))
+ {
+ g_free (self->values);
+ self->values = NULL;
+ }
+
+ if (self->values == NULL)
+ {
+ self->values = g_new (gdouble, 3 * n_bins);
+ }
+
+ self->n_bins = n_bins;
+
+ pixels = gimp_histogram_get_count (self->histogram,
+ GIMP_HISTOGRAM_VALUE, 0, n_bins - 1);
+
+ if (gimp_histogram_n_components (self->histogram) == 1 ||
+ gimp_histogram_n_components (self->histogram) == 2)
+ max = 1;
+ else
+ max = 3;
+
+ for (k = 0; k < 3; k++)
+ {
+ gdouble sum = 0;
+ gint i;
+
+ for (i = 0; i < n_bins; i++)
+ {
+ gdouble histi;
+
+ histi = gimp_histogram_get_component (self->histogram, k, i);
+
+ sum += histi;
+
+ self->values[k * n_bins + i] = sum / pixels;
+
+ if (max == 1)
+ {
+ self->values[n_bins + i] = self->values[i];
+ self->values[2 * n_bins + i] = self->values[i];
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static inline float
+gimp_operation_equalize_map (GimpOperationEqualize *self,
+ gint component,
+ gfloat value)
+{
+ gint index;
+ index = component * self->n_bins + \
+ (gint) (CLAMP (value * (self->n_bins - 1), 0.0, self->n_bins - 1));
+
+ return self->values[index];
+}
+
+static gboolean
+gimp_operation_equalize_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationEqualize *self = GIMP_OPERATION_EQUALIZE (operation);
+ gfloat *src = in_buf;
+ gfloat *dest = out_buf;
+
+ while (samples--)
+ {
+ dest[RED] = gimp_operation_equalize_map (self, RED, src[RED]);
+ dest[GREEN] = gimp_operation_equalize_map (self, GREEN, src[GREEN]);
+ dest[BLUE] = gimp_operation_equalize_map (self, BLUE, src[BLUE]);
+ dest[ALPHA] = src[ALPHA];
+
+ src += 4;
+ dest += 4;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationequalize.h b/app/operations/gimpoperationequalize.h
new file mode 100644
index 0000000..ea03b18
--- /dev/null
+++ b/app/operations/gimpoperationequalize.h
@@ -0,0 +1,57 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationequalize.h
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_EQUALIZE_H__
+#define __GIMP_OPERATION_EQUALIZE_H__
+
+
+#include "gimpoperationpointfilter.h"
+
+
+#define GIMP_TYPE_OPERATION_EQUALIZE (gimp_operation_equalize_get_type ())
+#define GIMP_OPERATION_EQUALIZE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_EQUALIZE, GimpOperationEqualize))
+#define GIMP_OPERATION_EQUALIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_EQUALIZE, GimpOperationEqualizeClass))
+#define GIMP_IS_OPERATION_EQUALIZE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_EQUALIZE))
+#define GIMP_IS_OPERATION_EQUALIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_EQUALIZE))
+#define GIMP_OPERATION_EQUALIZE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_EQUALIZE, GimpOperationEqualizeClass))
+
+
+typedef struct _GimpOperationEqualize GimpOperationEqualize;
+typedef struct _GimpOperationEqualizeClass GimpOperationEqualizeClass;
+
+struct _GimpOperationEqualize
+{
+ GimpOperationPointFilter parent_instance;
+
+ GimpHistogram *histogram;
+ gdouble *values;
+ gint n_bins;
+};
+
+struct _GimpOperationEqualizeClass
+{
+ GimpOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_equalize_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_EQUALIZE_H__ */
diff --git a/app/operations/gimpoperationfillsource.c b/app/operations/gimpoperationfillsource.c
new file mode 100644
index 0000000..5811611
--- /dev/null
+++ b/app/operations/gimpoperationfillsource.c
@@ -0,0 +1,254 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationfillsource.c
+ * Copyright (C) 2019 Ell
+ *
+ * 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 <cairo.h>
+#include <gegl-plugin.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "operations-types.h"
+
+#include "core/gimpdrawable.h"
+#include "core/gimpfilloptions.h"
+
+#include "gimpoperationfillsource.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_OPTIONS,
+ PROP_DRAWABLE,
+ PROP_PATTERN_OFFSET_X,
+ PROP_PATTERN_OFFSET_Y,
+};
+
+
+static void gimp_operation_fill_source_dispose (GObject *object);
+static void gimp_operation_fill_source_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_fill_source_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static GeglRectangle gimp_operation_fill_source_get_bounding_box (GeglOperation *operation);
+static void gimp_operation_fill_source_prepare (GeglOperation *operation);
+static gboolean gimp_operation_fill_source_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_pad,
+ const GeglRectangle *result,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationFillSource, gimp_operation_fill_source,
+ GEGL_TYPE_OPERATION_SOURCE)
+
+#define parent_class gimp_operation_fill_source_parent_class
+
+
+static void
+gimp_operation_fill_source_class_init (GimpOperationFillSourceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+
+ object_class->dispose = gimp_operation_fill_source_dispose;
+ object_class->set_property = gimp_operation_fill_source_set_property;
+ object_class->get_property = gimp_operation_fill_source_get_property;
+
+ operation_class->get_bounding_box = gimp_operation_fill_source_get_bounding_box;
+ operation_class->prepare = gimp_operation_fill_source_prepare;
+ operation_class->process = gimp_operation_fill_source_process;
+
+ operation_class->threaded = FALSE;
+ operation_class->cache_policy = GEGL_CACHE_POLICY_NEVER;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:fill-source",
+ "categories", "gimp",
+ "description", "GIMP Fill Source operation",
+ NULL);
+
+ g_object_class_install_property (object_class, PROP_OPTIONS,
+ g_param_spec_object ("options",
+ "Options",
+ "Fill options",
+ GIMP_TYPE_FILL_OPTIONS,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_DRAWABLE,
+ g_param_spec_object ("drawable",
+ "Drawable",
+ "Fill drawable",
+ GIMP_TYPE_DRAWABLE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_PATTERN_OFFSET_X,
+ g_param_spec_int ("pattern-offset-x",
+ "Pattern X-offset",
+ "Pattern X-offset",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_PATTERN_OFFSET_Y,
+ g_param_spec_int ("pattern-offset-y",
+ "Pattern Y-offset",
+ "Pattern Y-offset",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_fill_source_init (GimpOperationFillSource *self)
+{
+}
+
+static void
+gimp_operation_fill_source_dispose (GObject *object)
+{
+ GimpOperationFillSource *fill_source = GIMP_OPERATION_FILL_SOURCE (object);
+
+ g_clear_object (&fill_source->options);
+ g_clear_object (&fill_source->drawable);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_operation_fill_source_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationFillSource *fill_source = GIMP_OPERATION_FILL_SOURCE (object);
+
+ switch (property_id)
+ {
+ case PROP_OPTIONS:
+ g_value_set_object (value, fill_source->options);
+ break;
+
+ case PROP_DRAWABLE:
+ g_value_set_object (value, fill_source->drawable);
+ break;
+
+ case PROP_PATTERN_OFFSET_X:
+ g_value_set_int (value, fill_source->pattern_offset_x);
+ break;
+
+ case PROP_PATTERN_OFFSET_Y:
+ g_value_set_int (value, fill_source->pattern_offset_y);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_fill_source_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationFillSource *fill_source = GIMP_OPERATION_FILL_SOURCE (object);
+
+ switch (property_id)
+ {
+ case PROP_OPTIONS:
+ g_set_object (&fill_source->options, g_value_get_object (value));
+ break;
+
+ case PROP_DRAWABLE:
+ g_set_object (&fill_source->drawable, g_value_get_object (value));
+ break;
+
+ case PROP_PATTERN_OFFSET_X:
+ fill_source->pattern_offset_x = g_value_get_int (value);
+ break;
+
+ case PROP_PATTERN_OFFSET_Y:
+ fill_source->pattern_offset_y = g_value_get_int (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static GeglRectangle
+gimp_operation_fill_source_get_bounding_box (GeglOperation *operation)
+{
+ return gegl_rectangle_infinite_plane ();
+}
+
+static void
+gimp_operation_fill_source_prepare (GeglOperation *operation)
+{
+ GimpOperationFillSource *fill_source = GIMP_OPERATION_FILL_SOURCE (operation);
+ const Babl *format = NULL;
+
+ if (fill_source->options && fill_source->drawable)
+ {
+ format = gimp_fill_options_get_format (fill_source->options,
+ fill_source->drawable);
+ }
+
+ gegl_operation_set_format (operation, "output", format);
+}
+
+static gboolean
+gimp_operation_fill_source_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_pad,
+ const GeglRectangle *result,
+ gint level)
+{
+ GimpOperationFillSource *fill_source = GIMP_OPERATION_FILL_SOURCE (operation);
+
+ if (fill_source->options && fill_source->drawable)
+ {
+ GeglBuffer *buffer;
+ GeglRectangle rect;
+
+ gegl_rectangle_align_to_buffer (
+ &rect, result,
+ gimp_drawable_get_buffer (fill_source->drawable),
+ GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
+
+ buffer = gimp_fill_options_create_buffer (fill_source->options,
+ fill_source->drawable,
+ &rect,
+ fill_source->pattern_offset_x,
+ fill_source->pattern_offset_y);
+
+ gegl_operation_context_take_object (context, "output", G_OBJECT (buffer));
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationfillsource.h b/app/operations/gimpoperationfillsource.h
new file mode 100644
index 0000000..6a9f468
--- /dev/null
+++ b/app/operations/gimpoperationfillsource.h
@@ -0,0 +1,55 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationfillsource.h
+ * Copyright (C) 2019 Ell
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_FILL_SOURCE_H__
+#define __GIMP_OPERATION_FILL_SOURCE_H__
+
+
+#define GIMP_TYPE_OPERATION_FILL_SOURCE (gimp_operation_fill_source_get_type ())
+#define GIMP_OPERATION_FILL_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_FILL_SOURCE, GimpOperationFillSource))
+#define GIMP_OPERATION_FILL_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_FILL_SOURCE, GimpOperationFillSourceClass))
+#define GIMP_IS_OPERATION_FILL_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_FILL_SOURCE))
+#define GIMP_IS_OPERATION_FILL_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_FILL_SOURCE))
+#define GIMP_OPERATION_FILL_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_FILL_SOURCE, GimpOperationFillSourceClass))
+
+
+typedef struct _GimpOperationFillSource GimpOperationFillSource;
+typedef struct _GimpOperationFillSourceClass GimpOperationFillSourceClass;
+
+struct _GimpOperationFillSource
+{
+ GeglOperationSource parent_instance;
+
+ GimpFillOptions *options;
+ GimpDrawable *drawable;
+ gint pattern_offset_x;
+ gint pattern_offset_y;
+};
+
+struct _GimpOperationFillSourceClass
+{
+ GeglOperationSourceClass parent_class;
+};
+
+
+GType gimp_operation_fill_source_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_FILL_SOURCE_H__ */
diff --git a/app/operations/gimpoperationflood.c b/app/operations/gimpoperationflood.c
new file mode 100644
index 0000000..b35fe2e
--- /dev/null
+++ b/app/operations/gimpoperationflood.c
@@ -0,0 +1,1104 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationflood.c
+ * Copyright (C) 2016 Ell
+ *
+ * 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/>.
+ */
+
+
+/* Implementation of the Flood algorithm.
+ * See https://wiki.gimp.org/wiki/Algorithms:Flood for details.
+ */
+
+
+#include "config.h"
+
+#include <string.h> /* For `memcpy()`. */
+
+#include <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "operations-types.h"
+
+#include "gimpoperationflood.h"
+
+
+/* Maximal gap, in pixels, between consecutive dirty ranges, below (and
+ * including) which they are coalesced, at the beginning of the distribution
+ * step.
+ */
+#define GIMP_OPERATION_FLOOD_COALESCE_MAX_GAP 32
+
+
+typedef struct _GimpOperationFloodSegment GimpOperationFloodSegment;
+typedef struct _GimpOperationFloodDirtyRange GimpOperationFloodDirtyRange;
+typedef struct _GimpOperationFloodContext GimpOperationFloodContext;
+
+
+/* A segment. */
+struct _GimpOperationFloodSegment
+{
+ /* A boolean flag indicating whether the image- and ROI-virtual coordinate
+ * systems should be transposed when processing this segment. TRUE iff the
+ * segment is vertical.
+ */
+ guint transpose : 1;
+
+ /* The y-coordinate of the segment, in the ROI-virtual coordinate system. */
+ guint y : 8 * sizeof (guint) - 3;
+ /* The difference between the y-coordinates of the source segment and this
+ * segment, in the ROI-virtual coordinate system. Either -1 or +1 for
+ * ordinary segments, and 0 for seed segments, as a special case.
+ *
+ * Note the use of `signed` as the type specifier. The C standard doesn't
+ * specify the signedness of bit-fields whose type specifier is `int`, or a
+ * typedef-name defined as `int`, such as `gint`.
+ */
+ signed source_y_delta : 2;
+
+ /* The x-coordinates of the first and last pixels of the segment, in the ROI-
+ * virtual coordinate system. Note that this is a closed range:
+ * [x[0], x[1]].
+ */
+ gint x[2];
+};
+/* Make sure the maximal image dimension fits in
+ * `GimpOperationFloodSegment::y`.
+ */
+G_STATIC_ASSERT (GIMP_MAX_IMAGE_SIZE <= (1 << (8 * sizeof (guint) - 3)));
+
+/* A dirty range of the current segment. */
+struct _GimpOperationFloodDirtyRange
+{
+ /* A boolean flag indicating whether the range was extended, or its existing
+ * pixels were modified, during the horizontal propagation step.
+ */
+ gboolean modified;
+
+ /* The x-coordinates of the first and last pixels of the range, in the ROI-
+ * virtual coordinate system. Note that this is a closed range:
+ * [x[0], x[1]].
+ */
+ gint x[2];
+};
+
+/* Common parameters for the various parts of the algorithm. */
+struct _GimpOperationFloodContext
+{
+ /* Input image. */
+ GeglBuffer *input;
+ /* Input image format. */
+ const Babl *input_format;
+ /* Output image. */
+ GeglBuffer *output;
+ /* Output image format. */
+ const Babl *output_format;
+
+ /* Region of interset. */
+ GeglRectangle roi;
+
+ /* Current segment. */
+ GimpOperationFloodSegment segment;
+
+ /* The following arrays hold the ground- and water-level of the current- and
+ * source-segments. The vertical- and horizontal-propagation steps don't
+ * generally access the input and output GEGL buffers directly, but rather
+ * read from, and write to, these arrays, for efficiency. These arrays are
+ * read-from, and written-to, the corresponding GEGL buffers before and after
+ * these steps.
+ */
+
+ /* Ground level of the current segment, indexed by x-coordinate in the ROI-
+ * virtual coordinate system. Only valid inside the range
+ * `[segment.x[0], segment.x[1]]`.
+ */
+ gfloat *ground;
+ /* Water level of the current segment, indexed by x-coordinate in the ROI-
+ * virtual coordinate system. Initially only valid inside the range
+ * `[segment.x[0], segment.x[1]]`, but may be written-to outside this range
+ * during horizontal propagation, if the dirty ranges are extended past the
+ * bounds of the segment.
+ */
+ gfloat *water;
+ /* Water level of the source segment, indexed by x-coordinate in the ROI-
+ * virtual coordinate system. Only valid inside the range
+ * `[segment.x[0], segment.x[1]]`.
+ */
+ gfloat *source_water;
+
+ /* A common buffer for the water level of the current- and source-segments.
+ * `water` and `source_water` are pointers into this buffer. This buffer is
+ * used as an optimization, in order to read the water level of both segments
+ * from the output GEGL buffer in a single call, and is otherwise not used
+ * directly (`water` and `source_water` are used to access the water level
+ * instead.)
+ */
+ gfloat *water_buffer;
+};
+
+
+static void gimp_operation_flood_prepare (GeglOperation *operation);
+static GeglRectangle gimp_operation_flood_get_required_for_output (GeglOperation *self,
+ const gchar *input_pad,
+ const GeglRectangle *roi);
+static GeglRectangle gimp_operation_flood_get_cached_region (GeglOperation *self,
+ const GeglRectangle *roi);
+
+static void gimp_operation_flood_process_push (GQueue *queue,
+ gboolean transpose,
+ gint y,
+ gint source_y_delta,
+ gint x0,
+ gint x1);
+static void gimp_operation_flood_process_seed (GQueue *queue,
+ const GeglRectangle *roi);
+static void gimp_operation_flood_process_transform_rect (const GimpOperationFloodContext *ctx,
+ GeglRectangle *dest,
+ const GeglRectangle *src);
+static void gimp_operation_flood_process_fetch (GimpOperationFloodContext *ctx);
+static gint gimp_operation_flood_process_propagate_vertical (GimpOperationFloodContext *ctx,
+ GimpOperationFloodDirtyRange *dirty_ranges);
+static void gimp_operation_flood_process_propagate_horizontal (GimpOperationFloodContext *ctx,
+ gint dir,
+ GimpOperationFloodDirtyRange *dirty_ranges,
+ gint range_count);
+static gint gimp_operation_flood_process_coalesce (const GimpOperationFloodContext *ctx,
+ GimpOperationFloodDirtyRange *dirty_ranges,
+ gint range_count,
+ gint gap);
+static void gimp_operation_flood_process_commit (const GimpOperationFloodContext *ctx,
+ const GimpOperationFloodDirtyRange *dirty_ranges,
+ gint range_count);
+static void gimp_operation_flood_process_distribute (const GimpOperationFloodContext *ctx,
+ GQueue *queue,
+ const GimpOperationFloodDirtyRange *dirty_ranges,
+ gint range_count);
+static gboolean gimp_operation_flood_process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationFlood, gimp_operation_flood,
+ GEGL_TYPE_OPERATION_FILTER)
+
+#define parent_class gimp_operation_flood_parent_class
+
+
+/* GEGL graph for the test case. */
+static const gchar* reference_xml = "<?xml version='1.0' encoding='UTF-8'?>"
+"<gegl>"
+"<node operation='gimp:flood'> </node>"
+"<node operation='gegl:load'>"
+" <params>"
+" <param name='path'>flood-input.png</param>"
+" </params>"
+"</node>"
+"</gegl>";
+
+
+static void
+gimp_operation_flood_class_init (GimpOperationFloodClass *klass)
+{
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
+
+ /* The input and output buffers must be different, since we generally need to
+ * be able to access the input-image values after having written to the
+ * output buffer.
+ */
+ operation_class->want_in_place = FALSE;
+ /* We don't want `GeglOperationFilter` to split the image across multiple
+ * threads, since this operation depends on, and affects, the image as a
+ * whole.
+ */
+ operation_class->threaded = FALSE;
+ /* Note that both of these options are the default; we set them here for
+ * explicitness.
+ */
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:flood",
+ "categories", "gimp",
+ "description", "GIMP Flood operation",
+ "reference", "https://wiki.gimp.org/wiki/Algorithms:Flood",
+ "reference-image", "flood-output.png",
+ "reference-composition", reference_xml,
+ NULL);
+
+ operation_class->prepare = gimp_operation_flood_prepare;
+ operation_class->get_required_for_output = gimp_operation_flood_get_required_for_output;
+ operation_class->get_cached_region = gimp_operation_flood_get_cached_region;
+
+ filter_class->process = gimp_operation_flood_process;
+}
+
+static void
+gimp_operation_flood_init (GimpOperationFlood *self)
+{
+}
+
+static void
+gimp_operation_flood_prepare (GeglOperation *operation)
+{
+ const Babl *space = gegl_operation_get_source_space (operation, "input");
+ gegl_operation_set_format (operation, "input", babl_format_with_space ("Y float", space));
+ gegl_operation_set_format (operation, "output", babl_format_with_space ("Y float", space));
+}
+
+static GeglRectangle
+gimp_operation_flood_get_required_for_output (GeglOperation *self,
+ const gchar *input_pad,
+ const GeglRectangle *roi)
+{
+ return *gegl_operation_source_get_bounding_box (self, "input");
+}
+
+static GeglRectangle
+gimp_operation_flood_get_cached_region (GeglOperation *self,
+ const GeglRectangle *roi)
+{
+ return *gegl_operation_source_get_bounding_box (self, "input");
+}
+
+
+/* Pushes a single segment into the queue. */
+static void
+gimp_operation_flood_process_push (GQueue *queue,
+ gboolean transpose,
+ gint y,
+ gint source_y_delta,
+ gint x0,
+ gint x1)
+{
+ GimpOperationFloodSegment *segment;
+
+ segment = g_slice_new (GimpOperationFloodSegment);
+
+ segment->transpose = transpose;
+ segment->y = y;
+ segment->source_y_delta = source_y_delta;
+ segment->x[0] = x0;
+ segment->x[1] = x1;
+
+ g_queue_push_tail (queue, segment);
+}
+
+/* Pushes the seed segments into the queue. Recall that the seed segments are
+ * indicated by having their `source_y_delta` field equal 0.
+ *
+ * `roi` is given in the image-physical coordinate system.
+ */
+static void
+gimp_operation_flood_process_seed (GQueue *queue,
+ const GeglRectangle *roi)
+{
+ if (roi->width == 0 || roi->height == 0)
+ return;
+
+ /* Top edge. */
+ gimp_operation_flood_process_push (queue,
+ /* transpose = */ FALSE,
+ /* y = */ 0,
+ /* source_y_delta = */ 0,
+ /* x0 = */ 0,
+ /* x1 = */ roi->width - 1);
+
+ if (roi->height == 1)
+ return;
+
+ /* Bottom edge. */
+ gimp_operation_flood_process_push (queue,
+ /* transpose = */ FALSE,
+ /* y = */ roi->height - 1,
+ /* source_y_delta = */ 0,
+ /* x0 = */ 0,
+ /* x1 = */ roi->width - 1);
+
+ if (roi->height == 2)
+ return;
+
+ /* Left edge. */
+ gimp_operation_flood_process_push (queue,
+ /* transpose = */ TRUE,
+ /* y = */ 0,
+ /* source_y_delta = */ 0,
+ /* x0 = */ 1,
+ /* x1 = */ roi->height - 2);
+
+ if (roi->width == 1)
+ return;
+
+ /* Right edge. */
+ gimp_operation_flood_process_push (queue,
+ /* transpose = */ TRUE,
+ /* y = */ roi->width - 1,
+ /* source_y_delta = */ 0,
+ /* x0 = */ 1,
+ /* x1 = */ roi->height - 2);
+}
+
+/* Transforms a `GeglRectangle` between the image-physical and image-virtual
+ * coordinate systems, in either direction, based on the attributes of the
+ * current segment (namely, its `transpose` flag.)
+ *
+ * Takes the input rectangle through `src`, and stores the result in `dest`.
+ * Both parameters may refer to the same object.
+ */
+static void
+gimp_operation_flood_process_transform_rect (const GimpOperationFloodContext *ctx,
+ GeglRectangle *dest,
+ const GeglRectangle *src)
+{
+ if (! ctx->segment.transpose)
+ *dest = *src;
+ else
+ {
+ gint temp;
+
+ temp = src->x;
+ dest->x = src->y;
+ dest->y = temp;
+
+ temp = src->width;
+ dest->width = src->height;
+ dest->height = temp;
+ }
+}
+
+/* Reads the ground- and water-level for the current- and source-segments from
+ * the GEGL buffers into the corresponding arrays. Sets up the `water` and
+ * `source_water` pointers of `ctx` to point to the right location in
+ * `water_buffer`.
+ */
+static void
+gimp_operation_flood_process_fetch (GimpOperationFloodContext *ctx)
+{
+ /* Image-virtual and image-physical rectangles, respectively. */
+ GeglRectangle iv_rect, ip_rect;
+
+ /* Set the horizontal extent of the rectangle to span the entire segment. */
+ iv_rect.x = ctx->roi.x + ctx->segment.x[0];
+ iv_rect.width = ctx->segment.x[1] - ctx->segment.x[0] + 1;
+
+ /* For reading the water level, we treat ordinary (non-seed) and seed
+ * segments differently.
+ */
+ if (ctx->segment.source_y_delta != 0)
+ {
+ /* Ordinary segment. */
+
+ /* We set the vertical extent of the rectangle to span both the current-
+ * and the source-segments, and set the `water` and `source_water`
+ * pointers to point to two consecutive rows of the `water_buffer` array
+ * (the y-coordinate of the rectangle, and which row is above which,
+ * depends on whether the source segment is above, or below, the current
+ * one.)
+ */
+ if (ctx->segment.source_y_delta < 0)
+ {
+ iv_rect.y = ctx->roi.y + ctx->segment.y - 1;
+ ctx->water = ctx->water_buffer + ctx->roi.width;
+ ctx->source_water = ctx->water_buffer;
+ }
+ else
+ {
+ iv_rect.y = ctx->roi.y + ctx->segment.y;
+ ctx->water = ctx->water_buffer;
+ ctx->source_water = ctx->water_buffer + ctx->roi.width;
+ }
+ iv_rect.height = 2;
+
+ /* Transform `iv_rect` to the image-physical coordinate system, and store
+ * the result in `ip_rect`.
+ */
+ gimp_operation_flood_process_transform_rect (ctx, &ip_rect, &iv_rect);
+
+ /* Read the water level from the output GEGL buffer into `water_buffer`.
+ *
+ * Notice the stride: If the current segment is horizontal, then we're
+ * reading a pair of rows directly into the correct locations inside
+ * `water_buffer` (i.e., `water` and `source_water`). On the other hand,
+ * if the current segment is vertical, then we're reading a pair of
+ * *columns*; we set the stride to 2-pixels so that the current- and
+ * source-water levels are interleaved in `water_buffer`, and reorder
+ * them below.
+ */
+ gegl_buffer_get (ctx->output, &ip_rect, 1.0, ctx->output_format,
+ ctx->water_buffer + ctx->segment.x[0],
+ sizeof (gfloat) *
+ (ctx->segment.transpose ? 2 : ctx->roi.width),
+ GEGL_ABYSS_NONE);
+
+ /* As mentioned above, if the current segment is vertical, then the
+ * water levels of the current- and source-segments are interleaved in
+ * `water_buffer`. We deinterleave the water levels into `water` and
+ * `source_water`, using the yet-to-be-written-to `ground` array as a
+ * temporary buffer, as necessary.
+ */
+ if (ctx->segment.transpose)
+ {
+ const gfloat *src;
+ gfloat *dest1, *dest2, *temp;
+ gint size, temp_size;
+ gint i;
+
+ src = ctx->water_buffer + ctx->segment.x[0];
+
+ dest1 = ctx->water_buffer + ctx->segment.x[0];
+ dest2 = ctx->water_buffer + ctx->roi.width + ctx->segment.x[0];
+ temp = ctx->ground;
+
+ size = ctx->segment.x[1] - ctx->segment.x[0] + 1;
+ temp_size = MAX (0, 2 * size - ctx->roi.width);
+
+ for (i = 0; i < temp_size; i++)
+ {
+ dest1[i] = src[2 * i];
+ temp[i] = src[2 * i + 1];
+ }
+ for (; i < size; i++)
+ {
+ dest1[i] = src[2 * i];
+ dest2[i] = src[2 * i + 1];
+ }
+
+ memcpy (dest2, temp, sizeof (gfloat) * temp_size);
+ }
+ }
+ else
+ {
+ /* Seed segment. */
+
+ gint x;
+
+ /* Set the `water` and `source_water` pointers to point to consecutive
+ * rows of the `water_buffer` array.
+ */
+ ctx->water = ctx->water_buffer;
+ ctx->source_water = ctx->water_buffer + ctx->roi.width;
+
+ /* Set the vertical extent of the rectangle to span a the current
+ * segment's row.
+ */
+ iv_rect.y = ctx->roi.y + ctx->segment.y;
+ iv_rect.height = 1;
+
+ /* Transform `iv_rect` to the image-physical coordinate system, and store
+ * the result in `ip_rect`.
+ */
+ gimp_operation_flood_process_transform_rect (ctx, &ip_rect, &iv_rect);
+
+ /* Read the water level of the current segment from the output GEGL
+ * buffer into `water`.
+ */
+ gegl_buffer_get (ctx->output, &ip_rect, 1.0, ctx->output_format,
+ ctx->water + ctx->segment.x[0],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ /* Initialize `source_water` to 0, as this is a seed segment. */
+ for (x = ctx->segment.x[0]; x <= ctx->segment.x[1]; x++)
+ ctx->source_water[x] = 0.0;
+ }
+
+ /* Set the vertical extent of the rectangle to span a the current segment's
+ * row.
+ */
+ iv_rect.y = ctx->roi.y + ctx->segment.y;
+ iv_rect.height = 1;
+
+ /* Transform `iv_rect` to the image-physical coordinate system, and store the
+ * result in `ip_rect`.
+ */
+ gimp_operation_flood_process_transform_rect (ctx, &ip_rect, &iv_rect);
+
+ /* Read the ground level of the current segment from the input GEGL buffer
+ * into `ground`.
+ */
+ gegl_buffer_get (ctx->input, &ip_rect, 1.0, ctx->input_format,
+ ctx->ground + ctx->segment.x[0],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+}
+
+/* Performs the vertical propagation step of the algorithm. Writes the dirty
+ * ranges to the `dirty_ranges` parameter, and returns the number of dirty
+ * ranges as the function's result.
+ */
+static gint
+gimp_operation_flood_process_propagate_vertical (GimpOperationFloodContext *ctx,
+ GimpOperationFloodDirtyRange *dirty_ranges)
+{
+ GimpOperationFloodDirtyRange *range = dirty_ranges;
+ gint x;
+
+ for (x = ctx->segment.x[0]; x <= ctx->segment.x[1]; x++)
+ {
+ /* Scan the segment until we find a pixel whose water level needs to be
+ * updated.
+ */
+ if (ctx->source_water[x] < ctx->water[x] &&
+ ctx->ground[x] < ctx->water[x])
+ {
+ /* Compute and update the water level. */
+ gfloat level = MAX (ctx->source_water[x], ctx->ground[x]);
+
+ ctx->water[x] = level;
+
+ /* Start a new dirty range at the current pixel. */
+ range->x[0] = x;
+ range->modified = FALSE;
+
+ for (x++; x <= ctx->segment.x[1]; x++)
+ {
+ /* Keep scanning the segment while the water level of consecutive
+ * pixels needs to be updated.
+ */
+ if (ctx->source_water[x] < ctx->water[x] &&
+ ctx->ground[x] < ctx->water[x])
+ {
+ /* Compute and update the water level. */
+ gfloat other_level = MAX (ctx->source_water[x],
+ ctx->ground[x]);
+
+ ctx->water[x] = other_level;
+
+ /* If the water level of the current pixel, `other_level`,
+ * equals the water level of the current dirty range,
+ * `level`, we keep scanning, making the current pixel part
+ * of the current range. On the other hand, if the current
+ * pixel's water level is different than the that of the
+ * current range, we finalize the range, and start a new one
+ * at the current pixel.
+ */
+ if (other_level != level)
+ {
+ range->x[1] = x - 1;
+ range++;
+
+ range->x[0] = x;
+ range->modified = FALSE;
+ level = other_level;
+ }
+ }
+ else
+ break;
+ }
+
+ /* Finalize the current dirty range. */
+ range->x[1] = x - 1;
+ range++;
+
+ /* Make sure we don't over-increment `x` on the continuation of the
+ * loop.
+ */
+ if (x > ctx->segment.x[1])
+ break;
+ }
+ }
+
+ /* Return the number of dirty ranges. */
+ return range - dirty_ranges;
+}
+
+/* Performs a single pass of the horizontal propagation step of the algorithm.
+ * `dir` controls the direction of the pass: either +1 for a left-to-right
+ * pass, or -1 for a right-to-left pass. The dirty ranges are passed through
+ * the `dirty_ranges` array (and their number in `range_count`), and are
+ * modified in-place.
+ */
+static void
+gimp_operation_flood_process_propagate_horizontal (GimpOperationFloodContext *ctx,
+ gint dir,
+ GimpOperationFloodDirtyRange *dirty_ranges,
+ gint range_count)
+{
+ /* The index of the terminal (i.e., "`dir`-most") component of the `x[]`
+ * array of `GimpOperationFloodSegment` and `GimpOperationFloodDirtyRange`,
+ * based on the scan direction. Equals 1 (i.e., the right component) when
+ * `dir` is +1 (i.e., left-to-right), and equals 0 (i.e., the left component)
+ * when `dir` is -1 (i.e., right-to-left).
+ */
+ gint x_component;
+ /* One-past the final x-coordinate of the ROI, in the ROI-virtual coordinate
+ * system, based on the scan direction. That is, the x-coordinate of the
+ * pixel to the right of the rightmost pixel, for a left-to-right scan, and
+ * of the pixel to the left of the leftmost pixel, for a right-to-left scan.
+ */
+ gint roi_lim;
+ /* One-past the final x-coordinate of the segment, in the ROI-virtual
+ * coordinate system, based on the scan direction, in a similar fashion to
+ * `roi_lim`.
+ */
+ gint segment_lim;
+ /* The indices of the first, and one-past-the-last dirty ranges, based on the
+ * direction of the scan. Recall that when scanning right-to-left, we
+ * iterate over the ranges in reverse.
+ */
+ gint first_range, last_range;
+ /* Index of the current dirty range. */
+ gint range_index;
+ /* Image-virtual and image-physical rectangles, respectively. */
+ GeglRectangle iv_rect, ip_rect;
+
+ /* Initialize the above variables based on the scan direction. */
+ if (dir > 0)
+ {
+ /* Left-to-right. */
+ x_component = 1;
+ roi_lim = ctx->roi.width;
+ first_range = 0;
+ last_range = range_count;
+ }
+ else
+ {
+ /* Right-to-left. */
+ x_component = 0;
+ roi_lim = -1;
+ first_range = range_count - 1;
+ last_range = -1;
+ }
+ segment_lim = ctx->segment.x[x_component] + dir;
+
+ /* We loop over the dirty ranges, in the direction of the scan. For each
+ * range, we iterate over the pixels, in the scan direction, starting at the
+ * outer edge of the range, and update the water level, considering only the
+ * water level of the previous and current pixels, until we arrive at a pixel
+ * whose water level remains the same, at which point we move to the next
+ * range, as described in the algorithm overview.
+ */
+ for (range_index = first_range;
+ range_index != last_range;
+ range_index += dir)
+ {
+ /* Current dirty range. */
+ GimpOperationFloodDirtyRange *range;
+ /* Current pixel, in the ROI-virtual coordinate system. */
+ gint x;
+ /* We use `level` to compute the water level of the current pixel. At
+ * the beginning of each iteration, it holds the water level of the
+ * previous pixel.
+ */
+ gfloat level;
+ /* The `inside` flag indicates whether `x` is inside the current segment.
+ * Recall that we may iterate past the bounds of the current segment, in
+ * which case we need to read the ground- and water-levels from the GEGL
+ * buffers directly, instead of the corresponding arrays.
+ */
+ gboolean inside;
+ /* Loop limit. */
+ gint lim;
+
+ range = &dirty_ranges[range_index];
+ /* Last x-coordinate of the range, in the direction of the scan. */
+ x = range->x[x_component];
+ /* We start iterating on the pixel after `x`; initialize `level` to the
+ * water level of the previous pixel.
+ */
+ level = ctx->water[x];
+ /* The ranges produced by the vertical propagation step are all within
+ * the bounds of the segment; the horizontal propagation step may only
+ * extend them in the direction of the scan. Therefore, on both passes
+ * of the horizontal propagation step, the last pixel of each range, in
+ * the direction of the scan, is initially inside the segment.
+ */
+ inside = TRUE;
+ /* If this isn't the last range, break the loop at the beginning of the
+ * next range. Otherwise, break the loop at the edge of the ROI.
+ */
+ if (range_index + dir != last_range)
+ lim = (range + dir)->x[1 - x_component];
+ else
+ lim = roi_lim;
+
+ /* Loop over the pixels between the edge of the current range, and the
+ * beginning of the next range (or the edge of the ROI).
+ */
+ for (x += dir; x != lim; x += dir)
+ {
+ gfloat ground_level, water_level;
+
+ /* Recall that `segment_lim` is one-past the last pixel of the
+ * segment. If we hit it, we've gone outside the segment bounds.
+ */
+ if (x == segment_lim)
+ {
+ inside = FALSE;
+ /* Initialize the rectangle to sample pixels directly from the
+ * GEGL buffers.
+ */
+ iv_rect.y = ctx->roi.y + ctx->segment.y;
+ iv_rect.width = 1;
+ iv_rect.height = 1;
+ }
+
+ /* If we're inside the segment, read the ground- and water-levels
+ * from the corresponding arrays; otherwise, read them from the GEGL
+ * buffers directly. Note that, on each pass, we may only write to
+ * pixels outside the segment *in direction of the scan* (in which
+ * case, the new values are written to the `water` array, but not
+ * directly to the output GEGL buffer), hence, when reading from the
+ * GEGL buffers, there's no danger of reading stale values, that were
+ * changed on the previous pass.
+ */
+ if (inside)
+ {
+ ground_level = ctx->ground[x];
+ water_level = ctx->water[x];
+ }
+ else
+ {
+ iv_rect.x = ctx->roi.x + x;
+
+ /* Transform `iv_rect` to the image-physical coordinate system,
+ * and store the result in `ip_rect`.
+ */
+ gimp_operation_flood_process_transform_rect (ctx,
+ &ip_rect, &iv_rect);
+
+ /* Read the current pixel's ground level. */
+ gegl_buffer_get (ctx->input, &ip_rect, 1.0, ctx->input_format,
+ &ground_level,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ /* Read the current pixel's water level. */
+ gegl_buffer_get (ctx->output, &ip_rect, 1.0, ctx->output_format,
+ &water_level,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+
+ /* The new water level is the maximum of the current ground level,
+ * and the minimum of the current and previous water levels. Recall
+ * that `level` holds the previous water level, and that the current
+ * water level is never less than the ground level.
+ */
+ if (level < ground_level)
+ level = ground_level;
+ if (level < water_level)
+ {
+ /* The water level changed. Update the current pixel, and set
+ * the `modified` flag of the current range, since it will be
+ * extended to include the current pixel.
+ */
+ ctx->water[x] = level;
+ range->modified = TRUE;
+ }
+ else
+ /* The water level stayed the same. Break the loop. */
+ break;
+ }
+
+ /* Extend the current dirty range to include the last modified pixel, if
+ * any.
+ */
+ range->x[x_component] = x - dir;
+
+ /* If we stopped the loop before hitting the edge of the next range, or
+ * if we're at the last range, continue to the next range (or quit).
+ */
+ if (x != lim || range_index + dir == last_range)
+ continue;
+
+ /* If we hit the edge of the next range, we keep propagating the changes
+ * *inside* the next range, until we hit its other edge, or until the
+ * water level stays the same.
+ */
+ range += dir;
+ lim = range->x[x_component] + dir;
+
+ for (; x != lim; x += dir)
+ {
+ /* Note that we're necessarily inside the segment right now, since
+ * the only range that could have been extended past the edge of the
+ * segment by the previous pass, is the first range of the current
+ * pass, while the range we're currently inside is at least the
+ * second.
+ */
+ if (level < ctx->ground[x])
+ level = ctx->ground[x];
+ if (level < ctx->water[x])
+ {
+ ctx->water[x] = level;
+ /* Set the `modified` flag of the range, since the water level of
+ * its existing pixels changed.
+ */
+ range->modified = TRUE;
+ }
+ else
+ break;
+ }
+ }
+}
+
+/* Coalesces consecutive dirty ranges that are separated by a gap less-than or
+ * equal-to `max_gap`, in-place, and returns the new number of ranges.
+ */
+static gint
+gimp_operation_flood_process_coalesce (const GimpOperationFloodContext *ctx,
+ GimpOperationFloodDirtyRange *dirty_ranges,
+ gint range_count,
+ gint max_gap)
+{
+ /* First and last ranges to coalesce, respectively. */
+ const GimpOperationFloodDirtyRange *first_range, *last_range;
+ /* Destination range. */
+ GimpOperationFloodDirtyRange *range = dirty_ranges;
+
+ for (first_range = dirty_ranges;
+ first_range != dirty_ranges + range_count;
+ first_range++)
+ {
+ /* The `modified` flag of the coalesced range -- the logical-OR of the
+ * `modified` flags of the individual ranges.
+ */
+ gboolean modified = first_range->modified;
+
+ /* Find all consecutive ranges with a small-enough gap. */
+ for (last_range = first_range;
+ last_range + 1 != dirty_ranges + range_count;
+ last_range++)
+ {
+ if ((last_range + 1)->x[0] - last_range->x[1] > max_gap)
+ break;
+
+ modified |= (last_range + 1)->modified;
+ }
+
+ /* Write the coalesced range, or copy the current range, to the
+ * destination range.
+ */
+ if (first_range != last_range || first_range != range)
+ {
+ range->x[0] = first_range->x[0];
+ range->x[1] = last_range->x[1];
+ range->modified = modified;
+ }
+
+ first_range = last_range;
+ range++;
+ }
+
+ /* Return the new range count. */
+ return range - dirty_ranges;
+}
+
+/* Writes the updated water level of the dirty ranges back to the output GEGL
+ * buffer.
+ */
+static void
+gimp_operation_flood_process_commit (const GimpOperationFloodContext *ctx,
+ const GimpOperationFloodDirtyRange *dirty_ranges,
+ gint range_count)
+{
+ const GimpOperationFloodDirtyRange *range;
+ /* Image-virtual and image-physical rectangles, respectively. */
+ GeglRectangle iv_rect, ip_rect;
+
+ /* Set the vertical extent of the rectangle to span a the current segment's
+ * row.
+ */
+ iv_rect.y = ctx->roi.y + ctx->segment.y;
+ iv_rect.height = 1;
+
+ for (range = dirty_ranges; range != dirty_ranges + range_count; range++)
+ {
+ /* Set the horizontal extent of the rectangle to span the dirty range. */
+ iv_rect.x = ctx->roi.x + range->x[0];
+ iv_rect.width = range->x[1] - range->x[0] + 1;
+
+ /* Transform `iv_rect` to the image-physical coordinate system, and store
+ * the result in `ip_rect`.
+ */
+ gimp_operation_flood_process_transform_rect (ctx, &ip_rect, &iv_rect);
+
+ /* Write the updated water level to the output GEGL buffer. */
+ gegl_buffer_set (ctx->output, &ip_rect, 0, ctx->output_format,
+ ctx->water + range->x[0],
+ GEGL_AUTO_ROWSTRIDE);
+ }
+}
+
+/* Pushes the new segments, corresponding to the dirty ranges of the current
+ * segment, into the queue.
+ */
+static void
+gimp_operation_flood_process_distribute (const GimpOperationFloodContext *ctx,
+ GQueue *queue,
+ const GimpOperationFloodDirtyRange *dirty_ranges,
+ gint range_count)
+{
+ const GimpOperationFloodDirtyRange *range;
+ static const gint y_deltas[] = {-1, +1};
+ gint i;
+
+ /* For each neighboring row... */
+ for (i = 0; i < G_N_ELEMENTS (y_deltas); i++)
+ {
+ /* The difference between the negihboring row's y-coordinate and the
+ * current row's y-corindate, in the ROI-virtual coordinate system.
+ */
+ gint y_delta = y_deltas[i];
+ /* The negihboring row's y-coordinate in the ROI-virtual coordinate
+ * system.
+ */
+ gint y = ctx->segment.y + y_delta;
+
+ /* If the neighboring row is outside the ROI, skip it. */
+ if (y < 0 || y >= ctx->roi.height)
+ continue;
+
+ /* For each dirty range... */
+ for (range = dirty_ranges; range != dirty_ranges + range_count; range++)
+ {
+ /* If the range was modified during horizontal propagation, or if the
+ * neighboring row is not the source segment's row... (note that the
+ * latter is always true for seed segments.)
+ */
+ if (range->modified || y_delta != ctx->segment.source_y_delta)
+ {
+ /* Push a new segment into the queue, spanning the same pixels as
+ * the dirty range on the neighboring row, using the current row
+ * as its source segment.
+ */
+ gimp_operation_flood_process_push (queue,
+ ctx->segment.transpose,
+ y,
+ -y_delta,
+ range->x[0],
+ range->x[1]);
+ }
+ }
+ }
+}
+
+/* Main algorithm. */
+static gboolean
+gimp_operation_flood_process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *roi,
+ gint level)
+{
+ const Babl *input_format = gegl_operation_get_format (operation, "input");
+ const Babl *output_format = gegl_operation_get_format (operation, "output");
+ GeglColor *color;
+ gint max_size;
+ GimpOperationFloodContext ctx;
+ GimpOperationFloodDirtyRange *dirty_ranges;
+ GQueue *queue;
+
+ /* Make sure the input- and output-buffers are different. */
+ g_return_val_if_fail (input != output, FALSE);
+
+ /* Make sure the ROI is small enough for the `GimpOperationFloodSegment::y`
+ * field.
+ */
+ g_return_val_if_fail (roi->width <= GIMP_MAX_IMAGE_SIZE &&
+ roi->height <= GIMP_MAX_IMAGE_SIZE, FALSE);
+
+ ctx.input = input;
+ ctx.input_format = input_format;
+ ctx.output = output;
+ ctx.output_format = output_format;
+
+ /* All buffers need to have enough capacity to process a full row, or a full
+ * column, since, when processing vertical segments, we treat the image as
+ * transposed.
+ */
+ max_size = MAX (roi->width, roi->height);
+ ctx.ground = g_new (gfloat, max_size);
+ /* The `water_buffer` array needs to be able to hold two rows (or columns). */
+ ctx.water_buffer = g_new (gfloat, 2 * max_size);
+ dirty_ranges = g_new (GimpOperationFloodDirtyRange, max_size);
+
+ /* Initialize the water level to 1 everywhere. */
+ color = gegl_color_new ("#fff");
+ gegl_buffer_set_color (output, roi, color);
+ g_object_unref (color);
+
+ /* Create the queue and push the seed segments. */
+ queue = g_queue_new ();
+ gimp_operation_flood_process_seed (queue, roi);
+
+ /* While there are segments to process in the queue... */
+ while (! g_queue_is_empty (queue))
+ {
+ GimpOperationFloodSegment *segment;
+ gint range_count;
+
+ /* Pop a segment off the top of the queue, copy it to `ctx.segment`, and
+ * free its memory.
+ */
+ segment = (GimpOperationFloodSegment *) g_queue_pop_head (queue);
+ ctx.segment = *segment;
+ g_slice_free (GimpOperationFloodSegment, segment);
+
+ /* Transform the ROI from the image-physical coordinate system to the
+ * image-virtual coordinate system, and store the result in `ctx.roi`.
+ */
+ gimp_operation_flood_process_transform_rect (&ctx, &ctx.roi, roi);
+
+ /* Read the ground- and water-levels of the current- and source-segments
+ * from the corresponding GEGL buffers to the corresponding arrays.
+ */
+ gimp_operation_flood_process_fetch (&ctx);
+
+ /* Perform the vertical propagation step. */
+ range_count = gimp_operation_flood_process_propagate_vertical (&ctx,
+ dirty_ranges);
+ /* If no dirty ranges were produced during vertical propagation, then the
+ * water level of the current segment didn't change, and we can short-
+ * circuit early.
+ */
+ if (range_count == 0)
+ continue;
+
+ /* Perform both passes of the horizontal propagation step. */
+ gimp_operation_flood_process_propagate_horizontal (&ctx,
+ /* Left-to-right */ +1,
+ dirty_ranges,
+ range_count);
+ gimp_operation_flood_process_propagate_horizontal (&ctx,
+ /* Right-to-left */ -1,
+ dirty_ranges,
+ range_count);
+
+ /* Coalesce consecutive dirty ranges separated by a gap less-than or
+ * equal-to `GIMP_OPERATION_FLOOD_COALESCE_MAX_GAP`.
+ */
+ range_count = gimp_operation_flood_process_coalesce (&ctx,
+ dirty_ranges,
+ range_count,
+ GIMP_OPERATION_FLOOD_COALESCE_MAX_GAP);
+
+ /* Write the updated water level back to the output GEGL buffer. */
+ gimp_operation_flood_process_commit (&ctx, dirty_ranges, range_count);
+
+ /* Push the new segments into the queue. */
+ gimp_operation_flood_process_distribute (&ctx, queue,
+ dirty_ranges, range_count);
+ }
+
+ g_queue_free (queue);
+
+ g_free (dirty_ranges);
+ g_free (ctx.water_buffer);
+ g_free (ctx.ground);
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationflood.h b/app/operations/gimpoperationflood.h
new file mode 100644
index 0000000..ed2c1c6
--- /dev/null
+++ b/app/operations/gimpoperationflood.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationflood.h
+ * Copyright (C) 2016 Ell
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_FLOOD_H__
+#define __GIMP_OPERATION_FLOOD_H__
+
+
+#include <gegl-plugin.h>
+
+
+#define GIMP_TYPE_OPERATION_FLOOD (gimp_operation_flood_get_type ())
+#define GIMP_OPERATION_FLOOD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_FLOOD, GimpOperationFlood))
+#define GIMP_OPERATION_FLOOD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_FLOOD, GimpOperationFloodClass))
+#define GIMP_IS_OPERATION_FLOOD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_FLOOD))
+#define GIMP_IS_OPERATION_FLOOD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_FLOOD))
+#define GIMP_OPERATION_FLOOD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_FLOOD, GimpOperationFloodClass))
+
+
+typedef struct _GimpOperationFlood GimpOperationFlood;
+typedef struct _GimpOperationFloodClass GimpOperationFloodClass;
+
+struct _GimpOperationFlood
+{
+ GeglOperationFilter parent_instance;
+};
+
+struct _GimpOperationFloodClass
+{
+ GeglOperationFilterClass parent_class;
+};
+
+
+GType gimp_operation_flood_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_FLOOD_H__ */
diff --git a/app/operations/gimpoperationgradient.c b/app/operations/gimpoperationgradient.c
new file mode 100644
index 0000000..2dbb746
--- /dev/null
+++ b/app/operations/gimpoperationgradient.c
@@ -0,0 +1,1283 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Largely based on gimpdrawable-gradient.c
+ *
+ * gimpoperationgradient.c
+ * Copyright (C) 2014 Michael Henning <drawoc@darkrefraction.com>
+ *
+ * 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 <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "operations-types.h"
+
+#include "core/gimpgradient.h"
+
+#include "gimpoperationgradient.h"
+
+
+#define GRADIENT_CACHE_N_SUPERSAMPLES 4
+#define GRADIENT_CACHE_MAX_SIZE ((1 << 20) / sizeof (GimpRGB))
+
+
+enum
+{
+ PROP_0,
+ PROP_CONTEXT,
+ PROP_GRADIENT,
+ PROP_START_X,
+ PROP_START_Y,
+ PROP_END_X,
+ PROP_END_Y,
+ PROP_GRADIENT_TYPE,
+ PROP_GRADIENT_REPEAT,
+ PROP_OFFSET,
+ PROP_GRADIENT_REVERSE,
+ PROP_GRADIENT_BLEND_COLOR_SPACE,
+ PROP_SUPERSAMPLE,
+ PROP_SUPERSAMPLE_DEPTH,
+ PROP_SUPERSAMPLE_THRESHOLD,
+ PROP_DITHER
+};
+
+typedef struct
+{
+ GimpGradient *gradient;
+ gboolean reverse;
+ GimpGradientBlendColorSpace blend_color_space;
+ GimpRGB *gradient_cache;
+ gint gradient_cache_size;
+ GimpGradientSegment *last_seg;
+ gdouble offset;
+ gdouble sx, sy;
+ GimpGradientType gradient_type;
+ gdouble dist;
+ gdouble vec[2];
+ GimpRepeatMode repeat;
+ GeglSampler *dist_sampler;
+} RenderBlendData;
+
+
+typedef struct
+{
+ gfloat *data;
+ GeglRectangle roi;
+ GRand *dither_rand;
+} PutPixelData;
+
+
+/* local function prototypes */
+
+static void gimp_operation_gradient_dispose (GObject *gobject);
+static void gimp_operation_gradient_finalize (GObject *gobject);
+static void gimp_operation_gradient_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_gradient_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_operation_gradient_prepare (GeglOperation *operation);
+
+static GeglRectangle gimp_operation_gradient_get_bounding_box (GeglOperation *operation);
+
+static gdouble gradient_calc_conical_sym_factor (gdouble dist,
+ gdouble *axis,
+ gdouble offset,
+ gdouble x,
+ gdouble y);
+static gdouble gradient_calc_conical_asym_factor (gdouble dist,
+ gdouble *axis,
+ gdouble offset,
+ gdouble x,
+ gdouble y);
+static gdouble gradient_calc_square_factor (gdouble dist,
+ gdouble offset,
+ gdouble x,
+ gdouble y);
+static gdouble gradient_calc_radial_factor (gdouble dist,
+ gdouble offset,
+ gdouble x,
+ gdouble y);
+static gdouble gradient_calc_linear_factor (gdouble dist,
+ gdouble *vec,
+ gdouble offset,
+ gdouble x,
+ gdouble y);
+static gdouble gradient_calc_bilinear_factor (gdouble dist,
+ gdouble *vec,
+ gdouble offset,
+ gdouble x,
+ gdouble y);
+static gdouble gradient_calc_spiral_factor (gdouble dist,
+ gdouble *axis,
+ gdouble offset,
+ gdouble x,
+ gdouble y,
+ gboolean clockwise);
+
+static gdouble gradient_calc_shapeburst_angular_factor (GeglSampler *dist_sampler,
+ gdouble offset,
+ gdouble x,
+ gdouble y);
+static gdouble gradient_calc_shapeburst_spherical_factor (GeglSampler *dist_sampler,
+ gdouble offset,
+ gdouble x,
+ gdouble y);
+static gdouble gradient_calc_shapeburst_dimpled_factor (GeglSampler *dist_sampler,
+ gdouble offset,
+ gdouble x,
+ gdouble y);
+
+static void gradient_render_pixel (gdouble x,
+ gdouble y,
+ GimpRGB *color,
+ gpointer render_data);
+
+static void gradient_put_pixel (gint x,
+ gint y,
+ GimpRGB *color,
+ gpointer put_pixel_data);
+
+static void gradient_dither_pixel (GimpRGB *color,
+ GRand *dither_rand,
+ gfloat *dest);
+
+static gboolean gimp_operation_gradient_process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *result,
+ gint level);
+
+static void gimp_operation_gradient_invalidate_cache (GimpOperationGradient *self);
+static void gimp_operation_gradient_validate_cache (GimpOperationGradient *self);
+
+
+G_DEFINE_TYPE (GimpOperationGradient, gimp_operation_gradient,
+ GEGL_TYPE_OPERATION_FILTER)
+
+#define parent_class gimp_operation_gradient_parent_class
+
+
+static void
+gimp_operation_gradient_class_init (GimpOperationGradientClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
+
+ object_class->dispose = gimp_operation_gradient_dispose;
+ object_class->finalize = gimp_operation_gradient_finalize;
+ object_class->set_property = gimp_operation_gradient_set_property;
+ object_class->get_property = gimp_operation_gradient_get_property;
+
+ operation_class->prepare = gimp_operation_gradient_prepare;
+ operation_class->get_bounding_box = gimp_operation_gradient_get_bounding_box;
+
+ filter_class->process = gimp_operation_gradient_process;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:gradient",
+ "categories", "gimp",
+ "description", "GIMP Gradient operation",
+ NULL);
+
+ g_object_class_install_property (object_class, PROP_CONTEXT,
+ g_param_spec_object ("context",
+ "Context",
+ "A GimpContext",
+ GIMP_TYPE_OBJECT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_GRADIENT,
+ g_param_spec_object ("gradient",
+ "Gradient",
+ "A GimpGradient to render",
+ GIMP_TYPE_OBJECT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_START_X,
+ g_param_spec_double ("start-x",
+ "Start X",
+ "X coordinate of the first point",
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_START_Y,
+ g_param_spec_double ("start-y",
+ "Start Y",
+ "Y coordinate of the first point",
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_END_X,
+ g_param_spec_double ("end-x",
+ "End X",
+ "X coordinate of the second point",
+ -G_MAXDOUBLE, G_MAXDOUBLE, 200,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_END_Y,
+ g_param_spec_double ("end-y",
+ "End Y",
+ "Y coordinate of the second point",
+ -G_MAXDOUBLE, G_MAXDOUBLE, 200,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_GRADIENT_TYPE,
+ g_param_spec_enum ("gradient-type",
+ "Gradient Type",
+ "The type of gradient to render",
+ GIMP_TYPE_GRADIENT_TYPE,
+ GIMP_GRADIENT_LINEAR,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_GRADIENT_REPEAT,
+ g_param_spec_enum ("gradient-repeat",
+ "Repeat mode",
+ "Repeat mode",
+ GIMP_TYPE_REPEAT_MODE,
+ GIMP_REPEAT_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_OFFSET,
+ g_param_spec_double ("offset",
+ "Offset",
+ "Offset relates to the starting and ending coordinates "
+ "specified for the blend. This parameter is mode dependent.",
+ 0, G_MAXDOUBLE, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_GRADIENT_REVERSE,
+ g_param_spec_boolean ("gradient-reverse",
+ "Reverse",
+ "Reverse the gradient",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_GRADIENT_BLEND_COLOR_SPACE,
+ g_param_spec_enum ("gradient-blend-color-space",
+ "Blend Color Space",
+ "Which color space to use when blending RGB gradient segments",
+ GIMP_TYPE_GRADIENT_BLEND_COLOR_SPACE,
+ GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_SUPERSAMPLE,
+ g_param_spec_boolean ("supersample",
+ "Supersample",
+ "Do adaptive supersampling",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_SUPERSAMPLE_DEPTH,
+ g_param_spec_int ("supersample-depth",
+ "Max depth",
+ "Maximum recursion levels for supersampling",
+ 1, 9, 3,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_SUPERSAMPLE_THRESHOLD,
+ g_param_spec_double ("supersample-threshold",
+ "Threshold",
+ "Supersampling threshold",
+ 0, 4, 0.20,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_DITHER,
+ g_param_spec_boolean ("dither",
+ "Dither",
+ "Use dithering to reduce banding",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_gradient_init (GimpOperationGradient *self)
+{
+ g_mutex_init (&self->gradient_cache_mutex);
+}
+
+static void
+gimp_operation_gradient_dispose (GObject *object)
+{
+ GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (object);
+
+ gimp_operation_gradient_invalidate_cache (self);
+
+ g_clear_object (&self->gradient);
+ g_clear_object (&self->context);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_operation_gradient_finalize (GObject *object)
+{
+ GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (object);
+
+ g_mutex_clear (&self->gradient_cache_mutex);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_operation_gradient_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (object);
+
+ switch (property_id)
+ {
+ case PROP_CONTEXT:
+ g_value_set_object (value, self->context);
+ break;
+
+ case PROP_GRADIENT:
+ g_value_set_object (value, self->gradient);
+ break;
+
+ case PROP_START_X:
+ g_value_set_double (value, self->start_x);
+ break;
+
+ case PROP_START_Y:
+ g_value_set_double (value, self->start_y);
+ break;
+
+ case PROP_END_X:
+ g_value_set_double (value, self->end_x);
+ break;
+
+ case PROP_END_Y:
+ g_value_set_double (value, self->end_y);
+ break;
+
+ case PROP_GRADIENT_TYPE:
+ g_value_set_enum (value, self->gradient_type);
+ break;
+
+ case PROP_GRADIENT_REPEAT:
+ g_value_set_enum (value, self->gradient_repeat);
+ break;
+
+ case PROP_OFFSET:
+ g_value_set_double (value, self->offset);
+ break;
+
+ case PROP_GRADIENT_REVERSE:
+ g_value_set_boolean (value, self->gradient_reverse);
+ break;
+
+ case PROP_GRADIENT_BLEND_COLOR_SPACE:
+ g_value_set_enum (value, self->gradient_blend_color_space);
+ break;
+
+ case PROP_SUPERSAMPLE:
+ g_value_set_boolean (value, self->supersample);
+ break;
+
+ case PROP_SUPERSAMPLE_DEPTH:
+ g_value_set_int (value, self->supersample_depth);
+ break;
+
+ case PROP_SUPERSAMPLE_THRESHOLD:
+ g_value_set_double (value, self->supersample_threshold);
+ break;
+
+ case PROP_DITHER:
+ g_value_set_boolean (value, self->dither);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_gradient_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (object);
+
+ switch (property_id)
+ {
+ case PROP_CONTEXT:
+ if (self->context)
+ g_object_unref (self->context);
+
+ self->context = g_value_dup_object (value);
+ break;
+
+ case PROP_GRADIENT:
+ {
+ GimpGradient *gradient = g_value_get_object (value);
+
+ g_clear_object (&self->gradient);
+
+ if (gradient)
+ {
+ if (gimp_gradient_has_fg_bg_segments (gradient))
+ self->gradient = gimp_gradient_flatten (gradient, self->context);
+ else
+ self->gradient = g_object_ref (gradient);
+ }
+
+ gimp_operation_gradient_invalidate_cache (self);
+ }
+ break;
+
+ case PROP_START_X:
+ self->start_x = g_value_get_double (value);
+
+ gimp_operation_gradient_invalidate_cache (self);
+ break;
+
+ case PROP_START_Y:
+ self->start_y = g_value_get_double (value);
+
+ gimp_operation_gradient_invalidate_cache (self);
+ break;
+
+ case PROP_END_X:
+ self->end_x = g_value_get_double (value);
+
+ gimp_operation_gradient_invalidate_cache (self);
+ break;
+
+ case PROP_END_Y:
+ self->end_y = g_value_get_double (value);
+
+ gimp_operation_gradient_invalidate_cache (self);
+ break;
+
+ case PROP_GRADIENT_TYPE:
+ self->gradient_type = g_value_get_enum (value);
+ break;
+
+ case PROP_GRADIENT_REPEAT:
+ self->gradient_repeat = g_value_get_enum (value);
+ break;
+
+ case PROP_OFFSET:
+ self->offset = g_value_get_double (value);
+ break;
+
+ case PROP_GRADIENT_REVERSE:
+ self->gradient_reverse = g_value_get_boolean (value);
+
+ gimp_operation_gradient_invalidate_cache (self);
+ break;
+
+ case PROP_GRADIENT_BLEND_COLOR_SPACE:
+ self->gradient_blend_color_space = g_value_get_enum (value);
+
+ gimp_operation_gradient_invalidate_cache (self);
+ break;
+
+ case PROP_SUPERSAMPLE:
+ self->supersample = g_value_get_boolean (value);
+ break;
+
+ case PROP_SUPERSAMPLE_DEPTH:
+ self->supersample_depth = g_value_get_int (value);
+ break;
+
+ case PROP_SUPERSAMPLE_THRESHOLD:
+ self->supersample_threshold = g_value_get_double (value);
+ break;
+
+ case PROP_DITHER:
+ self->dither = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_gradient_prepare (GeglOperation *operation)
+{
+ gegl_operation_set_format (operation, "output", babl_format ("R'G'B'A float"));
+}
+
+static GeglRectangle
+gimp_operation_gradient_get_bounding_box (GeglOperation *operation)
+{
+ return gegl_rectangle_infinite_plane ();
+}
+
+static gdouble
+gradient_calc_conical_sym_factor (gdouble dist,
+ gdouble *axis,
+ gdouble offset,
+ gdouble x,
+ gdouble y)
+{
+ if (dist == 0.0)
+ {
+ return 0.0;
+ }
+ else if ((x != 0) || (y != 0))
+ {
+ gdouble vec[2];
+ gdouble r;
+ gdouble rat;
+
+ /* Calculate offset from the start in pixels */
+
+ r = sqrt (SQR (x) + SQR (y));
+
+ vec[0] = x / r;
+ vec[1] = y / r;
+
+ rat = axis[0] * vec[0] + axis[1] * vec[1]; /* Dot product */
+
+ if (rat > 1.0)
+ rat = 1.0;
+ else if (rat < -1.0)
+ rat = -1.0;
+
+ /* This cool idea is courtesy Josh MacDonald,
+ * Ali Rahimi --- two more XCF losers. */
+
+ rat = acos (rat) / G_PI;
+ rat = pow (rat, (offset / 10.0) + 1.0);
+
+ return CLAMP (rat, 0.0, 1.0);
+ }
+ else
+ {
+ return 0.5;
+ }
+}
+
+static gdouble
+gradient_calc_conical_asym_factor (gdouble dist,
+ gdouble *axis,
+ gdouble offset,
+ gdouble x,
+ gdouble y)
+{
+ if (dist == 0.0)
+ {
+ return 0.0;
+ }
+ else if (x != 0 || y != 0)
+ {
+ gdouble ang0, ang1;
+ gdouble ang;
+ gdouble rat;
+
+ ang0 = atan2 (axis[0], axis[1]) + G_PI;
+
+ ang1 = atan2 (x, y) + G_PI;
+
+ ang = ang1 - ang0;
+
+ if (ang < 0.0)
+ ang += (2.0 * G_PI);
+
+ rat = ang / (2.0 * G_PI);
+ rat = pow (rat, (offset / 10.0) + 1.0);
+
+ return CLAMP (rat, 0.0, 1.0);
+ }
+ else
+ {
+ return 0.5; /* We are on middle point */
+ }
+}
+
+static gdouble
+gradient_calc_square_factor (gdouble dist,
+ gdouble offset,
+ gdouble x,
+ gdouble y)
+{
+ if (dist == 0.0)
+ {
+ return 0.0;
+ }
+ else
+ {
+ gdouble r;
+ gdouble rat;
+
+ /* Calculate offset from start as a value in [0, 1] */
+
+ offset = offset / 100.0;
+
+ r = MAX (fabs (x), fabs (y));
+ rat = r / dist;
+
+ if (rat < offset)
+ return 0.0;
+ else if (offset == 1.0)
+ return (rat >= 1.0) ? 1.0 : 0.0;
+ else
+ return (rat - offset) / (1.0 - offset);
+ }
+}
+
+static gdouble
+gradient_calc_radial_factor (gdouble dist,
+ gdouble offset,
+ gdouble x,
+ gdouble y)
+{
+ if (dist == 0.0)
+ {
+ return 0.0;
+ }
+ else
+ {
+ gdouble r;
+ gdouble rat;
+
+ /* Calculate radial offset from start as a value in [0, 1] */
+
+ offset = offset / 100.0;
+
+ r = sqrt (SQR (x) + SQR (y));
+ rat = r / dist;
+
+ if (rat < offset)
+ return 0.0;
+ else if (offset == 1.0)
+ return (rat >= 1.0) ? 1.0 : 0.0;
+ else
+ return (rat - offset) / (1.0 - offset);
+ }
+}
+
+static gdouble
+gradient_calc_linear_factor (gdouble dist,
+ gdouble *vec,
+ gdouble offset,
+ gdouble x,
+ gdouble y)
+{
+ if (dist == 0.0)
+ {
+ return 0.0;
+ }
+ else
+ {
+ gdouble r;
+ gdouble rat;
+
+ offset = offset / 100.0;
+
+ r = vec[0] * x + vec[1] * y;
+ rat = r / dist;
+
+ if (rat >= 0.0 && rat < offset)
+ return 0.0;
+ else if (offset == 1.0)
+ return (rat >= 1.0) ? 1.0 : 0.0;
+ else if (rat < 0.0)
+ return rat / (1.0 - offset);
+ else
+ return (rat - offset) / (1.0 - offset);
+ }
+}
+
+static gdouble
+gradient_calc_bilinear_factor (gdouble dist,
+ gdouble *vec,
+ gdouble offset,
+ gdouble x,
+ gdouble y)
+{
+ if (dist == 0.0)
+ {
+ return 0.0;
+ }
+ else
+ {
+ gdouble r;
+ gdouble rat;
+
+ /* Calculate linear offset from the start line outward */
+
+ offset = offset / 100.0;
+
+ r = vec[0] * x + vec[1] * y;
+ rat = r / dist;
+
+ if (fabs (rat) < offset)
+ return 0.0;
+ else if (offset == 1.0)
+ return (rat == 1.0) ? 1.0 : 0.0;
+ else
+ return (fabs (rat) - offset) / (1.0 - offset);
+ }
+}
+
+static gdouble
+gradient_calc_spiral_factor (gdouble dist,
+ gdouble *axis,
+ gdouble offset,
+ gdouble x,
+ gdouble y,
+ gboolean clockwise)
+{
+ if (dist == 0.0)
+ {
+ return 0.0;
+ }
+ else if (x != 0.0 || y != 0.0)
+ {
+ gdouble ang0, ang1;
+ gdouble ang;
+ double r;
+
+ offset = offset / 100.0;
+
+ ang0 = atan2 (axis[0], axis[1]) + G_PI;
+ ang1 = atan2 (x, y) + G_PI;
+
+ if (clockwise)
+ ang = ang1 - ang0;
+ else
+ ang = ang0 - ang1;
+
+ if (ang < 0.0)
+ ang += (2.0 * G_PI);
+
+ r = sqrt (SQR (x) + SQR (y)) / dist;
+
+ return fmod (ang / (2.0 * G_PI) + r + offset, 1.0);
+ }
+ else
+ {
+ return 0.5 ; /* We are on the middle point */
+ }
+}
+
+static gdouble
+gradient_calc_shapeburst_angular_factor (GeglSampler *dist_sampler,
+ gdouble offset,
+ gdouble x,
+ gdouble y)
+{
+ gfloat value;
+
+ offset = offset / 100.0;
+
+ gegl_sampler_get (dist_sampler, x, y, NULL, &value, GEGL_ABYSS_NONE);
+
+ value = 1.0 - value;
+
+ if (value < offset)
+ value = 0.0;
+ else if (offset == 1.0)
+ value = (value >= 1.0) ? 1.0 : 0.0;
+ else
+ value = (value - offset) / (1.0 - offset);
+
+ return value;
+}
+
+
+static gdouble
+gradient_calc_shapeburst_spherical_factor (GeglSampler *dist_sampler,
+ gdouble offset,
+ gdouble x,
+ gdouble y)
+{
+ gfloat value;
+
+ offset = 1.0 - offset / 100.0;
+
+ gegl_sampler_get (dist_sampler, x, y, NULL, &value, GEGL_ABYSS_NONE);
+
+ if (value > offset)
+ value = 1.0;
+ else if (offset == 0.0)
+ value = (value <= 0.0) ? 0.0 : 1.0;
+ else
+ value = value / offset;
+
+ value = 1.0 - sin (0.5 * G_PI * value);
+
+ return value;
+}
+
+
+static gdouble
+gradient_calc_shapeburst_dimpled_factor (GeglSampler *dist_sampler,
+ gdouble offset,
+ gdouble x,
+ gdouble y)
+{
+ gfloat value;
+
+ offset = 1.0 - offset / 100.0;
+
+ gegl_sampler_get (dist_sampler, x, y, NULL, &value, GEGL_ABYSS_NONE);
+
+ if (value > offset)
+ value = 1.0;
+ else if (offset == 0.0)
+ value = (value <= 0.0) ? 0.0 : 1.0;
+ else
+ value = value / offset;
+
+ value = cos (0.5 * G_PI * value);
+
+ return value;
+}
+
+static void
+gradient_render_pixel (gdouble x,
+ gdouble y,
+ GimpRGB *color,
+ gpointer render_data)
+{
+ RenderBlendData *rbd = render_data;
+ gdouble factor;
+
+ /* we want to calculate the color at the pixel's center */
+ x += 0.5;
+ y += 0.5;
+
+ /* Calculate blending factor */
+
+ switch (rbd->gradient_type)
+ {
+ case GIMP_GRADIENT_LINEAR:
+ factor = gradient_calc_linear_factor (rbd->dist,
+ rbd->vec, rbd->offset,
+ x - rbd->sx, y - rbd->sy);
+ break;
+
+ case GIMP_GRADIENT_BILINEAR:
+ factor = gradient_calc_bilinear_factor (rbd->dist,
+ rbd->vec, rbd->offset,
+ x - rbd->sx, y - rbd->sy);
+ break;
+
+ case GIMP_GRADIENT_RADIAL:
+ factor = gradient_calc_radial_factor (rbd->dist,
+ rbd->offset,
+ x - rbd->sx, y - rbd->sy);
+ break;
+
+ case GIMP_GRADIENT_SQUARE:
+ factor = gradient_calc_square_factor (rbd->dist, rbd->offset,
+ x - rbd->sx, y - rbd->sy);
+ break;
+
+ case GIMP_GRADIENT_CONICAL_SYMMETRIC:
+ factor = gradient_calc_conical_sym_factor (rbd->dist,
+ rbd->vec, rbd->offset,
+ x - rbd->sx, y - rbd->sy);
+ break;
+
+ case GIMP_GRADIENT_CONICAL_ASYMMETRIC:
+ factor = gradient_calc_conical_asym_factor (rbd->dist,
+ rbd->vec, rbd->offset,
+ x - rbd->sx, y - rbd->sy);
+ break;
+
+ case GIMP_GRADIENT_SHAPEBURST_ANGULAR:
+ factor = gradient_calc_shapeburst_angular_factor (rbd->dist_sampler,
+ rbd->offset,
+ x, y);
+ break;
+
+ case GIMP_GRADIENT_SHAPEBURST_SPHERICAL:
+ factor = gradient_calc_shapeburst_spherical_factor (rbd->dist_sampler,
+ rbd->offset,
+ x, y);
+ break;
+
+ case GIMP_GRADIENT_SHAPEBURST_DIMPLED:
+ factor = gradient_calc_shapeburst_dimpled_factor (rbd->dist_sampler,
+ rbd->offset,
+ x, y);
+ break;
+
+ case GIMP_GRADIENT_SPIRAL_CLOCKWISE:
+ factor = gradient_calc_spiral_factor (rbd->dist,
+ rbd->vec, rbd->offset,
+ x - rbd->sx, y - rbd->sy, TRUE);
+ break;
+
+ case GIMP_GRADIENT_SPIRAL_ANTICLOCKWISE:
+ factor = gradient_calc_spiral_factor (rbd->dist,
+ rbd->vec, rbd->offset,
+ x - rbd->sx, y - rbd->sy, FALSE);
+ break;
+
+ default:
+ g_return_if_reached ();
+ break;
+ }
+
+ /* Adjust for repeat */
+
+ switch (rbd->repeat)
+ {
+ case GIMP_REPEAT_NONE:
+ break;
+
+ case GIMP_REPEAT_SAWTOOTH:
+ factor = factor - floor (factor);
+ break;
+
+ case GIMP_REPEAT_TRIANGULAR:
+ {
+ guint ifactor;
+
+ if (factor < 0.0)
+ factor = -factor;
+
+ ifactor = (guint) factor;
+ factor = factor - floor (factor);
+
+ if (ifactor & 1)
+ factor = 1.0 - factor;
+ }
+ break;
+
+ case GIMP_REPEAT_TRUNCATE:
+ if (factor < 0.0 || factor > 1.0)
+ {
+ gimp_rgba_set (color, 0.0, 0.0, 0.0, 0.0);
+ return;
+ }
+ break;
+ }
+
+ /* Blend the colors */
+
+ if (rbd->gradient_cache)
+ {
+ factor = CLAMP (factor, 0.0, 1.0);
+
+ *color =
+ rbd->gradient_cache[ROUND (factor * (rbd->gradient_cache_size - 1))];
+ }
+ else
+ {
+ rbd->last_seg = gimp_gradient_get_color_at (rbd->gradient, NULL,
+ rbd->last_seg, factor,
+ rbd->reverse,
+ rbd->blend_color_space,
+ color);
+ }
+}
+
+static void
+gradient_put_pixel (gint x,
+ gint y,
+ GimpRGB *color,
+ gpointer put_pixel_data)
+{
+ PutPixelData *ppd = put_pixel_data;
+ const gint index = (y - ppd->roi.y) * ppd->roi.width + (x - ppd->roi.x);
+ gfloat *dest = ppd->data + 4 * index;
+
+ if (ppd->dither_rand)
+ {
+ gradient_dither_pixel (color, ppd->dither_rand, dest);
+
+ dest += 4;
+ }
+ else
+ {
+ *dest++ = color->r;
+ *dest++ = color->g;
+ *dest++ = color->b;
+ *dest++ = color->a;
+ }
+}
+
+static void
+gradient_dither_pixel (GimpRGB *color,
+ GRand *dither_rand,
+ gfloat *dest)
+{
+ gfloat r, g, b, a;
+ guint i;
+
+ i = g_rand_int (dither_rand);
+
+ r = color->r + (gdouble) (i & 0xff) / 256.0 / 256.0 - 0.5 / 256.0; i >>= 8;
+ g = color->g + (gdouble) (i & 0xff) / 256.0 / 256.0 - 0.5 / 256.0; i >>= 8;
+ b = color->b + (gdouble) (i & 0xff) / 256.0 / 256.0 - 0.5 / 256.0; i >>= 8;
+
+ if (color->a > 0.0 && color->a < 1.0)
+ a = color->a + (gdouble) (i & 0xff) / 256.0 / 256.0 - 0.5 / 256.0;
+ else
+ a = color->a;
+
+ *dest++ = CLAMP (r, 0.0, 1.0);
+ *dest++ = CLAMP (g, 0.0, 1.0);
+ *dest++ = CLAMP (b, 0.0, 1.0);
+ *dest++ = CLAMP (a, 0.0, 1.0);
+}
+
+static gboolean
+gimp_operation_gradient_process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *result,
+ gint level)
+{
+ GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (operation);
+
+ const gdouble sx = self->start_x;
+ const gdouble sy = self->start_y;
+ const gdouble ex = self->end_x;
+ const gdouble ey = self->end_y;
+
+ RenderBlendData rbd = { 0, };
+
+ GeglBufferIterator *iter;
+ GeglRectangle *roi;
+ GRand *dither_rand = NULL;
+
+ if (! self->gradient)
+ return TRUE;
+
+ gimp_operation_gradient_validate_cache (self);
+
+ rbd.gradient = self->gradient;
+ rbd.reverse = self->gradient_reverse;
+ rbd.blend_color_space = self->gradient_blend_color_space;
+ rbd.gradient_cache = self->gradient_cache;
+ rbd.gradient_cache_size = self->gradient_cache_size;
+
+ /* Calculate type-specific parameters */
+
+ switch (self->gradient_type)
+ {
+ case GIMP_GRADIENT_RADIAL:
+ rbd.dist = sqrt (SQR (ex - sx) + SQR (ey - sy));
+ break;
+
+ case GIMP_GRADIENT_SQUARE:
+ rbd.dist = MAX (fabs (ex - sx), fabs (ey - sy));
+ break;
+
+ case GIMP_GRADIENT_CONICAL_SYMMETRIC:
+ case GIMP_GRADIENT_CONICAL_ASYMMETRIC:
+ case GIMP_GRADIENT_SPIRAL_CLOCKWISE:
+ case GIMP_GRADIENT_SPIRAL_ANTICLOCKWISE:
+ case GIMP_GRADIENT_LINEAR:
+ case GIMP_GRADIENT_BILINEAR:
+ rbd.dist = sqrt (SQR (ex - sx) + SQR (ey - sy));
+
+ if (rbd.dist > 0.0)
+ {
+ rbd.vec[0] = (ex - sx) / rbd.dist;
+ rbd.vec[1] = (ey - sy) / rbd.dist;
+ }
+
+ break;
+
+ case GIMP_GRADIENT_SHAPEBURST_ANGULAR:
+ case GIMP_GRADIENT_SHAPEBURST_SPHERICAL:
+ case GIMP_GRADIENT_SHAPEBURST_DIMPLED:
+ rbd.dist = sqrt (SQR (ex - sx) + SQR (ey - sy));
+ rbd.dist_sampler = gegl_buffer_sampler_new_at_level (
+ input, babl_format ("Y float"), GEGL_SAMPLER_NEAREST, level);
+ break;
+
+ default:
+ g_return_val_if_reached (FALSE);
+ break;
+ }
+
+ /* Initialize render data */
+
+ rbd.offset = self->offset;
+ rbd.sx = self->start_x;
+ rbd.sy = self->start_y;
+ rbd.gradient_type = self->gradient_type;
+ rbd.repeat = self->gradient_repeat;
+
+ /* Render the gradient! */
+
+ iter = gegl_buffer_iterator_new (output, result, 0,
+ babl_format ("R'G'B'A float"),
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
+ roi = &iter->items[0].roi;
+
+ if (self->dither)
+ dither_rand = g_rand_new ();
+
+ if (self->supersample)
+ {
+ PutPixelData ppd;
+
+ ppd.dither_rand = dither_rand;
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ ppd.data = iter->items[0].data;
+ ppd.roi = *roi;
+
+ gimp_adaptive_supersample_area (roi->x, roi->y,
+ roi->x + roi->width - 1,
+ roi->y + roi->height - 1,
+ self->supersample_depth,
+ self->supersample_threshold,
+ gradient_render_pixel, &rbd,
+ gradient_put_pixel, &ppd,
+ NULL,
+ NULL);
+ }
+ }
+ else
+ {
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *dest = iter->items[0].data;
+ gint endx = roi->x + roi->width;
+ gint endy = roi->y + roi->height;
+ gint x, y;
+
+ if (dither_rand)
+ {
+ for (y = roi->y; y < endy; y++)
+ for (x = roi->x; x < endx; x++)
+ {
+ GimpRGB color = { 0.0, 0.0, 0.0, 1.0 };
+
+ gradient_render_pixel (x, y, &color, &rbd);
+ gradient_dither_pixel (&color, dither_rand, dest);
+
+ dest += 4;
+ }
+ }
+ else
+ {
+ for (y = roi->y; y < endy; y++)
+ for (x = roi->x; x < endx; x++)
+ {
+ GimpRGB color = { 0.0, 0.0, 0.0, 1.0 };
+
+ gradient_render_pixel (x, y, &color, &rbd);
+
+ *dest++ = color.r;
+ *dest++ = color.g;
+ *dest++ = color.b;
+ *dest++ = color.a;
+ }
+ }
+ }
+ }
+
+ if (self->dither)
+ g_rand_free (dither_rand);
+
+ g_clear_object (&rbd.dist_sampler);
+
+ return TRUE;
+}
+
+static void
+gimp_operation_gradient_invalidate_cache (GimpOperationGradient *self)
+{
+ g_clear_pointer (&self->gradient_cache, g_free);
+}
+
+static void
+gimp_operation_gradient_validate_cache (GimpOperationGradient *self)
+{
+ GimpGradientSegment *last_seg = NULL;
+ gint cache_size;
+ gint i;
+
+ if (! self->gradient)
+ return;
+
+ g_mutex_lock (&self->gradient_cache_mutex);
+
+ if (self->gradient_cache)
+ {
+ g_mutex_unlock (&self->gradient_cache_mutex);
+
+ return;
+ }
+
+ cache_size = ceil (hypot (self->start_x - self->end_x,
+ self->start_y - self->end_y)) *
+ GRADIENT_CACHE_N_SUPERSAMPLES;
+
+ /* have at least two values in the cache */
+ cache_size = MAX (cache_size, 2);
+
+ /* don't use a cache if its necessary size is too big */
+ if (cache_size > GRADIENT_CACHE_MAX_SIZE)
+ {
+ g_mutex_unlock (&self->gradient_cache_mutex);
+
+ return;
+ }
+
+ self->gradient_cache = g_new0 (GimpRGB, cache_size);
+ self->gradient_cache_size = cache_size;
+
+ for (i = 0; i < self->gradient_cache_size; i++)
+ {
+ gdouble factor = (gdouble) i / (gdouble) (self->gradient_cache_size - 1);
+
+ last_seg = gimp_gradient_get_color_at (self->gradient, NULL, last_seg,
+ factor,
+ self->gradient_reverse,
+ self->gradient_blend_color_space,
+ self->gradient_cache + i);
+ }
+
+ g_mutex_unlock (&self->gradient_cache_mutex);
+}
diff --git a/app/operations/gimpoperationgradient.h b/app/operations/gimpoperationgradient.h
new file mode 100644
index 0000000..e2aa19c
--- /dev/null
+++ b/app/operations/gimpoperationgradient.h
@@ -0,0 +1,73 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationgradient.h
+ * Copyright (C) 2014 Michael Henning <drawoc@darkrefraction.com>
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_GRADIENT_H__
+#define __GIMP_OPERATION_GRADIENT_H__
+
+
+#include <gegl-plugin.h>
+
+
+#define GIMP_TYPE_OPERATION_GRADIENT (gimp_operation_gradient_get_type ())
+#define GIMP_OPERATION_GRADIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_GRADIENT, GimpOperationGradient))
+#define GIMP_OPERATION_GRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_GRADIENT, GimpOperationGradientClass))
+#define GIMP_IS_OPERATION_GRADIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_GRADIENT))
+#define GIMP_IS_OPERATION_GRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_GRADIENT))
+#define GIMP_OPERATION_GRADIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_GRADIENT, GimpOperationGradientClass))
+
+
+typedef struct _GimpOperationGradient GimpOperationGradient;
+typedef struct _GimpOperationGradientClass GimpOperationGradientClass;
+
+struct _GimpOperationGradient
+{
+ GeglOperationFilter parent_instance;
+
+ GimpContext *context;
+
+ GimpGradient *gradient;
+ gdouble start_x, start_y, end_x, end_y;
+ GimpGradientType gradient_type;
+ GimpRepeatMode gradient_repeat;
+ gdouble offset;
+ gboolean gradient_reverse;
+ GimpGradientBlendColorSpace gradient_blend_color_space;
+
+ gboolean supersample;
+ gint supersample_depth;
+ gdouble supersample_threshold;
+
+ gboolean dither;
+
+ GimpRGB *gradient_cache;
+ gint gradient_cache_size;
+ GMutex gradient_cache_mutex;
+};
+
+struct _GimpOperationGradientClass
+{
+ GeglOperationFilterClass parent_class;
+};
+
+
+GType gimp_operation_gradient_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_GRADIENT_H__ */
diff --git a/app/operations/gimpoperationgrow.c b/app/operations/gimpoperationgrow.c
new file mode 100644
index 0000000..2aabcee
--- /dev/null
+++ b/app/operations/gimpoperationgrow.c
@@ -0,0 +1,391 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationgrow.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 <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "operations-types.h"
+
+#include "gimpoperationgrow.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_RADIUS_X,
+ PROP_RADIUS_Y
+};
+
+
+static void gimp_operation_grow_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_grow_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_operation_grow_prepare (GeglOperation *operation);
+static GeglRectangle
+ gimp_operation_grow_get_required_for_output (GeglOperation *self,
+ const gchar *input_pad,
+ const GeglRectangle *roi);
+static GeglRectangle
+ gimp_operation_grow_get_cached_region (GeglOperation *self,
+ const GeglRectangle *roi);
+
+static gboolean gimp_operation_grow_process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationGrow, gimp_operation_grow,
+ GEGL_TYPE_OPERATION_FILTER)
+
+#define parent_class gimp_operation_grow_parent_class
+
+
+static void
+gimp_operation_grow_class_init (GimpOperationGrowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
+
+ object_class->set_property = gimp_operation_grow_set_property;
+ object_class->get_property = gimp_operation_grow_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:grow",
+ "categories", "gimp",
+ "description", "GIMP Grow operation",
+ NULL);
+
+ operation_class->prepare = gimp_operation_grow_prepare;
+ operation_class->get_required_for_output = gimp_operation_grow_get_required_for_output;
+ operation_class->get_cached_region = gimp_operation_grow_get_cached_region;
+ operation_class->threaded = FALSE;
+
+ filter_class->process = gimp_operation_grow_process;
+
+ g_object_class_install_property (object_class, PROP_RADIUS_X,
+ g_param_spec_int ("radius-x",
+ "Radius X",
+ "Grow radius in X direction",
+ 1, 2342, 1,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_RADIUS_Y,
+ g_param_spec_int ("radius-y",
+ "Radius Y",
+ "Grow radius in Y direction",
+ 1, 2342, 1,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_grow_init (GimpOperationGrow *self)
+{
+}
+
+static void
+gimp_operation_grow_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationGrow *self = GIMP_OPERATION_GROW (object);
+
+ switch (property_id)
+ {
+ case PROP_RADIUS_X:
+ g_value_set_int (value, self->radius_x);
+ break;
+
+ case PROP_RADIUS_Y:
+ g_value_set_int (value, self->radius_y);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_grow_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationGrow *self = GIMP_OPERATION_GROW (object);
+
+ switch (property_id)
+ {
+ case PROP_RADIUS_X:
+ self->radius_x = g_value_get_int (value);
+ break;
+
+ case PROP_RADIUS_Y:
+ self->radius_y = g_value_get_int (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_grow_prepare (GeglOperation *operation)
+{
+ const Babl *space = gegl_operation_get_source_space (operation, "input");
+ gegl_operation_set_format (operation, "input", babl_format_with_space ("Y float", space));
+ gegl_operation_set_format (operation, "output", babl_format_with_space ("Y float", space));
+}
+
+static GeglRectangle
+gimp_operation_grow_get_required_for_output (GeglOperation *self,
+ const gchar *input_pad,
+ const GeglRectangle *roi)
+{
+ return *gegl_operation_source_get_bounding_box (self, "input");
+}
+
+static GeglRectangle
+gimp_operation_grow_get_cached_region (GeglOperation *self,
+ const GeglRectangle *roi)
+{
+ return *gegl_operation_source_get_bounding_box (self, "input");
+}
+
+static void
+compute_border (gint16 *circ,
+ guint16 xradius,
+ guint16 yradius)
+{
+ gint32 i;
+ gint32 diameter = xradius * 2 + 1;
+ gdouble tmp;
+
+ for (i = 0; i < diameter; i++)
+ {
+ if (i > xradius)
+ tmp = (i - xradius) - 0.5;
+ else if (i < xradius)
+ tmp = (xradius - i) - 0.5;
+ else
+ tmp = 0.0;
+
+ circ[i] = RINT (yradius /
+ (gdouble) xradius * sqrt (SQR (xradius) - SQR (tmp)));
+ }
+}
+
+static inline void
+rotate_pointers (gfloat **p,
+ guint32 n)
+{
+ guint32 i;
+ gfloat *tmp;
+
+ tmp = p[0];
+
+ for (i = 0; i < n - 1; i++)
+ p[i] = p[i + 1];
+
+ p[i] = tmp;
+}
+
+static gboolean
+gimp_operation_grow_process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *roi,
+ gint level)
+{
+ /* Any bugs in this function are probably also in thin_region.
+ * Blame all bugs in this function on jaycox@gimp.org
+ */
+ GimpOperationGrow *self = GIMP_OPERATION_GROW (operation);
+ const Babl *input_format = gegl_operation_get_format (operation, "input");
+ const Babl *output_format = gegl_operation_get_format (operation, "output");
+ gint32 i, j, x, y;
+ gfloat **buf; /* caches the region's pixel data */
+ gfloat *out; /* holds the new scan line we are computing */
+ gfloat **max; /* caches the largest values for each column */
+ gint16 *circ; /* holds the y coords of the filter's mask */
+ gfloat last_max;
+ gint16 last_index;
+ gfloat *buffer;
+
+ max = g_new (gfloat *, roi->width + 2 * self->radius_x);
+ buf = g_new (gfloat *, self->radius_y + 1);
+
+ for (i = 0; i < self->radius_y + 1; i++)
+ buf[i] = g_new (gfloat, roi->width);
+
+ buffer = g_new (gfloat,
+ (roi->width + 2 * self->radius_x) * (self->radius_y + 1));
+
+ for (i = 0; i < roi->width + 2 * self->radius_x; i++)
+ {
+ if (i < self->radius_x)
+ max[i] = buffer;
+ else if (i < roi->width + self->radius_x)
+ max[i] = &buffer[(self->radius_y + 1) * (i - self->radius_x)];
+ else
+ max[i] = &buffer[(self->radius_y + 1) * (roi->width + self->radius_x - 1)];
+
+ for (j = 0; j < self->radius_y + 1; j++)
+ max[i][j] = 0.0;
+ }
+
+ /* offset the max pointer by self->radius_x so the range of the
+ * array is [-self->radius_x] to [roi->width + self->radius_x]
+ */
+ max += self->radius_x;
+
+ out = g_new (gfloat, roi->width);
+
+ circ = g_new (gint16, 2 * self->radius_x + 1);
+ compute_border (circ, self->radius_x, self->radius_y);
+
+ /* offset the circ pointer by self->radius_x so the range of the
+ * array is [-self->radius_x] to [self->radius_x]
+ */
+ circ += self->radius_x;
+
+ memset (buf[0], 0, roi->width * sizeof (gfloat));
+
+ for (i = 0; i < self->radius_y && i < roi->height; i++) /* load top of image */
+ gegl_buffer_get (input,
+ GEGL_RECTANGLE (roi->x, roi->y + i,
+ roi->width, 1),
+ 1.0, input_format, buf[i + 1],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (x = 0; x < roi->width; x++) /* set up max for top of image */
+ {
+ max[x][0] = 0.0; /* buf[0][x] is always 0 */
+ max[x][1] = buf[1][x]; /* MAX (buf[1][x], max[x][0]) always = buf[1][x]*/
+
+ for (j = 2; j < self->radius_y + 1; j++)
+ max[x][j] = MAX (buf[j][x], max[x][j - 1]);
+ }
+
+ for (y = 0; y < roi->height; y++)
+ {
+ rotate_pointers (buf, self->radius_y + 1);
+
+ if (y < roi->height - (self->radius_y))
+ gegl_buffer_get (input,
+ GEGL_RECTANGLE (roi->x, roi->y + y + self->radius_y,
+ roi->width, 1),
+ 1.0, input_format, buf[self->radius_y],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ else
+ memset (buf[self->radius_y], 0, roi->width * sizeof (gfloat));
+
+ for (x = 0; x < roi->width; x++) /* update max array */
+ {
+ for (i = self->radius_y; i > 0; i--)
+ max[x][i] = MAX (MAX (max[x][i - 1], buf[i - 1][x]), buf[i][x]);
+
+ max[x][0] = buf[0][x];
+ }
+
+ last_max = max[0][circ[-1]];
+ last_index = 1;
+
+ for (x = 0; x < roi->width; x++) /* render scan line */
+ {
+ last_index--;
+
+ if (last_index >= 0)
+ {
+ if (last_max >= 1.0)
+ {
+ out[x] = 1.0;
+ }
+ else
+ {
+ last_max = 0.0;
+
+ for (i = self->radius_x; i >= 0; i--)
+ if (last_max < max[x + i][circ[i]])
+ {
+ last_max = max[x + i][circ[i]];
+ last_index = i;
+ }
+
+ out[x] = last_max;
+ }
+ }
+ else
+ {
+ last_index = self->radius_x;
+ last_max = max[x + self->radius_x][circ[self->radius_x]];
+
+ for (i = self->radius_x - 1; i >= -self->radius_x; i--)
+ if (last_max < max[x + i][circ[i]])
+ {
+ last_max = max[x + i][circ[i]];
+ last_index = i;
+ }
+
+ out[x] = last_max;
+ }
+ }
+
+ gegl_buffer_set (output,
+ GEGL_RECTANGLE (roi->x, roi->y + y,
+ roi->width, 1),
+ 0, output_format, out,
+ GEGL_AUTO_ROWSTRIDE);
+ }
+
+ /* undo the offsets to the pointers so we can free the malloced memory */
+ circ -= self->radius_x;
+ max -= self->radius_x;
+
+ g_free (circ);
+ g_free (buffer);
+ g_free (max);
+
+ for (i = 0; i < self->radius_y + 1; i++)
+ g_free (buf[i]);
+
+ g_free (buf);
+ g_free (out);
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationgrow.h b/app/operations/gimpoperationgrow.h
new file mode 100644
index 0000000..d680997
--- /dev/null
+++ b/app/operations/gimpoperationgrow.h
@@ -0,0 +1,56 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationgrow.h
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_GROW_H__
+#define __GIMP_OPERATION_GROW_H__
+
+
+#include <gegl-plugin.h>
+
+
+#define GIMP_TYPE_OPERATION_GROW (gimp_operation_grow_get_type ())
+#define GIMP_OPERATION_GROW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_GROW, GimpOperationGrow))
+#define GIMP_OPERATION_GROW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_GROW, GimpOperationGrowClass))
+#define GIMP_IS_OPERATION_GROW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_GROW))
+#define GIMP_IS_OPERATION_GROW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_GROW))
+#define GIMP_OPERATION_GROW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_GROW, GimpOperationGrowClass))
+
+
+typedef struct _GimpOperationGrow GimpOperationGrow;
+typedef struct _GimpOperationGrowClass GimpOperationGrowClass;
+
+struct _GimpOperationGrow
+{
+ GeglOperationFilter parent_instance;
+
+ gint radius_x;
+ gint radius_y;
+};
+
+struct _GimpOperationGrowClass
+{
+ GeglOperationFilterClass parent_class;
+};
+
+
+GType gimp_operation_grow_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_GROW_H__ */
diff --git a/app/operations/gimpoperationhistogramsink.c b/app/operations/gimpoperationhistogramsink.c
new file mode 100644
index 0000000..cb0a7ac
--- /dev/null
+++ b/app/operations/gimpoperationhistogramsink.c
@@ -0,0 +1,244 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationhistogramsink.c
+ * Copyright (C) 2012 Øyvind Kolås
+ *
+ * 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>
+
+#include "operations-types.h"
+
+#include "core/gimphistogram.h"
+
+#include "gimpoperationhistogramsink.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_AUX,
+ PROP_HISTOGRAM
+};
+
+
+static void gimp_operation_histogram_sink_finalize (GObject *object);
+static void gimp_operation_histogram_sink_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_histogram_sink_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_operation_histogram_sink_attach (GeglOperation *operation);
+static void gimp_operation_histogram_sink_prepare (GeglOperation *operation);
+static GeglRectangle
+ gimp_operation_histogram_sink_get_required_for_output (GeglOperation *self,
+ const gchar *input_pad,
+ const GeglRectangle *roi);
+static gboolean gimp_operation_histogram_sink_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_prop,
+ const GeglRectangle *result,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationHistogramSink, gimp_operation_histogram_sink,
+ GEGL_TYPE_OPERATION_SINK)
+
+#define parent_class gimp_operation_histogram_sink_parent_class
+
+
+static void
+gimp_operation_histogram_sink_class_init (GimpOperationHistogramSinkClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+
+ object_class->finalize = gimp_operation_histogram_sink_finalize;
+ object_class->set_property = gimp_operation_histogram_sink_set_property;
+ object_class->get_property = gimp_operation_histogram_sink_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name" , "gimp:histogram-sink",
+ "categories" , "color",
+ "description", "GIMP Histogram sink operation",
+ NULL);
+
+ operation_class->attach = gimp_operation_histogram_sink_attach;
+ operation_class->prepare = gimp_operation_histogram_sink_prepare;
+ operation_class->get_required_for_output = gimp_operation_histogram_sink_get_required_for_output;
+ operation_class->process = gimp_operation_histogram_sink_process;
+
+ g_object_class_install_property (object_class, PROP_AUX,
+ g_param_spec_object ("aux",
+ "Aux",
+ "Auxiliary image buffer input pad.",
+ GEGL_TYPE_BUFFER,
+ G_PARAM_READWRITE |
+ GEGL_PARAM_PAD_INPUT));
+
+ g_object_class_install_property (object_class, PROP_HISTOGRAM,
+ g_param_spec_object ("histogram",
+ "Histogram",
+ "The result histogram",
+ GIMP_TYPE_HISTOGRAM,
+ G_PARAM_READWRITE));
+}
+
+static void
+gimp_operation_histogram_sink_init (GimpOperationHistogramSink *self)
+{
+}
+
+static void
+gimp_operation_histogram_sink_finalize (GObject *object)
+{
+ GimpOperationHistogramSink *sink = GIMP_OPERATION_HISTOGRAM_SINK (object);
+
+ g_clear_object (&sink->histogram);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_operation_histogram_sink_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationHistogramSink *sink = GIMP_OPERATION_HISTOGRAM_SINK (object);
+
+ switch (prop_id)
+ {
+ case PROP_AUX:
+ break;
+
+ case PROP_HISTOGRAM:
+ g_value_set_pointer (value, sink->histogram);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_histogram_sink_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationHistogramSink *sink = GIMP_OPERATION_HISTOGRAM_SINK (object);
+
+ switch (prop_id)
+ {
+ case PROP_AUX:
+ break;
+
+ case PROP_HISTOGRAM:
+ if (sink->histogram)
+ g_object_unref (sink->histogram);
+ sink->histogram = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_histogram_sink_attach (GeglOperation *self)
+{
+ GeglOperation *operation = GEGL_OPERATION (self);
+ GObjectClass *object_class = G_OBJECT_GET_CLASS (self);
+
+ GEGL_OPERATION_CLASS (parent_class)->attach (self);
+
+ gegl_operation_create_pad (operation,
+ g_object_class_find_property (object_class,
+ "aux"));
+}
+
+static void
+gimp_operation_histogram_sink_prepare (GeglOperation *operation)
+{
+ /* XXX gegl_operation_set_format (operation, "input", babl_format ("Y u8")); */
+ gegl_operation_set_format (operation, "aux", babl_format ("Y float"));
+}
+
+static GeglRectangle
+gimp_operation_histogram_sink_get_required_for_output (GeglOperation *self,
+ const gchar *input_pad,
+ const GeglRectangle *roi)
+{
+ /* dunno what to do here, make a wild guess */
+ return *roi;
+}
+
+static gboolean
+gimp_operation_histogram_sink_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_prop,
+ const GeglRectangle *result,
+ gint level)
+{
+ GeglBuffer *input;
+ GeglBuffer *aux;
+
+ if (strcmp (output_prop, "output"))
+ {
+ g_warning ("requested processing of %s pad on a sink", output_prop);
+ return FALSE;
+ }
+
+ input = (GeglBuffer*) gegl_operation_context_dup_object (context, "input");
+ aux = (GeglBuffer*) gegl_operation_context_dup_object (context, "aux");
+
+ if (! input)
+ {
+ g_warning ("received NULL input");
+
+ return FALSE;
+ }
+
+ if (aux)
+ {
+ /* do hist with mask */
+
+ g_printerr ("aux format: %s\n",
+ babl_get_name (gegl_buffer_get_format (aux)));
+
+ g_object_unref (aux);
+ }
+ else
+ {
+ /* without */
+ }
+
+ g_printerr ("input format: %s\n",
+ babl_get_name (gegl_buffer_get_format (input)));
+
+ g_object_unref (input);
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationhistogramsink.h b/app/operations/gimpoperationhistogramsink.h
new file mode 100644
index 0000000..719719c
--- /dev/null
+++ b/app/operations/gimpoperationhistogramsink.h
@@ -0,0 +1,56 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationhistogramsink.h
+ * Copyright (C) 2012 Øyvind Kolås
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_HISTOGRAM_SINK_H__
+#define __GIMP_OPERATION_HISTOGRAM_SINK_H__
+
+
+#include <gegl-plugin.h>
+#include <operation/gegl-operation-sink.h>
+
+
+#define GIMP_TYPE_OPERATION_HISTOGRAM_SINK (gimp_operation_histogram_sink_get_type ())
+#define GIMP_OPERATION_HISTOGRAM_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_HISTOGRAM_SINK, GimpOperationHistogramSink))
+#define GIMP_OPERATION_HISTOGRAM_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_HISTOGRAM_SINK, GimpOperationHistogramSinkClass))
+#define GEGL_IS_OPERATION_HISTOGRAM_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_HISTOGRAM_SINK))
+#define GEGL_IS_OPERATION_HISTOGRAM_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_HISTOGRAM_SINK))
+#define GIMP_OPERATION_HISTOGRAM_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_HISTOGRAM_SINK, GimpOperationHistogramSinkClass))
+
+
+typedef struct _GimpOperationHistogramSink GimpOperationHistogramSink;
+typedef struct _GimpOperationHistogramSinkClass GimpOperationHistogramSinkClass;
+
+struct _GimpOperationHistogramSink
+{
+ GeglOperation parent_instance;
+
+ GimpHistogram *histogram;
+};
+
+struct _GimpOperationHistogramSinkClass
+{
+ GeglOperationSinkClass parent_class;
+};
+
+
+GType gimp_operation_histogram_sink_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_HISTOGRAM_SINK_C__ */
diff --git a/app/operations/gimpoperationhuesaturation.c b/app/operations/gimpoperationhuesaturation.c
new file mode 100644
index 0000000..cdfb7d7
--- /dev/null
+++ b/app/operations/gimpoperationhuesaturation.c
@@ -0,0 +1,302 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationhuesaturation.c
+ * Copyright (C) 2007 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 <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "operations-types.h"
+
+#include "gimphuesaturationconfig.h"
+#include "gimpoperationhuesaturation.h"
+
+#include "gimp-intl.h"
+
+
+static gboolean gimp_operation_hue_saturation_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationHueSaturation, gimp_operation_hue_saturation,
+ GIMP_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_hue_saturation_parent_class
+
+
+static void
+gimp_operation_hue_saturation_class_init (GimpOperationHueSaturationClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+
+ object_class->set_property = gimp_operation_point_filter_set_property;
+ object_class->get_property = gimp_operation_point_filter_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:hue-saturation",
+ "categories", "color",
+ "description", _("Adjust hue, saturation, and lightness"),
+ NULL);
+
+ point_class->process = gimp_operation_hue_saturation_process;
+
+ g_object_class_install_property (object_class,
+ GIMP_OPERATION_POINT_FILTER_PROP_CONFIG,
+ g_param_spec_object ("config",
+ "Config",
+ "The config object",
+ GIMP_TYPE_HUE_SATURATION_CONFIG,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_hue_saturation_init (GimpOperationHueSaturation *self)
+{
+}
+
+static inline gdouble
+map_hue (GimpHueSaturationConfig *config,
+ GimpHueRange range,
+ gdouble value)
+{
+ value += (config->hue[GIMP_HUE_RANGE_ALL] + config->hue[range]) / 2.0;
+
+ if (value < 0)
+ return value + 1.0;
+ else if (value > 1.0)
+ return value - 1.0;
+ else
+ return value;
+}
+
+static inline gdouble
+map_hue_overlap (GimpHueSaturationConfig *config,
+ GimpHueRange primary_range,
+ GimpHueRange secondary_range,
+ gdouble value,
+ gdouble primary_intensity,
+ gdouble secondary_intensity)
+{
+ /* When calculating an overlap between two ranges, interpolate the
+ * hue adjustment from config->hue[primary_range] and
+ * config->hue[secondary_range] BEFORE mapping it to the input
+ * value. This fixes odd edge cases where only one of the ranges
+ * crosses the red/magenta wraparound (bug #527085), or if
+ * adjustments to different channels yield more than 180 degree
+ * difference from each other. (Why anyone would do that is beyond
+ * me, but still.)
+ *
+ * See bugs #527085 and #644032 for examples of such cases.
+ */
+ gdouble v = config->hue[primary_range] * primary_intensity +
+ config->hue[secondary_range] * secondary_intensity;
+
+ value += (config->hue[GIMP_HUE_RANGE_ALL] + v) / 2.0;
+
+ if (value < 0)
+ return value + 1.0;
+ else if (value > 1.0)
+ return value - 1.0;
+ else
+ return value;
+}
+
+static inline gdouble
+map_saturation (GimpHueSaturationConfig *config,
+ GimpHueRange range,
+ gdouble value)
+{
+ gdouble v = config->saturation[GIMP_HUE_RANGE_ALL] + config->saturation[range];
+
+ /* This change affects the way saturation is computed. With the old
+ * code (different code for value < 0), increasing the saturation
+ * affected muted colors very much, and bright colors less. With the
+ * new code, it affects muted colors and bright colors more or less
+ * evenly. For enhancing the color in photos, the new behavior is
+ * exactly what you want. It's hard for me to imagine a case in
+ * which the old behavior is better.
+ */
+ value *= (v + 1.0);
+
+ return CLAMP (value, 0.0, 1.0);
+}
+
+static inline gdouble
+map_lightness (GimpHueSaturationConfig *config,
+ GimpHueRange range,
+ gdouble value)
+{
+ gdouble v = (config->lightness[GIMP_HUE_RANGE_ALL] + config->lightness[range]) / 2.0;
+
+ if (v < 0)
+ return value * (v + 1.0);
+ else
+ return value + (v * (1.0 - value));
+}
+
+static gboolean
+gimp_operation_hue_saturation_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationPointFilter *point = GIMP_OPERATION_POINT_FILTER (operation);
+ GimpHueSaturationConfig *config = GIMP_HUE_SATURATION_CONFIG (point->config);
+ gfloat *src = in_buf;
+ gfloat *dest = out_buf;
+ gfloat overlap;
+
+ if (! config)
+ return FALSE;
+
+ overlap = config->overlap / 2.0;
+
+ while (samples--)
+ {
+ GimpRGB rgb;
+ GimpHSL hsl;
+ gdouble h;
+ gint hue_counter;
+ gint hue = 0;
+ gint secondary_hue = 0;
+ gboolean use_secondary_hue = FALSE;
+ gfloat primary_intensity = 0.0;
+ gfloat secondary_intensity = 0.0;
+
+ rgb.r = src[RED];
+ rgb.g = src[GREEN];
+ rgb.b = src[BLUE];
+ rgb.a = src[ALPHA];
+
+ gimp_rgb_to_hsl (&rgb, &hsl);
+
+ h = hsl.h * 6.0;
+
+ for (hue_counter = 0; hue_counter < 7; hue_counter++)
+ {
+ gdouble hue_threshold = (gdouble) hue_counter + 0.5;
+
+ if (h < ((gdouble) hue_threshold + overlap))
+ {
+ hue = hue_counter;
+
+ if (overlap > 0.0 && h > ((gdouble) hue_threshold - overlap))
+ {
+ use_secondary_hue = TRUE;
+
+ secondary_hue = hue_counter + 1;
+
+ secondary_intensity =
+ (h - (gdouble) hue_threshold + overlap) / (2.0 * overlap);
+
+ primary_intensity = 1.0 - secondary_intensity;
+ }
+ else
+ {
+ use_secondary_hue = FALSE;
+ }
+
+ break;
+ }
+ }
+
+ if (hue >= 6)
+ {
+ hue = 0;
+ use_secondary_hue = FALSE;
+ }
+
+ if (secondary_hue >= 6)
+ {
+ secondary_hue = 0;
+ }
+
+ /* transform into GimpHueRange values */
+ hue++;
+ secondary_hue++;
+
+ if (use_secondary_hue)
+ {
+ hsl.h = map_hue_overlap (config, hue, secondary_hue, hsl.h,
+ primary_intensity, secondary_intensity);
+
+ hsl.s = (map_saturation (config, hue, hsl.s) * primary_intensity +
+ map_saturation (config, secondary_hue, hsl.s) * secondary_intensity);
+
+ hsl.l = (map_lightness (config, hue, hsl.l) * primary_intensity +
+ map_lightness (config, secondary_hue, hsl.l) * secondary_intensity);
+ }
+ else
+ {
+ hsl.h = map_hue (config, hue, hsl.h);
+ hsl.s = map_saturation (config, hue, hsl.s);
+ hsl.l = map_lightness (config, hue, hsl.l);
+ }
+
+ gimp_hsl_to_rgb (&hsl, &rgb);
+
+ dest[RED] = rgb.r;
+ dest[GREEN] = rgb.g;
+ dest[BLUE] = rgb.b;
+ dest[ALPHA] = rgb.a;
+
+ src += 4;
+ dest += 4;
+ }
+
+ return TRUE;
+}
+
+
+/* public functions */
+
+void
+gimp_operation_hue_saturation_map (GimpHueSaturationConfig *config,
+ const GimpRGB *color,
+ GimpHueRange range,
+ GimpRGB *result)
+{
+ GimpHSL hsl;
+
+ g_return_if_fail (GIMP_IS_HUE_SATURATION_CONFIG (config));
+ g_return_if_fail (color != NULL);
+ g_return_if_fail (result != NULL);
+
+ gimp_rgb_to_hsl (color, &hsl);
+
+ hsl.h = map_hue (config, range, hsl.h);
+ hsl.s = map_saturation (config, range, hsl.s);
+ hsl.l = map_lightness (config, range, hsl.l);
+
+ gimp_hsl_to_rgb (&hsl, result);
+}
diff --git a/app/operations/gimpoperationhuesaturation.h b/app/operations/gimpoperationhuesaturation.h
new file mode 100644
index 0000000..b084bbe
--- /dev/null
+++ b/app/operations/gimpoperationhuesaturation.h
@@ -0,0 +1,58 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationhuesaturation.h
+ * Copyright (C) 2007 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_HUE_SATURATION_H__
+#define __GIMP_OPERATION_HUE_SATURATION_H__
+
+
+#include "gimpoperationpointfilter.h"
+
+
+#define GIMP_TYPE_OPERATION_HUE_SATURATION (gimp_operation_hue_saturation_get_type ())
+#define GIMP_OPERATION_HUE_SATURATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_HUE_SATURATION, GimpOperationHueSaturation))
+#define GIMP_OPERATION_HUE_SATURATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_HUE_SATURATION, GimpOperationHueSaturationClass))
+#define GIMP_IS_OPERATION_HUE_SATURATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_HUE_SATURATION))
+#define GIMP_IS_OPERATION_HUE_SATURATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_HUE_SATURATION))
+#define GIMP_OPERATION_HUE_SATURATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_HUE_SATURATION, GimpOperationHueSaturationClass))
+
+
+typedef struct _GimpOperationHueSaturation GimpOperationHueSaturation;
+typedef struct _GimpOperationHueSaturationClass GimpOperationHueSaturationClass;
+
+struct _GimpOperationHueSaturation
+{
+ GimpOperationPointFilter parent_instance;
+};
+
+struct _GimpOperationHueSaturationClass
+{
+ GimpOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_hue_saturation_get_type (void) G_GNUC_CONST;
+
+void gimp_operation_hue_saturation_map (GimpHueSaturationConfig *config,
+ const GimpRGB *color,
+ GimpHueRange range,
+ GimpRGB *result);
+
+
+#endif /* __GIMP_OPERATION_HUE_SATURATION_H__ */
diff --git a/app/operations/gimpoperationlevels.c b/app/operations/gimpoperationlevels.c
new file mode 100644
index 0000000..1bf0b06
--- /dev/null
+++ b/app/operations/gimpoperationlevels.c
@@ -0,0 +1,208 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationlevels.c
+ * Copyright (C) 2007 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 <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "operations-types.h"
+
+#include "gimplevelsconfig.h"
+#include "gimpoperationlevels.h"
+
+#include "gimp-intl.h"
+
+
+static gboolean gimp_operation_levels_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationLevels, gimp_operation_levels,
+ GIMP_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_levels_parent_class
+
+
+static void
+gimp_operation_levels_class_init (GimpOperationLevelsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+
+ object_class->set_property = gimp_operation_point_filter_set_property;
+ object_class->get_property = gimp_operation_point_filter_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:levels",
+ "categories", "color",
+ "description", _("Adjust color levels"),
+ NULL);
+
+ point_class->process = gimp_operation_levels_process;
+
+ g_object_class_install_property (object_class,
+ GIMP_OPERATION_POINT_FILTER_PROP_LINEAR,
+ g_param_spec_boolean ("linear",
+ "Linear",
+ "Whether to operate on linear RGB",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ GIMP_OPERATION_POINT_FILTER_PROP_CONFIG,
+ g_param_spec_object ("config",
+ "Config",
+ "The config object",
+ GIMP_TYPE_LEVELS_CONFIG,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_levels_init (GimpOperationLevels *self)
+{
+}
+
+static inline gdouble
+gimp_operation_levels_map (gdouble value,
+ gdouble low_input,
+ gdouble high_input,
+ gboolean clamp_input,
+ gdouble inv_gamma,
+ gdouble low_output,
+ gdouble high_output,
+ gboolean clamp_output)
+{
+ /* determine input intensity */
+ if (high_input != low_input)
+ value = (value - low_input) / (high_input - low_input);
+ else
+ value = (value - low_input);
+
+ if (clamp_input)
+ value = CLAMP (value, 0.0, 1.0);
+
+ if (inv_gamma != 1.0 && value > 0)
+ value = pow (value, inv_gamma);
+
+ /* determine the output intensity */
+ if (high_output >= low_output)
+ value = value * (high_output - low_output) + low_output;
+ else if (high_output < low_output)
+ value = low_output - value * (low_output - high_output);
+
+ if (clamp_output)
+ value = CLAMP (value, 0.0, 1.0);
+
+ return value;
+}
+
+static gboolean
+gimp_operation_levels_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationPointFilter *point = GIMP_OPERATION_POINT_FILTER (operation);
+ GimpLevelsConfig *config = GIMP_LEVELS_CONFIG (point->config);
+ gfloat *src = in_buf;
+ gfloat *dest = out_buf;
+ gfloat inv_gamma[5];
+ gint channel;
+
+ if (! config)
+ return FALSE;
+
+ for (channel = 0; channel < 5; channel++)
+ {
+ g_return_val_if_fail (config->gamma[channel] != 0.0, FALSE);
+
+ inv_gamma[channel] = 1.0 / config->gamma[channel];
+ }
+
+ while (samples--)
+ {
+ for (channel = 0; channel < 4; channel++)
+ {
+ gdouble value;
+
+ value = gimp_operation_levels_map (src[channel],
+ config->low_input[channel + 1],
+ config->high_input[channel + 1],
+ config->clamp_input,
+ inv_gamma[channel + 1],
+ config->low_output[channel + 1],
+ config->high_output[channel + 1],
+ config->clamp_output);
+
+ /* don't apply the overall curve to the alpha channel */
+ if (channel != ALPHA)
+ value = gimp_operation_levels_map (value,
+ config->low_input[0],
+ config->high_input[0],
+ config->clamp_input,
+ inv_gamma[0],
+ config->low_output[0],
+ config->high_output[0],
+ config->clamp_output);
+
+ dest[channel] = value;
+ }
+
+ src += 4;
+ dest += 4;
+ }
+
+ return TRUE;
+}
+
+
+/* public functions */
+
+gdouble
+gimp_operation_levels_map_input (GimpLevelsConfig *config,
+ GimpHistogramChannel channel,
+ gdouble value)
+{
+ g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), 0.0);
+
+ /* determine input intensity */
+ if (config->high_input[channel] != config->low_input[channel])
+ value = ((value - config->low_input[channel]) /
+ (config->high_input[channel] - config->low_input[channel]));
+ else
+ value = (value - config->low_input[channel]);
+
+ if (config->gamma[channel] != 0.0 && value > 0.0)
+ value = pow (value, 1.0 / config->gamma[channel]);
+
+ return value;
+}
diff --git a/app/operations/gimpoperationlevels.h b/app/operations/gimpoperationlevels.h
new file mode 100644
index 0000000..d977f42
--- /dev/null
+++ b/app/operations/gimpoperationlevels.h
@@ -0,0 +1,57 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationlevels.h
+ * Copyright (C) 2007 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_LEVELS_H__
+#define __GIMP_OPERATION_LEVELS_H__
+
+
+#include "gimpoperationpointfilter.h"
+
+
+#define GIMP_TYPE_OPERATION_LEVELS (gimp_operation_levels_get_type ())
+#define GIMP_OPERATION_LEVELS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_LEVELS, GimpOperationLevels))
+#define GIMP_OPERATION_LEVELS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_LEVELS, GimpOperationLevelsClass))
+#define GIMP_IS_OPERATION_LEVELS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_LEVELS))
+#define GIMP_IS_OPERATION_LEVELS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_LEVELS))
+#define GIMP_OPERATION_LEVELS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_LEVELS, GimpOperationLevelsClass))
+
+
+typedef struct _GimpOperationLevels GimpOperationLevels;
+typedef struct _GimpOperationLevelsClass GimpOperationLevelsClass;
+
+struct _GimpOperationLevels
+{
+ GimpOperationPointFilter parent_instance;
+};
+
+struct _GimpOperationLevelsClass
+{
+ GimpOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_levels_get_type (void) G_GNUC_CONST;
+
+gdouble gimp_operation_levels_map_input (GimpLevelsConfig *config,
+ GimpHistogramChannel channel,
+ gdouble value);
+
+
+#endif /* __GIMP_OPERATION_LEVELS_H__ */
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 ();
+ }
+}
diff --git a/app/operations/gimpoperationmaskcomponents.h b/app/operations/gimpoperationmaskcomponents.h
new file mode 100644
index 0000000..0919a38
--- /dev/null
+++ b/app/operations/gimpoperationmaskcomponents.h
@@ -0,0 +1,68 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationmaskcomponents.h
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_MASK_COMPONENTS_H__
+#define __GIMP_OPERATION_MASK_COMPONENTS_H__
+
+#include <gegl-plugin.h>
+
+
+#define GIMP_TYPE_OPERATION_MASK_COMPONENTS (gimp_operation_mask_components_get_type ())
+#define GIMP_OPERATION_MASK_COMPONENTS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_MASK_COMPONENTS, GimpOperationMaskComponents))
+#define GIMP_OPERATION_MASK_COMPONENTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_MASK_COMPONENTS, GimpOperationMaskComponentsClass))
+#define GIMP_IS_OPERATION_MASK_COMPONENTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_MASK_COMPONENTS))
+#define GIMP_IS_OPERATION_MASK_COMPONENTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_MASK_COMPONENTS))
+#define GIMP_OPERATION_MASK_COMPONENTS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_MASK_COMPONENTS, GimpOperationMaskComponentsClass))
+
+
+typedef struct _GimpOperationMaskComponents GimpOperationMaskComponents;
+typedef struct _GimpOperationMaskComponentsClass GimpOperationMaskComponentsClass;
+
+struct _GimpOperationMaskComponents
+{
+ GeglOperationPointComposer parent_instance;
+
+ GimpComponentMask mask;
+ gdouble alpha;
+
+ guint32 alpha_value;
+ gpointer process;
+ const Babl *format;
+};
+
+struct _GimpOperationMaskComponentsClass
+{
+ GeglOperationPointComposerClass parent_class;
+};
+
+
+GType gimp_operation_mask_components_get_type (void) G_GNUC_CONST;
+
+const Babl * gimp_operation_mask_components_get_format (const Babl *input_format);
+
+void gimp_operation_mask_components_process (const Babl *format,
+ gconstpointer in,
+ gconstpointer aux,
+ gpointer out,
+ gint n,
+ GimpComponentMask mask);
+
+
+#endif /* __GIMP_OPERATION_MASK_COMPONENTS_H__ */
diff --git a/app/operations/gimpoperationoffset.c b/app/operations/gimpoperationoffset.c
new file mode 100644
index 0000000..ee269ed
--- /dev/null
+++ b/app/operations/gimpoperationoffset.c
@@ -0,0 +1,497 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationoffset.c
+ * Copyright (C) 2019 Ell
+ *
+ * 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 <cairo.h>
+#include <gegl-plugin.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "operations-types.h"
+
+#include "gegl/gimp-gegl-loops.h"
+#include "gegl/gimp-gegl-utils.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimpoperationoffset.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_CONTEXT,
+ PROP_TYPE,
+ PROP_X,
+ PROP_Y
+};
+
+
+static void gimp_operation_offset_dispose (GObject *object);
+static void gimp_operation_offset_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_offset_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static GeglRectangle gimp_operation_offset_get_required_for_output (GeglOperation *operation,
+ const gchar *input_pad,
+ const GeglRectangle *output_roi);
+static GeglRectangle gimp_operation_offset_get_invalidated_by_change (GeglOperation *operation,
+ const gchar *input_pad,
+ const GeglRectangle *input_roi);
+static void gimp_operation_offset_prepare (GeglOperation *operation);
+static gboolean gimp_operation_offset_parent_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_pad,
+ const GeglRectangle *result,
+ gint level);
+
+static gboolean gimp_operation_offset_process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *roi,
+ gint level);
+
+static void gimp_operation_offset_get_offset (GimpOperationOffset *offset,
+ gboolean invert,
+ gint *x,
+ gint *y);
+static void gimp_operation_offset_get_rect (GimpOperationOffset *offset,
+ gboolean invert,
+ const GeglRectangle *roi,
+ GeglRectangle *rect);
+
+
+G_DEFINE_TYPE (GimpOperationOffset, gimp_operation_offset,
+ GEGL_TYPE_OPERATION_FILTER)
+
+#define parent_class gimp_operation_offset_parent_class
+
+
+static void
+gimp_operation_offset_class_init (GimpOperationOffsetClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
+
+ object_class->dispose = gimp_operation_offset_dispose;
+ object_class->set_property = gimp_operation_offset_set_property;
+ object_class->get_property = gimp_operation_offset_get_property;
+
+ operation_class->get_required_for_output = gimp_operation_offset_get_required_for_output;
+ operation_class->get_invalidated_by_change = gimp_operation_offset_get_invalidated_by_change;
+ operation_class->prepare = gimp_operation_offset_prepare;
+ operation_class->process = gimp_operation_offset_parent_process;
+
+ operation_class->threaded = FALSE;
+ operation_class->cache_policy = GEGL_CACHE_POLICY_NEVER;
+
+ filter_class->process = gimp_operation_offset_process;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:offset",
+ "categories", "transform",
+ "description", _("Shift the pixels, optionally wrapping them at the borders"),
+ NULL);
+
+ g_object_class_install_property (object_class, PROP_CONTEXT,
+ g_param_spec_object ("context",
+ "Context",
+ "A GimpContext",
+ GIMP_TYPE_CONTEXT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_TYPE,
+ g_param_spec_enum ("type",
+ "Type",
+ "Offset type",
+ GIMP_TYPE_OFFSET_TYPE,
+ GIMP_OFFSET_WRAP_AROUND,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_X,
+ g_param_spec_int ("x",
+ "X Offset",
+ "X offset",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_Y,
+ g_param_spec_int ("y",
+ "Y Offset",
+ "Y offset",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_offset_init (GimpOperationOffset *self)
+{
+}
+
+static void
+gimp_operation_offset_dispose (GObject *object)
+{
+ GimpOperationOffset *offset = GIMP_OPERATION_OFFSET (object);
+
+ g_clear_object (&offset->context);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_operation_offset_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationOffset *offset = GIMP_OPERATION_OFFSET (object);
+
+ switch (property_id)
+ {
+ case PROP_CONTEXT:
+ g_value_set_object (value, offset->context);
+ break;
+
+ case PROP_TYPE:
+ g_value_set_enum (value, offset->type);
+ break;
+
+ case PROP_X:
+ g_value_set_int (value, offset->x);
+ break;
+
+ case PROP_Y:
+ g_value_set_int (value, offset->y);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_offset_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationOffset *offset = GIMP_OPERATION_OFFSET (object);
+
+ switch (property_id)
+ {
+ case PROP_CONTEXT:
+ g_set_object (&offset->context, g_value_get_object (value));
+ break;
+
+ case PROP_TYPE:
+ offset->type = g_value_get_enum (value);
+ break;
+
+ case PROP_X:
+ offset->x = g_value_get_int (value);
+ break;
+
+ case PROP_Y:
+ offset->y = g_value_get_int (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static GeglRectangle
+gimp_operation_offset_get_required_for_output (GeglOperation *operation,
+ const gchar *input_pad,
+ const GeglRectangle *output_roi)
+{
+ GimpOperationOffset *offset = GIMP_OPERATION_OFFSET (operation);
+ GeglRectangle rect;
+
+ gimp_operation_offset_get_rect (offset, TRUE, output_roi, &rect);
+
+ return rect;
+}
+
+static GeglRectangle
+gimp_operation_offset_get_invalidated_by_change (GeglOperation *operation,
+ const gchar *input_pad,
+ const GeglRectangle *input_roi)
+{
+ GimpOperationOffset *offset = GIMP_OPERATION_OFFSET (operation);
+ GeglRectangle rect;
+
+ gimp_operation_offset_get_rect (offset, FALSE, input_roi, &rect);
+
+ return rect;
+}
+
+static void
+gimp_operation_offset_prepare (GeglOperation *operation)
+{
+ const Babl *format;
+
+ format = gegl_operation_get_source_format (operation, "input");
+
+ if (! format)
+ format = babl_format ("RGBA float");
+
+ gegl_operation_set_format (operation, "input", format);
+ gegl_operation_set_format (operation, "output", format);
+}
+
+static gboolean
+gimp_operation_offset_parent_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_pad,
+ const GeglRectangle *result,
+ gint level)
+{
+ GimpOperationOffset *offset = GIMP_OPERATION_OFFSET (operation);
+ GObject *input;
+ gint x;
+ gint y;
+
+ input = gegl_operation_context_get_object (context, "input");
+
+ gimp_operation_offset_get_offset (offset, FALSE, &x, &y);
+
+ if (x == 0 && y == 0)
+ {
+ gegl_operation_context_set_object (context, "output", input);
+
+ return TRUE;
+ }
+ else if (offset->type == GIMP_OFFSET_TRANSPARENT ||
+ (offset->type == GIMP_OFFSET_BACKGROUND &&
+ ! offset->context))
+ {
+ GObject *output = NULL;
+
+ if (input)
+ {
+ GeglRectangle bounds;
+ GeglRectangle extent;
+
+ bounds = gegl_operation_get_bounding_box (GEGL_OPERATION (offset));
+
+ extent = *gegl_buffer_get_extent (GEGL_BUFFER (input));
+
+ extent.x += x;
+ extent.y += y;
+
+ if (gegl_rectangle_intersect (&extent, &extent, &bounds))
+ {
+ output = g_object_new (GEGL_TYPE_BUFFER,
+ "source", input,
+ "x", extent.x,
+ "y", extent.y,
+ "width", extent.width,
+ "height", extent.height,
+ "shift-x", -x,
+ "shift-y", -y,
+ NULL);
+
+ if (gegl_object_get_has_forked (input))
+ gegl_object_set_has_forked (output);
+ }
+ }
+
+ gegl_operation_context_take_object (context, "output", output);
+
+ return TRUE;
+ }
+
+ return GEGL_OPERATION_CLASS (parent_class)->process (operation, context,
+ output_pad, result,
+ level);
+}
+
+static gboolean
+gimp_operation_offset_process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationOffset *offset = GIMP_OPERATION_OFFSET (operation);
+ GeglColor *color = NULL;
+ GeglRectangle bounds;
+ gint x;
+ gint y;
+ gint i;
+
+ bounds = gegl_operation_get_bounding_box (GEGL_OPERATION (offset));
+
+ gimp_operation_offset_get_offset (offset, FALSE, &x, &y);
+
+ if (offset->type == GIMP_OFFSET_BACKGROUND && offset->context)
+ {
+ GimpRGB bg;
+
+ gimp_context_get_background (offset->context, &bg);
+
+ color = gimp_gegl_color_new (&bg);
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ GeglRectangle offset_bounds = bounds;
+ gint offset_x = x;
+ gint offset_y = y;
+
+ if (i & 1)
+ offset_x += x < 0 ? bounds.width : -bounds.width;
+ if (i & 2)
+ offset_y += y < 0 ? bounds.height : -bounds.height;
+
+ offset_bounds.x += offset_x;
+ offset_bounds.y += offset_y;
+
+ if (gegl_rectangle_intersect (&offset_bounds, &offset_bounds, roi))
+ {
+ if (i == 0 || offset->type == GIMP_OFFSET_WRAP_AROUND)
+ {
+ GeglRectangle offset_roi = offset_bounds;
+
+ offset_roi.x -= offset_x;
+ offset_roi.y -= offset_y;
+
+ gimp_gegl_buffer_copy (input, &offset_roi, GEGL_ABYSS_NONE,
+ output, &offset_bounds);
+ }
+ else if (color)
+ {
+ gegl_buffer_set_color (output, &offset_bounds, color);
+ }
+ }
+ }
+
+ g_clear_object (&color);
+
+ return TRUE;
+}
+
+static void
+gimp_operation_offset_get_offset (GimpOperationOffset *offset,
+ gboolean invert,
+ gint *x,
+ gint *y)
+{
+ GeglRectangle bounds;
+
+ bounds = gegl_operation_get_bounding_box (GEGL_OPERATION (offset));
+
+ if (gegl_rectangle_is_empty (&bounds))
+ {
+ *x = 0;
+ *y = 0;
+
+ return;
+ }
+
+ *x = offset->x;
+ *y = offset->y;
+
+ if (invert)
+ {
+ *x = -*x;
+ *y = -*y;
+ }
+
+ if (offset->type == GIMP_OFFSET_WRAP_AROUND)
+ {
+ *x %= bounds.width;
+
+ if (*x < 0)
+ *x += bounds.width;
+
+ *y %= bounds.height;
+
+ if (*y < 0)
+ *y += bounds.height;
+ }
+ else
+ {
+ *x = CLAMP (*x, -bounds.width, +bounds.width);
+ *y = CLAMP (*y, -bounds.height, +bounds.height);
+ }
+}
+
+static void
+gimp_operation_offset_get_rect (GimpOperationOffset *offset,
+ gboolean invert,
+ const GeglRectangle *roi,
+ GeglRectangle *rect)
+{
+ GeglRectangle bounds;
+ gint x;
+ gint y;
+
+ bounds = gegl_operation_get_bounding_box (GEGL_OPERATION (offset));
+
+ if (gegl_rectangle_is_empty (&bounds))
+ {
+ rect->x = 0;
+ rect->y = 0;
+ rect->width = 0;
+ rect->height = 0;
+
+ return;
+ }
+
+ gimp_operation_offset_get_offset (offset, invert, &x, &y);
+
+ *rect = *roi;
+
+ rect->x += x;
+ rect->y += y;
+
+ if (offset->type == GIMP_OFFSET_WRAP_AROUND)
+ {
+ if (rect->x + rect->width > bounds.x + bounds.width)
+ {
+ rect->x = bounds.x;
+ rect->width = bounds.width;
+ }
+
+ if (rect->y + rect->height > bounds.y + bounds.height)
+ {
+ rect->y = bounds.y;
+ rect->height = bounds.height;
+ }
+ }
+
+ gegl_rectangle_intersect (rect, rect, &bounds);
+}
diff --git a/app/operations/gimpoperationoffset.h b/app/operations/gimpoperationoffset.h
new file mode 100644
index 0000000..dcd0e0b
--- /dev/null
+++ b/app/operations/gimpoperationoffset.h
@@ -0,0 +1,55 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationoffset.h
+ * Copyright (C) 2019 Ell
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_OFFSET_H__
+#define __GIMP_OPERATION_OFFSET_H__
+
+
+#define GIMP_TYPE_OPERATION_OFFSET (gimp_operation_offset_get_type ())
+#define GIMP_OPERATION_OFFSET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_OFFSET, GimpOperationOffset))
+#define GIMP_OPERATION_OFFSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_OFFSET, GimpOperationOffsetClass))
+#define GIMP_IS_OPERATION_OFFSET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_OFFSET))
+#define GIMP_IS_OPERATION_OFFSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_OFFSET))
+#define GIMP_OPERATION_OFFSET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_OFFSET, GimpOperationOffsetClass))
+
+
+typedef struct _GimpOperationOffset GimpOperationOffset;
+typedef struct _GimpOperationOffsetClass GimpOperationOffsetClass;
+
+struct _GimpOperationOffset
+{
+ GeglOperationFilter parent_instance;
+
+ GimpContext *context;
+ GimpOffsetType type;
+ gint x;
+ gint y;
+};
+
+struct _GimpOperationOffsetClass
+{
+ GeglOperationFilterClass parent_class;
+};
+
+
+GType gimp_operation_offset_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_OFFSET_H__ */
diff --git a/app/operations/gimpoperationpointfilter.c b/app/operations/gimpoperationpointfilter.c
new file mode 100644
index 0000000..b9bfa1d
--- /dev/null
+++ b/app/operations/gimpoperationpointfilter.c
@@ -0,0 +1,131 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationpointfilter.c
+ * Copyright (C) 2007 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>
+
+#include "operations-types.h"
+
+#include "gimpoperationpointfilter.h"
+
+
+static void gimp_operation_point_filter_finalize (GObject *object);
+static void gimp_operation_point_filter_prepare (GeglOperation *operation);
+
+
+G_DEFINE_ABSTRACT_TYPE (GimpOperationPointFilter, gimp_operation_point_filter,
+ GEGL_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_point_filter_parent_class
+
+
+static void
+gimp_operation_point_filter_class_init (GimpOperationPointFilterClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+
+ object_class->finalize = gimp_operation_point_filter_finalize;
+
+ operation_class->prepare = gimp_operation_point_filter_prepare;
+}
+
+static void
+gimp_operation_point_filter_init (GimpOperationPointFilter *self)
+{
+}
+
+static void
+gimp_operation_point_filter_finalize (GObject *object)
+{
+ GimpOperationPointFilter *self = GIMP_OPERATION_POINT_FILTER (object);
+
+ g_clear_object (&self->config);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+void
+gimp_operation_point_filter_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationPointFilter *self = GIMP_OPERATION_POINT_FILTER (object);
+
+ switch (property_id)
+ {
+ case GIMP_OPERATION_POINT_FILTER_PROP_LINEAR:
+ g_value_set_boolean (value, self->linear);
+ break;
+
+ case GIMP_OPERATION_POINT_FILTER_PROP_CONFIG:
+ g_value_set_object (value, self->config);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+void
+gimp_operation_point_filter_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationPointFilter *self = GIMP_OPERATION_POINT_FILTER (object);
+
+ switch (property_id)
+ {
+ case GIMP_OPERATION_POINT_FILTER_PROP_LINEAR:
+ self->linear = g_value_get_boolean (value);
+ break;
+
+ case GIMP_OPERATION_POINT_FILTER_PROP_CONFIG:
+ if (self->config)
+ g_object_unref (self->config);
+ self->config = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_point_filter_prepare (GeglOperation *operation)
+{
+ GimpOperationPointFilter *self = GIMP_OPERATION_POINT_FILTER (operation);
+ const Babl *space = gegl_operation_get_source_space (operation,
+ "input");
+ const Babl *format;
+
+ if (self->linear)
+ format = babl_format_with_space ("RGBA float", space);
+ else
+ format = babl_format_with_space ("R'G'B'A float", space);
+
+ gegl_operation_set_format (operation, "input", format);
+ gegl_operation_set_format (operation, "output", format);
+}
diff --git a/app/operations/gimpoperationpointfilter.h b/app/operations/gimpoperationpointfilter.h
new file mode 100644
index 0000000..f039af7
--- /dev/null
+++ b/app/operations/gimpoperationpointfilter.h
@@ -0,0 +1,73 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationpointfilter.h
+ * Copyright (C) 2007 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_POINT_FILTER_H__
+#define __GIMP_OPERATION_POINT_FILTER_H__
+
+
+#include <gegl-plugin.h>
+#include <operation/gegl-operation-point-filter.h>
+
+
+enum
+{
+ GIMP_OPERATION_POINT_FILTER_PROP_0,
+ GIMP_OPERATION_POINT_FILTER_PROP_LINEAR,
+ GIMP_OPERATION_POINT_FILTER_PROP_CONFIG
+};
+
+
+#define GIMP_TYPE_OPERATION_POINT_FILTER (gimp_operation_point_filter_get_type ())
+#define GIMP_OPERATION_POINT_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_POINT_FILTER, GimpOperationPointFilter))
+#define GIMP_OPERATION_POINT_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_POINT_FILTER, GimpOperationPointFilterClass))
+#define GIMP_IS_OPERATION_POINT_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_POINT_FILTER))
+#define GIMP_IS_OPERATION_POINT_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_POINT_FILTER))
+#define GIMP_OPERATION_POINT_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_POINT_FILTER, GimpOperationPointFilterClass))
+
+
+typedef struct _GimpOperationPointFilterClass GimpOperationPointFilterClass;
+
+struct _GimpOperationPointFilter
+{
+ GeglOperationPointFilter parent_instance;
+
+ gboolean linear;
+ GObject *config;
+};
+
+struct _GimpOperationPointFilterClass
+{
+ GeglOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_point_filter_get_type (void) G_GNUC_CONST;
+
+void gimp_operation_point_filter_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+void gimp_operation_point_filter_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+
+#endif /* __GIMP_OPERATION_POINT_FILTER_H__ */
diff --git a/app/operations/gimpoperationposterize.c b/app/operations/gimpoperationposterize.c
new file mode 100644
index 0000000..88f5d26
--- /dev/null
+++ b/app/operations/gimpoperationposterize.c
@@ -0,0 +1,165 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationposterize.c
+ * Copyright (C) 2007 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 <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "operations-types.h"
+
+#include "gimpoperationposterize.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_LEVELS
+};
+
+
+static void gimp_operation_posterize_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_posterize_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static gboolean gimp_operation_posterize_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationPosterize, gimp_operation_posterize,
+ GIMP_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_posterize_parent_class
+
+
+static void
+gimp_operation_posterize_class_init (GimpOperationPosterizeClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+
+ object_class->set_property = gimp_operation_posterize_set_property;
+ object_class->get_property = gimp_operation_posterize_get_property;
+
+ point_class->process = gimp_operation_posterize_process;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:posterize",
+ "categories", "color",
+ "description", _("Reduce to a limited set of colors"),
+ NULL);
+
+ GIMP_CONFIG_PROP_INT (object_class, PROP_LEVELS,
+ "levels",
+ _("Posterize levels"),
+ NULL,
+ 2, 256, 3,
+ GIMP_PARAM_STATIC_STRINGS);
+}
+
+static void
+gimp_operation_posterize_init (GimpOperationPosterize *self)
+{
+}
+
+static void
+gimp_operation_posterize_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationPosterize *posterize = GIMP_OPERATION_POSTERIZE (object);
+
+ switch (property_id)
+ {
+ case PROP_LEVELS:
+ g_value_set_int (value, posterize->levels);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_posterize_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationPosterize *posterize = GIMP_OPERATION_POSTERIZE (object);
+
+ switch (property_id)
+ {
+ case PROP_LEVELS:
+ posterize->levels = g_value_get_int (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gimp_operation_posterize_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationPosterize *posterize = GIMP_OPERATION_POSTERIZE (operation);
+ gfloat *src = in_buf;
+ gfloat *dest = out_buf;
+ gfloat levels;
+
+ levels = posterize->levels - 1.0;
+
+ while (samples--)
+ {
+ dest[RED] = RINT (src[RED] * levels) / levels;
+ dest[GREEN] = RINT (src[GREEN] * levels) / levels;
+ dest[BLUE] = RINT (src[BLUE] * levels) / levels;
+ dest[ALPHA] = RINT (src[ALPHA] * levels) / levels;
+
+ src += 4;
+ dest += 4;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationposterize.h b/app/operations/gimpoperationposterize.h
new file mode 100644
index 0000000..06e5731
--- /dev/null
+++ b/app/operations/gimpoperationposterize.h
@@ -0,0 +1,55 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationposterize.h
+ * Copyright (C) 2007 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_POSTERIZE_H__
+#define __GIMP_OPERATION_POSTERIZE_H__
+
+
+#include "gimpoperationpointfilter.h"
+
+
+#define GIMP_TYPE_OPERATION_POSTERIZE (gimp_operation_posterize_get_type ())
+#define GIMP_OPERATION_POSTERIZE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_POSTERIZE, GimpOperationPosterize))
+#define GIMP_OPERATION_POSTERIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_POSTERIZE, GimpOperationPosterizeClass))
+#define GIMP_IS_OPERATION_POSTERIZE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_POSTERIZE))
+#define GIMP_IS_OPERATION_POSTERIZE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_POSTERIZE))
+#define GIMP_OPERATION_POSTERIZE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_POSTERIZE, GimpOperationPosterizeClass))
+
+
+typedef struct _GimpOperationPosterize GimpOperationPosterize;
+typedef struct _GimpOperationPosterizeClass GimpOperationPosterizeClass;
+
+struct _GimpOperationPosterize
+{
+ GimpOperationPointFilter parent_instance;
+
+ gint levels;
+};
+
+struct _GimpOperationPosterizeClass
+{
+ GimpOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_posterize_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_POSTERIZE_H__ */
diff --git a/app/operations/gimpoperationprofiletransform.c b/app/operations/gimpoperationprofiletransform.c
new file mode 100644
index 0000000..2e42459
--- /dev/null
+++ b/app/operations/gimpoperationprofiletransform.c
@@ -0,0 +1,307 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationprofiletransform.c
+ * Copyright (C) 2016 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 <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpcolor/gimpcolor.h"
+
+#include "operations-types.h"
+
+#include "gimpoperationprofiletransform.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_SRC_PROFILE,
+ PROP_SRC_FORMAT,
+ PROP_DEST_PROFILE,
+ PROP_DEST_FORMAT,
+ PROP_RENDERING_INTENT,
+ PROP_BLACK_POINT_COMPENSATION
+};
+
+
+static void gimp_operation_profile_transform_finalize (GObject *object);
+
+static void gimp_operation_profile_transform_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_profile_transform_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_operation_profile_transform_prepare (GeglOperation *operation);
+static gboolean gimp_operation_profile_transform_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationProfileTransform, gimp_operation_profile_transform,
+ GEGL_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_profile_transform_parent_class
+
+
+static void
+gimp_operation_profile_transform_class_init (GimpOperationProfileTransformClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+
+ object_class->finalize = gimp_operation_profile_transform_finalize;
+ object_class->set_property = gimp_operation_profile_transform_set_property;
+ object_class->get_property = gimp_operation_profile_transform_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:profile-transform",
+ "categories", "color",
+ "description",
+ "Transform between two color profiles",
+ NULL);
+
+ operation_class->prepare = gimp_operation_profile_transform_prepare;
+
+ point_class->process = gimp_operation_profile_transform_process;
+
+ g_object_class_install_property (object_class, PROP_SRC_PROFILE,
+ g_param_spec_object ("src-profile",
+ "Source Profile",
+ "Source Profile",
+ GIMP_TYPE_COLOR_PROFILE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_SRC_FORMAT,
+ g_param_spec_pointer ("src-format",
+ "Source Format",
+ "Source Format",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_DEST_PROFILE,
+ g_param_spec_object ("dest-profile",
+ "Destination Profile",
+ "Destination Profile",
+ GIMP_TYPE_COLOR_PROFILE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_DEST_FORMAT,
+ g_param_spec_pointer ("dest-format",
+ "Destination Format",
+ "Destination Format",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_RENDERING_INTENT,
+ g_param_spec_enum ("rendering-intent",
+ "Rendering Intent",
+ "Rendering Intent",
+ GIMP_TYPE_COLOR_RENDERING_INTENT,
+ GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_BLACK_POINT_COMPENSATION,
+ g_param_spec_boolean ("black-point-compensation",
+ "Black Point Compensation",
+ "Black Point Compensation",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_profile_transform_init (GimpOperationProfileTransform *self)
+{
+}
+
+static void
+gimp_operation_profile_transform_finalize (GObject *object)
+{
+ GimpOperationProfileTransform *self = GIMP_OPERATION_PROFILE_TRANSFORM (object);
+
+ g_clear_object (&self->src_profile);
+ g_clear_object (&self->dest_profile);
+ g_clear_object (&self->transform);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_operation_profile_transform_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationProfileTransform *self = GIMP_OPERATION_PROFILE_TRANSFORM (object);
+
+ switch (property_id)
+ {
+ case PROP_SRC_PROFILE:
+ g_value_set_object (value, self->src_profile);
+ break;
+
+ case PROP_SRC_FORMAT:
+ g_value_set_pointer (value, (gpointer) self->src_format);
+ break;
+
+ case PROP_DEST_PROFILE:
+ g_value_set_object (value, self->dest_profile);
+ break;
+
+ case PROP_DEST_FORMAT:
+ g_value_set_pointer (value, (gpointer) self->dest_format);
+ break;
+
+ case PROP_RENDERING_INTENT:
+ g_value_set_enum (value, self->rendering_intent);
+ break;
+
+ case PROP_BLACK_POINT_COMPENSATION:
+ g_value_set_boolean (value, self->black_point_compensation);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_profile_transform_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationProfileTransform *self = GIMP_OPERATION_PROFILE_TRANSFORM (object);
+
+ switch (property_id)
+ {
+ case PROP_SRC_PROFILE:
+ if (self->src_profile)
+ g_object_unref (self->src_profile);
+ self->src_profile = g_value_dup_object (value);
+ break;
+
+ case PROP_SRC_FORMAT:
+ self->src_format = g_value_get_pointer (value);
+ break;
+
+ case PROP_DEST_PROFILE:
+ if (self->dest_profile)
+ g_object_unref (self->dest_profile);
+ self->dest_profile = g_value_dup_object (value);
+ break;
+
+ case PROP_DEST_FORMAT:
+ self->dest_format = g_value_get_pointer (value);
+ break;
+
+ case PROP_RENDERING_INTENT:
+ self->rendering_intent = g_value_get_enum (value);
+ break;
+
+ case PROP_BLACK_POINT_COMPENSATION:
+ self->black_point_compensation = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_profile_transform_prepare (GeglOperation *operation)
+{
+ GimpOperationProfileTransform *self = GIMP_OPERATION_PROFILE_TRANSFORM (operation);
+
+ g_clear_object (&self->transform);
+
+ if (! self->src_format)
+ self->src_format = babl_format ("RGBA float");
+
+ if (! self->dest_format)
+ self->dest_format = babl_format ("RGBA float");
+
+ if (self->src_profile && self->dest_profile)
+ {
+ GimpColorTransformFlags flags = 0;
+
+ if (self->black_point_compensation)
+ flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION;
+
+ flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE;
+
+ self->transform = gimp_color_transform_new (self->src_profile,
+ self->src_format,
+ self->dest_profile,
+ self->dest_format,
+ self->rendering_intent,
+ flags);
+ }
+
+ gegl_operation_set_format (operation, "input", self->src_format);
+ gegl_operation_set_format (operation, "output", self->dest_format);
+}
+
+static gboolean
+gimp_operation_profile_transform_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationProfileTransform *self = GIMP_OPERATION_PROFILE_TRANSFORM (operation);
+ gpointer *src = in_buf;
+ gpointer *dest = out_buf;
+
+ if (self->transform)
+ {
+ gimp_color_transform_process_pixels (self->transform,
+ self->src_format,
+ src,
+ self->dest_format,
+ dest,
+ samples);
+ }
+ else
+ {
+ babl_process (babl_fish (self->src_format,
+ self->dest_format),
+ src, dest, samples);
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationprofiletransform.h b/app/operations/gimpoperationprofiletransform.h
new file mode 100644
index 0000000..52aaf4a
--- /dev/null
+++ b/app/operations/gimpoperationprofiletransform.h
@@ -0,0 +1,65 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationprofiletransform.h
+ * Copyright (C) 2016 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_PROFILE_TRANSFORM_H__
+#define __GIMP_OPERATION_PROFILE_TRANSFORM_H__
+
+
+#include <gegl-plugin.h>
+#include <operation/gegl-operation-point-filter.h>
+
+
+#define GIMP_TYPE_OPERATION_PROFILE_TRANSFORM (gimp_operation_profile_transform_get_type ())
+#define GIMP_OPERATION_PROFILE_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_PROFILE_TRANSFORM, GimpOperationProfileTransform))
+#define GIMP_OPERATION_PROFILE_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_PROFILE_TRANSFORM, GimpOperationProfileTransformClass))
+#define GIMP_IS_OPERATION_PROFILE_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_PROFILE_TRANSFORM))
+#define GIMP_IS_OPERATION_PROFILE_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_PROFILE_TRANSFORM))
+#define GIMP_OPERATION_PROFILE_TRANSFORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_PROFILE_TRANSFORM, GimpOperationProfileTransformClass))
+
+
+typedef struct _GimpOperationProfileTransform GimpOperationProfileTransform;
+typedef struct _GimpOperationProfileTransformClass GimpOperationProfileTransformClass;
+
+struct _GimpOperationProfileTransform
+{
+ GeglOperationPointFilter parent_instance;
+
+ GimpColorProfile *src_profile;
+ const Babl *src_format;
+
+ GimpColorProfile *dest_profile;
+ const Babl *dest_format;
+
+ GimpColorRenderingIntent rendering_intent;
+ gboolean black_point_compensation;
+
+ GimpColorTransform *transform;
+};
+
+struct _GimpOperationProfileTransformClass
+{
+ GeglOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_profile_transform_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_PROFILE_TRANSFORM_H__ */
diff --git a/app/operations/gimpoperationscalarmultiply.c b/app/operations/gimpoperationscalarmultiply.c
new file mode 100644
index 0000000..6416300
--- /dev/null
+++ b/app/operations/gimpoperationscalarmultiply.c
@@ -0,0 +1,189 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationscalarmultiply.c
+ * Copyright (C) 2014 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>
+
+#include "operations-types.h"
+
+#include "gimpoperationscalarmultiply.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_N_COMPONENTS,
+ PROP_FACTOR
+};
+
+
+static void gimp_operation_scalar_multiply_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_scalar_multiply_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_operation_scalar_multiply_prepare (GeglOperation *operation);
+static gboolean gimp_operation_scalar_multiply_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationScalarMultiply, gimp_operation_scalar_multiply,
+ GEGL_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_scalar_multiply_parent_class
+
+
+static void
+gimp_operation_scalar_multiply_class_init (GimpOperationScalarMultiplyClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+
+ object_class->set_property = gimp_operation_scalar_multiply_set_property;
+ object_class->get_property = gimp_operation_scalar_multiply_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:scalar-multiply",
+ "categories", "gimp",
+ "description", "Multiply all floats in a buffer by a factor",
+ NULL);
+
+ operation_class->prepare = gimp_operation_scalar_multiply_prepare;
+
+ point_class->process = gimp_operation_scalar_multiply_process;
+
+ g_object_class_install_property (object_class, PROP_N_COMPONENTS,
+ g_param_spec_int ("n-components",
+ "N Components",
+ "Number of components in the input/output vectors",
+ 1, 16, 2,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_FACTOR,
+ g_param_spec_double ("factor",
+ "Factor",
+ "The scalar factor",
+ G_MINFLOAT, G_MAXFLOAT,
+ 1.0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_scalar_multiply_init (GimpOperationScalarMultiply *self)
+{
+}
+
+static void
+gimp_operation_scalar_multiply_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationScalarMultiply *self = GIMP_OPERATION_SCALAR_MULTIPLY (object);
+
+ switch (property_id)
+ {
+ case PROP_N_COMPONENTS:
+ g_value_set_int (value, self->n_components);
+ break;
+
+ case PROP_FACTOR:
+ g_value_set_double (value, self->factor);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_scalar_multiply_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationScalarMultiply *self = GIMP_OPERATION_SCALAR_MULTIPLY (object);
+
+ switch (property_id)
+ {
+ case PROP_N_COMPONENTS:
+ self->n_components = g_value_get_int (value);
+ break;
+
+ case PROP_FACTOR:
+ self->factor = g_value_get_double (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_scalar_multiply_prepare (GeglOperation *operation)
+{
+ GimpOperationScalarMultiply *self = GIMP_OPERATION_SCALAR_MULTIPLY (operation);
+ const Babl *format;
+
+ format = babl_format_n (babl_type ("float"), self->n_components);
+
+ gegl_operation_set_format (operation, "input", format);
+ gegl_operation_set_format (operation, "output", format);
+}
+
+static gboolean
+gimp_operation_scalar_multiply_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationScalarMultiply *self = GIMP_OPERATION_SCALAR_MULTIPLY (operation);
+ gfloat *src = in_buf;
+ gfloat *dest = out_buf;
+ glong n_samples;
+
+ n_samples = samples * self->n_components;
+
+ while (n_samples--)
+ {
+ *dest = *src * self->factor;
+
+ src++;
+ dest++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationscalarmultiply.h b/app/operations/gimpoperationscalarmultiply.h
new file mode 100644
index 0000000..61122b7
--- /dev/null
+++ b/app/operations/gimpoperationscalarmultiply.h
@@ -0,0 +1,56 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationscalarmultiply.h
+ * Copyright (C) 2014 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_SCALAR_MULTIPLY_H__
+#define __GIMP_OPERATION_SCALAR_MULTIPLY_H__
+
+
+#include <gegl-plugin.h>
+
+
+#define GIMP_TYPE_OPERATION_SCALAR_MULTIPLY (gimp_operation_scalar_multiply_get_type ())
+#define GIMP_OPERATION_SCALAR_MULTIPLY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SCALAR_MULTIPLY, GimpOperationScalarMultiply))
+#define GIMP_OPERATION_SCALAR_MULTIPLY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SCALAR_MULTIPLY, GimpOperationScalarMultiplyClass))
+#define GIMP_IS_OPERATION_SCALAR_MULTIPLY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SCALAR_MULTIPLY))
+#define GIMP_IS_OPERATION_SCALAR_MULTIPLY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SCALAR_MULTIPLY))
+#define GIMP_OPERATION_SCALAR_MULTIPLY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SCALAR_MULTIPLY, GimpOperationScalarMultiplyClass))
+
+
+typedef struct _GimpOperationScalarMultiply GimpOperationScalarMultiply;
+typedef struct _GimpOperationScalarMultiplyClass GimpOperationScalarMultiplyClass;
+
+struct _GimpOperationScalarMultiply
+{
+ GeglOperationPointFilter parent_instance;
+
+ gint n_components;
+ gdouble factor;
+};
+
+struct _GimpOperationScalarMultiplyClass
+{
+ GeglOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_scalar_multiply_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_SCALAR_MULTIPLY_H__ */
diff --git a/app/operations/gimpoperationsemiflatten.c b/app/operations/gimpoperationsemiflatten.c
new file mode 100644
index 0000000..1352f02
--- /dev/null
+++ b/app/operations/gimpoperationsemiflatten.c
@@ -0,0 +1,191 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationsemiflatten.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/>.
+ *
+ * Ported from the semi-flatten plug-in
+ * by Adam D. Moss, adam@foxbox.org. 1998/01/27
+ */
+
+#include "config.h"
+
+#include <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "operations-types.h"
+
+#include "gimpoperationsemiflatten.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_COLOR
+};
+
+
+static void gimp_operation_semi_flatten_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_semi_flatten_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_operation_semi_flatten_prepare (GeglOperation *operation);
+static gboolean gimp_operation_semi_flatten_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationSemiFlatten, gimp_operation_semi_flatten,
+ GEGL_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_semi_flatten_parent_class
+
+
+static void
+gimp_operation_semi_flatten_class_init (GimpOperationSemiFlattenClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+ GimpRGB white;
+
+ object_class->set_property = gimp_operation_semi_flatten_set_property;
+ object_class->get_property = gimp_operation_semi_flatten_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:semi-flatten",
+ "categories", "color",
+ "description", _("Replace partial transparency with a color"),
+ NULL);
+
+ operation_class->prepare = gimp_operation_semi_flatten_prepare;
+
+ point_class->process = gimp_operation_semi_flatten_process;
+
+ gimp_rgba_set (&white, 1.0, 1.0, 1.0, 1.0);
+
+ g_object_class_install_property (object_class, PROP_COLOR,
+ gimp_param_spec_rgb ("color",
+ _("Color"),
+ _("The color"),
+ FALSE, &white,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_semi_flatten_init (GimpOperationSemiFlatten *self)
+{
+}
+
+static void
+gimp_operation_semi_flatten_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationSemiFlatten *self = GIMP_OPERATION_SEMI_FLATTEN (object);
+
+ switch (property_id)
+ {
+ case PROP_COLOR:
+ gimp_value_set_rgb (value, &self->color);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_semi_flatten_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationSemiFlatten *self = GIMP_OPERATION_SEMI_FLATTEN (object);
+
+ switch (property_id)
+ {
+ case PROP_COLOR:
+ gimp_value_get_rgb (value, &self->color);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_semi_flatten_prepare (GeglOperation *operation)
+{
+ const Babl *space = gegl_operation_get_source_space (operation, "input");
+ gegl_operation_set_format (operation, "input", babl_format_with_space ("RGBA float", space));
+ gegl_operation_set_format (operation, "output", babl_format_with_space ("RGBA float", space));
+}
+
+static gboolean
+gimp_operation_semi_flatten_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationSemiFlatten *self = GIMP_OPERATION_SEMI_FLATTEN (operation);
+ gfloat *src = in_buf;
+ gfloat *dest = out_buf;
+
+ while (samples--)
+ {
+ gfloat alpha = src[ALPHA];
+
+ if (alpha <= 0.0 || alpha >= 1.0)
+ {
+ dest[RED] = src[RED];
+ dest[GREEN] = src[GREEN];
+ dest[BLUE] = src[BLUE];
+ dest[ALPHA] = alpha;
+ }
+ else
+ {
+ dest[RED] = src[RED] * alpha + self->color.r * (1.0 - alpha);
+ dest[GREEN] = src[GREEN] * alpha + self->color.g * (1.0 - alpha);
+ dest[BLUE] = src[BLUE] * alpha + self->color.b * (1.0 - alpha);
+ dest[ALPHA] = 1.0;
+ }
+
+ src += 4;
+ dest += 4;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationsemiflatten.h b/app/operations/gimpoperationsemiflatten.h
new file mode 100644
index 0000000..be6311e
--- /dev/null
+++ b/app/operations/gimpoperationsemiflatten.h
@@ -0,0 +1,55 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationsemiflatten.h
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_SEMI_FLATTEN_H__
+#define __GIMP_OPERATION_SEMI_FLATTEN_H__
+
+
+#include <gegl-plugin.h>
+
+
+#define GIMP_TYPE_OPERATION_SEMI_FLATTEN (gimp_operation_semi_flatten_get_type ())
+#define GIMP_OPERATION_SEMI_FLATTEN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SEMI_FLATTEN, GimpOperationSemiFlatten))
+#define GIMP_OPERATION_SEMI_FLATTEN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SEMI_FLATTEN, GimpOperationSemiFlattenClass))
+#define GIMP_IS_OPERATION_SEMI_FLATTEN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SEMI_FLATTEN))
+#define GIMP_IS_OPERATION_SEMI_FLATTEN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SEMI_FLATTEN))
+#define GIMP_OPERATION_SEMI_FLATTEN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SEMI_FLATTEN, GimpOperationSemiFlattenClass))
+
+
+typedef struct _GimpOperationSemiFlatten GimpOperationSemiFlatten;
+typedef struct _GimpOperationSemiFlattenClass GimpOperationSemiFlattenClass;
+
+struct _GimpOperationSemiFlatten
+{
+ GeglOperationPointFilter parent_instance;
+
+ GimpRGB color;
+};
+
+struct _GimpOperationSemiFlattenClass
+{
+ GeglOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_semi_flatten_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_SEMI_FLATTEN_H__ */
diff --git a/app/operations/gimpoperationsetalpha.c b/app/operations/gimpoperationsetalpha.c
new file mode 100644
index 0000000..b41af3c
--- /dev/null
+++ b/app/operations/gimpoperationsetalpha.c
@@ -0,0 +1,188 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationsetalpha.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>
+
+#include "operations-types.h"
+
+#include "gimpoperationsetalpha.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_VALUE
+};
+
+
+static void gimp_operation_set_alpha_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_set_alpha_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_operation_set_alpha_prepare (GeglOperation *operation);
+static gboolean gimp_operation_set_alpha_process (GeglOperation *operation,
+ void *in_buf,
+ void *aux_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationSetAlpha, gimp_operation_set_alpha,
+ GEGL_TYPE_OPERATION_POINT_COMPOSER)
+
+#define parent_class gimp_operation_set_alpha_parent_class
+
+
+static void
+gimp_operation_set_alpha_class_init (GimpOperationSetAlphaClass *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_set_alpha_set_property;
+ object_class->get_property = gimp_operation_set_alpha_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:set-alpha",
+ "categories", "color",
+ "description", "Set a buffer's alpha channel to a value",
+ NULL);
+
+ operation_class->prepare = gimp_operation_set_alpha_prepare;
+
+ point_class->process = gimp_operation_set_alpha_process;
+
+ g_object_class_install_property (object_class, PROP_VALUE,
+ g_param_spec_double ("value",
+ "Value",
+ "The alpha value",
+ 0.0, 1.0, 1.0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_set_alpha_init (GimpOperationSetAlpha *self)
+{
+}
+
+static void
+gimp_operation_set_alpha_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationSetAlpha *self = GIMP_OPERATION_SET_ALPHA (object);
+
+ switch (property_id)
+ {
+ case PROP_VALUE:
+ g_value_set_double (value, self->value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_set_alpha_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationSetAlpha *self = GIMP_OPERATION_SET_ALPHA (object);
+
+ switch (property_id)
+ {
+ case PROP_VALUE:
+ self->value = g_value_get_double (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_set_alpha_prepare (GeglOperation *operation)
+{
+ const Babl *space = gegl_operation_get_source_space (operation, "input");
+ gegl_operation_set_format (operation, "input", babl_format_with_space ("RGBA float", space));
+ gegl_operation_set_format (operation, "aux", babl_format_with_space ("Y float", space));
+ gegl_operation_set_format (operation, "output", babl_format_with_space ("RGBA float", space));
+}
+
+static gboolean
+gimp_operation_set_alpha_process (GeglOperation *operation,
+ void *in_buf,
+ void *aux_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationSetAlpha *self = GIMP_OPERATION_SET_ALPHA (operation);
+ gfloat *src = in_buf;
+ gfloat *aux = aux_buf;
+ gfloat *dest = out_buf;
+
+ if (aux)
+ {
+ while (samples--)
+ {
+ dest[RED] = src[RED];
+ dest[GREEN] = src[GREEN];
+ dest[BLUE] = src[BLUE];
+ dest[ALPHA] = self->value * *aux;
+
+ src += 4;
+ aux += 1;
+ dest += 4;
+ }
+ }
+ else
+ {
+ while (samples--)
+ {
+ dest[RED] = src[RED];
+ dest[GREEN] = src[GREEN];
+ dest[BLUE] = src[BLUE];
+ dest[ALPHA] = self->value;
+
+ src += 4;
+ dest += 4;
+ }
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationsetalpha.h b/app/operations/gimpoperationsetalpha.h
new file mode 100644
index 0000000..741ae1e
--- /dev/null
+++ b/app/operations/gimpoperationsetalpha.h
@@ -0,0 +1,55 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationsetalpha.h
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_SET_ALPHA_H__
+#define __GIMP_OPERATION_SET_ALPHA_H__
+
+
+#include <gegl-plugin.h>
+
+
+#define GIMP_TYPE_OPERATION_SET_ALPHA (gimp_operation_set_alpha_get_type ())
+#define GIMP_OPERATION_SET_ALPHA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SET_ALPHA, GimpOperationSetAlpha))
+#define GIMP_OPERATION_SET_ALPHA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SET_ALPHA, GimpOperationSetAlphaClass))
+#define GIMP_IS_OPERATION_SET_ALPHA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SET_ALPHA))
+#define GIMP_IS_OPERATION_SET_ALPHA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SET_ALPHA))
+#define GIMP_OPERATION_SET_ALPHA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SET_ALPHA, GimpOperationSetAlphaClass))
+
+
+typedef struct _GimpOperationSetAlpha GimpOperationSetAlpha;
+typedef struct _GimpOperationSetAlphaClass GimpOperationSetAlphaClass;
+
+struct _GimpOperationSetAlpha
+{
+ GeglOperationPointComposer parent_instance;
+
+ gdouble value;
+};
+
+struct _GimpOperationSetAlphaClass
+{
+ GeglOperationPointComposerClass parent_class;
+};
+
+
+GType gimp_operation_set_alpha_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_SET_ALPHA_H__ */
diff --git a/app/operations/gimpoperationsettings.c b/app/operations/gimpoperationsettings.c
new file mode 100644
index 0000000..88a6ace
--- /dev/null
+++ b/app/operations/gimpoperationsettings.c
@@ -0,0 +1,320 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationsettings.c
+ * Copyright (C) 2020 Ell
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpconfig/gimpconfig.h"
+
+#include "operations-types.h"
+
+#include "gegl/gimp-gegl-utils.h"
+
+#include "core/gimpdrawable.h"
+#include "core/gimpdrawablefilter.h"
+
+#include "gimpoperationsettings.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_CLIP,
+ PROP_REGION,
+ PROP_MODE,
+ PROP_OPACITY,
+ PROP_COLOR_MANAGED,
+ PROP_GAMMA_HACK
+};
+
+
+static void gimp_operation_settings_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_settings_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+
+G_DEFINE_TYPE (GimpOperationSettings, gimp_operation_settings,
+ GIMP_TYPE_SETTINGS)
+
+#define parent_class gimp_operation_settings_parent_class
+
+
+static void
+gimp_operation_settings_class_init (GimpOperationSettingsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = gimp_operation_settings_set_property;
+ object_class->get_property = gimp_operation_settings_get_property;
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_CLIP,
+ "gimp-clip",
+ _("Clipping"),
+ _("How to clip"),
+ GIMP_TYPE_TRANSFORM_RESIZE,
+ GIMP_TRANSFORM_RESIZE_ADJUST,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_REGION,
+ "gimp-region",
+ NULL, NULL,
+ GIMP_TYPE_FILTER_REGION,
+ GIMP_FILTER_REGION_SELECTION,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_MODE,
+ "gimp-mode",
+ _("Mode"),
+ NULL,
+ GIMP_TYPE_LAYER_MODE,
+ GIMP_LAYER_MODE_REPLACE,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OPACITY,
+ "gimp-opacity",
+ _("Opacity"),
+ NULL,
+ 0.0, 1.0, 1.0,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_COLOR_MANAGED,
+ "gimp-color-managed",
+ _("Color _managed"),
+ NULL,
+ FALSE,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_GAMMA_HACK,
+ "gimp-gamma-hack",
+ "Gamma hack (temp hack, please ignore)",
+ NULL,
+ FALSE,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+}
+
+static void
+gimp_operation_settings_init (GimpOperationSettings *settings)
+{
+}
+
+static void
+gimp_operation_settings_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationSettings *settings = GIMP_OPERATION_SETTINGS (object);
+
+ switch (property_id)
+ {
+ case PROP_CLIP:
+ g_value_set_enum (value, settings->clip);
+ break;
+
+ case PROP_REGION:
+ g_value_set_enum (value, settings->region);
+ break;
+
+ case PROP_MODE:
+ g_value_set_enum (value, settings->mode);
+ break;
+
+ case PROP_OPACITY:
+ g_value_set_double (value, settings->opacity);
+ break;
+
+ case PROP_COLOR_MANAGED:
+ g_value_set_boolean (value, settings->color_managed);
+ break;
+
+ case PROP_GAMMA_HACK:
+ g_value_set_boolean (value, settings->gamma_hack);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_settings_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationSettings *settings = GIMP_OPERATION_SETTINGS (object);
+
+ switch (property_id)
+ {
+ case PROP_CLIP:
+ settings->clip = g_value_get_enum (value);
+ break;
+
+ case PROP_REGION:
+ settings->region = g_value_get_enum (value);
+ break;
+
+ case PROP_MODE:
+ settings->mode = g_value_get_enum (value);
+ break;
+
+ case PROP_OPACITY:
+ settings->opacity = g_value_get_double (value);
+ break;
+
+ case PROP_COLOR_MANAGED:
+ settings->color_managed = g_value_get_boolean (value);
+ break;
+
+ case PROP_GAMMA_HACK:
+ settings->gamma_hack = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+/* public functions */
+
+void
+gimp_operation_settings_sync_drawable_filter (GimpOperationSettings *settings,
+ GimpDrawableFilter *filter)
+{
+ gboolean clip;
+
+ g_return_if_fail (GIMP_IS_OPERATION_SETTINGS (settings));
+ g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter));
+
+ clip = settings->clip == GIMP_TRANSFORM_RESIZE_CLIP ||
+ ! babl_format_has_alpha (gimp_drawable_filter_get_format (filter));
+
+ gimp_drawable_filter_set_region (filter, settings->region);
+ gimp_drawable_filter_set_clip (filter, clip);
+ gimp_drawable_filter_set_mode (filter,
+ settings->mode,
+ GIMP_LAYER_COLOR_SPACE_AUTO,
+ GIMP_LAYER_COLOR_SPACE_AUTO,
+ GIMP_LAYER_COMPOSITE_AUTO);
+ gimp_drawable_filter_set_opacity (filter, settings->opacity);
+ gimp_drawable_filter_set_color_managed (filter, settings->color_managed);
+ gimp_drawable_filter_set_gamma_hack (filter, settings->gamma_hack);
+}
+
+
+/* protected functions */
+
+static const gchar * const base_properties[] =
+{
+ "time",
+ "gimp-clip",
+ "gimp-region",
+ "gimp-mode",
+ "gimp-opacity",
+ "gimp-color-managed",
+ "gimp-gamma-hack"
+};
+
+gboolean
+gimp_operation_settings_config_serialize_base (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data)
+{
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (base_properties); i++)
+ {
+ if (! gimp_config_serialize_property_by_name (config,
+ base_properties[i],
+ writer))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+gimp_operation_settings_config_equal_base (GimpConfig *a,
+ GimpConfig *b)
+{
+ GimpOperationSettings *settings_a = GIMP_OPERATION_SETTINGS (a);
+ GimpOperationSettings *settings_b = GIMP_OPERATION_SETTINGS (b);
+
+ return settings_a->clip == settings_b->clip &&
+ settings_a->region == settings_b->region &&
+ settings_a->mode == settings_b->mode &&
+ settings_a->opacity == settings_b->opacity &&
+ settings_a->color_managed == settings_b->color_managed &&
+ settings_a->gamma_hack == settings_b->gamma_hack;
+}
+
+void
+gimp_operation_settings_config_reset_base (GimpConfig *config)
+{
+ gint i;
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ for (i = 0; i < G_N_ELEMENTS (base_properties); i++)
+ gimp_config_reset_property (G_OBJECT (config), base_properties[i]);
+
+ g_object_thaw_notify (G_OBJECT (config));
+}
+
+gboolean
+gimp_operation_settings_config_copy_base (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags)
+{
+ gint i;
+
+ g_object_freeze_notify (G_OBJECT (dest));
+
+ for (i = 0; i < G_N_ELEMENTS (base_properties); i++)
+ {
+ g_object_unref (g_object_bind_property (src, base_properties[i],
+ dest, base_properties[i],
+ G_BINDING_SYNC_CREATE));
+ }
+
+ g_object_thaw_notify (G_OBJECT (dest));
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationsettings.h b/app/operations/gimpoperationsettings.h
new file mode 100644
index 0000000..a081fce
--- /dev/null
+++ b/app/operations/gimpoperationsettings.h
@@ -0,0 +1,75 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationsettings.h
+ * Copyright (C) 2020 Ell
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_OPERATION_SETTINGS_H__
+#define __GIMP_OPERATION_SETTINGS_H__
+
+
+#include "core/gimpsettings.h"
+
+
+#define GIMP_TYPE_OPERATION_SETTINGS (gimp_operation_settings_get_type ())
+#define GIMP_OPERATION_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SETTINGS, GimpOperationSettings))
+#define GIMP_OPERATION_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SETTINGS, GimpOperationSettingsClass))
+#define GIMP_IS_OPERATION_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SETTINGS))
+#define GIMP_IS_OPERATION_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SETTINGS))
+#define GIMP_OPERATION_SETTINGS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SETTINGS, GimpOperationSettingsClass))
+
+
+typedef struct _GimpOperationSettingsClass GimpOperationSettingsClass;
+
+struct _GimpOperationSettings
+{
+ GimpSettings parent_instance;
+
+ GimpTransformResize clip;
+ GimpFilterRegion region;
+ GimpLayerMode mode;
+ gdouble opacity;
+ gboolean color_managed;
+ gboolean gamma_hack;
+};
+
+struct _GimpOperationSettingsClass
+{
+ GimpSettingsClass parent_class;
+};
+
+
+GType gimp_operation_settings_get_type (void) G_GNUC_CONST;
+
+void gimp_operation_settings_sync_drawable_filter (GimpOperationSettings *settings,
+ GimpDrawableFilter *filter);
+
+
+/* protected */
+
+gboolean gimp_operation_settings_config_serialize_base (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data);
+gboolean gimp_operation_settings_config_equal_base (GimpConfig *a,
+ GimpConfig *b);
+void gimp_operation_settings_config_reset_base (GimpConfig *config);
+gboolean gimp_operation_settings_config_copy_base (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags);
+
+
+#endif /* __GIMP_OPERATION_SETTINGS_H__ */
diff --git a/app/operations/gimpoperationshrink.c b/app/operations/gimpoperationshrink.c
new file mode 100644
index 0000000..c862edb
--- /dev/null
+++ b/app/operations/gimpoperationshrink.c
@@ -0,0 +1,443 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationshrink.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 <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "operations-types.h"
+
+#include "gimpoperationshrink.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_RADIUS_X,
+ PROP_RADIUS_Y,
+ PROP_EDGE_LOCK
+};
+
+
+static void gimp_operation_shrink_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_shrink_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_operation_shrink_prepare (GeglOperation *operation);
+static GeglRectangle
+ gimp_operation_shrink_get_required_for_output (GeglOperation *self,
+ const gchar *input_pad,
+ const GeglRectangle *roi);
+static GeglRectangle
+ gimp_operation_shrink_get_cached_region (GeglOperation *self,
+ const GeglRectangle *roi);
+
+static gboolean gimp_operation_shrink_process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationShrink, gimp_operation_shrink,
+ GEGL_TYPE_OPERATION_FILTER)
+
+#define parent_class gimp_operation_shrink_parent_class
+
+
+static void
+gimp_operation_shrink_class_init (GimpOperationShrinkClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
+
+ object_class->set_property = gimp_operation_shrink_set_property;
+ object_class->get_property = gimp_operation_shrink_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:shrink",
+ "categories", "gimp",
+ "description", "GIMP Shrink operation",
+ NULL);
+
+ operation_class->prepare = gimp_operation_shrink_prepare;
+ operation_class->get_required_for_output = gimp_operation_shrink_get_required_for_output;
+ operation_class->get_cached_region = gimp_operation_shrink_get_cached_region;
+ operation_class->threaded = FALSE;
+
+ filter_class->process = gimp_operation_shrink_process;
+
+ g_object_class_install_property (object_class, PROP_RADIUS_X,
+ g_param_spec_int ("radius-x",
+ "Radius X",
+ "Shrink radius in X diection",
+ 1, 2342, 1,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_RADIUS_Y,
+ g_param_spec_int ("radius-y",
+ "Radius Y",
+ "Shrink radius in Y diection",
+ 1, 2342, 1,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_EDGE_LOCK,
+ g_param_spec_boolean ("edge-lock",
+ "Edge Lock",
+ "Shrink from border",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_shrink_init (GimpOperationShrink *self)
+{
+}
+
+static void
+gimp_operation_shrink_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationShrink *self = GIMP_OPERATION_SHRINK (object);
+
+ switch (property_id)
+ {
+ case PROP_RADIUS_X:
+ g_value_set_int (value, self->radius_x);
+ break;
+
+ case PROP_RADIUS_Y:
+ g_value_set_int (value, self->radius_y);
+ break;
+
+ case PROP_EDGE_LOCK:
+ g_value_set_boolean (value, self->edge_lock);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_shrink_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationShrink *self = GIMP_OPERATION_SHRINK (object);
+
+ switch (property_id)
+ {
+ case PROP_RADIUS_X:
+ self->radius_x = g_value_get_int (value);
+ break;
+
+ case PROP_RADIUS_Y:
+ self->radius_y = g_value_get_int (value);
+ break;
+
+ case PROP_EDGE_LOCK:
+ self->edge_lock = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_shrink_prepare (GeglOperation *operation)
+{
+ const Babl *space = gegl_operation_get_source_space (operation, "input");
+ gegl_operation_set_format (operation, "input", babl_format_with_space ("Y float", space));
+ gegl_operation_set_format (operation, "output", babl_format_with_space ("Y float", space));
+}
+
+static GeglRectangle
+gimp_operation_shrink_get_required_for_output (GeglOperation *self,
+ const gchar *input_pad,
+ const GeglRectangle *roi)
+{
+ return *gegl_operation_source_get_bounding_box (self, "input");
+}
+
+static GeglRectangle
+gimp_operation_shrink_get_cached_region (GeglOperation *self,
+ const GeglRectangle *roi)
+{
+ return *gegl_operation_source_get_bounding_box (self, "input");
+}
+
+static void
+compute_border (gint16 *circ,
+ guint16 xradius,
+ guint16 yradius)
+{
+ gint32 i;
+ gint32 diameter = xradius * 2 + 1;
+ gdouble tmp;
+
+ for (i = 0; i < diameter; i++)
+ {
+ if (i > xradius)
+ tmp = (i - xradius) - 0.5;
+ else if (i < xradius)
+ tmp = (xradius - i) - 0.5;
+ else
+ tmp = 0.0;
+
+ circ[i] = RINT (yradius /
+ (gdouble) xradius * sqrt (SQR (xradius) - SQR (tmp)));
+ }
+}
+
+static inline void
+rotate_pointers (gfloat **p,
+ guint32 n)
+{
+ guint32 i;
+ gfloat *tmp;
+
+ tmp = p[0];
+
+ for (i = 0; i < n - 1; i++)
+ p[i] = p[i + 1];
+
+ p[i] = tmp;
+}
+
+static gboolean
+gimp_operation_shrink_process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *roi,
+ gint level)
+{
+ /* Pretty much the same as fatten_region only different.
+ * Blame all bugs in this function on jaycox@gimp.org
+ *
+ * If edge_lock is true we assume that pixels outside the region we
+ * are passed are identical to the edge pixels. If edge_lock is
+ * false, we assume that pixels outside the region are 0
+ */
+ GimpOperationShrink *self = GIMP_OPERATION_SHRINK (operation);
+ const Babl *input_format = babl_format ("Y float");
+ const Babl *output_format = babl_format ("Y float");
+ gint32 i, j, x, y;
+ gfloat **buf; /* caches the the region's pixels */
+ gfloat *out; /* holds the new scan line we are computing */
+ gfloat **max; /* caches the smallest values for each column */
+ gint16 *circ; /* holds the y coords of the filter's mask */
+ gfloat last_max;
+ gint16 last_index;
+ gfloat *buffer;
+ gint buffer_size;
+
+ max = g_new (gfloat *, roi->width + 2 * self->radius_x);
+ buf = g_new (gfloat *, self->radius_y + 1);
+
+ for (i = 0; i < self->radius_y + 1; i++)
+ buf[i] = g_new (gfloat, roi->width);
+
+ buffer_size = (roi->width+ 2 * self->radius_x + 1) * (self->radius_y + 1);
+ buffer = g_new (gfloat, buffer_size);
+
+ if (self->edge_lock)
+ {
+ for (i = 0; i < buffer_size; i++)
+ buffer[i] = 1.0;
+ }
+ else
+ {
+ memset (buffer, 0, buffer_size * sizeof (gfloat));
+ }
+
+ for (i = 0; i < roi->width + 2 * self->radius_x; i++)
+ {
+ if (i < self->radius_x)
+ {
+ if (self->edge_lock)
+ max[i] = buffer;
+ else
+ max[i] = &buffer[(self->radius_y + 1) * (roi->width + self->radius_x)];
+ }
+ else if (i < roi->width + self->radius_x)
+ {
+ max[i] = &buffer[(self->radius_y + 1) * (i - self->radius_x)];
+ }
+ else
+ {
+ if (self->edge_lock)
+ max[i] = &buffer[(self->radius_y + 1) * (roi->width + self->radius_x - 1)];
+ else
+ max[i] = &buffer[(self->radius_y + 1) * (roi->width + self->radius_x)];
+ }
+ }
+
+ if (! self->edge_lock)
+ for (j = 0 ; j < self->radius_y + 1; j++)
+ max[0][j] = 0.0;
+
+ /* offset the max pointer by self->radius_x so the range of the
+ * array is [-self->radius_x] to [roi->width + self->radius_x]
+ */
+ max += self->radius_x;
+
+ out = g_new (gfloat, roi->width);
+
+ circ = g_new (gint16, 2 * self->radius_x + 1);
+ compute_border (circ, self->radius_x, self->radius_y);
+
+ /* offset the circ pointer by self->radius_x so the range of the
+ * array is [-self->radius_x] to [self->radius_x]
+ */
+ circ += self->radius_x;
+
+ for (i = 0; i < self->radius_y && i < roi->height; i++) /* load top of image */
+ gegl_buffer_get (input,
+ GEGL_RECTANGLE (roi->x, roi->y + i,
+ roi->width, 1),
+ 1.0, input_format, buf[i + 1],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ if (self->edge_lock)
+ memcpy (buf[0], buf[1], roi->width * sizeof (gfloat));
+ else
+ memset (buf[0], 0, roi->width * sizeof (gfloat));
+
+ for (x = 0; x < roi->width; x++) /* set up max for top of image */
+ {
+ max[x][0] = buf[0][x];
+
+ for (j = 1; j < self->radius_y + 1; j++)
+ max[x][j] = MIN (buf[j][x], max[x][j - 1]);
+ }
+
+ for (y = 0; y < roi->height; y++)
+ {
+ rotate_pointers (buf, self->radius_y + 1);
+
+ if (y < roi->height - self->radius_y)
+ gegl_buffer_get (input,
+ GEGL_RECTANGLE (roi->x, roi->y + y + self->radius_y,
+ roi->width, 1),
+ 1.0, input_format, buf[self->radius_y],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ else if (self->edge_lock)
+ memcpy (buf[self->radius_y], buf[self->radius_y - 1],
+ roi->width * sizeof (gfloat));
+ else
+ memset (buf[self->radius_y], 0, roi->width * sizeof (gfloat));
+
+ for (x = 0 ; x < roi->width; x++) /* update max array */
+ {
+ for (i = self->radius_y; i > 0; i--)
+ max[x][i] = MIN (MIN (max[x][i - 1], buf[i - 1][x]), buf[i][x]);
+
+ max[x][0] = buf[0][x];
+ }
+
+ last_max = max[0][circ[-1]];
+ last_index = 0;
+
+ for (x = 0 ; x < roi->width; x++) /* render scan line */
+ {
+ last_index--;
+
+ if (last_index >= 0)
+ {
+ if (last_max <= 0.0)
+ {
+ out[x] = 0.0;
+ }
+ else
+ {
+ last_max = 1.0;
+
+ for (i = self->radius_x; i >= 0; i--)
+ if (last_max > max[x + i][circ[i]])
+ {
+ last_max = max[x + i][circ[i]];
+ last_index = i;
+ }
+
+ out[x] = last_max;
+ }
+ }
+ else
+ {
+ last_index = self->radius_x;
+ last_max = max[x + self->radius_x][circ[self->radius_x]];
+
+ for (i = self->radius_x - 1; i >= -self->radius_x; i--)
+ if (last_max > max[x + i][circ[i]])
+ {
+ last_max = max[x + i][circ[i]];
+ last_index = i;
+ }
+
+ out[x] = last_max;
+ }
+ }
+
+ gegl_buffer_set (output,
+ GEGL_RECTANGLE (roi->x, roi->y + y,
+ roi->width, 1),
+ 0, output_format, out,
+ GEGL_AUTO_ROWSTRIDE);
+ }
+
+ /* undo the offsets to the pointers so we can free the malloced memory */
+ circ -= self->radius_x;
+ max -= self->radius_x;
+
+ /* free the memory */
+ g_free (circ);
+ g_free (buffer);
+ g_free (max);
+
+ for (i = 0; i < self->radius_y + 1; i++)
+ g_free (buf[i]);
+
+ g_free (buf);
+ g_free (out);
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationshrink.h b/app/operations/gimpoperationshrink.h
new file mode 100644
index 0000000..1ba58e6
--- /dev/null
+++ b/app/operations/gimpoperationshrink.h
@@ -0,0 +1,57 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationshrink.h
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_SHRINK_H__
+#define __GIMP_OPERATION_SHRINK_H__
+
+
+#include <gegl-plugin.h>
+
+
+#define GIMP_TYPE_OPERATION_SHRINK (gimp_operation_shrink_get_type ())
+#define GIMP_OPERATION_SHRINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SHRINK, GimpOperationShrink))
+#define GIMP_OPERATION_SHRINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SHRINK, GimpOperationShrinkClass))
+#define GIMP_IS_OPERATION_SHRINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SHRINK))
+#define GIMP_IS_OPERATION_SHRINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SHRINK))
+#define GIMP_OPERATION_SHRINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SHRINK, GimpOperationShrinkClass))
+
+
+typedef struct _GimpOperationShrink GimpOperationShrink;
+typedef struct _GimpOperationShrinkClass GimpOperationShrinkClass;
+
+struct _GimpOperationShrink
+{
+ GeglOperationFilter parent_instance;
+
+ gint radius_x;
+ gint radius_y;
+ gboolean edge_lock;
+};
+
+struct _GimpOperationShrinkClass
+{
+ GeglOperationFilterClass parent_class;
+};
+
+
+GType gimp_operation_shrink_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_SHRINK_H__ */
diff --git a/app/operations/gimpoperationthreshold.c b/app/operations/gimpoperationthreshold.c
new file mode 100644
index 0000000..e33a9ec
--- /dev/null
+++ b/app/operations/gimpoperationthreshold.c
@@ -0,0 +1,232 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationthreshold.c
+ * Copyright (C) 2007 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 <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "operations-types.h"
+
+#include "gimpoperationthreshold.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_CHANNEL,
+ PROP_LOW,
+ PROP_HIGH
+};
+
+
+static void gimp_operation_threshold_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_threshold_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static gboolean gimp_operation_threshold_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationThreshold, gimp_operation_threshold,
+ GIMP_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_threshold_parent_class
+
+
+static void
+gimp_operation_threshold_class_init (GimpOperationThresholdClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+
+ object_class->set_property = gimp_operation_threshold_set_property;
+ object_class->get_property = gimp_operation_threshold_get_property;
+
+ point_class->process = gimp_operation_threshold_process;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:threshold",
+ "categories", "color",
+ "description", _("Reduce image to two colors using a threshold"),
+ NULL);
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_CHANNEL,
+ "channel",
+ _("Channel"),
+ NULL,
+ GIMP_TYPE_HISTOGRAM_CHANNEL,
+ GIMP_HISTOGRAM_VALUE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LOW,
+ "low",
+ _("Low threshold"),
+ NULL,
+ 0.0, 1.0, 0.5,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_HIGH,
+ "high",
+ _("High threshold"),
+ NULL,
+ 0.0, 1.0, 1.0,
+ GIMP_PARAM_STATIC_STRINGS);
+}
+
+static void
+gimp_operation_threshold_init (GimpOperationThreshold *self)
+{
+}
+
+static void
+gimp_operation_threshold_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationThreshold *self = GIMP_OPERATION_THRESHOLD (object);
+
+ switch (property_id)
+ {
+ case PROP_CHANNEL:
+ g_value_set_enum (value, self->channel);
+ break;
+
+ case PROP_LOW:
+ g_value_set_double (value, self->low);
+ break;
+
+ case PROP_HIGH:
+ g_value_set_double (value, self->high);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_threshold_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationThreshold *self = GIMP_OPERATION_THRESHOLD (object);
+
+ switch (property_id)
+ {
+ case PROP_CHANNEL:
+ self->channel = g_value_get_enum (value);
+ break;
+
+ case PROP_LOW:
+ self->low = g_value_get_double (value);
+ break;
+
+ case PROP_HIGH:
+ self->high = g_value_get_double (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+ static gboolean
+gimp_operation_threshold_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationThreshold *threshold = GIMP_OPERATION_THRESHOLD (operation);
+ gfloat *src = in_buf;
+ gfloat *dest = out_buf;
+
+ while (samples--)
+ {
+ gfloat value = 0.0;
+
+ switch (threshold->channel)
+ {
+ case GIMP_HISTOGRAM_VALUE:
+ value = MAX (src[RED], src[GREEN]);
+ value = MAX (value, src[BLUE]);
+ break;
+
+ case GIMP_HISTOGRAM_RED:
+ value = src[RED];
+ break;
+
+ case GIMP_HISTOGRAM_GREEN:
+ value = src[GREEN];
+ break;
+
+ case GIMP_HISTOGRAM_BLUE:
+ value = src[BLUE];
+ break;
+
+ case GIMP_HISTOGRAM_ALPHA:
+ value = src[ALPHA];
+ break;
+
+ case GIMP_HISTOGRAM_RGB:
+ value = MIN (src[RED], src[GREEN]);
+ value = MIN (value, src[BLUE]);
+ break;
+
+ case GIMP_HISTOGRAM_LUMINANCE:
+ value = GIMP_RGB_LUMINANCE (src[RED], src[GREEN], src[BLUE]);
+ break;
+ }
+
+ value = (value >= threshold->low && value <= threshold->high) ? 1.0 : 0.0;
+
+ dest[RED] = value;
+ dest[GREEN] = value;
+ dest[BLUE] = value;
+ dest[ALPHA] = src[ALPHA];
+
+ src += 4;
+ dest += 4;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationthreshold.h b/app/operations/gimpoperationthreshold.h
new file mode 100644
index 0000000..dcdf52b
--- /dev/null
+++ b/app/operations/gimpoperationthreshold.h
@@ -0,0 +1,57 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationthreshold.h
+ * Copyright (C) 2007 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_THRESHOLD_H__
+#define __GIMP_OPERATION_THRESHOLD_H__
+
+
+#include "gimpoperationpointfilter.h"
+
+
+#define GIMP_TYPE_OPERATION_THRESHOLD (gimp_operation_threshold_get_type ())
+#define GIMP_OPERATION_THRESHOLD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_THRESHOLD, GimpOperationThreshold))
+#define GIMP_OPERATION_THRESHOLD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_THRESHOLD, GimpOperationThresholdClass))
+#define GIMP_IS_OPERATION_THRESHOLD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_THRESHOLD))
+#define GIMP_IS_OPERATION_THRESHOLD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_THRESHOLD))
+#define GIMP_OPERATION_THRESHOLD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_THRESHOLD, GimpOperationThresholdClass))
+
+
+typedef struct _GimpOperationThreshold GimpOperationThreshold;
+typedef struct _GimpOperationThresholdClass GimpOperationThresholdClass;
+
+struct _GimpOperationThreshold
+{
+ GimpOperationPointFilter parent_instance;
+
+ GimpHistogramChannel channel;
+ gdouble low;
+ gdouble high;
+};
+
+struct _GimpOperationThresholdClass
+{
+ GimpOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_threshold_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_THRESHOLD_H__ */
diff --git a/app/operations/gimpoperationthresholdalpha.c b/app/operations/gimpoperationthresholdalpha.c
new file mode 100644
index 0000000..94cbaf5
--- /dev/null
+++ b/app/operations/gimpoperationthresholdalpha.c
@@ -0,0 +1,178 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationthresholdalpha.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/>.
+ *
+ * Ported from the threshold-alpha plug-in
+ * Copyright (C) 1997 Shuji Narazaki <narazaki@InetQ.or.jp>
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+
+#include "operations-types.h"
+
+#include "gimpoperationthresholdalpha.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_VALUE
+};
+
+
+static void gimp_operation_threshold_alpha_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_threshold_alpha_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_operation_threshold_alpha_prepare (GeglOperation *operation);
+static gboolean gimp_operation_threshold_alpha_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationThresholdAlpha, gimp_operation_threshold_alpha,
+ GEGL_TYPE_OPERATION_POINT_FILTER)
+
+#define parent_class gimp_operation_threshold_alpha_parent_class
+
+
+static void
+gimp_operation_threshold_alpha_class_init (GimpOperationThresholdAlphaClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointFilterClass *point_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+
+ object_class->set_property = gimp_operation_threshold_alpha_set_property;
+ object_class->get_property = gimp_operation_threshold_alpha_get_property;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:threshold-alpha",
+ "categories", "color",
+ "description",
+ _("Make transparency all-or-nothing, by "
+ "thresholding the alpha channel to a value"),
+ NULL);
+
+ operation_class->prepare = gimp_operation_threshold_alpha_prepare;
+
+ point_class->process = gimp_operation_threshold_alpha_process;
+
+ g_object_class_install_property (object_class, PROP_VALUE,
+ g_param_spec_double ("value",
+ _("Value"),
+ _("The alpha value"),
+ 0.0, 1.0, 0.5,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_operation_threshold_alpha_init (GimpOperationThresholdAlpha *self)
+{
+}
+
+static void
+gimp_operation_threshold_alpha_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationThresholdAlpha *self = GIMP_OPERATION_THRESHOLD_ALPHA (object);
+
+ switch (property_id)
+ {
+ case PROP_VALUE:
+ g_value_set_double (value, self->value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_threshold_alpha_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationThresholdAlpha *self = GIMP_OPERATION_THRESHOLD_ALPHA (object);
+
+ switch (property_id)
+ {
+ case PROP_VALUE:
+ self->value = g_value_get_double (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_threshold_alpha_prepare (GeglOperation *operation)
+{
+ const Babl *space = gegl_operation_get_source_space (operation, "input");
+ gegl_operation_set_format (operation, "input", babl_format_with_space ("RGBA float", space));
+ gegl_operation_set_format (operation, "output", babl_format_with_space ("RGBA float", space));
+}
+
+static gboolean
+gimp_operation_threshold_alpha_process (GeglOperation *operation,
+ void *in_buf,
+ void *out_buf,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationThresholdAlpha *self = GIMP_OPERATION_THRESHOLD_ALPHA (operation);
+ gfloat *src = in_buf;
+ gfloat *dest = out_buf;
+
+ while (samples--)
+ {
+ dest[RED] = src[RED];
+ dest[GREEN] = src[GREEN];
+ dest[BLUE] = src[BLUE];
+
+ if (src[ALPHA] > self->value)
+ dest[ALPHA] = 1.0;
+ else
+ dest[ALPHA] = 0.0;
+
+ src += 4;
+ dest += 4;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/gimpoperationthresholdalpha.h b/app/operations/gimpoperationthresholdalpha.h
new file mode 100644
index 0000000..0339289
--- /dev/null
+++ b/app/operations/gimpoperationthresholdalpha.h
@@ -0,0 +1,56 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationthresholdalpha.h
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_THRESHOLD_ALPHA_H__
+#define __GIMP_OPERATION_THRESHOLD_ALPHA_H__
+
+
+#include <gegl-plugin.h>
+#include <operation/gegl-operation-point-filter.h>
+
+
+#define GIMP_TYPE_OPERATION_THRESHOLD_ALPHA (gimp_operation_threshold_alpha_get_type ())
+#define GIMP_OPERATION_THRESHOLD_ALPHA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_THRESHOLD_ALPHA, GimpOperationThresholdAlpha))
+#define GIMP_OPERATION_THRESHOLD_ALPHA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_THRESHOLD_ALPHA, GimpOperationThresholdAlphaClass))
+#define GIMP_IS_OPERATION_THRESHOLD_ALPHA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_THRESHOLD_ALPHA))
+#define GIMP_IS_OPERATION_THRESHOLD_ALPHA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_THRESHOLD_ALPHA))
+#define GIMP_OPERATION_THRESHOLD_ALPHA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_THRESHOLD_ALPHA, GimpOperationThresholdAlphaClass))
+
+
+typedef struct _GimpOperationThresholdAlpha GimpOperationThresholdAlpha;
+typedef struct _GimpOperationThresholdAlphaClass GimpOperationThresholdAlphaClass;
+
+struct _GimpOperationThresholdAlpha
+{
+ GeglOperationPointFilter parent_instance;
+
+ gdouble value;
+};
+
+struct _GimpOperationThresholdAlphaClass
+{
+ GeglOperationPointFilterClass parent_class;
+};
+
+
+GType gimp_operation_threshold_alpha_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_THRESHOLD_ALPHA_H__ */
diff --git a/app/operations/layer-modes-legacy/Makefile.am b/app/operations/layer-modes-legacy/Makefile.am
new file mode 100644
index 0000000..b8241da
--- /dev/null
+++ b/app/operations/layer-modes-legacy/Makefile.am
@@ -0,0 +1,54 @@
+## Process this file with automake to produce Makefile.in
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"Gimp-Layer-Modes-Legacy\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/app \
+ -I$(top_srcdir)/app \
+ $(CAIRO_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ -I$(includedir)
+
+noinst_LIBRARIES = \
+ libapplayermodeslegacy.a
+
+libapplayermodeslegacy_a_SOURCES = \
+ gimpoperationadditionlegacy.c \
+ gimpoperationadditionlegacy.h \
+ gimpoperationburnlegacy.c \
+ gimpoperationburnlegacy.h \
+ gimpoperationdarkenonlylegacy.c \
+ gimpoperationdarkenonlylegacy.h \
+ gimpoperationdifferencelegacy.c \
+ gimpoperationdifferencelegacy.h \
+ gimpoperationdividelegacy.c \
+ gimpoperationdividelegacy.h \
+ gimpoperationdodgelegacy.c \
+ gimpoperationdodgelegacy.h \
+ gimpoperationgrainextractlegacy.c \
+ gimpoperationgrainextractlegacy.h \
+ gimpoperationgrainmergelegacy.c \
+ gimpoperationgrainmergelegacy.h \
+ gimpoperationhardlightlegacy.c \
+ gimpoperationhardlightlegacy.h \
+ gimpoperationhslcolorlegacy.c \
+ gimpoperationhslcolorlegacy.h \
+ gimpoperationhsvhuelegacy.c \
+ gimpoperationhsvhuelegacy.h \
+ gimpoperationhsvsaturationlegacy.c \
+ gimpoperationhsvsaturationlegacy.h \
+ gimpoperationhsvvaluelegacy.c \
+ gimpoperationhsvvaluelegacy.h \
+ gimpoperationlightenonlylegacy.c \
+ gimpoperationlightenonlylegacy.h \
+ gimpoperationmultiplylegacy.c \
+ gimpoperationmultiplylegacy.h \
+ gimpoperationscreenlegacy.c \
+ gimpoperationscreenlegacy.h \
+ gimpoperationsoftlightlegacy.c \
+ gimpoperationsoftlightlegacy.h \
+ gimpoperationsubtractlegacy.c \
+ gimpoperationsubtractlegacy.h
+
diff --git a/app/operations/layer-modes-legacy/Makefile.in b/app/operations/layer-modes-legacy/Makefile.in
new file mode 100644
index 0000000..f51eeed
--- /dev/null
+++ b/app/operations/layer-modes-legacy/Makefile.in
@@ -0,0 +1,1038 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = app/operations/layer-modes-legacy
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_@AM_V@)
+am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
+am__v_AR_0 = @echo " AR " $@;
+am__v_AR_1 =
+libapplayermodeslegacy_a_AR = $(AR) $(ARFLAGS)
+libapplayermodeslegacy_a_LIBADD =
+am_libapplayermodeslegacy_a_OBJECTS = \
+ gimpoperationadditionlegacy.$(OBJEXT) \
+ gimpoperationburnlegacy.$(OBJEXT) \
+ gimpoperationdarkenonlylegacy.$(OBJEXT) \
+ gimpoperationdifferencelegacy.$(OBJEXT) \
+ gimpoperationdividelegacy.$(OBJEXT) \
+ gimpoperationdodgelegacy.$(OBJEXT) \
+ gimpoperationgrainextractlegacy.$(OBJEXT) \
+ gimpoperationgrainmergelegacy.$(OBJEXT) \
+ gimpoperationhardlightlegacy.$(OBJEXT) \
+ gimpoperationhslcolorlegacy.$(OBJEXT) \
+ gimpoperationhsvhuelegacy.$(OBJEXT) \
+ gimpoperationhsvsaturationlegacy.$(OBJEXT) \
+ gimpoperationhsvvaluelegacy.$(OBJEXT) \
+ gimpoperationlightenonlylegacy.$(OBJEXT) \
+ gimpoperationmultiplylegacy.$(OBJEXT) \
+ gimpoperationscreenlegacy.$(OBJEXT) \
+ gimpoperationsoftlightlegacy.$(OBJEXT) \
+ gimpoperationsubtractlegacy.$(OBJEXT)
+libapplayermodeslegacy_a_OBJECTS = \
+ $(am_libapplayermodeslegacy_a_OBJECTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimpoperationadditionlegacy.Po \
+ ./$(DEPDIR)/gimpoperationburnlegacy.Po \
+ ./$(DEPDIR)/gimpoperationdarkenonlylegacy.Po \
+ ./$(DEPDIR)/gimpoperationdifferencelegacy.Po \
+ ./$(DEPDIR)/gimpoperationdividelegacy.Po \
+ ./$(DEPDIR)/gimpoperationdodgelegacy.Po \
+ ./$(DEPDIR)/gimpoperationgrainextractlegacy.Po \
+ ./$(DEPDIR)/gimpoperationgrainmergelegacy.Po \
+ ./$(DEPDIR)/gimpoperationhardlightlegacy.Po \
+ ./$(DEPDIR)/gimpoperationhslcolorlegacy.Po \
+ ./$(DEPDIR)/gimpoperationhsvhuelegacy.Po \
+ ./$(DEPDIR)/gimpoperationhsvsaturationlegacy.Po \
+ ./$(DEPDIR)/gimpoperationhsvvaluelegacy.Po \
+ ./$(DEPDIR)/gimpoperationlightenonlylegacy.Po \
+ ./$(DEPDIR)/gimpoperationmultiplylegacy.Po \
+ ./$(DEPDIR)/gimpoperationscreenlegacy.Po \
+ ./$(DEPDIR)/gimpoperationsoftlightlegacy.Po \
+ ./$(DEPDIR)/gimpoperationsubtractlegacy.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libapplayermodeslegacy_a_SOURCES)
+DIST_SOURCES = $(libapplayermodeslegacy_a_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"Gimp-Layer-Modes-Legacy\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/app \
+ -I$(top_srcdir)/app \
+ $(CAIRO_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ -I$(includedir)
+
+noinst_LIBRARIES = \
+ libapplayermodeslegacy.a
+
+libapplayermodeslegacy_a_SOURCES = \
+ gimpoperationadditionlegacy.c \
+ gimpoperationadditionlegacy.h \
+ gimpoperationburnlegacy.c \
+ gimpoperationburnlegacy.h \
+ gimpoperationdarkenonlylegacy.c \
+ gimpoperationdarkenonlylegacy.h \
+ gimpoperationdifferencelegacy.c \
+ gimpoperationdifferencelegacy.h \
+ gimpoperationdividelegacy.c \
+ gimpoperationdividelegacy.h \
+ gimpoperationdodgelegacy.c \
+ gimpoperationdodgelegacy.h \
+ gimpoperationgrainextractlegacy.c \
+ gimpoperationgrainextractlegacy.h \
+ gimpoperationgrainmergelegacy.c \
+ gimpoperationgrainmergelegacy.h \
+ gimpoperationhardlightlegacy.c \
+ gimpoperationhardlightlegacy.h \
+ gimpoperationhslcolorlegacy.c \
+ gimpoperationhslcolorlegacy.h \
+ gimpoperationhsvhuelegacy.c \
+ gimpoperationhsvhuelegacy.h \
+ gimpoperationhsvsaturationlegacy.c \
+ gimpoperationhsvsaturationlegacy.h \
+ gimpoperationhsvvaluelegacy.c \
+ gimpoperationhsvvaluelegacy.h \
+ gimpoperationlightenonlylegacy.c \
+ gimpoperationlightenonlylegacy.h \
+ gimpoperationmultiplylegacy.c \
+ gimpoperationmultiplylegacy.h \
+ gimpoperationscreenlegacy.c \
+ gimpoperationscreenlegacy.h \
+ gimpoperationsoftlightlegacy.c \
+ gimpoperationsoftlightlegacy.h \
+ gimpoperationsubtractlegacy.c \
+ gimpoperationsubtractlegacy.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu app/operations/layer-modes-legacy/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu app/operations/layer-modes-legacy/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+
+libapplayermodeslegacy.a: $(libapplayermodeslegacy_a_OBJECTS) $(libapplayermodeslegacy_a_DEPENDENCIES) $(EXTRA_libapplayermodeslegacy_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libapplayermodeslegacy.a
+ $(AM_V_AR)$(libapplayermodeslegacy_a_AR) libapplayermodeslegacy.a $(libapplayermodeslegacy_a_OBJECTS) $(libapplayermodeslegacy_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libapplayermodeslegacy.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationadditionlegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationburnlegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationdarkenonlylegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationdifferencelegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationdividelegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationdodgelegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationgrainextractlegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationgrainmergelegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationhardlightlegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationhslcolorlegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationhsvhuelegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationhsvsaturationlegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationhsvvaluelegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationlightenonlylegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationmultiplylegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationscreenlegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationsoftlightlegacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationsubtractlegacy.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimpoperationadditionlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationburnlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationdarkenonlylegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationdifferencelegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationdividelegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationdodgelegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationgrainextractlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationgrainmergelegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationhardlightlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationhslcolorlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationhsvhuelegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationhsvsaturationlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationhsvvaluelegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationlightenonlylegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationmultiplylegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationscreenlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationsoftlightlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationsubtractlegacy.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/gimpoperationadditionlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationburnlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationdarkenonlylegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationdifferencelegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationdividelegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationdodgelegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationgrainextractlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationgrainmergelegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationhardlightlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationhslcolorlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationhsvhuelegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationhsvsaturationlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationhsvvaluelegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationlightenonlylegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationmultiplylegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationscreenlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationsoftlightlegacy.Po
+ -rm -f ./$(DEPDIR)/gimpoperationsubtractlegacy.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/app/operations/layer-modes-legacy/gimpoperationadditionlegacy.c b/app/operations/layer-modes-legacy/gimpoperationadditionlegacy.c
new file mode 100644
index 0000000..02992f2
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationadditionlegacy.c
@@ -0,0 +1,126 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationadditionmode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationadditionlegacy.h"
+
+
+static gboolean gimp_operation_addition_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationAdditionLegacy, gimp_operation_addition_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_addition_legacy_class_init (GimpOperationAdditionLegacyClass *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:addition-legacy",
+ "description", "GIMP addition mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_addition_legacy_process;
+}
+
+static void
+gimp_operation_addition_legacy_init (GimpOperationAdditionLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_addition_legacy_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;
+
+ while (samples--)
+ {
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (has_mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gfloat ratio = comp_alpha / new_alpha;
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ gfloat comp = in[b] + layer[b];
+ comp = CLAMP (comp, 0.0f, 1.0f);
+
+ out[b] = comp * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationadditionlegacy.h b/app/operations/layer-modes-legacy/gimpoperationadditionlegacy.h
new file mode 100644
index 0000000..7f9df60
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationadditionlegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationadditionlegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_ADDITION_LEGACY_H__
+#define __GIMP_OPERATION_ADDITION_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_ADDITION_LEGACY (gimp_operation_addition_legacy_get_type ())
+#define GIMP_OPERATION_ADDITION_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_ADDITION_LEGACY, GimpOperationAdditionLegacy))
+#define GIMP_OPERATION_ADDITION_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_ADDITION_LEGACY, GimpOperationAdditionLegacyClass))
+#define GIMP_IS_OPERATION_ADDITION_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_ADDITION_LEGACY))
+#define GIMP_IS_OPERATION_ADDITION_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_ADDITION_LEGACY))
+#define GIMP_OPERATION_ADDITION_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_ADDITION_LEGACY, GimpOperationAdditionLegacyClass))
+
+
+typedef struct _GimpOperationAdditionLegacy GimpOperationAdditionLegacy;
+typedef struct _GimpOperationAdditionLegacyClass GimpOperationAdditionLegacyClass;
+
+struct _GimpOperationAdditionLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationAdditionLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_addition_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_ADDITION_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationburnlegacy.c b/app/operations/layer-modes-legacy/gimpoperationburnlegacy.c
new file mode 100644
index 0000000..5511b69
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationburnlegacy.c
@@ -0,0 +1,128 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationburnmode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationburnlegacy.h"
+
+
+static gboolean gimp_operation_burn_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationBurnLegacy, gimp_operation_burn_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_burn_legacy_class_init (GimpOperationBurnLegacyClass *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:burn-legacy",
+ "description", "GIMP burn mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_burn_legacy_process;
+}
+
+static void
+gimp_operation_burn_legacy_init (GimpOperationBurnLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_burn_legacy_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;
+
+ while (samples--)
+ {
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gfloat ratio = comp_alpha / new_alpha;
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ gfloat comp = 1.0f - (1.0f - in[b]) / layer[b];
+ /* The CLAMP macro is deliberately inlined and
+ * written to map comp == NAN (0 / 0) -> 1
+ */
+ comp = comp < 0.0f ? 0.0f : comp < 1.0f ? comp : 1.0f;
+
+ out[b] = comp * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationburnlegacy.h b/app/operations/layer-modes-legacy/gimpoperationburnlegacy.h
new file mode 100644
index 0000000..dc13e3f
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationburnlegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationburnlegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_BURN_LEGACY_H__
+#define __GIMP_OPERATION_BURN_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_BURN_LEGACY (gimp_operation_burn_legacy_get_type ())
+#define GIMP_OPERATION_BURN_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_BURN_LEGACY, GimpOperationBurnLegacy))
+#define GIMP_OPERATION_BURN_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_BURN_LEGACY, GimpOperationBurnLegacyClass))
+#define GIMP_IS_OPERATION_BURN_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_BURN_LEGACY))
+#define GIMP_IS_OPERATION_BURN_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_BURN_LEGACY))
+#define GIMP_OPERATION_BURN_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_BURN_LEGACY, GimpOperationBurnLegacyClass))
+
+
+typedef struct _GimpOperationBurnLegacy GimpOperationBurnLegacy;
+typedef struct _GimpOperationBurnLegacyClass GimpOperationBurnLegacyClass;
+
+struct _GimpOperationBurnLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationBurnLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_burn_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_BURN_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.c b/app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.c
new file mode 100644
index 0000000..1a3ebdf
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.c
@@ -0,0 +1,124 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationdarkenonlymode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationdarkenonlylegacy.h"
+
+
+static gboolean gimp_operation_darken_only_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationDarkenOnlyLegacy, gimp_operation_darken_only_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_darken_only_legacy_class_init (GimpOperationDarkenOnlyLegacyClass *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:darken-only-legacy",
+ "description", "GIMP darken only mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_darken_only_legacy_process;
+}
+
+static void
+gimp_operation_darken_only_legacy_init (GimpOperationDarkenOnlyLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_darken_only_legacy_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;
+
+ while (samples--)
+ {
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (new_alpha && comp_alpha)
+ {
+ gint b;
+ gfloat ratio = comp_alpha / new_alpha;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ gfloat comp = MIN (in[b], layer[b]);
+
+ out[b] = comp * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.h b/app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.h
new file mode 100644
index 0000000..c0406a1
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationdarkenonlylegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationdarkenonlylegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_DARKEN_ONLY_LEGACY_H__
+#define __GIMP_OPERATION_DARKEN_ONLY_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_DARKEN_ONLY_LEGACY (gimp_operation_darken_only_legacy_get_type ())
+#define GIMP_OPERATION_DARKEN_ONLY_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_DARKEN_ONLY_MODE, GimpOperationDarkenOnlyLegacy))
+#define GIMP_OPERATION_DARKEN_ONLY_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_DARKEN_ONLY_MODE, GimpOperationDarkenOnlyLegacyClass))
+#define GIMP_IS_OPERATION_DARKEN_ONLY_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_DARKEN_ONLY_MODE))
+#define GIMP_IS_OPERATION_DARKEN_ONLY_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_DARKEN_ONLY_MODE))
+#define GIMP_OPERATION_DARKEN_ONLY_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_DARKEN_ONLY_MODE, GimpOperationDarkenOnlyLegacyClass))
+
+
+typedef struct _GimpOperationDarkenOnlyLegacy GimpOperationDarkenOnlyLegacy;
+typedef struct _GimpOperationDarkenOnlyLegacyClass GimpOperationDarkenOnlyLegacyClass;
+
+struct _GimpOperationDarkenOnlyLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationDarkenOnlyLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_darken_only_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_DARKEN_ONLY_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.c b/app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.c
new file mode 100644
index 0000000..55f3328
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.c
@@ -0,0 +1,126 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationdifferencemode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationdifferencelegacy.h"
+
+
+static gboolean gimp_operation_difference_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationDifferenceLegacy, gimp_operation_difference_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_difference_legacy_class_init (GimpOperationDifferenceLegacyClass *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:difference-legacy",
+ "description", "GIMP difference mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_difference_legacy_process;
+}
+
+static void
+gimp_operation_difference_legacy_init (GimpOperationDifferenceLegacy *self)
+{
+}
+
+
+static gboolean
+gimp_operation_difference_legacy_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;
+
+ while (samples--)
+ {
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gfloat ratio = comp_alpha / new_alpha;
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ gfloat comp = in[b] - layer[b];
+ comp = (comp < 0.0f) ? -comp : comp;
+
+ out[b] = comp * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.h b/app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.h
new file mode 100644
index 0000000..145b9c2
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationdifferencelegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationdifferencelegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_DIFFERENCE_LEGACY_H__
+#define __GIMP_OPERATION_DIFFERENCE_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY (gimp_operation_difference_legacy_get_type ())
+#define GIMP_OPERATION_DIFFERENCE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY, GimpOperationDifferenceLegacy))
+#define GIMP_OPERATION_DIFFERENCE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY, GimpOperationDifferenceLegacyClass))
+#define GIMP_IS_OPERATION_DIFFERENCE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY))
+#define GIMP_IS_OPERATION_DIFFERENCE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY))
+#define GIMP_OPERATION_DIFFERENCE_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_DIFFERENCE_LEGACY, GimpOperationDifferenceLegacyClass))
+
+
+typedef struct _GimpOperationDifferenceLegacy GimpOperationDifferenceLegacy;
+typedef struct _GimpOperationDifferenceLegacyClass GimpOperationDifferenceLegacyClass;
+
+struct _GimpOperationDifferenceLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationDifferenceLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_difference_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_DIFFERENCE_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationdividelegacy.c b/app/operations/layer-modes-legacy/gimpoperationdividelegacy.c
new file mode 100644
index 0000000..8fe8dd8
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationdividelegacy.c
@@ -0,0 +1,127 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationdividemode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "../operations-types.h"
+
+#include "gimpoperationdividelegacy.h"
+
+
+static gboolean gimp_operation_divide_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationDivideLegacy, gimp_operation_divide_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_divide_legacy_class_init (GimpOperationDivideLegacyClass *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:divide-legacy",
+ "description", "GIMP divide mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_divide_legacy_process;
+}
+
+static void
+gimp_operation_divide_legacy_init (GimpOperationDivideLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_divide_legacy_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;
+
+ while (samples--)
+ {
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gint b;
+ gfloat ratio = comp_alpha / new_alpha;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ gfloat comp = in[b] / layer[b];
+ comp = SAFE_CLAMP (comp, 0.0f, 1.0f);
+
+ out[b] = comp * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationdividelegacy.h b/app/operations/layer-modes-legacy/gimpoperationdividelegacy.h
new file mode 100644
index 0000000..fa5de12
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationdividelegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationdividelegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_DIVIDE_LEGACY_H__
+#define __GIMP_OPERATION_DIVIDE_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_DIVIDE_LEGACY (gimp_operation_divide_legacy_get_type ())
+#define GIMP_OPERATION_DIVIDE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_DIVIDE_LEGACY, GimpOperationDivideLegacy))
+#define GIMP_OPERATION_DIVIDE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_DIVIDE_LEGACY, GimpOperationDivideLegacyClass))
+#define GIMP_IS_OPERATION_DIVIDE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_DIVIDE_LEGACY))
+#define GIMP_IS_OPERATION_DIVIDE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_DIVIDE_LEGACY))
+#define GIMP_OPERATION_DIVIDE_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_DIVIDE_LEGACY, GimpOperationDivideLegacyClass))
+
+
+typedef struct _GimpOperationDivideLegacy GimpOperationDivideLegacy;
+typedef struct _GimpOperationDivideLegacyClass GimpOperationDivideLegacyClass;
+
+struct _GimpOperationDivideLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationDivideLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_divide_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_DIVIDE_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationdodgelegacy.c b/app/operations/layer-modes-legacy/gimpoperationdodgelegacy.c
new file mode 100644
index 0000000..65adaef
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationdodgelegacy.c
@@ -0,0 +1,127 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationdodgelegacy.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "../operations-types.h"
+
+#include "gimpoperationdodgelegacy.h"
+
+
+static gboolean gimp_operation_dodge_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationDodgeLegacy, gimp_operation_dodge_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_dodge_legacy_class_init (GimpOperationDodgeLegacyClass *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:dodge-legacy",
+ "description", "GIMP dodge mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_dodge_legacy_process;
+}
+
+static void
+gimp_operation_dodge_legacy_init (GimpOperationDodgeLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_dodge_legacy_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;
+
+ while (samples--)
+ {
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gint b;
+ gfloat ratio = comp_alpha / new_alpha;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ gfloat comp = in[b] / (1.0f - layer[b]);
+ comp = SAFE_CLAMP (comp, 0.0f, 1.0f);
+
+ out[b] = comp * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationdodgelegacy.h b/app/operations/layer-modes-legacy/gimpoperationdodgelegacy.h
new file mode 100644
index 0000000..b640fed
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationdodgelegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationdodgelegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_DODGE_LEGACY_H__
+#define __GIMP_OPERATION_DODGE_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_DODGE_LEGACY (gimp_operation_dodge_legacy_get_type ())
+#define GIMP_OPERATION_DODGE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_DODGE_LEGACY, GimpOperationDodgeLegacy))
+#define GIMP_OPERATION_DODGE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_DODGE_LEGACY, GimpOperationDodgeLegacyClass))
+#define GIMP_IS_OPERATION_DODGE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_DODGE_LEGACY))
+#define GIMP_IS_OPERATION_DODGE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_DODGE_LEGACY))
+#define GIMP_OPERATION_DODGE_LEGACY_GET_CLASS(obj)(G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_DODGE_LEGACY, GimpOperationDodgeLegacyClass))
+
+
+typedef struct _GimpOperationDodgeLegacy GimpOperationDodgeLegacy;
+typedef struct _GimpOperationDodgeLegacyClass GimpOperationDodgeLegacyClass;
+
+struct _GimpOperationDodgeLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationDodgeLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_dodge_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_DODGE_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.c b/app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.c
new file mode 100644
index 0000000..77338c4
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.c
@@ -0,0 +1,125 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationgrainextractmode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationgrainextractlegacy.h"
+
+
+static gboolean gimp_operation_grain_extract_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationGrainExtractLegacy, gimp_operation_grain_extract_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_grain_extract_legacy_class_init (GimpOperationGrainExtractLegacyClass *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:grain-extract-legacy",
+ "description", "GIMP grain extract mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_grain_extract_legacy_process;
+}
+
+static void
+gimp_operation_grain_extract_legacy_init (GimpOperationGrainExtractLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_grain_extract_legacy_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;
+
+ while (samples--)
+ {
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gfloat ratio = comp_alpha / new_alpha;
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ gfloat comp = in[b] - layer[b] + 128.0f / 255.0f;
+ comp = CLAMP (comp, 0.0f, 1.0f);
+
+ out[b] = comp * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.h b/app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.h
new file mode 100644
index 0000000..149ee09
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationgrainextractlegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationgrainextractlegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_GRAIN_EXTRACT_LEGACY_H__
+#define __GIMP_OPERATION_GRAIN_EXTRACT_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY (gimp_operation_grain_extract_legacy_get_type ())
+#define GIMP_OPERATION_GRAIN_EXTRACT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY, GimpOperationGrainExtractLegacy))
+#define GIMP_OPERATION_GRAIN_EXTRACT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY, GimpOperationGrainExtractLegacyClass))
+#define GIMP_IS_OPERATION_GRAIN_EXTRACT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY))
+#define GIMP_IS_OPERATION_GRAIN_EXTRACT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY))
+#define GIMP_OPERATION_GRAIN_EXTRACT_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_GRAIN_EXTRACT_LEGACY, GimpOperationGrainExtractLegacyClass))
+
+
+typedef struct _GimpOperationGrainExtractLegacy GimpOperationGrainExtractLegacy;
+typedef struct _GimpOperationGrainExtractLegacyClass GimpOperationGrainExtractLegacyClass;
+
+struct _GimpOperationGrainExtractLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationGrainExtractLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_grain_extract_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_GRAIN_EXTRACT_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.c b/app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.c
new file mode 100644
index 0000000..c12a995
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.c
@@ -0,0 +1,125 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationgrainmergemode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationgrainmergelegacy.h"
+
+
+static gboolean gimp_operation_grain_merge_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationGrainMergeLegacy, gimp_operation_grain_merge_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_grain_merge_legacy_class_init (GimpOperationGrainMergeLegacyClass *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:grain-merge-legacy",
+ "description", "GIMP grain merge mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_grain_merge_legacy_process;
+}
+
+static void
+gimp_operation_grain_merge_legacy_init (GimpOperationGrainMergeLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_grain_merge_legacy_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;
+
+ while (samples--)
+ {
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gfloat ratio = comp_alpha / new_alpha;
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ gfloat comp = in[b] + layer[b] - 128.0f / 255.0f;
+ comp = CLAMP (comp, 0.0f, 1.0f);
+
+ out[b] = comp * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask ++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.h b/app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.h
new file mode 100644
index 0000000..344e895
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationgrainmergelegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationgrainmergelegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_GRAIN_MERGE_LEGACY_H__
+#define __GIMP_OPERATION_GRAIN_MERGE_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY (gimp_operation_grain_merge_legacy_get_type ())
+#define GIMP_OPERATION_GRAIN_MERGE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY, GimpOperationGrainMergeLegacy))
+#define GIMP_OPERATION_GRAIN_MERGE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY, GimpOperationGrainMergeLegacyClass))
+#define GIMP_IS_OPERATION_GRAIN_MERGE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY))
+#define GIMP_IS_OPERATION_GRAIN_MERGE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY))
+#define GIMP_OPERATION_GRAIN_MERGE_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_GRAIN_MERGE_LEGACY, GimpOperationGrainMergeLegacyClass))
+
+
+typedef struct _GimpOperationGrainMergeLegacy GimpOperationGrainMergeLegacy;
+typedef struct _GimpOperationGrainMergeLegacyClass GimpOperationGrainMergeLegacyClass;
+
+struct _GimpOperationGrainMergeLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationGrainMergeLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_grain_merge_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_GRAIN_MERGE_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.c b/app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.c
new file mode 100644
index 0000000..68f8eda
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.c
@@ -0,0 +1,135 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationhardlightmode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationhardlightlegacy.h"
+
+
+static gboolean gimp_operation_hardlight_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationHardlightLegacy, gimp_operation_hardlight_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_hardlight_legacy_class_init (GimpOperationHardlightLegacyClass *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:hardlight-legacy",
+ "description", "GIMP hardlight mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_hardlight_legacy_process;
+}
+
+static void
+gimp_operation_hardlight_legacy_init (GimpOperationHardlightLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_hardlight_legacy_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;
+
+ while (samples--)
+ {
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gfloat ratio = comp_alpha / new_alpha;
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ gfloat comp;
+
+ if (layer[b] > 128.0f / 255.0f)
+ {
+ comp = (1.0 - in[b]) * (1.0 - (layer[b] - 128.0f / 255.0f) * 2.0f);
+ comp = MIN (1.0f - comp, 1.0f);
+ }
+ else
+ {
+ comp = in[b] * (layer[b] * 2.0f);
+ comp = MIN (comp, 1.0f);
+ }
+
+ out[b] = comp * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask ++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.h b/app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.h
new file mode 100644
index 0000000..38791be
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationhardlightlegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationhardlightlegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_HARDLIGHT_LEGACY_H__
+#define __GIMP_OPERATION_HARDLIGHT_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY (gimp_operation_hardlight_legacy_get_type ())
+#define GIMP_OPERATION_HARDLIGHT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY, GimpOperationHardlightLegacy))
+#define GIMP_OPERATION_HARDLIGHT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY, GimpOperationHardlightLegacyClass))
+#define GIMP_IS_OPERATION_HARDLIGHT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY))
+#define GIMP_IS_OPERATION_HARDLIGHT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY))
+#define GIMP_OPERATION_HARDLIGHT_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_HARDLIGHT_LEGACY, GimpOperationHardlightLegacyClass))
+
+
+typedef struct _GimpOperationHardlightLegacy GimpOperationHardlightLegacy;
+typedef struct _GimpOperationHardlightLegacyClass GimpOperationHardlightLegacyClass;
+
+struct _GimpOperationHardlightLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationHardlightLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_hardlight_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_HARDLIGHT_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.c b/app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.c
new file mode 100644
index 0000000..6fd7a95
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.c
@@ -0,0 +1,141 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationcolormode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "../operations-types.h"
+
+#include "gimpoperationhslcolorlegacy.h"
+
+
+static gboolean gimp_operation_hsl_color_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationHslColorLegacy, gimp_operation_hsl_color_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_hsl_color_legacy_class_init (GimpOperationHslColorLegacyClass *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:hsl-color-legacy",
+ "description", "GIMP color mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_hsl_color_legacy_process;
+}
+
+static void
+gimp_operation_hsl_color_legacy_init (GimpOperationHslColorLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_hsl_color_legacy_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;
+
+ while (samples--)
+ {
+ GimpHSL layer_hsl, out_hsl;
+ GimpRGB layer_rgb = {layer[0], layer[1], layer[2]};
+ GimpRGB out_rgb = {in[0], in[1], in[2]};
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gint b;
+ gfloat out_tmp[3];
+ gfloat ratio = comp_alpha / new_alpha;
+
+ gimp_rgb_to_hsl (&layer_rgb, &layer_hsl);
+ gimp_rgb_to_hsl (&out_rgb, &out_hsl);
+
+ out_hsl.h = layer_hsl.h;
+ out_hsl.s = layer_hsl.s;
+ gimp_hsl_to_rgb (&out_hsl, &out_rgb);
+
+ out_tmp[0] = out_rgb.r;
+ out_tmp[1] = out_rgb.g;
+ out_tmp[2] = out_rgb.b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = out_tmp[b] * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.h b/app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.h
new file mode 100644
index 0000000..add2933
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationhslcolorlegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationhslcolorlegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_HSL_COLOR_LEGACY_H__
+#define __GIMP_OPERATION_HSL_COLOR_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_HSL_COLOR_LEGACY (gimp_operation_hsl_color_legacy_get_type ())
+#define GIMP_OPERATION_HSL_COLOR_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_HSL_COLOR_LEGACY, GimpOperationHslColorLegacy))
+#define GIMP_OPERATION_HSL_COLOR_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_HSL_COLOR_LEGACY, GimpOperationHslColorLegacyClass))
+#define GIMP_IS_OPERATION_HSL_COLOR_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_HSL_COLOR_LEGACY))
+#define GIMP_IS_OPERATION_HSL_COLOR_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_HSL_COLOR_LEGACY))
+#define GIMP_OPERATION_HSL_COLOR_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_HSL_COLOR_LEGACY, GimpOperationHslColorLegacyClass))
+
+
+typedef struct _GimpOperationHslColorLegacy GimpOperationHslColorLegacy;
+typedef struct _GimpOperationHslColorLegacyClass GimpOperationHslColorLegacyClass;
+
+struct _GimpOperationHslColorLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationHslColorLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_hsl_color_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_HSL_COLOR_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.c b/app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.c
new file mode 100644
index 0000000..eb9c040
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.c
@@ -0,0 +1,146 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationhuemode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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 <cairo.h>
+#include <gegl-plugin.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "../operations-types.h"
+
+#include "gimpoperationhsvhuelegacy.h"
+
+
+static gboolean gimp_operation_hsv_hue_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationHsvHueLegacy, gimp_operation_hsv_hue_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_hsv_hue_legacy_class_init (GimpOperationHsvHueLegacyClass *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:hsv-hue-legacy",
+ "description", "GIMP hue mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_hsv_hue_legacy_process;
+}
+
+static void
+gimp_operation_hsv_hue_legacy_init (GimpOperationHsvHueLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_hsv_hue_legacy_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;
+
+ while (samples--)
+ {
+ GimpHSV layer_hsv, out_hsv;
+ GimpRGB layer_rgb = {layer[0], layer[1], layer[2]};
+ GimpRGB out_rgb = {in[0], in[1], in[2]};
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gint b;
+ gfloat out_tmp[3];
+ gfloat ratio = comp_alpha / new_alpha;
+
+ gimp_rgb_to_hsv (&layer_rgb, &layer_hsv);
+ gimp_rgb_to_hsv (&out_rgb, &out_hsv);
+
+ /* Composition should have no effect if saturation is zero.
+ * otherwise, black would be painted red (see bug #123296).
+ */
+ if (layer_hsv.s)
+ {
+ out_hsv.h = layer_hsv.h;
+ }
+ gimp_hsv_to_rgb (&out_hsv, &out_rgb);
+
+ out_tmp[0] = out_rgb.r;
+ out_tmp[1] = out_rgb.g;
+ out_tmp[2] = out_rgb.b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = out_tmp[b] * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.h b/app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.h
new file mode 100644
index 0000000..59ef125
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationhsvhuelegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationhsvhuelegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_HSV_HUE_LEGACY_H__
+#define __GIMP_OPERATION_HSV_HUE_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_HSV_HUE_LEGACY (gimp_operation_hsv_hue_legacy_get_type ())
+#define GIMP_OPERATION_HSV_HUE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_HSV_HUE_LEGACY, GimpOperationHsvHueLegacy))
+#define GIMP_OPERATION_HSV_HUE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_HSV_HUE_LEGACY, GimpOperationHsvHueLegacyClass))
+#define GIMP_IS_OPERATION_HSV_HUE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_HSV_HUE_LEGACY))
+#define GIMP_IS_OPERATION_HSV_HUE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_HSV_HUE_LEGACY))
+#define GIMP_OPERATION_HSV_HUE_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_HSV_HUE_LEGACY, GimpOperationHsvHueLegacyClass))
+
+
+typedef struct _GimpOperationHsvHueLegacy GimpOperationHsvHueLegacy;
+typedef struct _GimpOperationHsvHueLegacyClass GimpOperationHsvHueLegacyClass;
+
+struct _GimpOperationHsvHueLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationHsvHueLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_hsv_hue_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_HSV_HUE_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.c b/app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.c
new file mode 100644
index 0000000..d150aa1
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.c
@@ -0,0 +1,140 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationsaturationmode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "../operations-types.h"
+
+#include "gimpoperationhsvsaturationlegacy.h"
+
+
+static gboolean gimp_operation_hsv_saturation_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationHsvSaturationLegacy, gimp_operation_hsv_saturation_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_hsv_saturation_legacy_class_init (GimpOperationHsvSaturationLegacyClass *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:hsv-saturation-legacy",
+ "description", "GIMP saturation mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_hsv_saturation_legacy_process;
+}
+
+static void
+gimp_operation_hsv_saturation_legacy_init (GimpOperationHsvSaturationLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_hsv_saturation_legacy_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;
+
+ while (samples--)
+ {
+ GimpHSV layer_hsv, out_hsv;
+ GimpRGB layer_rgb = {layer[0], layer[1], layer[2]};
+ GimpRGB out_rgb = {in[0], in[1], in[2]};
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gint b;
+ gfloat out_tmp[3];
+ gfloat ratio = comp_alpha / new_alpha;
+
+ gimp_rgb_to_hsv (&layer_rgb, &layer_hsv);
+ gimp_rgb_to_hsv (&out_rgb, &out_hsv);
+
+ out_hsv.s = layer_hsv.s;
+ gimp_hsv_to_rgb (&out_hsv, &out_rgb);
+
+ out_tmp[0] = out_rgb.r;
+ out_tmp[1] = out_rgb.g;
+ out_tmp[2] = out_rgb.b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = out_tmp[b] * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.h b/app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.h
new file mode 100644
index 0000000..ecb5589
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationhsvsaturationlegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationhsvsaturationlegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_HSV_SATURATION_LEGACY_H__
+#define __GIMP_OPERATION_HSV_SATURATION_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY (gimp_operation_hsv_saturation_legacy_get_type ())
+#define GIMP_OPERATION_HSV_SATURATION_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY, GimpOperationHsvSaturationLegacy))
+#define GIMP_OPERATION_HSV_SATURATION_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY, GimpOperationHsvSaturationLegacyClass))
+#define GIMP_IS_OPERATION_HSV_SATURATION_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY))
+#define GIMP_IS_OPERATION_HSV_SATURATION_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY))
+#define GIMP_OPERATION_HSV_SATURATION_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_HSV_SATURATION_LEGACY, GimpOperationHsvSaturationLegacyClass))
+
+
+typedef struct _GimpOperationHsvSaturationLegacy GimpOperationHsvSaturationLegacy;
+typedef struct _GimpOperationHsvSaturationLegacyClass GimpOperationHsvSaturationLegacyClass;
+
+struct _GimpOperationHsvSaturationLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationHsvSaturationLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_hsv_saturation_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_HSV_SATURATION_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.c b/app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.c
new file mode 100644
index 0000000..b873325
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.c
@@ -0,0 +1,140 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationvaluemode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "../operations-types.h"
+
+#include "gimpoperationhsvvaluelegacy.h"
+
+
+static gboolean gimp_operation_hsv_value_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationHsvValueLegacy, gimp_operation_hsv_value_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_hsv_value_legacy_class_init (GimpOperationHsvValueLegacyClass *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:hsv-value-legacy",
+ "description", "GIMP value mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_hsv_value_legacy_process;
+}
+
+static void
+gimp_operation_hsv_value_legacy_init (GimpOperationHsvValueLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_hsv_value_legacy_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;
+
+ while (samples--)
+ {
+ GimpHSV layer_hsv, out_hsv;
+ GimpRGB layer_rgb = {layer[0], layer[1], layer[2]};
+ GimpRGB out_rgb = {in[0], in[1], in[2]};
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gint b;
+ gfloat out_tmp[3];
+ gfloat ratio = comp_alpha / new_alpha;
+
+ gimp_rgb_to_hsv (&layer_rgb, &layer_hsv);
+ gimp_rgb_to_hsv (&out_rgb, &out_hsv);
+
+ out_hsv.v = layer_hsv.v;
+ gimp_hsv_to_rgb (&out_hsv, &out_rgb);
+
+ out_tmp[0] = out_rgb.r;
+ out_tmp[1] = out_rgb.g;
+ out_tmp[2] = out_rgb.b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = out_tmp[b] * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.h b/app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.h
new file mode 100644
index 0000000..7701ffd
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationhsvvaluelegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationhsvvaluelegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_HSV_VALUE_LEGACY_H__
+#define __GIMP_OPERATION_HSV_VALUE_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY (gimp_operation_hsv_value_legacy_get_type ())
+#define GIMP_OPERATION_HSV_VALUE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY, GimpOperationHsvValueLegacy))
+#define GIMP_OPERATION_HSV_VALUE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY, GimpOperationHsvValueLegacyClass))
+#define GIMP_IS_OPERATION_HSV_VALUE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY))
+#define GIMP_IS_OPERATION_HSV_VALUE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY))
+#define GIMP_OPERATION_HSV_VALUE_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_HSV_VALUE_LEGACY, GimpOperationHsvValueLegacyClass))
+
+
+typedef struct _GimpOperationHsvValueLegacy GimpOperationHsvValueLegacy;
+typedef struct _GimpOperationHsvValueLegacyClass GimpOperationHsvValueLegacyClass;
+
+struct _GimpOperationHsvValueLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationHsvValueLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_hsv_value_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_HSV_VALUE_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.c b/app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.c
new file mode 100644
index 0000000..0e54a0c
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.c
@@ -0,0 +1,124 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationlightenonlylegacy.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationlightenonlylegacy.h"
+
+
+static gboolean gimp_operation_lighten_only_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationLightenOnlyLegacy, gimp_operation_lighten_only_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_lighten_only_legacy_class_init (GimpOperationLightenOnlyLegacyClass *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:lighten-only-legacy",
+ "description", "GIMP lighten only legacy operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_lighten_only_legacy_process;
+}
+
+static void
+gimp_operation_lighten_only_legacy_init (GimpOperationLightenOnlyLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_lighten_only_legacy_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;
+
+ while (samples--)
+ {
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gint b;
+ gfloat ratio = comp_alpha / new_alpha;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ gfloat comp = MAX (layer[b], in[b]);
+
+ out[b] = comp * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.h b/app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.h
new file mode 100644
index 0000000..2417d57
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationlightenonlylegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationlightenonlylegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_LIGHTEN_ONLY_LEGACY_H__
+#define __GIMP_OPERATION_LIGHTEN_ONLY_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY (gimp_operation_lighten_only_legacy_get_type ())
+#define GIMP_OPERATION_LIGHTEN_ONLY_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY, GimpOperationLightenOnlyLegacy))
+#define GIMP_OPERATION_LIGHTEN_ONLY_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY, GimpOperationLightenOnlyLegacyClass))
+#define GIMP_IS_OPERATION_LIGHTEN_ONLY_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY))
+#define GIMP_IS_OPERATION_LIGHTEN_ONLY_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY))
+#define GIMP_OPERATION_LIGHTEN_ONLY_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_LIGHTEN_ONLY_LEGACY, GimpOperationLightenOnlyLegacyClass))
+
+
+typedef struct _GimpOperationLightenOnlyLegacy GimpOperationLightenOnlyLegacy;
+typedef struct _GimpOperationLightenOnlyLegacyClass GimpOperationLightenOnlyLegacyClass;
+
+struct _GimpOperationLightenOnlyLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationLightenOnlyLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_lighten_only_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_LIGHTEN_ONLY_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.c b/app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.c
new file mode 100644
index 0000000..69c63c4
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.c
@@ -0,0 +1,124 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationmultiplylegacy.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "operations/operations-types.h"
+
+#include "gimpoperationmultiplylegacy.h"
+
+
+static gboolean gimp_operation_multiply_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationMultiplyLegacy, gimp_operation_multiply_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_multiply_legacy_class_init (GimpOperationMultiplyLegacyClass *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:multiply-legacy",
+ "description", "GIMP multiply legacy operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_multiply_legacy_process;
+}
+
+static void
+gimp_operation_multiply_legacy_init (GimpOperationMultiplyLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_multiply_legacy_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;
+
+ while (samples--)
+ {
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gfloat ratio = comp_alpha / new_alpha;
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ gfloat comp = layer[b] * in[b];
+
+ out[b] = comp * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.h b/app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.h
new file mode 100644
index 0000000..f099ebd
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationmultiplylegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationmultiplylegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_MULTIPLY_LEGACY_H__
+#define __GIMP_OPERATION_MULTIPLY_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_MULTIPLY_LEGACY (gimp_operation_multiply_legacy_get_type ())
+#define GIMP_OPERATION_MULTIPLY_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_MULTIPLY_LEGACY, GimpOperationMultiplyLegacy))
+#define GIMP_OPERATION_MULTIPLY_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_MULTIPLY_LEGACY, GimpOperationMultiplyLegacyClass))
+#define GIMP_IS_OPERATION_MULTIPLY_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_MULTIPLY_LEGACY))
+#define GIMP_IS_OPERATION_MULTIPLY_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_MULTIPLY_LEGACY))
+#define GIMP_OPERATION_MULTIPLY_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_MULTIPLY_LEGACY, GimpOperationMultiplyLegacyClass))
+
+
+typedef struct _GimpOperationMultiplyLegacy GimpOperationMultiplyLegacy;
+typedef struct _GimpOperationMultiplyLegacyClass GimpOperationMultiplyLegacyClass;
+
+struct _GimpOperationMultiplyLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationMultiplyLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_multiply_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_MULTIPLY_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationscreenlegacy.c b/app/operations/layer-modes-legacy/gimpoperationscreenlegacy.c
new file mode 100644
index 0000000..f2ff2fc
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationscreenlegacy.c
@@ -0,0 +1,124 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationscreenlegacy.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationscreenlegacy.h"
+
+
+static gboolean gimp_operation_screen_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationScreenLegacy, gimp_operation_screen_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_screen_legacy_class_init (GimpOperationScreenLegacyClass *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:screen-legacy",
+ "description", "GIMP screen mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_screen_legacy_process;
+}
+
+static void
+gimp_operation_screen_legacy_init (GimpOperationScreenLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_screen_legacy_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;
+
+ while (samples--)
+ {
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gfloat ratio = comp_alpha / new_alpha;
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ gfloat comp = 1.0f - (1.0f - in[b]) * (1.0f - layer[b]);
+
+ out[b] = comp * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationscreenlegacy.h b/app/operations/layer-modes-legacy/gimpoperationscreenlegacy.h
new file mode 100644
index 0000000..5d0dc9e
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationscreenlegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationscreenlegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_SCREEN_LEGACY_H__
+#define __GIMP_OPERATION_SCREEN_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_SCREEN_LEGACY (gimp_operation_screen_legacy_get_type ())
+#define GIMP_OPERATION_SCREEN_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SCREEN_LEGACY, GimpOperationScreenLegacy))
+#define GIMP_OPERATION_SCREEN_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SCREEN_LEGACY, GimpOperationScreenLegacyClass))
+#define GIMP_IS_OPERATION_SCREEN_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SCREEN_LEGACY))
+#define GIMP_IS_OPERATION_SCREEN_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SCREEN_LEGACY))
+#define GIMP_OPERATION_SCREEN_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SCREEN_LEGACY, GimpOperationScreenLegacyClass))
+
+
+typedef struct _GimpOperationScreenLegacy GimpOperationScreenLegacy;
+typedef struct _GimpOperationScreenLegacyClass GimpOperationScreenLegacyClass;
+
+struct _GimpOperationScreenLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationScreenLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_screen_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_SCREEN_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.c b/app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.c
new file mode 100644
index 0000000..1736e00
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.c
@@ -0,0 +1,157 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationsoftlightmode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationsoftlightlegacy.h"
+
+
+static gboolean gimp_operation_softlight_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationSoftlightLegacy, gimp_operation_softlight_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static const gchar* reference_xml = "<?xml version='1.0' encoding='UTF-8'?>"
+"<gegl>"
+"<node operation='gimp:softlight-legacy'>"
+" <node operation='gegl:load'>"
+" <params>"
+" <param name='path'>B.png</param>"
+" </params>"
+" </node>"
+"</node>"
+"<node operation='gegl:load'>"
+" <params>"
+" <param name='path'>A.png</param>"
+" </params>"
+"</node>"
+"</gegl>";
+
+
+static void
+gimp_operation_softlight_legacy_class_init (GimpOperationSoftlightLegacyClass *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:softlight-legacy",
+ "description", "GIMP softlight mode operation",
+ "reference-image", "soft-light-mode.png",
+ "reference-composition", reference_xml,
+ NULL);
+
+ layer_mode_class->process = gimp_operation_softlight_legacy_process;
+}
+
+static void
+gimp_operation_softlight_legacy_init (GimpOperationSoftlightLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_softlight_legacy_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;
+
+ while (samples--)
+ {
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gfloat ratio = comp_alpha / new_alpha;
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+#if 0
+ /* softlight is now used for what GIMP formerly called
+ * OVERLAY. We fixed OVERLAY to use the right math
+ * (under the name NEW_OVERLAY), and redirect uses of
+ * the old OVERLAY blend mode here. This math was
+ * formerly used for OVERLAY and is exactly the same as
+ * the multiply, screen, comp math used below.
+ * See bug #673501.
+ */
+ gfloat comp = in[b] * (in[b] + (2.0f * layer[b]) * (1.0f - in[b]));
+#endif
+
+ gfloat multiply = in[b] * layer[b];
+ gfloat screen = 1.0f - (1.0f - in[b]) * (1.0f - layer[b]);
+ gfloat comp = (1.0f - in[b]) * multiply + in[b] * screen;
+
+ out[b] = comp * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask ++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.h b/app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.h
new file mode 100644
index 0000000..aa8930e
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationsoftlightlegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationsoftlightlegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_SOFTLIGHT_LEGACY_H__
+#define __GIMP_OPERATION_SOFTLIGHT_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY (gimp_operation_softlight_legacy_get_type ())
+#define GIMP_OPERATION_SOFTLIGHT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY, GimpOperationSoftlightLegacy))
+#define GIMP_OPERATION_SOFTLIGHT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY, GimpOperationSoftlightLegacyClass))
+#define GIMP_IS_OPERATION_SOFTLIGHT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY))
+#define GIMP_IS_OPERATION_SOFTLIGHT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY))
+#define GIMP_OPERATION_SOFTLIGHT_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SOFTLIGHT_LEGACY, GimpOperationSoftlightLegacyClass))
+
+
+typedef struct _GimpOperationSoftlightLegacy GimpOperationSoftlightLegacy;
+typedef struct _GimpOperationSoftlightLegacyClass GimpOperationSoftlightLegacyClass;
+
+struct _GimpOperationSoftlightLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationSoftlightLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_softlight_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_SOFTLIGHT_LEGACY_H__ */
diff --git a/app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.c b/app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.c
new file mode 100644
index 0000000..0bf3b5e
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.c
@@ -0,0 +1,125 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationsubtractmode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationsubtractlegacy.h"
+
+
+static gboolean gimp_operation_subtract_legacy_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationSubtractLegacy, gimp_operation_subtract_legacy,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_subtract_legacy_class_init (GimpOperationSubtractLegacyClass *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:subtract-legacy",
+ "description", "GIMP subtract mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_subtract_legacy_process;
+}
+
+static void
+gimp_operation_subtract_legacy_init (GimpOperationSubtractLegacy *self)
+{
+}
+
+static gboolean
+gimp_operation_subtract_legacy_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;
+
+ while (samples--)
+ {
+ gfloat comp_alpha, new_alpha;
+
+ comp_alpha = MIN (in[ALPHA], layer[ALPHA]) * opacity;
+ if (mask)
+ comp_alpha *= *mask;
+
+ new_alpha = in[ALPHA] + (1.0f - in[ALPHA]) * comp_alpha;
+
+ if (comp_alpha && new_alpha)
+ {
+ gint b;
+ gfloat ratio = comp_alpha / new_alpha;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ gfloat comp = in[b] - layer[b];
+ comp = CLAMP (comp, 0.0f, 1.0f);
+
+ out[b] = comp * ratio + in[b] * (1.0f - ratio);
+ }
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.h b/app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.h
new file mode 100644
index 0000000..34a55d5
--- /dev/null
+++ b/app/operations/layer-modes-legacy/gimpoperationsubtractlegacy.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationsubtractlegacy.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_SUBTRACT_LEGACY_H__
+#define __GIMP_OPERATION_SUBTRACT_LEGACY_H__
+
+
+#include "operations/layer-modes/gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_SUBTRACT_LEGACY (gimp_operation_subtract_legacy_get_type ())
+#define GIMP_OPERATION_SUBTRACT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SUBTRACT_LEGACY, GimpOperationSubtractLegacy))
+#define GIMP_OPERATION_SUBTRACT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SUBTRACT_LEGACY, GimpOperationSubtractLegacyClass))
+#define GIMP_IS_OPERATION_SUBTRACT_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SUBTRACT_LEGACY))
+#define GIMP_IS_OPERATION_SUBTRACT_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SUBTRACT_LEGACY))
+#define GIMP_OPERATION_SUBTRACT_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SUBTRACT_LEGACY, GimpOperationSubtractLegacyClass))
+
+
+typedef struct _GimpOperationSubtractLegacy GimpOperationSubtractLegacy;
+typedef struct _GimpOperationSubtractLegacyClass GimpOperationSubtractLegacyClass;
+
+struct _GimpOperationSubtractLegacy
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationSubtractLegacyClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_subtract_legacy_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_SUBTRACT_LEGACY_H__ */
diff --git a/app/operations/layer-modes/Makefile.am b/app/operations/layer-modes/Makefile.am
new file mode 100644
index 0000000..24af8fe
--- /dev/null
+++ b/app/operations/layer-modes/Makefile.am
@@ -0,0 +1,79 @@
+## Process this file with automake to produce Makefile.in
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"Gimp-Layer-Modes\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/app \
+ -I$(top_srcdir)/app \
+ $(CAIRO_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ -I$(includedir)
+
+noinst_LIBRARIES = \
+ libapplayermodes-generic.a \
+ libapplayermodes-sse2.a \
+ libapplayermodes-sse4.a \
+ libapplayermodes.a
+
+libapplayermodes_generic_a_sources = \
+ gimp-layer-modes.c \
+ gimp-layer-modes.h \
+ \
+ gimpoperationlayermode.c \
+ gimpoperationlayermode.h \
+ gimpoperationlayermode-blend.c \
+ gimpoperationlayermode-blend.h \
+ gimpoperationlayermode-composite.c \
+ gimpoperationlayermode-composite.h \
+ \
+ gimpoperationantierase.c \
+ gimpoperationantierase.h \
+ gimpoperationbehind.c \
+ gimpoperationbehind.h \
+ gimpoperationdissolve.c \
+ gimpoperationdissolve.h \
+ gimpoperationerase.c \
+ gimpoperationerase.h \
+ gimpoperationmerge.c \
+ gimpoperationmerge.h \
+ gimpoperationnormal.c \
+ gimpoperationnormal.h \
+ gimpoperationpassthrough.c \
+ gimpoperationpassthrough.h \
+ gimpoperationreplace.c \
+ gimpoperationreplace.h \
+ gimpoperationsplit.c \
+ gimpoperationsplit.h
+
+libapplayermodes_sse2_a_sources = \
+ gimpoperationlayermode-composite-sse2.c \
+ \
+ gimpoperationnormal-sse2.c
+
+libapplayermodes_sse4_a_sources = \
+ gimpoperationnormal-sse4.c
+
+
+libapplayermodes_generic_a_SOURCES = $(libapplayermodes_generic_a_sources)
+
+libapplayermodes_sse2_a_SOURCES = $(libapplayermodes_sse2_a_sources)
+
+libapplayermodes_sse2_a_CFLAGS = $(SSE2_EXTRA_CFLAGS)
+
+libapplayermodes_sse4_a_SOURCES = $(libapplayermodes_sse4_a_sources)
+
+libapplayermodes_sse4_a_CFLAGS = $(SSE4_1_EXTRA_CFLAGS)
+
+libapplayermodes_a_SOURCES =
+
+
+libapplayermodes.a: libapplayermodes-generic.a \
+ libapplayermodes-sse2.a \
+ libapplayermodes-sse4.a
+ $(AR) $(ARFLAGS) libapplayermodes.a \
+ $(libapplayermodes_generic_a_OBJECTS) \
+ $(libapplayermodes_sse2_a_OBJECTS) \
+ $(libapplayermodes_sse4_a_OBJECTS)
+ $(RANLIB) libapplayermodes.a
diff --git a/app/operations/layer-modes/Makefile.in b/app/operations/layer-modes/Makefile.in
new file mode 100644
index 0000000..ad8d4c1
--- /dev/null
+++ b/app/operations/layer-modes/Makefile.in
@@ -0,0 +1,1115 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = app/operations/layer-modes
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_@AM_V@)
+am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
+am__v_AR_0 = @echo " AR " $@;
+am__v_AR_1 =
+libapplayermodes_generic_a_AR = $(AR) $(ARFLAGS)
+libapplayermodes_generic_a_LIBADD =
+am__objects_1 = gimp-layer-modes.$(OBJEXT) \
+ gimpoperationlayermode.$(OBJEXT) \
+ gimpoperationlayermode-blend.$(OBJEXT) \
+ gimpoperationlayermode-composite.$(OBJEXT) \
+ gimpoperationantierase.$(OBJEXT) gimpoperationbehind.$(OBJEXT) \
+ gimpoperationdissolve.$(OBJEXT) gimpoperationerase.$(OBJEXT) \
+ gimpoperationmerge.$(OBJEXT) gimpoperationnormal.$(OBJEXT) \
+ gimpoperationpassthrough.$(OBJEXT) \
+ gimpoperationreplace.$(OBJEXT) gimpoperationsplit.$(OBJEXT)
+am_libapplayermodes_generic_a_OBJECTS = $(am__objects_1)
+libapplayermodes_generic_a_OBJECTS = \
+ $(am_libapplayermodes_generic_a_OBJECTS)
+libapplayermodes_sse2_a_AR = $(AR) $(ARFLAGS)
+libapplayermodes_sse2_a_LIBADD =
+am__objects_2 = libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.$(OBJEXT) \
+ libapplayermodes_sse2_a-gimpoperationnormal-sse2.$(OBJEXT)
+am_libapplayermodes_sse2_a_OBJECTS = $(am__objects_2)
+libapplayermodes_sse2_a_OBJECTS = \
+ $(am_libapplayermodes_sse2_a_OBJECTS)
+libapplayermodes_sse4_a_AR = $(AR) $(ARFLAGS)
+libapplayermodes_sse4_a_LIBADD =
+am__objects_3 = \
+ libapplayermodes_sse4_a-gimpoperationnormal-sse4.$(OBJEXT)
+am_libapplayermodes_sse4_a_OBJECTS = $(am__objects_3)
+libapplayermodes_sse4_a_OBJECTS = \
+ $(am_libapplayermodes_sse4_a_OBJECTS)
+libapplayermodes_a_AR = $(AR) $(ARFLAGS)
+libapplayermodes_a_LIBADD =
+am_libapplayermodes_a_OBJECTS =
+libapplayermodes_a_OBJECTS = $(am_libapplayermodes_a_OBJECTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimp-layer-modes.Po \
+ ./$(DEPDIR)/gimpoperationantierase.Po \
+ ./$(DEPDIR)/gimpoperationbehind.Po \
+ ./$(DEPDIR)/gimpoperationdissolve.Po \
+ ./$(DEPDIR)/gimpoperationerase.Po \
+ ./$(DEPDIR)/gimpoperationlayermode-blend.Po \
+ ./$(DEPDIR)/gimpoperationlayermode-composite.Po \
+ ./$(DEPDIR)/gimpoperationlayermode.Po \
+ ./$(DEPDIR)/gimpoperationmerge.Po \
+ ./$(DEPDIR)/gimpoperationnormal.Po \
+ ./$(DEPDIR)/gimpoperationpassthrough.Po \
+ ./$(DEPDIR)/gimpoperationreplace.Po \
+ ./$(DEPDIR)/gimpoperationsplit.Po \
+ ./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Po \
+ ./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Po \
+ ./$(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Po
+am__mv = mv -f
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libapplayermodes_generic_a_SOURCES) \
+ $(libapplayermodes_sse2_a_SOURCES) \
+ $(libapplayermodes_sse4_a_SOURCES) \
+ $(libapplayermodes_a_SOURCES)
+DIST_SOURCES = $(libapplayermodes_generic_a_SOURCES) \
+ $(libapplayermodes_sse2_a_SOURCES) \
+ $(libapplayermodes_sse4_a_SOURCES) \
+ $(libapplayermodes_a_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"Gimp-Layer-Modes\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/app \
+ -I$(top_srcdir)/app \
+ $(CAIRO_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ -I$(includedir)
+
+noinst_LIBRARIES = \
+ libapplayermodes-generic.a \
+ libapplayermodes-sse2.a \
+ libapplayermodes-sse4.a \
+ libapplayermodes.a
+
+libapplayermodes_generic_a_sources = \
+ gimp-layer-modes.c \
+ gimp-layer-modes.h \
+ \
+ gimpoperationlayermode.c \
+ gimpoperationlayermode.h \
+ gimpoperationlayermode-blend.c \
+ gimpoperationlayermode-blend.h \
+ gimpoperationlayermode-composite.c \
+ gimpoperationlayermode-composite.h \
+ \
+ gimpoperationantierase.c \
+ gimpoperationantierase.h \
+ gimpoperationbehind.c \
+ gimpoperationbehind.h \
+ gimpoperationdissolve.c \
+ gimpoperationdissolve.h \
+ gimpoperationerase.c \
+ gimpoperationerase.h \
+ gimpoperationmerge.c \
+ gimpoperationmerge.h \
+ gimpoperationnormal.c \
+ gimpoperationnormal.h \
+ gimpoperationpassthrough.c \
+ gimpoperationpassthrough.h \
+ gimpoperationreplace.c \
+ gimpoperationreplace.h \
+ gimpoperationsplit.c \
+ gimpoperationsplit.h
+
+libapplayermodes_sse2_a_sources = \
+ gimpoperationlayermode-composite-sse2.c \
+ \
+ gimpoperationnormal-sse2.c
+
+libapplayermodes_sse4_a_sources = \
+ gimpoperationnormal-sse4.c
+
+libapplayermodes_generic_a_SOURCES = $(libapplayermodes_generic_a_sources)
+libapplayermodes_sse2_a_SOURCES = $(libapplayermodes_sse2_a_sources)
+libapplayermodes_sse2_a_CFLAGS = $(SSE2_EXTRA_CFLAGS)
+libapplayermodes_sse4_a_SOURCES = $(libapplayermodes_sse4_a_sources)
+libapplayermodes_sse4_a_CFLAGS = $(SSE4_1_EXTRA_CFLAGS)
+libapplayermodes_a_SOURCES =
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu app/operations/layer-modes/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu app/operations/layer-modes/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+
+libapplayermodes-generic.a: $(libapplayermodes_generic_a_OBJECTS) $(libapplayermodes_generic_a_DEPENDENCIES) $(EXTRA_libapplayermodes_generic_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libapplayermodes-generic.a
+ $(AM_V_AR)$(libapplayermodes_generic_a_AR) libapplayermodes-generic.a $(libapplayermodes_generic_a_OBJECTS) $(libapplayermodes_generic_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libapplayermodes-generic.a
+
+libapplayermodes-sse2.a: $(libapplayermodes_sse2_a_OBJECTS) $(libapplayermodes_sse2_a_DEPENDENCIES) $(EXTRA_libapplayermodes_sse2_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libapplayermodes-sse2.a
+ $(AM_V_AR)$(libapplayermodes_sse2_a_AR) libapplayermodes-sse2.a $(libapplayermodes_sse2_a_OBJECTS) $(libapplayermodes_sse2_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libapplayermodes-sse2.a
+
+libapplayermodes-sse4.a: $(libapplayermodes_sse4_a_OBJECTS) $(libapplayermodes_sse4_a_DEPENDENCIES) $(EXTRA_libapplayermodes_sse4_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libapplayermodes-sse4.a
+ $(AM_V_AR)$(libapplayermodes_sse4_a_AR) libapplayermodes-sse4.a $(libapplayermodes_sse4_a_OBJECTS) $(libapplayermodes_sse4_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libapplayermodes-sse4.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-layer-modes.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationantierase.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationbehind.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationdissolve.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationerase.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationlayermode-blend.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationlayermode-composite.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationlayermode.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationmerge.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationnormal.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationpassthrough.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationreplace.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoperationsplit.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.o: gimpoperationlayermode-composite-sse2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -MT libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.o -MD -MP -MF $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Tpo -c -o libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.o `test -f 'gimpoperationlayermode-composite-sse2.c' || echo '$(srcdir)/'`gimpoperationlayermode-composite-sse2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Tpo $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimpoperationlayermode-composite-sse2.c' object='libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -c -o libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.o `test -f 'gimpoperationlayermode-composite-sse2.c' || echo '$(srcdir)/'`gimpoperationlayermode-composite-sse2.c
+
+libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.obj: gimpoperationlayermode-composite-sse2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -MT libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.obj -MD -MP -MF $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Tpo -c -o libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.obj `if test -f 'gimpoperationlayermode-composite-sse2.c'; then $(CYGPATH_W) 'gimpoperationlayermode-composite-sse2.c'; else $(CYGPATH_W) '$(srcdir)/gimpoperationlayermode-composite-sse2.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Tpo $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimpoperationlayermode-composite-sse2.c' object='libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -c -o libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.obj `if test -f 'gimpoperationlayermode-composite-sse2.c'; then $(CYGPATH_W) 'gimpoperationlayermode-composite-sse2.c'; else $(CYGPATH_W) '$(srcdir)/gimpoperationlayermode-composite-sse2.c'; fi`
+
+libapplayermodes_sse2_a-gimpoperationnormal-sse2.o: gimpoperationnormal-sse2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -MT libapplayermodes_sse2_a-gimpoperationnormal-sse2.o -MD -MP -MF $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Tpo -c -o libapplayermodes_sse2_a-gimpoperationnormal-sse2.o `test -f 'gimpoperationnormal-sse2.c' || echo '$(srcdir)/'`gimpoperationnormal-sse2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Tpo $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimpoperationnormal-sse2.c' object='libapplayermodes_sse2_a-gimpoperationnormal-sse2.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -c -o libapplayermodes_sse2_a-gimpoperationnormal-sse2.o `test -f 'gimpoperationnormal-sse2.c' || echo '$(srcdir)/'`gimpoperationnormal-sse2.c
+
+libapplayermodes_sse2_a-gimpoperationnormal-sse2.obj: gimpoperationnormal-sse2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -MT libapplayermodes_sse2_a-gimpoperationnormal-sse2.obj -MD -MP -MF $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Tpo -c -o libapplayermodes_sse2_a-gimpoperationnormal-sse2.obj `if test -f 'gimpoperationnormal-sse2.c'; then $(CYGPATH_W) 'gimpoperationnormal-sse2.c'; else $(CYGPATH_W) '$(srcdir)/gimpoperationnormal-sse2.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Tpo $(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimpoperationnormal-sse2.c' object='libapplayermodes_sse2_a-gimpoperationnormal-sse2.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse2_a_CFLAGS) $(CFLAGS) -c -o libapplayermodes_sse2_a-gimpoperationnormal-sse2.obj `if test -f 'gimpoperationnormal-sse2.c'; then $(CYGPATH_W) 'gimpoperationnormal-sse2.c'; else $(CYGPATH_W) '$(srcdir)/gimpoperationnormal-sse2.c'; fi`
+
+libapplayermodes_sse4_a-gimpoperationnormal-sse4.o: gimpoperationnormal-sse4.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse4_a_CFLAGS) $(CFLAGS) -MT libapplayermodes_sse4_a-gimpoperationnormal-sse4.o -MD -MP -MF $(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Tpo -c -o libapplayermodes_sse4_a-gimpoperationnormal-sse4.o `test -f 'gimpoperationnormal-sse4.c' || echo '$(srcdir)/'`gimpoperationnormal-sse4.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Tpo $(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimpoperationnormal-sse4.c' object='libapplayermodes_sse4_a-gimpoperationnormal-sse4.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse4_a_CFLAGS) $(CFLAGS) -c -o libapplayermodes_sse4_a-gimpoperationnormal-sse4.o `test -f 'gimpoperationnormal-sse4.c' || echo '$(srcdir)/'`gimpoperationnormal-sse4.c
+
+libapplayermodes_sse4_a-gimpoperationnormal-sse4.obj: gimpoperationnormal-sse4.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse4_a_CFLAGS) $(CFLAGS) -MT libapplayermodes_sse4_a-gimpoperationnormal-sse4.obj -MD -MP -MF $(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Tpo -c -o libapplayermodes_sse4_a-gimpoperationnormal-sse4.obj `if test -f 'gimpoperationnormal-sse4.c'; then $(CYGPATH_W) 'gimpoperationnormal-sse4.c'; else $(CYGPATH_W) '$(srcdir)/gimpoperationnormal-sse4.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Tpo $(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimpoperationnormal-sse4.c' object='libapplayermodes_sse4_a-gimpoperationnormal-sse4.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libapplayermodes_sse4_a_CFLAGS) $(CFLAGS) -c -o libapplayermodes_sse4_a-gimpoperationnormal-sse4.obj `if test -f 'gimpoperationnormal-sse4.c'; then $(CYGPATH_W) 'gimpoperationnormal-sse4.c'; else $(CYGPATH_W) '$(srcdir)/gimpoperationnormal-sse4.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimp-layer-modes.Po
+ -rm -f ./$(DEPDIR)/gimpoperationantierase.Po
+ -rm -f ./$(DEPDIR)/gimpoperationbehind.Po
+ -rm -f ./$(DEPDIR)/gimpoperationdissolve.Po
+ -rm -f ./$(DEPDIR)/gimpoperationerase.Po
+ -rm -f ./$(DEPDIR)/gimpoperationlayermode-blend.Po
+ -rm -f ./$(DEPDIR)/gimpoperationlayermode-composite.Po
+ -rm -f ./$(DEPDIR)/gimpoperationlayermode.Po
+ -rm -f ./$(DEPDIR)/gimpoperationmerge.Po
+ -rm -f ./$(DEPDIR)/gimpoperationnormal.Po
+ -rm -f ./$(DEPDIR)/gimpoperationpassthrough.Po
+ -rm -f ./$(DEPDIR)/gimpoperationreplace.Po
+ -rm -f ./$(DEPDIR)/gimpoperationsplit.Po
+ -rm -f ./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Po
+ -rm -f ./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Po
+ -rm -f ./$(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/gimp-layer-modes.Po
+ -rm -f ./$(DEPDIR)/gimpoperationantierase.Po
+ -rm -f ./$(DEPDIR)/gimpoperationbehind.Po
+ -rm -f ./$(DEPDIR)/gimpoperationdissolve.Po
+ -rm -f ./$(DEPDIR)/gimpoperationerase.Po
+ -rm -f ./$(DEPDIR)/gimpoperationlayermode-blend.Po
+ -rm -f ./$(DEPDIR)/gimpoperationlayermode-composite.Po
+ -rm -f ./$(DEPDIR)/gimpoperationlayermode.Po
+ -rm -f ./$(DEPDIR)/gimpoperationmerge.Po
+ -rm -f ./$(DEPDIR)/gimpoperationnormal.Po
+ -rm -f ./$(DEPDIR)/gimpoperationpassthrough.Po
+ -rm -f ./$(DEPDIR)/gimpoperationreplace.Po
+ -rm -f ./$(DEPDIR)/gimpoperationsplit.Po
+ -rm -f ./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationlayermode-composite-sse2.Po
+ -rm -f ./$(DEPDIR)/libapplayermodes_sse2_a-gimpoperationnormal-sse2.Po
+ -rm -f ./$(DEPDIR)/libapplayermodes_sse4_a-gimpoperationnormal-sse4.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+libapplayermodes.a: libapplayermodes-generic.a \
+ libapplayermodes-sse2.a \
+ libapplayermodes-sse4.a
+ $(AR) $(ARFLAGS) libapplayermodes.a \
+ $(libapplayermodes_generic_a_OBJECTS) \
+ $(libapplayermodes_sse2_a_OBJECTS) \
+ $(libapplayermodes_sse4_a_OBJECTS)
+ $(RANLIB) libapplayermodes.a
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/app/operations/layer-modes/gimp-layer-modes.c b/app/operations/layer-modes/gimp-layer-modes.c
new file mode 100644
index 0000000..deb1f3d
--- /dev/null
+++ b/app/operations/layer-modes/gimp-layer-modes.c
@@ -0,0 +1,1522 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimp-layer-modes.c
+ * Copyright (C) 2017 Michael Natterer <mitch@gimp.org>
+ * Øyvind Kolås <pippin@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 <glib-object.h>
+#include <gegl.h>
+
+#include "../operations-types.h"
+
+#include "gegl/gimp-babl.h"
+
+#include "gimpoperationlayermode.h"
+#include "gimpoperationlayermode-blend.h"
+
+#include "gimp-layer-modes.h"
+
+
+typedef struct _GimpLayerModeInfo GimpLayerModeInfo;
+
+struct _GimpLayerModeInfo
+{
+ GimpLayerMode layer_mode;
+ const gchar *op_name;
+ GimpLayerModeBlendFunc blend_function;
+ GimpLayerModeFlags flags;
+ GimpLayerModeContext context;
+ GimpLayerCompositeMode paint_composite_mode;
+ GimpLayerCompositeMode composite_mode;
+ GimpLayerColorSpace composite_space;
+ GimpLayerColorSpace blend_space;
+};
+
+
+/* static variables */
+
+static const GimpLayerModeInfo layer_mode_infos[] =
+{
+ { GIMP_LAYER_MODE_NORMAL_LEGACY,
+
+ .op_name = "gimp:normal",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_TRIVIAL,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_DISSOLVE,
+
+ .op_name = "gimp:dissolve",
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_TRIVIAL,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_UNION
+ },
+
+ { GIMP_LAYER_MODE_BEHIND_LEGACY,
+
+ .op_name = "gimp:behind",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_PAINT |
+ GIMP_LAYER_MODE_CONTEXT_FILTER,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_MULTIPLY_LEGACY,
+
+ .op_name = "gimp:multiply-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_SCREEN_LEGACY,
+
+ .op_name = "gimp:screen-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_OVERLAY_LEGACY,
+
+ .op_name = "gimp:softlight-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_DIFFERENCE_LEGACY,
+
+ .op_name = "gimp:difference-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_ADDITION_LEGACY,
+
+ .op_name = "gimp:addition-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_SUBTRACT_LEGACY,
+
+ .op_name = "gimp:subtract-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY,
+
+ .op_name = "gimp:darken-only-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY,
+
+ .op_name = "gimp:lighten-only-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_HSV_HUE_LEGACY,
+
+ .op_name = "gimp:hsv-hue-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_HSV_SATURATION_LEGACY,
+
+ .op_name = "gimp:hsv-saturation-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_HSL_COLOR_LEGACY,
+
+ .op_name = "gimp:hsl-color-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_HSV_VALUE_LEGACY,
+
+ .op_name = "gimp:hsv-value-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_DIVIDE_LEGACY,
+
+ .op_name = "gimp:divide-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_DODGE_LEGACY,
+
+ .op_name = "gimp:dodge-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_BURN_LEGACY,
+
+ .op_name = "gimp:burn-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_HARDLIGHT_LEGACY,
+
+ .op_name = "gimp:hardlight-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_SOFTLIGHT_LEGACY,
+
+ .op_name = "gimp:softlight-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY,
+
+ .op_name = "gimp:grain-extract-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY,
+
+ .op_name = "gimp:grain-merge-legacy",
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_COLOR_ERASE_LEGACY,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_color_erase,
+ .flags = GIMP_LAYER_MODE_FLAG_LEGACY |
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_SUBTRACTIVE,
+ .context = GIMP_LAYER_MODE_CONTEXT_PAINT |
+ GIMP_LAYER_MODE_CONTEXT_FILTER,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_OVERLAY,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_overlay,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_LCH_HUE,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_lch_hue,
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_LAB
+ },
+
+ { GIMP_LAYER_MODE_LCH_CHROMA,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_lch_chroma,
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_LAB
+ },
+
+ { GIMP_LAYER_MODE_LCH_COLOR,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_lch_color,
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_LAB
+ },
+
+ { GIMP_LAYER_MODE_LCH_LIGHTNESS,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_lch_lightness,
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_LAB
+ },
+
+ { GIMP_LAYER_MODE_NORMAL,
+
+ .op_name = "gimp:normal",
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_TRIVIAL,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR
+ },
+
+ { GIMP_LAYER_MODE_BEHIND,
+
+ .op_name = "gimp:behind",
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_PAINT |
+ GIMP_LAYER_MODE_CONTEXT_FILTER,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR
+ },
+
+ { GIMP_LAYER_MODE_MULTIPLY,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_multiply,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR
+ },
+
+ { GIMP_LAYER_MODE_SCREEN,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_screen,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_DIFFERENCE,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_difference,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_ADDITION,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_addition,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR
+ },
+
+ { GIMP_LAYER_MODE_SUBTRACT,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_subtract,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR
+ },
+
+ { GIMP_LAYER_MODE_DARKEN_ONLY,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_darken_only,
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR
+ /* no blend_space: reuse composite space, no conversion thus fewer copies */
+ },
+
+ { GIMP_LAYER_MODE_LIGHTEN_ONLY,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_lighten_only,
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR
+ /* no blend_space: reuse composite space, no conversion thus fewer copies */
+ },
+
+ { GIMP_LAYER_MODE_HSV_HUE,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_hsv_hue,
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_HSV_SATURATION,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_hsv_saturation,
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_HSL_COLOR,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_hsl_color,
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_HSV_VALUE,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_hsv_value,
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_DIVIDE,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_divide,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR
+ },
+
+ { GIMP_LAYER_MODE_DODGE,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_dodge,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_BURN,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_burn,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_HARDLIGHT,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_hardlight,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_SOFTLIGHT,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_softlight,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_GRAIN_EXTRACT,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_grain_extract,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_GRAIN_MERGE,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_grain_merge,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_VIVID_LIGHT,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_vivid_light,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_PIN_LIGHT,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_pin_light,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_LINEAR_LIGHT,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_linear_light,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_HARD_MIX,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_hard_mix,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_EXCLUSION,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_exclusion,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_LINEAR_BURN,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_linear_burn,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_LUMA_DARKEN_ONLY,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_luma_darken_only,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_luma_lighten_only,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+ },
+
+ { GIMP_LAYER_MODE_LUMINANCE,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_luminance,
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR
+ },
+
+ { GIMP_LAYER_MODE_COLOR_ERASE,
+
+ .op_name = "gimp:layer-mode",
+ .blend_function = gimp_operation_layer_mode_blend_color_erase,
+ .flags = GIMP_LAYER_MODE_FLAG_SUBTRACTIVE,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ .blend_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR
+ },
+
+ { GIMP_LAYER_MODE_ERASE,
+
+ .op_name = "gimp:erase",
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_SUBTRACTIVE |
+ GIMP_LAYER_MODE_FLAG_ALPHA_ONLY |
+ GIMP_LAYER_MODE_FLAG_TRIVIAL,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR
+ },
+
+ { GIMP_LAYER_MODE_MERGE,
+
+ .op_name = "gimp:merge",
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_TRIVIAL,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR
+ },
+
+ { GIMP_LAYER_MODE_SPLIT,
+
+ .op_name = "gimp:split",
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_SUBTRACTIVE |
+ GIMP_LAYER_MODE_FLAG_ALPHA_ONLY |
+ GIMP_LAYER_MODE_FLAG_TRIVIAL,
+ .context = GIMP_LAYER_MODE_CONTEXT_ALL,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP
+ },
+
+ { GIMP_LAYER_MODE_PASS_THROUGH,
+
+ .op_name = "gimp:pass-through",
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_TRIVIAL,
+ .context = GIMP_LAYER_MODE_CONTEXT_GROUP,
+ .composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR
+ },
+
+ { GIMP_LAYER_MODE_REPLACE,
+
+ .op_name = "gimp:replace",
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_TRIVIAL,
+ .context = GIMP_LAYER_MODE_CONTEXT_FILTER,
+ .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",
+ .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE |
+ GIMP_LAYER_MODE_FLAG_ALPHA_ONLY,
+ .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION,
+ .composite_mode = GIMP_LAYER_COMPOSITE_UNION
+ }
+};
+
+static const GimpLayerMode layer_mode_group_default[] =
+{
+ GIMP_LAYER_MODE_PASS_THROUGH,
+
+ GIMP_LAYER_MODE_SEPARATOR,
+
+ GIMP_LAYER_MODE_REPLACE,
+
+ GIMP_LAYER_MODE_SEPARATOR,
+
+ GIMP_LAYER_MODE_NORMAL,
+ GIMP_LAYER_MODE_DISSOLVE,
+ GIMP_LAYER_MODE_BEHIND,
+ GIMP_LAYER_MODE_COLOR_ERASE,
+ GIMP_LAYER_MODE_ERASE,
+ GIMP_LAYER_MODE_ANTI_ERASE,
+ GIMP_LAYER_MODE_MERGE,
+ GIMP_LAYER_MODE_SPLIT,
+
+ GIMP_LAYER_MODE_SEPARATOR,
+
+ GIMP_LAYER_MODE_LIGHTEN_ONLY,
+ GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY,
+ GIMP_LAYER_MODE_SCREEN,
+ GIMP_LAYER_MODE_DODGE,
+ GIMP_LAYER_MODE_ADDITION,
+
+ GIMP_LAYER_MODE_SEPARATOR,
+
+ GIMP_LAYER_MODE_DARKEN_ONLY,
+ GIMP_LAYER_MODE_LUMA_DARKEN_ONLY,
+ GIMP_LAYER_MODE_MULTIPLY,
+ GIMP_LAYER_MODE_BURN,
+ GIMP_LAYER_MODE_LINEAR_BURN,
+
+ GIMP_LAYER_MODE_SEPARATOR,
+
+ GIMP_LAYER_MODE_OVERLAY,
+ GIMP_LAYER_MODE_SOFTLIGHT,
+ GIMP_LAYER_MODE_HARDLIGHT,
+ GIMP_LAYER_MODE_VIVID_LIGHT,
+ GIMP_LAYER_MODE_PIN_LIGHT,
+ GIMP_LAYER_MODE_LINEAR_LIGHT,
+ GIMP_LAYER_MODE_HARD_MIX,
+
+ GIMP_LAYER_MODE_SEPARATOR,
+
+ GIMP_LAYER_MODE_DIFFERENCE,
+ GIMP_LAYER_MODE_EXCLUSION,
+ GIMP_LAYER_MODE_SUBTRACT,
+ GIMP_LAYER_MODE_GRAIN_EXTRACT,
+ GIMP_LAYER_MODE_GRAIN_MERGE,
+ GIMP_LAYER_MODE_DIVIDE,
+
+ GIMP_LAYER_MODE_SEPARATOR,
+
+ GIMP_LAYER_MODE_HSV_HUE,
+ GIMP_LAYER_MODE_HSV_SATURATION,
+ GIMP_LAYER_MODE_HSL_COLOR,
+ GIMP_LAYER_MODE_HSV_VALUE,
+
+ GIMP_LAYER_MODE_SEPARATOR,
+
+ GIMP_LAYER_MODE_LCH_HUE,
+ GIMP_LAYER_MODE_LCH_CHROMA,
+ GIMP_LAYER_MODE_LCH_COLOR,
+ GIMP_LAYER_MODE_LCH_LIGHTNESS,
+ GIMP_LAYER_MODE_LUMINANCE
+};
+
+static const GimpLayerMode layer_mode_group_legacy[] =
+{
+ GIMP_LAYER_MODE_NORMAL_LEGACY,
+ GIMP_LAYER_MODE_DISSOLVE,
+ GIMP_LAYER_MODE_BEHIND_LEGACY,
+ GIMP_LAYER_MODE_COLOR_ERASE_LEGACY,
+
+ GIMP_LAYER_MODE_SEPARATOR,
+
+ GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY,
+ GIMP_LAYER_MODE_SCREEN_LEGACY,
+ GIMP_LAYER_MODE_DODGE_LEGACY,
+ GIMP_LAYER_MODE_ADDITION_LEGACY,
+
+ GIMP_LAYER_MODE_SEPARATOR,
+
+ GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY,
+ GIMP_LAYER_MODE_MULTIPLY_LEGACY,
+ GIMP_LAYER_MODE_BURN_LEGACY,
+
+ GIMP_LAYER_MODE_SEPARATOR,
+
+ GIMP_LAYER_MODE_OVERLAY,
+ GIMP_LAYER_MODE_SOFTLIGHT_LEGACY,
+ GIMP_LAYER_MODE_HARDLIGHT_LEGACY,
+
+ GIMP_LAYER_MODE_SEPARATOR,
+
+ GIMP_LAYER_MODE_DIFFERENCE_LEGACY,
+ GIMP_LAYER_MODE_SUBTRACT_LEGACY,
+ GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY,
+ GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY,
+ GIMP_LAYER_MODE_DIVIDE_LEGACY,
+
+ GIMP_LAYER_MODE_SEPARATOR,
+
+ GIMP_LAYER_MODE_HSV_HUE_LEGACY,
+ GIMP_LAYER_MODE_HSV_SATURATION_LEGACY,
+ GIMP_LAYER_MODE_HSL_COLOR_LEGACY,
+ GIMP_LAYER_MODE_HSV_VALUE_LEGACY
+};
+
+static const GimpLayerMode layer_mode_groups[][2] =
+{
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_NORMAL,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_NORMAL_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_DISSOLVE,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_DISSOLVE
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_BEHIND,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_BEHIND_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_MULTIPLY,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_MULTIPLY_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_SCREEN,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_SCREEN_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_OVERLAY,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_DIFFERENCE,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_DIFFERENCE_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_ADDITION,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_ADDITION_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_SUBTRACT,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_SUBTRACT_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_DARKEN_ONLY,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_LIGHTEN_ONLY,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_HSV_HUE,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_HSV_HUE_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_HSV_SATURATION,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_HSV_SATURATION_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_HSL_COLOR,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_HSL_COLOR_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_HSV_VALUE,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_HSV_VALUE_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_DIVIDE,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_DIVIDE_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_DODGE,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_DODGE_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_BURN,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_BURN_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_HARDLIGHT,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_HARDLIGHT_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_SOFTLIGHT,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_GRAIN_EXTRACT,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_GRAIN_MERGE,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_COLOR_ERASE,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = GIMP_LAYER_MODE_COLOR_ERASE_LEGACY,
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_VIVID_LIGHT,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_PIN_LIGHT,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_LINEAR_LIGHT,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_HARD_MIX,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_EXCLUSION,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_LINEAR_BURN,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_LUMA_DARKEN_ONLY,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_LUMINANCE,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_ERASE,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_MERGE,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_SPLIT,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_PASS_THROUGH,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1,
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_REPLACE,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ },
+
+ { [GIMP_LAYER_MODE_GROUP_DEFAULT] = GIMP_LAYER_MODE_ANTI_ERASE,
+ [GIMP_LAYER_MODE_GROUP_LEGACY ] = -1
+ }
+};
+
+
+/* public functions */
+
+void
+gimp_layer_modes_init (void)
+{
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (layer_mode_infos); i++)
+ {
+ gimp_assert ((GimpLayerMode) i == layer_mode_infos[i].layer_mode);
+ }
+}
+
+static const GimpLayerModeInfo *
+gimp_layer_mode_info (GimpLayerMode mode)
+{
+ g_return_val_if_fail (mode >= 0 && mode < G_N_ELEMENTS (layer_mode_infos),
+ &layer_mode_infos[0]);
+
+ return &layer_mode_infos[mode];
+}
+
+gboolean
+gimp_layer_mode_is_legacy (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+ if (! info)
+ return FALSE;
+
+ return (info->flags & GIMP_LAYER_MODE_FLAG_LEGACY) != 0;
+}
+
+gboolean
+gimp_layer_mode_is_blend_space_mutable (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+ if (! info)
+ return FALSE;
+
+ return (info->flags & GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE) == 0;
+}
+
+gboolean
+gimp_layer_mode_is_composite_space_mutable (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+ if (! info)
+ return FALSE;
+
+ return (info->flags & GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE) == 0;
+}
+
+gboolean
+gimp_layer_mode_is_composite_mode_mutable (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+ if (! info)
+ return FALSE;
+
+ return (info->flags & GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE) == 0;
+}
+
+gboolean
+gimp_layer_mode_is_subtractive (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+ if (! info)
+ return FALSE;
+
+ return (info->flags & GIMP_LAYER_MODE_FLAG_SUBTRACTIVE) != 0;
+}
+
+gboolean
+gimp_layer_mode_is_alpha_only (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+ if (! info)
+ return FALSE;
+
+ return (info->flags & GIMP_LAYER_MODE_FLAG_ALPHA_ONLY) != 0;
+}
+
+gboolean
+gimp_layer_mode_is_trivial (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+ if (! info)
+ return FALSE;
+
+ return (info->flags & GIMP_LAYER_MODE_FLAG_TRIVIAL) != 0;
+}
+
+GimpLayerColorSpace
+gimp_layer_mode_get_blend_space (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+ if (! info)
+ return GIMP_LAYER_COLOR_SPACE_RGB_LINEAR;
+
+ return info->blend_space;
+}
+
+GimpLayerColorSpace
+gimp_layer_mode_get_composite_space (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+ if (! info)
+ return GIMP_LAYER_COLOR_SPACE_RGB_LINEAR;
+
+ return info->composite_space;
+}
+
+GimpLayerCompositeMode
+gimp_layer_mode_get_composite_mode (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+ if (! info)
+ return GIMP_LAYER_COMPOSITE_UNION;
+
+ return info->composite_mode;
+}
+
+GimpLayerCompositeMode
+gimp_layer_mode_get_paint_composite_mode (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+ if (! info)
+ return GIMP_LAYER_COMPOSITE_UNION;
+
+ return info->paint_composite_mode;
+}
+
+const gchar *
+gimp_layer_mode_get_operation (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+ if (! info)
+ return "gimp:layer-mode";
+
+ return info->op_name;
+}
+
+GimpLayerModeFunc
+gimp_layer_mode_get_function (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+ static GimpLayerModeFunc funcs[G_N_ELEMENTS (layer_mode_infos)];
+
+ if (! info)
+ info = layer_mode_infos;
+
+ mode = info - layer_mode_infos;
+
+ if (! funcs[mode])
+ {
+ GeglNode *node;
+ GeglOperation *operation;
+
+ node = gegl_node_new_child (NULL,
+ "operation", info->op_name,
+ NULL);
+
+ operation = gegl_node_get_gegl_operation (node);
+
+ funcs[mode] = GIMP_OPERATION_LAYER_MODE_GET_CLASS (operation)->process;
+
+ g_object_unref (node);
+ }
+
+ return funcs[mode];
+}
+
+GimpLayerModeBlendFunc
+gimp_layer_mode_get_blend_function (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+ if (! info)
+ return NULL;
+
+ return info->blend_function;
+}
+
+GimpLayerModeContext
+gimp_layer_mode_get_context (GimpLayerMode mode)
+{
+ const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+ if (! info)
+ return 0;
+
+ return info->context;
+}
+
+GimpLayerMode *
+gimp_layer_mode_get_context_array (GimpLayerMode mode,
+ GimpLayerModeContext context,
+ gint *n_modes)
+{
+ GimpLayerModeGroup group;
+ const GimpLayerMode *group_modes;
+ gint n_group_modes;
+ GimpLayerMode *array;
+ gint i;
+
+ group = gimp_layer_mode_get_group (mode);
+
+ group_modes = gimp_layer_mode_get_group_array (group, &n_group_modes);
+
+ array = g_new0 (GimpLayerMode, n_group_modes);
+ *n_modes = 0;
+
+ for (i = 0; i < n_group_modes; i++)
+ {
+ if (group_modes[i] != GIMP_LAYER_MODE_SEPARATOR &&
+ (gimp_layer_mode_get_context (group_modes[i]) & context))
+ {
+ array[*n_modes] = group_modes[i];
+ (*n_modes)++;
+ }
+ }
+
+ return array;
+}
+
+static gboolean
+is_mode_in_array (const GimpLayerMode *modes,
+ gint n_modes,
+ GimpLayerMode mode)
+{
+ gint i;
+
+ for (i = 0; i < n_modes; i++)
+ {
+ if (modes[i] == mode)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+GimpLayerModeGroup
+gimp_layer_mode_get_group (GimpLayerMode mode)
+{
+ if (is_mode_in_array (layer_mode_group_default,
+ G_N_ELEMENTS (layer_mode_group_default), mode))
+ {
+ return GIMP_LAYER_MODE_GROUP_DEFAULT;
+ }
+ else if (is_mode_in_array (layer_mode_group_legacy,
+ G_N_ELEMENTS (layer_mode_group_legacy), mode))
+ {
+ return GIMP_LAYER_MODE_GROUP_LEGACY;
+ }
+
+ return GIMP_LAYER_MODE_GROUP_DEFAULT;
+}
+
+const GimpLayerMode *
+gimp_layer_mode_get_group_array (GimpLayerModeGroup group,
+ gint *n_modes)
+{
+ g_return_val_if_fail (n_modes != NULL, NULL);
+
+ switch (group)
+ {
+ case GIMP_LAYER_MODE_GROUP_DEFAULT:
+ *n_modes = G_N_ELEMENTS (layer_mode_group_default);
+ return layer_mode_group_default;
+
+ case GIMP_LAYER_MODE_GROUP_LEGACY:
+ *n_modes = G_N_ELEMENTS (layer_mode_group_legacy);
+ return layer_mode_group_legacy;
+
+ default:
+ g_return_val_if_reached (NULL);
+ }
+}
+
+gboolean
+gimp_layer_mode_get_for_group (GimpLayerMode old_mode,
+ GimpLayerModeGroup new_group,
+ GimpLayerMode *new_mode)
+{
+ gint i;
+
+ g_return_val_if_fail (new_mode != NULL, FALSE);
+
+ for (i = 0; i < G_N_ELEMENTS (layer_mode_groups); i++)
+ {
+ if (is_mode_in_array (layer_mode_groups[i], 2, old_mode))
+ {
+ *new_mode = layer_mode_groups[i][new_group];
+
+ if (*new_mode != -1)
+ return TRUE;
+
+ return FALSE;
+ }
+ }
+
+ *new_mode = -1;
+
+ return FALSE;
+}
+
+const Babl *
+gimp_layer_mode_get_format (GimpLayerMode mode,
+ GimpLayerColorSpace blend_space,
+ GimpLayerColorSpace composite_space,
+ GimpLayerCompositeMode composite_mode,
+ const Babl *preferred_format)
+{
+ GimpLayerCompositeRegion composite_region;
+
+ /* for now, all modes perform i/o in the composite space. */
+ (void) mode;
+ (void) blend_space;
+
+ if (composite_space == GIMP_LAYER_COLOR_SPACE_AUTO)
+ composite_space = gimp_layer_mode_get_composite_space (mode);
+
+ if (composite_mode == GIMP_LAYER_COMPOSITE_AUTO)
+ composite_mode = gimp_layer_mode_get_composite_mode (mode);
+
+ composite_region = gimp_layer_mode_get_included_region (mode, composite_mode);
+
+ if (gimp_layer_mode_is_alpha_only (mode))
+ {
+ if (composite_region != GIMP_LAYER_COMPOSITE_REGION_UNION)
+ {
+ /* alpha-only layer modes don't combine colors in non-union composite
+ * modes, hence we can disregard the composite space.
+ */
+ composite_space = GIMP_LAYER_COLOR_SPACE_AUTO;
+ }
+ }
+ else if (gimp_layer_mode_is_trivial (mode))
+ {
+ if (! (composite_region & GIMP_LAYER_COMPOSITE_REGION_DESTINATION))
+ {
+ /* trivial layer modes don't combine colors when only the source
+ * region is included, hence we can disregard the composite space.
+ */
+ composite_space = GIMP_LAYER_COLOR_SPACE_AUTO;
+ }
+ }
+
+ switch (composite_space)
+ {
+ case GIMP_LAYER_COLOR_SPACE_AUTO:
+ /* compositing is color-space agnostic. return a format that has a fast
+ * conversion path to/from the preferred format.
+ */
+ if (! preferred_format || gimp_babl_format_get_linear (preferred_format))
+ return babl_format ("RGBA float");
+ else
+ return babl_format ("R'G'B'A float");
+
+ case GIMP_LAYER_COLOR_SPACE_RGB_LINEAR:
+ return babl_format ("RGBA float");
+
+ case GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL:
+ return babl_format ("R'G'B'A float");
+
+ case GIMP_LAYER_COLOR_SPACE_LAB:
+ return babl_format ("CIE Lab alpha float");
+ }
+
+ g_return_val_if_reached (babl_format ("RGBA float"));
+}
+
+GimpLayerCompositeRegion
+gimp_layer_mode_get_included_region (GimpLayerMode mode,
+ GimpLayerCompositeMode composite_mode)
+{
+ if (composite_mode == GIMP_LAYER_COMPOSITE_AUTO)
+ composite_mode = gimp_layer_mode_get_composite_mode (mode);
+
+ switch (composite_mode)
+ {
+ case GIMP_LAYER_COMPOSITE_UNION:
+ return GIMP_LAYER_COMPOSITE_REGION_UNION;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP:
+ return GIMP_LAYER_COMPOSITE_REGION_DESTINATION;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER:
+ return GIMP_LAYER_COMPOSITE_REGION_SOURCE;
+
+ case GIMP_LAYER_COMPOSITE_INTERSECTION:
+ return GIMP_LAYER_COMPOSITE_REGION_INTERSECTION;
+
+ default:
+ g_return_val_if_reached (GIMP_LAYER_COMPOSITE_REGION_INTERSECTION);
+ }
+}
diff --git a/app/operations/layer-modes/gimp-layer-modes.h b/app/operations/layer-modes/gimp-layer-modes.h
new file mode 100644
index 0000000..30c5e15
--- /dev/null
+++ b/app/operations/layer-modes/gimp-layer-modes.h
@@ -0,0 +1,73 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimp-layer-modes.h
+ * Copyright (C) 2017 Michael Natterer <mitch@gimp.org>
+ * Øyvind Kolås <pippin@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/>.
+ */
+
+#ifndef __GIMP_LAYER_MODES_H__
+#define __GIMP_LAYER_MODES_H__
+
+
+void gimp_layer_modes_init (void);
+
+gboolean gimp_layer_mode_is_legacy (GimpLayerMode mode);
+
+gboolean gimp_layer_mode_is_blend_space_mutable (GimpLayerMode mode);
+gboolean gimp_layer_mode_is_composite_space_mutable (GimpLayerMode mode);
+gboolean gimp_layer_mode_is_composite_mode_mutable (GimpLayerMode mode);
+
+gboolean gimp_layer_mode_is_subtractive (GimpLayerMode mode);
+gboolean gimp_layer_mode_is_alpha_only (GimpLayerMode mode);
+gboolean gimp_layer_mode_is_trivial (GimpLayerMode mode);
+
+GimpLayerColorSpace gimp_layer_mode_get_blend_space (GimpLayerMode mode);
+GimpLayerColorSpace gimp_layer_mode_get_composite_space (GimpLayerMode mode);
+GimpLayerCompositeMode gimp_layer_mode_get_composite_mode (GimpLayerMode mode);
+GimpLayerCompositeMode gimp_layer_mode_get_paint_composite_mode (GimpLayerMode mode);
+
+const gchar * gimp_layer_mode_get_operation (GimpLayerMode mode);
+
+GimpLayerModeFunc gimp_layer_mode_get_function (GimpLayerMode mode);
+GimpLayerModeBlendFunc gimp_layer_mode_get_blend_function (GimpLayerMode mode);
+
+GimpLayerModeContext gimp_layer_mode_get_context (GimpLayerMode mode);
+
+GimpLayerMode * gimp_layer_mode_get_context_array (GimpLayerMode mode,
+ GimpLayerModeContext context,
+ gint *n_modes);
+
+GimpLayerModeGroup gimp_layer_mode_get_group (GimpLayerMode mode);
+
+const GimpLayerMode * gimp_layer_mode_get_group_array (GimpLayerModeGroup group,
+ gint *n_modes);
+
+gboolean gimp_layer_mode_get_for_group (GimpLayerMode old_mode,
+ GimpLayerModeGroup new_group,
+ GimpLayerMode *new_mode);
+
+const Babl * gimp_layer_mode_get_format (GimpLayerMode mode,
+ GimpLayerColorSpace blend_space,
+ GimpLayerColorSpace composite_space,
+ GimpLayerCompositeMode composite_mode,
+ const Babl *preferred_format);
+
+GimpLayerCompositeRegion gimp_layer_mode_get_included_region (GimpLayerMode mode,
+ GimpLayerCompositeMode composite_mode);
+
+
+#endif /* __GIMP_LAYER_MODES_H__ */
diff --git a/app/operations/layer-modes/gimpoperationantierase.c b/app/operations/layer-modes/gimpoperationantierase.c
new file mode 100644
index 0000000..3fb2947
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationantierase.c
@@ -0,0 +1,188 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationantierase.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationantierase.h"
+
+
+
+static gboolean gimp_operation_anti_erase_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+static GimpLayerCompositeRegion gimp_operation_anti_erase_get_affected_region (GimpOperationLayerMode *layer_mode);
+
+
+G_DEFINE_TYPE (GimpOperationAntiErase, gimp_operation_anti_erase,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_anti_erase_class_init (GimpOperationAntiEraseClass *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:anti-erase",
+ "description", "GIMP anti erase mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_anti_erase_process;
+ layer_mode_class->get_affected_region = gimp_operation_anti_erase_get_affected_region;
+}
+
+static void
+gimp_operation_anti_erase_init (GimpOperationAntiErase *self)
+{
+}
+
+static gboolean
+gimp_operation_anti_erase_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--)
+ {
+ gfloat value = opacity;
+ gint b;
+
+ if (has_mask)
+ value *= *mask;
+
+ out[ALPHA] = in[ALPHA] + (1.0 - in[ALPHA]) * layer[ALPHA] * value;
+
+ 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_BACKDROP:
+ while (samples--)
+ {
+ gint b;
+
+ out[ALPHA] = in[ALPHA];
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+
+ in += 4;
+ out += 4;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER:
+ while (samples--)
+ {
+ gfloat value = opacity;
+ gint b;
+
+ if (has_mask)
+ value *= *mask;
+
+ out[ALPHA] = layer[ALPHA] * value;
+
+ 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 value = opacity;
+ gint b;
+
+ if (has_mask)
+ value *= *mask;
+
+ out[ALPHA] = in[ALPHA] * layer[ALPHA] * value;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+ break;
+ }
+
+ return TRUE;
+}
+
+static GimpLayerCompositeRegion
+gimp_operation_anti_erase_get_affected_region (GimpOperationLayerMode *layer_mode)
+{
+ return GIMP_LAYER_COMPOSITE_REGION_SOURCE;
+}
diff --git a/app/operations/layer-modes/gimpoperationantierase.h b/app/operations/layer-modes/gimpoperationantierase.h
new file mode 100644
index 0000000..0c5ddf8
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationantierase.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationantierase.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_ANTI_ERASE_H__
+#define __GIMP_OPERATION_ANTI_ERASE_H__
+
+
+#include "gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_ANTI_ERASE (gimp_operation_anti_erase_get_type ())
+#define GIMP_OPERATION_ANTI_ERASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_ANTI_ERASE, GimpOperationAntiErase))
+#define GIMP_OPERATION_ANTI_ERASE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_ANTI_ERASE, GimpOperationAntiEraseClass))
+#define GIMP_IS_OPERATION_ANTI_ERASE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_ANTI_ERASE))
+#define GIMP_IS_OPERATION_ANTI_ERASE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_ANTI_ERASE))
+#define GIMP_OPERATION_ANTI_ERASE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_ANTI_ERASE, GimpOperationAntiEraseClass))
+
+
+typedef struct _GimpOperationAntiErase GimpOperationAntiErase;
+typedef struct _GimpOperationAntiEraseClass GimpOperationAntiEraseClass;
+
+struct _GimpOperationAntiErase
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationAntiEraseClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_anti_erase_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_ANTI_ERASE_H__ */
diff --git a/app/operations/layer-modes/gimpoperationbehind.c b/app/operations/layer-modes/gimpoperationbehind.c
new file mode 100644
index 0000000..1dc2630
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationbehind.c
@@ -0,0 +1,236 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationbehind.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationbehind.h"
+
+
+
+static gboolean gimp_operation_behind_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationBehind, gimp_operation_behind,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_behind_class_init (GimpOperationBehindClass *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:behind",
+ "description", "GIMP behind mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_behind_process;
+}
+
+static void
+gimp_operation_behind_init (GimpOperationBehind *self)
+{
+}
+
+static gboolean
+gimp_operation_behind_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--)
+ {
+ gfloat src1_alpha = in[ALPHA];
+ gfloat src2_alpha = layer[ALPHA] * opacity;
+ gfloat new_alpha;
+ gint b;
+
+ if (has_mask)
+ src2_alpha *= *mask;
+
+ new_alpha = src2_alpha + (1.0 - src2_alpha) * src1_alpha;
+
+ if (new_alpha)
+ {
+ gfloat ratio = in[ALPHA] / new_alpha;
+ gfloat compl_ratio = 1.0f - ratio;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b] * ratio + layer[b] * compl_ratio;
+ }
+ }
+ else
+ {
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = layer[b];
+ }
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP:
+ while (samples--)
+ {
+ gfloat src1_alpha = in[ALPHA];
+ gfloat new_alpha;
+ gint b;
+
+ new_alpha = src1_alpha;
+
+ if (new_alpha)
+ {
+ for (b = RED; b < ALPHA; b++)
+ out[b] = in[b];
+ }
+ else
+ {
+ for (b = RED; b < ALPHA; b++)
+ out[b] = layer[b];
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER:
+ while (samples--)
+ {
+ gfloat src1_alpha = in[ALPHA];
+ gfloat src2_alpha = layer[ALPHA] * opacity;
+ gfloat new_alpha;
+ gint b;
+
+ if (has_mask)
+ src2_alpha *= *mask;
+
+ new_alpha = src2_alpha;
+
+ if (new_alpha)
+ {
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = layer[b] + (in[b] - layer[b]) * src1_alpha;
+ }
+ }
+ else
+ {
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = layer[b];
+ }
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_INTERSECTION:
+ while (samples--)
+ {
+ gfloat src1_alpha = in[ALPHA];
+ gfloat src2_alpha = layer[ALPHA] * opacity;
+ gfloat new_alpha;
+ gint b;
+
+ if (has_mask)
+ src2_alpha *= *mask;
+
+ new_alpha = src1_alpha * src2_alpha;
+
+ if (new_alpha)
+ {
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+ else
+ {
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = layer[b];
+ }
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes/gimpoperationbehind.h b/app/operations/layer-modes/gimpoperationbehind.h
new file mode 100644
index 0000000..46b9ac4
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationbehind.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationbehind.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_BEHIND_H__
+#define __GIMP_OPERATION_BEHIND_H__
+
+
+#include "gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_BEHIND (gimp_operation_behind_get_type ())
+#define GIMP_OPERATION_BEHIND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_BEHIND, GimpOperationBehind))
+#define GIMP_OPERATION_BEHIND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_BEHIND, GimpOperationBehindClass))
+#define GIMP_IS_OPERATION_BEHIND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_BEHIND))
+#define GIMP_IS_OPERATION_BEHIND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_BEHIND))
+#define GIMP_OPERATION_BEHIND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_BEHIND, GimpOperationBehindClass))
+
+
+typedef struct _GimpOperationBehind GimpOperationBehind;
+typedef struct _GimpOperationBehindClass GimpOperationBehindClass;
+
+struct _GimpOperationBehind
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationBehindClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_behind_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_BEHIND_H__ */
diff --git a/app/operations/layer-modes/gimpoperationdissolve.c b/app/operations/layer-modes/gimpoperationdissolve.c
new file mode 100644
index 0000000..fb4b1d0
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationdissolve.c
@@ -0,0 +1,175 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationdissolve.c
+ * Copyright (C) 2012 Ville Sokk <ville.sokk@gmail.com>
+ * 2012 Øyvind Kolås <pippin@gimp.org>
+ * 2003 Helvetix Victorinox
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationdissolve.h"
+
+
+#define RANDOM_TABLE_SIZE 4096
+
+
+static gboolean gimp_operation_dissolve_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *result,
+ gint level);
+static GimpLayerCompositeRegion gimp_operation_dissolve_get_affected_region (GimpOperationLayerMode *layer_mode);
+
+
+G_DEFINE_TYPE (GimpOperationDissolve, gimp_operation_dissolve,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static gint32 random_table[RANDOM_TABLE_SIZE];
+
+
+static void
+gimp_operation_dissolve_class_init (GimpOperationDissolveClass *klass)
+{
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GimpOperationLayerModeClass *layer_mode_class = GIMP_OPERATION_LAYER_MODE_CLASS (klass);
+ GRand *gr;
+ gint i;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:dissolve",
+ "description", "GIMP dissolve mode operation",
+ "categories", "compositors",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_dissolve_process;
+ layer_mode_class->get_affected_region = gimp_operation_dissolve_get_affected_region;
+
+ /* generate a table of random seeds */
+ gr = g_rand_new_with_seed (314159265);
+ for (i = 0; i < RANDOM_TABLE_SIZE; i++)
+ random_table[i] = g_rand_int (gr);
+
+ g_rand_free (gr);
+}
+
+static void
+gimp_operation_dissolve_init (GimpOperationDissolve *self)
+{
+}
+
+static gboolean
+gimp_operation_dissolve_process (GeglOperation *op,
+ void *in_p,
+ void *layer_p,
+ void *mask_p,
+ void *out_p,
+ glong samples,
+ const GeglRectangle *result,
+ 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;
+ gint x, y;
+
+ for (y = result->y; y < result->y + result->height; y++)
+ {
+ GRand *gr;
+
+ /* The offset can be negative. I could just abs() the result, but we
+ * probably prefer to use different indexes of the table when possible for
+ * nicer randomization, so let's cycle the modulo so that -1 is the last
+ * table index.
+ */
+ gr = g_rand_new_with_seed (random_table[((y % RANDOM_TABLE_SIZE) + RANDOM_TABLE_SIZE) % RANDOM_TABLE_SIZE]);
+
+ /* fast forward through the rows pseudo random sequence */
+ for (x = 0; x < result->x; x++)
+ g_rand_int (gr);
+
+ for (x = result->x; x < result->x + result->width; x++)
+ {
+ gfloat value = layer[ALPHA] * opacity * 255;
+
+ if (has_mask)
+ value *= *mask;
+
+ if (g_rand_int_range (gr, 0, 255) >= value)
+ {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+
+ if (layer_mode->composite_mode == GIMP_LAYER_COMPOSITE_UNION ||
+ layer_mode->composite_mode == GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP)
+ {
+ out[3] = in[3];
+ }
+ else
+ {
+ out[3] = 0.0f;
+ }
+ }
+ else
+ {
+ out[0] = layer[0];
+ out[1] = layer[1];
+ out[2] = layer[2];
+
+ if (layer_mode->composite_mode == GIMP_LAYER_COMPOSITE_UNION ||
+ layer_mode->composite_mode == GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER)
+ {
+ out[3] = 1.0f;
+ }
+ else
+ {
+ out[3] = in[3];
+ }
+ }
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+
+ g_rand_free (gr);
+ }
+
+ return TRUE;
+}
+
+static GimpLayerCompositeRegion
+gimp_operation_dissolve_get_affected_region (GimpOperationLayerMode *layer_mode)
+{
+ return GIMP_LAYER_COMPOSITE_REGION_SOURCE;
+}
diff --git a/app/operations/layer-modes/gimpoperationdissolve.h b/app/operations/layer-modes/gimpoperationdissolve.h
new file mode 100644
index 0000000..d448918
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationdissolve.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationdissolve.h
+ * Copyright (C) 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_DISSOLVE_H__
+#define __GIMP_OPERATION_DISSOLVE_H__
+
+
+#include "gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_DISSOLVE (gimp_operation_dissolve_get_type ())
+#define GIMP_OPERATION_DISSOLVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_DISSOLVE, GimpOperationDissolve))
+#define GIMP_OPERATION_DISSOLVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_DISSOLVE, GimpOperationDissolveClass))
+#define GIMP_IS_OPERATION_DISSOLVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_DISSOLVE))
+#define GIMP_IS_OPERATION_DISSOLVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_DISSOLVE))
+#define GIMP_OPERATION_DISSOLVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_DISSOLVE, GimpOperationDissolveClass))
+
+
+typedef struct _GimpOperationDissolve GimpOperationDissolve;
+typedef struct _GimpOperationDissolveClass GimpOperationDissolveClass;
+
+struct _GimpOperationDissolveClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+struct _GimpOperationDissolve
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+
+GType gimp_operation_dissolve_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_DISSOLVE_H__ */
diff --git a/app/operations/layer-modes/gimpoperationerase.c b/app/operations/layer-modes/gimpoperationerase.c
new file mode 100644
index 0000000..aae6eac
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationerase.c
@@ -0,0 +1,214 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationerase.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2012 Ville Sokk <ville.sokk@gmail.com>
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationerase.h"
+
+
+static gboolean gimp_operation_erase_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationErase, gimp_operation_erase,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_erase_class_init (GimpOperationEraseClass *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:erase",
+ "description", "GIMP erase mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_erase_process;
+}
+
+static void
+gimp_operation_erase_init (GimpOperationErase *self)
+{
+}
+
+static gboolean
+gimp_operation_erase_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:
+ while (samples--)
+ {
+ gfloat layer_alpha;
+ gfloat new_alpha;
+ gint b;
+
+ layer_alpha = layer[ALPHA] * opacity;
+
+ if (has_mask)
+ layer_alpha *= (*mask);
+
+ new_alpha = in[ALPHA] + layer_alpha - 2.0f * in[ALPHA] * layer_alpha;
+
+ if (new_alpha != 0.0f)
+ {
+ gfloat ratio;
+
+ ratio = (1.0f - in[ALPHA]) * layer_alpha / new_alpha;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = ratio * layer[b] + (1.0f - ratio) * in[b];
+ }
+ }
+ else
+ {
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask ++;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP:
+ case GIMP_LAYER_COMPOSITE_AUTO:
+ while (samples--)
+ {
+ gfloat layer_alpha;
+ gfloat new_alpha;
+ gint b;
+
+ layer_alpha = layer[ALPHA] * opacity;
+
+ if (has_mask)
+ layer_alpha *= (*mask);
+
+ new_alpha = (1.0f - layer_alpha) * in[ALPHA];
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask ++;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER:
+ while (samples--)
+ {
+ gfloat layer_alpha;
+ gfloat new_alpha;
+ const gfloat *src;
+ gint b;
+
+ layer_alpha = layer[ALPHA] * opacity;
+
+ if (has_mask)
+ layer_alpha *= (*mask);
+
+ new_alpha = (1.0f - in[ALPHA]) * layer_alpha;
+
+ src = layer;
+
+ if (new_alpha == 0.0f)
+ src = in;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = src[b];
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask ++;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_INTERSECTION:
+ while (samples--)
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+
+ out[ALPHA] = 0.0f;
+
+ in += 4;
+ out += 4;
+ }
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes/gimpoperationerase.h b/app/operations/layer-modes/gimpoperationerase.h
new file mode 100644
index 0000000..fec309b
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationerase.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationerase.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_ERASE_H__
+#define __GIMP_OPERATION_ERASE_H__
+
+
+#include "gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_ERASE (gimp_operation_erase_get_type ())
+#define GIMP_OPERATION_ERASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_ERASE, GimpOperationErase))
+#define GIMP_OPERATION_ERASE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_ERASE, GimpOperationEraseClass))
+#define GIMP_IS_OPERATION_ERASE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_ERASE))
+#define GIMP_IS_OPERATION_ERASE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_ERASE))
+#define GIMP_OPERATION_ERASE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_ERASE, GimpOperationEraseClass))
+
+
+typedef struct _GimpOperationErase GimpOperationErase;
+typedef struct _GimpOperationEraseClass GimpOperationEraseClass;
+
+struct _GimpOperationErase
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationEraseClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_erase_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_ERASE_MODE_H__ */
diff --git a/app/operations/layer-modes/gimpoperationlayermode-blend.c b/app/operations/layer-modes/gimpoperationlayermode-blend.c
new file mode 100644
index 0000000..e107462
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationlayermode-blend.c
@@ -0,0 +1,1215 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationlayermode-blend.c
+ * Copyright (C) 2017 Michael Natterer <mitch@gimp.org>
+ * 2017 Øyvind Kolås <pippin@gimp.org>
+ * 2017 Ell
+ *
+ * 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-plugin.h>
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpbase/gimpbase.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "../operations-types.h"
+
+#include "gimpoperationlayermode-blend.h"
+
+
+#define EPSILON 1e-6f
+
+#define SAFE_DIV_MIN EPSILON
+#define SAFE_DIV_MAX (1.0f / SAFE_DIV_MIN)
+
+
+/* local function prototypes */
+
+static inline gfloat safe_div (gfloat a,
+ gfloat b);
+
+
+/* private functions */
+
+
+/* returns a / b, clamped to [-SAFE_DIV_MAX, SAFE_DIV_MAX].
+ * if -SAFE_DIV_MIN <= a <= SAFE_DIV_MIN, returns 0.
+ */
+static inline gfloat
+safe_div (gfloat a,
+ gfloat b)
+{
+ gfloat result = 0.0f;
+
+ if (fabsf (a) > SAFE_DIV_MIN)
+ {
+ result = a / b;
+ result = CLAMP (result, -SAFE_DIV_MAX, SAFE_DIV_MAX);
+ }
+
+ return result;
+}
+
+
+/* public functions */
+
+
+/* non-subtractive blending functions. these functions must set comp[ALPHA]
+ * to the same value as layer[ALPHA]. when in[ALPHA] or layer[ALPHA] are
+ * zero, the value of comp[RED..BLUE] is unconstrained (in particular, it may
+ * be NaN).
+ */
+
+
+void /* aka linear_dodge */
+gimp_operation_layer_mode_blend_addition (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = in[c] + layer[c];
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_burn (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = 1.0f - safe_div (1.0f - in[c], layer[c]);
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_darken_only (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = MIN (in[c], layer[c]);
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_difference (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = fabsf (in[c] - layer[c]);
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_divide (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = safe_div (in[c], layer[c]);
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_dodge (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = safe_div (in[c], 1.0f - layer[c]);
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_exclusion (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = 0.5f - 2.0f * (in[c] - 0.5f) * (layer[c] - 0.5f);
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_grain_extract (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = in[c] - layer[c] + 0.5f;
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_grain_merge (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = in[c] + layer[c] - 0.5f;
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_hard_mix (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = in[c] + layer[c] < 1.0f ? 0.0f : 1.0f;
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_hardlight (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ {
+ gfloat val;
+
+ if (layer[c] > 0.5f)
+ {
+ val = (1.0f - in[c]) * (1.0f - (layer[c] - 0.5f) * 2.0f);
+ val = MIN (1.0f - val, 1.0f);
+ }
+ else
+ {
+ val = in[c] * (layer[c] * 2.0f);
+ val = MIN (val, 1.0f);
+ }
+
+ comp[c] = val;
+ }
+ }
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_hsl_color (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gfloat dest_min, dest_max, dest_l;
+ gfloat src_min, src_max, src_l;
+
+ dest_min = MIN (in[0], in[1]);
+ dest_min = MIN (dest_min, in[2]);
+ dest_max = MAX (in[0], in[1]);
+ dest_max = MAX (dest_max, in[2]);
+ dest_l = (dest_min + dest_max) / 2.0f;
+
+ src_min = MIN (layer[0], layer[1]);
+ src_min = MIN (src_min, layer[2]);
+ src_max = MAX (layer[0], layer[1]);
+ src_max = MAX (src_max, layer[2]);
+ src_l = (src_min + src_max) / 2.0f;
+
+ if (fabs (src_l) > EPSILON && fabs (1.0 - src_l) > EPSILON)
+ {
+ gboolean dest_high;
+ gboolean src_high;
+ gfloat ratio;
+ gfloat offset;
+ gint c;
+
+ dest_high = dest_l > 0.5f;
+ src_high = src_l > 0.5f;
+
+ dest_l = MIN (dest_l, 1.0f - dest_l);
+ src_l = MIN (src_l, 1.0f - src_l);
+
+ ratio = dest_l / src_l;
+
+ offset = 0.0f;
+ if (dest_high) offset += 1.0f - 2.0f * dest_l;
+ if (src_high) offset += 2.0f * dest_l - ratio;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = layer[c] * ratio + offset;
+ }
+ else
+ {
+ comp[RED] = dest_l;
+ comp[GREEN] = dest_l;
+ comp[BLUE] = dest_l;
+ }
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_hsv_hue (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gfloat src_min, src_max, src_delta;
+ gfloat dest_min, dest_max, dest_delta, dest_s;
+
+ src_min = MIN (layer[0], layer[1]);
+ src_min = MIN (src_min, layer[2]);
+ src_max = MAX (layer[0], layer[1]);
+ src_max = MAX (src_max, layer[2]);
+ src_delta = src_max - src_min;
+
+ if (src_delta > EPSILON)
+ {
+ gfloat ratio;
+ gfloat offset;
+ gint c;
+
+ dest_min = MIN (in[0], in[1]);
+ dest_min = MIN (dest_min, in[2]);
+ dest_max = MAX (in[0], in[1]);
+ dest_max = MAX (dest_max, in[2]);
+ dest_delta = dest_max - dest_min;
+ dest_s = dest_max ? dest_delta / dest_max : 0.0f;
+
+ ratio = dest_s * dest_max / src_delta;
+ offset = dest_max - src_max * ratio;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = layer[c] * ratio + offset;
+ }
+ else
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = in[c];
+ }
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_hsv_saturation (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gfloat src_min, src_max, src_delta, src_s;
+ gfloat dest_min, dest_max, dest_delta;
+
+ dest_min = MIN (in[0], in[1]);
+ dest_min = MIN (dest_min, in[2]);
+ dest_max = MAX (in[0], in[1]);
+ dest_max = MAX (dest_max, in[2]);
+ dest_delta = dest_max - dest_min;
+
+ if (dest_delta > EPSILON)
+ {
+ gfloat ratio;
+ gfloat offset;
+ gint c;
+
+ src_min = MIN (layer[0], layer[1]);
+ src_min = MIN (src_min, layer[2]);
+ src_max = MAX (layer[0], layer[1]);
+ src_max = MAX (src_max, layer[2]);
+ src_delta = src_max - src_min;
+ src_s = src_max ? src_delta / src_max : 0.0f;
+
+ ratio = src_s * dest_max / dest_delta;
+ offset = (1.0f - ratio) * dest_max;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = in[c] * ratio + offset;
+ }
+ else
+ {
+ comp[RED] = dest_max;
+ comp[GREEN] = dest_max;
+ comp[BLUE] = dest_max;
+ }
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_hsv_value (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gfloat dest_v;
+ gfloat src_v;
+
+ dest_v = MAX (in[0], in[1]);
+ dest_v = MAX (dest_v, in[2]);
+
+ src_v = MAX (layer[0], layer[1]);
+ src_v = MAX (src_v, layer[2]);
+
+ if (fabs (dest_v) > EPSILON)
+ {
+ gfloat ratio = src_v / dest_v;
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = in[c] * ratio;
+ }
+ else
+ {
+ comp[RED] = src_v;
+ comp[GREEN] = src_v;
+ comp[BLUE] = src_v;
+ }
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_lch_chroma (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gfloat A1 = in[1];
+ gfloat B1 = in[2];
+ gfloat c1 = hypotf (A1, B1);
+
+ if (c1 > EPSILON)
+ {
+ gfloat A2 = layer[1];
+ gfloat B2 = layer[2];
+ gfloat c2 = hypotf (A2, B2);
+ gfloat A = c2 * A1 / c1;
+ gfloat B = c2 * B1 / c1;
+
+ comp[0] = in[0];
+ comp[1] = A;
+ comp[2] = B;
+ }
+ else
+ {
+ comp[0] = in[0];
+ comp[1] = in[1];
+ comp[2] = in[2];
+ }
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_lch_color (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ comp[0] = in[0];
+ comp[1] = layer[1];
+ comp[2] = layer[2];
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_lch_hue (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gfloat A2 = layer[1];
+ gfloat B2 = layer[2];
+ gfloat c2 = hypotf (A2, B2);
+
+ if (c2 > EPSILON)
+ {
+ gfloat A1 = in[1];
+ gfloat B1 = in[2];
+ gfloat c1 = hypotf (A1, B1);
+ gfloat A = c1 * A2 / c2;
+ gfloat B = c1 * B2 / c2;
+
+ comp[0] = in[0];
+ comp[1] = A;
+ comp[2] = B;
+ }
+ else
+ {
+ comp[0] = in[0];
+ comp[1] = in[1];
+ comp[2] = in[2];
+ }
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_lch_lightness (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ comp[0] = layer[0];
+ comp[1] = in[1];
+ comp[2] = in[2];
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_lighten_only (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = MAX (in[c], layer[c]);
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_linear_burn (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = in[c] + layer[c] - 1.0f;
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+/* added according to:
+ http://www.deepskycolors.com/archivo/2010/04/21/formulas-for-Photoshop-blending-modes.html */
+void
+gimp_operation_layer_mode_blend_linear_light (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ {
+ gfloat val;
+
+ if (layer[c] <= 0.5f)
+ val = in[c] + 2.0f * layer[c] - 1.0f;
+ else
+ val = in[c] + 2.0f * (layer[c] - 0.5f);
+
+ comp[c] = val;
+ }
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_luma_darken_only (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gfloat dest_luminance;
+ gfloat src_luminance;
+ gint c;
+
+ dest_luminance = GIMP_RGB_LUMINANCE (in[0], in[1], in[2]);
+ src_luminance = GIMP_RGB_LUMINANCE (layer[0], layer[1], layer[2]);
+
+ if (dest_luminance <= src_luminance)
+ {
+ for (c = 0; c < 3; c++)
+ comp[c] = in[c];
+ }
+ else
+ {
+ for (c = 0; c < 3; c++)
+ comp[c] = layer[c];
+ }
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_luma_lighten_only (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gfloat dest_luminance;
+ gfloat src_luminance;
+ gint c;
+
+ dest_luminance = GIMP_RGB_LUMINANCE (in[0], in[1], in[2]);
+ src_luminance = GIMP_RGB_LUMINANCE (layer[0], layer[1], layer[2]);
+
+ if (dest_luminance >= src_luminance)
+ {
+ for (c = 0; c < 3; c++)
+ comp[c] = in[c];
+ }
+ else
+ {
+ for (c = 0; c < 3; c++)
+ comp[c] = layer[c];
+ }
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_luminance (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ static const Babl *fish;
+ gfloat *scratch;
+ gfloat *in_Y;
+ gfloat *layer_Y;
+
+ if (! fish)
+ fish = babl_fish ("RGBA float", "Y float");
+
+ scratch = gegl_scratch_new (gfloat, 2 * samples);
+
+ in_Y = scratch;
+ layer_Y = scratch + samples;
+
+ babl_process (fish, in, in_Y, samples);
+ babl_process (fish, layer, layer_Y, samples);
+
+ while (samples--)
+ {
+ if (layer[ALPHA] != 0.0f && in[ALPHA] != 0.0f)
+ {
+ gfloat ratio = safe_div (layer_Y[0], in_Y[0]);
+ gint c;
+
+ for (c = 0; c < 3; c ++)
+ comp[c] = in[c] * ratio;
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ in += 4;
+ layer += 4;
+ in_Y ++;
+ layer_Y ++;
+ }
+
+ gegl_scratch_free (scratch);
+}
+
+void
+gimp_operation_layer_mode_blend_multiply (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = in[c] * layer[c];
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_overlay (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ {
+ gfloat val;
+
+ if (in[c] < 0.5f)
+ val = 2.0f * in[c] * layer[c];
+ else
+ val = 1.0f - 2.0f * (1.0f - layer[c]) * (1.0f - in[c]);
+
+ comp[c] = val;
+ }
+ }
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+/* added according to:
+ http://www.deepskycolors.com/archivo/2010/04/21/formulas-for-Photoshop-blending-modes.html */
+void
+gimp_operation_layer_mode_blend_pin_light (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ {
+ gfloat val;
+
+ if (layer[c] > 0.5f)
+ val = MAX(in[c], 2.0f * (layer[c] - 0.5f));
+ else
+ val = MIN(in[c], 2.0f * layer[c]);
+
+ comp[c] = val;
+ }
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_screen (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = 1.0f - (1.0f - in[c]) * (1.0f - layer[c]);
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_softlight (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ {
+ gfloat multiply = in[c] * layer[c];
+ gfloat screen = 1.0f - (1.0f - in[c]) * (1.0f - layer[c]);
+ gfloat val = (1.0f - in[c]) * multiply + in[c] * screen;
+
+ comp[c] = val;
+ }
+ }
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+void
+gimp_operation_layer_mode_blend_subtract (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = in[c] - layer[c];
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+/* added according to:
+ http://www.simplefilter.de/en/basics/mixmods.html */
+void
+gimp_operation_layer_mode_blend_vivid_light (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ {
+ gfloat val;
+
+ if (layer[c] <= 0.5f)
+ {
+ val = 1.0f - safe_div (1.0f - in[c], 2.0f * layer[c]);
+ val = MAX (val, 0.0f);
+ }
+ else
+ {
+ val = safe_div (in[c], 2.0f * (1.0f - layer[c]));
+ val = MIN (val, 1.0f);
+ }
+
+ comp[c] = val;
+ }
+ }
+
+ comp[ALPHA] = layer[ALPHA];
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
+
+
+/* subtractive blending functions. these functions must set comp[ALPHA] to
+ * the modified alpha of the overlapping content, as a fraction of the
+ * original overlapping content (i.e., an alpha of 1.0 specifies that no
+ * content is subtracted.) when in[ALPHA] or layer[ALPHA] are zero, the value
+ * of comp[RED..BLUE] is unconstrained (in particular, it may be NaN).
+ */
+
+
+void
+gimp_operation_layer_mode_blend_color_erase (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples)
+{
+ while (samples--)
+ {
+ if (in[ALPHA] != 0.0f && layer[ALPHA] != 0.0f)
+ {
+ const gfloat *color = in;
+ const gfloat *bgcolor = layer;
+ gfloat alpha;
+ gint c;
+
+ alpha = 0.0f;
+
+ for (c = 0; c < 3; c++)
+ {
+ gfloat col = CLAMP (color[c], 0.0f, 1.0f);
+ gfloat bgcol = CLAMP (bgcolor[c], 0.0f, 1.0f);
+
+ if (fabs (col - bgcol) > EPSILON)
+ {
+ gfloat a;
+
+ if (col > bgcol)
+ a = (col - bgcol) / (1.0f - bgcol);
+ else
+ a = (bgcol - col) / bgcol;
+
+ alpha = MAX (alpha, a);
+ }
+ }
+
+ if (alpha > EPSILON)
+ {
+ gfloat alpha_inv = 1.0f / alpha;
+
+ for (c = 0; c < 3; c++)
+ comp[c] = (color[c] - bgcolor[c]) * alpha_inv + bgcolor[c];
+ }
+ else
+ {
+ comp[RED] = comp[GREEN] = comp[BLUE] = 0.0f;
+ }
+
+ comp[ALPHA] = alpha;
+ }
+ else
+ comp[ALPHA] = 0.0f;
+
+ comp += 4;
+ layer += 4;
+ in += 4;
+ }
+}
diff --git a/app/operations/layer-modes/gimpoperationlayermode-blend.h b/app/operations/layer-modes/gimpoperationlayermode-blend.h
new file mode 100644
index 0000000..3a8f995
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationlayermode-blend.h
@@ -0,0 +1,200 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationlayermode-blend.h
+ * Copyright (C) 2017 Michael Natterer <mitch@gimp.org>
+ * 2017 Øyvind Kolås <pippin@gimp.org>
+ * 2017 Ell
+ *
+ * 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; withcomp 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_LAYER_MODE_BLEND_H__
+#define __GIMP_OPERATION_LAYER_MODE_BLEND_H__
+
+
+/* nonsubtractive blend functions */
+
+void gimp_operation_layer_mode_blend_addition (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_burn (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_darken_only (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_difference (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_divide (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_dodge (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_exclusion (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_grain_extract (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_grain_merge (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_hard_mix (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_hardlight (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_hsl_color (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_hsv_hue (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_hsv_saturation (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_hsv_value (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_lch_chroma (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_lch_color (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_lch_hue (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_lch_lightness (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_lighten_only (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_linear_burn (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_linear_light (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_luma_darken_only (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_luma_lighten_only (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_luminance (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_multiply (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_overlay (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_pin_light (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_screen (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_softlight (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_subtract (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+void gimp_operation_layer_mode_blend_vivid_light (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+
+
+/* subtractive blend functions */
+
+void gimp_operation_layer_mode_blend_color_erase (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *comp,
+ gint samples);
+
+
+#endif /* __GIMP_OPERATION_LAYER_MODE_BLEND_H__ */
diff --git a/app/operations/layer-modes/gimpoperationlayermode-composite-sse2.c b/app/operations/layer-modes/gimpoperationlayermode-composite-sse2.c
new file mode 100644
index 0000000..2630751
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationlayermode-composite-sse2.c
@@ -0,0 +1,105 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationlayermode-composite-sse2.c
+ * Copyright (C) 2017 Michael Natterer <mitch@gimp.org>
+ * 2017 Øyvind Kolås <pippin@gimp.org>
+ * 2017 Ell
+ *
+ * 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-plugin.h>
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationlayermode-composite.h"
+
+
+#if COMPILE_SSE2_INTRINISICS
+
+/* SSE2 */
+#include <emmintrin.h>
+
+
+/* non-subtractive compositing functions. these functions expect comp[ALPHA]
+ * to be the same as layer[ALPHA]. when in[ALPHA] or layer[ALPHA] are zero,
+ * the value of comp[RED..BLUE] is unconstrained (in particular, it may be
+ * NaN).
+ */
+
+
+void
+gimp_operation_layer_mode_composite_clip_to_backdrop_sse2 (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples)
+{
+ if ((((uintptr_t)in) | /* alignment check */
+ ((uintptr_t)comp) |
+ ((uintptr_t)out) ) & 0x0F)
+ {
+ gimp_operation_layer_mode_composite_clip_to_backdrop (in, layer, comp,
+ mask, opacity, out,
+ samples);
+ }
+ else
+ {
+ const __v4sf *v_in = (const __v4sf*) in;
+ const __v4sf *v_comp = (const __v4sf*) comp;
+ __v4sf *v_out = (__v4sf*) out;
+ const __v4sf v_one = _mm_set1_ps (1.0f);
+ const __v4sf v_opacity = _mm_set1_ps (opacity);
+
+ while (samples--)
+ {
+ __v4sf alpha, rgba_in, rgba_comp;
+
+ rgba_in = *v_in ++;
+ rgba_comp = *v_comp++;
+
+ alpha = (__v4sf)_mm_shuffle_epi32((__m128i)rgba_comp,_MM_SHUFFLE(3,3,3,3)) * v_opacity;
+
+ if (mask)
+ {
+ alpha = alpha * _mm_set1_ps (*mask++);
+ }
+
+ if (rgba_in[ALPHA] != 0.0f && _mm_ucomineq_ss (alpha, _mm_setzero_ps ()))
+ {
+ __v4sf out_pixel, out_pixel_rbaa, out_alpha;
+
+ out_alpha = (__v4sf)_mm_shuffle_epi32((__m128i)rgba_in,_MM_SHUFFLE(3,3,3,3));
+ out_pixel = rgba_comp * alpha + rgba_in * (v_one - alpha);
+ out_pixel_rbaa = _mm_shuffle_ps (out_pixel, out_alpha, _MM_SHUFFLE (3, 3, 2, 0));
+ out_pixel = _mm_shuffle_ps (out_pixel, out_pixel_rbaa, _MM_SHUFFLE (2, 1, 1, 0));
+
+ *v_out++ = out_pixel;
+ }
+ else
+ {
+ *v_out ++ = rgba_in;
+ }
+ }
+ }
+}
+
+#endif /* COMPILE_SSE2_INTRINISICS */
diff --git a/app/operations/layer-modes/gimpoperationlayermode-composite.c b/app/operations/layer-modes/gimpoperationlayermode-composite.c
new file mode 100644
index 0000000..4275818
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationlayermode-composite.c
@@ -0,0 +1,434 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationlayermode-composite.c
+ * Copyright (C) 2017 Michael Natterer <mitch@gimp.org>
+ * 2017 Øyvind Kolås <pippin@gimp.org>
+ * 2017 Ell
+ *
+ * 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-plugin.h>
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationlayermode-composite.h"
+
+
+/* non-subtractive compositing functions. these functions expect comp[ALPHA]
+ * to be the same as layer[ALPHA]. when in[ALPHA] or layer[ALPHA] are zero,
+ * the value of comp[RED..BLUE] is unconstrained (in particular, it may be
+ * NaN).
+ */
+
+
+void
+gimp_operation_layer_mode_composite_union (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples)
+{
+ while (samples--)
+ {
+ gfloat new_alpha;
+ gfloat in_alpha = in[ALPHA];
+ gfloat layer_alpha = layer[ALPHA] * opacity;
+
+ if (mask)
+ layer_alpha *= *mask;
+
+ new_alpha = layer_alpha + (1.0f - layer_alpha) * in_alpha;
+
+ if (layer_alpha == 0.0f || new_alpha == 0.0f)
+ {
+ out[RED] = in[RED];
+ out[GREEN] = in[GREEN];
+ out[BLUE] = in[BLUE];
+ }
+ else if (in_alpha == 0.0f)
+ {
+ out[RED] = layer[RED];
+ out[GREEN] = layer[GREEN];
+ out[BLUE] = layer[BLUE];
+ }
+ else
+ {
+ gfloat ratio = layer_alpha / new_alpha;
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ out[b] = ratio * (in_alpha * (comp[b] - layer[b]) + layer[b] - in[b]) + in[b];
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ comp += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+}
+
+void
+gimp_operation_layer_mode_composite_clip_to_backdrop (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples)
+{
+ while (samples--)
+ {
+ gfloat layer_alpha = comp[ALPHA] * opacity;
+
+ if (mask)
+ layer_alpha *= *mask;
+
+ if (in[ALPHA] == 0.0f || layer_alpha == 0.0f)
+ {
+ out[RED] = in[RED];
+ out[GREEN] = in[GREEN];
+ out[BLUE] = in[BLUE];
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ out[b] = comp[b] * layer_alpha + in[b] * (1.0f - layer_alpha);
+ }
+
+ out[ALPHA] = in[ALPHA];
+
+ in += 4;
+ comp += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+}
+
+void
+gimp_operation_layer_mode_composite_clip_to_layer (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples)
+{
+ while (samples--)
+ {
+ gfloat layer_alpha = layer[ALPHA] * opacity;
+
+ if (mask)
+ layer_alpha *= *mask;
+
+ if (layer_alpha == 0.0f)
+ {
+ out[RED] = in[RED];
+ out[GREEN] = in[GREEN];
+ out[BLUE] = in[BLUE];
+ }
+ else if (in[ALPHA] == 0.0f)
+ {
+ out[RED] = layer[RED];
+ out[GREEN] = layer[GREEN];
+ out[BLUE] = layer[BLUE];
+ }
+ else
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ out[b] = comp[b] * in[ALPHA] + layer[b] * (1.0f - in[ALPHA]);
+ }
+
+ out[ALPHA] = layer_alpha;
+
+ in += 4;
+ layer += 4;
+ comp += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+}
+
+void
+gimp_operation_layer_mode_composite_intersection (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples)
+{
+ while (samples--)
+ {
+ gfloat new_alpha = in[ALPHA] * comp[ALPHA] * opacity;
+
+ if (mask)
+ new_alpha *= *mask;
+
+ if (new_alpha == 0.0f)
+ {
+ out[RED] = in[RED];
+ out[GREEN] = in[GREEN];
+ out[BLUE] = in[BLUE];
+ }
+ else
+ {
+ out[RED] = comp[RED];
+ out[GREEN] = comp[GREEN];
+ out[BLUE] = comp[BLUE];
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ comp += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+}
+
+/* subtractive compositing functions. these functions expect comp[ALPHA] to
+ * specify the modified alpha of the overlapping content, as a fraction of the
+ * original overlapping content (i.e., an alpha of 1.0 specifies that no
+ * content is subtracted.) when in[ALPHA] or layer[ALPHA] are zero, the value
+ * of comp[RED..BLUE] is unconstrained (in particular, it may be NaN).
+ */
+
+void
+gimp_operation_layer_mode_composite_union_sub (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples)
+{
+ while (samples--)
+ {
+ gfloat in_alpha = in[ALPHA];
+ gfloat layer_alpha = layer[ALPHA] * opacity;
+ gfloat comp_alpha = comp[ALPHA];
+ gfloat new_alpha;
+
+ if (mask)
+ layer_alpha *= *mask;
+
+ new_alpha = in_alpha + layer_alpha -
+ (2.0f - comp_alpha) * in_alpha * layer_alpha;
+
+ if (layer_alpha == 0.0f || new_alpha == 0.0f)
+ {
+ out[RED] = in[RED];
+ out[GREEN] = in[GREEN];
+ out[BLUE] = in[BLUE];
+ }
+ else if (in_alpha == 0.0f)
+ {
+ out[RED] = layer[RED];
+ out[GREEN] = layer[GREEN];
+ out[BLUE] = layer[BLUE];
+ }
+ else
+ {
+ gfloat ratio = in_alpha / new_alpha;
+ gfloat layer_coeff = 1.0f / in_alpha - 1.0f;
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ out[b] = ratio * (layer_alpha * (comp_alpha * comp[b] + layer_coeff * layer[b] - in[b]) + in[b]);
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ comp += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+}
+
+void
+gimp_operation_layer_mode_composite_clip_to_backdrop_sub (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples)
+{
+ while (samples--)
+ {
+ gfloat layer_alpha = layer[ALPHA] * opacity;
+ gfloat comp_alpha = comp[ALPHA];
+ gfloat new_alpha;
+
+ if (mask)
+ layer_alpha *= *mask;
+
+ comp_alpha *= layer_alpha;
+
+ new_alpha = 1.0f - layer_alpha + comp_alpha;
+
+ if (in[ALPHA] == 0.0f || comp_alpha == 0.0f)
+ {
+ out[RED] = in[RED];
+ out[GREEN] = in[GREEN];
+ out[BLUE] = in[BLUE];
+ }
+ else
+ {
+ gfloat ratio = comp_alpha / new_alpha;
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ out[b] = comp[b] * ratio + in[b] * (1.0f - ratio);
+ }
+
+ new_alpha *= in[ALPHA];
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ comp += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+}
+
+void
+gimp_operation_layer_mode_composite_clip_to_layer_sub (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples)
+{
+ while (samples--)
+ {
+ gfloat in_alpha = in[ALPHA];
+ gfloat layer_alpha = layer[ALPHA] * opacity;
+ gfloat comp_alpha = comp[ALPHA];
+ gfloat new_alpha;
+
+ if (mask)
+ layer_alpha *= *mask;
+
+ comp_alpha *= in_alpha;
+
+ new_alpha = 1.0f - in_alpha + comp_alpha;
+
+ if (layer_alpha == 0.0f)
+ {
+ out[RED] = in[RED];
+ out[GREEN] = in[GREEN];
+ out[BLUE] = in[BLUE];
+ }
+ else if (in_alpha == 0.0f)
+ {
+ out[RED] = layer[RED];
+ out[GREEN] = layer[GREEN];
+ out[BLUE] = layer[BLUE];
+ }
+ else
+ {
+ gfloat ratio = comp_alpha / new_alpha;
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ out[b] = comp[b] * ratio + layer[b] * (1.0f - ratio);
+ }
+
+ new_alpha *= layer_alpha;
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ comp += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+}
+
+void
+gimp_operation_layer_mode_composite_intersection_sub (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples)
+{
+ while (samples--)
+ {
+ gfloat new_alpha = in[ALPHA] * layer[ALPHA] * comp[ALPHA] * opacity;
+
+ if (mask)
+ new_alpha *= *mask;
+
+ if (new_alpha == 0.0f)
+ {
+ out[RED] = in[RED];
+ out[GREEN] = in[GREEN];
+ out[BLUE] = in[BLUE];
+ }
+ else
+ {
+ out[RED] = comp[RED];
+ out[GREEN] = comp[GREEN];
+ out[BLUE] = comp[BLUE];
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ comp += 4;
+ out += 4;
+
+ if (mask)
+ mask++;
+ }
+}
diff --git a/app/operations/layer-modes/gimpoperationlayermode-composite.h b/app/operations/layer-modes/gimpoperationlayermode-composite.h
new file mode 100644
index 0000000..f9ec2a5
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationlayermode-composite.h
@@ -0,0 +1,98 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationlayermode-composite.h
+ * Copyright (C) 2017 Michael Natterer <mitch@gimp.org>
+ * 2017 Øyvind Kolås <pippin@gimp.org>
+ * 2017 Ell
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_LAYER_MODE_COMPOSITE_H__
+#define __GIMP_OPERATION_LAYER_MODE_COMPOSITE_H__
+
+
+void gimp_operation_layer_mode_composite_union (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples);
+void gimp_operation_layer_mode_composite_clip_to_backdrop (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples);
+void gimp_operation_layer_mode_composite_clip_to_layer (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples);
+void gimp_operation_layer_mode_composite_intersection (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples);
+
+void gimp_operation_layer_mode_composite_union_sub (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples);
+void gimp_operation_layer_mode_composite_clip_to_backdrop_sub (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples);
+void gimp_operation_layer_mode_composite_clip_to_layer_sub (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples);
+void gimp_operation_layer_mode_composite_intersection_sub (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples);
+
+#if COMPILE_SSE2_INTRINISICS
+
+void gimp_operation_layer_mode_composite_clip_to_backdrop_sse2 (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ gfloat opacity,
+ gfloat *out,
+ gint samples);
+
+#endif /* COMPILE_SSE2_INTRINISICS */
+
+
+#endif /* __GIMP_OPERATION_LAYER_MODE_COMPOSITE_H__ */
diff --git a/app/operations/layer-modes/gimpoperationlayermode.c b/app/operations/layer-modes/gimpoperationlayermode.c
new file mode 100644
index 0000000..db74ed5
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationlayermode.c
@@ -0,0 +1,914 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationlayermode.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * Copyright (C) 2008 Martin Nordholts <martinn@svn.gnome.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-plugin.h>
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "../operations-types.h"
+
+#include "gimp-layer-modes.h"
+#include "gimpoperationlayermode.h"
+#include "gimpoperationlayermode-composite.h"
+
+
+/* the maximum number of samples to process in one go. used to limit
+ * the size of the buffers we allocate on the stack.
+ */
+#define GIMP_COMPOSITE_BLEND_MAX_SAMPLES ((1 << 18) /* 256 KiB */ / \
+ 16 /* bytes per pixel */ / \
+ 2 /* max number of buffers */)
+
+/* number of consecutive unblended samples (whose source or destination alpha
+ * is zero) above which to split the blending process, in order to avoid
+ * performing too many unnecessary conversions.
+ */
+#define GIMP_COMPOSITE_BLEND_SPLIT_THRESHOLD 32
+
+
+enum
+{
+ PROP_0,
+ PROP_LAYER_MODE,
+ PROP_OPACITY,
+ PROP_BLEND_SPACE,
+ PROP_COMPOSITE_SPACE,
+ PROP_COMPOSITE_MODE
+};
+
+
+typedef void (* CompositeFunc) (const gfloat *in,
+ const gfloat *layer,
+ const gfloat *comp,
+ const gfloat *mask,
+ float opacity,
+ gfloat *out,
+ gint samples);
+
+
+static void gimp_operation_layer_mode_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_operation_layer_mode_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_operation_layer_mode_prepare (GeglOperation *operation);
+static GeglRectangle gimp_operation_layer_mode_get_bounding_box (GeglOperation *operation);
+static gboolean gimp_operation_layer_mode_parent_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_prop,
+ const GeglRectangle *result,
+ gint level);
+
+static gboolean gimp_operation_layer_mode_process (GeglOperation *operation,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+static gboolean gimp_operation_layer_mode_real_parent_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_prop,
+ const GeglRectangle *result,
+ gint level);
+static gboolean gimp_operation_layer_mode_real_process (GeglOperation *operation,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+static gboolean process_last_node (GeglOperation *operation,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationLayerMode, gimp_operation_layer_mode,
+ GEGL_TYPE_OPERATION_POINT_COMPOSER3)
+
+#define parent_class gimp_operation_layer_mode_parent_class
+
+
+static const Babl *gimp_layer_color_space_fish[3 /* from */][3 /* to */];
+
+static CompositeFunc composite_union = gimp_operation_layer_mode_composite_union;
+static CompositeFunc composite_clip_to_backdrop = gimp_operation_layer_mode_composite_clip_to_backdrop;
+static CompositeFunc composite_clip_to_layer = gimp_operation_layer_mode_composite_clip_to_layer;
+static CompositeFunc composite_intersection = gimp_operation_layer_mode_composite_intersection;
+
+static CompositeFunc composite_union_sub = gimp_operation_layer_mode_composite_union_sub;
+static CompositeFunc composite_clip_to_backdrop_sub = gimp_operation_layer_mode_composite_clip_to_backdrop_sub;
+static CompositeFunc composite_clip_to_layer_sub = gimp_operation_layer_mode_composite_clip_to_layer_sub;
+static CompositeFunc composite_intersection_sub = gimp_operation_layer_mode_composite_intersection_sub;
+
+
+static void
+gimp_operation_layer_mode_class_init (GimpOperationLayerModeClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationPointComposer3Class *point_composer3_class = GEGL_OPERATION_POINT_COMPOSER3_CLASS (klass);
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gimp:layer-mode", NULL);
+
+ object_class->set_property = gimp_operation_layer_mode_set_property;
+ object_class->get_property = gimp_operation_layer_mode_get_property;
+
+ operation_class->prepare = gimp_operation_layer_mode_prepare;
+ operation_class->get_bounding_box = gimp_operation_layer_mode_get_bounding_box;
+ operation_class->process = gimp_operation_layer_mode_parent_process;
+
+ point_composer3_class->process = gimp_operation_layer_mode_process;
+
+ klass->parent_process = gimp_operation_layer_mode_real_parent_process;
+ klass->process = gimp_operation_layer_mode_real_process;
+ klass->get_affected_region = NULL;
+
+ g_object_class_install_property (object_class, PROP_LAYER_MODE,
+ g_param_spec_enum ("layer-mode",
+ NULL, NULL,
+ GIMP_TYPE_LAYER_MODE,
+ GIMP_LAYER_MODE_NORMAL,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_OPACITY,
+ g_param_spec_double ("opacity",
+ NULL, NULL,
+ 0.0, 1.0, 1.0,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_BLEND_SPACE,
+ g_param_spec_enum ("blend-space",
+ NULL, NULL,
+ GIMP_TYPE_LAYER_COLOR_SPACE,
+ GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+
+ g_object_class_install_property (object_class, PROP_COMPOSITE_SPACE,
+ g_param_spec_enum ("composite-space",
+ NULL, NULL,
+ GIMP_TYPE_LAYER_COLOR_SPACE,
+ GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_COMPOSITE_MODE,
+ g_param_spec_enum ("composite-mode",
+ NULL, NULL,
+ GIMP_TYPE_LAYER_COMPOSITE_MODE,
+ GIMP_LAYER_COMPOSITE_UNION,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ gimp_layer_color_space_fish
+ /* from */ [GIMP_LAYER_COLOR_SPACE_RGB_LINEAR - 1]
+ /* to */ [GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL - 1] =
+ babl_fish ("RGBA float", "R'G'B'A float");
+ gimp_layer_color_space_fish
+ /* from */ [GIMP_LAYER_COLOR_SPACE_RGB_LINEAR - 1]
+ /* to */ [GIMP_LAYER_COLOR_SPACE_LAB - 1] =
+ babl_fish ("RGBA float", "CIE Lab alpha float");
+
+ gimp_layer_color_space_fish
+ /* from */ [GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL - 1]
+ /* to */ [GIMP_LAYER_COLOR_SPACE_RGB_LINEAR - 1] =
+ babl_fish ("R'G'B'A float", "RGBA float");
+ gimp_layer_color_space_fish
+ /* from */ [GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL - 1]
+ /* to */ [GIMP_LAYER_COLOR_SPACE_LAB - 1] =
+ babl_fish ("R'G'B'A float", "CIE Lab alpha float");
+
+ gimp_layer_color_space_fish
+ /* from */ [GIMP_LAYER_COLOR_SPACE_LAB - 1]
+ /* to */ [GIMP_LAYER_COLOR_SPACE_RGB_LINEAR - 1] =
+ babl_fish ("CIE Lab alpha float", "RGBA float");
+ gimp_layer_color_space_fish
+ /* from */ [GIMP_LAYER_COLOR_SPACE_LAB - 1]
+ /* to */ [GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL - 1] =
+ babl_fish ("CIE Lab alpha float", "R'G'B'A float");
+
+#if COMPILE_SSE2_INTRINISICS
+ if (gimp_cpu_accel_get_support () & GIMP_CPU_ACCEL_X86_SSE2)
+ composite_clip_to_backdrop = gimp_operation_layer_mode_composite_clip_to_backdrop_sse2;
+#endif
+}
+
+static void
+gimp_operation_layer_mode_init (GimpOperationLayerMode *self)
+{
+}
+
+static void
+gimp_operation_layer_mode_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationLayerMode *self = GIMP_OPERATION_LAYER_MODE (object);
+
+ switch (property_id)
+ {
+ case PROP_LAYER_MODE:
+ self->layer_mode = g_value_get_enum (value);
+ break;
+
+ case PROP_OPACITY:
+ self->prop_opacity = g_value_get_double (value);
+ break;
+
+ case PROP_BLEND_SPACE:
+ self->blend_space = g_value_get_enum (value);
+ break;
+
+ case PROP_COMPOSITE_SPACE:
+ self->composite_space = g_value_get_enum (value);
+ break;
+
+ case PROP_COMPOSITE_MODE:
+ self->prop_composite_mode = g_value_get_enum (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_layer_mode_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpOperationLayerMode *self = GIMP_OPERATION_LAYER_MODE (object);
+
+ switch (property_id)
+ {
+ case PROP_LAYER_MODE:
+ g_value_set_enum (value, self->layer_mode);
+ break;
+
+ case PROP_OPACITY:
+ g_value_set_double (value, self->prop_opacity);
+ break;
+
+ case PROP_BLEND_SPACE:
+ g_value_set_enum (value, self->blend_space);
+ break;
+
+ case PROP_COMPOSITE_SPACE:
+ g_value_set_enum (value, self->composite_space);
+ break;
+
+ case PROP_COMPOSITE_MODE:
+ g_value_set_enum (value, self->prop_composite_mode);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_operation_layer_mode_prepare (GeglOperation *operation)
+{
+ GimpOperationLayerMode *self = GIMP_OPERATION_LAYER_MODE (operation);
+ const GeglRectangle *input_extent;
+ const GeglRectangle *mask_extent;
+ const Babl *preferred_format;
+ const Babl *format;
+
+ self->composite_mode = self->prop_composite_mode;
+
+ if (self->composite_mode == GIMP_LAYER_COMPOSITE_AUTO)
+ {
+ self->composite_mode =
+ gimp_layer_mode_get_composite_mode (self->layer_mode);
+
+ g_warn_if_fail (self->composite_mode != GIMP_LAYER_COMPOSITE_AUTO);
+ }
+
+ self->function = gimp_layer_mode_get_function (self->layer_mode);
+ self->blend_function = gimp_layer_mode_get_blend_function (self->layer_mode);
+
+ input_extent = gegl_operation_source_get_bounding_box (operation, "input");
+ mask_extent = gegl_operation_source_get_bounding_box (operation, "aux2");
+
+ /* if the input pad has data, work as usual. */
+ if (input_extent && ! gegl_rectangle_is_empty (input_extent))
+ {
+ self->is_last_node = FALSE;
+
+ preferred_format = gegl_operation_get_source_format (operation, "input");
+ }
+ /* otherwise, we're the last node (corresponding to the bottom layer).
+ * in this case, we render the layer (as if) using UNION mode.
+ */
+ else
+ {
+ self->is_last_node = TRUE;
+
+ /* if the layer mode doesn't affect the source, use a shortcut
+ * function that only applies the opacity/mask to the layer.
+ */
+ if (! (gimp_operation_layer_mode_get_affected_region (self) &
+ GIMP_LAYER_COMPOSITE_REGION_SOURCE))
+ {
+ self->function = process_last_node;
+ }
+ /* otherwise, use the original process function, but force the
+ * composite mode to UNION.
+ */
+ else
+ {
+ self->composite_mode = GIMP_LAYER_COMPOSITE_UNION;
+ }
+
+ preferred_format = gegl_operation_get_source_format (operation, "aux");
+ }
+
+ self->has_mask = mask_extent && ! gegl_rectangle_is_empty (mask_extent);
+
+ format = gimp_layer_mode_get_format (self->layer_mode,
+ self->blend_space,
+ self->composite_space,
+ self->composite_mode,
+ preferred_format);
+
+ gegl_operation_set_format (operation, "input", format);
+ gegl_operation_set_format (operation, "output", format);
+ gegl_operation_set_format (operation, "aux", format);
+ gegl_operation_set_format (operation, "aux2", babl_format ("Y float"));
+}
+
+static GeglRectangle
+gimp_operation_layer_mode_get_bounding_box (GeglOperation *op)
+{
+ GimpOperationLayerMode *self = (gpointer) op;
+ GeglRectangle *in_rect;
+ GeglRectangle *aux_rect;
+ GeglRectangle *aux2_rect;
+ GeglRectangle src_rect = {};
+ GeglRectangle dst_rect = {};
+ GeglRectangle result;
+ GimpLayerCompositeRegion included_region;
+
+ in_rect = gegl_operation_source_get_bounding_box (op, "input");
+ aux_rect = gegl_operation_source_get_bounding_box (op, "aux");
+ aux2_rect = gegl_operation_source_get_bounding_box (op, "aux2");
+
+ if (in_rect)
+ dst_rect = *in_rect;
+
+ if (aux_rect)
+ {
+ src_rect = *aux_rect;
+
+ if (aux2_rect)
+ gegl_rectangle_intersect (&src_rect, &src_rect, aux2_rect);
+ }
+
+ if (self->is_last_node)
+ {
+ included_region = GIMP_LAYER_COMPOSITE_REGION_SOURCE;
+ }
+ else
+ {
+ included_region = gimp_layer_mode_get_included_region (self->layer_mode,
+ self->composite_mode);
+ }
+
+ if (self->prop_opacity == 0.0)
+ included_region &= ~GIMP_LAYER_COMPOSITE_REGION_SOURCE;
+
+ gegl_rectangle_intersect (&result, &src_rect, &dst_rect);
+
+ if (included_region & GIMP_LAYER_COMPOSITE_REGION_SOURCE)
+ gegl_rectangle_bounding_box (&result, &result, &src_rect);
+
+ if (included_region & GIMP_LAYER_COMPOSITE_REGION_DESTINATION)
+ gegl_rectangle_bounding_box (&result, &result, &dst_rect);
+
+ return result;
+}
+
+static gboolean
+gimp_operation_layer_mode_parent_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_prop,
+ const GeglRectangle *result,
+ gint level)
+{
+ GimpOperationLayerMode *point = GIMP_OPERATION_LAYER_MODE (operation);
+
+ point->opacity = point->prop_opacity;
+
+ /* if we have a mask, but it's not included in the output, pretend the
+ * opacity is 0, so that we don't composite 'aux' over 'input' as if there
+ * was no mask.
+ */
+ if (point->has_mask)
+ {
+ GObject *mask;
+ gboolean has_mask;
+
+ /* get the raw value. this does not increase the reference count. */
+ mask = gegl_operation_context_get_object (context, "aux2");
+
+ /* disregard 'mask' if it's not included in the roi. */
+ has_mask =
+ mask &&
+ gegl_rectangle_intersect (NULL,
+ gegl_buffer_get_extent (GEGL_BUFFER (mask)),
+ result);
+
+ if (! has_mask)
+ point->opacity = 0.0;
+ }
+
+ return GIMP_OPERATION_LAYER_MODE_GET_CLASS (point)->parent_process (
+ operation, context, output_prop, result, level);
+}
+
+static gboolean
+gimp_operation_layer_mode_process (GeglOperation *operation,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ return ((GimpOperationLayerMode *) operation)->function (
+ operation, in, layer, mask, out, samples, roi, level);
+}
+
+static gboolean
+gimp_operation_layer_mode_real_parent_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_prop,
+ const GeglRectangle *result,
+ gint level)
+{
+ GimpOperationLayerMode *point = GIMP_OPERATION_LAYER_MODE (operation);
+ GObject *input;
+ GObject *aux;
+ gboolean has_input;
+ gboolean has_aux;
+ GimpLayerCompositeRegion included_region;
+
+ /* get the raw values. this does not increase the reference count. */
+ input = gegl_operation_context_get_object (context, "input");
+ aux = gegl_operation_context_get_object (context, "aux");
+
+ /* disregard 'input' if it's not included in the roi. */
+ has_input =
+ input &&
+ gegl_rectangle_intersect (NULL,
+ gegl_buffer_get_extent (GEGL_BUFFER (input)),
+ result);
+
+ /* disregard 'aux' if it's not included in the roi, or if it's fully
+ * transparent.
+ */
+ has_aux =
+ aux &&
+ point->opacity != 0.0 &&
+ gegl_rectangle_intersect (NULL,
+ gegl_buffer_get_extent (GEGL_BUFFER (aux)),
+ result);
+
+ if (point->is_last_node)
+ {
+ included_region = GIMP_LAYER_COMPOSITE_REGION_SOURCE;
+ }
+ else
+ {
+ included_region = gimp_layer_mode_get_included_region (point->layer_mode,
+ point->composite_mode);
+ }
+
+ /* if there's no 'input' ... */
+ if (! has_input)
+ {
+ /* ... and there's 'aux', and the composite mode includes it (or we're
+ * the last node) ...
+ */
+ if (has_aux && (included_region & GIMP_LAYER_COMPOSITE_REGION_SOURCE))
+ {
+ GimpLayerCompositeRegion affected_region;
+
+ affected_region =
+ gimp_operation_layer_mode_get_affected_region (point);
+
+ /* ... and the op doesn't otherwise affect 'aux', or changes its
+ * alpha ...
+ */
+ if (! (affected_region & GIMP_LAYER_COMPOSITE_REGION_SOURCE) &&
+ point->opacity == 1.0 &&
+ ! gegl_operation_context_get_object (context, "aux2"))
+ {
+ /* pass 'aux' directly as output; */
+ gegl_operation_context_set_object (context, "output", aux);
+ return TRUE;
+ }
+
+ /* otherwise, if the op affects 'aux', or changes its alpha, process
+ * it even though there's no 'input';
+ */
+ }
+ /* otherwise, there's no 'aux', or the composite mode doesn't include it,
+ * and so ...
+ */
+ else
+ {
+ /* ... the output is empty. */
+ gegl_operation_context_set_object (context, "output", NULL);
+ return TRUE;
+ }
+ }
+ /* otherwise, if there's 'input' but no 'aux' ... */
+ else if (! has_aux)
+ {
+ /* ... and the composite mode includes 'input' ... */
+ if (included_region & GIMP_LAYER_COMPOSITE_REGION_DESTINATION)
+ {
+ GimpLayerCompositeRegion affected_region;
+
+ affected_region =
+ gimp_operation_layer_mode_get_affected_region (point);
+
+ /* ... and the op doesn't otherwise affect 'input' ... */
+ if (! (affected_region & GIMP_LAYER_COMPOSITE_REGION_DESTINATION))
+ {
+ /* pass 'input' directly as output; */
+ gegl_operation_context_set_object (context, "output", input);
+ return TRUE;
+ }
+
+ /* otherwise, if the op affects 'input', process it even though
+ * there's no 'aux';
+ */
+ }
+
+ /* otherwise, the output is fully transparent, but we process it anyway
+ * to maintain the 'input' color values.
+ */
+ }
+
+ /* FIXME: we don't actually handle the case where one of the inputs
+ * is NULL -- it'll just segfault. 'input' is not expected to be NULL,
+ * but 'aux' might be, currently.
+ */
+ if (! input || ! aux)
+ {
+ GObject *empty = G_OBJECT (gegl_buffer_new (NULL, NULL));
+
+ if (! input) gegl_operation_context_set_object (context, "input", empty);
+ if (! aux) gegl_operation_context_set_object (context, "aux", empty);
+
+ if (! input && ! aux)
+ gegl_object_set_has_forked (G_OBJECT (empty));
+
+ g_object_unref (empty);
+ }
+
+ /* chain up, which will create the needed buffers for our actual
+ * process function
+ */
+ return GEGL_OPERATION_CLASS (parent_class)->process (operation, context,
+ output_prop, result,
+ level);
+}
+
+static gboolean
+gimp_operation_layer_mode_real_process (GeglOperation *operation,
+ void *in_p,
+ void *layer_p,
+ void *mask_p,
+ void *out_p,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GimpOperationLayerMode *layer_mode = (gpointer) operation;
+ gfloat *in = in_p;
+ gfloat *out = out_p;
+ gfloat *layer = layer_p;
+ gfloat *mask = mask_p;
+ gfloat opacity = layer_mode->opacity;
+ GimpLayerColorSpace blend_space = layer_mode->blend_space;
+ GimpLayerColorSpace composite_space = layer_mode->composite_space;
+ GimpLayerCompositeMode composite_mode = layer_mode->composite_mode;
+ GimpLayerModeBlendFunc blend_function = layer_mode->blend_function;
+ gboolean composite_needs_in_color;
+ gfloat *blend_in;
+ gfloat *blend_layer;
+ gfloat *blend_out;
+ const Babl *composite_to_blend_fish = NULL;
+ const Babl *blend_to_composite_fish = NULL;
+
+ /* make sure we don't process more than GIMP_COMPOSITE_BLEND_MAX_SAMPLES
+ * at a time, so that we don't overflow the stack if we allocate buffers
+ * on it. note that this has to be done with a nested function call,
+ * because alloca'd buffers remain for the duration of the stack frame.
+ */
+ while (samples > GIMP_COMPOSITE_BLEND_MAX_SAMPLES)
+ {
+ gimp_operation_layer_mode_real_process (operation,
+ in, layer, mask, out,
+ GIMP_COMPOSITE_BLEND_MAX_SAMPLES,
+ roi, level);
+
+ in += 4 * GIMP_COMPOSITE_BLEND_MAX_SAMPLES;
+ layer += 4 * GIMP_COMPOSITE_BLEND_MAX_SAMPLES;
+ if (mask)
+ mask += GIMP_COMPOSITE_BLEND_MAX_SAMPLES;
+ out += 4 * GIMP_COMPOSITE_BLEND_MAX_SAMPLES;
+
+ samples -= GIMP_COMPOSITE_BLEND_MAX_SAMPLES;
+ }
+
+ composite_needs_in_color =
+ composite_mode == GIMP_LAYER_COMPOSITE_UNION ||
+ composite_mode == GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP;
+
+ blend_in = in;
+ blend_layer = layer;
+ blend_out = out;
+
+ if (blend_space != GIMP_LAYER_COLOR_SPACE_AUTO)
+ {
+ gimp_assert (composite_space >= 1 && composite_space < 4);
+ gimp_assert (blend_space >= 1 && blend_space < 4);
+
+ composite_to_blend_fish = gimp_layer_color_space_fish [composite_space - 1]
+ [blend_space - 1];
+
+ blend_to_composite_fish = gimp_layer_color_space_fish [blend_space - 1]
+ [composite_space - 1];
+ }
+
+ /* if we need to convert the samples between the composite and blend
+ * spaces...
+ */
+ if (composite_to_blend_fish)
+ {
+ gint i;
+ gint end;
+
+ if (in != out || composite_needs_in_color)
+ {
+ /* don't convert input in-place if we're not doing in-place output,
+ * or if we're going to need the original input for compositing.
+ */
+ blend_in = g_alloca (sizeof (gfloat) * 4 * samples);
+ }
+ blend_layer = g_alloca (sizeof (gfloat) * 4 * samples);
+
+ if (in == out) /* in-place detected, avoid clobbering since we need to
+ read 'in' for the compositing stage */
+ {
+ if (blend_layer != layer)
+ blend_out = blend_layer;
+ else
+ blend_out = g_alloca (sizeof (gfloat) * 4 * samples);
+ }
+
+ /* samples whose the source or destination alpha is zero are not blended,
+ * and therefore do not need to be converted. while it's generally
+ * desirable to perform conversion and blending in bulk, when we have
+ * more than a certain number of consecutive unblended samples, the cost
+ * of converting them outweighs the cost of splitting the process around
+ * them to avoid the conversion.
+ */
+
+ i = ALPHA;
+ end = 4 * samples + ALPHA;
+
+ while (TRUE)
+ {
+ gint first;
+ gint last;
+ gint count;
+
+ /* skip any unblended samples. the color values of `blend_out` for
+ * these samples are unconstrained, in particular, they may be NaN,
+ * but the alpha values should generally be finite, and specifically
+ * 0 when the source alpha is 0.
+ */
+ while (i < end && (in[i] == 0.0f || layer[i] == 0.0f))
+ {
+ blend_out[i] = 0.0f;
+ i += 4;
+ }
+
+ /* stop if there are no more samples */
+ if (i == end)
+ break;
+
+ /* otherwise, keep scanning the samples until we find
+ * GIMP_COMPOSITE_BLEND_SPLIT_THRESHOLD consecutive unblended
+ * samples.
+ */
+
+ first = i;
+ i += 4;
+ last = i;
+
+ while (i < end && i - last < 4 * GIMP_COMPOSITE_BLEND_SPLIT_THRESHOLD)
+ {
+ gboolean blended;
+
+ blended = (in[i] != 0.0f && layer[i] != 0.0f);
+
+ i += 4;
+ if (blended)
+ last = i;
+ }
+
+ /* convert and blend the samples in the range [first, last) */
+
+ count = (last - first) / 4;
+ first -= ALPHA;
+
+ babl_process (composite_to_blend_fish,
+ in + first, blend_in + first, count);
+ babl_process (composite_to_blend_fish,
+ layer + first, blend_layer + first, count);
+
+ blend_function (operation, blend_in + first, blend_layer + first,
+ blend_out + first, count);
+
+ babl_process (blend_to_composite_fish,
+ blend_out + first, blend_out + first, count);
+
+ /* make sure the alpha values of `blend_out` are valid for the
+ * trailing unblended samples.
+ */
+ for (; last < i; last += 4)
+ blend_out[last] = 0.0f;
+ }
+ }
+ else
+ {
+ /* if both blending and compositing use the same color space, things are
+ * much simpler.
+ */
+
+ if (in == out) /* in-place detected, avoid clobbering since we need to
+ read 'in' for the compositing stage */
+ {
+ blend_out = g_alloca (sizeof (gfloat) * 4 * samples);
+ }
+
+ blend_function (operation, blend_in, blend_layer, blend_out, samples);
+ }
+
+ if (! gimp_layer_mode_is_subtractive (layer_mode->layer_mode))
+ {
+ switch (composite_mode)
+ {
+ case GIMP_LAYER_COMPOSITE_UNION:
+ case GIMP_LAYER_COMPOSITE_AUTO:
+ composite_union (in, layer, blend_out, mask, opacity,
+ out, samples);
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP:
+ composite_clip_to_backdrop (in, layer, blend_out, mask, opacity,
+ out, samples);
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER:
+ composite_clip_to_layer (in, layer, blend_out, mask, opacity,
+ out, samples);
+ break;
+
+ case GIMP_LAYER_COMPOSITE_INTERSECTION:
+ composite_intersection (in, layer, blend_out, mask, opacity,
+ out, samples);
+ break;
+ }
+ }
+ else
+ {
+ switch (composite_mode)
+ {
+ case GIMP_LAYER_COMPOSITE_UNION:
+ case GIMP_LAYER_COMPOSITE_AUTO:
+ composite_union_sub (in, layer, blend_out, mask, opacity,
+ out, samples);
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP:
+ composite_clip_to_backdrop_sub (in, layer, blend_out, mask, opacity,
+ out, samples);
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER:
+ composite_clip_to_layer_sub (in, layer, blend_out, mask, opacity,
+ out, samples);
+ break;
+
+ case GIMP_LAYER_COMPOSITE_INTERSECTION:
+ composite_intersection_sub (in, layer, blend_out, mask, opacity,
+ out, samples);
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+process_last_node (GeglOperation *operation,
+ void *in_p,
+ void *layer_p,
+ void *mask_p,
+ void *out_p,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ gfloat *out = out_p;
+ gfloat *layer = layer_p;
+ gfloat *mask = mask_p;
+ gfloat opacity = GIMP_OPERATION_LAYER_MODE (operation)->opacity;
+
+ while (samples--)
+ {
+ memcpy (out, layer, 3 * sizeof (gfloat));
+
+ out[ALPHA] = layer[ALPHA] * opacity;
+ if (mask)
+ out[ALPHA] *= *mask++;
+
+ layer += 4;
+ out += 4;
+ }
+
+ return TRUE;
+}
+
+
+/* public functions */
+
+
+GimpLayerCompositeRegion
+gimp_operation_layer_mode_get_affected_region (GimpOperationLayerMode *layer_mode)
+{
+ GimpOperationLayerModeClass *klass;
+
+ g_return_val_if_fail (GIMP_IS_OPERATION_LAYER_MODE (layer_mode),
+ GIMP_LAYER_COMPOSITE_REGION_INTERSECTION);
+
+ klass = GIMP_OPERATION_LAYER_MODE_GET_CLASS (layer_mode);
+
+ if (klass->get_affected_region)
+ return klass->get_affected_region (layer_mode);
+
+ return GIMP_LAYER_COMPOSITE_REGION_INTERSECTION;
+}
diff --git a/app/operations/layer-modes/gimpoperationlayermode.h b/app/operations/layer-modes/gimpoperationlayermode.h
new file mode 100644
index 0000000..a2a5463
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationlayermode.h
@@ -0,0 +1,89 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationlayermode.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_LAYER_MODE_H__
+#define __GIMP_OPERATION_LAYER_MODE_H__
+
+
+#include <gegl-plugin.h>
+
+
+#define GIMP_TYPE_OPERATION_LAYER_MODE (gimp_operation_layer_mode_get_type ())
+#define GIMP_OPERATION_LAYER_MODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_LAYER_MODE, GimpOperationLayerMode))
+#define GIMP_OPERATION_LAYER_MODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_LAYER_MODE, GimpOperationLayerModeClass))
+#define GIMP_IS_OPERATION_LAYER_MODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_LAYER_MODE))
+#define GIMP_IS_OPERATION_LAYER_MODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_LAYER_MODE))
+#define GIMP_OPERATION_LAYER_MODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_LAYER_MODE, GimpOperationLayerModeClass))
+
+
+typedef struct _GimpOperationLayerModeClass GimpOperationLayerModeClass;
+
+struct _GimpOperationLayerMode
+{
+ GeglOperationPointComposer3 parent_instance;
+
+ GimpLayerMode layer_mode;
+ gdouble opacity;
+ GimpLayerColorSpace blend_space;
+ GimpLayerColorSpace composite_space;
+ GimpLayerCompositeMode composite_mode;
+
+ gdouble prop_opacity;
+ GimpLayerCompositeMode prop_composite_mode;
+
+ GimpLayerModeFunc function;
+ GimpLayerModeBlendFunc blend_function;
+ gboolean is_last_node;
+ gboolean has_mask;
+};
+
+struct _GimpOperationLayerModeClass
+{
+ GeglOperationPointComposer3Class parent_class;
+
+ /* virtual functions */
+ gboolean (* parent_process) (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_prop,
+ const GeglRectangle *result,
+ gint level);
+ gboolean (* process) (GeglOperation *operation,
+ void *in,
+ void *aux,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+ /* Returns the composite region (any combination of the layer and the
+ * backdrop) that the layer mode affects. Most modes only affect the
+ * overlapping region, and don't need to override this function.
+ */
+ GimpLayerCompositeRegion (* get_affected_region) (GimpOperationLayerMode *layer_mode);
+};
+
+
+GType gimp_operation_layer_mode_get_type (void) G_GNUC_CONST;
+
+GimpLayerCompositeRegion gimp_operation_layer_mode_get_affected_region (GimpOperationLayerMode *layer_mode);
+
+
+#endif /* __GIMP_OPERATION_LAYER_MODE_H__ */
diff --git a/app/operations/layer-modes/gimpoperationmerge.c b/app/operations/layer-modes/gimpoperationmerge.c
new file mode 100644
index 0000000..e1b25ca
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationmerge.c
@@ -0,0 +1,243 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationmerge.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2017 Ell
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationmerge.h"
+
+
+static gboolean gimp_operation_merge_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationMerge, gimp_operation_merge,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_merge_class_init (GimpOperationMergeClass *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:merge",
+ "description", "GIMP merge mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_merge_process;
+}
+
+static void
+gimp_operation_merge_init (GimpOperationMerge *self)
+{
+}
+
+static gboolean
+gimp_operation_merge_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--)
+ {
+ gfloat in_alpha = in[ALPHA];
+ gfloat layer_alpha = layer[ALPHA] * opacity;
+ gfloat new_alpha;
+ gint b;
+
+ if (has_mask)
+ layer_alpha *= *mask;
+
+ in_alpha = MIN (in_alpha, 1.0f - layer_alpha);
+ new_alpha = in_alpha + layer_alpha;
+
+ if (new_alpha)
+ {
+ gfloat ratio = layer_alpha / new_alpha;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b] + (layer[b] - in[b]) * ratio;
+ }
+ }
+ else
+ {
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP:
+ while (samples--)
+ {
+ gfloat in_alpha = in[ALPHA];
+ gfloat layer_alpha = layer[ALPHA] * opacity;
+ gint b;
+
+ if (has_mask)
+ layer_alpha *= *mask;
+
+ layer_alpha -= 1.0f - in_alpha;
+
+ if (layer_alpha > 0.0f)
+ {
+ gfloat ratio = layer_alpha / in_alpha;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b] + (layer[b] - in[b]) * ratio;
+ }
+ }
+ else
+ {
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = in_alpha;
+
+ 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] * opacity;
+ gint b;
+
+ if (has_mask)
+ layer_alpha *= *mask;
+
+ if (layer_alpha != 0.0f)
+ {
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = layer[b];
+ }
+ }
+ else
+ {
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = layer_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_INTERSECTION:
+ while (samples--)
+ {
+ gfloat in_alpha = in[ALPHA];
+ gfloat layer_alpha = layer[ALPHA] * opacity;
+ gint b;
+
+ if (has_mask)
+ layer_alpha *= *mask;
+
+ layer_alpha -= 1.0f - in_alpha;
+ layer_alpha = MAX (layer_alpha, 0.0f);
+
+ if (layer_alpha != 0.0f)
+ {
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = layer[b];
+ }
+ }
+ else
+ {
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = layer_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes/gimpoperationmerge.h b/app/operations/layer-modes/gimpoperationmerge.h
new file mode 100644
index 0000000..01afde1
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationmerge.h
@@ -0,0 +1,54 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationmerge.h
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2017 Ell
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_MERGE_H__
+#define __GIMP_OPERATION_MERGE_H__
+
+
+#include "gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_MERGE (gimp_operation_merge_get_type ())
+#define GIMP_OPERATION_MERGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_MERGE, GimpOperationMerge))
+#define GIMP_OPERATION_MERGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_MERGE, GimpOperationMergeClass))
+#define GIMP_IS_OPERATION_MERGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_MERGE))
+#define GIMP_IS_OPERATION_MERGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_MERGE))
+#define GIMP_OPERATION_MERGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_MERGE, GimpOperationMergeClass))
+
+
+typedef struct _GimpOperationMerge GimpOperationMerge;
+typedef struct _GimpOperationMergeClass GimpOperationMergeClass;
+
+struct _GimpOperationMerge
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationMergeClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_merge_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_MERGE_H__ */
diff --git a/app/operations/layer-modes/gimpoperationnormal-sse2.c b/app/operations/layer-modes/gimpoperationnormal-sse2.c
new file mode 100644
index 0000000..73e42ed
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationnormal-sse2.c
@@ -0,0 +1,264 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationnormal-sse2.c
+ * Copyright (C) 2013 Daniel Sabo
+ *
+ * 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-plugin.h>
+
+#include "operations/operations-types.h"
+
+#include "gimpoperationnormal.h"
+
+
+#if COMPILE_SSE2_INTRINISICS
+
+/* SSE2 */
+#include <emmintrin.h>
+
+
+gboolean
+gimp_operation_normal_process_sse2 (GeglOperation *op,
+ void *in_p,
+ void *layer_p,
+ void *mask_p,
+ void *out_p,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ /* check alignment */
+ if ((((uintptr_t)in_p) | ((uintptr_t)layer_p) | ((uintptr_t)out_p)) & 0x0F)
+ {
+ return gimp_operation_normal_process (op,
+ in_p, layer_p, mask_p, out_p,
+ samples, roi, level);
+ }
+ else
+ {
+ GimpOperationLayerMode *layer_mode = (gpointer) op;
+ gfloat opacity = layer_mode->opacity;
+ gfloat *mask = mask_p;
+ const __v4sf *v_in = (const __v4sf*) in_p;
+ const __v4sf *v_layer = (const __v4sf*) layer_p;
+ __v4sf *v_out = ( __v4sf*) out_p;
+
+ const __v4sf one = _mm_set1_ps (1.0f);
+ const __v4sf v_opacity = _mm_set1_ps (opacity);
+
+ switch (layer_mode->composite_mode)
+ {
+ case GIMP_LAYER_COMPOSITE_UNION:
+ case GIMP_LAYER_COMPOSITE_AUTO:
+ while (samples--)
+ {
+ __v4sf rgba_in, rgba_layer, alpha;
+
+ rgba_in = *v_in++;
+ rgba_layer = *v_layer++;
+
+ /* expand alpha */
+ alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer,
+ _MM_SHUFFLE (3, 3, 3, 3));
+
+ if (mask)
+ {
+ __v4sf mask_alpha;
+
+ /* multiply layer's alpha by the mask */
+ mask_alpha = _mm_set1_ps (*mask++);
+ alpha = alpha * mask_alpha;
+ }
+
+ alpha = alpha * v_opacity;
+
+ if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ()))
+ {
+ __v4sf dst_alpha, a_term, out_pixel, out_alpha, out_pixel_rbaa;
+
+ /* expand alpha */
+ dst_alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_in,
+ _MM_SHUFFLE (3, 3, 3, 3));
+
+ /* a_term = dst_a * (1.0 - src_a) */
+ a_term = dst_alpha * (one - alpha);
+
+ /* out(color) = src * src_a + dst * a_term */
+ out_pixel = rgba_layer * alpha + rgba_in * a_term;
+
+ /* out(alpha) = 1.0 * src_a + 1.0 * a_term */
+ out_alpha = alpha + a_term;
+
+ /* un-premultiply */
+ out_pixel = out_pixel / out_alpha;
+
+ /* swap in the real alpha */
+ out_pixel_rbaa = _mm_shuffle_ps (out_pixel, out_alpha, _MM_SHUFFLE (3, 3, 2, 0));
+ out_pixel = _mm_shuffle_ps (out_pixel, out_pixel_rbaa, _MM_SHUFFLE (2, 1, 1, 0));
+
+ *v_out++ = out_pixel;
+ }
+ else
+ {
+ *v_out++ = rgba_in;
+ }
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP:
+ while (samples--)
+ {
+ __v4sf rgba_in, rgba_layer, alpha;
+
+ rgba_in = *v_in++;
+ rgba_layer = *v_layer++;
+
+ /* expand alpha */
+ alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer,
+ _MM_SHUFFLE (3, 3, 3, 3));
+
+ if (mask)
+ {
+ __v4sf mask_alpha;
+
+ /* multiply layer's alpha by the mask */
+ mask_alpha = _mm_set1_ps (*mask++);
+ alpha = alpha * mask_alpha;
+ }
+
+ alpha = alpha * v_opacity;
+
+ if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ()))
+ {
+ __v4sf dst_alpha, out_pixel, out_pixel_rbaa;
+
+ /* expand alpha */
+ dst_alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_in,
+ _MM_SHUFFLE (3, 3, 3, 3));
+
+ /* out(color) = dst * (1 - src_a) + src * src_a */
+ out_pixel = rgba_in + (rgba_layer - rgba_in) * alpha;
+
+ /* swap in the real alpha */
+ out_pixel_rbaa = _mm_shuffle_ps (out_pixel, dst_alpha, _MM_SHUFFLE (3, 3, 2, 0));
+ out_pixel = _mm_shuffle_ps (out_pixel, out_pixel_rbaa, _MM_SHUFFLE (2, 1, 1, 0));
+
+ *v_out++ = out_pixel;
+ }
+ else
+ {
+ *v_out++ = rgba_in;
+ }
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER:
+ while (samples--)
+ {
+ __v4sf rgba_in, rgba_layer, alpha;
+ __v4sf out_pixel, out_pixel_rbaa;
+
+ rgba_in = *v_in++;
+ rgba_layer = *v_layer++;
+
+ /* expand alpha */
+ alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer,
+ _MM_SHUFFLE (3, 3, 3, 3));
+
+ if (mask)
+ {
+ __v4sf mask_alpha;
+
+ /* multiply layer's alpha by the mask */
+ mask_alpha = _mm_set1_ps (*mask++);
+ alpha = alpha * mask_alpha;
+ }
+
+ alpha = alpha * v_opacity;
+
+ if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ()))
+ {
+ /* out(color) = src */
+ out_pixel = rgba_layer;
+ }
+ else
+ {
+ out_pixel = rgba_in;
+ }
+
+ /* swap in the real alpha */
+ out_pixel_rbaa = _mm_shuffle_ps (out_pixel, alpha, _MM_SHUFFLE (3, 3, 2, 0));
+ out_pixel = _mm_shuffle_ps (out_pixel, out_pixel_rbaa, _MM_SHUFFLE (2, 1, 1, 0));
+
+ *v_out++ = out_pixel;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_INTERSECTION:
+ while (samples--)
+ {
+ __v4sf rgba_in, rgba_layer, alpha;
+ __v4sf out_pixel, out_pixel_rbaa;
+
+ rgba_in = *v_in++;
+ rgba_layer = *v_layer++;
+
+ /* expand alpha */
+ alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer,
+ _MM_SHUFFLE (3, 3, 3, 3));
+
+ if (mask)
+ {
+ __v4sf mask_alpha;
+
+ /* multiply layer's alpha by the mask */
+ mask_alpha = _mm_set1_ps (*mask++);
+ alpha = alpha * mask_alpha;
+ }
+
+ alpha = alpha * v_opacity;
+
+ /* multiply the alpha by in's alpha */
+ alpha *= (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_in,
+ _MM_SHUFFLE (3, 3, 3, 3));
+
+ if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ()))
+ {
+ /* out(color) = src */
+ out_pixel = rgba_layer;
+ }
+ else
+ {
+ out_pixel = rgba_in;
+ }
+
+ /* swap in the real alpha */
+ out_pixel_rbaa = _mm_shuffle_ps (out_pixel, alpha, _MM_SHUFFLE (3, 3, 2, 0));
+ out_pixel = _mm_shuffle_ps (out_pixel, out_pixel_rbaa, _MM_SHUFFLE (2, 1, 1, 0));
+
+ *v_out++ = out_pixel;
+ }
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif /* COMPILE_SSE2_INTRINISICS */
diff --git a/app/operations/layer-modes/gimpoperationnormal-sse4.c b/app/operations/layer-modes/gimpoperationnormal-sse4.c
new file mode 100644
index 0000000..ba621a2
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationnormal-sse4.c
@@ -0,0 +1,260 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationnormalmode-sse2.c
+ * Copyright (C) 2013 Daniel Sabo
+ *
+ * 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-plugin.h>
+
+#include "operations/operations-types.h"
+
+#include "gimpoperationnormal.h"
+
+
+#if COMPILE_SSE4_1_INTRINISICS
+
+/* SSE4 */
+#include <smmintrin.h>
+
+
+gboolean
+gimp_operation_normal_process_sse4 (GeglOperation *op,
+ void *in_p,
+ void *layer_p,
+ void *mask_p,
+ void *out_p,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level)
+{
+ /* check alignment */
+ if ((((uintptr_t)in_p) | ((uintptr_t)layer_p) | ((uintptr_t)out_p)) & 0x0F)
+ {
+ return gimp_operation_normal_process (op,
+ in_p, layer_p, mask_p, out_p,
+ samples, roi, level);
+ }
+ else
+ {
+ GimpOperationLayerMode *layer_mode = (gpointer) op;
+ gfloat opacity = layer_mode->opacity;
+ gfloat *mask = mask_p;
+ const __v4sf *v_in = (const __v4sf*) in_p;
+ const __v4sf *v_layer = (const __v4sf*) layer_p;
+ __v4sf *v_out = ( __v4sf*) out_p;
+
+ const __v4sf one = _mm_set1_ps (1.0f);
+ const __v4sf v_opacity = _mm_set1_ps (opacity);
+
+ switch (layer_mode->composite_mode)
+ {
+ case GIMP_LAYER_COMPOSITE_UNION:
+ case GIMP_LAYER_COMPOSITE_AUTO:
+ while (samples--)
+ {
+ __v4sf rgba_in, rgba_layer, alpha;
+
+ rgba_in = *v_in++;
+ rgba_layer = *v_layer++;
+
+ /* expand alpha */
+ alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer,
+ _MM_SHUFFLE (3, 3, 3, 3));
+
+ if (mask)
+ {
+ __v4sf mask_alpha;
+
+ /* multiply layer's alpha by the mask */
+ mask_alpha = _mm_set1_ps (*mask++);
+ alpha = alpha * mask_alpha;
+ }
+
+ alpha = alpha * v_opacity;
+
+ if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ()))
+ {
+ __v4sf dst_alpha, a_term, out_pixel, out_alpha;
+
+ /* expand alpha */
+ dst_alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_in,
+ _MM_SHUFFLE (3, 3, 3, 3));
+
+ /* a_term = dst_a * (1.0 - src_a) */
+ a_term = dst_alpha * (one - alpha);
+
+ /* out(color) = src * src_a + dst * a_term */
+ out_pixel = rgba_layer * alpha + rgba_in * a_term;
+
+ /* out(alpha) = 1.0 * src_a + 1.0 * a_term */
+ out_alpha = alpha + a_term;
+
+ /* un-premultiply */
+ out_pixel = out_pixel / out_alpha;
+
+ /* swap in the real alpha */
+ out_pixel = _mm_blend_ps (out_pixel, out_alpha, 0x08);
+
+ *v_out++ = out_pixel;
+ }
+ else
+ {
+ *v_out++ = rgba_in;
+ }
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP:
+ while (samples--)
+ {
+ __v4sf rgba_in, rgba_layer, alpha;
+
+ rgba_in = *v_in++;
+ rgba_layer = *v_layer++;
+
+ /* expand alpha */
+ alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer,
+ _MM_SHUFFLE (3, 3, 3, 3));
+
+ if (mask)
+ {
+ __v4sf mask_alpha;
+
+ /* multiply layer's alpha by the mask */
+ mask_alpha = _mm_set1_ps (*mask++);
+ alpha = alpha * mask_alpha;
+ }
+
+ alpha = alpha * v_opacity;
+
+ if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ()))
+ {
+ __v4sf dst_alpha, out_pixel;
+
+ /* expand alpha */
+ dst_alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_in,
+ _MM_SHUFFLE (3, 3, 3, 3));
+
+ /* out(color) = dst * (1 - src_a) + src * src_a */
+ out_pixel = rgba_in + (rgba_layer - rgba_in) * alpha;
+
+ /* swap in the real alpha */
+ out_pixel = _mm_blend_ps (out_pixel, dst_alpha, 0x08);
+
+ *v_out++ = out_pixel;
+ }
+ else
+ {
+ *v_out++ = rgba_in;
+ }
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER:
+ while (samples--)
+ {
+ __v4sf rgba_in, rgba_layer, alpha;
+ __v4sf out_pixel;
+
+ rgba_in = *v_in++;
+ rgba_layer = *v_layer++;
+
+ /* expand alpha */
+ alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer,
+ _MM_SHUFFLE (3, 3, 3, 3));
+
+ if (mask)
+ {
+ __v4sf mask_alpha;
+
+ /* multiply layer's alpha by the mask */
+ mask_alpha = _mm_set1_ps (*mask++);
+ alpha = alpha * mask_alpha;
+ }
+
+ alpha = alpha * v_opacity;
+
+ if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ()))
+ {
+ /* out(color) = src */
+ out_pixel = rgba_layer;
+ }
+ else
+ {
+ out_pixel = rgba_in;
+ }
+
+ /* swap in the real alpha */
+ out_pixel = _mm_blend_ps (out_pixel, alpha, 0x08);
+
+ *v_out++ = out_pixel;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_INTERSECTION:
+ while (samples--)
+ {
+ __v4sf rgba_in, rgba_layer, alpha;
+ __v4sf out_pixel;
+
+ rgba_in = *v_in++;
+ rgba_layer = *v_layer++;
+
+ /* expand alpha */
+ alpha = (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_layer,
+ _MM_SHUFFLE (3, 3, 3, 3));
+
+ if (mask)
+ {
+ __v4sf mask_alpha;
+
+ /* multiply layer's alpha by the mask */
+ mask_alpha = _mm_set1_ps (*mask++);
+ alpha = alpha * mask_alpha;
+ }
+
+ alpha = alpha * v_opacity;
+
+ /* multiply the alpha by in's alpha */
+ alpha *= (__v4sf)_mm_shuffle_epi32 ((__m128i)rgba_in,
+ _MM_SHUFFLE (3, 3, 3, 3));
+
+ if (_mm_ucomigt_ss (alpha, _mm_setzero_ps ()))
+ {
+ /* out(color) = src */
+ out_pixel = rgba_layer;
+ }
+ else
+ {
+ out_pixel = rgba_in;
+ }
+
+ /* swap in the real alpha */
+ out_pixel = _mm_blend_ps (out_pixel, alpha, 0x08);
+
+ *v_out++ = out_pixel;
+ }
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+#endif /* COMPILE_SSE4_1_INTRINISICS */
diff --git a/app/operations/layer-modes/gimpoperationnormal.c b/app/operations/layer-modes/gimpoperationnormal.c
new file mode 100644
index 0000000..9bd8705
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationnormal.c
@@ -0,0 +1,266 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationnormalmode.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 <gio/gio.h>
+#include <gegl-plugin.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "../operations-types.h"
+
+#include "gimpoperationnormal.h"
+
+
+G_DEFINE_TYPE (GimpOperationNormal, gimp_operation_normal,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static const gchar* reference_xml = "<?xml version='1.0' encoding='UTF-8'?>"
+"<gegl>"
+"<node operation='gimp:normal'>"
+" <node operation='gegl:load'>"
+" <params>"
+" <param name='path'>blending-test-B.png</param>"
+" </params>"
+" </node>"
+"</node>"
+"<node operation='gegl:load'>"
+" <params>"
+" <param name='path'>blending-test-A.png</param>"
+" </params>"
+"</node>"
+"</gegl>";
+
+
+static void
+gimp_operation_normal_class_init (GimpOperationNormalClass *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:normal",
+ "description", "GIMP normal mode operation",
+ "reference-image", "normal-mode.png",
+ "reference-composition", reference_xml,
+ NULL);
+
+ layer_mode_class->process = gimp_operation_normal_process;
+
+#if COMPILE_SSE2_INTRINISICS
+ if (gimp_cpu_accel_get_support() & GIMP_CPU_ACCEL_X86_SSE2)
+ layer_mode_class->process = gimp_operation_normal_process_sse2;
+#endif /* COMPILE_SSE2_INTRINISICS */
+
+#if COMPILE_SSE4_1_INTRINISICS
+ if (gimp_cpu_accel_get_support() & GIMP_CPU_ACCEL_X86_SSE4_1)
+ layer_mode_class->process = gimp_operation_normal_process_sse4;
+#endif /* COMPILE_SSE4_1_INTRINISICS */
+}
+
+static void
+gimp_operation_normal_init (GimpOperationNormal *self)
+{
+}
+
+gboolean
+gimp_operation_normal_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--)
+ {
+ gfloat layer_alpha;
+
+ layer_alpha = layer[ALPHA] * opacity;
+ if (has_mask)
+ layer_alpha *= *mask;
+
+ out[ALPHA] = layer_alpha + in[ALPHA] - layer_alpha * in[ALPHA];
+
+ if (out[ALPHA])
+ {
+ gfloat layer_weight = layer_alpha / out[ALPHA];
+ gfloat in_weight = 1.0f - layer_weight;
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = layer[b] * layer_weight + in[b] * in_weight;
+ }
+ }
+ 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_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/gimpoperationnormal.h b/app/operations/layer-modes/gimpoperationnormal.h
new file mode 100644
index 0000000..6fa1b36
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationnormal.h
@@ -0,0 +1,91 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationnormal.h
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_NORMAL_H__
+#define __GIMP_OPERATION_NORMAL_H__
+
+
+#include "gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_NORMAL (gimp_operation_normal_get_type ())
+#define GIMP_OPERATION_NORMAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_NORMAL, GimpOperationNormal))
+#define GIMP_OPERATION_NORMAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_NORMAL, GimpOperationNormalClass))
+#define GIMP_IS_OPERATION_NORMAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_NORMAL))
+#define GIMP_IS_OPERATION_NORMAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_NORMAL))
+#define GIMP_OPERATION_NORMAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_NORMAL, GimpOperationNormalClass))
+
+
+typedef struct _GimpOperationNormal GimpOperationNormal;
+typedef struct _GimpOperationNormalClass GimpOperationNormalClass;
+
+struct _GimpOperationNormal
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationNormalClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_normal_get_type (void) G_GNUC_CONST;
+
+
+/* protected */
+
+gboolean gimp_operation_normal_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+#if COMPILE_SSE2_INTRINISICS
+
+gboolean gimp_operation_normal_process_sse2 (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+#endif /* COMPILE_SSE2_INTRINISICS */
+
+#if COMPILE_SSE4_1_INTRINISICS
+
+gboolean gimp_operation_normal_process_sse4 (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+#endif /* COMPILE_SSE4_1_INTRINISICS */
+
+
+#endif /* __GIMP_OPERATION_NORMAL_H__ */
diff --git a/app/operations/layer-modes/gimpoperationpassthrough.c b/app/operations/layer-modes/gimpoperationpassthrough.c
new file mode 100644
index 0000000..bc7d328
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationpassthrough.c
@@ -0,0 +1,55 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationpassthrough.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2017 Ell
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationpassthrough.h"
+
+
+G_DEFINE_TYPE (GimpOperationPassThrough, gimp_operation_pass_through,
+ GIMP_TYPE_OPERATION_REPLACE)
+
+
+static void
+gimp_operation_pass_through_class_init (GimpOperationPassThroughClass *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:pass-through",
+ "description", "GIMP pass through mode operation",
+ NULL);
+
+ /* don't use REPLACE mode's specialized get_affected_region(); PASS_THROUGH
+ * behaves like an ordinary layer mode here.
+ */
+ layer_mode_class->get_affected_region = NULL;
+}
+
+static void
+gimp_operation_pass_through_init (GimpOperationPassThrough *self)
+{
+}
diff --git a/app/operations/layer-modes/gimpoperationpassthrough.h b/app/operations/layer-modes/gimpoperationpassthrough.h
new file mode 100644
index 0000000..5a5b838
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationpassthrough.h
@@ -0,0 +1,54 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationpassthrough.h
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2017 Ell
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_PASS_THROUGH_H__
+#define __GIMP_OPERATION_PASS_THROUGH_H__
+
+
+#include "gimpoperationreplace.h"
+
+
+#define GIMP_TYPE_OPERATION_PASS_THROUGH (gimp_operation_pass_through_get_type ())
+#define GIMP_OPERATION_PASS_THROUGH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_PASS_THROUGH, GimpOperationPassThrough))
+#define GIMP_OPERATION_PASS_THROUGH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_PASS_THROUGH, GimpOperationPassThroughClass))
+#define GIMP_IS_OPERATION_PASS_THROUGH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_PASS_THROUGH))
+#define GIMP_IS_OPERATION_PASS_THROUGH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_PASS_THROUGH))
+#define GIMP_OPERATION_PASS_THROUGH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_PASS_THROUGH, GimpOperationPassThroughClass))
+
+
+typedef struct _GimpOperationPassThrough GimpOperationPassThrough;
+typedef struct _GimpOperationPassThroughClass GimpOperationPassThroughClass;
+
+struct _GimpOperationPassThrough
+{
+ GimpOperationReplace parent_instance;
+};
+
+struct _GimpOperationPassThroughClass
+{
+ GimpOperationReplaceClass parent_class;
+};
+
+
+GType gimp_operation_pass_through_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_PASS_THROUGH_H__ */
diff --git a/app/operations/layer-modes/gimpoperationreplace.c b/app/operations/layer-modes/gimpoperationreplace.c
new file mode 100644
index 0000000..ed1ac19
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationreplace.c
@@ -0,0 +1,347 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationreplace.c
+ * Copyright (C) 2008 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 <string.h>
+
+#include <gegl-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimp-layer-modes.h"
+#include "gimpoperationreplace.h"
+
+
+static GeglRectangle gimp_operation_replace_get_bounding_box (GeglOperation *op);
+
+static gboolean gimp_operation_replace_parent_process (GeglOperation *op,
+ GeglOperationContext *context,
+ const gchar *output_prop,
+ const GeglRectangle *result,
+ gint level);
+static gboolean gimp_operation_replace_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+static GimpLayerCompositeRegion gimp_operation_replace_get_affected_region (GimpOperationLayerMode *layer_mode);
+
+
+G_DEFINE_TYPE (GimpOperationReplace, gimp_operation_replace,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+#define parent_class gimp_operation_replace_parent_class
+
+
+static void
+gimp_operation_replace_class_init (GimpOperationReplaceClass *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:replace",
+ "description", "GIMP replace mode operation",
+ NULL);
+
+ operation_class->get_bounding_box = gimp_operation_replace_get_bounding_box;
+
+ layer_mode_class->parent_process = gimp_operation_replace_parent_process;
+ layer_mode_class->process = gimp_operation_replace_process;
+ layer_mode_class->get_affected_region = gimp_operation_replace_get_affected_region;
+}
+
+static void
+gimp_operation_replace_init (GimpOperationReplace *self)
+{
+}
+
+static GeglRectangle
+gimp_operation_replace_get_bounding_box (GeglOperation *op)
+{
+ GimpOperationLayerMode *self = (gpointer) op;
+ GeglRectangle *in_rect;
+ GeglRectangle *aux_rect;
+ GeglRectangle *aux2_rect;
+ GeglRectangle src_rect = {};
+ GeglRectangle dst_rect = {};
+ GeglRectangle result;
+ GimpLayerCompositeRegion included_region;
+
+ in_rect = gegl_operation_source_get_bounding_box (op, "input");
+ aux_rect = gegl_operation_source_get_bounding_box (op, "aux");
+ aux2_rect = gegl_operation_source_get_bounding_box (op, "aux2");
+
+ if (in_rect)
+ dst_rect = *in_rect;
+
+ if (aux_rect)
+ {
+ src_rect = *aux_rect;
+
+ if (aux2_rect)
+ gegl_rectangle_intersect (&src_rect, &src_rect, aux2_rect);
+ }
+
+ if (self->is_last_node)
+ {
+ included_region = GIMP_LAYER_COMPOSITE_REGION_SOURCE;
+ }
+ else
+ {
+ included_region = gimp_layer_mode_get_included_region (self->layer_mode,
+ self->composite_mode);
+ }
+
+ if (self->prop_opacity == 0.0)
+ included_region &= ~GIMP_LAYER_COMPOSITE_REGION_SOURCE;
+ else if (self->prop_opacity == 1.0 && ! aux2_rect)
+ included_region &= ~GIMP_LAYER_COMPOSITE_REGION_DESTINATION;
+
+ gegl_rectangle_intersect (&result, &src_rect, &dst_rect);
+
+ if (included_region & GIMP_LAYER_COMPOSITE_REGION_SOURCE)
+ gegl_rectangle_bounding_box (&result, &result, &src_rect);
+
+ if (included_region & GIMP_LAYER_COMPOSITE_REGION_DESTINATION)
+ gegl_rectangle_bounding_box (&result, &result, &dst_rect);
+
+ return result;
+}
+
+static gboolean
+gimp_operation_replace_parent_process (GeglOperation *op,
+ GeglOperationContext *context,
+ const gchar *output_prop,
+ const GeglRectangle *result,
+ gint level)
+{
+ GimpOperationLayerMode *layer_mode = (gpointer) op;
+ GimpLayerCompositeRegion included_region;
+
+ included_region = gimp_layer_mode_get_included_region
+ (layer_mode->layer_mode, layer_mode->composite_mode);
+
+ /* if the layer's opacity is 100%, it has no mask, and its composite mode
+ * contains "aux" (the latter should always be the case in practice,
+ * currently,) we can just pass "aux" directly as output.
+ */
+ if (layer_mode->opacity == 1.0 &&
+ ! gegl_operation_context_get_object (context, "aux2") &&
+ (included_region & GIMP_LAYER_COMPOSITE_REGION_SOURCE))
+ {
+ GObject *aux;
+
+ aux = gegl_operation_context_get_object (context, "aux");
+
+ gegl_operation_context_set_object (context, "output", aux);
+
+ return TRUE;
+ }
+ /* the opposite case, where the opacity is 0%, is handled by
+ * GimpOperationLayerMode.
+ */
+ else if (layer_mode->opacity == 0.0)
+ {
+ }
+ /* if both buffers are included in the result, and if both of them have the
+ * same content -- i.e., if they share the same storage, same alignment, and
+ * same abyss (or if the abyss is irrelevant) -- we can just pass either of
+ * them directly as output.
+ */
+ else if (included_region == GIMP_LAYER_COMPOSITE_REGION_UNION)
+ {
+ GObject *input;
+ GObject *aux;
+
+ input = gegl_operation_context_get_object (context, "input");
+ aux = gegl_operation_context_get_object (context, "aux");
+
+ if (input && aux &&
+ gegl_buffer_share_storage (GEGL_BUFFER (input), GEGL_BUFFER (aux)))
+ {
+ gint input_shift_x;
+ gint input_shift_y;
+ gint aux_shift_x;
+ gint aux_shift_y;
+
+ g_object_get (input,
+ "shift-x", &input_shift_x,
+ "shift-y", &input_shift_y,
+ NULL);
+ g_object_get (aux,
+ "shift-x", &aux_shift_x,
+ "shift-y", &aux_shift_y,
+ NULL);
+
+ if (input_shift_x == aux_shift_x && input_shift_y == aux_shift_y)
+ {
+ const GeglRectangle *input_abyss;
+ const GeglRectangle *aux_abyss;
+
+ input_abyss = gegl_buffer_get_abyss (GEGL_BUFFER (input));
+ aux_abyss = gegl_buffer_get_abyss (GEGL_BUFFER (aux));
+
+ if (gegl_rectangle_equal (input_abyss, aux_abyss) ||
+ (gegl_rectangle_contains (input_abyss, result) &&
+ gegl_rectangle_contains (aux_abyss, result)))
+ {
+ gegl_operation_context_set_object (context, "output", input);
+
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return GIMP_OPERATION_LAYER_MODE_CLASS (parent_class)->parent_process (
+ op, context, output_prop, result, level);
+}
+
+static gboolean
+gimp_operation_replace_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--)
+ {
+ gfloat opacity_value = opacity;
+ gfloat new_alpha;
+ gfloat ratio;
+ gint b;
+
+ if (has_mask)
+ opacity_value *= *mask;
+
+ new_alpha = (layer[ALPHA] - in[ALPHA]) * opacity_value + in[ALPHA];
+
+ ratio = opacity_value;
+
+ if (new_alpha)
+ ratio *= layer[ALPHA] / new_alpha;
+
+ for (b = RED; b < ALPHA; b++)
+ out[b] = (layer[b] - in[b]) * ratio + in[b];
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP:
+ while (samples--)
+ {
+ gfloat opacity_value = opacity;
+ gfloat new_alpha;
+ gint b;
+
+ if (has_mask)
+ opacity_value *= *mask;
+
+ new_alpha = in[ALPHA] * (1.0f - opacity_value);
+
+ for (b = RED; b < ALPHA; b++)
+ out[b] = in[b];
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER:
+ while (samples--)
+ {
+ gfloat opacity_value = opacity;
+ gfloat new_alpha;
+ gint b;
+
+ if (has_mask)
+ opacity_value *= *mask;
+
+ new_alpha = layer[ALPHA] * opacity_value;
+
+ for (b = RED; b < ALPHA; b++)
+ out[b] = layer[b];
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_INTERSECTION:
+ memset (out, 0, 4 * samples * sizeof (gfloat));
+ break;
+ }
+
+ return TRUE;
+}
+
+static GimpLayerCompositeRegion
+gimp_operation_replace_get_affected_region (GimpOperationLayerMode *layer_mode)
+{
+ GimpLayerCompositeRegion affected_region = GIMP_LAYER_COMPOSITE_REGION_INTERSECTION;
+
+ if (layer_mode->prop_opacity != 0.0)
+ affected_region |= GIMP_LAYER_COMPOSITE_REGION_DESTINATION;
+
+ /* if opacity != 1.0, or we have a mask, then we also affect SOURCE, but this
+ * is considered the case anyway, so no need for special handling.
+ */
+
+ return affected_region;
+}
diff --git a/app/operations/layer-modes/gimpoperationreplace.h b/app/operations/layer-modes/gimpoperationreplace.h
new file mode 100644
index 0000000..f8d7b0c
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationreplace.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationreplace.h
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_REPLACE_H__
+#define __GIMP_OPERATION_REPLACE_H__
+
+
+#include "gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_REPLACE (gimp_operation_replace_get_type ())
+#define GIMP_OPERATION_REPLACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_REPLACE, GimpOperationReplace))
+#define GIMP_OPERATION_REPLACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_REPLACE, GimpOperationReplaceClass))
+#define GIMP_IS_OPERATION_REPLACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_REPLACE))
+#define GIMP_IS_OPERATION_REPLACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_REPLACE))
+#define GIMP_OPERATION_REPLACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_REPLACE, GimpOperationReplaceClass))
+
+
+typedef struct _GimpOperationReplace GimpOperationReplace;
+typedef struct _GimpOperationReplaceClass GimpOperationReplaceClass;
+
+struct _GimpOperationReplace
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationReplaceClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_replace_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_REPLACE_H__ */
diff --git a/app/operations/layer-modes/gimpoperationsplit.c b/app/operations/layer-modes/gimpoperationsplit.c
new file mode 100644
index 0000000..fe83d3f
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationsplit.c
@@ -0,0 +1,213 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationsplit.c
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2017 Ell
+ *
+ * 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-plugin.h>
+
+#include "../operations-types.h"
+
+#include "gimpoperationsplit.h"
+
+
+static gboolean gimp_operation_split_process (GeglOperation *op,
+ void *in,
+ void *layer,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+
+G_DEFINE_TYPE (GimpOperationSplit, gimp_operation_split,
+ GIMP_TYPE_OPERATION_LAYER_MODE)
+
+
+static void
+gimp_operation_split_class_init (GimpOperationSplitClass *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:split",
+ "description", "GIMP split mode operation",
+ NULL);
+
+ layer_mode_class->process = gimp_operation_split_process;
+}
+
+static void
+gimp_operation_split_init (GimpOperationSplit *self)
+{
+}
+
+static gboolean
+gimp_operation_split_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:
+ while (samples--)
+ {
+ gfloat in_alpha = in[ALPHA];
+ gfloat layer_alpha = layer[ALPHA] * opacity;
+ gfloat new_alpha;
+ gint b;
+
+ if (has_mask)
+ layer_alpha *= *mask;
+
+ if (layer_alpha <= in_alpha)
+ {
+ new_alpha = in_alpha - layer_alpha;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+ else
+ {
+ new_alpha = layer_alpha - in_alpha;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = layer[b];
+ }
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP:
+ case GIMP_LAYER_COMPOSITE_AUTO:
+ while (samples--)
+ {
+ gfloat in_alpha = in[ALPHA];
+ gfloat layer_alpha = layer[ALPHA] * opacity;
+ gfloat new_alpha;
+ gint b;
+
+ if (has_mask)
+ layer_alpha *= *mask;
+
+ new_alpha = MAX (in_alpha - layer_alpha, 0.0f);
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER:
+ while (samples--)
+ {
+ gfloat in_alpha = in[ALPHA];
+ gfloat layer_alpha = layer[ALPHA] * opacity;
+ gfloat new_alpha;
+ gint b;
+
+ if (has_mask)
+ layer_alpha *= *mask;
+
+ new_alpha = MAX (layer_alpha - in_alpha, 0.0f);
+
+ if (new_alpha != 0.0f)
+ {
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = layer[b];
+ }
+ }
+ else
+ {
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+ }
+
+ out[ALPHA] = new_alpha;
+
+ in += 4;
+ layer += 4;
+ out += 4;
+
+ if (has_mask)
+ mask++;
+ }
+ break;
+
+ case GIMP_LAYER_COMPOSITE_INTERSECTION:
+ while (samples--)
+ {
+ gint b;
+
+ for (b = RED; b < ALPHA; b++)
+ {
+ out[b] = in[b];
+ }
+
+ out[ALPHA] = 0.0f;
+
+ in += 4;
+ out += 4;
+ }
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/app/operations/layer-modes/gimpoperationsplit.h b/app/operations/layer-modes/gimpoperationsplit.h
new file mode 100644
index 0000000..e1dbbe9
--- /dev/null
+++ b/app/operations/layer-modes/gimpoperationsplit.h
@@ -0,0 +1,54 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpoperationsplit.h
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ * 2017 Ell
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_OPERATION_SPLIT_H__
+#define __GIMP_OPERATION_SPLIT_H__
+
+
+#include "gimpoperationlayermode.h"
+
+
+#define GIMP_TYPE_OPERATION_SPLIT (gimp_operation_split_get_type ())
+#define GIMP_OPERATION_SPLIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SPLIT, GimpOperationSplit))
+#define GIMP_OPERATION_SPLIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SPLIT, GimpOperationSplitClass))
+#define GIMP_IS_OPERATION_SPLIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SPLIT))
+#define GIMP_IS_OPERATION_SPLIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SPLIT))
+#define GIMP_OPERATION_SPLIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SPLIT, GimpOperationSplitClass))
+
+
+typedef struct _GimpOperationSplit GimpOperationSplit;
+typedef struct _GimpOperationSplitClass GimpOperationSplitClass;
+
+struct _GimpOperationSplit
+{
+ GimpOperationLayerMode parent_instance;
+};
+
+struct _GimpOperationSplitClass
+{
+ GimpOperationLayerModeClass parent_class;
+};
+
+
+GType gimp_operation_split_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_OPERATION_SPLIT_H__ */
diff --git a/app/operations/operations-enums.c b/app/operations/operations-enums.c
new file mode 100644
index 0000000..1e8101d
--- /dev/null
+++ b/app/operations/operations-enums.c
@@ -0,0 +1,370 @@
+
+/* Generated data (by gimp-mkenums) */
+
+#include "config.h"
+#include <gio/gio.h>
+#include "libgimpbase/gimpbase.h"
+#include "operations-enums.h"
+#include "gimp-intl.h"
+
+/* enumerations from "operations-enums.h" */
+GType
+gimp_layer_color_space_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_LAYER_COLOR_SPACE_AUTO, "GIMP_LAYER_COLOR_SPACE_AUTO", "auto" },
+ { GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, "GIMP_LAYER_COLOR_SPACE_RGB_LINEAR", "rgb-linear" },
+ { GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, "GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL", "rgb-perceptual" },
+ { GIMP_LAYER_COLOR_SPACE_LAB, "GIMP_LAYER_COLOR_SPACE_LAB", "lab" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_LAYER_COLOR_SPACE_AUTO, NC_("layer-color-space", "Auto"), NULL },
+ { GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, NC_("layer-color-space", "RGB (linear)"), NULL },
+ { GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, NC_("layer-color-space", "RGB (perceptual)"), NULL },
+ { GIMP_LAYER_COLOR_SPACE_LAB, NC_("layer-color-space", "LAB"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpLayerColorSpace", values);
+ gimp_type_set_translation_context (type, "layer-color-space");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_layer_composite_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_LAYER_COMPOSITE_AUTO, "GIMP_LAYER_COMPOSITE_AUTO", "auto" },
+ { GIMP_LAYER_COMPOSITE_UNION, "GIMP_LAYER_COMPOSITE_UNION", "union" },
+ { GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, "GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP", "clip-to-backdrop" },
+ { GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER, "GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER", "clip-to-layer" },
+ { GIMP_LAYER_COMPOSITE_INTERSECTION, "GIMP_LAYER_COMPOSITE_INTERSECTION", "intersection" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_LAYER_COMPOSITE_AUTO, NC_("layer-composite-mode", "Auto"), NULL },
+ { GIMP_LAYER_COMPOSITE_UNION, NC_("layer-composite-mode", "Union"), NULL },
+ { GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, NC_("layer-composite-mode", "Clip to backdrop"), NULL },
+ { GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER, NC_("layer-composite-mode", "Clip to layer"), NULL },
+ { GIMP_LAYER_COMPOSITE_INTERSECTION, NC_("layer-composite-mode", "Intersection"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpLayerCompositeMode", values);
+ gimp_type_set_translation_context (type, "layer-composite-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_layer_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_LAYER_MODE_NORMAL_LEGACY, "GIMP_LAYER_MODE_NORMAL_LEGACY", "normal-legacy" },
+ { GIMP_LAYER_MODE_DISSOLVE, "GIMP_LAYER_MODE_DISSOLVE", "dissolve" },
+ { GIMP_LAYER_MODE_BEHIND_LEGACY, "GIMP_LAYER_MODE_BEHIND_LEGACY", "behind-legacy" },
+ { GIMP_LAYER_MODE_MULTIPLY_LEGACY, "GIMP_LAYER_MODE_MULTIPLY_LEGACY", "multiply-legacy" },
+ { GIMP_LAYER_MODE_SCREEN_LEGACY, "GIMP_LAYER_MODE_SCREEN_LEGACY", "screen-legacy" },
+ { GIMP_LAYER_MODE_OVERLAY_LEGACY, "GIMP_LAYER_MODE_OVERLAY_LEGACY", "overlay-legacy" },
+ { GIMP_LAYER_MODE_DIFFERENCE_LEGACY, "GIMP_LAYER_MODE_DIFFERENCE_LEGACY", "difference-legacy" },
+ { GIMP_LAYER_MODE_ADDITION_LEGACY, "GIMP_LAYER_MODE_ADDITION_LEGACY", "addition-legacy" },
+ { GIMP_LAYER_MODE_SUBTRACT_LEGACY, "GIMP_LAYER_MODE_SUBTRACT_LEGACY", "subtract-legacy" },
+ { GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, "GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY", "darken-only-legacy" },
+ { GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, "GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY", "lighten-only-legacy" },
+ { GIMP_LAYER_MODE_HSV_HUE_LEGACY, "GIMP_LAYER_MODE_HSV_HUE_LEGACY", "hsv-hue-legacy" },
+ { GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, "GIMP_LAYER_MODE_HSV_SATURATION_LEGACY", "hsv-saturation-legacy" },
+ { GIMP_LAYER_MODE_HSL_COLOR_LEGACY, "GIMP_LAYER_MODE_HSL_COLOR_LEGACY", "hsl-color-legacy" },
+ { GIMP_LAYER_MODE_HSV_VALUE_LEGACY, "GIMP_LAYER_MODE_HSV_VALUE_LEGACY", "hsv-value-legacy" },
+ { GIMP_LAYER_MODE_DIVIDE_LEGACY, "GIMP_LAYER_MODE_DIVIDE_LEGACY", "divide-legacy" },
+ { GIMP_LAYER_MODE_DODGE_LEGACY, "GIMP_LAYER_MODE_DODGE_LEGACY", "dodge-legacy" },
+ { GIMP_LAYER_MODE_BURN_LEGACY, "GIMP_LAYER_MODE_BURN_LEGACY", "burn-legacy" },
+ { GIMP_LAYER_MODE_HARDLIGHT_LEGACY, "GIMP_LAYER_MODE_HARDLIGHT_LEGACY", "hardlight-legacy" },
+ { GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, "GIMP_LAYER_MODE_SOFTLIGHT_LEGACY", "softlight-legacy" },
+ { GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY, "GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY", "grain-extract-legacy" },
+ { GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY, "GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY", "grain-merge-legacy" },
+ { GIMP_LAYER_MODE_COLOR_ERASE_LEGACY, "GIMP_LAYER_MODE_COLOR_ERASE_LEGACY", "color-erase-legacy" },
+ { GIMP_LAYER_MODE_OVERLAY, "GIMP_LAYER_MODE_OVERLAY", "overlay" },
+ { GIMP_LAYER_MODE_LCH_HUE, "GIMP_LAYER_MODE_LCH_HUE", "lch-hue" },
+ { GIMP_LAYER_MODE_LCH_CHROMA, "GIMP_LAYER_MODE_LCH_CHROMA", "lch-chroma" },
+ { GIMP_LAYER_MODE_LCH_COLOR, "GIMP_LAYER_MODE_LCH_COLOR", "lch-color" },
+ { GIMP_LAYER_MODE_LCH_LIGHTNESS, "GIMP_LAYER_MODE_LCH_LIGHTNESS", "lch-lightness" },
+ { GIMP_LAYER_MODE_NORMAL, "GIMP_LAYER_MODE_NORMAL", "normal" },
+ { GIMP_LAYER_MODE_BEHIND, "GIMP_LAYER_MODE_BEHIND", "behind" },
+ { GIMP_LAYER_MODE_MULTIPLY, "GIMP_LAYER_MODE_MULTIPLY", "multiply" },
+ { GIMP_LAYER_MODE_SCREEN, "GIMP_LAYER_MODE_SCREEN", "screen" },
+ { GIMP_LAYER_MODE_DIFFERENCE, "GIMP_LAYER_MODE_DIFFERENCE", "difference" },
+ { GIMP_LAYER_MODE_ADDITION, "GIMP_LAYER_MODE_ADDITION", "addition" },
+ { GIMP_LAYER_MODE_SUBTRACT, "GIMP_LAYER_MODE_SUBTRACT", "subtract" },
+ { GIMP_LAYER_MODE_DARKEN_ONLY, "GIMP_LAYER_MODE_DARKEN_ONLY", "darken-only" },
+ { GIMP_LAYER_MODE_LIGHTEN_ONLY, "GIMP_LAYER_MODE_LIGHTEN_ONLY", "lighten-only" },
+ { GIMP_LAYER_MODE_HSV_HUE, "GIMP_LAYER_MODE_HSV_HUE", "hsv-hue" },
+ { GIMP_LAYER_MODE_HSV_SATURATION, "GIMP_LAYER_MODE_HSV_SATURATION", "hsv-saturation" },
+ { GIMP_LAYER_MODE_HSL_COLOR, "GIMP_LAYER_MODE_HSL_COLOR", "hsl-color" },
+ { GIMP_LAYER_MODE_HSV_VALUE, "GIMP_LAYER_MODE_HSV_VALUE", "hsv-value" },
+ { GIMP_LAYER_MODE_DIVIDE, "GIMP_LAYER_MODE_DIVIDE", "divide" },
+ { GIMP_LAYER_MODE_DODGE, "GIMP_LAYER_MODE_DODGE", "dodge" },
+ { GIMP_LAYER_MODE_BURN, "GIMP_LAYER_MODE_BURN", "burn" },
+ { GIMP_LAYER_MODE_HARDLIGHT, "GIMP_LAYER_MODE_HARDLIGHT", "hardlight" },
+ { GIMP_LAYER_MODE_SOFTLIGHT, "GIMP_LAYER_MODE_SOFTLIGHT", "softlight" },
+ { GIMP_LAYER_MODE_GRAIN_EXTRACT, "GIMP_LAYER_MODE_GRAIN_EXTRACT", "grain-extract" },
+ { GIMP_LAYER_MODE_GRAIN_MERGE, "GIMP_LAYER_MODE_GRAIN_MERGE", "grain-merge" },
+ { GIMP_LAYER_MODE_VIVID_LIGHT, "GIMP_LAYER_MODE_VIVID_LIGHT", "vivid-light" },
+ { GIMP_LAYER_MODE_PIN_LIGHT, "GIMP_LAYER_MODE_PIN_LIGHT", "pin-light" },
+ { GIMP_LAYER_MODE_LINEAR_LIGHT, "GIMP_LAYER_MODE_LINEAR_LIGHT", "linear-light" },
+ { GIMP_LAYER_MODE_HARD_MIX, "GIMP_LAYER_MODE_HARD_MIX", "hard-mix" },
+ { GIMP_LAYER_MODE_EXCLUSION, "GIMP_LAYER_MODE_EXCLUSION", "exclusion" },
+ { GIMP_LAYER_MODE_LINEAR_BURN, "GIMP_LAYER_MODE_LINEAR_BURN", "linear-burn" },
+ { GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, "GIMP_LAYER_MODE_LUMA_DARKEN_ONLY", "luma-darken-only" },
+ { GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, "GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY", "luma-lighten-only" },
+ { GIMP_LAYER_MODE_LUMINANCE, "GIMP_LAYER_MODE_LUMINANCE", "luminance" },
+ { GIMP_LAYER_MODE_COLOR_ERASE, "GIMP_LAYER_MODE_COLOR_ERASE", "color-erase" },
+ { GIMP_LAYER_MODE_ERASE, "GIMP_LAYER_MODE_ERASE", "erase" },
+ { GIMP_LAYER_MODE_MERGE, "GIMP_LAYER_MODE_MERGE", "merge" },
+ { 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_ANTI_ERASE, "GIMP_LAYER_MODE_ANTI_ERASE", "anti-erase" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_LAYER_MODE_NORMAL_LEGACY, NC_("layer-mode", "Normal (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Normal (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_NORMAL_LEGACY, NC_("layer-mode", "Normal (l)"), NULL },
+ { GIMP_LAYER_MODE_DISSOLVE, NC_("layer-mode", "Dissolve"), NULL },
+ { GIMP_LAYER_MODE_BEHIND_LEGACY, NC_("layer-mode", "Behind (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Behind (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_BEHIND_LEGACY, NC_("layer-mode", "Behind (l)"), NULL },
+ { GIMP_LAYER_MODE_MULTIPLY_LEGACY, NC_("layer-mode", "Multiply (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Multiply (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_MULTIPLY_LEGACY, NC_("layer-mode", "Multiply (l)"), NULL },
+ { GIMP_LAYER_MODE_SCREEN_LEGACY, NC_("layer-mode", "Screen (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Screen (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_SCREEN_LEGACY, NC_("layer-mode", "Screen (l)"), NULL },
+ { GIMP_LAYER_MODE_OVERLAY_LEGACY, NC_("layer-mode", "Old broken Overlay"), NULL },
+ /* Translators: this is an abbreviated version of "Old broken Overlay".
+ Keep it short. */
+ { GIMP_LAYER_MODE_OVERLAY_LEGACY, NC_("layer-mode", "Old Overlay"), NULL },
+ { GIMP_LAYER_MODE_DIFFERENCE_LEGACY, NC_("layer-mode", "Difference (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Difference (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_DIFFERENCE_LEGACY, NC_("layer-mode", "Difference (l)"), NULL },
+ { GIMP_LAYER_MODE_ADDITION_LEGACY, NC_("layer-mode", "Addition (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Addition (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_ADDITION_LEGACY, NC_("layer-mode", "Addition (l)"), NULL },
+ { GIMP_LAYER_MODE_SUBTRACT_LEGACY, NC_("layer-mode", "Subtract (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Subtract (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_SUBTRACT_LEGACY, NC_("layer-mode", "Subtract (l)"), NULL },
+ { GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, NC_("layer-mode", "Darken only (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Darken only (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, NC_("layer-mode", "Darken only (l)"), NULL },
+ { GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, NC_("layer-mode", "Lighten only (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Lighten only (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, NC_("layer-mode", "Lighten only (l)"), NULL },
+ { GIMP_LAYER_MODE_HSV_HUE_LEGACY, NC_("layer-mode", "HSV Hue (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "HSV Hue (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_HSV_HUE_LEGACY, NC_("layer-mode", "HSV Hue (l)"), NULL },
+ { GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, NC_("layer-mode", "HSV Saturation (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "HSV Saturation (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, NC_("layer-mode", "HSV Saturation (l)"), NULL },
+ { GIMP_LAYER_MODE_HSL_COLOR_LEGACY, NC_("layer-mode", "HSL Color (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "HSL Color (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_HSL_COLOR_LEGACY, NC_("layer-mode", "HSL Color (l)"), NULL },
+ { GIMP_LAYER_MODE_HSV_VALUE_LEGACY, NC_("layer-mode", "HSV Value (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "HSV Value (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_HSV_VALUE_LEGACY, NC_("layer-mode", "HSV Value (l)"), NULL },
+ { GIMP_LAYER_MODE_DIVIDE_LEGACY, NC_("layer-mode", "Divide (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Divide (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_DIVIDE_LEGACY, NC_("layer-mode", "Divide (l)"), NULL },
+ { GIMP_LAYER_MODE_DODGE_LEGACY, NC_("layer-mode", "Dodge (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Dodge (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_DODGE_LEGACY, NC_("layer-mode", "Dodge (l)"), NULL },
+ { GIMP_LAYER_MODE_BURN_LEGACY, NC_("layer-mode", "Burn (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Burn (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_BURN_LEGACY, NC_("layer-mode", "Burn (l)"), NULL },
+ { GIMP_LAYER_MODE_HARDLIGHT_LEGACY, NC_("layer-mode", "Hard light (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Hard light (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_HARDLIGHT_LEGACY, NC_("layer-mode", "Hard light (l)"), NULL },
+ { GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, NC_("layer-mode", "Soft light (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Soft light (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, NC_("layer-mode", "Soft light (l)"), NULL },
+ { GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY, NC_("layer-mode", "Grain extract (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Grain extract (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY, NC_("layer-mode", "Grain extract (l)"), NULL },
+ { GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY, NC_("layer-mode", "Grain merge (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Grain merge (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY, NC_("layer-mode", "Grain merge (l)"), NULL },
+ { GIMP_LAYER_MODE_COLOR_ERASE_LEGACY, NC_("layer-mode", "Color erase (legacy)"), NULL },
+ /* Translators: this is an abbreviated version of "Color erase (legacy)".
+ Keep it short. */
+ { GIMP_LAYER_MODE_COLOR_ERASE_LEGACY, NC_("layer-mode", "Color erase (l)"), NULL },
+ { GIMP_LAYER_MODE_OVERLAY, NC_("layer-mode", "Overlay"), NULL },
+ { GIMP_LAYER_MODE_LCH_HUE, NC_("layer-mode", "LCh Hue"), NULL },
+ { GIMP_LAYER_MODE_LCH_CHROMA, NC_("layer-mode", "LCh Chroma"), NULL },
+ { GIMP_LAYER_MODE_LCH_COLOR, NC_("layer-mode", "LCh Color"), NULL },
+ { GIMP_LAYER_MODE_LCH_LIGHTNESS, NC_("layer-mode", "LCh Lightness"), NULL },
+ { GIMP_LAYER_MODE_NORMAL, NC_("layer-mode", "Normal"), NULL },
+ { GIMP_LAYER_MODE_BEHIND, NC_("layer-mode", "Behind"), NULL },
+ { GIMP_LAYER_MODE_MULTIPLY, NC_("layer-mode", "Multiply"), NULL },
+ { GIMP_LAYER_MODE_SCREEN, NC_("layer-mode", "Screen"), NULL },
+ { GIMP_LAYER_MODE_DIFFERENCE, NC_("layer-mode", "Difference"), NULL },
+ { GIMP_LAYER_MODE_ADDITION, NC_("layer-mode", "Addition"), NULL },
+ { GIMP_LAYER_MODE_SUBTRACT, NC_("layer-mode", "Subtract"), NULL },
+ { GIMP_LAYER_MODE_DARKEN_ONLY, NC_("layer-mode", "Darken only"), NULL },
+ { GIMP_LAYER_MODE_LIGHTEN_ONLY, NC_("layer-mode", "Lighten only"), NULL },
+ { GIMP_LAYER_MODE_HSV_HUE, NC_("layer-mode", "HSV Hue"), NULL },
+ { GIMP_LAYER_MODE_HSV_SATURATION, NC_("layer-mode", "HSV Saturation"), NULL },
+ { GIMP_LAYER_MODE_HSL_COLOR, NC_("layer-mode", "HSL Color"), NULL },
+ { GIMP_LAYER_MODE_HSV_VALUE, NC_("layer-mode", "HSV Value"), NULL },
+ { GIMP_LAYER_MODE_DIVIDE, NC_("layer-mode", "Divide"), NULL },
+ { GIMP_LAYER_MODE_DODGE, NC_("layer-mode", "Dodge"), NULL },
+ { GIMP_LAYER_MODE_BURN, NC_("layer-mode", "Burn"), NULL },
+ { GIMP_LAYER_MODE_HARDLIGHT, NC_("layer-mode", "Hard light"), NULL },
+ { GIMP_LAYER_MODE_SOFTLIGHT, NC_("layer-mode", "Soft light"), NULL },
+ { GIMP_LAYER_MODE_GRAIN_EXTRACT, NC_("layer-mode", "Grain extract"), NULL },
+ { GIMP_LAYER_MODE_GRAIN_MERGE, NC_("layer-mode", "Grain merge"), NULL },
+ { GIMP_LAYER_MODE_VIVID_LIGHT, NC_("layer-mode", "Vivid light"), NULL },
+ { GIMP_LAYER_MODE_PIN_LIGHT, NC_("layer-mode", "Pin light"), NULL },
+ { GIMP_LAYER_MODE_LINEAR_LIGHT, NC_("layer-mode", "Linear light"), NULL },
+ { GIMP_LAYER_MODE_HARD_MIX, NC_("layer-mode", "Hard mix"), NULL },
+ { GIMP_LAYER_MODE_EXCLUSION, NC_("layer-mode", "Exclusion"), NULL },
+ { GIMP_LAYER_MODE_LINEAR_BURN, NC_("layer-mode", "Linear burn"), NULL },
+ { GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, NC_("layer-mode", "Luma/Luminance darken only"), NULL },
+ /* Translators: this is an abbreviated version of "Luma/Luminance darken only".
+ Keep it short. */
+ { GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, NC_("layer-mode", "Luma darken only"), NULL },
+ { GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, NC_("layer-mode", "Luma/Luminance lighten only"), NULL },
+ /* Translators: this is an abbreviated version of "Luma/Luminance lighten only".
+ Keep it short. */
+ { GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, NC_("layer-mode", "Luma lighten only"), NULL },
+ { GIMP_LAYER_MODE_LUMINANCE, NC_("layer-mode", "Luminance"), NULL },
+ { GIMP_LAYER_MODE_COLOR_ERASE, NC_("layer-mode", "Color erase"), NULL },
+ { GIMP_LAYER_MODE_ERASE, NC_("layer-mode", "Erase"), NULL },
+ { GIMP_LAYER_MODE_MERGE, NC_("layer-mode", "Merge"), NULL },
+ { 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_ANTI_ERASE, NC_("layer-mode", "Anti erase"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpLayerMode", values);
+ gimp_type_set_translation_context (type, "layer-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_layer_mode_group_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_LAYER_MODE_GROUP_DEFAULT, "GIMP_LAYER_MODE_GROUP_DEFAULT", "default" },
+ { GIMP_LAYER_MODE_GROUP_LEGACY, "GIMP_LAYER_MODE_GROUP_LEGACY", "legacy" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_LAYER_MODE_GROUP_DEFAULT, NC_("layer-mode-group", "Default"), NULL },
+ { GIMP_LAYER_MODE_GROUP_LEGACY, NC_("layer-mode-group", "Legacy"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpLayerModeGroup", values);
+ gimp_type_set_translation_context (type, "layer-mode-group");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_layer_mode_context_get_type (void)
+{
+ static const GFlagsValue values[] =
+ {
+ { GIMP_LAYER_MODE_CONTEXT_LAYER, "GIMP_LAYER_MODE_CONTEXT_LAYER", "layer" },
+ { GIMP_LAYER_MODE_CONTEXT_GROUP, "GIMP_LAYER_MODE_CONTEXT_GROUP", "group" },
+ { GIMP_LAYER_MODE_CONTEXT_PAINT, "GIMP_LAYER_MODE_CONTEXT_PAINT", "paint" },
+ { GIMP_LAYER_MODE_CONTEXT_FILTER, "GIMP_LAYER_MODE_CONTEXT_FILTER", "filter" },
+ { GIMP_LAYER_MODE_CONTEXT_ALL, "GIMP_LAYER_MODE_CONTEXT_ALL", "all" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpFlagsDesc descs[] =
+ {
+ { GIMP_LAYER_MODE_CONTEXT_LAYER, "GIMP_LAYER_MODE_CONTEXT_LAYER", NULL },
+ { GIMP_LAYER_MODE_CONTEXT_GROUP, "GIMP_LAYER_MODE_CONTEXT_GROUP", NULL },
+ { GIMP_LAYER_MODE_CONTEXT_PAINT, "GIMP_LAYER_MODE_CONTEXT_PAINT", NULL },
+ { GIMP_LAYER_MODE_CONTEXT_FILTER, "GIMP_LAYER_MODE_CONTEXT_FILTER", NULL },
+ { GIMP_LAYER_MODE_CONTEXT_ALL, "GIMP_LAYER_MODE_CONTEXT_ALL", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_flags_register_static ("GimpLayerModeContext", values);
+ gimp_type_set_translation_context (type, "layer-mode-context");
+ gimp_flags_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+
+/* Generated data ends here */
+
diff --git a/app/operations/operations-enums.h b/app/operations/operations-enums.h
new file mode 100644
index 0000000..0dc51b9
--- /dev/null
+++ b/app/operations/operations-enums.h
@@ -0,0 +1,190 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * operations-enums.h
+ *
+ * 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/>.
+ */
+
+#ifndef __OPERATIONS_ENUMS_H__
+#define __OPERATIONS_ENUMS_H__
+
+
+#define GIMP_TYPE_LAYER_COLOR_SPACE (gimp_layer_color_space_get_type ())
+
+GType gimp_layer_color_space_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_LAYER_COLOR_SPACE_AUTO, /*< desc="Auto" >*/
+ GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, /*< desc="RGB (linear)" >*/
+ GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, /*< desc="RGB (perceptual)" >*/
+ GIMP_LAYER_COLOR_SPACE_LAB, /*< desc="LAB", pdb-skip >*/
+} GimpLayerColorSpace;
+
+
+#define GIMP_TYPE_LAYER_COMPOSITE_MODE (gimp_layer_composite_mode_get_type ())
+
+GType gimp_layer_composite_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_LAYER_COMPOSITE_AUTO, /*< desc="Auto" >*/
+ GIMP_LAYER_COMPOSITE_UNION, /*< desc="Union" >*/
+ GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, /*< desc="Clip to backdrop" >*/
+ GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER, /*< desc="Clip to layer" >*/
+ GIMP_LAYER_COMPOSITE_INTERSECTION /*< desc="Intersection" >*/
+} GimpLayerCompositeMode;
+
+
+#define GIMP_TYPE_LAYER_MODE (gimp_layer_mode_get_type ())
+
+GType gimp_layer_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ /* Modes that exist since ancient times */
+ GIMP_LAYER_MODE_NORMAL_LEGACY, /*< desc="Normal (legacy)", abbrev="Normal (l)" >*/
+ GIMP_LAYER_MODE_DISSOLVE, /*< desc="Dissolve" >*/
+ GIMP_LAYER_MODE_BEHIND_LEGACY, /*< desc="Behind (legacy)", abbrev="Behind (l)" >*/
+ GIMP_LAYER_MODE_MULTIPLY_LEGACY, /*< desc="Multiply (legacy)", abbrev="Multiply (l)" >*/
+ GIMP_LAYER_MODE_SCREEN_LEGACY, /*< desc="Screen (legacy)", abbrev="Screen (l)" >*/
+ GIMP_LAYER_MODE_OVERLAY_LEGACY, /*< desc="Old broken Overlay", abbrev="Old Overlay" >*/
+ GIMP_LAYER_MODE_DIFFERENCE_LEGACY, /*< desc="Difference (legacy)", abbrev="Difference (l)" >*/
+ GIMP_LAYER_MODE_ADDITION_LEGACY, /*< desc="Addition (legacy)", abbrev="Addition (l)" >*/
+ GIMP_LAYER_MODE_SUBTRACT_LEGACY, /*< desc="Subtract (legacy)", abbrev="Subtract (l)" >*/
+ GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, /*< desc="Darken only (legacy)", abbrev="Darken only (l)" >*/
+ GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, /*< desc="Lighten only (legacy)", abbrev="Lighten only (l)" >*/
+ GIMP_LAYER_MODE_HSV_HUE_LEGACY, /*< desc="HSV Hue (legacy)", abbrev="HSV Hue (l)" >*/
+ GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, /*< desc="HSV Saturation (legacy)", abbrev="HSV Saturation (l)" >*/
+ GIMP_LAYER_MODE_HSL_COLOR_LEGACY, /*< desc="HSL Color (legacy)", abbrev="HSL Color (l)" >*/
+ GIMP_LAYER_MODE_HSV_VALUE_LEGACY, /*< desc="HSV Value (legacy)", abbrev="HSV Value (l)" >*/
+ GIMP_LAYER_MODE_DIVIDE_LEGACY, /*< desc="Divide (legacy)", abbrev="Divide (l)" >*/
+ GIMP_LAYER_MODE_DODGE_LEGACY, /*< desc="Dodge (legacy)", abbrev="Dodge (l)" >*/
+ GIMP_LAYER_MODE_BURN_LEGACY, /*< desc="Burn (legacy)", abbrev="Burn (l)" >*/
+ GIMP_LAYER_MODE_HARDLIGHT_LEGACY, /*< desc="Hard light (legacy)", abbrev="Hard light (l)" >*/
+
+ /* Since 2.8 (XCF version 2) */
+ GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, /*< desc="Soft light (legacy)", abbrev="Soft light (l)" >*/
+ GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY, /*< desc="Grain extract (legacy)", abbrev="Grain extract (l)" >*/
+ GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY, /*< desc="Grain merge (legacy)", abbrev="Grain merge (l)" >*/
+ GIMP_LAYER_MODE_COLOR_ERASE_LEGACY, /*< desc="Color erase (legacy)", abbrev="Color erase (l)" >*/
+
+ /* Since 2.10 (XCF version 9) */
+ GIMP_LAYER_MODE_OVERLAY, /*< desc="Overlay" >*/
+ GIMP_LAYER_MODE_LCH_HUE, /*< desc="LCh Hue" >*/
+ GIMP_LAYER_MODE_LCH_CHROMA, /*< desc="LCh Chroma" >*/
+ GIMP_LAYER_MODE_LCH_COLOR, /*< desc="LCh Color" >*/
+ GIMP_LAYER_MODE_LCH_LIGHTNESS, /*< desc="LCh Lightness" >*/
+
+ /* Since 2.10 (XCF version 10) */
+ GIMP_LAYER_MODE_NORMAL, /*< desc="Normal" >*/
+ GIMP_LAYER_MODE_BEHIND, /*< desc="Behind" >*/
+ GIMP_LAYER_MODE_MULTIPLY, /*< desc="Multiply" >*/
+ GIMP_LAYER_MODE_SCREEN, /*< desc="Screen" >*/
+ GIMP_LAYER_MODE_DIFFERENCE, /*< desc="Difference" >*/
+ GIMP_LAYER_MODE_ADDITION, /*< desc="Addition" >*/
+ GIMP_LAYER_MODE_SUBTRACT, /*< desc="Subtract" >*/
+ GIMP_LAYER_MODE_DARKEN_ONLY, /*< desc="Darken only" >*/
+ GIMP_LAYER_MODE_LIGHTEN_ONLY, /*< desc="Lighten only" >*/
+ GIMP_LAYER_MODE_HSV_HUE, /*< desc="HSV Hue" >*/
+ GIMP_LAYER_MODE_HSV_SATURATION, /*< desc="HSV Saturation" >*/
+ GIMP_LAYER_MODE_HSL_COLOR, /*< desc="HSL Color" >*/
+ GIMP_LAYER_MODE_HSV_VALUE, /*< desc="HSV Value" >*/
+ GIMP_LAYER_MODE_DIVIDE, /*< desc="Divide" >*/
+ GIMP_LAYER_MODE_DODGE, /*< desc="Dodge" >*/
+ GIMP_LAYER_MODE_BURN, /*< desc="Burn" >*/
+ GIMP_LAYER_MODE_HARDLIGHT, /*< desc="Hard light" >*/
+ GIMP_LAYER_MODE_SOFTLIGHT, /*< desc="Soft light" >*/
+ GIMP_LAYER_MODE_GRAIN_EXTRACT, /*< desc="Grain extract" >*/
+ GIMP_LAYER_MODE_GRAIN_MERGE, /*< desc="Grain merge" >*/
+ GIMP_LAYER_MODE_VIVID_LIGHT, /*< desc="Vivid light" >*/
+ GIMP_LAYER_MODE_PIN_LIGHT, /*< desc="Pin light" >*/
+ GIMP_LAYER_MODE_LINEAR_LIGHT, /*< desc="Linear light" >*/
+ GIMP_LAYER_MODE_HARD_MIX, /*< desc="Hard mix" >*/
+ GIMP_LAYER_MODE_EXCLUSION, /*< desc="Exclusion" >*/
+ GIMP_LAYER_MODE_LINEAR_BURN, /*< desc="Linear burn" >*/
+ GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, /*< desc="Luma/Luminance darken only", abbrev="Luma darken only" >*/
+ GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, /*< desc="Luma/Luminance lighten only", abbrev="Luma lighten only" >*/
+ GIMP_LAYER_MODE_LUMINANCE, /*< desc="Luminance" >*/
+ GIMP_LAYER_MODE_COLOR_ERASE, /*< desc="Color erase" >*/
+ GIMP_LAYER_MODE_ERASE, /*< desc="Erase" >*/
+ GIMP_LAYER_MODE_MERGE, /*< desc="Merge" >*/
+ GIMP_LAYER_MODE_SPLIT, /*< desc="Split" >*/
+ GIMP_LAYER_MODE_PASS_THROUGH, /*< desc="Pass through" >*/
+
+ /* Internal modes, not available to the PDB, must be kept at the end */
+ GIMP_LAYER_MODE_REPLACE, /*< pdb-skip, desc="Replace" >*/
+ GIMP_LAYER_MODE_ANTI_ERASE, /*< pdb-skip, desc="Anti erase" >*/
+
+ /* Layer mode menu separator */
+ GIMP_LAYER_MODE_SEPARATOR = -1 /*< pdb-skip, skip >*/
+} GimpLayerMode;
+
+
+#define GIMP_TYPE_LAYER_MODE_GROUP (gimp_layer_mode_group_get_type ())
+
+GType gimp_layer_mode_group_get_type (void) G_GNUC_CONST;
+
+typedef enum /*< pdb-skip >*/
+{
+ GIMP_LAYER_MODE_GROUP_DEFAULT, /*< desc="Default" >*/
+ GIMP_LAYER_MODE_GROUP_LEGACY, /*< desc="Legacy" >*/
+} GimpLayerModeGroup;
+
+
+#define GIMP_TYPE_LAYER_MODE_CONTEXT (gimp_layer_mode_context_get_type ())
+
+GType gimp_layer_mode_context_get_type (void) G_GNUC_CONST;
+
+typedef enum /*< pdb-skip >*/
+{
+ GIMP_LAYER_MODE_CONTEXT_LAYER = 1 << 0,
+ GIMP_LAYER_MODE_CONTEXT_GROUP = 1 << 1,
+ GIMP_LAYER_MODE_CONTEXT_PAINT = 1 << 2,
+ GIMP_LAYER_MODE_CONTEXT_FILTER = 1 << 3,
+
+ GIMP_LAYER_MODE_CONTEXT_ALL = (GIMP_LAYER_MODE_CONTEXT_LAYER |
+ GIMP_LAYER_MODE_CONTEXT_GROUP |
+ GIMP_LAYER_MODE_CONTEXT_PAINT |
+ GIMP_LAYER_MODE_CONTEXT_FILTER)
+} GimpLayerModeContext;
+
+
+/*
+ * non-registered enums; register them if needed
+ */
+
+typedef enum /*< pdb-skip, skip >*/
+{
+ GIMP_LAYER_COMPOSITE_REGION_INTERSECTION = 0,
+ GIMP_LAYER_COMPOSITE_REGION_DESTINATION = 1 << 0,
+ GIMP_LAYER_COMPOSITE_REGION_SOURCE = 1 << 1,
+ GIMP_LAYER_COMPOSITE_REGION_UNION = (GIMP_LAYER_COMPOSITE_REGION_DESTINATION |
+ GIMP_LAYER_COMPOSITE_REGION_SOURCE),
+} GimpLayerCompositeRegion;
+
+typedef enum /*< pdb-skip, skip >*/
+{
+ GIMP_LAYER_MODE_FLAG_LEGACY = 1 << 0,
+ GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE = 1 << 1,
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE = 1 << 2,
+ GIMP_LAYER_MODE_FLAG_COMPOSITE_MODE_IMMUTABLE = 1 << 3,
+ GIMP_LAYER_MODE_FLAG_SUBTRACTIVE = 1 << 4,
+ GIMP_LAYER_MODE_FLAG_ALPHA_ONLY = 1 << 5,
+ GIMP_LAYER_MODE_FLAG_TRIVIAL = 1 << 6
+} GimpLayerModeFlags;
+
+
+#endif /* __OPERATIONS_ENUMS_H__ */
diff --git a/app/operations/operations-types.h b/app/operations/operations-types.h
new file mode 100644
index 0000000..15e97d8
--- /dev/null
+++ b/app/operations/operations-types.h
@@ -0,0 +1,76 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * operations-types.h
+ *
+ * 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/>.
+ */
+
+#ifndef __OPERATIONS_TYPES_H__
+#define __OPERATIONS_TYPES_H__
+
+
+#include <gegl-types.h>
+
+#include "gegl/gimp-gegl-types.h"
+
+#include "operations-enums.h"
+
+
+/* operations */
+
+typedef struct _GimpOperationPointFilter GimpOperationPointFilter;
+typedef struct _GimpOperationLayerMode GimpOperationLayerMode;
+
+
+/* operation config objects */
+
+typedef struct _GimpOperationSettings GimpOperationSettings;
+
+typedef struct _GimpBrightnessContrastConfig GimpBrightnessContrastConfig;
+typedef struct _GimpCageConfig GimpCageConfig;
+typedef struct _GimpColorBalanceConfig GimpColorBalanceConfig;
+typedef struct _GimpColorizeConfig GimpColorizeConfig;
+typedef struct _GimpCurvesConfig GimpCurvesConfig;
+typedef struct _GimpDesaturateConfig GimpDesaturateConfig;
+typedef struct _GimpHueSaturationConfig GimpHueSaturationConfig;
+typedef struct _GimpLevelsConfig GimpLevelsConfig;
+typedef struct _GimpPosterizeConfig GimpPosterizeConfig;
+typedef struct _GimpThresholdConfig GimpThresholdConfig;
+
+
+/* non-object types */
+
+typedef struct _GimpCagePoint GimpCagePoint;
+
+
+/* functions */
+
+typedef gboolean (* GimpLayerModeFunc) (GeglOperation *operation,
+ void *in,
+ void *aux,
+ void *mask,
+ void *out,
+ glong samples,
+ const GeglRectangle *roi,
+ gint level);
+
+typedef void (* GimpLayerModeBlendFunc) (GeglOperation *operation,
+ const gfloat *in,
+ const gfloat *layer,
+ gfloat *out,
+ gint samples);
+
+
+#endif /* __OPERATIONS_TYPES_H__ */
diff --git a/app/operations/tests/Makefile.am b/app/operations/tests/Makefile.am
new file mode 100644
index 0000000..fdf0a9a
--- /dev/null
+++ b/app/operations/tests/Makefile.am
@@ -0,0 +1,71 @@
+#TESTS = test-operations
+
+EXTRA_PROGRAMS = $(TESTS)
+CLEANFILES = $(EXTRA_PROGRAMS)
+
+$(TESTS): output-dir
+
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+libgimpmodule = $(top_builddir)/libgimpmodule/libgimpmodule-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimpthumb = $(top_builddir)/libgimpthumb/libgimpthumb-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+else
+libm = -lm
+endif
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/app \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+# We need this due to circular dependencies, see more detailed
+# comments about it in app/Makefile.am
+AM_LDFLAGS = \
+ -Wl,-u,$(SYMPREFIX)xcf_init \
+ -Wl,-u,$(SYMPREFIX)internal_procs_init \
+ -Wl,-u,$(SYMPREFIX)gimp_plug_in_manager_restore \
+ -Wl,-u,$(SYMPREFIX)gimp_pdb_compat_param_spec \
+ -Wl,-u,$(SYMPREFIX)gimp_vectors_undo_get_type \
+ -Wl,-u,$(SYMPREFIX)gimp_vectors_mod_undo_get_type \
+ -Wl,-u,$(SYMPREFIX)gimp_vectors_prop_undo_get_type
+
+# Note that we have some duplicate entries here too to work around
+# circular dependencies and systems on the same architectural layer as
+# an alternative to LDFLAGS above
+LDADD = \
+ $(top_builddir)/app/xcf/libappxcf.a \
+ $(top_builddir)/app/pdb/libappinternal-procs.a \
+ $(top_builddir)/app/pdb/libapppdb.a \
+ $(top_builddir)/app/plug-in/libappplug-in.a \
+ $(top_builddir)/app/vectors/libappvectors.a \
+ $(top_builddir)/app/core/libappcore.a \
+ $(top_builddir)/app/file/libappfile.a \
+ $(top_builddir)/app/text/libapptext.a \
+ $(top_builddir)/app/paint/libapppaint.a \
+ $(top_builddir)/app/config/libappconfig.a \
+ $(top_builddir)/app/libapp.a \
+ $(top_builddir)/app/gegl/libappgegl.a \
+ $(top_builddir)/app/operations/libappoperations.a \
+ $(libgimpconfig) \
+ $(libgimpmath) \
+ $(libgimpthumb) \
+ $(libgimpcolor) \
+ $(libgimpmodule) \
+ $(libgimpbase) \
+ $(GDK_PIXBUF_LIBS) \
+ $(PANGOCAIRO_LIBS) \
+ $(GEGL_LIBS) \
+ $(GLIB_LIBS) \
+ $(libm)
+
+output-dir:
+ mkdir -p output
+
+clean-local:
+ rm -rf output
diff --git a/app/operations/tests/Makefile.in b/app/operations/tests/Makefile.in
new file mode 100644
index 0000000..41af881
--- /dev/null
+++ b/app/operations/tests/Makefile.in
@@ -0,0 +1,815 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#TESTS = test-operations
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+EXTRA_PROGRAMS =
+subdir = app/operations/tests
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+CLEANFILES = $(EXTRA_PROGRAMS)
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+libgimpmodule = $(top_builddir)/libgimpmodule/libgimpmodule-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimpthumb = $(top_builddir)/libgimpthumb/libgimpthumb-$(GIMP_API_VERSION).la
+@OS_WIN32_FALSE@libm = -lm
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/app \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+
+# We need this due to circular dependencies, see more detailed
+# comments about it in app/Makefile.am
+AM_LDFLAGS = \
+ -Wl,-u,$(SYMPREFIX)xcf_init \
+ -Wl,-u,$(SYMPREFIX)internal_procs_init \
+ -Wl,-u,$(SYMPREFIX)gimp_plug_in_manager_restore \
+ -Wl,-u,$(SYMPREFIX)gimp_pdb_compat_param_spec \
+ -Wl,-u,$(SYMPREFIX)gimp_vectors_undo_get_type \
+ -Wl,-u,$(SYMPREFIX)gimp_vectors_mod_undo_get_type \
+ -Wl,-u,$(SYMPREFIX)gimp_vectors_prop_undo_get_type
+
+
+# Note that we have some duplicate entries here too to work around
+# circular dependencies and systems on the same architectural layer as
+# an alternative to LDFLAGS above
+LDADD = \
+ $(top_builddir)/app/xcf/libappxcf.a \
+ $(top_builddir)/app/pdb/libappinternal-procs.a \
+ $(top_builddir)/app/pdb/libapppdb.a \
+ $(top_builddir)/app/plug-in/libappplug-in.a \
+ $(top_builddir)/app/vectors/libappvectors.a \
+ $(top_builddir)/app/core/libappcore.a \
+ $(top_builddir)/app/file/libappfile.a \
+ $(top_builddir)/app/text/libapptext.a \
+ $(top_builddir)/app/paint/libapppaint.a \
+ $(top_builddir)/app/config/libappconfig.a \
+ $(top_builddir)/app/libapp.a \
+ $(top_builddir)/app/gegl/libappgegl.a \
+ $(top_builddir)/app/operations/libappoperations.a \
+ $(libgimpconfig) \
+ $(libgimpmath) \
+ $(libgimpthumb) \
+ $(libgimpcolor) \
+ $(libgimpmodule) \
+ $(libgimpbase) \
+ $(GDK_PIXBUF_LIBS) \
+ $(PANGOCAIRO_LIBS) \
+ $(GEGL_LIBS) \
+ $(GLIB_LIBS) \
+ $(libm)
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu app/operations/tests/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu app/operations/tests/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-local mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ clean-local cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+$(TESTS): output-dir
+
+output-dir:
+ mkdir -p output
+
+clean-local:
+ rm -rf output
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT: