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;
150 }
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 }
172 }
174 void FilterSlot::set(int slot_nr, NRPixBlock *pb, bool takeOwnership)
175 {
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;
284 }
286 int FilterSlot::get_slot_count()
287 {
288 return _slots.size();
289 }
291 NRArenaItem const* FilterSlot::get_arenaitem()
292 {
293 return _arena_item;
294 }
296 int FilterSlot::_get_index(int slot_nr)
297 {
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;
324 }
326 void FilterSlot::set_units(FilterUnits const &units) {
327 this->units = units;
328 }
330 void FilterSlot::set_quality(FilterQuality const q) {
331 filterquality = q;
332 }
334 void FilterSlot::set_blurquality(int const q) {
335 blurquality = q;
336 }
338 int FilterSlot::get_blurquality(void) {
339 return blurquality;
340 }
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 :