Code

From trunk
[inkscape.git] / src / display / nr-filter-slot.cpp
1 #define __NR_FILTER_SLOT_CPP__
3 /*
4  * A container class for filter slots. Allows for simple getting and
5  * setting images in filter slots without having to bother with
6  * table indexes and such.
7  *
8  * Author:
9  *   Niko Kiirala <niko@kiirala.com>
10  *
11  * Copyright (C) 2006,2007 Niko Kiirala
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #include <assert.h>
17 #include <string.h>
19 #include "display/nr-arena-item.h"
20 #include "display/nr-filter-types.h"
21 #include "display/nr-filter-slot.h"
22 #include "display/nr-filter-getalpha.h"
23 #include "display/nr-filter-units.h"
24 #include "display/pixblock-scaler.h"
25 #include "display/pixblock-transform.h"
26 #include "libnr/nr-pixblock.h"
27 #include "libnr/nr-blit.h"
29 __attribute__ ((const))
30 inline static int _max4(const double a, const double b,
31                         const double c, const double d) {
32     double ret = a;
33     if (b > ret) ret = b;
34     if (c > ret) ret = c;
35     if (d > ret) ret = d;
36     return (int)round(ret);
37 }
39 __attribute__ ((const))
40 inline static int _min4(const double a, const double b,
41                         const double c, const double d) {
42     double ret = a;
43     if (b < ret) ret = b;
44     if (c < ret) ret = c;
45     if (d < ret) ret = d;
46     return (int)round(ret);
47 }
49 __attribute__ ((const))
50 inline static int _max2(const double a, const double b) {
51     if (a > b)
52         return (int)round(a);
53     else
54         return (int)round(b);
55 }
57 __attribute__ ((const))
58 inline static int _min2(const double a, const double b) {
59     if (a > b)
60         return (int)round(b);
61     else
62         return (int)round(a);
63 }
65 namespace NR {
67 FilterSlot::FilterSlot(int slots, NRArenaItem const *item)
68     : filterquality(FILTER_QUALITY_BEST),
69       _last_out(-1),
70       _arena_item(item)
71 {
72     _slot_count = ((slots > 0) ? slots : 2);
73     _slot = new NRPixBlock*[_slot_count];
74     _slot_number = new int[_slot_count];
76     for (int i = 0 ; i < _slot_count ; i++) {
77         _slot[i] = NULL;
78         _slot_number[i] = NR_FILTER_SLOT_NOT_SET;
79     }
80 }
82 FilterSlot::~FilterSlot()
83 {
84     for (int i = 0 ; i < _slot_count ; i++) {
85         if (_slot[i]) {
86             nr_pixblock_release(_slot[i]);
87             delete _slot[i];
88         }
89     }
90     delete[] _slot;
91     delete[] _slot_number;
92 }
94 NRPixBlock *FilterSlot::get(int slot_nr)
95 {
96     int index = _get_index(slot_nr);
97     assert(index >= 0);
99     /* If we didn't have the specified image, but we could create it
100      * from the other information we have, let's do that */
101     if (_slot[index] == NULL
102         && (slot_nr == NR_FILTER_SOURCEALPHA
103             || slot_nr == NR_FILTER_BACKGROUNDIMAGE
104             || slot_nr == NR_FILTER_BACKGROUNDALPHA
105             || slot_nr == NR_FILTER_FILLPAINT
106             || slot_nr == NR_FILTER_STROKEPAINT))
107     {
108         /* If needed, fetch background */
109         if (slot_nr == NR_FILTER_BACKGROUNDIMAGE) {
110             NRPixBlock *pb;
111             pb = nr_arena_item_get_background(_arena_item);
112             if (pb) {
113                 pb->empty = false;
114                 this->set(NR_FILTER_BACKGROUNDIMAGE, pb);
115             } else {
116                 NRPixBlock *source = this->get(NR_FILTER_SOURCEGRAPHIC);
117                 pb = new NRPixBlock();
118                 if (!pb) return NULL; // Allocation failed
119                 nr_pixblock_setup_fast(pb, source->mode,
120                                        source->area.x0, source->area.y0,
121                                        source->area.x1, source->area.y1, true);
122                 if (pb->size != NR_PIXBLOCK_SIZE_TINY && pb->data.px == NULL) {
123                     // allocation failed
124                     delete pb;
125                     return NULL;
126                 }
127                 pb->empty = FALSE;
128                 this->set(NR_FILTER_BACKGROUNDIMAGE, pb);
129             }
130         } else if (slot_nr == NR_FILTER_SOURCEALPHA) {
131             /* If only a alpha channel is needed, strip it from full image */
132             NRPixBlock *src = get(NR_FILTER_SOURCEGRAPHIC);
133             NRPixBlock *sa = filter_get_alpha(src);
134             set(NR_FILTER_SOURCEALPHA, sa);
135         } else if (slot_nr == NR_FILTER_BACKGROUNDALPHA) {
136             NRPixBlock *src = get(NR_FILTER_BACKGROUNDIMAGE);
137             NRPixBlock *ba = filter_get_alpha(src);
138             set(NR_FILTER_BACKGROUNDALPHA, ba);
139         } else if (slot_nr == NR_FILTER_FILLPAINT) {
140             /* When a paint is needed, fetch it from arena item */
141             // TODO
142         } else if (slot_nr == NR_FILTER_STROKEPAINT) {
143             // TODO
144         }
145     }
147     if (_slot[index]) {
148         _slot[index]->empty = false;
149     }
151     assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slot_number[index] == slot_nr);
152     return _slot[index];
155 void FilterSlot::get_final(int slot_nr, NRPixBlock *result) {
156     NRPixBlock *final_usr = get(slot_nr);
157     Matrix trans = units.get_matrix_pb2display();
159     int size = (result->area.x1 - result->area.x0)
160         * (result->area.y1 - result->area.y0)
161         * NR_PIXBLOCK_BPP(result);
162     memset(NR_PIXBLOCK_PX(result), 0, size);
164     if (fabs(trans[1]) > 1e-6 || fabs(trans[2]) > 1e-6) {
165         if (filterquality == FILTER_QUALITY_BEST) {
166             transform_bicubic(result, final_usr, trans);
167         } else {
168             transform_nearest(result, final_usr, trans);
169         }
170     } else if (fabs(trans[0] - 1) > 1e-6 || fabs(trans[3] - 1) > 1e-6) {
171         scale_bicubic(result, final_usr);
172     } else {
173         nr_blit_pixblock_pixblock(result, final_usr);
174     }
177 void FilterSlot::set(int slot_nr, NRPixBlock *pb)
179     /* Unnamed slot is for saving filter primitive results, when parameter
180      * 'result' is not set. Only the filter immediately after this one
181      * can access unnamed results, so we don't have to worry about overwriting
182      * previous results in filter chain. On the other hand, we may not
183      * overwrite any other image with this one, because they might be
184      * accessed later on. */
185     int index = ((slot_nr != NR_FILTER_SLOT_NOT_SET)
186                  ? _get_index(slot_nr)
187                  : _get_index(NR_FILTER_UNNAMED_SLOT));
188     assert(index >= 0);
189     // Unnamed slot is only for NR::FilterSlot internal use.
190     assert(slot_nr != NR_FILTER_UNNAMED_SLOT);
191     assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slot_number[index] == slot_nr);
193     if (slot_nr == NR_FILTER_SOURCEGRAPHIC || slot_nr == NR_FILTER_BACKGROUNDIMAGE) {
194         Matrix trans = units.get_matrix_display2pb();
195         if (fabs(trans[1]) > 1e-6 || fabs(trans[2]) > 1e-6) {
196             NRPixBlock *trans_pb = new NRPixBlock;
197             int x0 = pb->area.x0;
198             int y0 = pb->area.y0;
199             int x1 = pb->area.x1;
200             int y1 = pb->area.y1;
201             int min_x = _min4(trans[0] * x0 + trans[2] * y0 + trans[4],
202                               trans[0] * x0 + trans[2] * y1 + trans[4],
203                               trans[0] * x1 + trans[2] * y0 + trans[4],
204                               trans[0] * x1 + trans[2] * y1 + trans[4]);
205             int max_x = _max4(trans[0] * x0 + trans[2] * y0 + trans[4],
206                               trans[0] * x0 + trans[2] * y1 + trans[4],
207                               trans[0] * x1 + trans[2] * y0 + trans[4],
208                               trans[0] * x1 + trans[2] * y1 + trans[4]);
209             int min_y = _min4(trans[1] * x0 + trans[3] * y0 + trans[5],
210                               trans[1] * x0 + trans[3] * y1 + trans[5],
211                               trans[1] * x1 + trans[3] * y0 + trans[5],
212                               trans[1] * x1 + trans[3] * y1 + trans[5]);
213             int max_y = _max4(trans[1] * x0 + trans[3] * y0 + trans[5],
214                               trans[1] * x0 + trans[3] * y1 + trans[5],
215                               trans[1] * x1 + trans[3] * y0 + trans[5],
216                               trans[1] * x1 + trans[3] * y1 + trans[5]);
217             
218             nr_pixblock_setup_fast(trans_pb, pb->mode,
219                                    min_x, min_y,
220                                    max_x, max_y, true);
221             if (trans_pb->size != NR_PIXBLOCK_SIZE_TINY && trans_pb->data.px == NULL) {
222                 /* TODO: this gets hit occasionally. Worst case scenario:
223                  * images are exported in horizontal stripes. One stripe
224                  * is not too high, but can get thousands of pixels wide.
225                  * Rotate this 45 degrees -> _huge_ image */
226                 g_warning("Memory allocation failed in NR::FilterSlot::set (transform)");
227                 return;
228             }
229             if (filterquality == FILTER_QUALITY_BEST) {
230                 transform_bicubic(trans_pb, pb, trans);
231             } else {
232                 transform_nearest(trans_pb, pb, trans);
233             }
234             nr_pixblock_release(pb);
235             delete pb;
236             pb = trans_pb;
237         } else if (fabs(trans[0] - 1) > 1e-6 || fabs(trans[3] - 1) > 1e-6) {
238             NRPixBlock *trans_pb = new NRPixBlock;
240             int x0 = pb->area.x0;
241             int y0 = pb->area.y0;
242             int x1 = pb->area.x1;
243             int y1 = pb->area.y1;
244             int min_x = _min2(trans[0] * x0 + trans[4],
245                               trans[0] * x1 + trans[4]);
246             int max_x = _max2(trans[0] * x0 + trans[4],
247                               trans[0] * x1 + trans[4]);
248             int min_y = _min2(trans[3] * y0 + trans[5],
249                               trans[3] * y1 + trans[5]);
250             int max_y = _max2(trans[3] * y0 + trans[5],
251                               trans[3] * y1 + trans[5]);
253             nr_pixblock_setup_fast(trans_pb, pb->mode,
254                                    min_x, min_y, max_x, max_y, true);
255             if (trans_pb->size != NR_PIXBLOCK_SIZE_TINY && trans_pb->data.px == NULL) {
256                 g_warning("Memory allocation failed in NR::FilterSlot::set (scaling)");
257                 return;
258             }
259             scale_bicubic(trans_pb, pb);
260             nr_pixblock_release(pb);
261             delete pb;
262             pb = trans_pb;
263         }
264     }
266     if(_slot[index]) {
267         nr_pixblock_release(_slot[index]);
268         delete _slot[index];
269     }
270     _slot[index] = pb;
271     _last_out = index;
274 int FilterSlot::get_slot_count()
276     int seek = _slot_count;
277     do {
278         seek--;
279     } while (!_slot[seek] && _slot_number[seek] == NR_FILTER_SLOT_NOT_SET);
281     return seek + 1;
284 NRArenaItem const* FilterSlot::get_arenaitem()
286         return _arena_item;
289 int FilterSlot::_get_index(int slot_nr)
291     assert(slot_nr >= 0 ||
292            slot_nr == NR_FILTER_SLOT_NOT_SET ||
293            slot_nr == NR_FILTER_SOURCEGRAPHIC ||
294            slot_nr == NR_FILTER_SOURCEALPHA ||
295            slot_nr == NR_FILTER_BACKGROUNDIMAGE ||
296            slot_nr == NR_FILTER_BACKGROUNDALPHA ||
297            slot_nr == NR_FILTER_FILLPAINT ||
298            slot_nr == NR_FILTER_STROKEPAINT ||
299            slot_nr == NR_FILTER_UNNAMED_SLOT);
301     int index = -1;
302     if (slot_nr == NR_FILTER_SLOT_NOT_SET) {
303         return _last_out;
304     }
305     /* Search, if the slot already exists */
306     for (int i = 0 ; i < _slot_count ; i++) {
307         if (_slot_number[i] == slot_nr) {
308             index = i;
309             break;
310         }
311     }
313     /* If the slot doesn't already exist, create it */
314     if (index == -1) {
315         int seek = _slot_count;
316         do {
317             seek--;
318         } while ((seek >= 0) && (_slot_number[seek] == NR_FILTER_SLOT_NOT_SET));
319         /* If there is no space for more slots, create more space */
320         if (seek == _slot_count - 1) {
321             NRPixBlock **new_slot = new NRPixBlock*[_slot_count * 2];
322             int *new_number = new int[_slot_count * 2];
323             for (int i = 0 ; i < _slot_count ; i++) {
324                 new_slot[i] = _slot[i];
325                 new_number[i] = _slot_number[i];
326             }
327             for (int i = _slot_count ; i < _slot_count * 2 ; i++) {
328                 new_slot[i] = NULL;
329                 new_number[i] = NR_FILTER_SLOT_NOT_SET;
330             }
331             delete[] _slot;
332             delete[] _slot_number;
333             _slot = new_slot;
334             _slot_number = new_number;
335             _slot_count *= 2;
336         }
337         /* Now that there is space, create the slot */
338         _slot_number[seek + 1] = slot_nr;
339         index = seek + 1;
340     }
341     return index;
344 void FilterSlot::set_units(FilterUnits const &units) {
345     this->units = units;
348 void FilterSlot::set_quality(FilterQuality const q) {
349     filterquality = q;
354 /*
355   Local Variables:
356   mode:c++
357   c-file-style:"stroustrup"
358   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
359   indent-tabs-mode:nil
360   fill-column:99
361   End:
362 */
363 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :