d9031824a17d5bfa3ac851af5236653bcc773602
1 #define __NR_ARENA_SHAPE_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 */
17 #include <display/nr-arena.h>
18 #include <display/nr-arena-shape.h>
19 #include "display/nr-filter.h"
20 #include "display/nr-filter-gaussian.h"
21 #include "display/nr-filter-types.h"
22 #include <libnr/n-art-bpath.h>
23 #include <libnr/nr-path.h>
24 #include <libnr/nr-pixops.h>
25 #include <libnr/nr-matrix-ops.h>
26 #include <libnr/nr-matrix-fns.h>
27 #include <libnr/nr-blit.h>
28 #include <livarot/Path.h>
29 #include <livarot/float-line.h>
30 #include <livarot/int-line.h>
31 #include <style.h>
32 /* prefs-utils used for deciding, whether to run filtering test or not */
33 #include "prefs-utils.h"
34 #include "sp-filter.h"
35 #include "sp-gaussian-blur.h"
37 //int showRuns=0;
38 void nr_pixblock_render_shape_mask_or(NRPixBlock &m,Shape* theS);
40 static void nr_arena_shape_class_init(NRArenaShapeClass *klass);
41 static void nr_arena_shape_init(NRArenaShape *shape);
42 static void nr_arena_shape_finalize(NRObject *object);
44 static NRArenaItem *nr_arena_shape_children(NRArenaItem *item);
45 static void nr_arena_shape_add_child(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref);
46 static void nr_arena_shape_remove_child(NRArenaItem *item, NRArenaItem *child);
47 static void nr_arena_shape_set_child_position(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref);
49 static guint nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset);
50 static unsigned int nr_arena_shape_render(NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags);
51 static guint nr_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb);
52 static NRArenaItem *nr_arena_shape_pick(NRArenaItem *item, NR::Point p, double delta, unsigned int sticky);
54 static NRArenaItemClass *shape_parent_class;
56 NRType
57 nr_arena_shape_get_type(void)
58 {
59 static NRType type = 0;
60 if (!type) {
61 type = nr_object_register_type(NR_TYPE_ARENA_ITEM,
62 "NRArenaShape",
63 sizeof(NRArenaShapeClass),
64 sizeof(NRArenaShape),
65 (void (*)(NRObjectClass *)) nr_arena_shape_class_init,
66 (void (*)(NRObject *)) nr_arena_shape_init);
67 }
68 return type;
69 }
71 static void
72 nr_arena_shape_class_init(NRArenaShapeClass *klass)
73 {
74 NRObjectClass *object_class;
75 NRArenaItemClass *item_class;
77 object_class = (NRObjectClass *) klass;
78 item_class = (NRArenaItemClass *) klass;
80 shape_parent_class = (NRArenaItemClass *) ((NRObjectClass *) klass)->parent;
82 object_class->finalize = nr_arena_shape_finalize;
83 object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaShape>;
85 item_class->children = nr_arena_shape_children;
86 item_class->add_child = nr_arena_shape_add_child;
87 item_class->set_child_position = nr_arena_shape_set_child_position;
88 item_class->remove_child = nr_arena_shape_remove_child;
89 item_class->update = nr_arena_shape_update;
90 item_class->render = nr_arena_shape_render;
91 item_class->clip = nr_arena_shape_clip;
92 item_class->pick = nr_arena_shape_pick;
93 }
95 /**
96 * Initializes the arena shape, setting all parameters to null, 0, false,
97 * or other defaults
98 */
99 static void
100 nr_arena_shape_init(NRArenaShape *shape)
101 {
102 shape->curve = NULL;
103 shape->style = NULL;
104 shape->paintbox.x0 = shape->paintbox.y0 = 0.0F;
105 shape->paintbox.x1 = shape->paintbox.y1 = 256.0F;
107 nr_matrix_set_identity(&shape->ctm);
108 shape->fill_painter = NULL;
109 shape->stroke_painter = NULL;
110 shape->cached_fill = NULL;
111 shape->cached_stroke = NULL;
112 shape->cached_fpartialy = false;
113 shape->cached_spartialy = false;
114 shape->fill_shp = NULL;
115 shape->stroke_shp = NULL;
117 shape->delayed_shp = false;
119 shape->approx_bbox.x0 = shape->approx_bbox.y0 = 0;
120 shape->approx_bbox.x1 = shape->approx_bbox.y1 = 0;
121 nr_matrix_set_identity(&shape->cached_fctm);
122 nr_matrix_set_identity(&shape->cached_sctm);
124 shape->markers = NULL;
125 }
127 static void
128 nr_arena_shape_finalize(NRObject *object)
129 {
130 NRArenaShape *shape = (NRArenaShape *) object;
132 if (shape->fill_shp) delete shape->fill_shp;
133 if (shape->stroke_shp) delete shape->stroke_shp;
134 if (shape->cached_fill) delete shape->cached_fill;
135 if (shape->cached_stroke) delete shape->cached_stroke;
136 if (shape->fill_painter) sp_painter_free(shape->fill_painter);
137 if (shape->stroke_painter) sp_painter_free(shape->stroke_painter);
139 if (shape->style) sp_style_unref(shape->style);
140 if (shape->curve) sp_curve_unref(shape->curve);
142 ((NRObjectClass *) shape_parent_class)->finalize(object);
143 }
145 /**
146 * Retrieves the markers from the item
147 */
148 static NRArenaItem *
149 nr_arena_shape_children(NRArenaItem *item)
150 {
151 NRArenaShape *shape = (NRArenaShape *) item;
153 return shape->markers;
154 }
156 /**
157 * Attaches child to item, and if ref is not NULL, sets it and ref->next as
158 * the prev and next items. If ref is NULL, then it sets the item's markers
159 * as the next items.
160 */
161 static void
162 nr_arena_shape_add_child(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
163 {
164 NRArenaShape *shape = (NRArenaShape *) item;
166 if (!ref) {
167 shape->markers = nr_arena_item_attach(item, child, NULL, shape->markers);
168 } else {
169 ref->next = nr_arena_item_attach(item, child, ref, ref->next);
170 }
172 nr_arena_item_request_update(item, NR_ARENA_ITEM_STATE_ALL, FALSE);
173 }
175 /**
176 * Removes child from the shape. If there are no prev items in
177 * the child, it sets items' markers to the next item in the child.
178 */
179 static void
180 nr_arena_shape_remove_child(NRArenaItem *item, NRArenaItem *child)
181 {
182 NRArenaShape *shape = (NRArenaShape *) item;
184 if (child->prev) {
185 nr_arena_item_detach(item, child);
186 } else {
187 shape->markers = nr_arena_item_detach(item, child);
188 }
190 nr_arena_item_request_update(item, NR_ARENA_ITEM_STATE_ALL, FALSE);
191 }
193 /**
194 * Detaches child from item, and if there are no previous items in child, it
195 * sets item's markers to the child. It then attaches the child back onto the item.
196 * If ref is null, it sets the markers to be the next item, otherwise it uses
197 * the next/prev items in ref.
198 */
199 static void
200 nr_arena_shape_set_child_position(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
201 {
202 NRArenaShape *shape = (NRArenaShape *) item;
204 if (child->prev) {
205 nr_arena_item_detach(item, child);
206 } else {
207 shape->markers = nr_arena_item_detach(item, child);
208 }
210 if (!ref) {
211 shape->markers = nr_arena_item_attach(item, child, NULL, shape->markers);
212 } else {
213 ref->next = nr_arena_item_attach(item, child, ref, ref->next);
214 }
216 nr_arena_item_request_render(child);
217 }
219 void nr_arena_shape_update_stroke(NRArenaShape *shape, NRGC* gc, NRRectL *area);
220 void nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, NRRectL *area, bool force_shape = false);
221 void nr_arena_shape_add_bboxes(NRArenaShape* shape,NRRect &bbox);
223 /**
224 * Updates the arena shape 'item' and all of its children, including the markers.
225 */
226 static guint
227 nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset)
228 {
229 NRRect bbox;
231 NRArenaShape *shape = NR_ARENA_SHAPE(item);
233 unsigned int beststate = NR_ARENA_ITEM_STATE_ALL;
235 unsigned int newstate;
236 for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) {
237 newstate = nr_arena_item_invoke_update(child, area, gc, state, reset);
238 beststate = beststate & newstate;
239 }
241 if (!(state & NR_ARENA_ITEM_STATE_RENDER)) {
242 /* We do not have to create rendering structures */
243 shape->ctm = gc->transform;
244 if (state & NR_ARENA_ITEM_STATE_BBOX) {
245 if (shape->curve) {
246 NRBPath bp;
247 /* fixme: */
248 bbox.x0 = bbox.y0 = NR_HUGE;
249 bbox.x1 = bbox.y1 = -NR_HUGE;
250 bp.path = SP_CURVE_BPATH(shape->curve);
251 nr_path_matrix_bbox_union(&bp, gc->transform, &bbox);
252 item->bbox.x0 = (gint32)(bbox.x0 - 1.0F);
253 item->bbox.y0 = (gint32)(bbox.y0 - 1.0F);
254 item->bbox.x1 = (gint32)(bbox.x1 + 1.9999F);
255 item->bbox.y1 = (gint32)(bbox.y1 + 1.9999F);
256 }
257 if (beststate & NR_ARENA_ITEM_STATE_BBOX) {
258 for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) {
259 nr_rect_l_union(&item->bbox, &item->bbox, &child->bbox);
260 }
261 }
262 }
263 return (state | item->state);
264 }
266 shape->delayed_shp=true;
267 shape->ctm = gc->transform;
268 bbox.x0 = bbox.y0 = NR_HUGE;
269 bbox.x1 = bbox.y1 = -NR_HUGE;
271 bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == RENDERMODE_OUTLINE);
273 if (shape->curve) {
274 NRBPath bp;
275 /* fixme: */
276 bbox.x0 = bbox.y0 = NR_HUGE;
277 bbox.x1 = bbox.y1 = -NR_HUGE;
278 bp.path = SP_CURVE_BPATH(shape->curve);
279 nr_path_matrix_bbox_union(&bp, gc->transform, &bbox);
280 if (shape->_stroke.paint.type() != NRArenaShape::Paint::NONE || outline) {
281 float width, scale;
282 scale = NR_MATRIX_DF_EXPANSION(&gc->transform);
283 width = MAX(0.125, shape->_stroke.width * scale);
284 if ( fabs(shape->_stroke.width * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
285 bbox.x0-=width;
286 bbox.x1+=width;
287 bbox.y0-=width;
288 bbox.y1+=width;
289 }
290 // those pesky miters, now
291 float miterMax=width*shape->_stroke.mitre_limit;
292 if ( miterMax > 0.01 ) {
293 // grunt mode. we should compute the various miters instead (one for each point on the curve)
294 bbox.x0-=miterMax;
295 bbox.x1+=miterMax;
296 bbox.y0-=miterMax;
297 bbox.y1+=miterMax;
298 }
299 }
300 } else {
301 }
302 shape->approx_bbox.x0 = (gint32)(bbox.x0 - 1.0F);
303 shape->approx_bbox.y0 = (gint32)(bbox.y0 - 1.0F);
304 shape->approx_bbox.x1 = (gint32)(bbox.x1 + 1.9999F);
305 shape->approx_bbox.y1 = (gint32)(bbox.y1 + 1.9999F);
306 if ( area && nr_rect_l_test_intersect(area, &shape->approx_bbox) ) shape->delayed_shp=false;
308 /* Release state data */
309 if (TRUE || !nr_matrix_test_transform_equal(&gc->transform, &shape->ctm, NR_EPSILON)) {
310 /* Concept test */
311 if (shape->fill_shp) {
312 delete shape->fill_shp;
313 shape->fill_shp = NULL;
314 }
315 }
316 if (shape->stroke_shp) {
317 delete shape->stroke_shp;
318 shape->stroke_shp = NULL;
319 }
320 if (shape->fill_painter) {
321 sp_painter_free(shape->fill_painter);
322 shape->fill_painter = NULL;
323 }
324 if (shape->stroke_painter) {
325 sp_painter_free(shape->stroke_painter);
326 shape->stroke_painter = NULL;
327 }
329 if (!shape->curve ||
330 !shape->style ||
331 sp_curve_is_empty(shape->curve) ||
332 (( shape->_fill.paint.type() == NRArenaShape::Paint::NONE ) &&
333 ( shape->_stroke.paint.type() == NRArenaShape::Paint::NONE && !outline) ))
334 {
335 item->bbox = shape->approx_bbox;
336 return NR_ARENA_ITEM_STATE_ALL;
337 }
339 /* Build state data */
340 if ( shape->delayed_shp ) {
341 item->bbox=shape->approx_bbox;
342 } else {
343 nr_arena_shape_update_stroke(shape, gc, area);
344 nr_arena_shape_update_fill(shape, gc, area);
346 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0.0;
347 nr_arena_shape_add_bboxes(shape,bbox);
349 shape->approx_bbox.x0 = (gint32)(bbox.x0 - 1.0F);
350 shape->approx_bbox.y0 = (gint32)(bbox.y0 - 1.0F);
351 shape->approx_bbox.x1 = (gint32)(bbox.x1 + 1.9999F);
352 shape->approx_bbox.y1 = (gint32)(bbox.y1 + 1.9999F);
353 }
355 if (nr_rect_d_test_empty(&bbox)) return NR_ARENA_ITEM_STATE_ALL;
357 item->bbox.x0 = (gint32)(bbox.x0 - 1.0F);
358 item->bbox.y0 = (gint32)(bbox.y0 - 1.0F);
359 item->bbox.x1 = (gint32)(bbox.x1 + 1.0F);
360 item->bbox.y1 = (gint32)(bbox.y1 + 1.0F);
361 nr_arena_request_render_rect(item->arena, &item->bbox);
363 item->render_opacity = TRUE;
364 if ( shape->_fill.paint.type() == NRArenaShape::Paint::SERVER ) {
365 if (gc && gc->parent) {
366 shape->fill_painter = sp_paint_server_painter_new(shape->_fill.paint.server(),
367 NR::Matrix(&gc->transform), NR::Matrix(&gc->parent->transform),
368 &shape->paintbox);
369 }
370 item->render_opacity = FALSE;
371 }
372 if ( shape->_stroke.paint.type() == NRArenaShape::Paint::SERVER ) {
373 if (gc && gc->parent) {
374 shape->stroke_painter = sp_paint_server_painter_new(shape->_stroke.paint.server(),
375 NR::Matrix(&gc->transform), NR::Matrix(&gc->parent->transform),
376 &shape->paintbox);
377 }
378 item->render_opacity = FALSE;
379 }
380 if ( item->render_opacity == TRUE
381 && shape->_fill.paint.type() != NRArenaShape::Paint::NONE
382 && shape->_stroke.paint.type() != NRArenaShape::Paint::NONE )
383 {
384 // don't merge item opacity with paint opacity if there is a stroke on the fill
385 item->render_opacity = FALSE;
386 }
388 if (beststate & NR_ARENA_ITEM_STATE_BBOX) {
389 for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) {
390 nr_rect_l_union(&item->bbox, &item->bbox, &child->bbox);
391 }
392 }
394 return NR_ARENA_ITEM_STATE_ALL;
395 }
397 int matrix_is_isometry(NR::Matrix p) {
398 NR::Matrix tp;
399 // transposition
400 tp[0]=p[0];
401 tp[1]=p[2];
402 tp[2]=p[1];
403 tp[3]=p[3];
404 for (int i = 4; i < 6; i++) // shut valgrind up :)
405 tp[i] = p[i] = 0;
406 NR::Matrix isom = tp*p; // A^T * A = adjunct?
407 // Is the adjunct nearly an identity function?
408 if (isom.is_translation(0.01)) {
409 // the transformation is an isometry -> no need to recompute
410 // the uncrossed polygon
411 if ( p.det() < 0 )
412 return -1;
413 else
414 return 1;
415 }
416 return 0;
417 }
419 static bool is_inner_area(NRRectL const &outer, NRRectL const &inner) {
420 return (outer.x0 <= inner.x0 && outer.y0 <= inner.y0 && outer.x1 >= inner.x1 && outer.y1 >= inner.y1);
421 }
423 /** force_shape is used for clipping paths, when we need the shape for clipping even if it's not filled */
424 void
425 nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, NRRectL *area, bool force_shape)
426 {
427 if ((shape->_fill.paint.type() != NRArenaShape::Paint::NONE || force_shape) &&
428 ((shape->curve->end > 2) || (SP_CURVE_BPATH(shape->curve)[1].code == NR_CURVETO)) ) {
429 if (TRUE || !shape->fill_shp) {
430 NR::Matrix cached_to_new = NR::identity();
431 int isometry = 0;
432 if ( shape->cached_fill ) {
433 if (shape->cached_fctm == gc->transform) {
434 isometry = 2; // identity
435 } else {
436 cached_to_new = shape->cached_fctm.inverse() * gc->transform;
437 isometry = matrix_is_isometry(cached_to_new);
438 }
439 if (0 != isometry && !is_inner_area(shape->cached_farea, *area))
440 isometry = 0;
441 }
442 if ( isometry == 0 ) {
443 if ( shape->cached_fill == NULL ) shape->cached_fill=new Shape;
444 shape->cached_fill->Reset();
446 Path* thePath=new Path;
447 Shape* theShape=new Shape;
448 {
449 NR::Matrix tempMat(gc->transform);
450 thePath->LoadArtBPath(SP_CURVE_BPATH(shape->curve),tempMat,true);
451 }
453 if (is_inner_area(*area, NR_ARENA_ITEM(shape)->bbox)) {
454 thePath->Convert(1.0);
455 shape->cached_fpartialy = false;
456 } else {
457 thePath->Convert(area, 1.0);
458 shape->cached_fpartialy = true;
459 }
461 thePath->Fill(theShape, 0);
463 if ( shape->_fill.rule == NRArenaShape::EVEN_ODD ) {
464 shape->cached_fill->ConvertToShape(theShape, fill_oddEven);
465 // alternatively, this speeds up rendering of oddeven shapes but disables AA :(
466 //shape->cached_fill->Copy(theShape);
467 } else {
468 shape->cached_fill->ConvertToShape(theShape, fill_nonZero);
469 }
470 shape->cached_fctm=gc->transform;
471 shape->cached_farea = *area;
472 delete theShape;
473 delete thePath;
474 if ( shape->fill_shp == NULL )
475 shape->fill_shp = new Shape;
477 shape->fill_shp->Copy(shape->cached_fill);
479 } else if ( 2 == isometry ) {
480 if ( shape->fill_shp == NULL ) {
481 shape->fill_shp = new Shape;
482 shape->fill_shp->Copy(shape->cached_fill);
483 }
484 } else {
486 if ( shape->fill_shp == NULL )
487 shape->fill_shp = new Shape;
489 shape->fill_shp->Reset(shape->cached_fill->numberOfPoints(),
490 shape->cached_fill->numberOfEdges());
491 for (int i = 0; i < shape->cached_fill->numberOfPoints(); i++)
492 shape->fill_shp->AddPoint(shape->cached_fill->getPoint(i).x * cached_to_new);
493 if ( isometry == 1 ) {
494 for (int i = 0; i < shape->cached_fill->numberOfEdges(); i++)
495 shape->fill_shp->AddEdge(shape->cached_fill->getEdge(i).st,
496 shape->cached_fill->getEdge(i).en);
497 } else if ( isometry == -1 ) { // need to flip poly.
498 for (int i = 0; i < shape->cached_fill->numberOfEdges(); i++)
499 shape->fill_shp->AddEdge(shape->cached_fill->getEdge(i).en,
500 shape->cached_fill->getEdge(i).st);
501 }
502 shape->fill_shp->ForceToPolygon();
503 shape->fill_shp->needPointsSorting();
504 shape->fill_shp->needEdgesSorting();
505 }
506 shape->delayed_shp |= shape->cached_fpartialy;
507 }
508 }
509 }
511 void
512 nr_arena_shape_update_stroke(NRArenaShape *shape,NRGC* gc, NRRectL *area)
513 {
514 SPStyle* style = shape->style;
516 float const scale = NR_MATRIX_DF_EXPANSION(&gc->transform);
518 bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == RENDERMODE_OUTLINE);
520 if (outline ||
521 ((shape->_stroke.paint.type() != NRArenaShape::Paint::NONE) &&
522 ( fabs(shape->_stroke.width * scale) > 0.01 ))) { // sinon c'est 0=oon veut pas de bord
524 float style_width = MAX(0.125, shape->_stroke.width * scale);
525 float width;
526 if (outline) {
527 width = 0.5; // 1 pixel wide, independent of zoom
528 } else {
529 width = style_width;
530 }
532 NR::Matrix cached_to_new = NR::identity();
534 int isometry = 0;
535 if ( shape->cached_stroke ) {
536 if (shape->cached_sctm == gc->transform) {
537 isometry = 2; // identity
538 } else {
539 cached_to_new = shape->cached_sctm.inverse() * gc->transform;
540 isometry = matrix_is_isometry(cached_to_new);
541 }
542 if (0 != isometry && !is_inner_area(shape->cached_sarea, *area))
543 isometry = 0;
544 if (0 != isometry && width != shape->cached_width) {
545 // if this happens without setting style, we have just switched to outline or back
546 isometry = 0;
547 }
548 }
550 if ( isometry == 0 ) {
551 if ( shape->cached_stroke == NULL ) shape->cached_stroke=new Shape;
552 shape->cached_stroke->Reset();
553 Path* thePath = new Path;
554 Shape* theShape = new Shape;
555 {
556 NR::Matrix tempMat(gc->transform);
557 thePath->LoadArtBPath(SP_CURVE_BPATH(shape->curve), tempMat, true);
558 }
560 // add some padding to the rendering area, so clipped path does not go into a render area
561 NRRectL padded_area = *area;
562 padded_area.x0 -= (NR::ICoord)width;
563 padded_area.x1 += (NR::ICoord)width;
564 padded_area.y0 -= (NR::ICoord)width;
565 padded_area.y1 += (NR::ICoord)width;
566 if ((style->stroke_dash.n_dash && !outline) || is_inner_area(padded_area, NR_ARENA_ITEM(shape)->bbox)) {
567 thePath->Convert((outline) ? 4.0 : 1.0);
568 shape->cached_spartialy = false;
569 }
570 else {
571 thePath->Convert(&padded_area, (outline) ? 4.0 : 1.0);
572 shape->cached_spartialy = true;
573 }
575 if (style->stroke_dash.n_dash && !outline) {
576 thePath->DashPolylineFromStyle(style, scale, 1.0);
577 }
579 ButtType butt=butt_straight;
580 switch (shape->_stroke.cap) {
581 case NRArenaShape::BUTT_CAP:
582 butt = butt_straight;
583 break;
584 case NRArenaShape::ROUND_CAP:
585 butt = butt_round;
586 break;
587 case NRArenaShape::SQUARE_CAP:
588 butt = butt_square;
589 break;
590 }
591 JoinType join=join_straight;
592 switch (shape->_stroke.join) {
593 case NRArenaShape::MITRE_JOIN:
594 join = join_pointy;
595 break;
596 case NRArenaShape::ROUND_JOIN:
597 join = join_round;
598 break;
599 case NRArenaShape::BEVEL_JOIN:
600 join = join_straight;
601 break;
602 }
604 if (outline) {
605 butt = butt_straight;
606 join = join_straight;
607 }
609 thePath->Stroke(theShape, false, 0.5*width, join, butt,
610 0.5*width*shape->_stroke.mitre_limit);
613 if (outline) {
614 // speeds it up, but uses evenodd for the stroke shape (which does not matter for 1-pixel wide outline)
615 shape->cached_stroke->Copy(theShape);
616 } else {
617 shape->cached_stroke->ConvertToShape(theShape, fill_nonZero);
618 }
620 shape->cached_width = width;
622 shape->cached_sctm=gc->transform;
623 shape->cached_sarea = *area;
624 delete thePath;
625 delete theShape;
626 if ( shape->stroke_shp == NULL ) shape->stroke_shp=new Shape;
628 shape->stroke_shp->Copy(shape->cached_stroke);
630 } else if ( 2 == isometry ) {
631 if ( shape->stroke_shp == NULL ) {
632 shape->stroke_shp=new Shape;
633 shape->stroke_shp->Copy(shape->cached_stroke);
634 }
635 } else {
636 if ( shape->stroke_shp == NULL )
637 shape->stroke_shp=new Shape;
638 shape->stroke_shp->Reset(shape->cached_stroke->numberOfPoints(), shape->cached_stroke->numberOfEdges());
639 for (int i = 0; i < shape->cached_stroke->numberOfPoints(); i++)
640 shape->stroke_shp->AddPoint(shape->cached_stroke->getPoint(i).x * cached_to_new);
641 if ( isometry == 1 ) {
642 for (int i = 0; i < shape->cached_stroke->numberOfEdges(); i++)
643 shape->stroke_shp->AddEdge(shape->cached_stroke->getEdge(i).st,
644 shape->cached_stroke->getEdge(i).en);
645 } else if ( isometry == -1 ) {
646 for (int i = 0; i < shape->cached_stroke->numberOfEdges(); i++)
647 shape->stroke_shp->AddEdge(shape->cached_stroke->getEdge(i).en,
648 shape->cached_stroke->getEdge(i).st);
649 }
650 shape->stroke_shp->ForceToPolygon();
651 shape->stroke_shp->needPointsSorting();
652 shape->stroke_shp->needEdgesSorting();
653 }
654 shape->delayed_shp |= shape->cached_spartialy;
655 }
656 }
659 void
660 nr_arena_shape_add_bboxes(NRArenaShape* shape, NRRect &bbox)
661 {
662 if ( shape->stroke_shp ) {
663 shape->stroke_shp->CalcBBox();
664 shape->stroke_shp->leftX=floor(shape->stroke_shp->leftX);
665 shape->stroke_shp->rightX=ceil(shape->stroke_shp->rightX);
666 shape->stroke_shp->topY=floor(shape->stroke_shp->topY);
667 shape->stroke_shp->bottomY=ceil(shape->stroke_shp->bottomY);
668 if ( bbox.x0 >= bbox.x1 ) {
669 if ( shape->stroke_shp->leftX < shape->stroke_shp->rightX ) {
670 bbox.x0=shape->stroke_shp->leftX;
671 bbox.x1=shape->stroke_shp->rightX;
672 }
673 } else {
674 if ( shape->stroke_shp->leftX < bbox.x0 )
675 bbox.x0=shape->stroke_shp->leftX;
676 if ( shape->stroke_shp->rightX > bbox.x1 )
677 bbox.x1=shape->stroke_shp->rightX;
678 }
679 if ( bbox.y0 >= bbox.y1 ) {
680 if ( shape->stroke_shp->topY < shape->stroke_shp->bottomY ) {
681 bbox.y0=shape->stroke_shp->topY;
682 bbox.y1=shape->stroke_shp->bottomY;
683 }
684 } else {
685 if ( shape->stroke_shp->topY < bbox.y0 )
686 bbox.y0=shape->stroke_shp->topY;
687 if ( shape->stroke_shp->bottomY > bbox.y1 )
688 bbox.y1=shape->stroke_shp->bottomY;
689 }
690 }
691 if ( shape->fill_shp ) {
692 shape->fill_shp->CalcBBox();
693 shape->fill_shp->leftX=floor(shape->fill_shp->leftX);
694 shape->fill_shp->rightX=ceil(shape->fill_shp->rightX);
695 shape->fill_shp->topY=floor(shape->fill_shp->topY);
696 shape->fill_shp->bottomY=ceil(shape->fill_shp->bottomY);
697 if ( bbox.x0 >= bbox.x1 ) {
698 if ( shape->fill_shp->leftX < shape->fill_shp->rightX ) {
699 bbox.x0=shape->fill_shp->leftX;
700 bbox.x1=shape->fill_shp->rightX;
701 }
702 } else {
703 if ( shape->fill_shp->leftX < bbox.x0 ) bbox.x0=shape->fill_shp->leftX;
704 if ( shape->fill_shp->rightX > bbox.x1 ) bbox.x1=shape->fill_shp->rightX;
705 }
706 if ( bbox.y0 >= bbox.y1 ) {
707 if ( shape->fill_shp->topY < shape->fill_shp->bottomY ) {
708 bbox.y0=shape->fill_shp->topY;
709 bbox.y1=shape->fill_shp->bottomY;
710 }
711 } else {
712 if ( shape->fill_shp->topY < bbox.y0 ) bbox.y0=shape->fill_shp->topY;
713 if ( shape->fill_shp->bottomY > bbox.y1 ) bbox.y1=shape->fill_shp->bottomY;
714 }
715 }
716 }
718 /**
719 * Renders the item. Markers are just composed into the parent buffer.
720 */
721 static unsigned int
722 nr_arena_shape_render(NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags)
723 {
724 NRArenaShape *shape = NR_ARENA_SHAPE(item);
726 if (!shape->curve) return item->state;
727 if (!shape->style) return item->state;
729 if ( shape->delayed_shp ) {
730 if ( nr_rect_l_test_intersect(area, &item->bbox) ) {
731 NRGC tempGC(NULL);
732 tempGC.transform=shape->ctm;
733 shape->delayed_shp = false;
734 nr_arena_shape_update_stroke(shape,&tempGC,&pb->visible_area);
735 nr_arena_shape_update_fill(shape,&tempGC,&pb->visible_area);
736 /* NRRect bbox;
737 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0.0;
738 nr_arena_shape_add_bboxes(shape,bbox);
739 item->bbox.x0 = (gint32)(bbox.x0 - 1.0F);
740 item->bbox.y0 = (gint32)(bbox.y0 - 1.0F);
741 item->bbox.x1 = (gint32)(bbox.x1 + 1.0F);
742 item->bbox.y1 = (gint32)(bbox.y1 + 1.0F);
743 shape->approx_bbox=item->bbox;*/
744 } else {
745 return item->state;
746 }
747 }
749 bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == RENDERMODE_OUTLINE);
751 SPStyle const *style = shape->style;
752 if ( shape->fill_shp && !outline) {
753 NRPixBlock m;
754 guint32 rgba;
756 nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE);
758 // if memory allocation failed, abort render
759 if (m.data.px == NULL) {
760 nr_pixblock_release (&m);
761 return (item->state);
762 }
764 m.visible_area = pb->visible_area;
765 nr_pixblock_render_shape_mask_or(m,shape->fill_shp);
766 m.empty = FALSE;
768 if (shape->_fill.paint.type() == NRArenaShape::Paint::NONE) {
769 // do not render fill in any way
770 } else if (shape->_fill.paint.type() == NRArenaShape::Paint::COLOR) {
771 if ( item->render_opacity ) {
772 rgba = sp_color_get_rgba32_falpha(&shape->_fill.paint.color(),
773 shape->_fill.opacity *
774 SP_SCALE24_TO_FLOAT(style->opacity.value));
775 } else {
776 rgba = sp_color_get_rgba32_falpha(&shape->_fill.paint.color(),
777 shape->_fill.opacity);
778 }
779 nr_blit_pixblock_mask_rgba32(pb, &m, rgba);
780 pb->empty = FALSE;
781 } else if (shape->_fill.paint.type() == NRArenaShape::Paint::SERVER) {
782 if (shape->fill_painter) {
783 nr_arena_render_paintserver_fill(pb, area, shape->fill_painter, shape->_fill.opacity, &m);
784 }
785 }
787 nr_pixblock_release(&m);
788 }
790 if ( shape->stroke_shp ) {
791 NRPixBlock m;
792 guint32 rgba;
794 nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE);
796 // if memory allocation failed, abort render
797 if (m.data.px == NULL) {
798 nr_pixblock_release (&m);
799 return (item->state);
800 }
802 m.visible_area = pb->visible_area;
803 nr_pixblock_render_shape_mask_or(m, shape->stroke_shp);
804 m.empty = FALSE;
806 if (shape->_stroke.paint.type() == NRArenaShape::Paint::COLOR || outline) {
807 if (outline) {
808 rgba = NR_ARENA_ITEM(shape)->arena->outlinecolor;
809 } else if ( item->render_opacity ) {
810 rgba = sp_color_get_rgba32_falpha(&shape->_stroke.paint.color(),
811 shape->_stroke.opacity *
812 SP_SCALE24_TO_FLOAT(style->opacity.value));
813 } else {
814 rgba = sp_color_get_rgba32_falpha(&shape->_stroke.paint.color(),
815 shape->_stroke.opacity);
816 }
817 nr_blit_pixblock_mask_rgba32(pb, &m, rgba);
818 pb->empty = FALSE;
819 } else if (shape->_stroke.paint.type() == NRArenaShape::Paint::SERVER) {
820 if (shape->stroke_painter) {
821 nr_arena_render_paintserver_fill(pb, area, shape->stroke_painter, shape->_stroke.opacity, &m);
822 }
823 }
825 nr_pixblock_release(&m);
826 }
828 /* Just compose children into parent buffer */
829 for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) {
830 unsigned int ret;
831 ret = nr_arena_item_invoke_render(child, area, pb, flags);
832 if (ret & NR_ARENA_ITEM_STATE_INVALID) return ret;
833 }
835 return item->state;
836 }
838 static guint
839 nr_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
840 {
841 NRArenaShape *shape = NR_ARENA_SHAPE(item);
842 if (!shape->curve) return item->state;
844 if ( shape->delayed_shp || shape->fill_shp == NULL) { // we need a fill shape no matter what
845 if ( nr_rect_l_test_intersect(area, &item->bbox) ) {
846 NRGC tempGC(NULL);
847 tempGC.transform=shape->ctm;
848 shape->delayed_shp = false;
849 nr_arena_shape_update_fill(shape, &tempGC, &pb->visible_area, true);
850 } else {
851 return item->state;
852 }
853 }
855 if ( shape->fill_shp ) {
856 NRPixBlock m;
858 /* fixme: We can OR in one step (Lauris) */
859 nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE);
861 // if memory allocation failed, abort
862 if (m.data.px == NULL) {
863 nr_pixblock_release (&m);
864 return (item->state);
865 }
867 m.visible_area = pb->visible_area;
868 nr_pixblock_render_shape_mask_or(m,shape->fill_shp);
870 for (int y = area->y0; y < area->y1; y++) {
871 unsigned char *s, *d;
872 s = NR_PIXBLOCK_PX(&m) + (y - area->y0) * m.rs;
873 d = NR_PIXBLOCK_PX(pb) + (y - area->y0) * pb->rs;
874 for (int x = area->x0; x < area->x1; x++) {
875 *d = NR_COMPOSEA_111(*s, *d);
876 d ++;
877 s ++;
878 }
879 }
880 nr_pixblock_release(&m);
881 pb->empty = FALSE;
882 }
884 return item->state;
885 }
887 static NRArenaItem *
888 nr_arena_shape_pick(NRArenaItem *item, NR::Point p, double delta, unsigned int /*sticky*/)
889 {
890 NRArenaShape *shape = NR_ARENA_SHAPE(item);
892 if (!shape->curve) return NULL;
893 if (!shape->style) return NULL;
894 if ( shape->delayed_shp ) {
895 NRRectL area, updateArea;
896 area.x0=(int)floor(p[NR::X]);
897 area.x1=(int)ceil(p[NR::X]);
898 area.y0=(int)floor(p[NR::Y]);
899 area.y1=(int)ceil(p[NR::Y]);
900 int idelta = (int)ceil(delta) + 1;
901 // njh: inset rect
902 area.x0-=idelta;
903 area.x1+=idelta;
904 area.y0-=idelta;
905 area.y1+=idelta;
906 if ( nr_rect_l_test_intersect(&area, &item->bbox) ) {
907 NRGC tempGC(NULL);
908 tempGC.transform=shape->ctm;
909 updateArea = item->bbox;
910 if (shape->cached_stroke)
911 nr_rect_l_intersect (&updateArea, &updateArea, &shape->cached_sarea);
913 shape->delayed_shp = false;
914 nr_arena_shape_update_stroke(shape, &tempGC, &updateArea);
915 nr_arena_shape_update_fill(shape, &tempGC, &updateArea);
916 /* NRRect bbox;
917 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0.0;
918 nr_arena_shape_add_bboxes(shape,bbox);
919 item->bbox.x0 = (gint32)(bbox.x0 - 1.0F);
920 item->bbox.y0 = (gint32)(bbox.y0 - 1.0F);
921 item->bbox.x1 = (gint32)(bbox.x1 + 1.0F);
922 item->bbox.y1 = (gint32)(bbox.y1 + 1.0F);
923 shape->approx_bbox=item->bbox;*/
924 }
925 }
927 bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == RENDERMODE_OUTLINE);
929 if (item->state & NR_ARENA_ITEM_STATE_RENDER) {
930 if (shape->fill_shp && (shape->_fill.paint.type() != NRArenaShape::Paint::NONE)) {
931 if (shape->fill_shp->PtWinding(p) > 0 ) return item;
932 }
933 if (shape->stroke_shp && (shape->_stroke.paint.type() != NRArenaShape::Paint::NONE || outline)) {
934 if (shape->stroke_shp->PtWinding(p) > 0 ) return item;
935 }
936 if (delta > 1e-3) {
937 if (shape->fill_shp && (shape->_fill.paint.type() != NRArenaShape::Paint::NONE)) {
938 if (distanceLessThanOrEqual(shape->fill_shp, p, delta)) return item;
939 }
940 if (shape->stroke_shp && (shape->_stroke.paint.type() != NRArenaShape::Paint::NONE || outline)) {
941 if (distanceLessThanOrEqual(shape->stroke_shp, p, delta)) return item;
942 }
943 }
944 } else {
945 NRBPath bp;
946 bp.path = SP_CURVE_BPATH(shape->curve);
947 double dist = NR_HUGE;
948 int wind = 0;
949 nr_path_matrix_point_bbox_wind_distance(&bp, shape->ctm, p, NULL, &wind, &dist, NR_EPSILON);
950 if (shape->_fill.paint.type() != NRArenaShape::Paint::NONE) {
951 if (!shape->style->fill_rule.computed) {
952 if (wind != 0) return item;
953 } else {
954 if (wind & 0x1) return item;
955 }
956 }
957 if (shape->_stroke.paint.type() != NRArenaShape::Paint::NONE || outline) {
958 /* fixme: We do not take stroke width into account here (Lauris) */
959 if (dist < delta) return item;
960 }
961 }
963 return NULL;
964 }
966 /**
967 *
968 * Requests a render of the shape, then if the shape is already a curve it
969 * unrefs the old curve; if the new curve is valid it creates a copy of the
970 * curve and adds it to the shape. Finally, it requests an update of the
971 * arena for the shape.
972 */
973 void nr_arena_shape_set_path(NRArenaShape *shape, SPCurve *curve,bool justTrans)
974 {
975 g_return_if_fail(shape != NULL);
976 g_return_if_fail(NR_IS_ARENA_SHAPE(shape));
978 if ( justTrans == false ) {
979 // dirty cached versions
980 if ( shape->cached_fill ) {
981 delete shape->cached_fill;
982 shape->cached_fill=NULL;
983 }
984 if ( shape->cached_stroke ) {
985 delete shape->cached_stroke;
986 shape->cached_stroke=NULL;
987 }
988 }
990 nr_arena_item_request_render(NR_ARENA_ITEM(shape));
992 if (shape->curve) {
993 sp_curve_unref(shape->curve);
994 shape->curve = NULL;
995 }
997 if (curve) {
998 shape->curve = curve;
999 sp_curve_ref(curve);
1000 }
1002 nr_arena_item_request_update(NR_ARENA_ITEM(shape), NR_ARENA_ITEM_STATE_ALL, FALSE);
1003 }
1005 void NRArenaShape::setFill(SPPaintServer *server) {
1006 _fill.paint.set(server);
1007 _invalidateCachedFill();
1008 }
1010 void NRArenaShape::setFill(SPColor const &color) {
1011 _fill.paint.set(color);
1012 _invalidateCachedFill();
1013 }
1015 void NRArenaShape::setFillOpacity(double opacity) {
1016 _fill.opacity = opacity;
1017 _invalidateCachedFill();
1018 }
1020 void NRArenaShape::setFillRule(NRArenaShape::FillRule rule) {
1021 _fill.rule = rule;
1022 _invalidateCachedFill();
1023 }
1025 void NRArenaShape::setStroke(SPPaintServer *server) {
1026 _stroke.paint.set(server);
1027 _invalidateCachedStroke();
1028 }
1030 void NRArenaShape::setStroke(SPColor const &color) {
1031 _stroke.paint.set(color);
1032 _invalidateCachedStroke();
1033 }
1035 void NRArenaShape::setStrokeOpacity(double opacity) {
1036 _stroke.opacity = opacity;
1037 _invalidateCachedStroke();
1038 }
1040 void NRArenaShape::setStrokeWidth(double width) {
1041 _stroke.width = width;
1042 _invalidateCachedStroke();
1043 }
1045 void NRArenaShape::setMitreLimit(double limit) {
1046 _stroke.mitre_limit = limit;
1047 _invalidateCachedStroke();
1048 }
1050 void NRArenaShape::setLineCap(NRArenaShape::CapType cap) {
1051 _stroke.cap = cap;
1052 _invalidateCachedStroke();
1053 }
1055 void NRArenaShape::setLineJoin(NRArenaShape::JoinType join) {
1056 _stroke.join = join;
1057 _invalidateCachedStroke();
1058 }
1060 /** nr_arena_shape_set_style
1061 *
1062 * Unrefs any existing style and ref's to the given one, then requests an update of the arena
1063 */
1064 void
1065 nr_arena_shape_set_style(NRArenaShape *shape, SPStyle *style)
1066 {
1067 g_return_if_fail(shape != NULL);
1068 g_return_if_fail(NR_IS_ARENA_SHAPE(shape));
1070 if (style) sp_style_ref(style);
1071 if (shape->style) sp_style_unref(shape->style);
1072 shape->style = style;
1074 switch (style->fill.type) {
1075 case SP_PAINT_TYPE_NONE: {
1076 shape->setFill(NULL);
1077 break;
1078 }
1079 case SP_PAINT_TYPE_COLOR: {
1080 shape->setFill(style->fill.value.color);
1081 break;
1082 }
1083 case SP_PAINT_TYPE_PAINTSERVER: {
1084 shape->setFill(style->fill.value.paint.server);
1085 break;
1086 }
1087 default: {
1088 g_assert_not_reached();
1089 }
1090 }
1091 shape->setFillOpacity(SP_SCALE24_TO_FLOAT(style->fill_opacity.value));
1092 switch (style->fill_rule.computed) {
1093 case SP_WIND_RULE_EVENODD: {
1094 shape->setFillRule(NRArenaShape::EVEN_ODD);
1095 break;
1096 }
1097 case SP_WIND_RULE_NONZERO: {
1098 shape->setFillRule(NRArenaShape::NONZERO);
1099 break;
1100 }
1101 default: {
1102 g_assert_not_reached();
1103 }
1104 }
1106 switch (style->stroke.type) {
1107 case SP_PAINT_TYPE_NONE: {
1108 shape->setStroke(NULL);
1109 break;
1110 }
1111 case SP_PAINT_TYPE_COLOR: {
1112 shape->setStroke(style->stroke.value.color);
1113 break;
1114 }
1115 case SP_PAINT_TYPE_PAINTSERVER: {
1116 shape->setStroke(style->stroke.value.paint.server);
1117 break;
1118 }
1119 default: {
1120 g_assert_not_reached();
1121 }
1122 }
1123 shape->setStrokeWidth(style->stroke_width.computed);
1124 shape->setStrokeOpacity(SP_SCALE24_TO_FLOAT(style->stroke_opacity.value));
1125 switch (style->stroke_linecap.computed) {
1126 case SP_STROKE_LINECAP_ROUND: {
1127 shape->setLineCap(NRArenaShape::ROUND_CAP);
1128 break;
1129 }
1130 case SP_STROKE_LINECAP_SQUARE: {
1131 shape->setLineCap(NRArenaShape::SQUARE_CAP);
1132 break;
1133 }
1134 case SP_STROKE_LINECAP_BUTT: {
1135 shape->setLineCap(NRArenaShape::BUTT_CAP);
1136 break;
1137 }
1138 default: {
1139 g_assert_not_reached();
1140 }
1141 }
1142 switch (style->stroke_linejoin.computed) {
1143 case SP_STROKE_LINEJOIN_ROUND: {
1144 shape->setLineJoin(NRArenaShape::ROUND_JOIN);
1145 break;
1146 }
1147 case SP_STROKE_LINEJOIN_BEVEL: {
1148 shape->setLineJoin(NRArenaShape::BEVEL_JOIN);
1149 break;
1150 }
1151 case SP_STROKE_LINEJOIN_MITER: {
1152 shape->setLineJoin(NRArenaShape::MITRE_JOIN);
1153 break;
1154 }
1155 default: {
1156 g_assert_not_reached();
1157 }
1158 }
1159 shape->setMitreLimit(style->stroke_miterlimit.value);
1161 //if shape has a filter
1162 if (style->filter.set && style->filter.filter)
1163 {
1164 shape->filter = new NR::Filter();
1165 shape->filter->set_x(style->filter.filter->x);
1166 shape->filter->set_y(style->filter.filter->y);
1167 shape->filter->set_width(style->filter.filter->width);
1168 shape->filter->set_height(style->filter.filter->height);
1170 //go through all SP filter primitives
1171 for(int i=0; i<style->filter.filter->_primitive_count; i++)
1172 {
1173 SPFilterPrimitive *primitive = style->filter.filter->_primitives[i];
1174 //if primitive is gaussianblur
1175 // if(SP_IS_GAUSSIANBLUR(primitive))
1176 {
1177 NR::FilterGaussian * gaussian = (NR::FilterGaussian *) shape->filter->add_primitive(NR::NR_FILTER_GAUSSIANBLUR);
1178 SPGaussianBlur * spblur = SP_GAUSSIANBLUR(primitive);
1179 float num = spblur->stdDeviation.getNumber();
1180 if( num>=0.0 )
1181 {
1182 float optnum = spblur->stdDeviation.getOptNumber();
1183 if( optnum>=0.0 )
1184 gaussian->set_deviation((double) num, (double) optnum);
1185 else
1186 gaussian->set_deviation((double) num);
1187 }
1188 }
1189 }
1190 }
1191 else
1192 {
1193 //no filter set for this shape
1194 shape->filter = NULL;
1195 }
1197 nr_arena_item_request_update(shape, NR_ARENA_ITEM_STATE_ALL, FALSE);
1198 }
1200 void
1201 nr_arena_shape_set_paintbox(NRArenaShape *shape, NRRect const *pbox)
1202 {
1203 g_return_if_fail(shape != NULL);
1204 g_return_if_fail(NR_IS_ARENA_SHAPE(shape));
1205 g_return_if_fail(pbox != NULL);
1207 if ((pbox->x0 < pbox->x1) && (pbox->y0 < pbox->y1)) {
1208 shape->paintbox = *pbox;
1209 } else {
1210 /* fixme: We kill warning, although not sure what to do here (Lauris) */
1211 shape->paintbox.x0 = shape->paintbox.y0 = 0.0F;
1212 shape->paintbox.x1 = shape->paintbox.y1 = 256.0F;
1213 }
1215 nr_arena_item_request_update(shape, NR_ARENA_ITEM_STATE_ALL, FALSE);
1216 }
1218 void NRArenaShape::setPaintBox(NR::Rect const &pbox)
1219 {
1220 paintbox.x0 = pbox.min()[NR::X];
1221 paintbox.y0 = pbox.min()[NR::Y];
1222 paintbox.x1 = pbox.max()[NR::X];
1223 paintbox.y1 = pbox.max()[NR::Y];
1225 nr_arena_item_request_update(this, NR_ARENA_ITEM_STATE_ALL, FALSE);
1226 }
1228 static void
1229 shape_run_A8_OR(raster_info &dest,void */*data*/,int st,float vst,int en,float ven)
1230 {
1231 if ( st >= en ) return;
1232 if ( vst < 0 ) vst=0;
1233 if ( vst > 1 ) vst=1;
1234 if ( ven < 0 ) ven=0;
1235 if ( ven > 1 ) ven=1;
1236 float sv=vst;
1237 float dv=ven-vst;
1238 int len=en-st;
1239 unsigned char* d=(unsigned char*)dest.buffer;
1240 d+=(st-dest.startPix);
1241 if ( fabs(dv) < 0.001 ) {
1242 if ( vst > 0.999 ) {
1243 /* Simple copy */
1244 while (len > 0) {
1245 d[0] = 255;
1246 d += 1;
1247 len -= 1;
1248 }
1249 } else {
1250 sv*=256;
1251 unsigned int c0_24=(int)sv;
1252 c0_24&=0xFF;
1253 while (len > 0) {
1254 /* Draw */
1255 d[0] = NR_COMPOSEA_111(c0_24,d[0]);
1256 d += 1;
1257 len -= 1;
1258 }
1259 }
1260 } else {
1261 if ( en <= st+1 ) {
1262 sv=0.5*(vst+ven);
1263 sv*=256;
1264 unsigned int c0_24=(int)sv;
1265 c0_24&=0xFF;
1266 /* Draw */
1267 d[0] = NR_COMPOSEA_111(c0_24,d[0]);
1268 } else {
1269 dv/=len;
1270 sv+=0.5*dv; // correction trapezoidale
1271 sv*=16777216;
1272 dv*=16777216;
1273 int c0_24 = static_cast<int>(CLAMP(sv, 0, 16777216));
1274 int s0_24 = static_cast<int>(dv);
1275 while (len > 0) {
1276 unsigned int ca;
1277 /* Draw */
1278 ca = c0_24 >> 16;
1279 if ( ca > 255 ) ca=255;
1280 d[0] = NR_COMPOSEA_111(ca,d[0]);
1281 d += 1;
1282 c0_24 += s0_24;
1283 c0_24 = CLAMP(c0_24, 0, 16777216);
1284 len -= 1;
1285 }
1286 }
1287 }
1288 }
1290 void nr_pixblock_render_shape_mask_or(NRPixBlock &m,Shape* theS)
1291 {
1292 theS->CalcBBox();
1293 float l = theS->leftX, r = theS->rightX, t = theS->topY, b = theS->bottomY;
1294 int il,ir,it,ib;
1295 il=(int)floor(l);
1296 ir=(int)ceil(r);
1297 it=(int)floor(t);
1298 ib=(int)ceil(b);
1300 if ( il >= m.area.x1 || ir <= m.area.x0 || it >= m.area.y1 || ib <= m.area.y0 ) return;
1301 if ( il < m.area.x0 ) il=m.area.x0;
1302 if ( it < m.area.y0 ) it=m.area.y0;
1303 if ( ir > m.area.x1 ) ir=m.area.x1;
1304 if ( ib > m.area.y1 ) ib=m.area.y1;
1306 /* This is the FloatLigne version. See svn (prior to Apr 2006) for versions using BitLigne or direct BitLigne. */
1307 int curPt;
1308 float curY;
1309 theS->BeginQuickRaster(curY, curPt);
1311 FloatLigne *theI = new FloatLigne();
1312 IntLigne *theIL = new IntLigne();
1314 theS->DirectQuickScan(curY, curPt, (float) it, true, 1.0);
1316 char *mdata = (char*)m.data.px;
1317 if ( m.size == NR_PIXBLOCK_SIZE_TINY ) mdata=(char*)m.data.p;
1318 uint32_t *ligStart = ((uint32_t*)(mdata + ((il - m.area.x0) + m.rs * (it - m.area.y0))));
1319 for (int y = it; y < ib; y++) {
1320 theI->Reset();
1321 theS->QuickScan(curY, curPt, ((float)(y+1)), theI, 1.0);
1322 theI->Flatten();
1323 theIL->Copy(theI);
1325 raster_info dest;
1326 dest.startPix=il;
1327 dest.endPix=ir;
1328 dest.sth=il;
1329 dest.stv=y;
1330 dest.buffer=ligStart;
1331 theIL->Raster(dest, NULL, shape_run_A8_OR);
1332 ligStart=((uint32_t*)(((char*)ligStart)+m.rs));
1333 }
1334 theS->EndQuickRaster();
1335 delete theI;
1336 delete theIL;
1337 }
1340 /*
1341 Local Variables:
1342 mode:c++
1343 c-file-style:"stroustrup"
1344 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1345 indent-tabs-mode:nil
1346 fill-column:99
1347 End:
1348 */
1349 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :