diff options
Diffstat (limited to '')
-rw-r--r-- | src/display/nr-filter-composite.cpp | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/src/display/nr-filter-composite.cpp b/src/display/nr-filter-composite.cpp new file mode 100644 index 0000000..50ec9f7 --- /dev/null +++ b/src/display/nr-filter-composite.cpp @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * feComposite filter effect renderer + * + * Authors: + * Niko Kiirala <niko@kiirala.com> + * + * Copyright (C) 2007 authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include <cmath> + +#include "display/cairo-templates.h" +#include "display/cairo-utils.h" +#include "display/nr-filter-composite.h" +#include "display/nr-filter-slot.h" +#include "display/nr-filter-units.h" + +namespace Inkscape { +namespace Filters { + +FilterComposite::FilterComposite() : + op(COMPOSITE_DEFAULT), k1(0), k2(0), k3(0), k4(0), + _input2(Inkscape::Filters::NR_FILTER_SLOT_NOT_SET) +{} + +FilterPrimitive * FilterComposite::create() { + return new FilterComposite(); +} + +FilterComposite::~FilterComposite() += default; + +struct ComposeArithmetic { + ComposeArithmetic(double k1, double k2, double k3, double k4) + : _k1(round(k1 * 255)) + , _k2(round(k2 * 255*255)) + , _k3(round(k3 * 255*255)) + , _k4(round(k4 * 255*255*255)) + {} + guint32 operator()(guint32 in1, guint32 in2) { + EXTRACT_ARGB32(in1, aa, ra, ga, ba) + EXTRACT_ARGB32(in2, ab, rb, gb, bb) + + gint32 ao = _k1*aa*ab + _k2*aa + _k3*ab + _k4; + gint32 ro = _k1*ra*rb + _k2*ra + _k3*rb + _k4; + gint32 go = _k1*ga*gb + _k2*ga + _k3*gb + _k4; + gint32 bo = _k1*ba*bb + _k2*ba + _k3*bb + _k4; + + ao = pxclamp(ao, 0, 255*255*255); // r, g and b are premultiplied, so should be clamped to the alpha channel + ro = (pxclamp(ro, 0, ao) + (255*255/2)) / (255*255); + go = (pxclamp(go, 0, ao) + (255*255/2)) / (255*255); + bo = (pxclamp(bo, 0, ao) + (255*255/2)) / (255*255); + ao = (ao + (255*255/2)) / (255*255); + + ASSEMBLE_ARGB32(pxout, ao, ro, go, bo) + return pxout; + } +private: + gint32 _k1, _k2, _k3, _k4; +}; + +void FilterComposite::render_cairo(FilterSlot &slot) +{ + cairo_surface_t *input1 = slot.getcairo(_input); + cairo_surface_t *input2 = slot.getcairo(_input2); + + // We may need to transform input surface to correct color interpolation space. The input surface + // might be used as input to another primitive but it is likely that all the primitives in a given + // filter use the same color interpolation space so we don't copy the input before converting. + SPColorInterpolation ci_fp = SP_CSS_COLOR_INTERPOLATION_AUTO; + if( _style ) { + ci_fp = (SPColorInterpolation)_style->color_interpolation_filters.computed; + } + set_cairo_surface_ci( input1, ci_fp ); + set_cairo_surface_ci( input2, ci_fp ); + + cairo_surface_t *out = ink_cairo_surface_create_output(input1, input2); + set_cairo_surface_ci(out, ci_fp ); + + Geom::Rect vp = filter_primitive_area( slot.get_units() ); + slot.set_primitive_area(_output, vp); // Needed for tiling + + if (op == COMPOSITE_ARITHMETIC) { + ink_cairo_surface_blend(input1, input2, out, ComposeArithmetic(k1, k2, k3, k4)); + } else { + ink_cairo_surface_blit(input2, out); + cairo_t *ct = cairo_create(out); + cairo_set_source_surface(ct, input1, 0, 0); + switch(op) { + case COMPOSITE_IN: + cairo_set_operator(ct, CAIRO_OPERATOR_IN); + break; + case COMPOSITE_OUT: + cairo_set_operator(ct, CAIRO_OPERATOR_OUT); + break; + case COMPOSITE_ATOP: + cairo_set_operator(ct, CAIRO_OPERATOR_ATOP); + break; + case COMPOSITE_XOR: + cairo_set_operator(ct, CAIRO_OPERATOR_XOR); + break; + case COMPOSITE_LIGHTER: + cairo_set_operator(ct, CAIRO_OPERATOR_ADD); + break; + case COMPOSITE_OVER: + case COMPOSITE_DEFAULT: + default: + // OVER is the default operator + break; + } + cairo_paint(ct); + cairo_destroy(ct); + } + + slot.set(_output, out); + cairo_surface_destroy(out); +} + +bool FilterComposite::can_handle_affine(Geom::Affine const &) +{ + return true; +} + +void FilterComposite::set_input(int input) { + _input = input; +} + +void FilterComposite::set_input(int input, int slot) { + if (input == 0) _input = slot; + if (input == 1) _input2 = slot; +} + +void FilterComposite::set_operator(FeCompositeOperator op) { + if (op == COMPOSITE_DEFAULT) { + this->op = COMPOSITE_OVER; + } else if (op != COMPOSITE_ENDOPERATOR) { + this->op = op; + } +} + +void FilterComposite::set_arithmetic(double k1, double k2, double k3, double k4) { + if (!std::isfinite(k1) || !std::isfinite(k2) || !std::isfinite(k3) || !std::isfinite(k4)) { + g_warning("Non-finite parameter for feComposite arithmetic operator"); + return; + } + this->k1 = k1; + this->k2 = k2; + this->k3 = k3; + this->k4 = k4; +} + +double FilterComposite::complexity(Geom::Affine const &) +{ + return 1.1; +} + +} /* namespace Filters */ +} /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |