Code

Added more error checking to filter effects code
[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>
18 #include "display/nr-arena-item.h"
19 #include "display/nr-filter-types.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 NR {
66 FilterSlot::FilterSlot(int slots, NRArenaItem const *item)
67 {
68     _slot_count = ((slots > 0) ? slots : 2);
69     _slot = new NRPixBlock*[_slot_count];
70     _slot_number = new int[_slot_count];
72     for (int i = 0 ; i < _slot_count ; i++) {
73         _slot[i] = NULL;
74         _slot_number[i] = NR_FILTER_SLOT_NOT_SET;
75     }
77     _last_out = -1;
79     _arena_item = item;
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         transform_nearest(result, final_usr, trans);
166     } else if (fabs(trans[0] - 1) > 1e-6 || fabs(trans[3] - 1) > 1e-6) {
167         scale_bicubic(result, final_usr);
168     } else {
169         nr_blit_pixblock_pixblock(result, final_usr);
170     }
173 void FilterSlot::set(int slot_nr, NRPixBlock *pb)
175     int index = _get_index(slot_nr);
176     assert(index >= 0);
177     assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slot_number[index] == slot_nr);
179     if (slot_nr == NR_FILTER_SOURCEGRAPHIC || slot_nr == NR_FILTER_BACKGROUNDIMAGE) {
180         Matrix trans = units.get_matrix_display2pb();
181         if (fabs(trans[1]) > 1e-6 || fabs(trans[2]) > 1e-6) {
182             NRPixBlock *trans_pb = new NRPixBlock;
183             int x0 = pb->area.x0;
184             int y0 = pb->area.y0;
185             int x1 = pb->area.x1;
186             int y1 = pb->area.y1;
187             int min_x = _min4(trans[0] * x0 + trans[2] * y0 + trans[4],
188                               trans[0] * x0 + trans[2] * y1 + trans[4],
189                               trans[0] * x1 + trans[2] * y0 + trans[4],
190                               trans[0] * x1 + trans[2] * y1 + trans[4]);
191             int max_x = _max4(trans[0] * x0 + trans[2] * y0 + trans[4],
192                               trans[0] * x0 + trans[2] * y1 + trans[4],
193                               trans[0] * x1 + trans[2] * y0 + trans[4],
194                               trans[0] * x1 + trans[2] * y1 + trans[4]);
195             int min_y = _min4(trans[1] * x0 + trans[3] * y0 + trans[5],
196                               trans[1] * x0 + trans[3] * y1 + trans[5],
197                               trans[1] * x1 + trans[3] * y0 + trans[5],
198                               trans[1] * x1 + trans[3] * y1 + trans[5]);
199             int max_y = _max4(trans[1] * x0 + trans[3] * y0 + trans[5],
200                               trans[1] * x0 + trans[3] * y1 + trans[5],
201                               trans[1] * x1 + trans[3] * y0 + trans[5],
202                               trans[1] * x1 + trans[3] * y1 + trans[5]);
203             
204             nr_pixblock_setup_fast(trans_pb, pb->mode,
205                                    min_x, min_y,
206                                    max_x, max_y, true);
207             if (trans_pb->size != NR_PIXBLOCK_SIZE_TINY && trans_pb->data.px == NULL) {
208                 /* TODO: this gets hit occasionally. Worst case scenario:
209                  * images are exported in horizontal stripes. One stripe
210                  * is not too high, but can get thousands of pixels wide.
211                  * Rotate this 45 degrees -> _huge_ image */
212                 g_warning("Memory allocation failed in NR::FilterSlot::set (transform)");
213                 return;
214             }
215             transform_nearest(trans_pb, pb, trans);
216             nr_pixblock_release(pb);
217             delete pb;
218             pb = trans_pb;
219         } else if (fabs(trans[0] - 1) > 1e-6 || fabs(trans[3] - 1) > 1e-6) {
220             NRPixBlock *trans_pb = new NRPixBlock;
222             int x0 = pb->area.x0;
223             int y0 = pb->area.y0;
224             int x1 = pb->area.x1;
225             int y1 = pb->area.y1;
226             int min_x = _min2(trans[0] * x0 + trans[4],
227                               trans[0] * x1 + trans[4]);
228             int max_x = _max2(trans[0] * x0 + trans[4],
229                               trans[0] * x1 + trans[4]);
230             int min_y = _min2(trans[3] * y0 + trans[5],
231                               trans[3] * y1 + trans[5]);
232             int max_y = _max2(trans[3] * y0 + trans[5],
233                               trans[3] * y1 + trans[5]);
235             nr_pixblock_setup_fast(trans_pb, pb->mode,
236                                    min_x, min_y, max_x, max_y, true);
237             if (trans_pb->size != NR_PIXBLOCK_SIZE_TINY && trans_pb->data.px == NULL) {
238                 g_warning("Memory allocation failed in NR::FilterSlot::set (scaling)");
239                 return;
240             }
241             scale_bicubic(trans_pb, pb);
242             nr_pixblock_release(pb);
243             delete pb;
244             pb = trans_pb;
245         }
246     }
248     if(_slot[index]) {
249         nr_pixblock_release(_slot[index]);
250         delete _slot[index];
251     }
252     _slot[index] = pb;
253     _last_out = index;
256 int FilterSlot::get_slot_count()
258     int seek = _slot_count;
259     do {
260         seek--;
261     } while (!_slot[seek] && _slot_number[seek] == NR_FILTER_SLOT_NOT_SET);
263     return seek + 1;
266 NRArenaItem const* FilterSlot::get_arenaitem()
268         return _arena_item;
271 int FilterSlot::_get_index(int slot_nr)
273     assert(slot_nr >= 0 ||
274            slot_nr == NR_FILTER_SLOT_NOT_SET ||
275            slot_nr == NR_FILTER_SOURCEGRAPHIC ||
276            slot_nr == NR_FILTER_SOURCEALPHA ||
277            slot_nr == NR_FILTER_BACKGROUNDIMAGE ||
278            slot_nr == NR_FILTER_BACKGROUNDALPHA ||
279            slot_nr == NR_FILTER_FILLPAINT ||
280            slot_nr == NR_FILTER_STROKEPAINT);
282     int index = -1;
283     if (slot_nr == NR_FILTER_SLOT_NOT_SET) {
284         return _last_out;
285     }
286     /* Search, if the slot already exists */
287     for (int i = 0 ; i < _slot_count ; i++) {
288         if (_slot_number[i] == slot_nr) {
289             index = i;
290             break;
291         }
292     }
294     /* If the slot doesn't already exist, create it */
295     if (index == -1) {
296         int seek = _slot_count;
297         do {
298             seek--;
299         } while (_slot_number[seek] == NR_FILTER_SLOT_NOT_SET && seek >= 0);
300         /* If there is no space for more slots, create more space */
301         if (seek == _slot_count - 1) {
302             NRPixBlock **new_slot = new NRPixBlock*[_slot_count * 2];
303             int *new_number = new int[_slot_count * 2];
304             for (int i = 0 ; i < _slot_count ; i++) {
305                 new_slot[i] = _slot[i];
306                 new_number[i] = _slot_number[i];
307             }
308             for (int i = _slot_count ; i < _slot_count * 2 ; i++) {
309                 new_slot[i] = NULL;
310                 new_number[i] = NR_FILTER_SLOT_NOT_SET;
311             }
312             delete[] _slot;
313             delete[] _slot_number;
314             _slot = new_slot;
315             _slot_number = new_number;
316             _slot_count *= 2;
317         }
318         /* Now that there is space, create the slot */
319         _slot_number[seek + 1] = slot_nr;
320         index = seek + 1;
321     }
322     return index;
325 void FilterSlot::set_units(FilterUnits const &units) {
326     this->units = units;
331 /*
332   Local Variables:
333   mode:c++
334   c-file-style:"stroustrup"
335   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
336   indent-tabs-mode:nil
337   fill-column:99
338   End:
339 */
340 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :