b4a6c82e5b8b4d37e7f21cb28aeeea153cacf7fc
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/canvas-arena.h>
18 #include <display/nr-arena.h>
19 #include <display/nr-arena-shape.h>
20 #include "display/curve.h"
21 #include <libnr/n-art-bpath.h>
22 #include <libnr/nr-path.h>
23 #include <libnr/nr-pixops.h>
24 #include <libnr/nr-matrix-ops.h>
25 #include <libnr/nr-matrix-fns.h>
26 #include <libnr/nr-blit.h>
27 #include <libnr/nr-convert2geom.h>
28 #include <2geom/pathvector.h>
29 #include <livarot/Path.h>
30 #include <livarot/float-line.h>
31 #include <livarot/int-line.h>
32 #include <style.h>
33 #include "prefs-utils.h"
34 #include "inkscape-cairo.h"
35 #include "helper/geom.h"
36 #include "sp-filter.h"
37 #include "sp-filter-reference.h"
38 #include "display/nr-filter.h"
40 #include <cairo.h>
42 //int showRuns=0;
43 void nr_pixblock_render_shape_mask_or(NRPixBlock &m,Shape* theS);
45 static void nr_arena_shape_class_init(NRArenaShapeClass *klass);
46 static void nr_arena_shape_init(NRArenaShape *shape);
47 static void nr_arena_shape_finalize(NRObject *object);
49 static NRArenaItem *nr_arena_shape_children(NRArenaItem *item);
50 static void nr_arena_shape_add_child(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref);
51 static void nr_arena_shape_remove_child(NRArenaItem *item, NRArenaItem *child);
52 static void nr_arena_shape_set_child_position(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref);
54 static guint nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset);
55 static unsigned int nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags);
56 static guint nr_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb);
57 static NRArenaItem *nr_arena_shape_pick(NRArenaItem *item, NR::Point p, double delta, unsigned int sticky);
59 static NRArenaItemClass *shape_parent_class;
61 NRType
62 nr_arena_shape_get_type(void)
63 {
64 static NRType type = 0;
65 if (!type) {
66 type = nr_object_register_type(NR_TYPE_ARENA_ITEM,
67 "NRArenaShape",
68 sizeof(NRArenaShapeClass),
69 sizeof(NRArenaShape),
70 (void (*)(NRObjectClass *)) nr_arena_shape_class_init,
71 (void (*)(NRObject *)) nr_arena_shape_init);
72 }
73 return type;
74 }
76 static void
77 nr_arena_shape_class_init(NRArenaShapeClass *klass)
78 {
79 NRObjectClass *object_class;
80 NRArenaItemClass *item_class;
82 object_class = (NRObjectClass *) klass;
83 item_class = (NRArenaItemClass *) klass;
85 shape_parent_class = (NRArenaItemClass *) ((NRObjectClass *) klass)->parent;
87 object_class->finalize = nr_arena_shape_finalize;
88 object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaShape>;
90 item_class->children = nr_arena_shape_children;
91 item_class->add_child = nr_arena_shape_add_child;
92 item_class->set_child_position = nr_arena_shape_set_child_position;
93 item_class->remove_child = nr_arena_shape_remove_child;
94 item_class->update = nr_arena_shape_update;
95 item_class->render = nr_arena_shape_render;
96 item_class->clip = nr_arena_shape_clip;
97 item_class->pick = nr_arena_shape_pick;
98 }
100 /**
101 * Initializes the arena shape, setting all parameters to null, 0, false,
102 * or other defaults
103 */
104 static void
105 nr_arena_shape_init(NRArenaShape *shape)
106 {
107 shape->curve = NULL;
108 shape->style = NULL;
109 shape->paintbox.x0 = shape->paintbox.y0 = 0.0F;
110 shape->paintbox.x1 = shape->paintbox.y1 = 256.0F;
112 shape->ctm.set_identity();
113 shape->fill_painter = NULL;
114 shape->stroke_painter = NULL;
115 shape->cached_fill = NULL;
116 shape->cached_stroke = NULL;
117 shape->cached_fpartialy = false;
118 shape->cached_spartialy = false;
119 shape->fill_shp = NULL;
120 shape->stroke_shp = NULL;
122 shape->delayed_shp = false;
124 shape->approx_bbox.x0 = shape->approx_bbox.y0 = 0;
125 shape->approx_bbox.x1 = shape->approx_bbox.y1 = 0;
126 shape->cached_fctm.set_identity();
127 shape->cached_sctm.set_identity();
129 shape->markers = NULL;
131 shape->last_pick = NULL;
132 shape->repick_after = 0;
133 }
135 static void
136 nr_arena_shape_finalize(NRObject *object)
137 {
138 NRArenaShape *shape = (NRArenaShape *) object;
140 if (shape->fill_shp) delete shape->fill_shp;
141 if (shape->stroke_shp) delete shape->stroke_shp;
142 if (shape->cached_fill) delete shape->cached_fill;
143 if (shape->cached_stroke) delete shape->cached_stroke;
144 if (shape->fill_painter) sp_painter_free(shape->fill_painter);
145 if (shape->stroke_painter) sp_painter_free(shape->stroke_painter);
147 if (shape->style) sp_style_unref(shape->style);
148 if (shape->curve) shape->curve->unref();
150 ((NRObjectClass *) shape_parent_class)->finalize(object);
151 }
153 /**
154 * Retrieves the markers from the item
155 */
156 static NRArenaItem *
157 nr_arena_shape_children(NRArenaItem *item)
158 {
159 NRArenaShape *shape = (NRArenaShape *) item;
161 return shape->markers;
162 }
164 /**
165 * Attaches child to item, and if ref is not NULL, sets it and ref->next as
166 * the prev and next items. If ref is NULL, then it sets the item's markers
167 * as the next items.
168 */
169 static void
170 nr_arena_shape_add_child(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
171 {
172 NRArenaShape *shape = (NRArenaShape *) item;
174 if (!ref) {
175 shape->markers = nr_arena_item_attach(item, child, NULL, shape->markers);
176 } else {
177 ref->next = nr_arena_item_attach(item, child, ref, ref->next);
178 }
180 nr_arena_item_request_update(item, NR_ARENA_ITEM_STATE_ALL, FALSE);
181 }
183 /**
184 * Removes child from the shape. If there are no prev items in
185 * the child, it sets items' markers to the next item in the child.
186 */
187 static void
188 nr_arena_shape_remove_child(NRArenaItem *item, NRArenaItem *child)
189 {
190 NRArenaShape *shape = (NRArenaShape *) item;
192 if (child->prev) {
193 nr_arena_item_detach(item, child);
194 } else {
195 shape->markers = nr_arena_item_detach(item, child);
196 }
198 nr_arena_item_request_update(item, NR_ARENA_ITEM_STATE_ALL, FALSE);
199 }
201 /**
202 * Detaches child from item, and if there are no previous items in child, it
203 * sets item's markers to the child. It then attaches the child back onto the item.
204 * If ref is null, it sets the markers to be the next item, otherwise it uses
205 * the next/prev items in ref.
206 */
207 static void
208 nr_arena_shape_set_child_position(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
209 {
210 NRArenaShape *shape = (NRArenaShape *) item;
212 if (child->prev) {
213 nr_arena_item_detach(item, child);
214 } else {
215 shape->markers = nr_arena_item_detach(item, child);
216 }
218 if (!ref) {
219 shape->markers = nr_arena_item_attach(item, child, NULL, shape->markers);
220 } else {
221 ref->next = nr_arena_item_attach(item, child, ref, ref->next);
222 }
224 nr_arena_item_request_render(child);
225 }
227 void nr_arena_shape_update_stroke(NRArenaShape *shape, NRGC* gc, NRRectL *area);
228 void nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, NRRectL *area, bool force_shape = false);
229 void nr_arena_shape_add_bboxes(NRArenaShape* shape, Geom::Rect &bbox);
231 /**
232 * Updates the arena shape 'item' and all of its children, including the markers.
233 */
234 static guint
235 nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset)
236 {
237 Geom::Rect boundingbox;
239 NRArenaShape *shape = NR_ARENA_SHAPE(item);
241 unsigned int beststate = NR_ARENA_ITEM_STATE_ALL;
243 unsigned int newstate;
244 for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) {
245 newstate = nr_arena_item_invoke_update(child, area, gc, state, reset);
246 beststate = beststate & newstate;
247 }
249 if (!(state & NR_ARENA_ITEM_STATE_RENDER)) {
250 /* We do not have to create rendering structures */
251 shape->ctm = gc->transform;
252 if (state & NR_ARENA_ITEM_STATE_BBOX) {
253 if (shape->curve) {
254 // note: the original code before 2geom used to calculate the exact bounding box, for speed we take the approx bbox here
255 boundingbox = bounds_fast_transformed(shape->curve->get_pathvector(), to_2geom(gc->transform));
256 item->bbox.x0 = (gint32)(boundingbox[0][0] - 1.0F);
257 item->bbox.y0 = (gint32)(boundingbox[1][0] - 1.0F);
258 item->bbox.x1 = (gint32)(boundingbox[0][1] + 1.9999F);
259 item->bbox.y1 = (gint32)(boundingbox[1][1] + 1.9999F);
260 }
261 if (beststate & NR_ARENA_ITEM_STATE_BBOX) {
262 for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) {
263 nr_rect_l_union(&item->bbox, &item->bbox, &child->bbox);
264 }
265 }
266 }
267 return (state | item->state);
268 }
270 shape->delayed_shp=true;
271 shape->ctm = gc->transform;
272 boundingbox[0][0] = boundingbox[1][0] = NR_HUGE;
273 boundingbox[0][1] = boundingbox[1][1] = -NR_HUGE;
275 bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE);
277 if (shape->curve) {
278 // note: the original code before 2geom used to calculate the exact bounding box, for speed we take the approx bbox here
279 boundingbox = bounds_fast_transformed(shape->curve->get_pathvector(), to_2geom(gc->transform));
281 if (shape->_stroke.paint.type() != NRArenaShape::Paint::NONE || outline) {
282 float width, scale;
283 scale = NR::expansion(gc->transform);
284 width = MAX(0.125, shape->_stroke.width * scale);
285 if ( fabs(shape->_stroke.width * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
286 boundingbox.expandBy(width);
287 }
288 // those pesky miters, now
289 float miterMax=width*shape->_stroke.mitre_limit;
290 if ( miterMax > 0.01 ) {
291 // grunt mode. we should compute the various miters instead (one for each point on the curve)
292 boundingbox.expandBy(miterMax);
293 }
294 }
295 } else {
296 }
297 shape->approx_bbox.x0 = (gint32)(boundingbox[0][0] - 1.0F);
298 shape->approx_bbox.y0 = (gint32)(boundingbox[1][0] - 1.0F);
299 shape->approx_bbox.x1 = (gint32)(boundingbox[0][1] + 1.9999F);
300 shape->approx_bbox.y1 = (gint32)(boundingbox[1][1] + 1.9999F);
301 if ( area && nr_rect_l_test_intersect(area, &shape->approx_bbox) ) shape->delayed_shp=false;
303 /* Release state data */
304 if (TRUE || !NR::transform_equalp(gc->transform, shape->ctm, NR_EPSILON)) {
305 /* Concept test */
306 if (shape->fill_shp) {
307 delete shape->fill_shp;
308 shape->fill_shp = NULL;
309 }
310 }
311 if (shape->stroke_shp) {
312 delete shape->stroke_shp;
313 shape->stroke_shp = NULL;
314 }
315 if (shape->fill_painter) {
316 sp_painter_free(shape->fill_painter);
317 shape->fill_painter = NULL;
318 }
319 if (shape->stroke_painter) {
320 sp_painter_free(shape->stroke_painter);
321 shape->stroke_painter = NULL;
322 }
324 if (!shape->curve ||
325 !shape->style ||
326 shape->curve->is_empty() ||
327 (( shape->_fill.paint.type() == NRArenaShape::Paint::NONE ) &&
328 ( shape->_stroke.paint.type() == NRArenaShape::Paint::NONE && !outline) ))
329 {
330 item->bbox = shape->approx_bbox;
331 return NR_ARENA_ITEM_STATE_ALL;
332 }
334 /* Build state data */
335 if ( shape->delayed_shp ) {
336 item->bbox=shape->approx_bbox;
337 } else {
338 nr_arena_shape_update_stroke(shape, gc, area);
339 nr_arena_shape_update_fill(shape, gc, area);
341 boundingbox[0][0] = boundingbox[0][1] = boundingbox[1][0] = boundingbox[1][1] = 0.0;
342 nr_arena_shape_add_bboxes(shape, boundingbox);
344 shape->approx_bbox.x0 = (gint32)(boundingbox[0][0] - 1.0F);
345 shape->approx_bbox.y0 = (gint32)(boundingbox[1][0] - 1.0F);
346 shape->approx_bbox.x1 = (gint32)(boundingbox[0][1] + 1.9999F);
347 shape->approx_bbox.y1 = (gint32)(boundingbox[1][1] + 1.9999F);
348 }
350 if (boundingbox.isEmpty())
351 return NR_ARENA_ITEM_STATE_ALL;
353 item->bbox.x0 = (gint32)(boundingbox[0][0] - 1.0F);
354 item->bbox.y0 = (gint32)(boundingbox[1][0] - 1.0F);
355 item->bbox.x1 = (gint32)(boundingbox[0][1] + 1.0F);
356 item->bbox.y1 = (gint32)(boundingbox[1][1] + 1.0F);
357 nr_arena_request_render_rect(item->arena, &item->bbox);
359 item->render_opacity = TRUE;
360 if ( shape->_fill.paint.type() == NRArenaShape::Paint::SERVER ) {
361 if (gc && gc->parent) {
362 shape->fill_painter = sp_paint_server_painter_new(shape->_fill.paint.server(),
363 gc->transform, gc->parent->transform,
364 &shape->paintbox);
365 }
366 item->render_opacity = FALSE;
367 }
368 if ( shape->_stroke.paint.type() == NRArenaShape::Paint::SERVER ) {
369 if (gc && gc->parent) {
370 shape->stroke_painter = sp_paint_server_painter_new(shape->_stroke.paint.server(),
371 gc->transform, gc->parent->transform,
372 &shape->paintbox);
373 }
374 item->render_opacity = FALSE;
375 }
376 if ( (shape->_fill.paint.type() != NRArenaShape::Paint::NONE &&
377 shape->_stroke.paint.type() != NRArenaShape::Paint::NONE)
378 || (shape->markers)
379 )
380 {
381 // don't merge item opacity with paint opacity if there is a stroke on the fill, or markers on stroke
382 item->render_opacity = FALSE;
383 }
385 if (beststate & NR_ARENA_ITEM_STATE_BBOX) {
386 for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) {
387 nr_rect_l_union(&item->bbox, &item->bbox, &child->bbox);
388 }
389 }
391 return NR_ARENA_ITEM_STATE_ALL;
392 }
394 int matrix_is_isometry(NR::Matrix p) {
395 NR::Matrix tp;
396 // transposition
397 tp[0]=p[0];
398 tp[1]=p[2];
399 tp[2]=p[1];
400 tp[3]=p[3];
401 for (int i = 4; i < 6; i++) // shut valgrind up :)
402 tp[i] = p[i] = 0;
403 NR::Matrix isom = tp*p; // A^T * A = adjunct?
404 // Is the adjunct nearly an identity function?
405 if (isom.is_translation(0.01)) {
406 // the transformation is an isometry -> no need to recompute
407 // the uncrossed polygon
408 if ( p.det() < 0 )
409 return -1;
410 else
411 return 1;
412 }
413 return 0;
414 }
416 static bool is_inner_area(NRRectL const &outer, NRRectL const &inner) {
417 return (outer.x0 <= inner.x0 && outer.y0 <= inner.y0 && outer.x1 >= inner.x1 && outer.y1 >= inner.y1);
418 }
420 /** force_shape is used for clipping paths, when we need the shape for clipping even if it's not filled */
421 void
422 nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, NRRectL *area, bool force_shape)
423 {
424 if ((shape->_fill.paint.type() != NRArenaShape::Paint::NONE || force_shape) &&
425 ((shape->curve->get_length() > 2) || (SP_CURVE_BPATH(shape->curve)[1].code == NR_CURVETO)) ) {
426 if (TRUE || !shape->fill_shp) {
427 NR::Matrix cached_to_new = NR::identity();
428 int isometry = 0;
429 if ( shape->cached_fill ) {
430 if (shape->cached_fctm == gc->transform) {
431 isometry = 2; // identity
432 } else {
433 cached_to_new = shape->cached_fctm.inverse() * gc->transform;
434 isometry = matrix_is_isometry(cached_to_new);
435 }
436 if (0 != isometry && !is_inner_area(shape->cached_farea, *area))
437 isometry = 0;
438 }
439 if ( isometry == 0 ) {
440 if ( shape->cached_fill == NULL ) shape->cached_fill=new Shape;
441 shape->cached_fill->Reset();
443 Path* thePath=new Path;
444 Shape* theShape=new Shape;
445 {
446 NR::Matrix tempMat(gc->transform);
447 thePath->LoadArtBPath(SP_CURVE_BPATH(shape->curve),tempMat,true);
448 }
450 if (is_inner_area(*area, NR_ARENA_ITEM(shape)->bbox)) {
451 thePath->Convert(1.0);
452 shape->cached_fpartialy = false;
453 } else {
454 thePath->Convert(area, 1.0);
455 shape->cached_fpartialy = true;
456 }
458 thePath->Fill(theShape, 0);
460 if ( shape->_fill.rule == NRArenaShape::EVEN_ODD ) {
461 shape->cached_fill->ConvertToShape(theShape, fill_oddEven);
462 // alternatively, this speeds up rendering of oddeven shapes but disables AA :(
463 //shape->cached_fill->Copy(theShape);
464 } else {
465 shape->cached_fill->ConvertToShape(theShape, fill_nonZero);
466 }
467 shape->cached_fctm=gc->transform;
468 shape->cached_farea = *area;
469 delete theShape;
470 delete thePath;
471 if ( shape->fill_shp == NULL )
472 shape->fill_shp = new Shape;
474 shape->fill_shp->Copy(shape->cached_fill);
476 } else if ( 2 == isometry ) {
477 if ( shape->fill_shp == NULL ) {
478 shape->fill_shp = new Shape;
479 shape->fill_shp->Copy(shape->cached_fill);
480 }
481 } else {
483 if ( shape->fill_shp == NULL )
484 shape->fill_shp = new Shape;
486 shape->fill_shp->Reset(shape->cached_fill->numberOfPoints(),
487 shape->cached_fill->numberOfEdges());
488 for (int i = 0; i < shape->cached_fill->numberOfPoints(); i++)
489 shape->fill_shp->AddPoint(shape->cached_fill->getPoint(i).x * cached_to_new);
490 if ( isometry == 1 ) {
491 for (int i = 0; i < shape->cached_fill->numberOfEdges(); i++)
492 shape->fill_shp->AddEdge(shape->cached_fill->getEdge(i).st,
493 shape->cached_fill->getEdge(i).en);
494 } else if ( isometry == -1 ) { // need to flip poly.
495 for (int i = 0; i < shape->cached_fill->numberOfEdges(); i++)
496 shape->fill_shp->AddEdge(shape->cached_fill->getEdge(i).en,
497 shape->cached_fill->getEdge(i).st);
498 }
499 shape->fill_shp->ForceToPolygon();
500 shape->fill_shp->needPointsSorting();
501 shape->fill_shp->needEdgesSorting();
502 }
503 shape->delayed_shp |= shape->cached_fpartialy;
504 }
505 }
506 }
508 void
509 nr_arena_shape_update_stroke(NRArenaShape *shape,NRGC* gc, NRRectL *area)
510 {
511 SPStyle* style = shape->style;
513 float const scale = NR::expansion(gc->transform);
515 bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE);
517 if (outline) {
518 // cairo does not need the livarot path for rendering
519 return;
520 }
522 // after switching normal stroke rendering to cairo too, optimize this: lower tolerance, disregard dashes
523 // (since it will only be used for picking, not for rendering)
525 if (outline ||
526 ((shape->_stroke.paint.type() != NRArenaShape::Paint::NONE) &&
527 ( fabs(shape->_stroke.width * scale) > 0.01 ))) { // sinon c'est 0=oon veut pas de bord
529 float style_width = MAX(0.125, shape->_stroke.width * scale);
530 float width;
531 if (outline) {
532 width = 0.5; // 1 pixel wide, independent of zoom
533 } else {
534 width = style_width;
535 }
537 NR::Matrix cached_to_new = NR::identity();
539 int isometry = 0;
540 if ( shape->cached_stroke ) {
541 if (shape->cached_sctm == gc->transform) {
542 isometry = 2; // identity
543 } else {
544 cached_to_new = shape->cached_sctm.inverse() * gc->transform;
545 isometry = matrix_is_isometry(cached_to_new);
546 }
547 if (0 != isometry && !is_inner_area(shape->cached_sarea, *area))
548 isometry = 0;
549 if (0 != isometry && width != shape->cached_width) {
550 // if this happens without setting style, we have just switched to outline or back
551 isometry = 0;
552 }
553 }
555 if ( isometry == 0 ) {
556 if ( shape->cached_stroke == NULL ) shape->cached_stroke=new Shape;
557 shape->cached_stroke->Reset();
558 Path* thePath = new Path;
559 Shape* theShape = new Shape;
560 {
561 NR::Matrix tempMat(gc->transform);
562 thePath->LoadArtBPath(SP_CURVE_BPATH(shape->curve), tempMat, true);
563 }
565 // add some padding to the rendering area, so clipped path does not go into a render area
566 NRRectL padded_area = *area;
567 padded_area.x0 -= (NR::ICoord)width;
568 padded_area.x1 += (NR::ICoord)width;
569 padded_area.y0 -= (NR::ICoord)width;
570 padded_area.y1 += (NR::ICoord)width;
571 if ((style->stroke_dash.n_dash && !outline) || is_inner_area(padded_area, NR_ARENA_ITEM(shape)->bbox)) {
572 thePath->Convert((outline) ? 4.0 : 1.0);
573 shape->cached_spartialy = false;
574 }
575 else {
576 thePath->Convert(&padded_area, (outline) ? 4.0 : 1.0);
577 shape->cached_spartialy = true;
578 }
580 if (style->stroke_dash.n_dash && !outline) {
581 thePath->DashPolylineFromStyle(style, scale, 1.0);
582 }
584 ButtType butt=butt_straight;
585 switch (shape->_stroke.cap) {
586 case NRArenaShape::BUTT_CAP:
587 butt = butt_straight;
588 break;
589 case NRArenaShape::ROUND_CAP:
590 butt = butt_round;
591 break;
592 case NRArenaShape::SQUARE_CAP:
593 butt = butt_square;
594 break;
595 }
596 JoinType join=join_straight;
597 switch (shape->_stroke.join) {
598 case NRArenaShape::MITRE_JOIN:
599 join = join_pointy;
600 break;
601 case NRArenaShape::ROUND_JOIN:
602 join = join_round;
603 break;
604 case NRArenaShape::BEVEL_JOIN:
605 join = join_straight;
606 break;
607 }
609 if (outline) {
610 butt = butt_straight;
611 join = join_straight;
612 }
614 thePath->Stroke(theShape, false, 0.5*width, join, butt,
615 0.5*width*shape->_stroke.mitre_limit);
618 if (outline) {
619 // speeds it up, but uses evenodd for the stroke shape (which does not matter for 1-pixel wide outline)
620 shape->cached_stroke->Copy(theShape);
621 } else {
622 shape->cached_stroke->ConvertToShape(theShape, fill_nonZero);
623 }
625 shape->cached_width = width;
627 shape->cached_sctm=gc->transform;
628 shape->cached_sarea = *area;
629 delete thePath;
630 delete theShape;
631 if ( shape->stroke_shp == NULL ) shape->stroke_shp=new Shape;
633 shape->stroke_shp->Copy(shape->cached_stroke);
635 } else if ( 2 == isometry ) {
636 if ( shape->stroke_shp == NULL ) {
637 shape->stroke_shp=new Shape;
638 shape->stroke_shp->Copy(shape->cached_stroke);
639 }
640 } else {
641 if ( shape->stroke_shp == NULL )
642 shape->stroke_shp=new Shape;
643 shape->stroke_shp->Reset(shape->cached_stroke->numberOfPoints(), shape->cached_stroke->numberOfEdges());
644 for (int i = 0; i < shape->cached_stroke->numberOfPoints(); i++)
645 shape->stroke_shp->AddPoint(shape->cached_stroke->getPoint(i).x * cached_to_new);
646 if ( isometry == 1 ) {
647 for (int i = 0; i < shape->cached_stroke->numberOfEdges(); i++)
648 shape->stroke_shp->AddEdge(shape->cached_stroke->getEdge(i).st,
649 shape->cached_stroke->getEdge(i).en);
650 } else if ( isometry == -1 ) {
651 for (int i = 0; i < shape->cached_stroke->numberOfEdges(); i++)
652 shape->stroke_shp->AddEdge(shape->cached_stroke->getEdge(i).en,
653 shape->cached_stroke->getEdge(i).st);
654 }
655 shape->stroke_shp->ForceToPolygon();
656 shape->stroke_shp->needPointsSorting();
657 shape->stroke_shp->needEdgesSorting();
658 }
659 shape->delayed_shp |= shape->cached_spartialy;
660 }
661 }
664 void
665 nr_arena_shape_add_bboxes(NRArenaShape* shape, Geom::Rect &bbox)
666 {
667 /* TODO: are these two if's mutually exclusive? ( i.e. "shape->stroke_shp <=> !shape->fill_shp" )
668 * if so, then this can be written much more compact ! */
670 if ( shape->stroke_shp ) {
671 Shape *larger = shape->stroke_shp;
672 larger->CalcBBox();
673 larger->leftX = floor(larger->leftX);
674 larger->rightX = ceil(larger->rightX);
675 larger->topY = floor(larger->topY);
676 larger->bottomY = ceil(larger->bottomY);
677 Geom::Rect stroke_bbox( Geom::Interval(larger->leftX, larger->rightX),
678 Geom::Interval(larger->topY, larger->bottomY) );
679 bbox.unionWith(stroke_bbox);
680 }
682 if ( shape->fill_shp ) {
683 Shape *larger = shape->fill_shp;
684 larger->CalcBBox();
685 larger->leftX = floor(larger->leftX);
686 larger->rightX = ceil(larger->rightX);
687 larger->topY = floor(larger->topY);
688 larger->bottomY = ceil(larger->bottomY);
689 Geom::Rect fill_bbox( Geom::Interval(larger->leftX, larger->rightX),
690 Geom::Interval(larger->topY, larger->bottomY) );
691 bbox.unionWith(fill_bbox);
692 }
693 }
695 // cairo outline rendering:
696 static unsigned int
697 cairo_arena_shape_render_outline(cairo_t *ct, NRArenaItem *item, NR::Maybe<NR::Rect> area)
698 {
699 NRArenaShape *shape = NR_ARENA_SHAPE(item);
701 if (!ct)
702 return item->state;
704 guint32 rgba = NR_ARENA_ITEM(shape)->arena->outlinecolor;
705 // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat
706 // by setting color channels in the "wrong" order
707 cairo_set_source_rgba(ct, SP_RGBA32_B_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_R_F(rgba), SP_RGBA32_A_F(rgba));
709 cairo_set_line_width(ct, 0.5);
710 cairo_set_tolerance(ct, 1.25); // low quality, but good enough for outline mode
711 cairo_new_path(ct);
713 feed_pathvector_to_cairo (ct, shape->curve->get_pathvector(), to_2geom(shape->ctm), area, true, 0);
715 cairo_stroke(ct);
717 return item->state;
718 }
720 // cairo stroke rendering (flat color only so far!):
721 // works on canvas, but wrongs the colors in nonpremul buffers: icons and png export
722 // (need to switch them to premul before this can be enabled)
723 void
724 cairo_arena_shape_render_stroke(NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
725 {
726 NRArenaShape *shape = NR_ARENA_SHAPE(item);
727 SPStyle const *style = shape->style;
729 float const scale = NR::expansion(shape->ctm);
731 if (fabs(shape->_stroke.width * scale) < 0.01)
732 return;
734 cairo_t *ct = nr_create_cairo_context (area, pb);
736 if (!ct)
737 return;
739 guint32 rgba;
740 if ( item->render_opacity ) {
741 rgba = shape->_stroke.paint.color().toRGBA32( shape->_stroke.opacity *
742 SP_SCALE24_TO_FLOAT(style->opacity.value) );
743 } else {
744 rgba = shape->_stroke.paint.color().toRGBA32( shape->_stroke.opacity );
745 }
747 // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat
748 // by setting color channels in the "wrong" order
749 cairo_set_source_rgba(ct, SP_RGBA32_B_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_R_F(rgba), SP_RGBA32_A_F(rgba));
751 float style_width = MAX(0.125, shape->_stroke.width * scale);
752 cairo_set_line_width(ct, style_width);
754 switch (shape->_stroke.cap) {
755 case NRArenaShape::BUTT_CAP:
756 cairo_set_line_cap(ct, CAIRO_LINE_CAP_BUTT);
757 break;
758 case NRArenaShape::ROUND_CAP:
759 cairo_set_line_cap(ct, CAIRO_LINE_CAP_ROUND);
760 break;
761 case NRArenaShape::SQUARE_CAP:
762 cairo_set_line_cap(ct, CAIRO_LINE_CAP_SQUARE);
763 break;
764 }
765 switch (shape->_stroke.join) {
766 case NRArenaShape::MITRE_JOIN:
767 cairo_set_line_join(ct, CAIRO_LINE_JOIN_MITER);
768 break;
769 case NRArenaShape::ROUND_JOIN:
770 cairo_set_line_join(ct, CAIRO_LINE_JOIN_ROUND);
771 break;
772 case NRArenaShape::BEVEL_JOIN:
773 cairo_set_line_join(ct, CAIRO_LINE_JOIN_BEVEL);
774 break;
775 }
777 cairo_set_miter_limit (ct, style->stroke_miterlimit.value);
779 if (style->stroke_dash.n_dash) {
780 NRVpathDash dash;
781 dash.offset = style->stroke_dash.offset * scale;
782 dash.n_dash = style->stroke_dash.n_dash;
783 dash.dash = g_new(double, dash.n_dash);
784 for (int i = 0; i < dash.n_dash; i++) {
785 dash.dash[i] = style->stroke_dash.dash[i] * scale;
786 }
787 cairo_set_dash (ct, dash.dash, dash.n_dash, dash.offset);
788 g_free(dash.dash);
789 }
791 cairo_set_tolerance(ct, 0.1);
792 cairo_new_path(ct);
794 feed_pathvector_to_cairo (ct, shape->curve->get_pathvector(), to_2geom(shape->ctm), area->upgrade(), true, style_width);
796 cairo_stroke(ct);
798 cairo_surface_t *cst = cairo_get_target(ct);
799 cairo_destroy (ct);
800 cairo_surface_finish (cst);
801 cairo_surface_destroy (cst);
803 pb->empty = FALSE;
804 }
807 /**
808 * Renders the item. Markers are just composed into the parent buffer.
809 */
810 static unsigned int
811 nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags)
812 {
813 NRArenaShape *shape = NR_ARENA_SHAPE(item);
815 if (!shape->curve) return item->state;
816 if (!shape->style) return item->state;
818 bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE);
820 if (outline) { // cairo outline rendering
822 pb->empty = FALSE;
823 unsigned int ret = cairo_arena_shape_render_outline (ct, item, (&pb->area)->upgrade());
824 if (ret & NR_ARENA_ITEM_STATE_INVALID) return ret;
826 } else {
828 if ( shape->delayed_shp ) {
829 if ( nr_rect_l_test_intersect(area, &item->bbox) ) {
830 NRGC tempGC(NULL);
831 tempGC.transform=shape->ctm;
832 shape->delayed_shp = false;
833 nr_arena_shape_update_stroke(shape,&tempGC,&pb->visible_area);
834 nr_arena_shape_update_fill(shape,&tempGC,&pb->visible_area);
835 /* NRRect bbox;
836 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0.0;
837 nr_arena_shape_add_bboxes(shape,bbox);
838 item->bbox.x0 = (gint32)(bbox.x0 - 1.0F);
839 item->bbox.y0 = (gint32)(bbox.y0 - 1.0F);
840 item->bbox.x1 = (gint32)(bbox.x1 + 1.0F);
841 item->bbox.y1 = (gint32)(bbox.y1 + 1.0F);
842 shape->approx_bbox=item->bbox;*/
843 } else {
844 return item->state;
845 }
846 }
848 SPStyle const *style = shape->style;
849 if (shape->fill_shp) {
850 NRPixBlock m;
851 guint32 rgba;
853 nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE);
855 // if memory allocation failed, abort render
856 if (m.size != NR_PIXBLOCK_SIZE_TINY && m.data.px == NULL) {
857 nr_pixblock_release (&m);
858 return (item->state);
859 }
861 m.visible_area = pb->visible_area;
862 nr_pixblock_render_shape_mask_or(m,shape->fill_shp);
863 m.empty = FALSE;
865 if (shape->_fill.paint.type() == NRArenaShape::Paint::NONE) {
866 // do not render fill in any way
867 } else if (shape->_fill.paint.type() == NRArenaShape::Paint::COLOR) {
868 if ( item->render_opacity ) {
869 rgba = shape->_fill.paint.color().toRGBA32( shape->_fill.opacity *
870 SP_SCALE24_TO_FLOAT(style->opacity.value) );
871 } else {
872 rgba = shape->_fill.paint.color().toRGBA32( shape->_fill.opacity );
873 }
874 nr_blit_pixblock_mask_rgba32(pb, &m, rgba);
875 pb->empty = FALSE;
876 } else if (shape->_fill.paint.type() == NRArenaShape::Paint::SERVER) {
877 if (shape->fill_painter) {
878 nr_arena_render_paintserver_fill(pb, area, shape->fill_painter, shape->_fill.opacity, &m);
879 }
880 }
882 nr_pixblock_release(&m);
883 }
885 if (shape->stroke_shp && shape->_stroke.paint.type() == NRArenaShape::Paint::COLOR) {
887 // cairo_arena_shape_render_stroke(item, area, pb);
889 guint32 rgba;
890 NRPixBlock m;
892 nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE);
894 // if memory allocation failed, abort render
895 if (m.size != NR_PIXBLOCK_SIZE_TINY && m.data.px == NULL) {
896 nr_pixblock_release (&m);
897 return (item->state);
898 }
900 m.visible_area = pb->visible_area;
901 nr_pixblock_render_shape_mask_or(m, shape->stroke_shp);
902 m.empty = FALSE;
904 if ( item->render_opacity ) {
905 rgba = shape->_stroke.paint.color().toRGBA32( shape->_stroke.opacity *
906 SP_SCALE24_TO_FLOAT(style->opacity.value) );
907 } else {
908 rgba = shape->_stroke.paint.color().toRGBA32( shape->_stroke.opacity );
909 }
910 nr_blit_pixblock_mask_rgba32(pb, &m, rgba);
911 pb->empty = FALSE;
913 nr_pixblock_release(&m);
915 } else if (shape->stroke_shp && shape->_stroke.paint.type() == NRArenaShape::Paint::SERVER) {
917 NRPixBlock m;
919 nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE);
921 // if memory allocation failed, abort render
922 if (m.size != NR_PIXBLOCK_SIZE_TINY && m.data.px == NULL) {
923 nr_pixblock_release (&m);
924 return (item->state);
925 }
927 m.visible_area = pb->visible_area;
928 nr_pixblock_render_shape_mask_or(m, shape->stroke_shp);
929 m.empty = FALSE;
931 if (shape->stroke_painter) {
932 nr_arena_render_paintserver_fill(pb, area, shape->stroke_painter, shape->_stroke.opacity, &m);
933 }
935 nr_pixblock_release(&m);
936 }
938 } // non-cairo non-outline branch
940 /* Render markers into parent buffer */
941 for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) {
942 unsigned int ret = nr_arena_item_invoke_render(ct, child, area, pb, flags);
943 if (ret & NR_ARENA_ITEM_STATE_INVALID) return ret;
944 }
946 return item->state;
947 }
950 // cairo clipping: this basically works except for the stride-must-be-divisible-by-4 cairo bug;
951 // reenable this when the bug is fixed and remove the rest of this function
952 // TODO
953 #if defined(DEADCODE) && !defined(DEADCODE)
954 static guint
955 cairo_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
956 {
957 NRArenaShape *shape = NR_ARENA_SHAPE(item);
958 if (!shape->curve) return item->state;
960 cairo_t *ct = nr_create_cairo_context (area, pb);
962 if (!ct)
963 return item->state;
965 cairo_set_source_rgba(ct, 0, 0, 0, 1);
967 cairo_new_path(ct);
969 feed_pathvector_to_cairo (ct, shape->curve->get_pathvector(), to_2geom(shape->ctm), (area)->upgrade(), false, 0);
971 cairo_fill(ct);
973 cairo_surface_t *cst = cairo_get_target(ct);
974 cairo_destroy (ct);
975 cairo_surface_finish (cst);
976 cairo_surface_destroy (cst);
978 pb->empty = FALSE;
980 return item->state;
981 }
982 #endif //defined(DEADCODE) && !defined(DEADCODE)
985 static guint
986 nr_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
987 {
988 //return cairo_arena_shape_clip(item, area, pb);
990 NRArenaShape *shape = NR_ARENA_SHAPE(item);
991 if (!shape->curve) return item->state;
993 if ( shape->delayed_shp || shape->fill_shp == NULL) { // we need a fill shape no matter what
994 if ( nr_rect_l_test_intersect(area, &item->bbox) ) {
995 NRGC tempGC(NULL);
996 tempGC.transform=shape->ctm;
997 shape->delayed_shp = false;
998 nr_arena_shape_update_fill(shape, &tempGC, &pb->visible_area, true);
999 } else {
1000 return item->state;
1001 }
1002 }
1004 if ( shape->fill_shp ) {
1005 NRPixBlock m;
1007 /* fixme: We can OR in one step (Lauris) */
1008 nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE);
1010 // if memory allocation failed, abort
1011 if (m.size != NR_PIXBLOCK_SIZE_TINY && m.data.px == NULL) {
1012 nr_pixblock_release (&m);
1013 return (item->state);
1014 }
1016 m.visible_area = pb->visible_area;
1017 nr_pixblock_render_shape_mask_or(m,shape->fill_shp);
1019 for (int y = area->y0; y < area->y1; y++) {
1020 unsigned char *s, *d;
1021 s = NR_PIXBLOCK_PX(&m) + (y - area->y0) * m.rs;
1022 d = NR_PIXBLOCK_PX(pb) + (y - area->y0) * pb->rs;
1023 for (int x = area->x0; x < area->x1; x++) {
1024 *d = NR_COMPOSEA_111(*s, *d);
1025 d ++;
1026 s ++;
1027 }
1028 }
1029 nr_pixblock_release(&m);
1030 pb->empty = FALSE;
1031 }
1033 return item->state;
1034 }
1036 static NRArenaItem *
1037 nr_arena_shape_pick(NRArenaItem *item, NR::Point p, double delta, unsigned int /*sticky*/)
1038 {
1039 NRArenaShape *shape = NR_ARENA_SHAPE(item);
1041 if (shape->repick_after > 0)
1042 shape->repick_after--;
1044 if (shape->repick_after > 0) // we are a slow, huge path. skip this pick, returning what was returned last time
1045 return shape->last_pick;
1047 if (!shape->curve) return NULL;
1048 if (!shape->style) return NULL;
1049 if (SP_SCALE24_TO_FLOAT(shape->style->opacity.value) == 0) // fully transparent, no pick
1050 return NULL;
1052 GTimeVal tstart, tfinish;
1053 g_get_current_time (&tstart);
1055 bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE);
1057 double width;
1058 if (outline) {
1059 width = 0.5;
1060 } else if (shape->_stroke.paint.type() != NRArenaShape::Paint::NONE && shape->_stroke.opacity > 1e-3) {
1061 float const scale = NR::expansion(shape->ctm);
1062 width = MAX(0.125, shape->_stroke.width * scale) / 2;
1063 } else {
1064 width = 0;
1065 }
1067 const_NRBPath bp;
1068 bp.path = SP_CURVE_BPATH(shape->curve);
1069 double dist = NR_HUGE;
1070 int wind = 0;
1071 bool needfill = (shape->_fill.paint.type() != NRArenaShape::Paint::NONE
1072 && shape->_fill.opacity > 1e-3 && !outline);
1074 if (item->arena->canvasarena) {
1075 NR::Rect viewbox = item->arena->canvasarena->item.canvas->getViewbox();
1076 viewbox.growBy (width);
1077 nr_path_matrix_point_bbox_wind_distance(&bp, shape->ctm, p, NULL, needfill? &wind : NULL, &dist, 0.5, &viewbox);
1078 } else {
1079 nr_path_matrix_point_bbox_wind_distance(&bp, shape->ctm, p, NULL, needfill? &wind : NULL, &dist, 0.5, NULL);
1080 }
1082 g_get_current_time (&tfinish);
1083 glong this_pick = (tfinish.tv_sec - tstart.tv_sec) * 1000000 + (tfinish.tv_usec - tstart.tv_usec);
1084 //g_print ("pick time %lu\n", this_pick);
1086 if (this_pick > 10000) { // slow picking, remember to skip several new picks
1087 shape->repick_after = this_pick / 5000;
1088 }
1090 // covered by fill?
1091 if (needfill) {
1092 if (!shape->style->fill_rule.computed) {
1093 if (wind != 0) {
1094 shape->last_pick = item;
1095 return item;
1096 }
1097 } else {
1098 if (wind & 0x1) {
1099 shape->last_pick = item;
1100 return item;
1101 }
1102 }
1103 }
1105 // close to the edge, as defined by strokewidth and delta?
1106 // this ignores dashing (as if the stroke is solid) and always works as if caps are round
1107 if (needfill || width > 0) { // if either fill or stroke visible,
1108 if ((dist - width) < delta) {
1109 shape->last_pick = item;
1110 return item;
1111 }
1112 }
1114 // if not picked on the shape itself, try its markers
1115 for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) {
1116 NRArenaItem *ret = nr_arena_item_invoke_pick(child, p, delta, 0);
1117 if (ret) {
1118 shape->last_pick = item;
1119 return item;
1120 }
1121 }
1123 shape->last_pick = NULL;
1124 return NULL;
1125 }
1127 /**
1128 *
1129 * Requests a render of the shape, then if the shape is already a curve it
1130 * unrefs the old curve; if the new curve is valid it creates a copy of the
1131 * curve and adds it to the shape. Finally, it requests an update of the
1132 * arena for the shape.
1133 */
1134 void nr_arena_shape_set_path(NRArenaShape *shape, SPCurve *curve,bool justTrans)
1135 {
1136 g_return_if_fail(shape != NULL);
1137 g_return_if_fail(NR_IS_ARENA_SHAPE(shape));
1139 if ( justTrans == false ) {
1140 // dirty cached versions
1141 if ( shape->cached_fill ) {
1142 delete shape->cached_fill;
1143 shape->cached_fill=NULL;
1144 }
1145 if ( shape->cached_stroke ) {
1146 delete shape->cached_stroke;
1147 shape->cached_stroke=NULL;
1148 }
1149 }
1151 nr_arena_item_request_render(NR_ARENA_ITEM(shape));
1153 if (shape->curve) {
1154 shape->curve->unref();
1155 shape->curve = NULL;
1156 }
1158 if (curve) {
1159 shape->curve = curve;
1160 curve->ref();
1161 }
1163 nr_arena_item_request_update(NR_ARENA_ITEM(shape), NR_ARENA_ITEM_STATE_ALL, FALSE);
1164 }
1166 void NRArenaShape::setFill(SPPaintServer *server) {
1167 _fill.paint.set(server);
1168 _invalidateCachedFill();
1169 }
1171 void NRArenaShape::setFill(SPColor const &color) {
1172 _fill.paint.set(color);
1173 _invalidateCachedFill();
1174 }
1176 void NRArenaShape::setFillOpacity(double opacity) {
1177 _fill.opacity = opacity;
1178 _invalidateCachedFill();
1179 }
1181 void NRArenaShape::setFillRule(NRArenaShape::FillRule rule) {
1182 _fill.rule = rule;
1183 _invalidateCachedFill();
1184 }
1186 void NRArenaShape::setStroke(SPPaintServer *server) {
1187 _stroke.paint.set(server);
1188 _invalidateCachedStroke();
1189 }
1191 void NRArenaShape::setStroke(SPColor const &color) {
1192 _stroke.paint.set(color);
1193 _invalidateCachedStroke();
1194 }
1196 void NRArenaShape::setStrokeOpacity(double opacity) {
1197 _stroke.opacity = opacity;
1198 _invalidateCachedStroke();
1199 }
1201 void NRArenaShape::setStrokeWidth(double width) {
1202 _stroke.width = width;
1203 _invalidateCachedStroke();
1204 }
1206 void NRArenaShape::setMitreLimit(double limit) {
1207 _stroke.mitre_limit = limit;
1208 _invalidateCachedStroke();
1209 }
1211 void NRArenaShape::setLineCap(NRArenaShape::CapType cap) {
1212 _stroke.cap = cap;
1213 _invalidateCachedStroke();
1214 }
1216 void NRArenaShape::setLineJoin(NRArenaShape::JoinType join) {
1217 _stroke.join = join;
1218 _invalidateCachedStroke();
1219 }
1221 /** nr_arena_shape_set_style
1222 *
1223 * Unrefs any existing style and ref's to the given one, then requests an update of the arena
1224 */
1225 void
1226 nr_arena_shape_set_style(NRArenaShape *shape, SPStyle *style)
1227 {
1228 g_return_if_fail(shape != NULL);
1229 g_return_if_fail(NR_IS_ARENA_SHAPE(shape));
1231 if (style) sp_style_ref(style);
1232 if (shape->style) sp_style_unref(shape->style);
1233 shape->style = style;
1235 if ( style->fill.isPaintserver() ) {
1236 shape->setFill(style->getFillPaintServer());
1237 } else if ( style->fill.isColor() ) {
1238 shape->setFill(style->fill.value.color);
1239 } else if ( style->fill.isNone() ) {
1240 shape->setFill(NULL);
1241 } else {
1242 g_assert_not_reached();
1243 }
1244 shape->setFillOpacity(SP_SCALE24_TO_FLOAT(style->fill_opacity.value));
1245 switch (style->fill_rule.computed) {
1246 case SP_WIND_RULE_EVENODD: {
1247 shape->setFillRule(NRArenaShape::EVEN_ODD);
1248 break;
1249 }
1250 case SP_WIND_RULE_NONZERO: {
1251 shape->setFillRule(NRArenaShape::NONZERO);
1252 break;
1253 }
1254 default: {
1255 g_assert_not_reached();
1256 }
1257 }
1259 if ( style->stroke.isPaintserver() ) {
1260 shape->setStroke(style->getStrokePaintServer());
1261 } else if ( style->stroke.isColor() ) {
1262 shape->setStroke(style->stroke.value.color);
1263 } else if ( style->stroke.isNone() ) {
1264 shape->setStroke(NULL);
1265 } else {
1266 g_assert_not_reached();
1267 }
1268 shape->setStrokeWidth(style->stroke_width.computed);
1269 shape->setStrokeOpacity(SP_SCALE24_TO_FLOAT(style->stroke_opacity.value));
1270 switch (style->stroke_linecap.computed) {
1271 case SP_STROKE_LINECAP_ROUND: {
1272 shape->setLineCap(NRArenaShape::ROUND_CAP);
1273 break;
1274 }
1275 case SP_STROKE_LINECAP_SQUARE: {
1276 shape->setLineCap(NRArenaShape::SQUARE_CAP);
1277 break;
1278 }
1279 case SP_STROKE_LINECAP_BUTT: {
1280 shape->setLineCap(NRArenaShape::BUTT_CAP);
1281 break;
1282 }
1283 default: {
1284 g_assert_not_reached();
1285 }
1286 }
1287 switch (style->stroke_linejoin.computed) {
1288 case SP_STROKE_LINEJOIN_ROUND: {
1289 shape->setLineJoin(NRArenaShape::ROUND_JOIN);
1290 break;
1291 }
1292 case SP_STROKE_LINEJOIN_BEVEL: {
1293 shape->setLineJoin(NRArenaShape::BEVEL_JOIN);
1294 break;
1295 }
1296 case SP_STROKE_LINEJOIN_MITER: {
1297 shape->setLineJoin(NRArenaShape::MITRE_JOIN);
1298 break;
1299 }
1300 default: {
1301 g_assert_not_reached();
1302 }
1303 }
1304 shape->setMitreLimit(style->stroke_miterlimit.value);
1306 //if shape has a filter
1307 if (style->filter.set && style->getFilter()) {
1308 if (!shape->filter) {
1309 int primitives = sp_filter_primitive_count(SP_FILTER(style->getFilter()));
1310 shape->filter = new NR::Filter(primitives);
1311 }
1312 sp_filter_build_renderer(SP_FILTER(style->getFilter()), shape->filter);
1313 } else {
1314 //no filter set for this shape
1315 delete shape->filter;
1316 shape->filter = NULL;
1317 }
1319 nr_arena_item_request_update(shape, NR_ARENA_ITEM_STATE_ALL, FALSE);
1320 }
1322 void
1323 nr_arena_shape_set_paintbox(NRArenaShape *shape, NRRect const *pbox)
1324 {
1325 g_return_if_fail(shape != NULL);
1326 g_return_if_fail(NR_IS_ARENA_SHAPE(shape));
1327 g_return_if_fail(pbox != NULL);
1329 if ((pbox->x0 < pbox->x1) && (pbox->y0 < pbox->y1)) {
1330 shape->paintbox = *pbox;
1331 } else {
1332 /* fixme: We kill warning, although not sure what to do here (Lauris) */
1333 shape->paintbox.x0 = shape->paintbox.y0 = 0.0F;
1334 shape->paintbox.x1 = shape->paintbox.y1 = 256.0F;
1335 }
1337 nr_arena_item_request_update(shape, NR_ARENA_ITEM_STATE_ALL, FALSE);
1338 }
1340 void NRArenaShape::setPaintBox(NR::Rect const &pbox)
1341 {
1342 paintbox.x0 = pbox.min()[NR::X];
1343 paintbox.y0 = pbox.min()[NR::Y];
1344 paintbox.x1 = pbox.max()[NR::X];
1345 paintbox.y1 = pbox.max()[NR::Y];
1347 nr_arena_item_request_update(this, NR_ARENA_ITEM_STATE_ALL, FALSE);
1348 }
1350 static void
1351 shape_run_A8_OR(raster_info &dest,void */*data*/,int st,float vst,int en,float ven)
1352 {
1353 if ( st >= en ) return;
1354 if ( vst < 0 ) vst=0;
1355 if ( vst > 1 ) vst=1;
1356 if ( ven < 0 ) ven=0;
1357 if ( ven > 1 ) ven=1;
1358 float sv=vst;
1359 float dv=ven-vst;
1360 int len=en-st;
1361 unsigned char* d=(unsigned char*)dest.buffer;
1362 d+=(st-dest.startPix);
1363 if ( fabs(dv) < 0.001 ) {
1364 if ( vst > 0.999 ) {
1365 /* Simple copy */
1366 while (len > 0) {
1367 d[0] = 255;
1368 d += 1;
1369 len -= 1;
1370 }
1371 } else {
1372 sv*=256;
1373 unsigned int c0_24=(int)sv;
1374 c0_24&=0xFF;
1375 while (len > 0) {
1376 /* Draw */
1377 d[0] = NR_COMPOSEA_111(c0_24,d[0]);
1378 d += 1;
1379 len -= 1;
1380 }
1381 }
1382 } else {
1383 if ( en <= st+1 ) {
1384 sv=0.5*(vst+ven);
1385 sv*=256;
1386 unsigned int c0_24=(int)sv;
1387 c0_24&=0xFF;
1388 /* Draw */
1389 d[0] = NR_COMPOSEA_111(c0_24,d[0]);
1390 } else {
1391 dv/=len;
1392 sv+=0.5*dv; // correction trapezoidale
1393 sv*=16777216;
1394 dv*=16777216;
1395 int c0_24 = static_cast<int>(CLAMP(sv, 0, 16777216));
1396 int s0_24 = static_cast<int>(dv);
1397 while (len > 0) {
1398 unsigned int ca;
1399 /* Draw */
1400 ca = c0_24 >> 16;
1401 if ( ca > 255 ) ca=255;
1402 d[0] = NR_COMPOSEA_111(ca,d[0]);
1403 d += 1;
1404 c0_24 += s0_24;
1405 c0_24 = CLAMP(c0_24, 0, 16777216);
1406 len -= 1;
1407 }
1408 }
1409 }
1410 }
1412 void nr_pixblock_render_shape_mask_or(NRPixBlock &m,Shape* theS)
1413 {
1414 theS->CalcBBox();
1415 float l = theS->leftX, r = theS->rightX, t = theS->topY, b = theS->bottomY;
1416 int il,ir,it,ib;
1417 il=(int)floor(l);
1418 ir=(int)ceil(r);
1419 it=(int)floor(t);
1420 ib=(int)ceil(b);
1422 if ( il >= m.area.x1 || ir <= m.area.x0 || it >= m.area.y1 || ib <= m.area.y0 ) return;
1423 if ( il < m.area.x0 ) il=m.area.x0;
1424 if ( it < m.area.y0 ) it=m.area.y0;
1425 if ( ir > m.area.x1 ) ir=m.area.x1;
1426 if ( ib > m.area.y1 ) ib=m.area.y1;
1428 /* This is the FloatLigne version. See svn (prior to Apr 2006) for versions using BitLigne or direct BitLigne. */
1429 int curPt;
1430 float curY;
1431 theS->BeginQuickRaster(curY, curPt);
1433 FloatLigne *theI = new FloatLigne();
1434 IntLigne *theIL = new IntLigne();
1436 theS->DirectQuickScan(curY, curPt, (float) it, true, 1.0);
1438 char *mdata = (char*)m.data.px;
1439 if ( m.size == NR_PIXBLOCK_SIZE_TINY ) mdata=(char*)m.data.p;
1440 uint32_t *ligStart = ((uint32_t*)(mdata + ((il - m.area.x0) + m.rs * (it - m.area.y0))));
1441 for (int y = it; y < ib; y++) {
1442 theI->Reset();
1443 theS->QuickScan(curY, curPt, ((float)(y+1)), theI, 1.0);
1444 theI->Flatten();
1445 theIL->Copy(theI);
1447 raster_info dest;
1448 dest.startPix=il;
1449 dest.endPix=ir;
1450 dest.sth=il;
1451 dest.stv=y;
1452 dest.buffer=ligStart;
1453 theIL->Raster(dest, NULL, shape_run_A8_OR);
1454 ligStart=((uint32_t*)(((char*)ligStart)+m.rs));
1455 }
1456 theS->EndQuickRaster();
1457 delete theI;
1458 delete theIL;
1459 }
1462 /*
1463 Local Variables:
1464 mode:c++
1465 c-file-style:"stroustrup"
1466 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1467 indent-tabs-mode:nil
1468 fill-column:99
1469 End:
1470 */
1471 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :