summaryrefslogtreecommitdiffstats
path: root/src/display/nr-filter-displacement-map.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/display/nr-filter-displacement-map.cpp')
-rw-r--r--src/display/nr-filter-displacement-map.cpp172
1 files changed, 172 insertions, 0 deletions
diff --git a/src/display/nr-filter-displacement-map.cpp b/src/display/nr-filter-displacement-map.cpp
new file mode 100644
index 0000000..9d83e54
--- /dev/null
+++ b/src/display/nr-filter-displacement-map.cpp
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * feDisplacementMap filter primitive renderer
+ *
+ * Authors:
+ * Felipe CorrĂȘa da Silva Sanches <juca@members.fsf.org>
+ *
+ * Copyright (C) 2007 authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "display/cairo-templates.h"
+#include "display/cairo-utils.h"
+#include "display/nr-filter-displacement-map.h"
+#include "display/nr-filter-types.h"
+#include "display/nr-filter-units.h"
+
+namespace Inkscape {
+namespace Filters {
+
+FilterDisplacementMap::FilterDisplacementMap()
+= default;
+
+FilterPrimitive * FilterDisplacementMap::create() {
+ return new FilterDisplacementMap();
+}
+
+FilterDisplacementMap::~FilterDisplacementMap()
+= default;
+
+struct Displace {
+ Displace(cairo_surface_t *texture, cairo_surface_t *map,
+ unsigned xch, unsigned ych, double scalex, double scaley)
+ : _texture(texture)
+ , _map(map)
+ , _xch(xch)
+ , _ych(ych)
+ , _scalex(scalex/255.0)
+ , _scaley(scaley/255.0)
+ {}
+ guint32 operator()(int x, int y) {
+ guint32 mappx = _map.pixelAt(x, y);
+ guint32 a = (mappx & 0xff000000) >> 24;
+ guint32 xpx = 0, ypx = 0;
+ double xtex = x, ytex = y;
+
+ guint32 xshift = _xch * 8, yshift = _ych * 8;
+ xpx = (mappx & (0xff << xshift)) >> xshift;
+ ypx = (mappx & (0xff << yshift)) >> yshift;
+ if (a) {
+ if (_xch != 3) xpx = unpremul_alpha(xpx, a);
+ if (_ych != 3) ypx = unpremul_alpha(ypx, a);
+ }
+ xtex += _scalex * (xpx - 127.5);
+ ytex += _scaley * (ypx - 127.5);
+
+ if (xtex >= 0 && xtex < (_texture._w - 1) &&
+ ytex >= 0 && ytex < (_texture._h - 1))
+ {
+ return _texture.pixelAt(xtex, ytex);
+ } else {
+ return 0;
+ }
+ }
+private:
+ SurfaceSynth _texture;
+ SurfaceSynth _map;
+ unsigned _xch, _ych;
+ double _scalex, _scaley;
+};
+
+void FilterDisplacementMap::render_cairo(FilterSlot &slot)
+{
+ cairo_surface_t *texture = slot.getcairo(_input);
+ cairo_surface_t *map = slot.getcairo(_input2);
+ cairo_surface_t *out = ink_cairo_surface_create_identical(texture);
+ // color_interpolation_filters for out same as texture. See spec.
+ copy_cairo_surface_ci( texture, out );
+
+ // We may need to transform map surface to correct color interpolation space. The map 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 map before converting.
+ SPColorInterpolation ci_fp = SP_CSS_COLOR_INTERPOLATION_AUTO;
+ if( _style ) {
+ ci_fp = (SPColorInterpolation)_style->color_interpolation_filters.computed;
+ }
+ set_cairo_surface_ci( map, ci_fp );
+
+ Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb();
+
+ int device_scale = slot.get_device_scale();
+ double scalex = scale * trans.expansionX() * device_scale;
+ double scaley = scale * trans.expansionY() * device_scale;
+
+ ink_cairo_surface_synthesize(out, Displace(texture, map, Xchannel, Ychannel, scalex, scaley));
+
+ slot.set(_output, out);
+ cairo_surface_destroy(out);
+}
+
+void FilterDisplacementMap::set_input(int slot) {
+ _input = slot;
+}
+
+void FilterDisplacementMap::set_scale(double s) {
+ scale = s;
+}
+
+void FilterDisplacementMap::set_input(int input, int slot) {
+ if (input == 0) _input = slot;
+ if (input == 1) _input2 = slot;
+}
+
+void FilterDisplacementMap::set_channel_selector(int s, FilterDisplacementMapChannelSelector channel) {
+ if (channel > DISPLACEMENTMAP_CHANNEL_ALPHA || channel < DISPLACEMENTMAP_CHANNEL_RED) {
+ g_warning("Selected an invalid channel value. (%d)", channel);
+ return;
+ }
+
+ // channel numbering:
+ // a = 3, r = 2, g = 1, b = 0
+ // this way we can get the component value using:
+ // component = (color & (ch*8)) >> (ch*8)
+ unsigned ch = 4;
+ switch (channel) {
+ case DISPLACEMENTMAP_CHANNEL_ALPHA:
+ ch = 3; break;
+ case DISPLACEMENTMAP_CHANNEL_RED:
+ ch = 2; break;
+ case DISPLACEMENTMAP_CHANNEL_GREEN:
+ ch = 1; break;
+ case DISPLACEMENTMAP_CHANNEL_BLUE:
+ ch = 0; break;
+ default: break;
+ }
+ if (ch == 4) return;
+
+ if (s == 0) Xchannel = ch;
+ if (s == 1) Ychannel = ch;
+}
+
+void FilterDisplacementMap::area_enlarge(Geom::IntRect &area, Geom::Affine const &trans)
+{
+ //I assume scale is in user coordinates (?!?)
+ //FIXME: trans should be multiplied by some primitiveunits2user, shouldn't it?
+
+ double scalex = scale/2.*(std::fabs(trans[0])+std::fabs(trans[1]));
+ double scaley = scale/2.*(std::fabs(trans[2])+std::fabs(trans[3]));
+
+ //FIXME: no +2 should be there!... (noticeable only for big scales at big zoom factor)
+ area.expandBy(scalex+2, scaley+2);
+}
+
+double FilterDisplacementMap::complexity(Geom::Affine const &)
+{
+ return 3.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 :