Code

245bdfa4815b68e206c74cbc0111b7659b87e753
[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     _slot_count = ((slots > 0) ? slots : 2);
74     _slot = new NRPixBlock*[_slot_count];
75     _slot_number = new int[_slot_count];
77     for (int i = 0 ; i < _slot_count ; i++) {
78         _slot[i] = NULL;
79         _slot_number[i] = NR_FILTER_SLOT_NOT_SET;
80     }
81 }
83 FilterSlot::~FilterSlot()
84 {
85     for (int i = 0 ; i < _slot_count ; i++) {
86         if (_slot[i]) {
87             nr_pixblock_release(_slot[i]);
88             delete _slot[i];
89         }
90     }
91     delete[] _slot;
92     delete[] _slot_number;
93 }
95 NRPixBlock *FilterSlot::get(int slot_nr)
96 {
97     int index = _get_index(slot_nr);
98     assert(index >= 0);
100     /* If we didn't have the specified image, but we could create it
101      * from the other information we have, let's do that */
102     if (_slot[index] == NULL
103         && (slot_nr == NR_FILTER_SOURCEALPHA
104             || slot_nr == NR_FILTER_BACKGROUNDIMAGE
105             || slot_nr == NR_FILTER_BACKGROUNDALPHA
106             || slot_nr == NR_FILTER_FILLPAINT
107             || slot_nr == NR_FILTER_STROKEPAINT))
108     {
109         /* If needed, fetch background */
110         if (slot_nr == NR_FILTER_BACKGROUNDIMAGE) {
111             NRPixBlock *pb;
112             pb = nr_arena_item_get_background(_arena_item);
113             if (pb) {
114                 pb->empty = false;
115                 this->set(NR_FILTER_BACKGROUNDIMAGE, pb);
116             } else {
117                 NRPixBlock *source = this->get(NR_FILTER_SOURCEGRAPHIC);
118                 pb = new NRPixBlock();
119                 if (!pb) return NULL; // Allocation failed
120                 nr_pixblock_setup_fast(pb, source->mode,
121                                        source->area.x0, source->area.y0,
122                                        source->area.x1, source->area.y1, true);
123                 if (pb->size != NR_PIXBLOCK_SIZE_TINY && pb->data.px == NULL) {
124                     // allocation failed
125                     delete pb;
126                     return NULL;
127                 }
128                 pb->empty = FALSE;
129                 this->set(NR_FILTER_BACKGROUNDIMAGE, pb);
130             }
131         } else if (slot_nr == NR_FILTER_SOURCEALPHA) {
132             /* If only a alpha channel is needed, strip it from full image */
133             NRPixBlock *src = get(NR_FILTER_SOURCEGRAPHIC);
134             NRPixBlock *sa = filter_get_alpha(src);
135             set(NR_FILTER_SOURCEALPHA, sa);
136         } else if (slot_nr == NR_FILTER_BACKGROUNDALPHA) {
137             NRPixBlock *src = get(NR_FILTER_BACKGROUNDIMAGE);
138             NRPixBlock *ba = filter_get_alpha(src);
139             set(NR_FILTER_BACKGROUNDALPHA, ba);
140         } else if (slot_nr == NR_FILTER_FILLPAINT) {
141             /* When a paint is needed, fetch it from arena item */
142             // TODO
143         } else if (slot_nr == NR_FILTER_STROKEPAINT) {
144             // TODO
145         }
146     }
148     if (_slot[index]) {
149         _slot[index]->empty = false;
150     }
152     assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slot_number[index] == slot_nr);
153     return _slot[index];
156 void FilterSlot::get_final(int slot_nr, NRPixBlock *result) {
157     NRPixBlock *final_usr = get(slot_nr);
158     Geom::Matrix trans = units.get_matrix_pb2display();
160     int size = (result->area.x1 - result->area.x0)
161         * (result->area.y1 - result->area.y0)
162         * NR_PIXBLOCK_BPP(result);
163     memset(NR_PIXBLOCK_PX(result), 0, size);
165     if (fabs(trans[1]) > 1e-6 || fabs(trans[2]) > 1e-6) {
166         if (filterquality == FILTER_QUALITY_BEST) {
167             NR::transform_bicubic(result, final_usr, trans);
168         } else {
169             NR::transform_nearest(result, final_usr, trans);
170         }
171     } else if (fabs(trans[0] - 1) > 1e-6 || fabs(trans[3] - 1) > 1e-6) {
172         NR::scale_bicubic(result, final_usr, trans);
173     } else {
174         nr_blit_pixblock_pixblock(result, final_usr);
175     }
178 void FilterSlot::set(int slot_nr, NRPixBlock *pb)
180     /* Unnamed slot is for saving filter primitive results, when parameter
181      * 'result' is not set. Only the filter immediately after this one
182      * can access unnamed results, so we don't have to worry about overwriting
183      * previous results in filter chain. On the other hand, we may not
184      * overwrite any other image with this one, because they might be
185      * accessed later on. */
186     int index = ((slot_nr != NR_FILTER_SLOT_NOT_SET)
187                  ? _get_index(slot_nr)
188                  : _get_index(NR_FILTER_UNNAMED_SLOT));
189     assert(index >= 0);
190     // Unnamed slot is only for Inkscape::Filters::FilterSlot internal use.
191     assert(slot_nr != NR_FILTER_UNNAMED_SLOT);
192     assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slot_number[index] == slot_nr);
194     if (slot_nr == NR_FILTER_SOURCEGRAPHIC || slot_nr == NR_FILTER_BACKGROUNDIMAGE) {
195         Geom::Matrix trans = units.get_matrix_display2pb();
196         if (fabs(trans[1]) > 1e-6 || fabs(trans[2]) > 1e-6) {
197             NRPixBlock *trans_pb = new NRPixBlock;
198             int x0 = pb->area.x0;
199             int y0 = pb->area.y0;
200             int x1 = pb->area.x1;
201             int y1 = pb->area.y1;
202             int min_x = _min4(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 max_x = _max4(trans[0] * x0 + trans[2] * y0 + trans[4],
207                               trans[0] * x0 + trans[2] * y1 + trans[4],
208                               trans[0] * x1 + trans[2] * y0 + trans[4],
209                               trans[0] * x1 + trans[2] * y1 + trans[4]);
210             int min_y = _min4(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]);
214             int max_y = _max4(trans[1] * x0 + trans[3] * y0 + trans[5],
215                               trans[1] * x0 + trans[3] * y1 + trans[5],
216                               trans[1] * x1 + trans[3] * y0 + trans[5],
217                               trans[1] * x1 + trans[3] * y1 + trans[5]);
219             nr_pixblock_setup_fast(trans_pb, pb->mode,
220                                    min_x, min_y,
221                                    max_x, max_y, true);
222             if (trans_pb->size != NR_PIXBLOCK_SIZE_TINY && trans_pb->data.px == NULL) {
223                 /* TODO: this gets hit occasionally. Worst case scenario:
224                  * images are exported in horizontal stripes. One stripe
225                  * is not too high, but can get thousands of pixels wide.
226                  * Rotate this 45 degrees -> _huge_ image */
227                 g_warning("Memory allocation failed in Inkscape::Filters::FilterSlot::set (transform)");
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             nr_pixblock_release(pb);
236             delete pb;
237             pb = trans_pb;
238         } else if (fabs(trans[0] - 1) > 1e-6 || fabs(trans[3] - 1) > 1e-6) {
239             NRPixBlock *trans_pb = new NRPixBlock;
241             int x0 = pb->area.x0;
242             int y0 = pb->area.y0;
243             int x1 = pb->area.x1;
244             int y1 = pb->area.y1;
245             int min_x = _min2(trans[0] * x0 + trans[4],
246                               trans[0] * x1 + trans[4]);
247             int max_x = _max2(trans[0] * x0 + trans[4],
248                               trans[0] * x1 + trans[4]);
249             int min_y = _min2(trans[3] * y0 + trans[5],
250                               trans[3] * y1 + trans[5]);
251             int max_y = _max2(trans[3] * y0 + trans[5],
252                               trans[3] * y1 + trans[5]);
254             nr_pixblock_setup_fast(trans_pb, pb->mode,
255                                    min_x, min_y, max_x, max_y, true);
256             if (trans_pb->size != NR_PIXBLOCK_SIZE_TINY && trans_pb->data.px == NULL) {
257                 g_warning("Memory allocation failed in Inkscape::Filters::FilterSlot::set (scaling)");
258                 return;
259             }
260             NR::scale_bicubic(trans_pb, pb, trans);
261             nr_pixblock_release(pb);
262             delete pb;
263             pb = trans_pb;
264         }
265     }
267     if(_slot[index]) {
268         nr_pixblock_release(_slot[index]);
269         delete _slot[index];
270     }
271     _slot[index] = pb;
272     _last_out = index;
275 int FilterSlot::get_slot_count()
277     int seek = _slot_count;
278     do {
279         seek--;
280     } while (!_slot[seek] && _slot_number[seek] == NR_FILTER_SLOT_NOT_SET);
282     return seek + 1;
285 NRArenaItem const* FilterSlot::get_arenaitem()
287         return _arena_item;
290 int FilterSlot::_get_index(int slot_nr)
292     assert(slot_nr >= 0 ||
293            slot_nr == NR_FILTER_SLOT_NOT_SET ||
294            slot_nr == NR_FILTER_SOURCEGRAPHIC ||
295            slot_nr == NR_FILTER_SOURCEALPHA ||
296            slot_nr == NR_FILTER_BACKGROUNDIMAGE ||
297            slot_nr == NR_FILTER_BACKGROUNDALPHA ||
298            slot_nr == NR_FILTER_FILLPAINT ||
299            slot_nr == NR_FILTER_STROKEPAINT ||
300            slot_nr == NR_FILTER_UNNAMED_SLOT);
302     int index = -1;
303     if (slot_nr == NR_FILTER_SLOT_NOT_SET) {
304         return _last_out;
305     }
306     /* Search, if the slot already exists */
307     for (int i = 0 ; i < _slot_count ; i++) {
308         if (_slot_number[i] == slot_nr) {
309             index = i;
310             break;
311         }
312     }
314     /* If the slot doesn't already exist, create it */
315     if (index == -1) {
316         int seek = _slot_count;
317         do {
318             seek--;
319         } while ((seek >= 0) && (_slot_number[seek] == NR_FILTER_SLOT_NOT_SET));
320         /* If there is no space for more slots, create more space */
321         if (seek == _slot_count - 1) {
322             NRPixBlock **new_slot = new NRPixBlock*[_slot_count * 2];
323             int *new_number = new int[_slot_count * 2];
324             for (int i = 0 ; i < _slot_count ; i++) {
325                 new_slot[i] = _slot[i];
326                 new_number[i] = _slot_number[i];
327             }
328             for (int i = _slot_count ; i < _slot_count * 2 ; i++) {
329                 new_slot[i] = NULL;
330                 new_number[i] = NR_FILTER_SLOT_NOT_SET;
331             }
332             delete[] _slot;
333             delete[] _slot_number;
334             _slot = new_slot;
335             _slot_number = new_number;
336             _slot_count *= 2;
337         }
338         /* Now that there is space, create the slot */
339         _slot_number[seek + 1] = slot_nr;
340         index = seek + 1;
341     }
342     return index;
345 void FilterSlot::set_units(FilterUnits const &units) {
346     this->units = units;
349 void FilterSlot::set_quality(FilterQuality const q) {
350     filterquality = q;
353 void FilterSlot::set_blurquality(int const q) {
354     g_warning("FilterSlot::set_blurquality");
355     blurquality = q;
358 int FilterSlot::get_blurquality(void) {
359     return blurquality;
362 } /* namespace Filters */
363 } /* namespace Inkscape */
365 /*
366   Local Variables:
367   mode:c++
368   c-file-style:"stroustrup"
369   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
370   indent-tabs-mode:nil
371   fill-column:99
372   End:
373 */
374 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :