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 }
102 static void
103 nr_arena_item_private_finalize (NRObject *object)
104 {
105 NRArenaItem *item=static_cast<NRArenaItem *>(object);
107 #ifdef arena_item_tile_cache
108 remove_caches(item);
109 #endif
111 item->px = NULL;
112 item->transform = NULL;
114 ((NRObjectClass *) (parent_class))->finalize (object);
115 }
117 NRArenaItem *
118 nr_arena_item_children (NRArenaItem *item)
119 {
120 nr_return_val_if_fail (item != NULL, NULL);
121 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
123 if (NR_ARENA_ITEM_VIRTUAL (item, children))
124 return NR_ARENA_ITEM_VIRTUAL (item, children) (item);
126 return NULL;
127 }
129 NRArenaItem *
130 nr_arena_item_last_child (NRArenaItem *item)
131 {
132 nr_return_val_if_fail (item != NULL, NULL);
133 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
135 if (NR_ARENA_ITEM_VIRTUAL (item, last_child)) {
136 return NR_ARENA_ITEM_VIRTUAL (item, last_child) (item);
137 } else {
138 NRArenaItem *ref;
139 ref = nr_arena_item_children (item);
140 if (ref) while (ref->next) ref = ref->next;
141 return ref;
142 }
143 }
145 void
146 nr_arena_item_add_child (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
147 {
148 nr_return_if_fail (item != NULL);
149 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
150 nr_return_if_fail (child != NULL);
151 nr_return_if_fail (NR_IS_ARENA_ITEM (child));
152 nr_return_if_fail (child->parent == NULL);
153 nr_return_if_fail (child->prev == NULL);
154 nr_return_if_fail (child->next == NULL);
155 nr_return_if_fail (child->arena == item->arena);
156 nr_return_if_fail (child != ref);
157 nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
158 nr_return_if_fail (!ref || (ref->parent == item));
160 if (NR_ARENA_ITEM_VIRTUAL (item, add_child))
161 NR_ARENA_ITEM_VIRTUAL (item, add_child) (item, child, ref);
162 }
164 void
165 nr_arena_item_remove_child (NRArenaItem *item, NRArenaItem *child)
166 {
167 nr_return_if_fail (item != NULL);
168 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
169 nr_return_if_fail (child != NULL);
170 nr_return_if_fail (NR_IS_ARENA_ITEM (child));
171 nr_return_if_fail (child->parent == item);
173 if (NR_ARENA_ITEM_VIRTUAL (item, remove_child))
174 NR_ARENA_ITEM_VIRTUAL (item, remove_child) (item, child);
175 }
177 void
178 nr_arena_item_set_child_position (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
179 {
180 nr_return_if_fail (item != NULL);
181 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
182 nr_return_if_fail (child != NULL);
183 nr_return_if_fail (NR_IS_ARENA_ITEM (child));
184 nr_return_if_fail (child->parent == item);
185 nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
186 nr_return_if_fail (!ref || (ref->parent == item));
188 if (NR_ARENA_ITEM_VIRTUAL (item, set_child_position))
189 NR_ARENA_ITEM_VIRTUAL (item, set_child_position) (item, child, ref);
190 }
192 NRArenaItem *
193 nr_arena_item_ref (NRArenaItem *item)
194 {
195 nr_object_ref ((NRObject *) item);
197 return item;
198 }
200 NRArenaItem *
201 nr_arena_item_unref (NRArenaItem *item)
202 {
203 nr_object_unref ((NRObject *) item);
205 return NULL;
206 }
208 unsigned int
209 nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset)
210 {
211 NRGC childgc(gc);
213 nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
214 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
215 nr_return_val_if_fail (!(state & NR_ARENA_ITEM_STATE_INVALID), NR_ARENA_ITEM_STATE_INVALID);
217 #ifdef NR_ARENA_ITEM_DEBUG_CASCADE
218 printf ("Update %s:%p %x %x %x\n", nr_type_name_from_instance ((GTypeInstance *) item), item, state, item->state, reset);
219 #endif
221 /* return if in error */
222 if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
223 /* Set reset flags according to propagation status */
224 if (item->propagate) {
225 reset |= ~item->state;
226 item->propagate = FALSE;
227 }
228 /* Reset our state */
229 item->state &= ~reset;
230 /* Return if NOP */
231 if (!(~item->state & state)) return item->state;
232 /* Test whether to return immediately */
233 if (area && (item->state & NR_ARENA_ITEM_STATE_BBOX)) {
234 if (!nr_rect_l_test_intersect (area, &item->bbox)) return item->state;
235 }
237 /* Reset image cache, if not to be kept */
238 if (!(item->state & NR_ARENA_ITEM_STATE_IMAGE) && (item->px)) {
239 item->px = NULL;
240 }
241 #ifdef arena_item_tile_cache
242 remove_caches(item);
243 #endif
245 /* Set up local gc */
246 childgc = *gc;
247 if (item->transform) {
248 nr_matrix_multiply (&childgc.transform, item->transform, &childgc.transform);
249 }
250 /* Remember the transformation matrix */
251 item->ctm = childgc.transform;
253 /* Invoke the real method */
254 item->state = NR_ARENA_ITEM_VIRTUAL (item, update) (item, area, &childgc, state, reset);
255 if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
256 /* Enlarge the bounding box to contain filter effects */
257 if(item->filter) {
258 item->filter->bbox_enlarge(item->bbox);
259 }
261 /* Clipping */
262 if (item->clip) {
263 unsigned int newstate;
264 newstate = nr_arena_item_invoke_update (item->clip, area, &childgc, state, reset);
265 if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
266 item->state |= NR_ARENA_ITEM_STATE_INVALID;
267 return item->state;
268 }
269 nr_rect_l_intersect (&item->bbox, &item->bbox, &item->clip->bbox);
270 }
271 /* Masking */
272 if (item->mask) {
273 unsigned int newstate;
274 newstate = nr_arena_item_invoke_update (item->mask, area, &childgc, state, reset);
275 if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
276 item->state |= NR_ARENA_ITEM_STATE_INVALID;
277 return item->state;
278 }
279 nr_rect_l_intersect (&item->bbox, &item->bbox, &item->mask->bbox);
280 }
282 return item->state;
283 }
285 /**
286 * Render item to pixblock.
287 *
288 * \return Has NR_ARENA_ITEM_STATE_RENDER set on success.
289 */
291 unsigned int nr_arena_item_invoke_render(NRArenaItem *item, NRRectL const *area, NRPixBlock *pb, unsigned int flags)
292 {
293 NRRectL carea;
294 NRPixBlock *dpb;
295 NRPixBlock cpb;
296 unsigned int state;
298 nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
299 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
300 nr_return_val_if_fail (item->state & NR_ARENA_ITEM_STATE_BBOX, item->state);
302 #ifdef NR_ARENA_ITEM_VERBOSE
303 printf ("Invoke render %p: %d %d - %d %d\n", item, area->x0, area->y0, area->x1, area->y1);
304 #endif
306 #ifdef arena_item_tile_cache
307 item->activity*=0.5;
308 #endif
310 /* If we are outside bbox just return successfully */
311 if (!item->visible) return item->state | NR_ARENA_ITEM_STATE_RENDER;
312 nr_rect_l_intersect (&carea, area, &item->bbox);
313 if (nr_rect_l_test_empty (&carea)) return item->state | NR_ARENA_ITEM_STATE_RENDER;
314 if(item->filter) {
315 nr_rect_l_enlarge(&carea, item->filter->get_enlarge(item->ctm));
316 nr_rect_l_intersect(&carea, &carea, &item->bbox);
317 }
319 if (item->px) {
320 /* Has cache pixblock, render this and return */
321 nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
322 /* fixme: This probably cannot overflow, because we render only if visible */
323 /* fixme: and pixel cache is there only for small items */
324 /* fixme: But this still needs extra check (Lauris) */
325 item->bbox.x0, item->bbox.y0,
326 item->bbox.x1, item->bbox.y1,
327 item->px, 4 * (item->bbox.x1 - item->bbox.x0), FALSE, FALSE);
328 nr_blit_pixblock_pixblock (pb, &cpb);
329 nr_pixblock_release (&cpb);
330 pb->empty = FALSE;
331 return item->state | NR_ARENA_ITEM_STATE_RENDER;
332 }
334 dpb = pb;
335 bool canCache=false;
336 #ifdef arena_item_tile_cache
337 bool checkCache=false;
338 int tile_h=0,tile_v=0;
339 #endif
340 /* Setup cache if we can */
341 if ((!(flags & NR_ARENA_ITEM_RENDER_NO_CACHE)) &&
342 (carea.x0 <= item->bbox.x0) && (carea.y0 <= item->bbox.y0) &&
343 (carea.x1 >= item->bbox.x1) && (carea.y1 >= item->bbox.y1) &&
344 (((item->bbox.x1 - item->bbox.x0) * (item->bbox.y1 - item->bbox.y0)) <= 4096)) {
345 // Item bbox is fully in renderable area and size is acceptable
346 carea.x0 = item->bbox.x0;
347 carea.y0 = item->bbox.y0;
348 carea.x1 = item->bbox.x1;
349 carea.y1 = item->bbox.y1;
350 item->px = new (GC::ATOMIC) unsigned char[4 * (carea.x1 - carea.x0) * (carea.y1 - carea.y0)];
351 nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
352 carea.x0, carea.y0, carea.x1, carea.y1,
353 item->px, 4 * (carea.x1 - carea.x0), TRUE, TRUE);
354 cpb.visible_area = pb->visible_area;
355 dpb = &cpb;
356 // Set nocache flag for downstream rendering
357 flags |= NR_ARENA_ITEM_RENDER_NO_CACHE;
358 } else {
359 #ifdef arena_item_tile_cache
360 if ( item->skipCaching ) {
361 } else {
362 int tl=area->x0&(~127);
363 int tt=area->y0&(~127);
364 if ( area->x1 <= tl+128 && area->y1 <= tt+128 ) {
365 checkCache=true;
366 tile_h=tl/128;
367 tile_v=tt/128;
368 int surf=(area->x1-area->x0)*(area->y1-area->y0);
369 if ( surf >= 4096 ) {
370 canCache=true;
371 carea.x0=tl;
372 carea.y0=tt;
373 carea.x1=tl+128;
374 carea.y1=tt+128;
375 }
376 }
377 }
378 #endif
379 }
381 #ifdef arena_item_tile_cache
382 item->activity+=1.0;
383 #endif
385 #ifdef arena_item_tile_cache
386 if ( checkCache ) {
387 NRPixBlock ipb, mpb;
388 bool hasMask;
389 if ( test_cache(item,tile_h,tile_v,ipb,mpb,hasMask) ) {
390 // youpi! c'etait deja cache
391 if ( hasMask ) {
392 nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
393 } else if ( ((item->opacity != 255) && !item->render_opacity) ) {
394 nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
395 } else {
396 nr_blit_pixblock_pixblock (pb, &ipb);
397 }
398 pb->empty = FALSE;
399 return item->state | NR_ARENA_ITEM_STATE_RENDER;
400 }
401 }
402 #endif
403 if ( canCache ) {
404 #ifdef arena_item_tile_cache
405 // nota: exclusif de dpb != pb, donc pas de cas particulier a la fin
406 NRPixBlock ipb, mpb;
408 // struct timeval start_time,end_time;
409 // gettimeofday(&start_time,NULL);
410 GTimeVal start_time,end_time;
411 g_get_current_time (&start_time);
412 int duration=0;
414 /* Setup and render item buffer */
415 nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
416 ipb.visible_area = pb->visible_area;
417 state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
418 if (state & NR_ARENA_ITEM_STATE_INVALID) {
419 /* Clean up and return error */
420 nr_pixblock_release (&ipb);
421 if (dpb != pb) nr_pixblock_release (dpb);
422 item->state |= NR_ARENA_ITEM_STATE_INVALID;
423 return item->state;
424 }
425 ipb.empty = FALSE;
427 if (item->clip || item->mask) {
428 /* Setup mask pixblock */
429 nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
430 mpb.visible_area = pb->visible_area;
431 /* Do clip if needed */
432 if (item->clip) {
433 state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
434 if (state & NR_ARENA_ITEM_STATE_INVALID) {
435 /* Clean up and return error */
436 nr_pixblock_release (&mpb);
437 nr_pixblock_release (&ipb);
438 if (dpb != pb) nr_pixblock_release (dpb);
439 item->state |= NR_ARENA_ITEM_STATE_INVALID;
440 return item->state;
441 }
442 mpb.empty = FALSE;
443 }
444 /* Do mask if needed */
445 if (item->mask) {
446 NRPixBlock tpb;
447 /* Set up yet another temporary pixblock */
448 nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
449 tpb.visible_area = pb->visible_area;
450 state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
451 if (state & NR_ARENA_ITEM_STATE_INVALID) {
452 /* Clean up and return error */
453 nr_pixblock_release (&tpb);
454 nr_pixblock_release (&mpb);
455 nr_pixblock_release (&ipb);
456 if (dpb != pb) nr_pixblock_release (dpb);
457 item->state |= NR_ARENA_ITEM_STATE_INVALID;
458 return item->state;
459 }
460 /* Composite with clip */
461 if (item->clip) {
462 int x, y;
463 for (y = carea.y0; y < carea.y1; y++) {
464 unsigned char *s, *d;
465 s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
466 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
467 for (x = carea.x0; x < carea.x1; x++) {
468 unsigned int m;
469 m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
470 d[0] = NR_PREMUL (d[0], m);
471 s += 4;
472 d += 1;
473 }
474 }
475 } else {
476 int x, y;
477 for (y = carea.y0; y < carea.y1; y++) {
478 unsigned char *s, *d;
479 s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
480 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
481 for (x = carea.x0; x < carea.x1; x++) {
482 unsigned int m;
483 m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
484 d[0] = m;
485 s += 4;
486 d += 1;
487 }
488 }
489 mpb.empty = FALSE;
490 }
491 nr_pixblock_release (&tpb);
492 }
493 /* Multiply with opacity if needed */
494 if ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) {
495 int x, y;
496 unsigned int a;
497 a = item->opacity;
498 for (y = carea.y0; y < carea.y1; y++) {
499 unsigned char *d;
500 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
501 for (x = carea.x0; x < carea.x1; x++) {
502 d[0] = NR_PREMUL (d[0], a);
503 d += 1;
504 }
505 }
506 }
507 /* Compose rendering pixblock int destination */
508 // gettimeofday(&end_time,NULL);
509 g_get_current_time (&end_time);
510 duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
511 if ( !(ipb.empty) ) {
512 nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
513 if ( insert_cache(item,tile_h,tile_v,&ipb,&mpb,item->activity,(double)duration) ) {
514 } else {
515 nr_pixblock_release (&mpb);
516 nr_pixblock_release (&ipb);
517 }
518 dpb->empty = FALSE;
519 } else {
520 nr_pixblock_release (&ipb);
521 }
522 } else if ( ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) ) {
523 /* Opacity only */
524 // gettimeofday(&end_time,NULL);
525 g_get_current_time (&end_time);
526 duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
527 if ( !(ipb.empty) ) {
528 nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
529 if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
530 } else {
531 nr_pixblock_release (&ipb);
532 }
533 dpb->empty = FALSE;
534 } else {
535 nr_pixblock_release (&ipb);
536 }
537 } else {
538 // gettimeofday(&end_time,NULL);
539 g_get_current_time (&end_time);
540 duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
541 if ( !(ipb.empty) ) {
542 nr_blit_pixblock_pixblock (dpb, &ipb);
543 if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
544 } else {
545 nr_pixblock_release (&ipb);
546 }
547 dpb->empty = FALSE;
548 } else {
549 nr_pixblock_release (&ipb);
550 }
551 }
552 #endif
553 } else {
554 /* Determine, whether we need temporary buffer */
555 if (item->clip || item->mask || ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) || item->filter) {
556 NRPixBlock ipb, mpb;
558 /* Setup and render item buffer */
559 nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
560 ipb.visible_area = pb->visible_area;
561 state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
562 if (state & NR_ARENA_ITEM_STATE_INVALID) {
563 /* Clean up and return error */
564 nr_pixblock_release (&ipb);
565 if (dpb != pb) nr_pixblock_release (dpb);
566 item->state |= NR_ARENA_ITEM_STATE_INVALID;
567 return item->state;
568 }
569 ipb.empty = FALSE;
571 /* Run filtering test, if a filter is set for this object */
572 if(item->filter) {
573 item->filter->render(item, &ipb);
574 }
576 if (item->clip || item->mask) {
577 /* Setup mask pixblock */
578 nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
579 mpb.visible_area = pb->visible_area;
580 /* Do clip if needed */
581 if (item->clip) {
582 state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
583 if (state & NR_ARENA_ITEM_STATE_INVALID) {
584 /* Clean up and return error */
585 nr_pixblock_release (&mpb);
586 nr_pixblock_release (&ipb);
587 if (dpb != pb) nr_pixblock_release (dpb);
588 item->state |= NR_ARENA_ITEM_STATE_INVALID;
589 return item->state;
590 }
591 mpb.empty = FALSE;
592 }
593 /* Do mask if needed */
594 if (item->mask) {
595 NRPixBlock tpb;
596 /* Set up yet another temporary pixblock */
597 nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
598 tpb.visible_area = pb->visible_area;
599 state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
600 if (state & NR_ARENA_ITEM_STATE_INVALID) {
601 /* Clean up and return error */
602 nr_pixblock_release (&tpb);
603 nr_pixblock_release (&mpb);
604 nr_pixblock_release (&ipb);
605 if (dpb != pb) nr_pixblock_release (dpb);
606 item->state |= NR_ARENA_ITEM_STATE_INVALID;
607 return item->state;
608 }
609 /* Composite with clip */
610 if (item->clip) {
611 int x, y;
612 for (y = carea.y0; y < carea.y1; y++) {
613 unsigned char *s, *d;
614 s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
615 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
616 for (x = carea.x0; x < carea.x1; x++) {
617 unsigned int m;
618 m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
619 d[0] = NR_PREMUL (d[0], m);
620 s += 4;
621 d += 1;
622 }
623 }
624 } else {
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 = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
633 d[0] = m;
634 s += 4;
635 d += 1;
636 }
637 }
638 mpb.empty = FALSE;
639 }
640 nr_pixblock_release (&tpb);
641 }
642 /* Multiply with opacity if needed */
643 if ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) {
644 int x, y;
645 unsigned int a;
646 a = item->opacity;
647 for (y = carea.y0; y < carea.y1; y++) {
648 unsigned char *d;
649 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
650 for (x = carea.x0; x < carea.x1; x++) {
651 d[0] = NR_PREMUL (d[0], a);
652 d += 1;
653 }
654 }
655 }
656 /* Compose rendering pixblock int destination */
657 nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
658 nr_pixblock_release (&mpb);
659 } else {
660 /* Opacity only */
661 nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
662 }
663 nr_pixblock_release (&ipb);
664 dpb->empty = FALSE;
665 } else {
666 /* Just render */
667 state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, dpb, flags);
668 if (state & NR_ARENA_ITEM_STATE_INVALID) {
669 /* Clean up and return error */
670 if (dpb != pb) nr_pixblock_release (dpb);
671 item->state |= NR_ARENA_ITEM_STATE_INVALID;
672 return item->state;
673 }
674 dpb->empty = FALSE;
675 }
677 if (dpb != pb) {
678 /* Have to blit from cache */
679 nr_blit_pixblock_pixblock (pb, dpb);
680 nr_pixblock_release (dpb);
681 pb->empty = FALSE;
682 item->state |= NR_ARENA_ITEM_STATE_IMAGE;
683 }
684 }
685 return item->state | NR_ARENA_ITEM_STATE_RENDER;
686 }
688 unsigned int
689 nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
690 {
691 nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
692 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
693 /* we originally short-circuited if the object state included
694 * NR_ARENA_ITEM_STATE_CLIP (and showed a warning on the console);
695 * anyone know why we stopped doing so?
696 */
697 nr_return_val_if_fail ((pb->area.x1 - pb->area.x0) >= (area->x1 - area->x0), NR_ARENA_ITEM_STATE_INVALID);
698 nr_return_val_if_fail ((pb->area.y1 - pb->area.y0) >= (area->y1 - area->y0), NR_ARENA_ITEM_STATE_INVALID);
700 #ifdef NR_ARENA_ITEM_VERBOSE
701 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);
702 #endif
704 if (item->visible && nr_rect_l_test_intersect (area, &item->bbox)) {
705 /* Need render that item */
706 if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->clip) {
707 return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS(item))->clip (item, area, pb);
708 }
709 }
711 return item->state;
712 }
714 NRArenaItem *
715 nr_arena_item_invoke_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky)
716 {
717 nr_return_val_if_fail (item != NULL, NULL);
718 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
720 // Sometimes there's no BBOX in item->state, reason unknown (bug 992817); I made this not an assert to remove the warning
721 if (!(item->state & NR_ARENA_ITEM_STATE_BBOX) || !(item->state & NR_ARENA_ITEM_STATE_PICK))
722 return NULL;
724 if (!sticky && !(item->visible && item->sensitive)) return NULL;
726 // TODO: rewrite using NR::Rect
727 const double x = p[NR::X];
728 const double y = p[NR::Y];
730 if (((x + delta) >= item->bbox.x0) &&
731 ((x - delta) < item->bbox.x1) &&
732 ((y + delta) >= item->bbox.y0) &&
733 ((y - delta) < item->bbox.y1)) {
734 if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick)
735 return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick (item, p, delta, sticky);
736 }
738 return NULL;
739 }
741 void
742 nr_arena_item_request_update (NRArenaItem *item, unsigned int reset, unsigned int propagate)
743 {
744 nr_return_if_fail (item != NULL);
745 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
746 nr_return_if_fail (!(reset & NR_ARENA_ITEM_STATE_INVALID));
748 if (propagate && !item->propagate) item->propagate = TRUE;
750 if (item->state & reset) {
751 item->state &= ~reset;
752 if (item->parent) {
753 nr_arena_item_request_update (item->parent, reset, FALSE);
754 } else {
755 nr_arena_request_update (item->arena, item);
756 }
757 }
758 }
760 void
761 nr_arena_item_request_render (NRArenaItem *item)
762 {
763 nr_return_if_fail (item != NULL);
764 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
766 nr_arena_request_render_rect (item->arena, &item->bbox);
767 }
769 /* Public */
771 NRArenaItem *
772 nr_arena_item_unparent (NRArenaItem *item)
773 {
774 nr_return_val_if_fail (item != NULL, NULL);
775 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
777 nr_arena_item_request_render (item);
779 if (item->parent) {
780 nr_arena_item_remove_child (item->parent, item);
781 }
783 return NULL;
784 }
786 void
787 nr_arena_item_append_child (NRArenaItem *parent, NRArenaItem *child)
788 {
789 nr_return_if_fail (parent != NULL);
790 nr_return_if_fail (NR_IS_ARENA_ITEM (parent));
791 nr_return_if_fail (child != NULL);
792 nr_return_if_fail (NR_IS_ARENA_ITEM (child));
793 nr_return_if_fail (parent->arena == child->arena);
794 nr_return_if_fail (child->parent == NULL);
795 nr_return_if_fail (child->prev == NULL);
796 nr_return_if_fail (child->next == NULL);
798 nr_arena_item_add_child (parent, child, nr_arena_item_last_child (parent));
799 }
801 void
802 nr_arena_item_set_transform(NRArenaItem *item, NR::Matrix const &transform)
803 {
804 NRMatrix const t(transform);
805 nr_arena_item_set_transform(item, &t);
806 }
808 void
809 nr_arena_item_set_transform(NRArenaItem *item, NRMatrix const *transform)
810 {
811 const NRMatrix *ms, *md;
813 nr_return_if_fail (item != NULL);
814 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
816 if (!transform && !item->transform) return;
818 md = (item->transform) ? item->transform : &NR_MATRIX_IDENTITY;
819 ms = (transform) ? transform : &NR_MATRIX_IDENTITY;
821 if (!NR_MATRIX_DF_TEST_CLOSE (md, ms, NR_EPSILON)) {
822 nr_arena_item_request_render (item);
823 if (!transform || nr_matrix_test_identity (transform, NR_EPSILON)) {
824 /* Set to identity affine */
825 item->transform = NULL;
826 } else {
827 if (!item->transform) item->transform = new (GC::ATOMIC) NRMatrix();
828 *item->transform = *transform;
829 }
830 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
831 }
832 }
834 void
835 nr_arena_item_set_opacity (NRArenaItem *item, double opacity)
836 {
837 nr_return_if_fail (item != NULL);
838 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
840 nr_arena_item_request_render (item);
842 item->opacity = (unsigned int) (opacity * 255.9999);
843 }
845 void
846 nr_arena_item_set_sensitive (NRArenaItem *item, unsigned int sensitive)
847 {
848 nr_return_if_fail (item != NULL);
849 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
851 /* fixme: mess with pick/repick... */
853 item->sensitive = sensitive;
854 }
856 void
857 nr_arena_item_set_visible (NRArenaItem *item, unsigned int visible)
858 {
859 nr_return_if_fail (item != NULL);
860 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
862 item->visible = visible;
864 nr_arena_item_request_render (item);
865 }
867 void
868 nr_arena_item_set_clip (NRArenaItem *item, NRArenaItem *clip)
869 {
870 nr_return_if_fail (item != NULL);
871 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
872 nr_return_if_fail (!clip || NR_IS_ARENA_ITEM (clip));
874 if (clip != item->clip) {
875 nr_arena_item_request_render (item);
876 if (item->clip) item->clip = nr_arena_item_detach (item, item->clip);
877 if (clip) item->clip = nr_arena_item_attach (item, clip, NULL, NULL);
878 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
879 }
880 }
882 void
883 nr_arena_item_set_mask (NRArenaItem *item, NRArenaItem *mask)
884 {
885 nr_return_if_fail (item != NULL);
886 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
887 nr_return_if_fail (!mask || NR_IS_ARENA_ITEM (mask));
889 if (mask != item->mask) {
890 nr_arena_item_request_render (item);
891 if (item->mask) item->mask = nr_arena_item_detach (item, item->mask);
892 if (mask) item->mask = nr_arena_item_attach (item, mask, NULL, NULL);
893 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
894 }
895 }
897 void
898 nr_arena_item_set_order (NRArenaItem *item, int order)
899 {
900 NRArenaItem *children, *child, *ref;
901 int pos;
903 nr_return_if_fail (item != NULL);
904 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
906 if (!item->parent) return;
908 children = nr_arena_item_children (item->parent);
910 ref = NULL;
911 pos = 0;
912 for (child = children; child != NULL; child = child->next) {
913 if (pos >= order) break;
914 if (child != item) {
915 ref = child;
916 pos += 1;
917 }
918 }
920 nr_arena_item_set_child_position (item->parent, item, ref);
921 }
923 /* Helpers */
925 NRArenaItem *
926 nr_arena_item_attach (NRArenaItem *parent, NRArenaItem *child, NRArenaItem *prev, NRArenaItem *next)
927 {
928 nr_return_val_if_fail (parent != NULL, NULL);
929 nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
930 nr_return_val_if_fail (child != NULL, NULL);
931 nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
932 nr_return_val_if_fail (child->parent == NULL, NULL);
933 nr_return_val_if_fail (child->prev == NULL, NULL);
934 nr_return_val_if_fail (child->next == NULL, NULL);
935 nr_return_val_if_fail (!prev || NR_IS_ARENA_ITEM (prev), NULL);
936 nr_return_val_if_fail (!prev || (prev->parent == parent), NULL);
937 nr_return_val_if_fail (!prev || (prev->next == next), NULL);
938 nr_return_val_if_fail (!next || NR_IS_ARENA_ITEM (next), NULL);
939 nr_return_val_if_fail (!next || (next->parent == parent), NULL);
940 nr_return_val_if_fail (!next || (next->prev == prev), NULL);
942 child->parent = parent;
943 child->prev = prev;
944 child->next = next;
946 if (prev) prev->next = child;
947 if (next) next->prev = child;
949 return child;
950 }
952 NRArenaItem *
953 nr_arena_item_detach (NRArenaItem *parent, NRArenaItem *child)
954 {
955 NRArenaItem *prev, *next;
957 nr_return_val_if_fail (parent != NULL, NULL);
958 nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
959 nr_return_val_if_fail (child != NULL, NULL);
960 nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
961 nr_return_val_if_fail (child->parent == parent, NULL);
963 prev = child->prev;
964 next = child->next;
966 child->parent = NULL;
967 child->prev = NULL;
968 child->next = NULL;
970 if (prev) prev->next = next;
971 if (next) next->prev = prev;
973 return next;
974 }
976 /*
977 *
978 * caches
979 *
980 */
982 #ifdef arena_item_tile_cache
983 typedef struct cache_entry {
984 int key;
985 double score;
986 NRArenaItem* owner;
987 int th,tv;
988 int prev,next;
989 NRPixBlock ipb;
990 bool hasMask;
991 NRPixBlock mpb;
992 } cache_entry;
994 int hash_max=2048,hash_fill=1024;
996 int *keys=NULL;
997 int nbCch=0;
999 int nbEnt=0,maxEnt=0;
1000 cache_entry* entries=NULL;
1002 //#define tile_cache_stats
1003 #ifdef tile_cache_stats
1004 double hits=0,misses=0;
1005 int hitMissCount=0;
1006 #endif
1008 int hash_that(NRArenaItem* owner,int th,int tv)
1009 {
1010 int res=GPOINTER_TO_INT(owner);
1011 res*=17;
1012 res+=th;
1013 res*=59;
1014 res+=tv;
1015 res*=217;
1016 if ( res < 0 ) res=-res;
1017 res%=hash_max;
1018 return res;
1019 }
1021 bool test_cache(NRArenaItem* owner,int th,int tv,NRPixBlock &ipb,NRPixBlock &mpb,bool &hasMask)
1022 {
1023 if ( keys == NULL ) {
1024 hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1025 hash_fill=(hash_max*3)/4;
1026 keys=(int*)malloc(hash_max*sizeof(int));
1027 for (int i=0;i<hash_max;i++) keys[i]=-1;
1028 }
1029 int key=hash_that(owner,th,tv);
1030 if ( keys[key] < 0 ) {
1031 #ifdef tile_cache_stats
1032 misses+=1.0;
1033 #endif
1034 return false;
1035 }
1036 int cur=keys[key];
1037 while ( cur >= 0 && cur < nbEnt ) {
1038 if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
1039 hasMask=entries[cur].hasMask;
1040 ipb=entries[cur].ipb;
1041 mpb=entries[cur].mpb;
1042 #ifdef tile_cache_stats
1043 hits+=1.0;
1044 #endif
1045 return true;
1046 }
1047 cur=entries[cur].next;
1048 }
1049 #ifdef tile_cache_stats
1050 misses+=1.0;
1051 #endif
1052 return false;
1053 }
1054 void remove_one_cache(int no)
1055 {
1056 if ( no < 0 || no >= nbEnt ) return;
1058 nr_pixblock_release(&entries[no].ipb);
1059 if ( entries[no].hasMask ) nr_pixblock_release(&entries[no].mpb);
1061 if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=entries[no].next;
1062 if ( entries[no].next >= 0 ) entries[entries[no].next].prev=entries[no].prev;
1063 if ( entries[no].prev < 0 ) keys[entries[no].key]=entries[no].next;
1064 entries[no].prev=entries[no].next=entries[no].key=-1;
1066 if ( no == nbEnt-1 ) {
1067 nbEnt--;
1068 return;
1069 }
1070 entries[no]=entries[--nbEnt];
1071 if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=no;
1072 if ( entries[no].next >= 0 ) entries[entries[no].next].prev=no;
1073 if ( entries[no].prev < 0 ) keys[entries[no].key]=no;
1074 }
1075 void remove_caches(NRArenaItem* owner)
1076 {
1077 if ( keys == NULL ) {
1078 hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1079 hash_fill=(hash_max*3)/4;
1080 keys=(int*)malloc(hash_max*sizeof(int));
1081 for (int i=0;i<hash_max;i++) keys[i]=-1;
1082 }
1083 for (int i=nbEnt-1;i>=0;i--) {
1084 if ( entries[i].owner == owner ) {
1085 remove_one_cache(i);
1086 }
1087 }
1088 }
1089 void age_cache(void)
1090 {
1091 for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
1092 }
1093 bool insert_cache(NRArenaItem* owner,int th,int tv,NRPixBlock *ipb,NRPixBlock *mpb,double activity,double duration)
1094 {
1095 if ( keys == NULL ) {
1096 hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1097 hash_fill=(hash_max*3)/4;
1098 keys=(int*)malloc(hash_max*sizeof(int));
1099 for (int i=0;i<hash_max;i++) keys[i]=-1;
1100 }
1101 for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
1102 #ifdef tile_cache_stats
1103 hits*=0.95;
1104 misses*=0.95;
1105 hitMissCount++;
1106 if ( hitMissCount > 100 ) {
1107 hitMissCount=0;
1108 printf("hit/miss = %f used/total=%i/%i\n",(misses>0.001)?hits/misses:100000.0,nbEnt,hash_max); // localizing ok
1109 }
1110 #endif
1111 int key=hash_that(owner,th,tv);
1112 double nScore=/*activity**/duration;
1114 if ( keys[key] >= 0 ) {
1115 int cur=keys[key];
1116 while ( cur >= 0 && cur < nbEnt ) {
1117 if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
1118 remove_one_cache(cur);
1119 break;
1120 }
1121 cur=entries[cur].next;
1122 }
1123 }
1125 bool doAdd=false;
1126 if ( nbEnt < hash_fill ) {
1127 doAdd=true;
1128 } else {
1129 double worstS=entries[0].score;
1130 int worstE=0;
1131 for (int i=1;i<nbEnt;i++) {
1132 if ( entries[i].score < worstS ) {
1133 worstS=entries[i].score;
1134 worstE=i;
1135 }
1136 }
1137 if ( worstS < nScore ) {
1138 doAdd=true;
1139 remove_one_cache(worstE);
1140 }
1141 }
1142 if ( doAdd == false ) return false;
1143 if ( nbEnt >= maxEnt ) {
1144 maxEnt=2*nbEnt+1;
1145 entries=(cache_entry*)realloc(entries,maxEnt*sizeof(cache_entry));
1146 }
1147 entries[nbEnt].key=key;
1148 entries[nbEnt].score=nScore;
1149 entries[nbEnt].owner=owner;
1150 entries[nbEnt].th=th;
1151 entries[nbEnt].tv=tv;
1152 entries[nbEnt].prev=entries[nbEnt].next=-1;
1153 entries[nbEnt].ipb=*ipb;
1154 if ( mpb ) {
1155 entries[nbEnt].hasMask=true;
1156 entries[nbEnt].mpb=*mpb;
1157 } else {
1158 entries[nbEnt].hasMask=false;
1159 }
1160 entries[nbEnt].next=keys[key];
1161 if ( entries[nbEnt].next >= 0 ) entries[entries[nbEnt].next].prev=nbEnt;
1162 keys[key]=nbEnt;
1164 nbEnt++;
1165 return true;
1166 }
1167 #endif