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];
154 }
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 }
176 }
178 void FilterSlot::set(int slot_nr, NRPixBlock *pb)
179 {
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;
273 }
275 int FilterSlot::get_slot_count()
276 {
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;
283 }
285 NRArenaItem const* FilterSlot::get_arenaitem()
286 {
287 return _arena_item;
288 }
290 int FilterSlot::_get_index(int slot_nr)
291 {
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;
343 }
345 void FilterSlot::set_units(FilterUnits const &units) {
346 this->units = units;
347 }
349 void FilterSlot::set_quality(FilterQuality const q) {
350 filterquality = q;
351 }
353 void FilterSlot::set_blurquality(int const q) {
354 blurquality = q;
355 }
357 int FilterSlot::get_blurquality(void) {
358 return blurquality;
359 }
361 } /* namespace Filters */
362 } /* namespace Inkscape */
364 /*
365 Local Variables:
366 mode:c++
367 c-file-style:"stroustrup"
368 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
369 indent-tabs-mode:nil
370 fill-column:99
371 End:
372 */
373 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :