X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fdisplay%2Fnr-arena-shape.cpp;h=6f38fd97d8d018d57a57af5bae793749235b0246;hb=0dc33d4ce43e0bb49c63aa53b826ec4a1ff68e28;hp=743a70f9c085dab5d2d6283ba13b0b211ad89eee;hpb=c51d6f6db62a250e2dd74bd71beda9468c47d663;p=inkscape.git diff --git a/src/display/nr-arena-shape.cpp b/src/display/nr-arena-shape.cpp index 743a70f9c..6f38fd97d 100644 --- a/src/display/nr-arena-shape.cpp +++ b/src/display/nr-arena-shape.cpp @@ -1,5 +1,3 @@ -#define __NR_ARENA_SHAPE_C__ - /* * RGBA display list system for inkscape * @@ -12,19 +10,36 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include + +#include <2geom/svg-path.h> +#include <2geom/svg-path-parser.h> +#include <2geom/pathvector.h> +#include <2geom/curves.h> +#include "display/sp-canvas.h" +#include "display/canvas-arena.h" +#include "display/nr-arena.h" +#include "display/nr-arena-shape.h" +#include "display/curve.h" +#include "libnr/nr-pixops.h" +#include "libnr/nr-blit.h" +#include "libnr/nr-convert2geom.h" +#include "livarot/Path.h" +#include "livarot/float-line.h" +#include "livarot/int-line.h" +#include "style.h" +#include "inkscape-cairo.h" +#include "helper/geom.h" +#include "helper/geom-curves.h" +#include "sp-filter.h" +#include "sp-filter-reference.h" +#include "display/nr-filter.h" +#include "preferences.h" + +#include "svg/svg.h" //int showRuns=0; void nr_pixblock_render_shape_mask_or(NRPixBlock &m,Shape* theS); @@ -39,9 +54,9 @@ static void nr_arena_shape_remove_child(NRArenaItem *item, NRArenaItem *child); static void nr_arena_shape_set_child_position(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref); static guint nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset); -static unsigned int nr_arena_shape_render(NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags); +static unsigned int nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags); static guint nr_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb); -static NRArenaItem *nr_arena_shape_pick(NRArenaItem *item, NR::Point p, double delta, unsigned int sticky); +static NRArenaItem *nr_arena_shape_pick(NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky); static NRArenaItemClass *shape_parent_class; @@ -84,6 +99,10 @@ nr_arena_shape_class_init(NRArenaShapeClass *klass) item_class->pick = nr_arena_shape_pick; } +/** + * Initializes the arena shape, setting all parameters to null, 0, false, + * or other defaults + */ static void nr_arena_shape_init(NRArenaShape *shape) { @@ -92,11 +111,13 @@ nr_arena_shape_init(NRArenaShape *shape) shape->paintbox.x0 = shape->paintbox.y0 = 0.0F; shape->paintbox.x1 = shape->paintbox.y1 = 256.0F; - nr_matrix_set_identity(&shape->ctm); + shape->ctm.setIdentity(); shape->fill_painter = NULL; shape->stroke_painter = NULL; shape->cached_fill = NULL; shape->cached_stroke = NULL; + shape->cached_fpartialy = false; + shape->cached_spartialy = false; shape->fill_shp = NULL; shape->stroke_shp = NULL; @@ -104,10 +125,13 @@ nr_arena_shape_init(NRArenaShape *shape) shape->approx_bbox.x0 = shape->approx_bbox.y0 = 0; shape->approx_bbox.x1 = shape->approx_bbox.y1 = 0; - nr_matrix_set_identity(&shape->cached_fctm); - nr_matrix_set_identity(&shape->cached_sctm); + shape->cached_fctm.setIdentity(); + shape->cached_sctm.setIdentity(); shape->markers = NULL; + + shape->last_pick = NULL; + shape->repick_after = 0; } static void @@ -123,11 +147,14 @@ nr_arena_shape_finalize(NRObject *object) if (shape->stroke_painter) sp_painter_free(shape->stroke_painter); if (shape->style) sp_style_unref(shape->style); - if (shape->curve) sp_curve_unref(shape->curve); + if (shape->curve) shape->curve->unref(); ((NRObjectClass *) shape_parent_class)->finalize(object); } +/** + * Retrieves the markers from the item + */ static NRArenaItem * nr_arena_shape_children(NRArenaItem *item) { @@ -136,62 +163,80 @@ nr_arena_shape_children(NRArenaItem *item) return shape->markers; } +/** + * Attaches child to item, and if ref is not NULL, sets it and ref->next as + * the prev and next items. If ref is NULL, then it sets the item's markers + * as the next items. + */ static void nr_arena_shape_add_child(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref) { NRArenaShape *shape = (NRArenaShape *) item; if (!ref) { - shape->markers = nr_arena_item_attach_ref(item, child, NULL, shape->markers); + shape->markers = nr_arena_item_attach(item, child, NULL, shape->markers); } else { - ref->next = nr_arena_item_attach_ref(item, child, ref, ref->next); + ref->next = nr_arena_item_attach(item, child, ref, ref->next); } nr_arena_item_request_update(item, NR_ARENA_ITEM_STATE_ALL, FALSE); } +/** + * Removes child from the shape. If there are no prev items in + * the child, it sets items' markers to the next item in the child. + */ static void nr_arena_shape_remove_child(NRArenaItem *item, NRArenaItem *child) { NRArenaShape *shape = (NRArenaShape *) item; if (child->prev) { - nr_arena_item_detach_unref(item, child); + nr_arena_item_detach(item, child); } else { - shape->markers = nr_arena_item_detach_unref(item, child); + shape->markers = nr_arena_item_detach(item, child); } nr_arena_item_request_update(item, NR_ARENA_ITEM_STATE_ALL, FALSE); } +/** + * Detaches child from item, and if there are no previous items in child, it + * sets item's markers to the child. It then attaches the child back onto the item. + * If ref is null, it sets the markers to be the next item, otherwise it uses + * the next/prev items in ref. + */ static void nr_arena_shape_set_child_position(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref) { NRArenaShape *shape = (NRArenaShape *) item; if (child->prev) { - nr_arena_item_detach_unref(item, child); + nr_arena_item_detach(item, child); } else { - shape->markers = nr_arena_item_detach_unref(item, child); + shape->markers = nr_arena_item_detach(item, child); } if (!ref) { - shape->markers = nr_arena_item_attach_ref(item, child, NULL, shape->markers); + shape->markers = nr_arena_item_attach(item, child, NULL, shape->markers); } else { - ref->next = nr_arena_item_attach_ref(item, child, ref, ref->next); + ref->next = nr_arena_item_attach(item, child, ref, ref->next); } nr_arena_item_request_render(child); } -void nr_arena_shape_update_stroke(NRArenaShape *shape,NRGC* gc); -void nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, bool force_shape = false); -void nr_arena_shape_add_bboxes(NRArenaShape* shape,NRRect &bbox); +void nr_arena_shape_update_stroke(NRArenaShape *shape, NRGC* gc, NRRectL *area); +void nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, NRRectL *area, bool force_shape = false); +void nr_arena_shape_add_bboxes(NRArenaShape* shape, Geom::OptRect &bbox); +/** + * Updates the arena shape 'item' and all of its children, including the markers. + */ static guint nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset) { - NRRect bbox; + Geom::OptRect boundingbox; NRArenaShape *shape = NR_ARENA_SHAPE(item); @@ -208,16 +253,15 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g shape->ctm = gc->transform; if (state & NR_ARENA_ITEM_STATE_BBOX) { if (shape->curve) { - NRBPath bp; - /* fixme: */ - bbox.x0 = bbox.y0 = NR_HUGE; - bbox.x1 = bbox.y1 = -NR_HUGE; - bp.path = shape->curve->bpath; - nr_path_matrix_bbox_union(&bp, gc->transform, &bbox); - item->bbox.x0 = (gint32)(bbox.x0 - 1.0F); - item->bbox.y0 = (gint32)(bbox.y0 - 1.0F); - item->bbox.x1 = (gint32)(bbox.x1 + 1.9999F); - item->bbox.y1 = (gint32)(bbox.y1 + 1.9999F); + boundingbox = bounds_exact_transformed(shape->curve->get_pathvector(), gc->transform); + if (boundingbox) { + item->bbox.x0 = static_cast(floor((*boundingbox)[0][0])); // Floor gives the coordinate in which the point resides + item->bbox.y0 = static_cast(floor((*boundingbox)[1][0])); + item->bbox.x1 = static_cast(ceil ((*boundingbox)[0][1])); // Ceil gives the first coordinate beyond the point + item->bbox.y1 = static_cast(ceil ((*boundingbox)[1][1])); + } else { + item->bbox = NR_RECT_L_EMPTY; + } } if (beststate & NR_ARENA_ITEM_STATE_BBOX) { for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) { @@ -230,51 +274,44 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g shape->delayed_shp=true; shape->ctm = gc->transform; - bbox.x0 = bbox.y0 = NR_HUGE; - bbox.x1 = bbox.y1 = -NR_HUGE; + boundingbox = Geom::OptRect(); + + bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); if (shape->curve) { - NRBPath bp; - /* fixme: */ - bbox.x0 = bbox.y0 = NR_HUGE; - bbox.x1 = bbox.y1 = -NR_HUGE; - bp.path = shape->curve->bpath; - nr_path_matrix_bbox_union(&bp, gc->transform, &bbox); - if (shape->_stroke.paint.type() != NRArenaShape::Paint::NONE) { + boundingbox = bounds_exact_transformed(shape->curve->get_pathvector(), gc->transform); + + if (boundingbox && (shape->_stroke.paint.type() != NRArenaShape::Paint::NONE || outline)) { float width, scale; - scale = NR_MATRIX_DF_EXPANSION(&gc->transform); + scale = gc->transform.descrim(); width = MAX(0.125, shape->_stroke.width * scale); if ( fabs(shape->_stroke.width * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord - bbox.x0-=width; - bbox.x1+=width; - bbox.y0-=width; - bbox.y1+=width; + boundingbox->expandBy(width); } // those pesky miters, now float miterMax=width*shape->_stroke.mitre_limit; if ( miterMax > 0.01 ) { // grunt mode. we should compute the various miters instead (one for each point on the curve) - bbox.x0-=miterMax; - bbox.x1+=miterMax; - bbox.y0-=miterMax; - bbox.y1+=miterMax; + boundingbox->expandBy(miterMax); } } + } + + /// \todo just write item->bbox = boundingbox + if (boundingbox) { + shape->approx_bbox.x0 = static_cast(floor((*boundingbox)[0][0])); + shape->approx_bbox.y0 = static_cast(floor((*boundingbox)[1][0])); + shape->approx_bbox.x1 = static_cast(ceil ((*boundingbox)[0][1])); + shape->approx_bbox.y1 = static_cast(ceil ((*boundingbox)[1][1])); } else { + shape->approx_bbox = NR_RECT_L_EMPTY; } - shape->approx_bbox.x0 = (gint32)(bbox.x0 - 1.0F); - shape->approx_bbox.y0 = (gint32)(bbox.y0 - 1.0F); - shape->approx_bbox.x1 = (gint32)(bbox.x1 + 1.9999F); - shape->approx_bbox.y1 = (gint32)(bbox.y1 + 1.9999F); - if ( area && nr_rect_l_test_intersect(area, &shape->approx_bbox) ) shape->delayed_shp=false; + if ( area && nr_rect_l_test_intersect_ptr(area, &shape->approx_bbox) ) shape->delayed_shp=false; /* Release state data */ - if (TRUE || !nr_matrix_test_transform_equal(&gc->transform, &shape->ctm, NR_EPSILON)) { - /* Concept test */ - if (shape->fill_shp) { - delete shape->fill_shp; - shape->fill_shp = NULL; - } + if (shape->fill_shp) { + delete shape->fill_shp; + shape->fill_shp = NULL; } if (shape->stroke_shp) { delete shape->stroke_shp; @@ -289,11 +326,13 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g shape->stroke_painter = NULL; } - if (!shape->curve || !shape->style) return NR_ARENA_ITEM_STATE_ALL; - if (sp_curve_is_empty(shape->curve)) return NR_ARENA_ITEM_STATE_ALL; - if ( ( shape->_fill.paint.type() == NRArenaShape::Paint::NONE ) && - ( shape->_stroke.paint.type() == NRArenaShape::Paint::NONE ) ) + if (!shape->curve || + !shape->style || + shape->curve->is_empty() || + (( shape->_fill.paint.type() == NRArenaShape::Paint::NONE ) && + ( shape->_stroke.paint.type() == NRArenaShape::Paint::NONE && !outline) )) { + item->bbox = shape->approx_bbox; return NR_ARENA_ITEM_STATE_ALL; } @@ -301,31 +340,37 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g if ( shape->delayed_shp ) { item->bbox=shape->approx_bbox; } else { - nr_arena_shape_update_stroke(shape,gc); - nr_arena_shape_update_fill(shape,gc); - - bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0.0; - nr_arena_shape_add_bboxes(shape,bbox); - - shape->approx_bbox.x0 = (gint32)(bbox.x0 - 1.0F); - shape->approx_bbox.y0 = (gint32)(bbox.y0 - 1.0F); - shape->approx_bbox.x1 = (gint32)(bbox.x1 + 1.9999F); - shape->approx_bbox.y1 = (gint32)(bbox.y1 + 1.9999F); + nr_arena_shape_update_stroke(shape, gc, area); + nr_arena_shape_update_fill(shape, gc, area); + + boundingbox = Geom::OptRect(); + nr_arena_shape_add_bboxes(shape, boundingbox); + + /// \todo just write shape->approx_bbox = boundingbox + if (boundingbox) { + shape->approx_bbox.x0 = static_cast(floor((*boundingbox)[0][0])); + shape->approx_bbox.y0 = static_cast(floor((*boundingbox)[1][0])); + shape->approx_bbox.x1 = static_cast(ceil ((*boundingbox)[0][1])); + shape->approx_bbox.y1 = static_cast(ceil ((*boundingbox)[1][1])); + } else { + shape->approx_bbox = NR_RECT_L_EMPTY; + } } - if (nr_rect_d_test_empty(&bbox)) return NR_ARENA_ITEM_STATE_ALL; + if (!boundingbox) + return NR_ARENA_ITEM_STATE_ALL; - item->bbox.x0 = (gint32)(bbox.x0 - 1.0F); - item->bbox.y0 = (gint32)(bbox.y0 - 1.0F); - item->bbox.x1 = (gint32)(bbox.x1 + 1.0F); - item->bbox.y1 = (gint32)(bbox.y1 + 1.0F); - nr_arena_request_render_rect(item->arena, &item->bbox); + /// \todo just write item->bbox = boundingbox + item->bbox.x0 = static_cast(floor((*boundingbox)[0][0])); + item->bbox.y0 = static_cast(floor((*boundingbox)[1][0])); + item->bbox.x1 = static_cast(ceil ((*boundingbox)[0][1])); + item->bbox.y1 = static_cast(ceil ((*boundingbox)[1][1])); item->render_opacity = TRUE; if ( shape->_fill.paint.type() == NRArenaShape::Paint::SERVER ) { if (gc && gc->parent) { shape->fill_painter = sp_paint_server_painter_new(shape->_fill.paint.server(), - NR::Matrix(&gc->transform), NR::Matrix(&gc->parent->transform), + gc->transform, gc->parent->transform, &shape->paintbox); } item->render_opacity = FALSE; @@ -333,16 +378,17 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g if ( shape->_stroke.paint.type() == NRArenaShape::Paint::SERVER ) { if (gc && gc->parent) { shape->stroke_painter = sp_paint_server_painter_new(shape->_stroke.paint.server(), - NR::Matrix(&gc->transform), NR::Matrix(&gc->parent->transform), + gc->transform, gc->parent->transform, &shape->paintbox); } item->render_opacity = FALSE; } - if ( item->render_opacity == TRUE - && shape->_fill.paint.type() != NRArenaShape::Paint::NONE - && shape->_stroke.paint.type() != NRArenaShape::Paint::NONE ) + if ( (shape->_fill.paint.type() != NRArenaShape::Paint::NONE && + shape->_stroke.paint.type() != NRArenaShape::Paint::NONE) + || (shape->markers) + ) { - // don't merge item opacity with paint opacity if there is a stroke on the fill + // don't merge item opacity with paint opacity if there is a stroke on the fill, or markers on stroke item->render_opacity = FALSE; } @@ -355,8 +401,8 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g return NR_ARENA_ITEM_STATE_ALL; } -int matrix_is_isometry(NR::Matrix p) { - NR::Matrix tp; +int matrix_is_isometry(Geom::Matrix p) { + Geom::Matrix tp; // transposition tp[0]=p[0]; tp[1]=p[2]; @@ -364,9 +410,9 @@ int matrix_is_isometry(NR::Matrix p) { tp[3]=p[3]; for (int i = 4; i < 6; i++) // shut valgrind up :) tp[i] = p[i] = 0; - NR::Matrix isom = tp*p; // A^T * A = adjunct? + Geom::Matrix isom = tp*p; // A^T * A = adjunct? // Is the adjunct nearly an identity function? - if (isom.is_translation(0.01)) { + if (isom.isTranslation(0.01)) { // the transformation is an isometry -> no need to recompute // the uncrossed polygon if ( p.det() < 0 ) @@ -377,19 +423,47 @@ int matrix_is_isometry(NR::Matrix p) { return 0; } +static bool is_inner_area(NRRectL const &outer, NRRectL const &inner) { + return (outer.x0 <= inner.x0 && outer.y0 <= inner.y0 && outer.x1 >= inner.x1 && outer.y1 >= inner.y1); +} + +/* returns true if the pathvector has a region that needs fill. + * is for optimizing purposes, so should be fast and can falsely return true. + * CANNOT falsely return false. */ +static bool has_inner_area(Geom::PathVector const & pv) { + // return false for the cases where there is surely no region to be filled + if (pv.empty()) + return false; + + if ( (pv.size() == 1) && (pv.front().size() <= 1) ) { + // vector has only one path with only one segment, see if that's a non-curve segment: that would mean no internal region + if ( is_straight_curve(pv.front().front()) ) + { + return false; + } + } + + return true; //too costly to see if it has region to be filled, so return true. +} + /** force_shape is used for clipping paths, when we need the shape for clipping even if it's not filled */ void -nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, bool force_shape) +nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, NRRectL *area, bool force_shape) { - shape->delayed_shp = false; if ((shape->_fill.paint.type() != NRArenaShape::Paint::NONE || force_shape) && - ((shape->curve->end > 2) || (shape->curve->bpath[1].code == NR_CURVETO)) ) { - if (TRUE || !shape->fill_shp) { - NR::Matrix cached_to_new; + has_inner_area(shape->curve->get_pathvector()) ) { + + Geom::Matrix cached_to_new = Geom::identity(); int isometry = 0; if ( shape->cached_fill ) { - cached_to_new = shape->cached_fctm.inverse()*gc->transform; - isometry = matrix_is_isometry(cached_to_new); + if (shape->cached_fctm == gc->transform) { + isometry = 2; // identity + } else { + cached_to_new = shape->cached_fctm.inverse() * gc->transform; + isometry = matrix_is_isometry(cached_to_new); + } + if (0 != isometry && !is_inner_area(shape->cached_farea, *area)) + isometry = 0; } if ( isometry == 0 ) { if ( shape->cached_fill == NULL ) shape->cached_fill=new Shape; @@ -398,11 +472,17 @@ nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, bool force_shape) Path* thePath=new Path; Shape* theShape=new Shape; { - NR::Matrix tempMat(gc->transform); - thePath->LoadArtBPath(shape->curve->bpath,tempMat,true); + Geom::Matrix tempMat(gc->transform); + thePath->LoadPathVector(shape->curve->get_pathvector(), tempMat, true); } - thePath->Convert(1.0); + if (is_inner_area(*area, NR_ARENA_ITEM(shape)->bbox)) { + thePath->Convert(1.0); + shape->cached_fpartialy = false; + } else { + thePath->Convert(area, 1.0); + shape->cached_fpartialy = true; + } thePath->Fill(theShape, 0); @@ -414,6 +494,7 @@ nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, bool force_shape) shape->cached_fill->ConvertToShape(theShape, fill_nonZero); } shape->cached_fctm=gc->transform; + shape->cached_farea = *area; delete theShape; delete thePath; if ( shape->fill_shp == NULL ) @@ -421,6 +502,11 @@ nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, bool force_shape) shape->fill_shp->Copy(shape->cached_fill); + } else if ( 2 == isometry ) { + if ( shape->fill_shp == NULL ) { + shape->fill_shp = new Shape; + shape->fill_shp->Copy(shape->cached_fill); + } } else { if ( shape->fill_shp == NULL ) @@ -429,7 +515,7 @@ nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, bool force_shape) shape->fill_shp->Reset(shape->cached_fill->numberOfPoints(), shape->cached_fill->numberOfEdges()); for (int i = 0; i < shape->cached_fill->numberOfPoints(); i++) - shape->fill_shp->AddPoint(shape->cached_fill->getPoint(i).x * cached_to_new); + shape->fill_shp->AddPoint(to_2geom(shape->cached_fill->getPoint(i).x) * cached_to_new); if ( isometry == 1 ) { for (int i = 0; i < shape->cached_fill->numberOfEdges(); i++) shape->fill_shp->AddEdge(shape->cached_fill->getEdge(i).st, @@ -443,33 +529,55 @@ nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, bool force_shape) shape->fill_shp->needPointsSorting(); shape->fill_shp->needEdgesSorting(); } - } + shape->delayed_shp |= shape->cached_fpartialy; } } void -nr_arena_shape_update_stroke(NRArenaShape *shape,NRGC* gc) +nr_arena_shape_update_stroke(NRArenaShape *shape,NRGC* gc, NRRectL *area) { SPStyle* style = shape->style; - shape->delayed_shp = false; + float const scale = gc->transform.descrim(); - float const scale = NR_MATRIX_DF_EXPANSION(&gc->transform); + bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); - if (NR_ARENA_ITEM(shape)->arena->rendermode == RENDERMODE_OUTLINE || + if (outline) { + // cairo does not need the livarot path for rendering + return; + } + + // after switching normal stroke rendering to cairo too, optimize this: lower tolerance, disregard dashes + // (since it will only be used for picking, not for rendering) + + if (outline || ((shape->_stroke.paint.type() != NRArenaShape::Paint::NONE) && ( fabs(shape->_stroke.width * scale) > 0.01 ))) { // sinon c'est 0=oon veut pas de bord - float width = MAX(0.125, shape->_stroke.width * scale); - if (NR_ARENA_ITEM(shape)->arena->rendermode == RENDERMODE_OUTLINE) + float style_width = MAX(0.125, shape->_stroke.width * scale); + float width; + if (outline) { width = 0.5; // 1 pixel wide, independent of zoom + } else { + width = style_width; + } - NR::Matrix cached_to_new; + Geom::Matrix cached_to_new = Geom::identity(); int isometry = 0; if ( shape->cached_stroke ) { - cached_to_new = shape->cached_sctm.inverse() * gc->transform; - isometry = matrix_is_isometry(cached_to_new); + if (shape->cached_sctm == gc->transform) { + isometry = 2; // identity + } else { + cached_to_new = shape->cached_sctm.inverse() * gc->transform; + isometry = matrix_is_isometry(cached_to_new); + } + if (0 != isometry && !is_inner_area(shape->cached_sarea, *area)) + isometry = 0; + if (0 != isometry && width != shape->cached_width) { + // if this happens without setting style, we have just switched to outline or back + isometry = 0; + } } if ( isometry == 0 ) { @@ -478,41 +586,29 @@ nr_arena_shape_update_stroke(NRArenaShape *shape,NRGC* gc) Path* thePath = new Path; Shape* theShape = new Shape; { - NR::Matrix tempMat(gc->transform); - thePath->LoadArtBPath(shape->curve->bpath, tempMat, true); + Geom::Matrix tempMat( gc->transform ); + thePath->LoadPathVector(shape->curve->get_pathvector(), tempMat, true); } - if (NR_ARENA_ITEM(shape)->arena->rendermode != RENDERMODE_OUTLINE) - thePath->Convert(1.0); - else - thePath->Convert(4.0); // slightly rougher & faster + // add some padding to the rendering area, so clipped path does not go into a render area + NRRectL padded_area = *area; + padded_area.x0 -= (NR::ICoord)width; + padded_area.x1 += (NR::ICoord)width; + padded_area.y0 -= (NR::ICoord)width; + padded_area.y1 += (NR::ICoord)width; + if ((style->stroke_dash.n_dash && !outline) || is_inner_area(padded_area, NR_ARENA_ITEM(shape)->bbox)) { + thePath->Convert((outline) ? 4.0 : 1.0); + shape->cached_spartialy = false; + } + else { + thePath->Convert(&padded_area, (outline) ? 4.0 : 1.0); + shape->cached_spartialy = true; + } - if (style->stroke_dash.n_dash && NR_ARENA_ITEM(shape)->arena->rendermode != RENDERMODE_OUTLINE) { - double dlen = 0.0; - for (int i = 0; i < style->stroke_dash.n_dash; i++) { - dlen += style->stroke_dash.dash[i] * scale; - } - if (dlen >= 1.0) { - NRVpathDash dash; - dash.offset = style->stroke_dash.offset * scale; - dash.n_dash = style->stroke_dash.n_dash; - dash.dash = g_new(double, dash.n_dash); - for (int i = 0; i < dash.n_dash; i++) { - dash.dash[i] = style->stroke_dash.dash[i] * scale; - } - int nbD=dash.n_dash; - float *dashs=(float*)malloc((nbD+1)*sizeof(float)); - while ( dash.offset >= dlen ) dash.offset-=dlen; - dashs[0]=dash.dash[0]; - for (int i=1; iDashPolyline(0.0,0.0,dlen,nbD,dashs,true,dash.offset); - free(dashs); - g_free(dash.dash); - } + if (style->stroke_dash.n_dash && !outline) { + thePath->DashPolylineFromStyle(style, scale, 1.0); } + ButtType butt=butt_straight; switch (shape->_stroke.cap) { case NRArenaShape::BUTT_CAP: @@ -538,7 +634,7 @@ nr_arena_shape_update_stroke(NRArenaShape *shape,NRGC* gc) break; } - if (NR_ARENA_ITEM(shape)->arena->rendermode == RENDERMODE_OUTLINE) { + if (outline) { butt = butt_straight; join = join_straight; } @@ -547,27 +643,34 @@ nr_arena_shape_update_stroke(NRArenaShape *shape,NRGC* gc) 0.5*width*shape->_stroke.mitre_limit); - if (NR_ARENA_ITEM(shape)->arena->rendermode == RENDERMODE_OUTLINE) { + if (outline) { // speeds it up, but uses evenodd for the stroke shape (which does not matter for 1-pixel wide outline) shape->cached_stroke->Copy(theShape); } else { shape->cached_stroke->ConvertToShape(theShape, fill_nonZero); } + shape->cached_width = width; + shape->cached_sctm=gc->transform; + shape->cached_sarea = *area; delete thePath; delete theShape; if ( shape->stroke_shp == NULL ) shape->stroke_shp=new Shape; shape->stroke_shp->Copy(shape->cached_stroke); + } else if ( 2 == isometry ) { + if ( shape->stroke_shp == NULL ) { + shape->stroke_shp=new Shape; + shape->stroke_shp->Copy(shape->cached_stroke); + } } else { - if ( shape->stroke_shp == NULL ) shape->stroke_shp=new Shape; shape->stroke_shp->Reset(shape->cached_stroke->numberOfPoints(), shape->cached_stroke->numberOfEdges()); for (int i = 0; i < shape->cached_stroke->numberOfPoints(); i++) - shape->stroke_shp->AddPoint(shape->cached_stroke->getPoint(i).x * cached_to_new); + shape->stroke_shp->AddPoint(to_2geom(shape->cached_stroke->getPoint(i).x) * cached_to_new); if ( isometry == 1 ) { for (int i = 0; i < shape->cached_stroke->numberOfEdges(); i++) shape->stroke_shp->AddEdge(shape->cached_stroke->getEdge(i).st, @@ -581,82 +684,182 @@ nr_arena_shape_update_stroke(NRArenaShape *shape,NRGC* gc) shape->stroke_shp->needPointsSorting(); shape->stroke_shp->needEdgesSorting(); } + shape->delayed_shp |= shape->cached_spartialy; } } void -nr_arena_shape_add_bboxes(NRArenaShape* shape, NRRect &bbox) +nr_arena_shape_add_bboxes(NRArenaShape* shape, Geom::OptRect &bbox) { + /* TODO: are these two if's mutually exclusive? ( i.e. "shape->stroke_shp <=> !shape->fill_shp" ) + * if so, then this can be written much more compact ! */ + if ( shape->stroke_shp ) { - shape->stroke_shp->CalcBBox(); - shape->stroke_shp->leftX=floor(shape->stroke_shp->leftX); - shape->stroke_shp->rightX=ceil(shape->stroke_shp->rightX); - shape->stroke_shp->topY=floor(shape->stroke_shp->topY); - shape->stroke_shp->bottomY=ceil(shape->stroke_shp->bottomY); - if ( bbox.x0 >= bbox.x1 ) { - if ( shape->stroke_shp->leftX < shape->stroke_shp->rightX ) { - bbox.x0=shape->stroke_shp->leftX; - bbox.x1=shape->stroke_shp->rightX; - } - } else { - if ( shape->stroke_shp->leftX < bbox.x0 ) - bbox.x0=shape->stroke_shp->leftX; - if ( shape->stroke_shp->rightX > bbox.x1 ) - bbox.x1=shape->stroke_shp->rightX; - } - if ( bbox.y0 >= bbox.y1 ) { - if ( shape->stroke_shp->topY < shape->stroke_shp->bottomY ) { - bbox.y0=shape->stroke_shp->topY; - bbox.y1=shape->stroke_shp->bottomY; - } - } else { - if ( shape->stroke_shp->topY < bbox.y0 ) - bbox.y0=shape->stroke_shp->topY; - if ( shape->stroke_shp->bottomY > bbox.y1 ) - bbox.y1=shape->stroke_shp->bottomY; - } + Shape *larger = shape->stroke_shp; + larger->CalcBBox(); + larger->leftX = floor(larger->leftX); + larger->rightX = ceil(larger->rightX); + larger->topY = floor(larger->topY); + larger->bottomY = ceil(larger->bottomY); + Geom::Rect stroke_bbox( Geom::Interval(larger->leftX, larger->rightX), + Geom::Interval(larger->topY, larger->bottomY) ); + bbox.unionWith(stroke_bbox); } + if ( shape->fill_shp ) { - shape->fill_shp->CalcBBox(); - shape->fill_shp->leftX=floor(shape->fill_shp->leftX); - shape->fill_shp->rightX=ceil(shape->fill_shp->rightX); - shape->fill_shp->topY=floor(shape->fill_shp->topY); - shape->fill_shp->bottomY=ceil(shape->fill_shp->bottomY); - if ( bbox.x0 >= bbox.x1 ) { - if ( shape->fill_shp->leftX < shape->fill_shp->rightX ) { - bbox.x0=shape->fill_shp->leftX; - bbox.x1=shape->fill_shp->rightX; - } - } else { - if ( shape->fill_shp->leftX < bbox.x0 ) bbox.x0=shape->fill_shp->leftX; - if ( shape->fill_shp->rightX > bbox.x1 ) bbox.x1=shape->fill_shp->rightX; - } - if ( bbox.y0 >= bbox.y1 ) { - if ( shape->fill_shp->topY < shape->fill_shp->bottomY ) { - bbox.y0=shape->fill_shp->topY; - bbox.y1=shape->fill_shp->bottomY; - } - } else { - if ( shape->fill_shp->topY < bbox.y0 ) bbox.y0=shape->fill_shp->topY; - if ( shape->fill_shp->bottomY > bbox.y1 ) bbox.y1=shape->fill_shp->bottomY; + Shape *larger = shape->fill_shp; + larger->CalcBBox(); + larger->leftX = floor(larger->leftX); + larger->rightX = ceil(larger->rightX); + larger->topY = floor(larger->topY); + larger->bottomY = ceil(larger->bottomY); + Geom::Rect fill_bbox( Geom::Interval(larger->leftX, larger->rightX), + Geom::Interval(larger->topY, larger->bottomY) ); + bbox.unionWith(fill_bbox); + } +} + +// cairo outline rendering: +static unsigned int +cairo_arena_shape_render_outline(cairo_t *ct, NRArenaItem *item, Geom::OptRect area) +{ + NRArenaShape *shape = NR_ARENA_SHAPE(item); + + if (!ct) + return item->state; + + guint32 rgba = NR_ARENA_ITEM(shape)->arena->outlinecolor; + // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat + // by setting color channels in the "wrong" order + 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)); + + cairo_set_line_width(ct, 0.5); + cairo_set_tolerance(ct, 1.25); // low quality, but good enough for outline mode + cairo_new_path(ct); + + feed_pathvector_to_cairo (ct, shape->curve->get_pathvector(), shape->ctm, area, true, 0); + + cairo_stroke(ct); + + return item->state; +} + +// cairo stroke rendering (flat color only so far!): +// works on canvas, but wrongs the colors in nonpremul buffers: icons and png export +// (need to switch them to premul before this can be enabled) +void +cairo_arena_shape_render_stroke(NRArenaItem *item, NRRectL *area, NRPixBlock *pb) +{ + NRArenaShape *shape = NR_ARENA_SHAPE(item); + SPStyle const *style = shape->style; + + float const scale = shape->ctm.descrim(); + + if (fabs(shape->_stroke.width * scale) < 0.01) + return; + + cairo_t *ct = nr_create_cairo_context (area, pb); + + if (!ct) + return; + + guint32 rgba; + if ( item->render_opacity ) { + rgba = shape->_stroke.paint.color().toRGBA32( shape->_stroke.opacity * + SP_SCALE24_TO_FLOAT(style->opacity.value) ); + } else { + rgba = shape->_stroke.paint.color().toRGBA32( shape->_stroke.opacity ); + } + + // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat + // by setting color channels in the "wrong" order + 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)); + + float style_width = MAX(0.125, shape->_stroke.width * scale); + cairo_set_line_width(ct, style_width); + + switch (shape->_stroke.cap) { + case NRArenaShape::BUTT_CAP: + cairo_set_line_cap(ct, CAIRO_LINE_CAP_BUTT); + break; + case NRArenaShape::ROUND_CAP: + cairo_set_line_cap(ct, CAIRO_LINE_CAP_ROUND); + break; + case NRArenaShape::SQUARE_CAP: + cairo_set_line_cap(ct, CAIRO_LINE_CAP_SQUARE); + break; + } + switch (shape->_stroke.join) { + case NRArenaShape::MITRE_JOIN: + cairo_set_line_join(ct, CAIRO_LINE_JOIN_MITER); + break; + case NRArenaShape::ROUND_JOIN: + cairo_set_line_join(ct, CAIRO_LINE_JOIN_ROUND); + break; + case NRArenaShape::BEVEL_JOIN: + cairo_set_line_join(ct, CAIRO_LINE_JOIN_BEVEL); + break; + } + + cairo_set_miter_limit (ct, style->stroke_miterlimit.value); + + if (style->stroke_dash.n_dash) { + NRVpathDash dash; + dash.offset = style->stroke_dash.offset * scale; + dash.n_dash = style->stroke_dash.n_dash; + dash.dash = g_new(double, dash.n_dash); + for (int i = 0; i < dash.n_dash; i++) { + dash.dash[i] = style->stroke_dash.dash[i] * scale; } + cairo_set_dash (ct, dash.dash, dash.n_dash, dash.offset); + g_free(dash.dash); } + + cairo_set_tolerance(ct, 0.1); + cairo_new_path(ct); + + feed_pathvector_to_cairo (ct, shape->curve->get_pathvector(), shape->ctm, to_2geom(area->upgrade()), true, style_width); + + cairo_stroke(ct); + + cairo_surface_t *cst = cairo_get_target(ct); + cairo_destroy (ct); + cairo_surface_finish (cst); + cairo_surface_destroy (cst); + + pb->empty = FALSE; } + +/** + * Renders the item. Markers are just composed into the parent buffer. + */ static unsigned int -nr_arena_shape_render(NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags) +nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags) { NRArenaShape *shape = NR_ARENA_SHAPE(item); if (!shape->curve) return item->state; if (!shape->style) return item->state; + bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); + bool print_colors_preview = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW); + + if (outline) { // cairo outline rendering + + pb->empty = FALSE; + unsigned int ret = cairo_arena_shape_render_outline (ct, item, to_2geom((&pb->area)->upgrade())); + if (ret & NR_ARENA_ITEM_STATE_INVALID) return ret; + + } else { + if ( shape->delayed_shp ) { - if ( nr_rect_l_test_intersect(area, &item->bbox) ) { + if ( nr_rect_l_test_intersect_ptr(area, &item->bbox) ) { NRGC tempGC(NULL); tempGC.transform=shape->ctm; - nr_arena_shape_update_stroke(shape,&tempGC); - nr_arena_shape_update_fill(shape,&tempGC); + shape->delayed_shp = false; + nr_arena_shape_update_stroke(shape,&tempGC,&pb->visible_area); + nr_arena_shape_update_fill(shape,&tempGC,&pb->visible_area); /* NRRect bbox; bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0.0; nr_arena_shape_add_bboxes(shape,bbox); @@ -665,29 +868,44 @@ nr_arena_shape_render(NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned item->bbox.x1 = (gint32)(bbox.x1 + 1.0F); item->bbox.y1 = (gint32)(bbox.y1 + 1.0F); shape->approx_bbox=item->bbox;*/ + } else { + return item->state; } } SPStyle const *style = shape->style; - if ( shape->fill_shp && NR_ARENA_ITEM(shape)->arena->rendermode != RENDERMODE_OUTLINE) { + + if (shape->fill_shp) { NRPixBlock m; guint32 rgba; nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE); + + // if memory allocation failed, abort render + if (m.size != NR_PIXBLOCK_SIZE_TINY && m.data.px == NULL) { + nr_pixblock_release (&m); + return (item->state); + } + + m.visible_area = pb->visible_area; nr_pixblock_render_shape_mask_or(m,shape->fill_shp); m.empty = FALSE; if (shape->_fill.paint.type() == NRArenaShape::Paint::NONE) { // do not render fill in any way } else if (shape->_fill.paint.type() == NRArenaShape::Paint::COLOR) { + + const SPColor* fill_color = &shape->_fill.paint.color(); if ( item->render_opacity ) { - rgba = sp_color_get_rgba32_falpha(&shape->_fill.paint.color(), - shape->_fill.opacity * - SP_SCALE24_TO_FLOAT(style->opacity.value)); + rgba = fill_color->toRGBA32( shape->_fill.opacity * + SP_SCALE24_TO_FLOAT(style->opacity.value) ); } else { - rgba = sp_color_get_rgba32_falpha(&shape->_fill.paint.color(), - shape->_fill.opacity); + rgba = fill_color->toRGBA32( shape->_fill.opacity ); } + + if (print_colors_preview) + nr_arena_separate_color_plates(&rgba); + nr_blit_pixblock_mask_rgba32(pb, &m, rgba); pb->empty = FALSE; } else if (shape->_fill.paint.type() == NRArenaShape::Paint::SERVER) { @@ -699,58 +917,127 @@ nr_arena_shape_render(NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned nr_pixblock_release(&m); } - if ( shape->stroke_shp ) { - NRPixBlock m; + if (shape->stroke_shp && shape->_stroke.paint.type() == NRArenaShape::Paint::COLOR) { + + // cairo_arena_shape_render_stroke(item, area, pb); + guint32 rgba; + NRPixBlock m; nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE); + + // if memory allocation failed, abort render + if (m.size != NR_PIXBLOCK_SIZE_TINY && m.data.px == NULL) { + nr_pixblock_release (&m); + return (item->state); + } + + m.visible_area = pb->visible_area; nr_pixblock_render_shape_mask_or(m, shape->stroke_shp); m.empty = FALSE; - if (shape->_stroke.paint.type() == NRArenaShape::Paint::COLOR || - NR_ARENA_ITEM(shape)->arena->rendermode == RENDERMODE_OUTLINE) { - if ( NR_ARENA_ITEM(shape)->arena->rendermode == RENDERMODE_OUTLINE) { - rgba = NR_ARENA_ITEM(shape)->arena->outlinecolor; - } else if ( item->render_opacity ) { - rgba = sp_color_get_rgba32_falpha(&shape->_stroke.paint.color(), - shape->_stroke.opacity * - SP_SCALE24_TO_FLOAT(style->opacity.value)); - } else { - rgba = sp_color_get_rgba32_falpha(&shape->_stroke.paint.color(), - shape->_stroke.opacity); - } - nr_blit_pixblock_mask_rgba32(pb, &m, rgba); - pb->empty = FALSE; - } else if (shape->_stroke.paint.type() == NRArenaShape::Paint::SERVER) { - if (shape->stroke_painter) { - nr_arena_render_paintserver_fill(pb, area, shape->stroke_painter, shape->_stroke.opacity, &m); - } + const SPColor* stroke_color = &shape->_stroke.paint.color(); + if ( item->render_opacity ) { + rgba = stroke_color->toRGBA32( shape->_stroke.opacity * + SP_SCALE24_TO_FLOAT(style->opacity.value) ); + } else { + rgba = stroke_color->toRGBA32( shape->_stroke.opacity ); + } + + if (print_colors_preview) + nr_arena_separate_color_plates(&rgba); + + nr_blit_pixblock_mask_rgba32(pb, &m, rgba); + pb->empty = FALSE; + + nr_pixblock_release(&m); + + } else if (shape->stroke_shp && shape->_stroke.paint.type() == NRArenaShape::Paint::SERVER) { + + NRPixBlock m; + + nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE); + + // if memory allocation failed, abort render + if (m.size != NR_PIXBLOCK_SIZE_TINY && m.data.px == NULL) { + nr_pixblock_release (&m); + return (item->state); + } + + m.visible_area = pb->visible_area; + nr_pixblock_render_shape_mask_or(m, shape->stroke_shp); + m.empty = FALSE; + + if (shape->stroke_painter) { + nr_arena_render_paintserver_fill(pb, area, shape->stroke_painter, shape->_stroke.opacity, &m); } nr_pixblock_release(&m); } - /* Just compose children into parent buffer */ + } // non-cairo non-outline branch + + /* Render markers into parent buffer */ for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) { - unsigned int ret; - ret = nr_arena_item_invoke_render(child, area, pb, flags); + unsigned int ret = nr_arena_item_invoke_render(ct, child, area, pb, flags); if (ret & NR_ARENA_ITEM_STATE_INVALID) return ret; } return item->state; } + +// cairo clipping: this basically works except for the stride-must-be-divisible-by-4 cairo bug; +// reenable this when the bug is fixed and remove the rest of this function +// TODO +#if defined(DEADCODE) && !defined(DEADCODE) +static guint +cairo_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb) +{ + NRArenaShape *shape = NR_ARENA_SHAPE(item); + if (!shape->curve) return item->state; + + cairo_t *ct = nr_create_cairo_context (area, pb); + + if (!ct) + return item->state; + + cairo_set_source_rgba(ct, 0, 0, 0, 1); + + cairo_new_path(ct); + + feed_pathvector_to_cairo (ct, shape->curve->get_pathvector(), shape->ctm, (area)->upgrade(), false, 0); + + cairo_fill(ct); + + cairo_surface_t *cst = cairo_get_target(ct); + cairo_destroy (ct); + cairo_surface_finish (cst); + cairo_surface_destroy (cst); + + pb->empty = FALSE; + + return item->state; +} +#endif //defined(DEADCODE) && !defined(DEADCODE) + + static guint nr_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb) { + //return cairo_arena_shape_clip(item, area, pb); + NRArenaShape *shape = NR_ARENA_SHAPE(item); if (!shape->curve) return item->state; if ( shape->delayed_shp || shape->fill_shp == NULL) { // we need a fill shape no matter what - if ( nr_rect_l_test_intersect(area, &item->bbox) ) { + if ( nr_rect_l_test_intersect_ptr(area, &item->bbox) ) { NRGC tempGC(NULL); tempGC.transform=shape->ctm; - nr_arena_shape_update_fill(shape, &tempGC, true); + shape->delayed_shp = false; + nr_arena_shape_update_fill(shape, &tempGC, &pb->visible_area, true); + } else { + return item->state; } } @@ -759,6 +1046,14 @@ nr_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb) /* fixme: We can OR in one step (Lauris) */ nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE); + + // if memory allocation failed, abort + if (m.size != NR_PIXBLOCK_SIZE_TINY && m.data.px == NULL) { + nr_pixblock_release (&m); + return (item->state); + } + + m.visible_area = pb->visible_area; nr_pixblock_render_shape_mask_or(m,shape->fill_shp); for (int y = area->y0; y < area->y1; y++) { @@ -766,7 +1061,7 @@ nr_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb) s = NR_PIXBLOCK_PX(&m) + (y - area->y0) * m.rs; d = NR_PIXBLOCK_PX(pb) + (y - area->y0) * pb->rs; for (int x = area->x0; x < area->x1; x++) { - *d = NR_A7_NORMALIZED(*s,*d); + *d = NR_COMPOSEA_111(*s, *d); d ++; s ++; } @@ -779,74 +1074,93 @@ nr_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb) } static NRArenaItem * -nr_arena_shape_pick(NRArenaItem *item, NR::Point p, double delta, unsigned int /*sticky*/) +nr_arena_shape_pick(NRArenaItem *item, Geom::Point p, double delta, unsigned int /*sticky*/) { NRArenaShape *shape = NR_ARENA_SHAPE(item); + if (shape->repick_after > 0) + shape->repick_after--; + + if (shape->repick_after > 0) // we are a slow, huge path. skip this pick, returning what was returned last time + return shape->last_pick; + if (!shape->curve) return NULL; if (!shape->style) return NULL; - if ( shape->delayed_shp ) { - NRRectL area; - area.x0=(int)floor(p[NR::X]); - area.x1=(int)ceil(p[NR::X]); - area.y0=(int)floor(p[NR::Y]); - area.y1=(int)ceil(p[NR::Y]); - int idelta = (int)ceil(delta) + 1; - // njh: inset rect - area.x0-=idelta; - area.x1+=idelta; - area.y0-=idelta; - area.y1+=idelta; - if ( nr_rect_l_test_intersect(&area, &item->bbox) ) { - NRGC tempGC(NULL); - tempGC.transform=shape->ctm; - nr_arena_shape_update_stroke(shape,&tempGC); - nr_arena_shape_update_fill(shape,&tempGC); - /* NRRect bbox; - bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0.0; - nr_arena_shape_add_bboxes(shape,bbox); - item->bbox.x0 = (gint32)(bbox.x0 - 1.0F); - item->bbox.y0 = (gint32)(bbox.y0 - 1.0F); - item->bbox.x1 = (gint32)(bbox.x1 + 1.0F); - item->bbox.y1 = (gint32)(bbox.y1 + 1.0F); - shape->approx_bbox=item->bbox;*/ - } + + bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); + + if (SP_SCALE24_TO_FLOAT(shape->style->opacity.value) == 0 && !outline) + // fully transparent, no pick unless outline mode + return NULL; + + GTimeVal tstart, tfinish; + g_get_current_time (&tstart); + + double width; + if (outline) { + width = 0.5; + } else if (shape->_stroke.paint.type() != NRArenaShape::Paint::NONE && shape->_stroke.opacity > 1e-3) { + float const scale = shape->ctm.descrim(); + width = MAX(0.125, shape->_stroke.width * scale) / 2; + } else { + width = 0; } - if (item->state & NR_ARENA_ITEM_STATE_RENDER) { - if (shape->fill_shp && (shape->_fill.paint.type() != NRArenaShape::Paint::NONE)) { - if (shape->fill_shp->PtWinding(p) > 0 ) return item; - } - if (shape->stroke_shp && (shape->_stroke.paint.type() != NRArenaShape::Paint::NONE)) { - if (shape->stroke_shp->PtWinding(p) > 0 ) return item; - } - if (delta > 1e-3) { - if (shape->fill_shp && (shape->_fill.paint.type() != NRArenaShape::Paint::NONE)) { - if (distanceLessThanOrEqual(shape->fill_shp, p, delta)) return item; + double dist = NR_HUGE; + int wind = 0; + bool needfill = (shape->_fill.paint.type() != NRArenaShape::Paint::NONE + && shape->_fill.opacity > 1e-3 && !outline); + + if (item->arena->canvasarena) { + Geom::Rect viewbox = item->arena->canvasarena->item.canvas->getViewbox(); + viewbox.expandBy (width); + pathv_matrix_point_bbox_wind_distance(shape->curve->get_pathvector(), shape->ctm, p, NULL, needfill? &wind : NULL, &dist, 0.5, &viewbox); + } else { + pathv_matrix_point_bbox_wind_distance(shape->curve->get_pathvector(), shape->ctm, p, NULL, needfill? &wind : NULL, &dist, 0.5, NULL); + } + + g_get_current_time (&tfinish); + glong this_pick = (tfinish.tv_sec - tstart.tv_sec) * 1000000 + (tfinish.tv_usec - tstart.tv_usec); + //g_print ("pick time %lu\n", this_pick); + + if (this_pick > 10000) { // slow picking, remember to skip several new picks + shape->repick_after = this_pick / 5000; + } + + // covered by fill? + if (needfill) { + if (!shape->style->fill_rule.computed) { + if (wind != 0) { + shape->last_pick = item; + return item; } - if (shape->stroke_shp && (shape->_stroke.paint.type() != NRArenaShape::Paint::NONE)) { - if (distanceLessThanOrEqual(shape->stroke_shp, p, delta)) return item; + } else { + if (wind & 0x1) { + shape->last_pick = item; + return item; } } - } else { - NRBPath bp; - bp.path = shape->curve->bpath; - double dist = NR_HUGE; - int wind = 0; - nr_path_matrix_point_bbox_wind_distance(&bp, shape->ctm, p, NULL, &wind, &dist, NR_EPSILON); - if (shape->_fill.paint.type() != NRArenaShape::Paint::NONE) { - if (!shape->style->fill_rule.computed) { - if (wind != 0) return item; - } else { - if (wind & 0x1) return item; - } + } + + // close to the edge, as defined by strokewidth and delta? + // this ignores dashing (as if the stroke is solid) and always works as if caps are round + if (needfill || width > 0) { // if either fill or stroke visible, + if ((dist - width) < delta) { + shape->last_pick = item; + return item; } - if (shape->_stroke.paint.type() != NRArenaShape::Paint::NONE) { - /* fixme: We do not take stroke width into account here (Lauris) */ - if (dist < delta) return item; + } + + // if not picked on the shape itself, try its markers + for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) { + NRArenaItem *ret = nr_arena_item_invoke_pick(child, p, delta, 0); + if (ret) { + shape->last_pick = item; + return item; } } + shape->last_pick = NULL; return NULL; } @@ -877,13 +1191,13 @@ void nr_arena_shape_set_path(NRArenaShape *shape, SPCurve *curve,bool justTrans) nr_arena_item_request_render(NR_ARENA_ITEM(shape)); if (shape->curve) { - sp_curve_unref(shape->curve); + shape->curve->unref(); shape->curve = NULL; } if (curve) { shape->curve = curve; - sp_curve_ref(curve); + curve->ref(); } nr_arena_item_request_update(NR_ARENA_ITEM(shape), NR_ARENA_ITEM_STATE_ALL, FALSE); @@ -958,22 +1272,14 @@ nr_arena_shape_set_style(NRArenaShape *shape, SPStyle *style) if (shape->style) sp_style_unref(shape->style); shape->style = style; - switch (style->fill.type) { - case SP_PAINT_TYPE_NONE: { - shape->setFill(NULL); - break; - } - case SP_PAINT_TYPE_COLOR: { - shape->setFill(style->fill.value.color); - break; - } - case SP_PAINT_TYPE_PAINTSERVER: { - shape->setFill(style->fill.value.paint.server); - break; - } - default: { - g_assert_not_reached(); - } + if ( style->fill.isPaintserver() ) { + shape->setFill(style->getFillPaintServer()); + } else if ( style->fill.isColor() ) { + shape->setFill(style->fill.value.color); + } else if ( style->fill.isNone() ) { + shape->setFill(NULL); + } else { + g_assert_not_reached(); } shape->setFillOpacity(SP_SCALE24_TO_FLOAT(style->fill_opacity.value)); switch (style->fill_rule.computed) { @@ -990,22 +1296,14 @@ nr_arena_shape_set_style(NRArenaShape *shape, SPStyle *style) } } - switch (style->stroke.type) { - case SP_PAINT_TYPE_NONE: { - shape->setStroke(NULL); - break; - } - case SP_PAINT_TYPE_COLOR: { - shape->setStroke(style->stroke.value.color); - break; - } - case SP_PAINT_TYPE_PAINTSERVER: { - shape->setStroke(style->stroke.value.paint.server); - break; - } - default: { - g_assert_not_reached(); - } + if ( style->stroke.isPaintserver() ) { + shape->setStroke(style->getStrokePaintServer()); + } else if ( style->stroke.isColor() ) { + shape->setStroke(style->stroke.value.color); + } else if ( style->stroke.isNone() ) { + shape->setStroke(NULL); + } else { + g_assert_not_reached(); } shape->setStrokeWidth(style->stroke_width.computed); shape->setStrokeOpacity(SP_SCALE24_TO_FLOAT(style->stroke_opacity.value)); @@ -1045,6 +1343,19 @@ nr_arena_shape_set_style(NRArenaShape *shape, SPStyle *style) } shape->setMitreLimit(style->stroke_miterlimit.value); + //if shape has a filter + if (style->filter.set && style->getFilter()) { + if (!shape->filter) { + int primitives = sp_filter_primitive_count(SP_FILTER(style->getFilter())); + shape->filter = new Inkscape::Filters::Filter(primitives); + } + sp_filter_build_renderer(SP_FILTER(style->getFilter()), shape->filter); + } else { + //no filter set for this shape + delete shape->filter; + shape->filter = NULL; + } + nr_arena_item_request_update(shape, NR_ARENA_ITEM_STATE_ALL, FALSE); } @@ -1066,12 +1377,12 @@ nr_arena_shape_set_paintbox(NRArenaShape *shape, NRRect const *pbox) nr_arena_item_request_update(shape, NR_ARENA_ITEM_STATE_ALL, FALSE); } -void NRArenaShape::setPaintBox(NR::Rect const &pbox) +void NRArenaShape::setPaintBox(Geom::Rect const &pbox) { - paintbox.x0 = pbox.min()[NR::X]; - paintbox.y0 = pbox.min()[NR::Y]; - paintbox.x1 = pbox.max()[NR::X]; - paintbox.y1 = pbox.max()[NR::Y]; + paintbox.x0 = pbox.min()[Geom::X]; + paintbox.y0 = pbox.min()[Geom::Y]; + paintbox.x1 = pbox.max()[Geom::X]; + paintbox.y1 = pbox.max()[Geom::Y]; nr_arena_item_request_update(this, NR_ARENA_ITEM_STATE_ALL, FALSE); } @@ -1102,10 +1413,8 @@ shape_run_A8_OR(raster_info &dest,void */*data*/,int st,float vst,int en,float v unsigned int c0_24=(int)sv; c0_24&=0xFF; while (len > 0) { - unsigned int da; /* Draw */ - da = NR_A7(c0_24,d[0]); - d[0] = NR_PREMUL_SINGLE(da); + d[0] = NR_COMPOSEA_111(c0_24,d[0]); d += 1; len -= 1; } @@ -1116,10 +1425,8 @@ shape_run_A8_OR(raster_info &dest,void */*data*/,int st,float vst,int en,float v sv*=256; unsigned int c0_24=(int)sv; c0_24&=0xFF; - unsigned int da; /* Draw */ - da = NR_A7(c0_24,d[0]); - d[0] = NR_PREMUL_SINGLE(da); + d[0] = NR_COMPOSEA_111(c0_24,d[0]); } else { dv/=len; sv+=0.5*dv; // correction trapezoidale @@ -1128,12 +1435,11 @@ shape_run_A8_OR(raster_info &dest,void */*data*/,int st,float vst,int en,float v int c0_24 = static_cast(CLAMP(sv, 0, 16777216)); int s0_24 = static_cast(dv); while (len > 0) { - unsigned int ca, da; + unsigned int ca; /* Draw */ ca = c0_24 >> 16; if ( ca > 255 ) ca=255; - da = NR_A7(ca,d[0]); - d[0] = NR_PREMUL_SINGLE(da); + d[0] = NR_COMPOSEA_111(ca,d[0]); d += 1; c0_24 += s0_24; c0_24 = CLAMP(c0_24, 0, 16777216); @@ -1202,4 +1508,4 @@ void nr_pixblock_render_shape_mask_or(NRPixBlock &m,Shape* theS) fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :