Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
nr-filter.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * SVG filters rendering
4 *
5 * Author:
6 * Niko Kiirala <niko@kiirala.com>
7 *
8 * Copyright (C) 2006-2008 Niko Kiirala
9 *
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13#include <glib.h>
14#include <cmath>
15#include <cstring>
16#include <string>
17#include <cairo.h>
18
19#include "display/nr-filter.h"
24
41
42#include "display/cairo-utils.h"
43#include "display/drawing.h"
47#include <2geom/affine.h>
48#include <2geom/rect.h>
49#include "svg/svg-length.h"
50//#include "sp-filter-units.h"
51
52namespace Inkscape {
53namespace Filters {
54
55using Geom::X;
56using Geom::Y;
57
62
64{
65 if (n > 0) primitives.reserve(n);
67}
68
70{
71 _slot_count = 1;
72 // Having "not set" here as value means the output of last filter
73 // primitive will be used as output of this filter
75
76 // These are the default values for filter region,
77 // as specified in SVG standard
78 // NB: SVGLength.set takes prescaled percent values: -.10 means -10%
83
84 // Filter resolution, negative value here stands for "automatic"
85 _x_pixels = -1.0;
86 _y_pixels = -1.0;
87
90}
91
93{
94 for (auto &p : primitives) {
95 p->update();
96 }
97}
98
100{
101 // std::cout << "Filter::render() for: " << const_cast<Inkscape::DrawingItem *>(item)->name() << std::endl;
102 // std::cout << " graphic drawing_scale: " << graphic.surface()->device_scale() << std::endl;
103
104 if (primitives.empty()) {
105 // when no primitives are defined, clear source graphic
106 graphic.setSource(0,0,0,0);
107 graphic.setOperator(CAIRO_OPERATOR_SOURCE);
108 graphic.paint();
109 graphic.setOperator(CAIRO_OPERATOR_OVER);
110 return 1;
111 }
112 FilterQuality filterquality = (FilterQuality)item->drawing().filterQuality();
113 int blurquality = item->drawing().blurQuality();
114
115 Geom::Affine trans = item->ctm();
116
117 Geom::OptRect filter_area = filter_effect_area(item->itemBounds());
118 if (!filter_area) return 1;
119
121 units.set_ctm(trans);
122 units.set_item_bbox(item->itemBounds());
123 units.set_filter_area(*filter_area);
124
125 auto resolution = _filter_resolution(*filter_area, trans, filterquality);
126 if (!(resolution.first > 0 && resolution.second > 0)) {
127 // zero resolution - clear source graphic and return
128 graphic.setSource(0,0,0,0);
129 graphic.setOperator(CAIRO_OPERATOR_SOURCE);
130 graphic.paint();
131 graphic.setOperator(CAIRO_OPERATOR_OVER);
132 return 1;
133 }
134
135 units.set_resolution(resolution.first, resolution.second);
136 if (_x_pixels > 0) {
137 units.set_automatic_resolution(false);
138 }
139 else {
140 units.set_automatic_resolution(true);
141 }
142
143 units.set_paraller(false);
144 Geom::Affine pbtrans = units.get_matrix_display2pb();
145 for (auto &i : primitives) {
146 if (!i->can_handle_affine(pbtrans)) {
147 units.set_paraller(true);
148 break;
149 }
150 }
151
152 auto slot = FilterSlot(bgdc, graphic, units, rc, blurquality);
153
154 for (auto &i : primitives) {
155 i->render_cairo(slot);
156 }
157
159 cairo_surface_t *result = slot.get_result(_output_slot);
160
161 // Assume for the moment that we paint the filter in sRGB
163
165 graphic.setOperator(CAIRO_OPERATOR_SOURCE);
166 graphic.paint();
167 graphic.setOperator(CAIRO_OPERATOR_OVER);
168 cairo_surface_destroy(result);
169
170 return 0;
171}
172
173void Filter::add_primitive(std::unique_ptr<FilterPrimitive> primitive)
174{
175 primitives.emplace_back(std::move(primitive));
176}
177
179{
180 _filter_units = unit;
181}
182
187
189{
190 for (auto const &i : primitives) {
191 if (i) i->area_enlarge(bbox, item->ctm());
192 }
193
194/*
195 TODO: something. See images at the bottom of filters.svg with medium-low
196 filtering quality.
197
198 FilterQuality const filterquality = ...
199
200 if (_x_pixels <= 0 && (filterquality == FILTER_QUALITY_BEST ||
201 filterquality == FILTER_QUALITY_BETTER)) {
202 return;
203 }
204
205 Geom::Rect item_bbox;
206 Geom::OptRect maybe_bbox = item->itemBounds();
207 if (maybe_bbox.empty()) {
208 // Code below needs a bounding box
209 return;
210 }
211 item_bbox = *maybe_bbox;
212
213 std::pair<double,double> res_low
214 = _filter_resolution(item_bbox, item->ctm(), filterquality);
215 //std::pair<double,double> res_full
216 // = _filter_resolution(item_bbox, item->ctm(), FILTER_QUALITY_BEST);
217 double pixels_per_block = fmax(item_bbox.width() / res_low.first,
218 item_bbox.height() / res_low.second);
219 bbox.x0 -= (int)pixels_per_block;
220 bbox.x1 += (int)pixels_per_block;
221 bbox.y0 -= (int)pixels_per_block;
222 bbox.y1 += (int)pixels_per_block;
223*/
224}
225
227{
228 Geom::Point minp, maxp;
229
231 double len_x = bbox ? bbox->width() : 0;
232 double len_y = bbox ? bbox->height() : 0;
233 /* TODO: fetch somehow the object ex and em lengths */
234
235 // Update for em, ex, and % values
236 auto compute = [] (SVGLength length, double scale) {
237 length.update(12, 6, scale);
238 return length.computed;
239 };
240 auto const region_x_computed = compute(_region_x, len_x);
241 auto const region_y_computed = compute(_region_y, len_y);
242 auto const region_w_computed = compute(_region_width, len_x);
243 auto const region_h_computed = compute(_region_height, len_y);;
244
245 if (!bbox) return Geom::OptRect();
246
248 minp[X] = bbox->left() + region_x_computed;
249 } else {
250 minp[X] = bbox->left() + region_x_computed * len_x;
251 }
253 maxp[X] = minp[X] + region_w_computed;
254 } else {
255 maxp[X] = minp[X] + region_w_computed * len_x;
256 }
257
259 minp[Y] = bbox->top() + region_y_computed;
260 } else {
261 minp[Y] = bbox->top() + region_y_computed * len_y;
262 }
264 maxp[Y] = minp[Y] + region_h_computed;
265 } else {
266 maxp[Y] = minp[Y] + region_h_computed * len_y;
267 }
269 // Region already set in sp-filter.cpp
270 minp[X] = _region_x.computed;
271 maxp[X] = minp[X] + _region_width.computed;
272 minp[Y] = _region_y.computed;
273 maxp[Y] = minp[Y] + _region_height.computed;
274 } else {
275 g_warning("Error in Inkscape::Filters::Filter::filter_effect_area: unrecognized value of _filter_units");
276 }
277
278 Geom::OptRect area(minp, maxp);
279 // std::cout << "Filter::filter_effect_area: area: " << *area << std::endl;
280 return area;
281}
282
283double Filter::complexity(Geom::Affine const &ctm) const
284{
285 double factor = 1.0;
286 for (auto &i : primitives) {
287 if (i) {
288 double f = i->complexity(ctm);
289 factor += f - 1.0;
290 }
291 }
292 return factor;
293}
294
296{
297 for (auto &i : primitives) {
298 if (i && i->uses_background()) {
299 return true;
300 }
301 }
302 return false;
303}
304
306{
307 primitives.clear();
308}
309
310void Filter::set_x(SVGLength const &length)
311{
312 if (length._set)
313 _region_x = length;
314}
315
316void Filter::set_y(SVGLength const &length)
317{
318 if (length._set)
319 _region_y = length;
320}
321
322void Filter::set_width(SVGLength const &length)
323{
324 if (length._set)
325 _region_width = length;
326}
327
328void Filter::set_height(SVGLength const &length)
329{
330 if (length._set)
331 _region_height = length;
332}
333
334void Filter::set_resolution(double pixels)
335{
336 if (pixels > 0) {
337 _x_pixels = pixels;
338 _y_pixels = pixels;
339 }
340}
341
342void Filter::set_resolution(double x_pixels, double y_pixels)
343{
344 if (x_pixels >= 0 && y_pixels >= 0) {
345 _x_pixels = x_pixels;
346 _y_pixels = y_pixels;
347 }
348}
349
351{
352 _x_pixels = -1;
353 _y_pixels = -1;
354}
355
357{
358 switch (quality) {
360 return 32;
362 return 64;
364 return 256;
366 return 1024;
368 default:
369 return -1;
370 }
371}
372
373std::pair<double, double> Filter::_filter_resolution(Geom::Rect const &area, Geom::Affine const &trans, FilterQuality filterquality) const
374{
375 std::pair<double, double> resolution;
376 if (_x_pixels > 0) {
377 double y_len;
378 if (_y_pixels > 0) {
379 y_len = _y_pixels;
380 } else {
381 y_len = (_x_pixels * (area.max()[Y] - area.min()[Y])) / (area.max()[X] - area.min()[X]);
382 }
383 resolution.first = _x_pixels;
384 resolution.second = y_len;
385 } else {
386 auto origo = area.min() * trans;
387 auto max_i = Geom::Point(area.max()[X], area.min()[Y]) * trans;
388 auto max_j = Geom::Point(area.min()[X], area.max()[Y]) * trans;
389 double i_len = (origo - max_i).length();
390 double j_len = (origo - max_j).length();
391 int limit = _resolution_limit(filterquality);
392 if (limit > 0 && (i_len > limit || j_len > limit)) {
393 double aspect_ratio = i_len / j_len;
394 if (i_len > j_len) {
395 i_len = limit;
396 j_len = i_len / aspect_ratio;
397 }
398 else {
399 j_len = limit;
400 i_len = j_len * aspect_ratio;
401 }
402 }
403 resolution.first = i_len;
404 resolution.second = j_len;
405 }
406 return resolution;
407}
408
409} // namespace Filters
410} // namespace Inkscape
411
412/*
413 Local Variables:
414 mode:c++
415 c-file-style:"stroustrup"
416 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
417 indent-tabs-mode:nil
418 fill-column:99
419 End:
420*/
421// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
double scale
Definition aa.cpp:228
Point origin
Definition aa.cpp:227
3x3 affine transformation matrix.
void set_cairo_surface_ci(cairo_surface_t *surface, SPColorInterpolation ci)
Set the color_interpolation_value for a Cairo surface.
Cairo integration helpers.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Axis aligned, non-empty, generic rectangle.
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
CPoint max() const
Get the corner of the rectangle with largest coordinate values.
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Two-dimensional point that doubles as a vector.
Definition point.h:66
Axis aligned, non-empty rectangle.
Definition rect.h:92
Minimal wrapper over Cairo.
Geom::Rect targetLogicalBounds() const
void setSource(cairo_pattern_t *source)
void paint(double alpha=1.0)
void setOperator(cairo_operator_t op)
SVG drawing item for display.
void set_automatic_resolution(bool const automatic)
Sets, if filter resolution is automatic.
Geom::Affine get_matrix_display2pb() const
Gets the display coordinates to pixblock coordinates transformation matrix.
void set_paraller(bool const paraller)
Sets, if x and y axis in pixblock coordinates should be paraller to x and y of user coordinates.
void set_filter_area(Geom::OptRect const &area)
Sets the filter effects area in user coordinates.
void set_item_bbox(Geom::OptRect const &bbox)
Sets the item bounding box in user coordinates.
void set_ctm(Geom::Affine const &ctm)
Sets the current transformation matrix, i.e.
void set_resolution(double const x_res, double const y_res)
Sets the resolution, the filter should be rendered with.
void set_x(SVGLength const &length)
void reset_resolution()
Resets the filter resolution to its default value, i.e.
std::vector< std::unique_ptr< FilterPrimitive > > primitives
Definition nr-filter.h:165
static int _resolution_limit(FilterQuality quality)
void set_height(SVGLength const &length)
Filter()
Creates a new filter with space for one filter element.
Definition nr-filter.cpp:58
void set_y(SVGLength const &length)
std::pair< double, double > _filter_resolution(Geom::Rect const &area, Geom::Affine const &trans, FilterQuality q) const
void update()
Update any embedded DrawingItems prior to rendering.
Definition nr-filter.cpp:92
int render(Inkscape::DrawingItem const *item, DrawingContext &graphic, DrawingContext *bgdc, RenderContext &rc) const
Given background state from bgdc and an intermediate rendering from the surface backing graphic,...
Definition nr-filter.cpp:99
void set_filter_units(SPFilterUnits unit)
Set the filterUnits-property.
void area_enlarge(Geom::IntRect &area, Inkscape::DrawingItem const *item) const
Modifies the given area to accommodate for filters needing pixels outside the rendered area.
void set_primitive_units(SPFilterUnits unit)
Set the primitiveUnits-property.
void add_primitive(std::unique_ptr< FilterPrimitive > primitive)
Creates a new filter primitive under this filter object.
void clear_primitives()
Removes all filter primitives from this filter.
SPFilterUnits _filter_units
Definition nr-filter.h:185
SPFilterUnits _primitive_units
Definition nr-filter.h:186
void set_resolution(double x_pixels)
Sets the width of intermediate images in pixels.
int _output_slot
Image slot from which filter output should be read.
Definition nr-filter.h:172
Geom::OptRect filter_effect_area(Geom::OptRect const &bbox) const
Returns the filter effects area in user coordinate system.
double complexity(Geom::Affine const &ctm) const
void set_width(SVGLength const &length)
int _slot_count
Amount of image slots used when this filter was rendered last time.
Definition nr-filter.h:168
SVG length type.
Definition svg-length.h:22
void set(Unit u, float v)
Unit unit
Definition svg-length.h:44
float computed
Definition svg-length.h:50
RectangularCluster rc
Css & result
Cairo drawing context with Inkscape extensions.
Canvas item belonging to an SVG drawing element.
Cairo surface that remembers its origin.
struct _cairo_surface cairo_surface_t
SVG drawing for display.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
SPItem * item
Helper class to stream background task notifications as a series of messages.
TODO: insert short description here.
Axis-aligned rectangle.
SPFilterUnits
@ SP_FILTER_UNITS_USERSPACEONUSE
@ SP_FILTER_UNITS_OBJECTBOUNDINGBOX
@ SP_CSS_COLOR_INTERPOLATION_SRGB