1 #define __NR_FILTER_CPP__
3 /*
4 * SVG filters rendering
5 *
6 * Author:
7 * Niko Kiirala <niko@kiirala.com>
8 *
9 * Copyright (C) 2006,2007 Niko Kiirala
10 *
11 * Released under GNU GPL, read the file 'COPYING' for more information
12 */
14 #include <glib.h>
15 #include <cmath>
17 #include "display/nr-filter.h"
18 #include "display/nr-filter-primitive.h"
19 #include "display/nr-filter-slot.h"
20 #include "display/nr-filter-types.h"
21 #include "display/nr-filter-units.h"
23 #include "display/nr-filter-blend.h"
24 #include "display/nr-filter-composite.h"
25 #include "display/nr-filter-convolve-matrix.h"
26 #include "display/nr-filter-colormatrix.h"
27 #include "display/nr-filter-component-transfer.h"
28 #include "display/nr-filter-diffuselighting.h"
29 #include "display/nr-filter-displacement-map.h"
30 #include "display/nr-filter-flood.h"
31 #include "display/nr-filter-gaussian.h"
32 #include "display/nr-filter-image.h"
33 #include "display/nr-filter-merge.h"
34 #include "display/nr-filter-morphology.h"
35 #include "display/nr-filter-offset.h"
36 #include "display/nr-filter-specularlighting.h"
37 #include "display/nr-filter-tile.h"
38 #include "display/nr-filter-turbulence.h"
40 #include "display/nr-arena-item.h"
41 #include "libnr/nr-pixblock.h"
42 #include "libnr/nr-blit.h"
43 #include "libnr/nr-matrix.h"
44 #include "libnr/nr-scale.h"
45 #include "svg/svg-length.h"
46 #include "sp-filter-units.h"
47 #if defined (SOLARIS_2_8)
48 #include "round.h"
49 using Inkscape::round;
50 #endif
52 namespace NR {
54 Filter::Filter()
55 {
56 _primitive_count = 0;
57 _primitive_table_size = 1;
58 _primitive = new FilterPrimitive*[1];
59 _primitive[0] = NULL;
60 //_primitive_count = 1;
61 //_primitive[0] = new FilterGaussian;
62 _common_init();
63 }
65 Filter::Filter(int n)
66 {
67 _primitive_count = 0;
68 _primitive_table_size = n;
69 _primitive = new FilterPrimitive*[n];
70 for ( int i = 0 ; i < n ; i++ ) {
71 _primitive[i] = NULL;
72 }
73 _common_init();
74 }
76 void Filter::_common_init() {
77 _slot_count = 1;
78 // Having "not set" here as value means the output of last filter
79 // primitive will be used as output of this filter
80 _output_slot = NR_FILTER_SLOT_NOT_SET;
82 // These are the default values for filter region,
83 // as specified in SVG standard
84 // NB: SVGLength.set takes prescaled percent values: -.10 means -10%
85 _region_x.set(SVGLength::PERCENT, -.10, 0);
86 _region_y.set(SVGLength::PERCENT, -.10, 0);
87 _region_width.set(SVGLength::PERCENT, 1.20, 0);
88 _region_height.set(SVGLength::PERCENT, 1.20, 0);
90 // Filter resolution, negative value here stands for "automatic"
91 _x_pixels = -1.0;
92 _y_pixels = -1.0;
94 _filter_units = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
95 _primitive_units = SP_FILTER_UNITS_USERSPACEONUSE;
96 }
98 Filter::~Filter()
99 {
100 clear_primitives();
101 delete[] _primitive;
102 }
105 int Filter::render(NRArenaItem const *item, NRPixBlock *pb)
106 {
107 if(!_primitive[0]) {
108 // TODO: Should clear the input buffer instead of just returning
109 return 0;
110 }
112 Matrix trans = *item->ctm;
113 FilterSlot slot(_slot_count, item);
115 Rect item_bbox = *item->item_bbox;
116 Rect filter_area = filter_effect_area(item_bbox);
117 FilterUnits units(_filter_units, _primitive_units);
118 units.set_ctm(trans);
119 units.set_item_bbox(item_bbox);
120 units.set_filter_area(filter_area);
122 // TODO: with filterRes of 0x0 should return an empty image
123 if (_x_pixels > 0) {
124 double y_len;
125 if (_y_pixels > 0) {
126 y_len = _y_pixels;
127 } else {
128 y_len = (_x_pixels * (filter_area.max()[Y] - filter_area.min()[Y]))
129 / (filter_area.max()[X] - filter_area.min()[X]);
130 }
131 units.set_automatic_resolution(false);
132 units.set_resolution(_x_pixels, y_len);
133 } else {
134 Point origo = filter_area.min();
135 origo *= trans;
136 Point max_i(filter_area.max()[X], filter_area.min()[Y]);
137 max_i *= trans;
138 Point max_j(filter_area.min()[X], filter_area.max()[Y]);
139 max_j *= trans;
140 double i_len = sqrt((origo[X] - max_i[X]) * (origo[X] - max_i[X])
141 + (origo[Y] - max_i[Y]) * (origo[Y] - max_i[Y]));
142 double j_len = sqrt((origo[X] - max_j[X]) * (origo[X] - max_j[X])
143 + (origo[Y] - max_j[Y]) * (origo[Y] - max_j[Y]));
144 units.set_automatic_resolution(true);
145 units.set_resolution(i_len, j_len);
146 }
148 units.set_paraller(false);
149 for (int i = 0 ; i < _primitive_count ; i++) {
150 if (_primitive[i]->get_input_traits() & TRAIT_PARALLER) {
151 units.set_paraller(true);
152 break;
153 }
154 }
156 slot.set_units(units);
158 NRPixBlock *in = new NRPixBlock;
159 nr_pixblock_setup_fast(in, pb->mode, pb->area.x0, pb->area.y0,
160 pb->area.x1, pb->area.y1, false);
161 if (in->size != NR_PIXBLOCK_SIZE_TINY && in->data.px == NULL) {
162 g_warning("NR::Filter::render: failed to reserve temporary buffer");
163 return 0;
164 }
165 nr_blit_pixblock_pixblock(in, pb);
166 in->empty = FALSE;
167 slot.set(NR_FILTER_SOURCEGRAPHIC, in);
169 // Check that we are rendering a non-empty area
170 in = slot.get(NR_FILTER_SOURCEGRAPHIC);
171 if (in->area.x1 - in->area.x0 <= 0 || in->area.y1 - in->area.y0 <= 0) {
172 if (in->area.x1 - in->area.x0 < 0 || in->area.y1 - in->area.y0 < 0) {
173 g_warning("NR::Filter::render: negative area! (%d, %d) (%d, %d)",
174 in->area.x0, in->area.y0, in->area.x1, in->area.y1);
175 }
176 return 0;
177 }
178 in = NULL; // in is now handled by FilterSlot, we should not touch it
180 // TODO: filters may need both filterUnits and primitiveUnits,
181 // so we should pass FilterUnits to render method, not just one Matrix
182 Matrix primitiveunits2pixblock = units.get_matrix_primitiveunits2pb();
183 for (int i = 0 ; i < _primitive_count ; i++) {
184 _primitive[i]->render(slot, primitiveunits2pixblock);
185 }
187 slot.get_final(_output_slot, pb);
189 // Take note of the amount of used image slots
190 // -> next time this filter is rendered, we can reserve enough slots
191 // immediately
192 _slot_count = slot.get_slot_count();
193 return 0;
194 }
196 void Filter::area_enlarge(NRRectL &bbox, Matrix const &m) {
197 for (int i = 0 ; i < _primitive_count ; i++) {
198 if (_primitive[i]) _primitive[i]->area_enlarge(bbox, m);
199 }
200 }
202 void Filter::bbox_enlarge(NRRectL &bbox) {
203 /* TODO: this is wrong. Should use bounding box in user coordinates
204 * and find its extents in display coordinates. */
205 Point min(bbox.x0, bbox.y0);
206 Point max(bbox.x1, bbox.y1);
207 Rect tmp_bbox(min, max);
209 Rect enlarged = filter_effect_area(tmp_bbox);
211 bbox.x0 = (ICoord)enlarged.min()[X];
212 bbox.y0 = (ICoord)enlarged.min()[Y];
213 bbox.x1 = (ICoord)enlarged.max()[X];
214 bbox.y1 = (ICoord)enlarged.max()[Y];
215 }
217 Rect Filter::filter_effect_area(Rect const &bbox)
218 {
219 Point minp, maxp;
220 double len_x = bbox.max()[X] - bbox.min()[X];
221 double len_y = bbox.max()[Y] - bbox.min()[Y];
222 /* TODO: fetch somehow the object ex and em lengths */
223 _region_x.update(12, 6, len_x);
224 _region_y.update(12, 6, len_y);
225 _region_width.update(12, 6, len_x);
226 _region_height.update(12, 6, len_y);
227 if (_filter_units == SP_FILTER_UNITS_OBJECTBOUNDINGBOX) {
228 if (_region_x.unit == SVGLength::PERCENT) {
229 minp[X] = bbox.min()[X] + _region_x.computed;
230 } else {
231 minp[X] = bbox.min()[X] + _region_x.computed * len_x;
232 }
233 if (_region_width.unit == SVGLength::PERCENT) {
234 maxp[X] = minp[X] + _region_width.computed;
235 } else {
236 maxp[X] = minp[X] + _region_width.computed * len_x;
237 }
239 if (_region_y.unit == SVGLength::PERCENT) {
240 minp[Y] = bbox.min()[Y] + _region_y.computed;
241 } else {
242 minp[Y] = bbox.min()[Y] + _region_y.computed * len_y;
243 }
244 if (_region_height.unit == SVGLength::PERCENT) {
245 maxp[Y] = minp[Y] + _region_height.computed;
246 } else {
247 maxp[Y] = minp[Y] + _region_height.computed * len_y;
248 }
249 } else if (_filter_units == SP_FILTER_UNITS_USERSPACEONUSE) {
250 /* TODO: make sure bbox and fe region are in same coordinate system */
251 minp[X] = _region_x.computed;
252 maxp[X] = minp[X] + _region_width.computed;
253 minp[Y] = _region_y.computed;
254 maxp[Y] = minp[Y] + _region_height.computed;
255 } else {
256 g_warning("Error in NR::Filter::bbox_enlarge: unrecognized value of _filter_units");
257 }
258 Rect area(minp, maxp);
259 return area;
260 }
262 /* Constructor table holds pointers to static methods returning filter
263 * primitives. This table is indexed with FilterPrimitiveType, so that
264 * for example method in _constructor[NR_FILTER_GAUSSIANBLUR]
265 * returns a filter object of type NR::FilterGaussian.
266 */
267 typedef FilterPrimitive*(*FilterConstructor)();
268 static FilterConstructor _constructor[NR_FILTER_ENDPRIMITIVETYPE];
270 void Filter::_create_constructor_table()
271 {
272 // Constructor table won't change in run-time, so no need to recreate
273 static bool created = false;
274 if(created) return;
276 /* Some filter classes are not implemented yet.
277 Some of them still have only boilerplate code.*/
278 _constructor[NR_FILTER_BLEND] = &FilterBlend::create;
279 _constructor[NR_FILTER_COLORMATRIX] = &FilterColorMatrix::create;
280 _constructor[NR_FILTER_COMPONENTTRANSFER] = &FilterComponentTransfer::create;
281 _constructor[NR_FILTER_COMPOSITE] = &FilterComposite::create;
282 _constructor[NR_FILTER_CONVOLVEMATRIX] = &FilterConvolveMatrix::create;
283 _constructor[NR_FILTER_DIFFUSELIGHTING] = &FilterDiffuseLighting::create;
284 _constructor[NR_FILTER_DISPLACEMENTMAP] = &FilterDisplacementMap::create;
285 _constructor[NR_FILTER_FLOOD] = &FilterFlood::create;
286 _constructor[NR_FILTER_GAUSSIANBLUR] = &FilterGaussian::create;
287 _constructor[NR_FILTER_IMAGE] = &FilterImage::create;
288 _constructor[NR_FILTER_MERGE] = &FilterMerge::create;
289 _constructor[NR_FILTER_MORPHOLOGY] = &FilterMorphology::create;
290 _constructor[NR_FILTER_OFFSET] = &FilterOffset::create;
291 _constructor[NR_FILTER_SPECULARLIGHTING] = &FilterSpecularLighting::create;
292 _constructor[NR_FILTER_TILE] = &FilterTile::create;
293 _constructor[NR_FILTER_TURBULENCE] = &FilterTurbulence::create;
294 created = true;
295 }
297 /** Helper method for enlarging table of filter primitives. When new
298 * primitives are added, but we have no space for them, this function
299 * makes some more space.
300 */
301 void Filter::_enlarge_primitive_table() {
302 FilterPrimitive **new_tbl = new FilterPrimitive*[_primitive_table_size * 2];
303 for (int i = 0 ; i < _primitive_count ; i++) {
304 new_tbl[i] = _primitive[i];
305 }
306 _primitive_table_size *= 2;
307 for (int i = _primitive_count ; i < _primitive_table_size ; i++) {
308 new_tbl[i] = NULL;
309 }
310 delete[] _primitive;
311 _primitive = new_tbl;
312 }
314 int Filter::add_primitive(FilterPrimitiveType type)
315 {
316 _create_constructor_table();
318 // Check that we can create a new filter of specified type
319 if (type < 0 || type >= NR_FILTER_ENDPRIMITIVETYPE)
320 return -1;
321 if (!_constructor[type]) return -1;
322 FilterPrimitive *created = _constructor[type]();
324 // If there is no space for new filter primitive, enlarge the table
325 if (_primitive_count >= _primitive_table_size) {
326 _enlarge_primitive_table();
327 }
329 _primitive[_primitive_count] = created;
330 int handle = _primitive_count;
331 _primitive_count++;
332 return handle;
333 }
335 int Filter::replace_primitive(int target, FilterPrimitiveType type)
336 {
337 _create_constructor_table();
339 // Check that target is valid primitive inside this filter
340 if (target < 0) return -1;
341 if (target >= _primitive_count) return -1;
342 if (!_primitive[target]) return -1;
344 // Check that we can create a new filter of specified type
345 if (type < 0 || type >= NR_FILTER_ENDPRIMITIVETYPE)
346 return -1;
347 if (!_constructor[type]) return -1;
348 FilterPrimitive *created = _constructor[type]();
350 // If there is no space for new filter primitive, enlarge the table
351 if (_primitive_count >= _primitive_table_size) {
352 _enlarge_primitive_table();
353 }
355 delete _primitive[target];
356 _primitive[target] = created;
357 return target;
358 }
360 FilterPrimitive *Filter::get_primitive(int handle) {
361 if (handle < 0 || handle >= _primitive_count) return NULL;
362 return _primitive[handle];
363 }
365 void Filter::clear_primitives()
366 {
367 for (int i = 0 ; i < _primitive_count ; i++) {
368 if (_primitive[i]) delete _primitive[i];
369 }
370 _primitive_count = 0;
371 }
373 void Filter::set_x(SVGLength const &length)
374 {
375 if (length._set)
376 _region_x = length;
377 }
378 void Filter::set_y(SVGLength const &length)
379 {
380 if (length._set)
381 _region_y = length;
382 }
383 void Filter::set_width(SVGLength const &length)
384 {
385 if (length._set)
386 _region_width = length;
387 }
388 void Filter::set_height(SVGLength const &length)
389 {
390 if (length._set)
391 _region_height = length;
392 }
394 void Filter::set_resolution(double const pixels) {
395 if (pixels > 0) {
396 _x_pixels = pixels;
397 _y_pixels = pixels;
398 }
399 }
401 void Filter::set_resolution(double const x_pixels, double const y_pixels) {
402 if (x_pixels >= 0 && y_pixels >= 0) {
403 _x_pixels = x_pixels;
404 _y_pixels = y_pixels;
405 }
406 }
408 void Filter::reset_resolution() {
409 _x_pixels = -1;
410 _y_pixels = -1;
411 }
413 } /* namespace NR */
415 /*
416 Local Variables:
417 mode:c++
418 c-file-style:"stroustrup"
419 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
420 indent-tabs-mode:nil
421 fill-column:99
422 End:
423 */
424 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :