~centralelyon2010/inkscape/imagelinks2

« back to all changes in this revision

Viewing changes to src/display/nr-filter.cpp

  • Committer: JazzyNico
  • Date: 2011-08-29 20:25:30 UTC
  • Revision ID: nicoduf@yahoo.fr-20110829202530-6deuoz11q90usldv
Code refactoring and merging with trunk (revision 10599).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#define __NR_FILTER_CPP__
2
 
 
3
1
/*
4
2
 * SVG filters rendering
5
3
 *
15
13
#include <cmath>
16
14
#include <cstring>
17
15
#include <string>
 
16
#include <cairo.h>
18
17
 
19
18
#include "display/nr-filter.h"
20
19
#include "display/nr-filter-primitive.h"
39
38
#include "display/nr-filter-tile.h"
40
39
#include "display/nr-filter-turbulence.h"
41
40
 
42
 
#include "display/nr-arena.h"
43
 
#include "display/nr-arena-item.h"
44
 
#include "libnr/nr-pixblock.h"
45
 
#include "libnr/nr-blit.h"
46
 
#include <2geom/matrix.h>
 
41
#include "display/drawing.h"
 
42
#include "display/drawing-item.h"
 
43
#include "display/drawing-context.h"
 
44
#include <2geom/affine.h>
47
45
#include <2geom/rect.h>
48
46
#include "svg/svg-length.h"
49
47
#include "sp-filter-units.h"
60
58
using Geom::X;
61
59
using Geom::Y;
62
60
 
63
 
static Geom::OptRect get_item_bbox(NRArenaItem const *item) {
64
 
    Geom::Rect item_bbox;
65
 
    if (item->item_bbox) {
66
 
        item_bbox = *(item->item_bbox);
67
 
    } else {
68
 
        // Bounding box might not exist, so create a dummy one.
69
 
        Geom::Point zero(0, 0);
70
 
        item_bbox = Geom::Rect(zero, zero);
71
 
    }
72
 
    if (item_bbox.min()[X] > item_bbox.max()[X]
73
 
        || item_bbox.min()[Y] > item_bbox.max()[Y])
74
 
    {
75
 
        // In case of negative-size bbox, return an empty OptRect
76
 
        return Geom::OptRect();
77
 
    }
78
 
    return Geom::OptRect(item_bbox);
79
 
}
80
 
 
81
61
Filter::Filter()
82
62
{
83
 
    _primitive_count = 0;
84
 
    _primitive_table_size = 1;
85
 
    _primitive = new FilterPrimitive*[1];
86
 
    _primitive[0] = NULL;
87
 
    //_primitive_count = 1;
88
 
    //_primitive[0] = new FilterGaussian;
89
63
    _common_init();
90
64
}
91
65
 
92
66
Filter::Filter(int n)
93
67
{
94
 
    _primitive_count = 0;
95
 
    _primitive_table_size = (n > 0) ? n : 1;    // we guarantee there is at least 1(one) filter slot
96
 
    _primitive = new FilterPrimitive*[_primitive_table_size];
97
 
    for ( int i = 0 ; i < _primitive_table_size ; i++ ) {
98
 
        _primitive[i] = NULL;
99
 
    }
 
68
    if (n > 0) _primitive.reserve(n);
100
69
    _common_init();
101
70
}
102
71
 
125
94
Filter::~Filter()
126
95
{
127
96
    clear_primitives();
128
 
    delete[] _primitive;
129
97
}
130
98
 
131
99
 
132
 
int Filter::render(NRArenaItem const *item, NRPixBlock *pb)
 
100
int Filter::render(Inkscape::DrawingItem const *item, DrawingContext &graphic, DrawingContext *bgct)
133
101
{
134
 
    if (!_primitive[0]) {
135
 
        // TODO: Should clear the input buffer instead of just returning
136
 
       return 1;
137
 
    }
138
 
 
139
 
    FilterQuality const filterquality = (FilterQuality)item->arena->filterquality;
140
 
    int const blurquality = item->arena->blurquality;
141
 
 
142
 
    Geom::Matrix trans = item->ctm;
143
 
    FilterSlot slot(_slot_count, item);
144
 
    slot.set_quality(filterquality);
145
 
    slot.set_blurquality(blurquality);
146
 
 
147
 
    Geom::Rect item_bbox;
148
 
    {
149
 
        Geom::OptRect maybe_bbox = get_item_bbox(item);
150
 
        if (maybe_bbox.isEmpty()) {
151
 
            // Code below needs a bounding box
152
 
            return 1;
153
 
        }
154
 
        item_bbox = *maybe_bbox;
155
 
    }
156
 
 
157
 
    Geom::Rect filter_area = filter_effect_area(item_bbox);
158
 
    if (item_bbox.hasZeroArea()) {
159
 
        // It's no use to try and filter an empty object.
 
102
    if (_primitive.empty()) {
 
103
        // when no primitives are defined, clear source graphic
 
104
        graphic.setSource(0,0,0,0);
 
105
        graphic.setOperator(CAIRO_OPERATOR_SOURCE);
 
106
        graphic.paint();
 
107
        graphic.setOperator(CAIRO_OPERATOR_OVER);
160
108
        return 1;
161
109
    }
162
110
 
 
111
    FilterQuality const filterquality = (FilterQuality)item->drawing().filterQuality();
 
112
    int const blurquality = item->drawing().blurQuality();
 
113
 
 
114
    Geom::Affine trans = item->ctm();
 
115
 
 
116
    Geom::OptRect filter_area = filter_effect_area(item->itemBounds());
 
117
    if (!filter_area) return 1;
 
118
 
163
119
    FilterUnits units(_filter_units, _primitive_units);
164
120
    units.set_ctm(trans);
165
 
    units.set_item_bbox(item_bbox);
166
 
    units.set_filter_area(filter_area);
 
121
    units.set_item_bbox(item->itemBounds());
 
122
    units.set_filter_area(*filter_area);
167
123
 
168
 
    // TODO: with filterRes of 0x0 should return an empty image
169
124
    std::pair<double,double> resolution
170
 
        = _filter_resolution(filter_area, trans, filterquality);
171
 
    if(!(resolution.first > 0 && resolution.second > 0))
172
 
        return 1;
 
125
        = _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
 
173
135
    units.set_resolution(resolution.first, resolution.second);
174
136
    if (_x_pixels > 0) {
175
137
        units.set_automatic_resolution(false);
179
141
    }
180
142
 
181
143
    units.set_paraller(false);
182
 
    for (int i = 0 ; i < _primitive_count ; i++) {
183
 
        if (_primitive[i]->get_input_traits() & TRAIT_PARALLER) {
 
144
    Geom::Affine pbtrans = units.get_matrix_display2pb();
 
145
    for (unsigned i = 0 ; i < _primitive.size() ; i++) {
 
146
        if (!_primitive[i]->can_handle_affine(pbtrans)) {
184
147
            units.set_paraller(true);
185
148
            break;
186
149
        }
187
150
    }
188
151
 
189
 
    slot.set_units(units);
190
 
 
191
 
    NRPixBlock *in = new NRPixBlock;
192
 
    nr_pixblock_setup_fast(in, pb->mode, pb->area.x0, pb->area.y0,
193
 
                           pb->area.x1, pb->area.y1, true);
194
 
    if (in->size != NR_PIXBLOCK_SIZE_TINY && in->data.px == NULL) {
195
 
        g_warning("Inkscape::Filters::Filter::render: failed to reserve temporary buffer");
196
 
        return 0;
197
 
    }
198
 
    nr_blit_pixblock_pixblock(in, pb);
199
 
    in->empty = FALSE;
200
 
    slot.set(NR_FILTER_SOURCEGRAPHIC, in);
201
 
 
202
 
    // Check that we are rendering a non-empty area
203
 
    in = slot.get(NR_FILTER_SOURCEGRAPHIC);
204
 
    if (in->area.x1 - in->area.x0 <= 0 || in->area.y1 - in->area.y0 <= 0) {
205
 
        if (in->area.x1 - in->area.x0 < 0 || in->area.y1 - in->area.y0 < 0) {
206
 
            g_warning("Inkscape::Filters::Filter::render: negative area! (%d, %d) (%d, %d)",
207
 
                      in->area.x0, in->area.y0, in->area.x1, in->area.y1);
208
 
        }
209
 
        return 0;
210
 
    }
211
 
    in = NULL; // in is now handled by FilterSlot, we should not touch it
212
 
 
213
 
    for (int i = 0 ; i < _primitive_count ; i++) {
214
 
        _primitive[i]->render(slot, units);
215
 
    }
216
 
 
217
 
    slot.get_final(_output_slot, pb);
218
 
 
219
 
    // Take note of the amount of used image slots
220
 
    // -> next time this filter is rendered, we can reserve enough slots
221
 
    // immediately
222
 
    _slot_count = slot.get_slot_count();
 
152
    FilterSlot slot(const_cast<Inkscape::DrawingItem*>(item), bgct, graphic, units);
 
153
    slot.set_quality(filterquality);
 
154
    slot.set_blurquality(blurquality);
 
155
 
 
156
    for (unsigned i = 0 ; i < _primitive.size() ; i++) {
 
157
        _primitive[i]->render_cairo(slot);
 
158
    }
 
159
 
 
160
    Geom::Point origin = graphic.targetLogicalBounds().min();
 
161
    cairo_surface_t *result = slot.get_result(_output_slot);
 
162
    graphic.setSource(result, origin[Geom::X], origin[Geom::Y]);
 
163
    graphic.setOperator(CAIRO_OPERATOR_SOURCE);
 
164
    graphic.paint();
 
165
    graphic.setOperator(CAIRO_OPERATOR_OVER);
 
166
    cairo_surface_destroy(result);
 
167
 
223
168
    return 0;
224
169
}
225
170
 
231
176
    _primitive_units = unit;
232
177
}
233
178
 
234
 
void Filter::area_enlarge(NRRectL &bbox, NRArenaItem const *item) const {
235
 
    for (int i = 0 ; i < _primitive_count ; i++) {
236
 
        if (_primitive[i]) _primitive[i]->area_enlarge(bbox, item->ctm);
 
179
void Filter::area_enlarge(Geom::IntRect &bbox, Inkscape::DrawingItem const *item) const {
 
180
    for (unsigned i = 0 ; i < _primitive.size() ; i++) {
 
181
        if (_primitive[i]) _primitive[i]->area_enlarge(bbox, item->ctm());
237
182
    }
 
183
 
238
184
/*
239
185
  TODO: something. See images at the bottom of filters.svg with medium-low
240
186
  filtering quality.
248
194
    }
249
195
 
250
196
    Geom::Rect item_bbox;
251
 
    Geom::OptRect maybe_bbox = get_item_bbox(item);
 
197
    Geom::OptRect maybe_bbox = item->itemBounds();
252
198
    if (maybe_bbox.isEmpty()) {
253
199
        // Code below needs a bounding box
254
200
        return;
256
202
    item_bbox = *maybe_bbox;
257
203
 
258
204
    std::pair<double,double> res_low
259
 
        = _filter_resolution(item_bbox, item->ctm, filterquality);
 
205
        = _filter_resolution(item_bbox, item->ctm(), filterquality);
260
206
    //std::pair<double,double> res_full
261
 
    //    = _filter_resolution(item_bbox, item->ctm, FILTER_QUALITY_BEST);
 
207
    //    = _filter_resolution(item_bbox, item->ctm(), FILTER_QUALITY_BEST);
262
208
    double pixels_per_block = fmax(item_bbox.width() / res_low.first,
263
209
                                   item_bbox.height() / res_low.second);
264
210
    bbox.x0 -= (int)pixels_per_block;
268
214
*/
269
215
}
270
216
 
