Code

354b31b4da65964bc1a4d4992869844d394b44b0
[inkscape.git] / src / display / nr-filter-slot.cpp
1 /*
2  * A container class for filter slots. Allows for simple getting and
3  * setting images in filter slots without having to bother with
4  * table indexes and such.
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 <assert.h>
15 #include <string.h>
17 #include "display/nr-arena-item.h"
18 #include "display/nr-filter-types.h"
19 #include "display/nr-filter-gaussian.h"
20 #include "display/nr-filter-slot.h"
21 #include "display/nr-filter-getalpha.h"
22 #include "display/nr-filter-units.h"
23 #include "display/pixblock-scaler.h"
24 #include "display/pixblock-transform.h"
25 #include "libnr/nr-pixblock.h"
26 #include "libnr/nr-blit.h"
28 __attribute__ ((const))
29 inline static int _max4(const double a, const double b,
30                         const double c, const double d) {
31     double ret = a;
32     if (b > ret) ret = b;
33     if (c > ret) ret = c;
34     if (d > ret) ret = d;
35     return (int)round(ret);
36 }
38 __attribute__ ((const))
39 inline static int _min4(const double a, const double b,
40                         const double c, const double d) {
41     double ret = a;
42     if (b < ret) ret = b;
43     if (c < ret) ret = c;
44     if (d < ret) ret = d;
45     return (int)round(ret);
46 }
48 __attribute__ ((const))
49 inline static int _max2(const double a, const double b) {
50     if (a > b)
51         return (int)round(a);
52     else
53         return (int)round(b);
54 }
56 __attribute__ ((const))
57 inline static int _min2(const double a, const double b) {
58     if (a > b)
59         return (int)round(b);
60     else
61         return (int)round(a);
62 }
64 namespace Inkscape {
65 namespace Filters {
67 FilterSlot::FilterSlot(int slots, NRArenaItem const *item)
68     : _last_out(-1),
69       filterquality(FILTER_QUALITY_BEST),
70       blurquality(BLUR_QUALITY_BEST),
71       _arena_item(item)
72 {
73     _slots.reserve((slots > 0) ? slots : 2);
74 }
76 FilterSlot::~FilterSlot()
77 {
78     for (unsigned int i = 0 ; i < _slots.size() ; i++) {
79         if (_slots[i].owned) {
80             nr_pixblock_release(_slots[i].pb);
81             delete _slots[i].pb;
82         }
83     }
84 }
86 FilterSlot::slot_entry_t::~slot_entry_t()
87 {
88     // It's a bad idea to destruct pixblocks here, as this will also be called upon resizing _slots
89 }
91 NRPixBlock *FilterSlot::get(int slot_nr)
92 {
93     int index = _get_index(slot_nr);
94     assert(index >= 0);
96     /* If we didn't have the specified image, but we could create it
97      * from the other information we have, let's do that */
98     if (_slots[index].pb == NULL
99         && (slot_nr == NR_FILTER_SOURCEALPHA
100             || slot_nr == NR_FILTER_BACKGROUNDIMAGE
101             || slot_nr == NR_FILTER_BACKGROUNDALPHA
102             || slot_nr == NR_FILTER_FILLPAINT
103             || slot_nr == NR_FILTER_STROKEPAINT))
104     {
105         /* If needed, fetch background */
106         if (slot_nr == NR_FILTER_BACKGROUNDIMAGE) {
107             NRPixBlock *pb;
108             pb = nr_arena_item_get_background(_arena_item);
109             if (pb) {
110                 pb->empty = false;
111                 this->set(NR_FILTER_BACKGROUNDIMAGE, pb, false);
112             } else {
113                 NRPixBlock *source = this->get(NR_FILTER_SOURCEGRAPHIC);
114                 pb = new NRPixBlock();
115                 if (!pb) return NULL; // Allocation failed
116                 nr_pixblock_setup_fast(pb, source->mode,
117                                        source->area.x0, source->area.y0,
118                                        source->area.x1, source->area.y1, true);
119                 if (pb->size != NR_PIXBLOCK_SIZE_TINY && pb->data.px == NULL) {
120                     // allocation failed
121                     delete pb;
122                     return NULL;
123                 }
124                 pb->empty = FALSE;
125                 this->set(NR_FILTER_BACKGROUNDIMAGE, pb);
126             }
127         } else if (slot_nr == NR_FILTER_SOURCEALPHA) {
128             /* If only a alpha channel is needed, strip it from full image */
129             NRPixBlock *src = get(NR_FILTER_SOURCEGRAPHIC);
130             NRPixBlock *sa = filter_get_alpha(src);
131             set(NR_FILTER_SOURCEALPHA, sa);
132         } else if (slot_nr == NR_FILTER_BACKGROUNDALPHA) {
133             NRPixBlock *src = get(NR_FILTER_BACKGROUNDIMAGE);
134             NRPixBlock *ba = filter_get_alpha(src);
135             set(NR_FILTER_BACKGROUNDALPHA, ba);
136         } else if (slot_nr == NR_FILTER_FILLPAINT) {
137             /* When a paint is needed, fetch it from arena item */
138             // TODO
139         } else if (slot_nr == NR_FILTER_STROKEPAINT) {
140             // TODO
141         }
142     }
144     if (_slots[index].pb) {
145         _slots[index].pb->empty = false;
146     }
148     assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slots[index].number == slot_nr);
149     return _slots[index].pb;
152 void FilterSlot::get_final(int slot_nr, NRPixBlock *result) {
153     NRPixBlock *final_usr = get(slot_nr);
154     Geom::Matrix trans = units.get_matrix_pb2display();
156     int size = (result->area.x1 - result->area.x0)
157         * (result->area.y1 - result->area.y0)
158         * NR_PIXBLOCK_BPP(result);
159     memset(NR_PIXBLOCK_PX(result), 0, size);
161     if (fabs(trans[1]) > 1e-6 || fabs(trans[2]) > 1e-6) {
162         if (filterquality == FILTER_QUALITY_BEST) {
163             NR::transform_bicubic(result, final_usr, trans);
164         } else {
165             NR::transform_nearest(result, final_usr, trans);
166         }
167     } else if (fabs(trans[0] - 1) > 1e-6 || fabs(trans[3] - 1) > 1e-6) {
168         NR::scale_bicubic(result, final_usr, trans);
169     } else {
170         nr_blit_pixblock_pixblock(result, final_usr);
171     }
174 void FilterSlot::set(int slot_nr, NRPixBlock *pb, bool takeOwnership)
176     /* Unnamed slot is for saving filter primitive results, when parameter
177      * 'result' is not set. Only the filter immediately after this one
178      * can access unnamed results, so we don't have to worry about overwriting
179      * previous results in filter chain. On the other hand, we may not
180      * overwrite any other image with this one, because they might be
181      * accessed later on. */
182     int index = ((slot_nr != NR_FILTER_SLOT_NOT_SET)
183                  ? _get_index(slot_nr)
184                  : _get_index(NR_FILTER_UNNAMED_SLOT));
185     assert(index >= 0);
186     // Unnamed slot is only for Inkscape::Filters::FilterSlot internal use.
187     assert(slot_nr != NR_FILTER_UNNAMED_SLOT);
188     assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slots[index].number == slot_nr);
190     if (slot_nr == NR_FILTER_SOURCEGRAPHIC || slot_nr == NR_FILTER_BACKGROUNDIMAGE) {
191         Geom::Matrix trans = units.get_matrix_display2pb();
192         if (fabs(trans[1]) > 1e-6 || fabs(trans[2]) > 1e-6) {
193             NRPixBlock *trans_pb = new NRPixBlock;
194             int x0 = pb->area.x0;
195             int y0 = pb->area.y0;
196             int x1 = pb->area.x1;
197             int y1 = pb->area.y1;
198             int min_x = _min4(trans[0] * x0 + trans[2] * y0 + trans[4],
199                               trans[0] * x0 + trans[2] * y1 + trans[4],
200                               trans[0] * x1 + trans[2] * y0 + trans[4],
201                               trans[0] * x1 + trans[2] * y1 + trans[4]);
202             int max_x = _max4(trans[0] * x0 + trans[2] * y0 + trans[4],
203                               trans[0] * x0 + trans[2] * y1 + trans[4],
204                               trans[0] * x1 + trans[2] * y0 + trans[4],
205                               trans[0] * x1 + trans[2] * y1 + trans[4]);
206             int min_y = _min4(trans[1] * x0 + trans[3] * y0 + trans[5],
207                               trans[1] * x0 + trans[3] * y1 + trans[5],
208                               trans[1] * x1 + trans[3] * y0 + trans[5],
209                               trans[1] * x1 + trans[3] * y1 + trans[5]);
210             int max_y = _max4(trans[1] * x0 + trans[3] * y0 + trans[5],
211                               trans[1] * x0 + trans[3] * y1 + trans[5],
212                               trans[1] * x1 + trans[3] * y0 + trans[5],
213                               trans[1] * x1 + trans[3] * y1 + trans[5]);
215             nr_pixblock_setup_fast(trans_pb, pb->mode,
216                                    min_x, min_y,
217                                    max_x, max_y, true);
218             if (trans_pb->size != NR_PIXBLOCK_SIZE_TINY && trans_pb->data.px == NULL) {
219                 /* TODO: this gets hit occasionally. Worst case scenario:
220                  * images are exported in horizontal stripes. One stripe
221                  * is not too high, but can get thousands of pixels wide.
222                  * Rotate this 45 degrees -> _huge_ image */
223                 g_warning("Memory allocation failed in Inkscape::Filters::FilterSlot::set (transform)");
224                 if (takeOwnership) {
225                     nr_pixblock_release(pb);
226                     delete pb;
227                 }
228                 return;
229             }
230             if (filterquality == FILTER_QUALITY_BEST) {
231                 NR::transform_bicubic(trans_pb, pb, trans);
232             } else {
233                 NR::transform_nearest(trans_pb, pb, trans);
234             }
235             if (takeOwnership) {
236                 nr_pixblock_release(pb);
237                 delete pb;
238             }
239             takeOwnership = true;
240             pb = trans_pb;
241         } else if (fabs(trans[0] - 1) > 1e-6 || fabs(trans[3] - 1) > 1e-6) {
242             NRPixBlock *trans_pb = new NRPixBlock;
244             int x0 = pb->area.x0;
245             int y0 = pb->area.y0;
246             int x1 = pb->area.x1;
247             int y1 = pb->area.y1;
248             int min_x = _min2(trans[0] * x0 + trans[4],
249                               trans[0] * x1 + trans[4]);
250             int max_x = _max2(trans[0] * x0 + trans[4],
251                               trans[0] * x1 + trans[4]);
252             int min_y = _min2(trans[3] * y0 + trans[5],
253                               trans[3] * y1 + trans[5]);
254             int max_y = _max2(trans[3] * y0 + trans[5],
255                               trans[3] * y1 + trans[5]);
257             nr_pixblock_setup_fast(trans_pb, pb->mode,
258                                    min_x, min_y, max_x, max_y, true);
259             if (trans_pb->size != NR_PIXBLOCK_SIZE_TINY && trans_pb->data.px == NULL) {
260                 g_warning("Memory allocation failed in Inkscape::Filters::FilterSlot::set (scaling)");
261                 if (takeOwnership) {
262                     nr_pixblock_release(pb);
263                     delete pb;
264                 }
265                 return;
266             }
267             NR::scale_bicubic(trans_pb, pb, trans);
268             if (takeOwnership) {
269                 nr_pixblock_release(pb);
270                 delete pb;
271             }
272             takeOwnership = true;
273             pb = trans_pb;
274         }
275     }
277     if(_slots[index].owned) {
278         nr_pixblock_release(_slots[index].pb);
279         delete _slots[index].pb;
280     }
281     _slots[index].pb = pb;
282     _slots[index].owned = takeOwnership;
283     _last_out = index;
286 int FilterSlot::get_slot_count()
288     return _slots.size();
291 NRArenaItem const* FilterSlot::get_arenaitem()
293         return _arena_item;
296 int FilterSlot::_get_index(int slot_nr)
298     assert(slot_nr >= 0 ||
299            slot_nr == NR_FILTER_SLOT_NOT_SET ||
300            slot_nr == NR_FILTER_SOURCEGRAPHIC ||
301            slot_nr == NR_FILTER_SOURCEALPHA ||
302            slot_nr == NR_FILTER_BACKGROUNDIMAGE ||
303            slot_nr == NR_FILTER_BACKGROUNDALPHA ||
304            slot_nr == NR_FILTER_FILLPAINT ||
305            slot_nr == NR_FILTER_STROKEPAINT ||
306            slot_nr == NR_FILTER_UNNAMED_SLOT);
308     if (slot_nr == NR_FILTER_SLOT_NOT_SET) {
309         return _last_out;
310     }
312     /* Search, if the slot already exists */
313     for (int i = 0 ; i < (int)_slots.size() ; i++) {
314         if (_slots[i].number == slot_nr) {
315             return i;
316         }
317     }
319     /* If the slot doesn't already exist, create it */
320     slot_entry_t entry;
321     entry.number = slot_nr;
322     _slots.push_back(entry);
323     return (int)_slots.size()-1;
326 void FilterSlot::set_units(FilterUnits const &units) {
327     this->units = units;
330 void FilterSlot::set_quality(FilterQuality const q) {
331     filterquality = q;
334 void FilterSlot::set_blurquality(int const q) {
335     blurquality = q;
338 int FilterSlot::get_blurquality(void) {
339     return blurquality;
342 } /* namespace Filters */
343 } /* namespace Inkscape */
345 /*
346   Local Variables:
347   mode:c++
348   c-file-style:"stroustrup"
349   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
350   indent-tabs-mode:nil
351   fill-column:99
352   End:
353 */
354 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :