1 #define __NR_ARENA_ITEM_C__
3 /*
4 * RGBA display list system for inkscape
5 *
6 * Author:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 *
9 * Copyright (C) 2001-2002 Lauris Kaplinski
10 * Copyright (C) 2001 Ximian, Inc.
11 *
12 * Released under GNU GPL, read the file 'COPYING' for more information
13 */
15 #define noNR_ARENA_ITEM_VERBOSE
16 #define noNR_ARENA_ITEM_DEBUG_CASCADE
19 #include <libnr/nr-blit.h>
20 #include <libnr/nr-pixops.h>
21 #include "nr-arena.h"
22 #include "nr-arena-item.h"
23 #include "gc-core.h"
25 #include "nr-filter.h"
26 #include "libnr/nr-rect.h"
27 #include "nr-arena-group.h"
29 namespace GC = Inkscape::GC;
31 static void nr_arena_item_class_init (NRArenaItemClass *klass);
32 static void nr_arena_item_init (NRArenaItem *item);
33 static void nr_arena_item_private_finalize (NRObject *object);
35 #ifdef arena_item_tile_cache
36 bool insert_cache(NRArenaItem* owner,int th,int tv,NRPixBlock *ipb,NRPixBlock *mpb,double activity,double duration);
37 void remove_caches(NRArenaItem* owner);
38 bool test_cache(NRArenaItem* owner,int th,int tv,NRPixBlock &ipb,NRPixBlock &mpb,bool &hasMask);
39 #endif
41 static NRObjectClass *parent_class;
43 NRType
44 nr_arena_item_get_type (void)
45 {
46 static NRType type = 0;
47 if (!type) {
48 type = nr_object_register_type (NR_TYPE_OBJECT,
49 "NRArenaItem",
50 sizeof (NRArenaItemClass),
51 sizeof (NRArenaItem),
52 (void (*) (NRObjectClass *)) nr_arena_item_class_init,
53 (void (*) (NRObject *)) nr_arena_item_init);
54 }
55 return type;
56 }
58 static void
59 nr_arena_item_class_init (NRArenaItemClass *klass)
60 {
61 NRObjectClass *object_class;
63 object_class = (NRObjectClass *) klass;
65 parent_class = ((NRObjectClass *) klass)->parent;
67 object_class->finalize = nr_arena_item_private_finalize;
68 object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaItem>;
69 }
71 static void
72 nr_arena_item_init (NRArenaItem *item)
73 {
74 item->arena = NULL;
75 item->parent = NULL;
76 item->next = item->prev = NULL;
78 item->key = 0;
80 item->state = 0;
81 item->sensitive = TRUE;
82 item->visible = TRUE;
84 memset(&item->bbox, 0, sizeof(item->bbox));
85 item->transform = NULL;
86 item->opacity = 255;
87 item->render_opacity = FALSE;
89 #ifdef arena_item_tile_cache
90 item->activity=0.0;
91 item->skipCaching=false;
92 #endif
94 item->transform = NULL;
95 item->clip = NULL;
96 item->mask = NULL;
97 item->px = NULL;
98 item->data = NULL;
99 item->filter = NULL;
100 item->background_pb = NULL;
101 item->background_new = false;
102 }
104 static void
105 nr_arena_item_private_finalize (NRObject *object)
106 {
107 NRArenaItem *item=static_cast<NRArenaItem *>(object);
109 #ifdef arena_item_tile_cache
110 remove_caches(item);
111 #endif
113 item->px = NULL;
114 item->transform = NULL;
116 ((NRObjectClass *) (parent_class))->finalize (object);
117 }
119 NRArenaItem *
120 nr_arena_item_children (NRArenaItem *item)
121 {
122 nr_return_val_if_fail (item != NULL, NULL);
123 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
125 if (NR_ARENA_ITEM_VIRTUAL (item, children))
126 return NR_ARENA_ITEM_VIRTUAL (item, children) (item);
128 return NULL;
129 }
131 NRArenaItem *
132 nr_arena_item_last_child (NRArenaItem *item)
133 {
134 nr_return_val_if_fail (item != NULL, NULL);
135 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
137 if (NR_ARENA_ITEM_VIRTUAL (item, last_child)) {
138 return NR_ARENA_ITEM_VIRTUAL (item, last_child) (item);
139 } else {
140 NRArenaItem *ref;
141 ref = nr_arena_item_children (item);
142 if (ref) while (ref->next) ref = ref->next;
143 return ref;
144 }
145 }
147 void
148 nr_arena_item_add_child (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
149 {
150 nr_return_if_fail (item != NULL);
151 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
152 nr_return_if_fail (child != NULL);
153 nr_return_if_fail (NR_IS_ARENA_ITEM (child));
154 nr_return_if_fail (child->parent == NULL);
155 nr_return_if_fail (child->prev == NULL);
156 nr_return_if_fail (child->next == NULL);
157 nr_return_if_fail (child->arena == item->arena);
158 nr_return_if_fail (child != ref);
159 nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
160 nr_return_if_fail (!ref || (ref->parent == item));
162 if (NR_ARENA_ITEM_VIRTUAL (item, add_child))
163 NR_ARENA_ITEM_VIRTUAL (item, add_child) (item, child, ref);
164 }
166 void
167 nr_arena_item_remove_child (NRArenaItem *item, NRArenaItem *child)
168 {
169 nr_return_if_fail (item != NULL);
170 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
171 nr_return_if_fail (child != NULL);
172 nr_return_if_fail (NR_IS_ARENA_ITEM (child));
173 nr_return_if_fail (child->parent == item);
175 if (NR_ARENA_ITEM_VIRTUAL (item, remove_child))
176 NR_ARENA_ITEM_VIRTUAL (item, remove_child) (item, child);
177 }
179 void
180 nr_arena_item_set_child_position (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
181 {
182 nr_return_if_fail (item != NULL);
183 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
184 nr_return_if_fail (child != NULL);
185 nr_return_if_fail (NR_IS_ARENA_ITEM (child));
186 nr_return_if_fail (child->parent == item);
187 nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
188 nr_return_if_fail (!ref || (ref->parent == item));
190 if (NR_ARENA_ITEM_VIRTUAL (item, set_child_position))
191 NR_ARENA_ITEM_VIRTUAL (item, set_child_position) (item, child, ref);
192 }
194 NRArenaItem *
195 nr_arena_item_ref (NRArenaItem *item)
196 {
197 nr_object_ref ((NRObject *) item);
199 return item;
200 }
202 NRArenaItem *
203 nr_arena_item_unref (NRArenaItem *item)
204 {
205 nr_object_unref ((NRObject *) item);
207 return NULL;
208 }
210 unsigned int
211 nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset)
212 {
213 NRGC childgc(gc);
215 nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
216 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
217 nr_return_val_if_fail (!(state & NR_ARENA_ITEM_STATE_INVALID), NR_ARENA_ITEM_STATE_INVALID);
219 #ifdef NR_ARENA_ITEM_DEBUG_CASCADE
220 printf ("Update %s:%p %x %x %x\n", nr_type_name_from_instance ((GTypeInstance *) item), item, state, item->state, reset);
221 #endif
223 /* return if in error */
224 if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
225 /* Set reset flags according to propagation status */
226 if (item->propagate) {
227 reset |= ~item->state;
228 item->propagate = FALSE;
229 }
230 /* Reset our state */
231 item->state &= ~reset;
232 /* Return if NOP */
233 if (!(~item->state & state)) return item->state;
234 /* Test whether to return immediately */
235 if (area && (item->state & NR_ARENA_ITEM_STATE_BBOX)) {
236 if (!nr_rect_l_test_intersect (area, &item->bbox)) return item->state;
237 }
239 /* Reset image cache, if not to be kept */
240 if (!(item->state & NR_ARENA_ITEM_STATE_IMAGE) && (item->px)) {
241 item->px = NULL;
242 }
243 #ifdef arena_item_tile_cache
244 remove_caches(item);
245 #endif
247 /* Set up local gc */
248 childgc = *gc;
249 if (item->transform) {
250 nr_matrix_multiply (&childgc.transform, item->transform, &childgc.transform);
251 }
252 /* Remember the transformation matrix */
253 item->ctm = childgc.transform;
255 /* Invoke the real method */
256 item->state = NR_ARENA_ITEM_VIRTUAL (item, update) (item, area, &childgc, state, reset);
257 if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
258 /* Enlarge the bounding box to contain filter effects */
259 if(item->filter) {
260 item->filter->bbox_enlarge(item->bbox);
261 }
263 /* Clipping */
264 if (item->clip) {
265 unsigned int newstate;
266 newstate = nr_arena_item_invoke_update (item->clip, area, &childgc, state, reset);
267 if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
268 item->state |= NR_ARENA_ITEM_STATE_INVALID;
269 return item->state;
270 }
271 nr_rect_l_intersect (&item->bbox, &item->bbox, &item->clip->bbox);
272 }
273 /* Masking */
274 if (item->mask) {
275 unsigned int newstate;
276 newstate = nr_arena_item_invoke_update (item->mask, area, &childgc, state, reset);
277 if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
278 item->state |= NR_ARENA_ITEM_STATE_INVALID;
279 return item->state;
280 }
281 nr_rect_l_intersect (&item->bbox, &item->bbox, &item->mask->bbox);
282 }
284 return item->state;
285 }
287 /**
288 * Render item to pixblock.
289 *
290 * \return Has NR_ARENA_ITEM_STATE_RENDER set on success.
291 */
293 unsigned int nr_arena_item_invoke_render(NRArenaItem *item, NRRectL const *area, NRPixBlock *pb, unsigned int flags)
294 {
295 NRRectL carea;
296 NRPixBlock *dpb;
297 NRPixBlock cpb;
298 unsigned int state;
300 bool outline = (item->arena->rendermode == RENDERMODE_OUTLINE);
302 nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
303 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
304 nr_return_val_if_fail (item->state & NR_ARENA_ITEM_STATE_BBOX, item->state);
306 #ifdef NR_ARENA_ITEM_VERBOSE
307 printf ("Invoke render %p: %d %d - %d %d\n", item, area->x0, area->y0, area->x1, area->y1);
308 #endif
310 #ifdef arena_item_tile_cache
311 item->activity*=0.5;
312 #endif
314 /* If we are outside bbox just return successfully */
315 if (!item->visible) return item->state | NR_ARENA_ITEM_STATE_RENDER;
316 nr_rect_l_intersect (&carea, area, &item->bbox);
317 if (nr_rect_l_test_empty (&carea)) return item->state | NR_ARENA_ITEM_STATE_RENDER;
318 if(item->filter && !outline) {
319 nr_rect_l_enlarge(&carea, item->filter->get_enlarge(item->ctm));
320 nr_rect_l_intersect(&carea, &carea, &item->bbox);
321 }
323 if (item->px) {
324 /* Has cache pixblock, render this and return */
325 nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
326 /* fixme: This probably cannot overflow, because we render only if visible */
327 /* fixme: and pixel cache is there only for small items */
328 /* fixme: But this still needs extra check (Lauris) */
329 item->bbox.x0, item->bbox.y0,
330 item->bbox.x1, item->bbox.y1,
331 item->px, 4 * (item->bbox.x1 - item->bbox.x0), FALSE, FALSE);
332 nr_blit_pixblock_pixblock (pb, &cpb);
333 nr_pixblock_release (&cpb);
334 pb->empty = FALSE;
335 return item->state | NR_ARENA_ITEM_STATE_RENDER;
336 }
338 dpb = pb;
339 bool canCache=false;
340 #ifdef arena_item_tile_cache
341 bool checkCache=false;
342 int tile_h=0,tile_v=0;
343 #endif
344 /* Setup cache if we can */
345 if ((!(flags & NR_ARENA_ITEM_RENDER_NO_CACHE)) &&
346 (carea.x0 <= item->bbox.x0) && (carea.y0 <= item->bbox.y0) &&
347 (carea.x1 >= item->bbox.x1) && (carea.y1 >= item->bbox.y1) &&
348 (((item->bbox.x1 - item->bbox.x0) * (item->bbox.y1 - item->bbox.y0)) <= 4096)) {
349 // Item bbox is fully in renderable area and size is acceptable
350 carea.x0 = item->bbox.x0;
351 carea.y0 = item->bbox.y0;
352 carea.x1 = item->bbox.x1;
353 carea.y1 = item->bbox.y1;
354 item->px = new (GC::ATOMIC) unsigned char[4 * (carea.x1 - carea.x0) * (carea.y1 - carea.y0)];
355 nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
356 carea.x0, carea.y0, carea.x1, carea.y1,
357 item->px, 4 * (carea.x1 - carea.x0), TRUE, TRUE);
358 cpb.visible_area = pb->visible_area;
359 dpb = &cpb;
360 // Set nocache flag for downstream rendering
361 flags |= NR_ARENA_ITEM_RENDER_NO_CACHE;
362 } else {
363 #ifdef arena_item_tile_cache
364 if ( item->skipCaching ) {
365 } else {
366 int tl=area->x0&(~127);
367 int tt=area->y0&(~127);
368 if ( area->x1 <= tl+128 && area->y1 <= tt+128 ) {
369 checkCache=true;
370 tile_h=tl/128;
371 tile_v=tt/128;
372 int surf=(area->x1-area->x0)*(area->y1-area->y0);
373 if ( surf >= 4096 ) {
374 canCache=true;
375 carea.x0=tl;
376 carea.y0=tt;
377 carea.x1=tl+128;
378 carea.y1=tt+128;
379 }
380 }
381 }
382 #endif
383 }
385 #ifdef arena_item_tile_cache
386 item->activity+=1.0;
387 #endif
389 #ifdef arena_item_tile_cache
390 if ( checkCache ) {
391 NRPixBlock ipb, mpb;
392 bool hasMask;
393 if ( test_cache(item,tile_h,tile_v,ipb,mpb,hasMask) ) {
394 // youpi! c'etait deja cache
395 if ( hasMask ) {
396 nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
397 } else if ( ((item->opacity != 255) && !item->render_opacity) ) {
398 nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
399 } else {
400 nr_blit_pixblock_pixblock (pb, &ipb);
401 }
402 pb->empty = FALSE;
403 return item->state | NR_ARENA_ITEM_STATE_RENDER;
404 }
405 }
406 #endif
407 if ( canCache ) {
408 #ifdef arena_item_tile_cache
409 // nota: exclusif de dpb != pb, donc pas de cas particulier a la fin
410 NRPixBlock ipb, mpb;
412 // struct timeval start_time,end_time;
413 // gettimeofday(&start_time,NULL);
414 GTimeVal start_time,end_time;
415 g_get_current_time (&start_time);
416 int duration=0;
418 /* Setup and render item buffer */
419 nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
420 ipb.visible_area = pb->visible_area;
421 state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
422 if (state & NR_ARENA_ITEM_STATE_INVALID) {
423 /* Clean up and return error */
424 nr_pixblock_release (&ipb);
425 if (dpb != pb) nr_pixblock_release (dpb);
426 item->state |= NR_ARENA_ITEM_STATE_INVALID;
427 return item->state;
428 }
429 ipb.empty = FALSE;
431 if (item->clip || item->mask) {
432 /* Setup mask pixblock */
433 nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
434 mpb.visible_area = pb->visible_area;
435 /* Do clip if needed */
436 if (item->clip) {
437 state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
438 if (state & NR_ARENA_ITEM_STATE_INVALID) {
439 /* Clean up and return error */
440 nr_pixblock_release (&mpb);
441 nr_pixblock_release (&ipb);
442 if (dpb != pb) nr_pixblock_release (dpb);
443 item->state |= NR_ARENA_ITEM_STATE_INVALID;
444 return item->state;
445 }
446 mpb.empty = FALSE;
447 }
448 /* Do mask if needed */
449 if (item->mask) {
450 NRPixBlock tpb;
451 /* Set up yet another temporary pixblock */
452 nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
453 tpb.visible_area = pb->visible_area;
454 state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
455 if (state & NR_ARENA_ITEM_STATE_INVALID) {
456 /* Clean up and return error */
457 nr_pixblock_release (&tpb);
458 nr_pixblock_release (&mpb);
459 nr_pixblock_release (&ipb);
460 if (dpb != pb) nr_pixblock_release (dpb);
461 item->state |= NR_ARENA_ITEM_STATE_INVALID;
462 return item->state;
463 }
464 /* Composite with clip */
465 if (item->clip) {
466 int x, y;
467 for (y = carea.y0; y < carea.y1; y++) {
468 unsigned char *s, *d;
469 s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
470 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
471 for (x = carea.x0; x < carea.x1; x++) {
472 unsigned int m;
473 m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
474 d[0] = NR_PREMUL (d[0], m);
475 s += 4;
476 d += 1;
477 }
478 }
479 } else {
480 int x, y;
481 for (y = carea.y0; y < carea.y1; y++) {
482 unsigned char *s, *d;
483 s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
484 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
485 for (x = carea.x0; x < carea.x1; x++) {
486 unsigned int m;
487 m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
488 d[0] = m;
489 s += 4;
490 d += 1;
491 }
492 }
493 mpb.empty = FALSE;
494 }
495 nr_pixblock_release (&tpb);
496 }
497 /* Multiply with opacity if needed */
498 if ((item->opacity != 255) && !item->render_opacity && !outline) {
499 int x, y;
500 unsigned int a;
501 a = item->opacity;
502 for (y = carea.y0; y < carea.y1; y++) {
503 unsigned char *d;
504 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
505 for (x = carea.x0; x < carea.x1; x++) {
506 d[0] = NR_PREMUL (d[0], a);
507 d += 1;
508 }
509 }
510 }
511 /* Compose rendering pixblock int destination */
512 // gettimeofday(&end_time,NULL);
513 g_get_current_time (&end_time);
514 duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
515 if ( !(ipb.empty) ) {
516 nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
517 if ( insert_cache(item,tile_h,tile_v,&ipb,&mpb,item->activity,(double)duration) ) {
518 } else {
519 nr_pixblock_release (&mpb);
520 nr_pixblock_release (&ipb);
521 }
522 dpb->empty = FALSE;
523 } else {
524 nr_pixblock_release (&ipb);
525 }
526 } else if ( ((item->opacity != 255) && !item->render_opacity && !outline) ) {
527 /* Opacity only */
528 // gettimeofday(&end_time,NULL);
529 g_get_current_time (&end_time);
530 duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
531 if ( !(ipb.empty) ) {
532 nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
533 if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
534 } else {
535 nr_pixblock_release (&ipb);
536 }
537 dpb->empty = FALSE;
538 } else {
539 nr_pixblock_release (&ipb);
540 }
541 } else {
542 // gettimeofday(&end_time,NULL);
543 g_get_current_time (&end_time);
544 duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
545 if ( !(ipb.empty) ) {
546 nr_blit_pixblock_pixblock (dpb, &ipb);
547 if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
548 } else {
549 nr_pixblock_release (&ipb);
550 }
551 dpb->empty = FALSE;
552 } else {
553 nr_pixblock_release (&ipb);
554 }
555 }
556 #endif
557 } else {
558 /* Determine, whether we need temporary buffer */
559 if (item->clip || item->mask
560 || ((item->opacity != 255) && !item->render_opacity && !outline)
561 || (item->filter && !outline) || item->background_new
562 || (item->parent && item->parent->background_pb) )
563 {
564 NRPixBlock ipb, mpb;
566 /* Setup and render item buffer */
567 nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
568 /* If background access is used, save the pixblock address.
569 * This address is set to NULL at the end of this block */
570 if (item->background_new
571 || (item->parent && item->parent->background_pb)) {
572 item->background_pb = &ipb;
573 }
574 ipb.visible_area = pb->visible_area;
575 state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
576 if (state & NR_ARENA_ITEM_STATE_INVALID) {
577 /* Clean up and return error */
578 nr_pixblock_release (&ipb);
579 if (dpb != pb) nr_pixblock_release (dpb);
580 item->state |= NR_ARENA_ITEM_STATE_INVALID;
581 return item->state;
582 }
583 ipb.empty = FALSE;
585 /* Run filtering, if a filter is set for this object */
586 if(item->filter && !outline) {
587 item->filter->render(item, &ipb);
588 }
590 if (item->clip || item->mask) {
591 /* Setup mask pixblock */
592 nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
593 mpb.visible_area = pb->visible_area;
594 /* Do clip if needed */
595 if (item->clip) {
596 state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
597 if (state & NR_ARENA_ITEM_STATE_INVALID) {
598 /* Clean up and return error */
599 nr_pixblock_release (&mpb);
600 nr_pixblock_release (&ipb);
601 if (dpb != pb) nr_pixblock_release (dpb);
602 item->state |= NR_ARENA_ITEM_STATE_INVALID;
603 return item->state;
604 }
605 mpb.empty = FALSE;
606 }
607 /* Do mask if needed */
608 if (item->mask) {
609 NRPixBlock tpb;
610 /* Set up yet another temporary pixblock */
611 nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
612 tpb.visible_area = pb->visible_area;
613 state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
614 if (state & NR_ARENA_ITEM_STATE_INVALID) {
615 /* Clean up and return error */
616 nr_pixblock_release (&tpb);
617 nr_pixblock_release (&mpb);
618 nr_pixblock_release (&ipb);
619 if (dpb != pb) nr_pixblock_release (dpb);
620 item->state |= NR_ARENA_ITEM_STATE_INVALID;
621 return item->state;
622 }
623 /* Composite with clip */
624 if (item->clip) {
625 int x, y;
626 for (y = carea.y0; y < carea.y1; y++) {
627 unsigned char *s, *d;
628 s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
629 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
630 for (x = carea.x0; x < carea.x1; x++) {
631 unsigned int m;
632 m = NR_PREMUL_112(s[0]+s[1]+s[2], s[3]);
633 d[0] = FAST_DIV_ROUND<3*255*255>(NR_PREMUL_123(d[0], m));
634 s += 4;
635 d += 1;
636 }
637 }
638 } else {
639 int x, y;
640 for (y = carea.y0; y < carea.y1; y++) {
641 unsigned char *s, *d;
642 s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
643 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
644 for (x = carea.x0; x < carea.x1; x++) {
645 unsigned int m;
646 m = NR_PREMUL_112(s[0]+s[1]+s[2], s[3]);
647 d[0] = FAST_DIV_ROUND<3*255>(m);
648 s += 4;
649 d += 1;
650 }
651 }
652 mpb.empty = FALSE;
653 }
654 nr_pixblock_release (&tpb);
655 }
656 /* Multiply with opacity if needed */
657 if ((item->opacity != 255) && !item->render_opacity && !outline) {
658 int x, y;
659 unsigned int a;
660 a = item->opacity;
661 for (y = carea.y0; y < carea.y1; y++) {
662 unsigned char *d;
663 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
664 for (x = carea.x0; x < carea.x1; x++) {
665 d[0] = NR_PREMUL_111 (d[0], a);
666 d += 1;
667 }
668 }
669 }
670 /* Compose rendering pixblock int destination */
671 nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
672 nr_pixblock_release (&mpb);
673 /* This pointer wouldn't be valid outside this block, so clear it */
674 item->background_pb = NULL;
675 } else {
676 /* Opacity only */
677 nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
678 }
679 nr_pixblock_release (&ipb);
680 dpb->empty = FALSE;
681 } else {
682 /* Just render */
683 state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, dpb, flags);
684 if (state & NR_ARENA_ITEM_STATE_INVALID) {
685 /* Clean up and return error */
686 if (dpb != pb) nr_pixblock_release (dpb);
687 item->state |= NR_ARENA_ITEM_STATE_INVALID;
688 return item->state;
689 }
690 dpb->empty = FALSE;
691 }
693 if (dpb != pb) {
694 /* Have to blit from cache */
695 nr_blit_pixblock_pixblock (pb, dpb);
696 nr_pixblock_release (dpb);
697 pb->empty = FALSE;
698 item->state |= NR_ARENA_ITEM_STATE_IMAGE;
699 }
700 }
701 return item->state | NR_ARENA_ITEM_STATE_RENDER;
702 }
704 unsigned int
705 nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
706 {
707 nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
708 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
709 /* we originally short-circuited if the object state included
710 * NR_ARENA_ITEM_STATE_CLIP (and showed a warning on the console);
711 * anyone know why we stopped doing so?
712 */
713 nr_return_val_if_fail ((pb->area.x1 - pb->area.x0) >= (area->x1 - area->x0), NR_ARENA_ITEM_STATE_INVALID);
714 nr_return_val_if_fail ((pb->area.y1 - pb->area.y0) >= (area->y1 - area->y0), NR_ARENA_ITEM_STATE_INVALID);
716 #ifdef NR_ARENA_ITEM_VERBOSE
717 printf ("Invoke clip by %p: %d %d - %d %d, item bbox %d %d - %d %d\n", item, area->x0, area->y0, area->x1, area->y1, (&item->bbox)->x0, (&item->bbox)->y0, (&item->bbox)->x1, (&item->bbox)->y1);
718 #endif
720 if (item->visible && nr_rect_l_test_intersect (area, &item->bbox)) {
721 /* Need render that item */
722 if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->clip) {
723 return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS(item))->clip (item, area, pb);
724 }
725 }
727 return item->state;
728 }
730 NRArenaItem *
731 nr_arena_item_invoke_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky)
732 {
733 nr_return_val_if_fail (item != NULL, NULL);
734 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
736 // Sometimes there's no BBOX in item->state, reason unknown (bug 992817); I made this not an assert to remove the warning
737 if (!(item->state & NR_ARENA_ITEM_STATE_BBOX) || !(item->state & NR_ARENA_ITEM_STATE_PICK))
738 return NULL;
740 if (!sticky && !(item->visible && item->sensitive)) return NULL;
742 // TODO: rewrite using NR::Rect
743 const double x = p[NR::X];
744 const double y = p[NR::Y];
746 if (((x + delta) >= item->bbox.x0) &&
747 ((x - delta) < item->bbox.x1) &&
748 ((y + delta) >= item->bbox.y0) &&
749 ((y - delta) < item->bbox.y1)) {
750 if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick)
751 return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick (item, p, delta, sticky);
752 }
754 return NULL;
755 }
757 void
758 nr_arena_item_request_update (NRArenaItem *item, unsigned int reset, unsigned int propagate)
759 {
760 nr_return_if_fail (item != NULL);
761 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
762 nr_return_if_fail (!(reset & NR_ARENA_ITEM_STATE_INVALID));
764 if (propagate && !item->propagate) item->propagate = TRUE;
766 if (item->state & reset) {
767 item->state &= ~reset;
768 if (item->parent) {
769 nr_arena_item_request_update (item->parent, reset, FALSE);
770 } else {
771 nr_arena_request_update (item->arena, item);
772 }
773 }
774 }
776 void
777 nr_arena_item_request_render (NRArenaItem *item)
778 {
779 nr_return_if_fail (item != NULL);
780 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
782 nr_arena_request_render_rect (item->arena, &item->bbox);
783 }
785 /* Public */
787 NRArenaItem *
788 nr_arena_item_unparent (NRArenaItem *item)
789 {
790 nr_return_val_if_fail (item != NULL, NULL);
791 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
793 nr_arena_item_request_render (item);
795 if (item->parent) {
796 nr_arena_item_remove_child (item->parent, item);
797 }
799 return NULL;
800 }
802 void
803 nr_arena_item_append_child (NRArenaItem *parent, NRArenaItem *child)
804 {
805 nr_return_if_fail (parent != NULL);
806 nr_return_if_fail (NR_IS_ARENA_ITEM (parent));
807 nr_return_if_fail (child != NULL);
808 nr_return_if_fail (NR_IS_ARENA_ITEM (child));
809 nr_return_if_fail (parent->arena == child->arena);
810 nr_return_if_fail (child->parent == NULL);
811 nr_return_if_fail (child->prev == NULL);
812 nr_return_if_fail (child->next == NULL);
814 nr_arena_item_add_child (parent, child, nr_arena_item_last_child (parent));
815 }
817 void
818 nr_arena_item_set_transform(NRArenaItem *item, NR::Matrix const &transform)
819 {
820 NRMatrix const t(transform);
821 nr_arena_item_set_transform(item, &t);
822 }
824 void
825 nr_arena_item_set_transform(NRArenaItem *item, NRMatrix const *transform)
826 {
827 const NRMatrix *ms, *md;
829 nr_return_if_fail (item != NULL);
830 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
832 if (!transform && !item->transform) return;
834 md = (item->transform) ? item->transform : &NR_MATRIX_IDENTITY;
835 ms = (transform) ? transform : &NR_MATRIX_IDENTITY;
837 if (!NR_MATRIX_DF_TEST_CLOSE (md, ms, NR_EPSILON)) {
838 nr_arena_item_request_render (item);
839 if (!transform || nr_matrix_test_identity (transform, NR_EPSILON)) {
840 /* Set to identity affine */
841 item->transform = NULL;
842 } else {
843 if (!item->transform) item->transform = new (GC::ATOMIC) NRMatrix();
844 *item->transform = *transform;
845 }
846 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
847 }
848 }
850 void
851 nr_arena_item_set_opacity (NRArenaItem *item, double opacity)
852 {
853 nr_return_if_fail (item != NULL);
854 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
856 nr_arena_item_request_render (item);
858 item->opacity = (unsigned int) (opacity * 255.9999);
859 }
861 void
862 nr_arena_item_set_sensitive (NRArenaItem *item, unsigned int sensitive)
863 {
864 nr_return_if_fail (item != NULL);
865 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
867 /* fixme: mess with pick/repick... */
869 item->sensitive = sensitive;
870 }
872 void
873 nr_arena_item_set_visible (NRArenaItem *item, unsigned int visible)
874 {
875 nr_return_if_fail (item != NULL);
876 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
878 item->visible = visible;
880 nr_arena_item_request_render (item);
881 }
883 void
884 nr_arena_item_set_clip (NRArenaItem *item, NRArenaItem *clip)
885 {
886 nr_return_if_fail (item != NULL);
887 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
888 nr_return_if_fail (!clip || NR_IS_ARENA_ITEM (clip));
890 if (clip != item->clip) {
891 nr_arena_item_request_render (item);
892 if (item->clip) item->clip = nr_arena_item_detach (item, item->clip);
893 if (clip) item->clip = nr_arena_item_attach (item, clip, NULL, NULL);
894 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
895 }
896 }
898 void
899 nr_arena_item_set_mask (NRArenaItem *item, NRArenaItem *mask)
900 {
901 nr_return_if_fail (item != NULL);
902 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
903 nr_return_if_fail (!mask || NR_IS_ARENA_ITEM (mask));
905 if (mask != item->mask) {
906 nr_arena_item_request_render (item);
907 if (item->mask) item->mask = nr_arena_item_detach (item, item->mask);
908 if (mask) item->mask = nr_arena_item_attach (item, mask, NULL, NULL);
909 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
910 }
911 }
913 void
914 nr_arena_item_set_order (NRArenaItem *item, int order)
915 {
916 NRArenaItem *children, *child, *ref;
917 int pos;
919 nr_return_if_fail (item != NULL);
920 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
922 if (!item->parent) return;
924 children = nr_arena_item_children (item->parent);
926 ref = NULL;
927 pos = 0;
928 for (child = children; child != NULL; child = child->next) {
929 if (pos >= order) break;
930 if (child != item) {
931 ref = child;
932 pos += 1;
933 }
934 }
936 nr_arena_item_set_child_position (item->parent, item, ref);
937 }
939 /** Returns a background image for use with filter effects. */
940 NRPixBlock *nr_arena_item_get_background (NRArenaItem const *item, int depth)
941 {
942 NRPixBlock *pb;
943 if (!item->background_pb) return NULL;
944 if (item->background_new) {
945 pb = new NRPixBlock();
946 nr_pixblock_setup_fast(pb, item->background_pb->mode,
947 item->background_pb->area.x0,
948 item->background_pb->area.y0,
949 item->background_pb->area.x1,
950 item->background_pb->area.y1, true);
951 } else if (item->parent) {
952 pb = nr_arena_item_get_background(item->parent, depth + 1);
953 } else return NULL;
955 if (depth > 0)
956 nr_blit_pixblock_pixblock(pb, item->background_pb);
958 return pb;
959 }
961 /* Helpers */
963 NRArenaItem *
964 nr_arena_item_attach (NRArenaItem *parent, NRArenaItem *child, NRArenaItem *prev, NRArenaItem *next)
965 {
966 nr_return_val_if_fail (parent != NULL, NULL);
967 nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
968 nr_return_val_if_fail (child != NULL, NULL);
969 nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
970 nr_return_val_if_fail (child->parent == NULL, NULL);
971 nr_return_val_if_fail (child->prev == NULL, NULL);
972 nr_return_val_if_fail (child->next == NULL, NULL);
973 nr_return_val_if_fail (!prev || NR_IS_ARENA_ITEM (prev), NULL);
974 nr_return_val_if_fail (!prev || (prev->parent == parent), NULL);
975 nr_return_val_if_fail (!prev || (prev->next == next), NULL);
976 nr_return_val_if_fail (!next || NR_IS_ARENA_ITEM (next), NULL);
977 nr_return_val_if_fail (!next || (next->parent == parent), NULL);
978 nr_return_val_if_fail (!next || (next->prev == prev), NULL);
980 child->parent = parent;
981 child->prev = prev;
982 child->next = next;
984 if (prev) prev->next = child;
985 if (next) next->prev = child;
987 return child;
988 }
990 NRArenaItem *
991 nr_arena_item_detach (NRArenaItem *parent, NRArenaItem *child)
992 {
993 NRArenaItem *prev, *next;
995 nr_return_val_if_fail (parent != NULL, NULL);
996 nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
997 nr_return_val_if_fail (child != NULL, NULL);
998 nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
999 nr_return_val_if_fail (child->parent == parent, NULL);
1001 prev = child->prev;
1002 next = child->next;
1004 child->parent = NULL;
1005 child->prev = NULL;
1006 child->next = NULL;
1008 if (prev) prev->next = next;
1009 if (next) next->prev = prev;
1011 return next;
1012 }
1014 /*
1015 *
1016 * caches
1017 *
1018 */
1020 #ifdef arena_item_tile_cache
1021 typedef struct cache_entry {
1022 int key;
1023 double score;
1024 NRArenaItem* owner;
1025 int th,tv;
1026 int prev,next;
1027 NRPixBlock ipb;
1028 bool hasMask;
1029 NRPixBlock mpb;
1030 } cache_entry;
1032 int hash_max=2048,hash_fill=1024;
1034 int *keys=NULL;
1035 int nbCch=0;
1037 int nbEnt=0,maxEnt=0;
1038 cache_entry* entries=NULL;
1040 //#define tile_cache_stats
1041 #ifdef tile_cache_stats
1042 double hits=0,misses=0;
1043 int hitMissCount=0;
1044 #endif
1046 int hash_that(NRArenaItem* owner,int th,int tv)
1047 {
1048 int res=GPOINTER_TO_INT(owner);
1049 res*=17;
1050 res+=th;
1051 res*=59;
1052 res+=tv;
1053 res*=217;
1054 if ( res < 0 ) res=-res;
1055 res%=hash_max;
1056 return res;
1057 }
1059 bool test_cache(NRArenaItem* owner,int th,int tv,NRPixBlock &ipb,NRPixBlock &mpb,bool &hasMask)
1060 {
1061 if ( keys == NULL ) {
1062 hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1063 hash_fill=(hash_max*3)/4;
1064 keys=(int*)malloc(hash_max*sizeof(int));
1065 for (int i=0;i<hash_max;i++) keys[i]=-1;
1066 }
1067 int key=hash_that(owner,th,tv);
1068 if ( keys[key] < 0 ) {
1069 #ifdef tile_cache_stats
1070 misses+=1.0;
1071 #endif
1072 return false;
1073 }
1074 int cur=keys[key];
1075 while ( cur >= 0 && cur < nbEnt ) {
1076 if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
1077 hasMask=entries[cur].hasMask;
1078 ipb=entries[cur].ipb;
1079 mpb=entries[cur].mpb;
1080 #ifdef tile_cache_stats
1081 hits+=1.0;
1082 #endif
1083 return true;
1084 }
1085 cur=entries[cur].next;
1086 }
1087 #ifdef tile_cache_stats
1088 misses+=1.0;
1089 #endif
1090 return false;
1091 }
1092 void remove_one_cache(int no)
1093 {
1094 if ( no < 0 || no >= nbEnt ) return;
1096 nr_pixblock_release(&entries[no].ipb);
1097 if ( entries[no].hasMask ) nr_pixblock_release(&entries[no].mpb);
1099 if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=entries[no].next;
1100 if ( entries[no].next >= 0 ) entries[entries[no].next].prev=entries[no].prev;
1101 if ( entries[no].prev < 0 ) keys[entries[no].key]=entries[no].next;
1102 entries[no].prev=entries[no].next=entries[no].key=-1;
1104 if ( no == nbEnt-1 ) {
1105 nbEnt--;
1106 return;
1107 }
1108 entries[no]=entries[--nbEnt];
1109 if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=no;
1110 if ( entries[no].next >= 0 ) entries[entries[no].next].prev=no;
1111 if ( entries[no].prev < 0 ) keys[entries[no].key]=no;
1112 }
1113 void remove_caches(NRArenaItem* owner)
1114 {
1115 if ( keys == NULL ) {
1116 hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1117 hash_fill=(hash_max*3)/4;
1118 keys=(int*)malloc(hash_max*sizeof(int));
1119 for (int i=0;i<hash_max;i++) keys[i]=-1;
1120 }
1121 for (int i=nbEnt-1;i>=0;i--) {
1122 if ( entries[i].owner == owner ) {
1123 remove_one_cache(i);
1124 }
1125 }
1126 }
1127 void age_cache(void)
1128 {
1129 for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
1130 }
1131 bool insert_cache(NRArenaItem* owner,int th,int tv,NRPixBlock *ipb,NRPixBlock *mpb,double activity,double duration)
1132 {
1133 if ( keys == NULL ) {
1134 hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1135 hash_fill=(hash_max*3)/4;
1136 keys=(int*)malloc(hash_max*sizeof(int));
1137 for (int i=0;i<hash_max;i++) keys[i]=-1;
1138 }
1139 for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
1140 #ifdef tile_cache_stats
1141 hits*=0.95;
1142 misses*=0.95;
1143 hitMissCount++;
1144 if ( hitMissCount > 100 ) {
1145 hitMissCount=0;
1146 printf("hit/miss = %f used/total=%i/%i\n",(misses>0.001)?hits/misses:100000.0,nbEnt,hash_max); // localizing ok
1147 }
1148 #endif
1149 int key=hash_that(owner,th,tv);
1150 double nScore=/*activity**/duration;
1152 if ( keys[key] >= 0 ) {
1153 int cur=keys[key];
1154 while ( cur >= 0 && cur < nbEnt ) {
1155 if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
1156 remove_one_cache(cur);
1157 break;
1158 }
1159 cur=entries[cur].next;
1160 }
1161 }
1163 bool doAdd=false;
1164 if ( nbEnt < hash_fill ) {
1165 doAdd=true;
1166 } else {
1167 double worstS=entries[0].score;
1168 int worstE=0;
1169 for (int i=1;i<nbEnt;i++) {
1170 if ( entries[i].score < worstS ) {
1171 worstS=entries[i].score;
1172 worstE=i;
1173 }
1174 }
1175 if ( worstS < nScore ) {
1176 doAdd=true;
1177 remove_one_cache(worstE);
1178 }
1179 }
1180 if ( doAdd == false ) return false;
1181 if ( nbEnt >= maxEnt ) {
1182 maxEnt=2*nbEnt+1;
1183 entries=(cache_entry*)realloc(entries,maxEnt*sizeof(cache_entry));
1184 }
1185 entries[nbEnt].key=key;
1186 entries[nbEnt].score=nScore;
1187 entries[nbEnt].owner=owner;
1188 entries[nbEnt].th=th;
1189 entries[nbEnt].tv=tv;
1190 entries[nbEnt].prev=entries[nbEnt].next=-1;
1191 entries[nbEnt].ipb=*ipb;
1192 if ( mpb ) {
1193 entries[nbEnt].hasMask=true;
1194 entries[nbEnt].mpb=*mpb;
1195 } else {
1196 entries[nbEnt].hasMask=false;
1197 }
1198 entries[nbEnt].next=keys[key];
1199 if ( entries[nbEnt].next >= 0 ) entries[entries[nbEnt].next].prev=nbEnt;
1200 keys[key]=nbEnt;
1202 nbEnt++;
1203 return true;
1204 }
1205 #endif