// SPDX-License-Identifier: GPL-2.0-or-later /* * feDisplacementMap filter primitive renderer * * Authors: * Felipe CorrĂȘa da Silva Sanches * * 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 :