Code

when bailing out on update due to missing curve or style, don't forget to update...
[inkscape.git] / src / display / nr-arena-shape.cpp
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)
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;
127 static void
128 nr_arena_shape_finalize(NRObject *object)
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);
145 /**
146  * Retrieves the markers from the item
147  */
148 static NRArenaItem *
149 nr_arena_shape_children(NRArenaItem *item)
151     NRArenaShape *shape = (NRArenaShape *) item;
153     return shape->markers;
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)
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);
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)
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);
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)
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);
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)
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;
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;
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);
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)
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     }
511 void
512 nr_arena_shape_update_stroke(NRArenaShape *shape,NRGC* gc, NRRectL *area)
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     }
659 void
660 nr_arena_shape_add_bboxes(NRArenaShape* shape, NRRect &bbox)
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     }
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)
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;
838 static guint
839 nr_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
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;
887 static NRArenaItem *
888 nr_arena_shape_pick(NRArenaItem *item, NR::Point p, double delta, unsigned int /*sticky*/)
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;
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)
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);
1005 void NRArenaShape::setFill(SPPaintServer *server) {
1006     _fill.paint.set(server);
1007     _invalidateCachedFill();
1010 void NRArenaShape::setFill(SPColor const &color) {
1011     _fill.paint.set(color);
1012     _invalidateCachedFill();
1015 void NRArenaShape::setFillOpacity(double opacity) {
1016     _fill.opacity = opacity;
1017     _invalidateCachedFill();
1020 void NRArenaShape::setFillRule(NRArenaShape::FillRule rule) {
1021     _fill.rule = rule;
1022     _invalidateCachedFill();
1025 void NRArenaShape::setStroke(SPPaintServer *server) {
1026     _stroke.paint.set(server);
1027     _invalidateCachedStroke();
1030 void NRArenaShape::setStroke(SPColor const &color) {
1031     _stroke.paint.set(color);
1032     _invalidateCachedStroke();
1035 void NRArenaShape::setStrokeOpacity(double opacity) {
1036     _stroke.opacity = opacity;
1037     _invalidateCachedStroke();
1040 void NRArenaShape::setStrokeWidth(double width) {
1041     _stroke.width = width;
1042     _invalidateCachedStroke();
1045 void NRArenaShape::setMitreLimit(double limit) {
1046     _stroke.mitre_limit = limit;
1047     _invalidateCachedStroke();
1050 void NRArenaShape::setLineCap(NRArenaShape::CapType cap) {
1051     _stroke.cap = cap;
1052     _invalidateCachedStroke();
1055 void NRArenaShape::setLineJoin(NRArenaShape::JoinType join) {
1056     _stroke.join = join;
1057     _invalidateCachedStroke();
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)
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);
1169         
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);
1200 void
1201 nr_arena_shape_set_paintbox(NRArenaShape *shape, NRRect const *pbox)
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);
1218 void NRArenaShape::setPaintBox(NR::Rect const &pbox)
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);
1228 static void
1229 shape_run_A8_OR(raster_info &dest,void */*data*/,int st,float vst,int en,float ven)
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     }
1290 void nr_pixblock_render_shape_mask_or(NRPixBlock &m,Shape* theS)
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;
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 :