summaryrefslogtreecommitdiffstats
path: root/src/display/nr-filter-component-transfer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/display/nr-filter-component-transfer.cpp')
-rw-r--r--src/display/nr-filter-component-transfer.cpp244
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 :