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];
153 }
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 }
171 }
173 void FilterSlot::set(int slot_nr, NRPixBlock *pb)
174 {
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]);
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;
254 }
256 int FilterSlot::get_slot_count()
257 {
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;
264 }
266 NRArenaItem const* FilterSlot::get_arenaitem()
267 {
268 return _arena_item;
269 }
271 int FilterSlot::_get_index(int slot_nr)
272 {
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;
323 }
325 void FilterSlot::set_units(FilterUnits const &units) {
326 this->units = units;
327 }
329 }
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 :