271
 
void Filter::bbox_enlarge(NRRectL &bbox) {
272
 
    // Modifying empty bounding boxes confuses rest of the renderer, so
273
 
    // let's not do that.
274
 
    if (bbox.x0 > bbox.x1 || bbox.y0 > bbox.y1) return;
275
 
 
276
 
    /* TODO: this is wrong. Should use bounding box in user coordinates
277
 
     * and find its extents in display coordinates. */
278
 
    Geom::Point min(bbox.x0, bbox.y0);
279
 
    Geom::Point max(bbox.x1, bbox.y1);
280
 
    Geom::Rect tmp_bbox(min, max);
281
 
 
282
 
    Geom::Rect enlarged = filter_effect_area(tmp_bbox);
283
 
 
284
 
    bbox.x0 = (NR::ICoord)enlarged.min()[X];
285
 
    bbox.y0 = (NR::ICoord)enlarged.min()[Y];
286
 
    bbox.x1 = (NR::ICoord)enlarged.max()[X];
287
 
    bbox.y1 = (NR::ICoord)enlarged.max()[Y];
 
217
Geom::OptIntRect Filter::compute_drawbox(Inkscape::DrawingItem const *item, Geom::OptRect const &item_bbox) {
 
218
 
 
219
    Geom::OptRect enlarged = filter_effect_area(item_bbox);
 
220
    if (enlarged) {
 
221
        *enlarged *= item->ctm();
 
222
 
 
223
        Geom::OptIntRect ret(enlarged->roundOutwards());
 
224
        return ret;
 
225
    } else {
 
226
        return Geom::OptIntRect();
 
227
    }
288
228
}
289
229
 
290
 
Geom::Rect Filter::filter_effect_area(Geom::Rect const &bbox)
 
230
Geom::OptRect Filter::filter_effect_area(Geom::OptRect const &bbox)
291
231
{
292
232
    Geom::Point minp, maxp;
293
 
    double len_x = bbox.max()[X] - bbox.min()[X];
294
 
    double len_y = bbox.max()[Y] - bbox.min()[Y];
 
233
    double len_x = bbox ? bbox->width() : 0;
 
234
    double len_y = bbox ? bbox->height() : 0;
295
235
    /* TODO: fetch somehow the object ex and em lengths */
296
236
    _region_x.update(12, 6, len_x);
297
237
    _region_y.update(12, 6, len_y);
298
238
    _region_width.update(12, 6, len_x);
299
239
    _region_height.update(12, 6, len_y);
300
240
    if (_filter_units == SP_FILTER_UNITS_OBJECTBOUNDINGBOX) {
 
241
        if (!bbox) return Geom::OptRect();
 
242
 
301
243
        if (_region_x.unit == SVGLength::PERCENT) {
302
 
            minp[X] = bbox.min()[X] + _region_x.computed;
 
244
            minp[X] = bbox->left() + _region_x.computed;
303
245
        } else {
304
 
            minp[X] = bbox.min()[X] + _region_x.computed * len_x;
 
246
            minp[X] = bbox->left() + _region_x.computed * len_x;
305
247
        }
306
248
        if (_region_width.unit == SVGLength::PERCENT) {
307
249
            maxp[X] = minp[X] + _region_width.computed;
310
252
        }
311
253
 
312
254
        if (_region_y.unit == SVGLength::PERCENT) {
313
 
            minp[Y] = bbox.min()[Y] + _region_y.computed;
 
255
            minp[Y] = bbox->top() + _region_y.computed;
314
256
        } else {
315
 
            minp[Y] = bbox.min()[Y] + _region_y.computed * len_y;
 
257
            minp[Y] = bbox->top() + _region_y.computed * len_y;
316
258
        }
317
259
        if (_region_height.unit == SVGLength::PERCENT) {
318
260
            maxp[Y] = minp[Y] + _region_height.computed;
326
268
        minp[Y] = _region_y.computed;
327
269
        maxp[Y] = minp[Y] + _region_height.computed;
328
270
    } else {
329
 
        g_warning("Error in Inkscape::Filters::Filter::bbox_enlarge: unrecognized value of _filter_units");
 
271
        g_warning("Error in Inkscape::Filters::Filter::filter_effect_area: unrecognized value of _filter_units");
330
272
    }
331
 
    Geom::Rect area(minp, maxp);
 
273
    Geom::OptRect area(minp, maxp);
332
274
    return area;
333
275
}
334
276
 
 
277
double Filter::complexity(Geom::Affine const &ctm)
 
278
{
 
279
    double factor = 1.0;
 
280
    for (unsigned i = 0 ; i < _primitive.size() ; i++) {
 
281
        if (_primitive[i]) {
 
282
            double f = _primitive[i]->complexity(ctm);
 
283
            factor += (f - 1.0);
 
284
        }
 
285
    }
 
286
    return factor;
 
287
}
 
288
 
 
289
bool Filter::uses_background()
 
290
{
 
291
    for (unsigned i = 0 ; i < _primitive.size() ; i++) {
 
292
        if (_primitive[i] && _primitive[i]->uses_background()) {
 
293
            return true;
 
294
        }
 
295
    }
 
296
    return false;
 
297
}
 
298
 
335
299
/* Constructor table holds pointers to static methods returning filter
336
300
 * primitives. This table is indexed with FilterPrimitiveType, so that
337
301
 * for example method in _constructor[NR_FILTER_GAUSSIANBLUR]
367
331
    created = true;
368
332
}
369
333
 
370
 
/** Helper method for enlarging table of filter primitives. When new
371
 
 * primitives are added, but we have no space for them, this function
372
 
 * makes some more space.
373
 
 */
374
 
void Filter::_enlarge_primitive_table() {
375
 
    FilterPrimitive **new_tbl = new FilterPrimitive*[_primitive_table_size * 2];
376
 
    for (int i = 0 ; i < _primitive_count ; i++) {
377
 
        new_tbl[i] = _primitive[i];
378
 
    }
379
 
    _primitive_table_size *= 2;
380
 
    for (int i = _primitive_count ; i < _primitive_table_size ; i++) {
381
 
        new_tbl[i] = NULL;
382
 
    }
383
 
    if(_primitive != NULL) {
384
 
        delete[] _primitive;
385
 
    } else {
386
 
        g_warning("oh oh");
387
 
    }
388
 
    _primitive = new_tbl;
389
 
}
390
 
 
391
334
int Filter::add_primitive(FilterPrimitiveType type)
392
335
{
393
336
    _create_constructor_table();
398
341
    if (!_constructor[type]) return -1;
399
342
    FilterPrimitive *created = _constructor[type]();
400
343
 
401
 
    // If there is no space for new filter primitive, enlarge the table
402
 
    if (_primitive_count >= _primitive_table_size) {
403
 
        _enlarge_primitive_table();
404
 
    }
405
 
 
406
 
    _primitive[_primitive_count] = created;
407
 
    int handle = _primitive_count;
408
 
    _primitive_count++;
 
344
    int handle = _primitive.size();
 
345
    _primitive.push_back(created);
409
346
    return handle;
410
347
}
411
348
 
415
352
 
416
353
    // Check that target is valid primitive inside this filter
417
354
    if (target < 0) return -1;
418
 
    if (target >= _primitive_count) return -1;
419
 
    if (!_primitive[target]) return -1;
 
355
    if (static_cast<unsigned>(target) >= _primitive.size()) return -1;
420
356
 
421
357
    // Check that we can create a new filter of specified type
422
358
    if (type < 0 || type >= NR_FILTER_ENDPRIMITIVETYPE)
424
360
    if (!_constructor[type]) return -1;
425
361
    FilterPrimitive *created = _constructor[type]();
426
362
 
427
 
    // If there is no space for new filter primitive, enlarge the table
428
 
    if (_primitive_count >= _primitive_table_size) {
429
 
        _enlarge_primitive_table();
430
 
    }
431
 
 
432
363
    delete _primitive[target];
433
364
    _primitive[target] = created;
434
365
    return target;
435
366
}
436
367
 
437
368
FilterPrimitive *Filter::get_primitive(int handle) {
438
 
    if (handle < 0 || handle >= _primitive_count) return NULL;
 
369
    if (handle < 0 || handle >= static_cast<int>(_primitive.size())) return NULL;
439
370
    return _primitive[handle];
440
371
}
441
372
 
442
373
void Filter::clear_primitives()
443
374
{
444
 
    for (int i = 0 ; i < _primitive_count ; i++) {
445
 
        if (_primitive[i]) delete _primitive[i];
 
375
    for (unsigned i = 0 ; i < _primitive.size() ; i++) {
 
376
        delete _primitive[i];
446
377
    }
447
 
    _primitive_count = 0;
 
378
    _primitive.clear();
448
379
}
449
380
 
450
381
void Filter::set_x(SVGLength const &length)
508
439
}
509
440
 
510
441
std::pair<double,double> Filter::_filter_resolution(
511
 
    Geom::Rect const &area, Geom::Matrix const &trans,
 
442
    Geom::Rect const &area, Geom::Affine const &trans,
512
443
    FilterQuality const filterquality) const
513
444
{
514
445
    std::pair<double,double> resolution;
563
494
  fill-column:99
564
495
  End:
565
496
*/
566
 
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
 
497
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :