diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:24:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:24:48 +0000 |
commit | cca66b9ec4e494c1d919bff0f71a820d8afab1fa (patch) | |
tree | 146f39ded1c938019e1ed42d30923c2ac9e86789 /src/display/nr-filter-component-transfer.cpp | |
parent | Initial commit. (diff) | |
download | inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.tar.xz inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.zip |
Adding upstream version 1.2.2.upstream/1.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/display/nr-filter-component-transfer.cpp | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/src/display/nr-filter-component-transfer.cpp b/src/display/nr-filter-component-transfer.cpp new file mode 100644 index 0000000..9591285 --- /dev/null +++ b/src/display/nr-filter-component-transfer.cpp @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * feComponentTransfer filter primitive renderer + * + * Authors: + * Felipe CorrĂȘa da Silva Sanches <juca@members.fsf.org> + * Jasper van de Gronde <th.v.d.gronde@hccnet.nl> + * + * 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-component-transfer.h" +#include "display/nr-filter-slot.h" + +namespace Inkscape { +namespace Filters { + +FilterComponentTransfer::FilterComponentTransfer() += default; + +FilterPrimitive * FilterComponentTransfer::create() { + return new FilterComponentTransfer(); +} + +FilterComponentTransfer::~FilterComponentTransfer() += default; + +struct UnmultiplyAlpha { + guint32 operator()(guint32 in) { + EXTRACT_ARGB32(in, a, r, g, b); + if (a == 0 ) + return in; + r = unpremul_alpha(r, a); + g = unpremul_alpha(g, a); + b = unpremul_alpha(b, a); + ASSEMBLE_ARGB32(out, a, r, g, b); + return out; + } +}; + +struct MultiplyAlpha { + guint32 operator()(guint32 in) { + EXTRACT_ARGB32(in, a, r, g, b); + r = premul_alpha(r, a); + g = premul_alpha(g, a); + b = premul_alpha(b, a); + ASSEMBLE_ARGB32(out, a, r, g, b); + return out; + } +}; + +struct ComponentTransfer { + ComponentTransfer(guint32 color) + : _shift(color * 8) + , _mask(0xff << _shift) + {} +protected: + guint32 _shift; + guint32 _mask; +}; + +struct ComponentTransferTable : public ComponentTransfer { + ComponentTransferTable(guint32 color, std::vector<double> const &values) + : ComponentTransfer(color) + , _v(values.size()) + { + for (unsigned i = 0; i< values.size(); ++i) { + _v[i] = round(CLAMP(values[i], 0.0, 1.0) * 255); + } + } + guint32 operator()(guint32 in) { + if (_v.empty()) { + return in; + } + + guint32 component = (in & _mask) >> _shift; + if (_v.size() == 1 || component == 255) { + component = _v.back(); + } else { + guint32 k = ((_v.size() - 1) * component); + guint32 dx = k % 255; + k /= 255; + component = _v[k]*255 + (_v[k+1] - _v[k])*dx; + component = (component + 127) / 255; + } + return (in & ~_mask) | (component << _shift); + } +private: + std::vector<guint32> _v; +}; + +struct ComponentTransferDiscrete : public ComponentTransfer { + ComponentTransferDiscrete(guint32 color, std::vector<double> const &values) + : ComponentTransfer(color) + , _v(values.size()) + { + for (unsigned i = 0; i< values.size(); ++i) { + _v[i] = round(CLAMP(values[i], 0.0, 1.0) * 255); + } + } + guint32 operator()(guint32 in) { + guint32 component = (in & _mask) >> _shift; + guint32 k = (_v.size()) * component / 255; + if( k == _v.size() ) --k; + component = _v[k]; + return (in & ~_mask) | ((guint32)component << _shift); + } +private: + std::vector<guint32> _v; +}; + +struct ComponentTransferLinear : public ComponentTransfer { + ComponentTransferLinear(guint32 color, double intercept, double slope) + : ComponentTransfer(color) + , _intercept(round(intercept*255*255)) + , _slope(round(slope*255)) + {} + guint32 operator()(guint32 in) { + gint32 component = (in & _mask) >> _shift; + + // TODO: this can probably be reduced to something simpler + component = pxclamp(_slope * component + _intercept, 0, 255*255); + component = (component + 127) / 255; + return (in & ~_mask) | (component << _shift); + } +private: + gint32 _intercept; + gint32 _slope; +}; + +struct ComponentTransferGamma : public ComponentTransfer { + ComponentTransferGamma(guint32 color, double amplitude, double exponent, double offset) + : ComponentTransfer(color) + , _amplitude(amplitude) + , _exponent(exponent) + , _offset(offset) + {} + guint32 operator()(guint32 in) { + double component = (in & _mask) >> _shift; + component /= 255.0; + component = _amplitude * pow(component, _exponent) + _offset; + guint32 cpx = pxclamp(component * 255.0, 0, 255); + return (in & ~_mask) | (cpx << _shift); + } +private: + double _amplitude; + double _exponent; + double _offset; +}; + +void FilterComponentTransfer::render_cairo(FilterSlot &slot) +{ + cairo_surface_t *input = slot.getcairo(_input); + cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA); + + // 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(out, ci_fp ); + } + set_cairo_surface_ci( input, ci_fp ); + + //cairo_surface_t *outtemp = ink_cairo_surface_create_identical(out); + ink_cairo_surface_blit(input, out); + + // We need to operate on unmultipled by alpha color values otherwise a change in alpha screws + // up the premultiplied by alpha r, g, b values. + ink_cairo_surface_filter(out, out, UnmultiplyAlpha()); + + // parameters: R = 0, G = 1, B = 2, A = 3 + // Cairo: R = 2, G = 1, B = 0, A = 3 + // If tableValues is empty, use identity. + for (unsigned i = 0; i < 4; ++i) { + + guint32 color = 2 - i; + if(i==3) color = 3; // alpha + + switch (type[i]) { + case COMPONENTTRANSFER_TYPE_TABLE: + if(!tableValues[i].empty()) { + ink_cairo_surface_filter(out, out, + ComponentTransferTable(color, tableValues[i])); + } + break; + case COMPONENTTRANSFER_TYPE_DISCRETE: + if(!tableValues[i].empty()) { + ink_cairo_surface_filter(out, out, + ComponentTransferDiscrete(color, tableValues[i])); + } + break; + case COMPONENTTRANSFER_TYPE_LINEAR: + ink_cairo_surface_filter(out, out, + ComponentTransferLinear(color, intercept[i], slope[i])); + break; + case COMPONENTTRANSFER_TYPE_GAMMA: + ink_cairo_surface_filter(out, out, + ComponentTransferGamma(color, amplitude[i], exponent[i], offset[i])); + break; + case COMPONENTTRANSFER_TYPE_ERROR: + case COMPONENTTRANSFER_TYPE_IDENTITY: + default: + break; + } + //ink_cairo_surface_blit(out, outtemp); + } + + ink_cairo_surface_filter(out, out, MultiplyAlpha()); + + slot.set(_output, out); + cairo_surface_destroy(out); + //cairo_surface_destroy(outtemp); +} + +bool FilterComponentTransfer::can_handle_affine(Geom::Affine const &) +{ + return true; +} + +double FilterComponentTransfer::complexity(Geom::Affine const &) +{ + return 2.0; +} + +} /* 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 : |