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