diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2010-06-26 01:02:06 +0000 |
|---|---|---|
| committer | Krzysztof Kosiński <tweenk.pl@gmail.com> | 2010-06-26 01:02:06 +0000 |
| commit | 31f84a59da72b31b932833ce1af7d78f0a67e185 (patch) | |
| tree | f72a45ade3678183b262c7d92455e457373aed6e /src | |
| parent | Merge from trunk (diff) | |
| download | inkscape-31f84a59da72b31b932833ce1af7d78f0a67e185.tar.gz inkscape-31f84a59da72b31b932833ce1af7d78f0a67e185.zip | |
Implement clipping (slightly incorrect) and masking
(bzr r9508.1.4)
Diffstat (limited to 'src')
| -rw-r--r-- | src/display/Makefile_insert | 2 | ||||
| -rw-r--r-- | src/display/cairo-utils.cpp | 113 | ||||
| -rw-r--r-- | src/display/cairo-utils.h | 88 | ||||
| -rw-r--r-- | src/display/nr-arena-glyphs.cpp | 11 | ||||
| -rw-r--r-- | src/display/nr-arena-group.cpp | 6 | ||||
| -rw-r--r-- | src/display/nr-arena-item.cpp | 148 | ||||
| -rw-r--r-- | src/display/nr-arena-item.h | 4 | ||||
| -rw-r--r-- | src/display/nr-arena-shape.cpp | 838 | ||||
| -rw-r--r-- | src/display/nr-arena-shape.h | 68 | ||||
| -rw-r--r-- | src/display/sp-canvas.cpp | 38 |
10 files changed, 446 insertions, 870 deletions
diff --git a/src/display/Makefile_insert b/src/display/Makefile_insert index 58e667402..7660d2c70 100644 --- a/src/display/Makefile_insert +++ b/src/display/Makefile_insert @@ -33,6 +33,8 @@ ink_common_sources += \ display/canvas-temporary-item-list.h \ display/canvas-text.h \ display/canvas-text.cpp \ + display/cairo-utils.h \ + display/cairo-utils.cpp \ display/curve.cpp \ display/curve.h \ display/display-forward.h \ diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp new file mode 100644 index 000000000..58db5d551 --- /dev/null +++ b/src/display/cairo-utils.cpp @@ -0,0 +1,113 @@ +/* + * Helper functions to use cairo with inkscape + * + * Copyright (C) 2007 bulia byak + * Copyright (C) 2008 Johan Engelen + * + * Released under GNU GPL + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdexcept> +#include <cairo.h> +#include <2geom/matrix.h> +#include "display/cairo-utils.h" +#include "display/inkscape-cairo.h" +#include "color.h" + +namespace Inkscape { + +CairoGroup::CairoGroup(cairo_t *_ct) : ct(_ct), pushed(false) {} +CairoGroup::~CairoGroup() { + if (pushed) { + cairo_pattern_t *p = cairo_pop_group(ct); + cairo_pattern_destroy(p); + } +} +void CairoGroup::push() { + cairo_push_group(ct); + pushed = true; +} +void CairoGroup::push_with_content(cairo_content_t content) { + cairo_push_group_with_content(ct, content); + pushed = true; +} +cairo_pattern_t *CairoGroup::pop() { + if (pushed) { + cairo_pattern_t *ret = cairo_pop_group(ct); + pushed = false; + return ret; + } else { + throw std::logic_error("Cairo group popped without pushing it first"); + } +} +Cairo::RefPtr<Cairo::Pattern> CairoGroup::popmm() { + if (pushed) { + cairo_pattern_t *ret = cairo_pop_group(ct); + Cairo::RefPtr<Cairo::Pattern> retmm(new Cairo::Pattern(ret, true)); + pushed = false; + return retmm; + } else { + throw std::logic_error("Cairo group popped without pushing it first"); + } +} +void CairoGroup::pop_to_source() { + if (pushed) { + cairo_pop_group_to_source(ct); + pushed = false; + } +} + +CairoContext::CairoContext(cairo_t *obj, bool ref) + : Cairo::Context(obj, ref) +{} + +void CairoContext::transform(Geom::Matrix const &m) +{ + cairo_matrix_t cm; + cm.xx = m[0]; + cm.xy = m[2]; + cm.x0 = m[4]; + cm.yx = m[1]; + cm.yy = m[3]; + cm.y0 = m[5]; + cairo_transform(cobj(), &cm); +} + +void CairoContext::set_source_rgba32(guint32 color) +{ + double red = SP_RGBA32_R_F(color); + double gre = SP_RGBA32_G_F(color); + double blu = SP_RGBA32_B_F(color); + double alp = SP_RGBA32_A_F(color); + cairo_set_source_rgba(cobj(), red, gre, blu, alp); +} + +void CairoContext::append_path(Geom::PathVector const &pv) +{ + feed_pathvector_to_cairo(cobj(), pv); +} + +Cairo::RefPtr<CairoContext> CairoContext::create(Cairo::RefPtr<Cairo::Surface> const &target) +{ + cairo_t *ct = cairo_create(target->cobj()); + Cairo::RefPtr<CairoContext> ret(new CairoContext(ct, true)); + return ret; +} + +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/display/cairo-utils.h b/src/display/cairo-utils.h new file mode 100644 index 000000000..feed987bd --- /dev/null +++ b/src/display/cairo-utils.h @@ -0,0 +1,88 @@ +/** + * @file + * @brief Cairo integration helpers + *//* + * Authors: + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * Copyright (C) 2010 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_CAIRO_UTILS_H +#define SEEN_INKSCAPE_DISPLAY_CAIRO_UTILS_H + +#include <glib.h> +#include <cairomm/cairomm.h> +#include <2geom/forward.h> + +namespace Inkscape { + +/** @brief RAII idiom for Cairo groups. + * Groups are temporary surfaces used when rendering e.g. masks and opacity. + * Use this class to ensure that each group push is matched with a pop. */ +class CairoGroup { +public: + CairoGroup(cairo_t *_ct); + ~CairoGroup(); + void push(); + void push_with_content(cairo_content_t content); + cairo_pattern_t *pop(); + Cairo::RefPtr<Cairo::Pattern> popmm(); + void pop_to_source(); +private: + cairo_t *ct; + bool pushed; +}; + +/** @brief RAII idiom for Cairo state saving */ +class CairoSave { +public: + CairoSave(cairo_t *_ct, bool save=false) + : ct(_ct) + , saved(save) + { + if (save) { + cairo_save(ct); + } + } + void save() { + if (!saved) { + cairo_save(ct); + saved = true; + } + } + ~CairoSave() { + if (saved) + cairo_restore(ct); + } +private: + cairo_t *ct; + bool saved; +}; + +/** @brief Cairo context with Inkscape-specific operations */ +class CairoContext : public Cairo::Context { +public: + CairoContext(cairo_t *obj, bool ref = false); + + void transform(Geom::Matrix const &m); + void set_source_rgba32(guint32 color); + void append_path(Geom::PathVector const &pv); + + static Cairo::RefPtr<CairoContext> create(Cairo::RefPtr<Cairo::Surface> const &target); +}; + +} // namespace Inkscape + +#endif +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/display/nr-arena-glyphs.cpp b/src/display/nr-arena-glyphs.cpp index 33b08a91c..d229157ed 100644 --- a/src/display/nr-arena-glyphs.cpp +++ b/src/display/nr-arena-glyphs.cpp @@ -44,7 +44,7 @@ static void nr_arena_glyphs_init(NRArenaGlyphs *glyphs); static void nr_arena_glyphs_finalize(NRObject *object); static guint nr_arena_glyphs_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset); -static guint nr_arena_glyphs_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb); +static guint nr_arena_glyphs_clip(cairo_t *ct, NRArenaItem *item, NRRectL *area); static NRArenaItem *nr_arena_glyphs_pick(NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky); static NRArenaItemClass *glyphs_parent_class; @@ -220,7 +220,7 @@ nr_arena_glyphs_update(NRArenaItem *item, NRRectL */*area*/, NRGC *gc, guint /*s } static guint -nr_arena_glyphs_clip(NRArenaItem *item, NRRectL */*area*/, NRPixBlock */*pb*/) +nr_arena_glyphs_clip(cairo_t *ct, NRArenaItem *item, NRRectL */*area*/) { NRArenaGlyphs *glyphs; @@ -319,7 +319,7 @@ static void nr_arena_glyphs_group_finalize(NRObject *object); static guint nr_arena_glyphs_group_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset); static unsigned int nr_arena_glyphs_group_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags); -static unsigned int nr_arena_glyphs_group_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb); +static unsigned int nr_arena_glyphs_group_clip(cairo_t *ct, NRArenaItem *item, NRRectL *area); static NRArenaItem *nr_arena_glyphs_group_pick(NRArenaItem *item, Geom::Point p, gdouble delta, unsigned int sticky); static NRArenaGroupClass *group_parent_class; @@ -572,17 +572,18 @@ nr_arena_glyphs_group_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPi } static unsigned int -nr_arena_glyphs_group_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb) +nr_arena_glyphs_group_clip(cairo_t *ct, NRArenaItem *item, NRRectL *area) { NRArenaGroup *group = NR_ARENA_GROUP(item); guint ret = item->state; /* Render children fill mask */ + /* for (NRArenaItem *child = group->children; child != NULL; child = child->next) { ret = nr_arena_glyphs_fill_mask(NR_ARENA_GLYPHS(child), area, pb); if (!(ret & NR_ARENA_ITEM_STATE_RENDER)) return ret; - } + }*/ return ret; } diff --git a/src/display/nr-arena-group.cpp b/src/display/nr-arena-group.cpp index 38d37c233..0fa5f332a 100644 --- a/src/display/nr-arena-group.cpp +++ b/src/display/nr-arena-group.cpp @@ -35,7 +35,7 @@ static void nr_arena_group_set_child_position (NRArenaItem *item, NRArenaItem *c static unsigned int nr_arena_group_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset); static unsigned int nr_arena_group_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags); -static unsigned int nr_arena_group_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb); +static unsigned int nr_arena_group_clip (cairo_t *ct, NRArenaItem *item, NRRectL *area); static NRArenaItem *nr_arena_group_pick (NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky); static NRArenaItemClass *parent_class; @@ -233,7 +233,7 @@ nr_arena_group_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock } static unsigned int -nr_arena_group_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb) +nr_arena_group_clip (cairo_t *ct, NRArenaItem *item, NRRectL *area) { NRArenaGroup *group = NR_ARENA_GROUP (item); @@ -241,7 +241,7 @@ nr_arena_group_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb) /* Just compose children into parent buffer */ for (NRArenaItem *child = group->children; child != NULL; child = child->next) { - ret = nr_arena_item_invoke_clip (child, area, pb); + ret = nr_arena_item_invoke_clip (ct, child, area); if (ret & NR_ARENA_ITEM_STATE_INVALID) break; } diff --git a/src/display/nr-arena-item.cpp b/src/display/nr-arena-item.cpp index d101a9e54..8f11db191 100644 --- a/src/display/nr-arena-item.cpp +++ b/src/display/nr-arena-item.cpp @@ -17,9 +17,11 @@ #include <cstring> #include <string> +#include <cairomm/cairomm.h> #include <libnr/nr-blit.h> #include <libnr/nr-pixops.h> +#include "display/cairo-utils.h" #include "nr-arena.h" #include "nr-arena-item.h" #include "gc-core.h" @@ -321,6 +323,7 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area NR_ARENA_ITEM_STATE_INVALID); nr_return_val_if_fail (item->state & NR_ARENA_ITEM_STATE_BBOX, item->state); + if (!ct) return item->state; #ifdef NR_ARENA_ITEM_VERBOSE printf ("Invoke render %p: %d %d - %d %d\n", item, area->x0, area->y0, @@ -341,78 +344,36 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area } if (outline) { - // No caching in outline mode for now; investigate if it really gives any advantage with cairo. - // Also no attempts to clip anything; just render everything: item, clip, mask - // First, render the object itself - unsigned int state = NR_ARENA_ITEM_VIRTUAL (item, render) (ct, item, &carea, pb, flags); - if (state & NR_ARENA_ITEM_STATE_INVALID) { - /* Clean up and return error */ - item->state |= NR_ARENA_ITEM_STATE_INVALID; - return item->state; - } + // No caching in outline mode for now; investigate if it really gives any advantage with cairo. + // Also no attempts to clip anything; just render everything: item, clip, mask + // First, render the object itself + unsigned int state = NR_ARENA_ITEM_VIRTUAL (item, render) (ct, item, &carea, pb, flags); + if (state & NR_ARENA_ITEM_STATE_INVALID) { + /* Clean up and return error */ + item->state |= NR_ARENA_ITEM_STATE_INVALID; + return item->state; + } - // render clip and mask, if any - guint32 saved_rgba = item->arena->outlinecolor; // save current outline color - // render clippath as an object, using a different color - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - if (item->clip) { - item->arena->outlinecolor = prefs->getInt("/options/wireframecolors/clips", 0x00ff00ff); // green clips - NR_ARENA_ITEM_VIRTUAL (item->clip, render) (ct, item->clip, &carea, pb, flags); - } - // render mask as an object, using a different color - if (item->mask) { - item->arena->outlinecolor = prefs->getInt("/options/wireframecolors/masks", 0x0000ffff); // blue masks - NR_ARENA_ITEM_VIRTUAL (item->mask, render) (ct, item->mask, &carea, pb, flags); - } - item->arena->outlinecolor = saved_rgba; // restore outline color + // render clip and mask, if any + guint32 saved_rgba = item->arena->outlinecolor; // save current outline color + // render clippath as an object, using a different color + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + if (item->clip) { + item->arena->outlinecolor = prefs->getInt("/options/wireframecolors/clips", 0x00ff00ff); // green clips + NR_ARENA_ITEM_VIRTUAL (item->clip, render) (ct, item->clip, &carea, pb, flags); + } + // render mask as an object, using a different color + if (item->mask) { + item->arena->outlinecolor = prefs->getInt("/options/wireframecolors/masks", 0x0000ffff); // blue masks + NR_ARENA_ITEM_VIRTUAL (item->mask, render) (ct, item->mask, &carea, pb, flags); + } + item->arena->outlinecolor = saved_rgba; // restore outline color - return item->state | NR_ARENA_ITEM_STATE_RENDER; + return item->state | NR_ARENA_ITEM_STATE_RENDER; } #if 0 - NRPixBlock cpb; - if (item->px) { - /* Has cache pixblock, render this and return */ - nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P, - /* fixme: This probably cannot overflow, because we render only if visible */ - /* fixme: and pixel cache is there only for small items */ - /* fixme: But this still needs extra check (Lauris) */ - item->drawbox.x0, item->drawbox.y0, - item->drawbox.x1, item->drawbox.y1, - item->px, - 4 * (item->drawbox.x1 - item->drawbox.x0), FALSE, - FALSE); - nr_blit_pixblock_pixblock (pb, &cpb); - nr_pixblock_release (&cpb); - pb->empty = FALSE; - return item->state | NR_ARENA_ITEM_STATE_RENDER; - } -#endif NRPixBlock *dpb = pb; -#if 0 - /* Setup cache if we can */ - if ((!(flags & NR_ARENA_ITEM_RENDER_NO_CACHE)) && - (carea.x0 <= item->drawbox.x0) && (carea.y0 <= item->drawbox.y0) && - (carea.x1 >= item->drawbox.x1) && (carea.y1 >= item->drawbox.y1) && - (((item->drawbox.x1 - item->drawbox.x0) * (item->drawbox.y1 - - item->drawbox.y0)) <= 4096)) { - // Item drawbox is fully in renderable area and size is acceptable - carea.x0 = item->drawbox.x0; - carea.y0 = item->drawbox.y0; - carea.x1 = item->drawbox.x1; - carea.y1 = item->drawbox.y1; - item->px = - new (GC::ATOMIC) unsigned char[4 * (carea.x1 - carea.x0) * - (carea.y1 - carea.y0)]; - nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, - carea.y0, carea.x1, carea.y1, item->px, - 4 * (carea.x1 - carea.x0), TRUE, TRUE); - cpb.visible_area = pb->visible_area; - dpb = &cpb; - // Set nocache flag for downstream rendering - flags |= NR_ARENA_ITEM_RENDER_NO_CACHE; - } -#endif /* Determine, whether we need temporary buffer */ /* if (item->clip || item->mask @@ -596,12 +557,59 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area pb->empty = FALSE; item->state |= NR_ARENA_ITEM_STATE_IMAGE; } +#endif + + using namespace Inkscape; + + // clipping and masks + unsigned int state; + Cairo::Context cct(ct); + Cairo::RefPtr<Cairo::Pattern> mask; + CairoSave clipsave(ct); + CairoGroup maskgroup(ct); + CairoGroup drawgroup(ct); + + if (item->clip) { + clipsave.save(); + state = nr_arena_item_invoke_clip(ct, item->clip, const_cast<NRRectL*>(area)); + if (state & NR_ARENA_ITEM_STATE_INVALID) { + item->state |= NR_ARENA_ITEM_STATE_INVALID; + return item->state; + } + + cct.clip(); + } + + if (item->mask) { + maskgroup.push_with_content(CAIRO_CONTENT_ALPHA); + + state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (ct, item->mask, const_cast<NRRectL*>(area), pb, flags); + if (state & NR_ARENA_ITEM_STATE_INVALID) { + item->state |= NR_ARENA_ITEM_STATE_INVALID; + return item->state; + } + mask = maskgroup.popmm(); + } + + if (mask) { + drawgroup.push(); + } + state = NR_ARENA_ITEM_VIRTUAL (item, render) (ct, item, const_cast<NRRectL*>(area), pb, flags); + if (state & NR_ARENA_ITEM_STATE_INVALID) { + /* Clean up and return error */ + item->state |= NR_ARENA_ITEM_STATE_INVALID; + return item->state; + } + if (mask) { + drawgroup.pop_to_source(); + cct.mask(mask); + } return item->state | NR_ARENA_ITEM_STATE_RENDER; } unsigned int -nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb) +nr_arena_item_invoke_clip (cairo_t *ct, NRArenaItem *item, NRRectL *area) { nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID); nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), @@ -610,12 +618,12 @@ nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb) * NR_ARENA_ITEM_STATE_CLIP (and showed a warning on the console); * anyone know why we stopped doing so? */ - nr_return_val_if_fail ((pb->area.x1 - pb->area.x0) >= + /*nr_return_val_if_fail ((pb->area.x1 - pb->area.x0) >= (area->x1 - area->x0), NR_ARENA_ITEM_STATE_INVALID); nr_return_val_if_fail ((pb->area.y1 - pb->area.y0) >= (area->y1 - area->y0), - NR_ARENA_ITEM_STATE_INVALID); + NR_ARENA_ITEM_STATE_INVALID);*/ #ifdef NR_ARENA_ITEM_VERBOSE printf ("Invoke clip by %p: %d %d - %d %d, item bbox %d %d - %d %d\n", @@ -627,7 +635,7 @@ nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb) /* Need render that item */ if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->clip) { return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))-> - clip (item, area, pb); + clip (ct, item, area); } } diff --git a/src/display/nr-arena-item.h b/src/display/nr-arena-item.h index 2faa7d2d0..035013cd8 100644 --- a/src/display/nr-arena-item.h +++ b/src/display/nr-arena-item.h @@ -129,7 +129,7 @@ struct NRArenaItemClass : public NRObjectClass { unsigned int (* update) (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset); unsigned int (* render) (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags); - unsigned int (* clip) (NRArenaItem *item, NRRectL *area, NRPixBlock *pb); + unsigned int (* clip) (cairo_t *ct, NRArenaItem *item, NRRectL *area); NRArenaItem * (* pick) (NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky); }; @@ -159,7 +159,7 @@ unsigned int nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC unsigned int nr_arena_item_invoke_render(cairo_t *ct, NRArenaItem *item, NRRectL const *area, NRPixBlock *pb, unsigned int flags); -unsigned int nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb); +unsigned int nr_arena_item_invoke_clip (cairo_t *ct, NRArenaItem *item, NRRectL *area); NRArenaItem *nr_arena_item_invoke_pick (NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky); void nr_arena_item_request_update (NRArenaItem *item, unsigned int reset, unsigned int propagate); diff --git a/src/display/nr-arena-shape.cpp b/src/display/nr-arena-shape.cpp index 9ec8f1100..548a17127 100644 --- a/src/display/nr-arena-shape.cpp +++ b/src/display/nr-arena-shape.cpp @@ -55,7 +55,7 @@ static void nr_arena_shape_set_child_position(NRArenaItem *item, NRArenaItem *ch static guint nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset); 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 guint nr_arena_shape_clip(cairo_t *ct, NRArenaItem *item, NRRectL *area); static NRArenaItem *nr_arena_shape_pick(NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky); static NRArenaItemClass *shape_parent_class; @@ -112,21 +112,15 @@ nr_arena_shape_init(NRArenaShape *shape) shape->paintbox.x1 = shape->paintbox.y1 = 256.0F; 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; shape->delayed_shp = false; + shape->fill_pattern = NULL; + shape->stroke_pattern = NULL; + shape->path = NULL; + shape->approx_bbox.x0 = shape->approx_bbox.y0 = 0; shape->approx_bbox.x1 = shape->approx_bbox.y1 = 0; - shape->cached_fctm.setIdentity(); - shape->cached_sctm.setIdentity(); shape->markers = NULL; @@ -139,14 +133,9 @@ nr_arena_shape_finalize(NRObject *object) { NRArenaShape *shape = (NRArenaShape *) object; - if (shape->fill_shp) delete shape->fill_shp; - if (shape->stroke_shp) delete shape->stroke_shp; - if (shape->cached_fill) delete shape->cached_fill; - if (shape->cached_stroke) delete shape->cached_stroke; - if (shape->fill_painter) sp_painter_free(shape->fill_painter); - if (shape->stroke_painter) sp_painter_free(shape->stroke_painter); if (shape->fill_pattern) cairo_pattern_destroy(shape->fill_pattern); if (shape->stroke_pattern) cairo_pattern_destroy(shape->stroke_pattern); + if (shape->path) cairo_path_destroy(shape->path); if (shape->style) sp_style_unref(shape->style); if (shape->curve) shape->curve->unref(); @@ -228,10 +217,6 @@ nr_arena_shape_set_child_position(NRArenaItem *item, NRArenaItem *child, NRArena nr_arena_item_request_render(child); } -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. */ @@ -244,6 +229,7 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g unsigned int beststate = NR_ARENA_ITEM_STATE_ALL; + // update markers unsigned int newstate; for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) { newstate = nr_arena_item_invoke_update(child, area, gc, state, reset); @@ -280,6 +266,20 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); + // clear Cairo data to force update + if (shape->fill_pattern) { + cairo_pattern_destroy(shape->fill_pattern); + shape->fill_pattern = NULL; + } + if (shape->stroke_pattern) { + cairo_pattern_destroy(shape->stroke_pattern); + shape->stroke_pattern = NULL; + } + if (shape->path) { + cairo_path_destroy(shape->path); + shape->path = NULL; + } + if (shape->curve) { boundingbox = bounds_exact_transformed(shape->curve->get_pathvector(), gc->transform); @@ -287,7 +287,7 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g float width, scale; 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 + if ( fabs(shape->_stroke.width * scale) > 0.01 ) { boundingbox->expandBy(width); } // those pesky miters, now @@ -297,7 +297,7 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g boundingbox->expandBy(miterMax); } } - } + } /// \todo just write item->bbox = boundingbox if (boundingbox) { @@ -310,25 +310,8 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g } if ( area && nr_rect_l_test_intersect_ptr(area, &shape->approx_bbox) ) shape->delayed_shp=false; - /* Release state data */ - if (shape->fill_shp) { - delete shape->fill_shp; - shape->fill_shp = NULL; - } - if (shape->stroke_shp) { - delete shape->stroke_shp; - shape->stroke_shp = NULL; - } - - // clear Cairo patterns to force update - if (shape->fill_pattern) { - cairo_pattern_destroy(shape->fill_pattern); - shape->fill_pattern = NULL; - } - if (shape->stroke_pattern) { - cairo_pattern_destroy(shape->stroke_pattern); - shape->stroke_pattern = NULL; - } + // TODO: compute a better bounding box that respects miters + item->bbox = shape->approx_bbox; if (!shape->curve || !shape->style || @@ -336,45 +319,10 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g (( shape->_fill.paint.type() == NRArenaShape::Paint::NONE ) && ( shape->_stroke.paint.type() == NRArenaShape::Paint::NONE && !outline) )) { - item->bbox = shape->approx_bbox; + //item->bbox = shape->approx_bbox; return NR_ARENA_ITEM_STATE_ALL; } - /* Build state data */ - if ( shape->delayed_shp ) { - item->bbox=shape->approx_bbox; - } else { - 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<NR::ICoord>(floor((*boundingbox)[0][0])); - shape->approx_bbox.y0 = static_cast<NR::ICoord>(floor((*boundingbox)[1][0])); - shape->approx_bbox.x1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[0][1])); - shape->approx_bbox.y1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[1][1])); - } else { - shape->approx_bbox = NR_RECT_L_EMPTY; - } - } - - if (!boundingbox) - return NR_ARENA_ITEM_STATE_ALL; - - /// \todo just write item->bbox = boundingbox - item->bbox.x0 = static_cast<NR::ICoord>(floor((*boundingbox)[0][0])); - item->bbox.y0 = static_cast<NR::ICoord>(floor((*boundingbox)[1][0])); - item->bbox.x1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[0][1])); - item->bbox.y1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[1][1])); - - // to render opacity, use Cairo groups - item->render_opacity = FALSE; - - // update patterns when rendering - if (beststate & NR_ARENA_ITEM_STATE_BBOX) { for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) { nr_rect_l_union(&item->bbox, &item->bbox, &child->bbox); @@ -384,325 +332,6 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g return NR_ARENA_ITEM_STATE_ALL; } -int matrix_is_isometry(Geom::Matrix p) { - Geom::Matrix tp; - // transposition - tp[0]=p[0]; - tp[1]=p[2]; - tp[2]=p[1]; - tp[3]=p[3]; - for (int i = 4; i < 6; i++) // shut valgrind up :) - tp[i] = p[i] = 0; - Geom::Matrix isom = tp*p; // A^T * A = adjunct? - // Is the adjunct nearly an identity function? - if (isom.isTranslation(0.01)) { - // the transformation is an isometry -> no need to recompute - // the uncrossed polygon - if ( p.det() < 0 ) - return -1; - else - return 1; - } - 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, NRRectL *area, bool force_shape) -{ - if ((shape->_fill.paint.type() != NRArenaShape::Paint::NONE || force_shape) && - has_inner_area(shape->curve->get_pathvector()) ) { - - Geom::Matrix cached_to_new = Geom::identity(); - int isometry = 0; - if ( shape->cached_fill ) { - 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; - shape->cached_fill->Reset(); - - Path* thePath=new Path; - Shape* theShape=new Shape; - { - Geom::Matrix tempMat(gc->transform); - thePath->LoadPathVector(shape->curve->get_pathvector(), tempMat, true); - } - - 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); - - if ( shape->_fill.rule == NRArenaShape::EVEN_ODD ) { - shape->cached_fill->ConvertToShape(theShape, fill_oddEven); - // alternatively, this speeds up rendering of oddeven shapes but disables AA :( - //shape->cached_fill->Copy(theShape); - } else { - 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 ) - shape->fill_shp = new 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 ) - shape->fill_shp = new 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(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, - shape->cached_fill->getEdge(i).en); - } else if ( isometry == -1 ) { // need to flip poly. - for (int i = 0; i < shape->cached_fill->numberOfEdges(); i++) - shape->fill_shp->AddEdge(shape->cached_fill->getEdge(i).en, - shape->cached_fill->getEdge(i).st); - } - shape->fill_shp->ForceToPolygon(); - shape->fill_shp->needPointsSorting(); - shape->fill_shp->needEdgesSorting(); - } - shape->delayed_shp |= shape->cached_fpartialy; - } -} - -void -nr_arena_shape_update_stroke(NRArenaShape *shape,NRGC* gc, NRRectL *area) -{ - SPStyle* style = shape->style; - - float const scale = gc->transform.descrim(); - - bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::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 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; - } - - Geom::Matrix cached_to_new = Geom::identity(); - - int isometry = 0; - if ( shape->cached_stroke ) { - 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 ) { - if ( shape->cached_stroke == NULL ) shape->cached_stroke=new Shape; - shape->cached_stroke->Reset(); - Path* thePath = new Path; - Shape* theShape = new Shape; - { - Geom::Matrix tempMat( gc->transform ); - thePath->LoadPathVector(shape->curve->get_pathvector(), tempMat, true); - } - - // 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 && !outline) { - thePath->DashPolylineFromStyle(style, scale, 1.0); - } - - ButtType butt=butt_straight; - switch (shape->_stroke.cap) { - case NRArenaShape::BUTT_CAP: - butt = butt_straight; - break; - case NRArenaShape::ROUND_CAP: - butt = butt_round; - break; - case NRArenaShape::SQUARE_CAP: - butt = butt_square; - break; - } - JoinType join=join_straight; - switch (shape->_stroke.join) { - case NRArenaShape::MITRE_JOIN: - join = join_pointy; - break; - case NRArenaShape::ROUND_JOIN: - join = join_round; - break; - case NRArenaShape::BEVEL_JOIN: - join = join_straight; - break; - } - - if (outline) { - butt = butt_straight; - join = join_straight; - } - - thePath->Stroke(theShape, false, 0.5*width, join, butt, - 0.5*width*shape->_stroke.mitre_limit); - - - 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(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, - shape->cached_stroke->getEdge(i).en); - } else if ( isometry == -1 ) { - for (int i = 0; i < shape->cached_stroke->numberOfEdges(); i++) - shape->stroke_shp->AddEdge(shape->cached_stroke->getEdge(i).en, - shape->cached_stroke->getEdge(i).st); - } - shape->stroke_shp->ForceToPolygon(); - shape->stroke_shp->needPointsSorting(); - shape->stroke_shp->needEdgesSorting(); - } - shape->delayed_shp |= shape->cached_spartialy; - } -} - - -void -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 *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 *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) @@ -740,6 +369,11 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock if (!shape->style) return item->state; if (!ct) return item->state; + // skip if not within bounding box + if (!nr_rect_l_test_intersect_ptr(area, &item->bbox)) { + 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); @@ -750,246 +384,89 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock if (ret & NR_ARENA_ITEM_STATE_INVALID) return ret; } else { - - if ( shape->delayed_shp ) { - if ( nr_rect_l_test_intersect_ptr(area, &item->bbox) ) { - NRGC tempGC(NULL); - tempGC.transform=shape->ctm; - 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); - 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;*/ - } else { - return item->state; - } - } - - SPStyle const *style = shape->style; - - // set up context and feed path - float opacity = SP_SCALE24_TO_FLOAT(shape->style->opacity.value); - bool needs_opacity = ((1.0 - opacity) >= 1e-3); - - cairo_save(ct); - //cairo_new_path(ct); // we assume the context is clean - cairo_translate(ct, -area->x0, -area->y0); - ink_cairo_transform(ct, shape->ctm); - - // update fill and stroke paints. - // this cannot be done during nr_arena_shape_update, because we need a Cairo context - // to use groups for svg:pattern - if (!shape->fill_pattern) { - switch (shape->_fill.paint.type()) { - case NRArenaShape::Paint::SERVER: { - SPPaintServer *ps = shape->_fill.paint.server(); - shape->fill_pattern = sp_paint_server_create_pattern(ps, ct, &shape->paintbox, shape->_fill.opacity); - } break; - case NRArenaShape::Paint::COLOR: { - SPColor const &c = shape->_fill.paint.color(); - shape->fill_pattern = cairo_pattern_create_rgba( - c.v.c[0], c.v.c[1], c.v.c[2], shape->_fill.opacity); - } break; - default: break; - } - } - - if (!shape->stroke_pattern) { - switch (shape->_stroke.paint.type()) { - case NRArenaShape::Paint::SERVER: { - SPPaintServer *ps = shape->_stroke.paint.server(); - shape->stroke_pattern = sp_paint_server_create_pattern(ps, ct, &shape->paintbox, shape->_stroke.opacity); - } break; - case NRArenaShape::Paint::COLOR: { - SPColor const &c = shape->_stroke.paint.color(); - shape->stroke_pattern = cairo_pattern_create_rgba( - c.v.c[0], c.v.c[1], c.v.c[2], shape->_stroke.opacity); - } break; - default: break; - } - } - - if (shape->fill_pattern || shape->stroke_pattern) { - - if (needs_opacity) { - cairo_push_group(ct); - } - - // TODO: remove segments outside of bbox when no dashes present - feed_pathvector_to_cairo(ct, shape->curve->get_pathvector()); - - if (shape->fill_pattern) { - switch (shape->_fill.rule) { - case NRArenaShape::EVEN_ODD: - cairo_set_fill_rule(ct, CAIRO_FILL_RULE_EVEN_ODD); - break; - default: - cairo_set_fill_rule(ct, CAIRO_FILL_RULE_WINDING); - break; + SPStyle const *style = shape->style; + + // set up context and feed path + float opacity = SP_SCALE24_TO_FLOAT(shape->style->opacity.value); + bool needs_opacity = ((1.0 - opacity) >= 1e-3); + + // we assume the context has no path + cairo_save(ct); + cairo_translate(ct, -area->x0, -area->y0); + ink_cairo_transform(ct, shape->ctm); + + // update fill and stroke paints. + // this cannot be done during nr_arena_shape_update, because we need a Cairo context + // to render svg:pattern + if (!shape->fill_pattern) { + switch (shape->_fill.paint.type()) { + case NRArenaShape::Paint::SERVER: { + SPPaintServer *ps = shape->_fill.paint.server(); + shape->fill_pattern = sp_paint_server_create_pattern(ps, ct, &shape->paintbox, shape->_fill.opacity); + } break; + case NRArenaShape::Paint::COLOR: { + SPColor const &c = shape->_fill.paint.color(); + shape->fill_pattern = cairo_pattern_create_rgba( + c.v.c[0], c.v.c[1], c.v.c[2], shape->_fill.opacity); + } break; + default: break; } - cairo_set_source(ct, shape->fill_pattern); - cairo_fill_preserve(ct); } - if (shape->stroke_pattern) { - // float style_width = shape->_stroke.width * scale; - cairo_set_line_width(ct, shape->_stroke.width); - - // stroke caps - 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; - } - // stroke join - 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; - } - - // miter limit - cairo_set_miter_limit (ct, style->stroke_miterlimit.value); - - // dashes - if (style->stroke_dash.n_dash) { - cairo_set_dash (ct, style->stroke_dash.dash, style->stroke_dash.n_dash, - style->stroke_dash.offset); + if (!shape->stroke_pattern) { + switch (shape->_stroke.paint.type()) { + case NRArenaShape::Paint::SERVER: { + SPPaintServer *ps = shape->_stroke.paint.server(); + shape->stroke_pattern = sp_paint_server_create_pattern(ps, ct, &shape->paintbox, shape->_stroke.opacity); + } break; + case NRArenaShape::Paint::COLOR: { + SPColor const &c = shape->_stroke.paint.color(); + shape->stroke_pattern = cairo_pattern_create_rgba( + c.v.c[0], c.v.c[1], c.v.c[2], shape->_stroke.opacity); + } break; + default: break; } - cairo_set_source(ct, shape->stroke_pattern); - cairo_stroke_preserve(ct); } - cairo_new_path(ct); // clear path - if (needs_opacity) { - cairo_pop_group_to_source(ct); - cairo_paint_with_alpha(ct, opacity); - } - } // has fill or stroke pattern - - cairo_restore(ct); + if (shape->fill_pattern || shape->stroke_pattern) { -/* - 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 = fill_color->toRGBA32( shape->_fill.opacity * - SP_SCALE24_TO_FLOAT(style->opacity.value) ); - } else { - rgba = fill_color->toRGBA32( shape->_fill.opacity ); + if (needs_opacity) { + cairo_push_group(ct); } - if (print_colors_preview) - nr_arena_separate_color_plates(&rgba); + // TODO: remove segments outside of bbox when no dashes present + feed_pathvector_to_cairo(ct, shape->curve->get_pathvector()); - nr_blit_pixblock_mask_rgba32(pb, &m, rgba); - pb->empty = FALSE; - } else if (shape->_fill.paint.type() == NRArenaShape::Paint::SERVER) { - if (shape->fill_painter) { - nr_arena_render_paintserver_fill(pb, area, shape->fill_painter, shape->_fill.opacity, &m); + if (shape->fill_pattern) { + cairo_set_fill_rule(ct, shape->_fill.rule); + cairo_set_source(ct, shape->fill_pattern); + cairo_fill_preserve(ct); } - } - - nr_pixblock_release(&m); - } - - if (shape->_stroke.paint.type() == NRArenaShape::Paint::COLOR) { - - - - - 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; - 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) { + if (shape->stroke_pattern) { + cairo_set_line_width(ct, shape->_stroke.width); + cairo_set_line_cap(ct, shape->_stroke.cap); + cairo_set_line_join(ct, shape->_stroke.join); + cairo_set_miter_limit (ct, style->stroke_miterlimit.value); - 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); - } + // dashes + if (style->stroke_dash.n_dash) { + cairo_set_dash (ct, style->stroke_dash.dash, style->stroke_dash.n_dash, + style->stroke_dash.offset); + } + cairo_set_source(ct, shape->stroke_pattern); + cairo_stroke_preserve(ct); + } + cairo_new_path(ct); // clear path - m.visible_area = pb->visible_area; - nr_pixblock_render_shape_mask_or(m, shape->stroke_shp); - m.empty = FALSE; + if (needs_opacity) { + cairo_pop_group_to_source(ct); + cairo_paint_with_alpha(ct, opacity); + } + } // has fill or stroke pattern - if (shape->stroke_painter) { - nr_arena_render_paintserver_fill(pb, area, shape->stroke_painter, shape->_stroke.opacity, &m); - } + cairo_restore(ct); - nr_pixblock_release(&m); - } -*/ } // non-cairo non-outline branch /* Render markers into parent buffer */ @@ -1002,88 +479,19 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock } -// 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) +nr_arena_shape_clip(cairo_t *ct, NRArenaItem *item, NRRectL *area) { - //return cairo_arena_shape_clip(item, area, pb); - + // NOTE: for now this is incorrect, because it doesn't honor clip-rule, + // and will be incorrect for nested clipping paths. 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_ptr(area, &item->bbox) ) { - NRGC tempGC(NULL); - tempGC.transform=shape->ctm; - shape->delayed_shp = false; - nr_arena_shape_update_fill(shape, &tempGC, &pb->visible_area, true); - } else { - return item->state; - } - } - - if ( shape->fill_shp ) { - NRPixBlock m; - - /* 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++) { - unsigned char *s, *d; - 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_COMPOSEA_111(*s, *d); - d ++; - s ++; - } - } - nr_pixblock_release(&m); - pb->empty = FALSE; - } + cairo_save(ct); + cairo_translate(ct, -area->x0, -area->y0); + ink_cairo_transform(ct, shape->ctm); + feed_pathvector_to_cairo(ct, shape->curve->get_pathvector()); + cairo_restore(ct); return item->state; } @@ -1191,18 +599,6 @@ void nr_arena_shape_set_path(NRArenaShape *shape, SPCurve *curve,bool justTrans) g_return_if_fail(shape != NULL); g_return_if_fail(NR_IS_ARENA_SHAPE(shape)); - if ( justTrans == false ) { - // dirty cached versions - if ( shape->cached_fill ) { - delete shape->cached_fill; - shape->cached_fill=NULL; - } - if ( shape->cached_stroke ) { - delete shape->cached_stroke; - shape->cached_stroke=NULL; - } - } - nr_arena_item_request_render(NR_ARENA_ITEM(shape)); if (shape->curve) { @@ -1233,7 +629,7 @@ void NRArenaShape::setFillOpacity(double opacity) { _invalidateCachedFill(); } -void NRArenaShape::setFillRule(NRArenaShape::FillRule rule) { +void NRArenaShape::setFillRule(cairo_fill_rule_t rule) { _fill.rule = rule; _invalidateCachedFill(); } @@ -1263,12 +659,12 @@ void NRArenaShape::setMitreLimit(double limit) { _invalidateCachedStroke(); } -void NRArenaShape::setLineCap(NRArenaShape::CapType cap) { +void NRArenaShape::setLineCap(cairo_line_cap_t cap) { _stroke.cap = cap; _invalidateCachedStroke(); } -void NRArenaShape::setLineJoin(NRArenaShape::JoinType join) { +void NRArenaShape::setLineJoin(cairo_line_join_t join) { _stroke.join = join; _invalidateCachedStroke(); } @@ -1299,11 +695,11 @@ nr_arena_shape_set_style(NRArenaShape *shape, SPStyle *style) shape->setFillOpacity(SP_SCALE24_TO_FLOAT(style->fill_opacity.value)); switch (style->fill_rule.computed) { case SP_WIND_RULE_EVENODD: { - shape->setFillRule(NRArenaShape::EVEN_ODD); + shape->setFillRule(CAIRO_FILL_RULE_EVEN_ODD); break; } case SP_WIND_RULE_NONZERO: { - shape->setFillRule(NRArenaShape::NONZERO); + shape->setFillRule(CAIRO_FILL_RULE_WINDING); break; } default: { @@ -1324,15 +720,15 @@ nr_arena_shape_set_style(NRArenaShape *shape, SPStyle *style) shape->setStrokeOpacity(SP_SCALE24_TO_FLOAT(style->stroke_opacity.value)); switch (style->stroke_linecap.computed) { case SP_STROKE_LINECAP_ROUND: { - shape->setLineCap(NRArenaShape::ROUND_CAP); + shape->setLineCap(CAIRO_LINE_CAP_ROUND); break; } case SP_STROKE_LINECAP_SQUARE: { - shape->setLineCap(NRArenaShape::SQUARE_CAP); + shape->setLineCap(CAIRO_LINE_CAP_SQUARE); break; } case SP_STROKE_LINECAP_BUTT: { - shape->setLineCap(NRArenaShape::BUTT_CAP); + shape->setLineCap(CAIRO_LINE_CAP_BUTT); break; } default: { @@ -1341,15 +737,15 @@ nr_arena_shape_set_style(NRArenaShape *shape, SPStyle *style) } switch (style->stroke_linejoin.computed) { case SP_STROKE_LINEJOIN_ROUND: { - shape->setLineJoin(NRArenaShape::ROUND_JOIN); + shape->setLineJoin(CAIRO_LINE_JOIN_ROUND); break; } case SP_STROKE_LINEJOIN_BEVEL: { - shape->setLineJoin(NRArenaShape::BEVEL_JOIN); + shape->setLineJoin(CAIRO_LINE_JOIN_BEVEL); break; } case SP_STROKE_LINEJOIN_MITER: { - shape->setLineJoin(NRArenaShape::MITRE_JOIN); + shape->setLineJoin(CAIRO_LINE_JOIN_MITER); break; } default: { diff --git a/src/display/nr-arena-shape.h b/src/display/nr-arena-shape.h index a88129286..97001c82d 100644 --- a/src/display/nr-arena-shape.h +++ b/src/display/nr-arena-shape.h @@ -17,6 +17,7 @@ #define NR_ARENA_SHAPE(obj) (NR_CHECK_INSTANCE_CAST ((obj), NR_TYPE_ARENA_SHAPE, NRArenaShape)) #define NR_IS_ARENA_SHAPE(obj) (NR_CHECK_INSTANCE_TYPE ((obj), NR_TYPE_ARENA_SHAPE)) +#include <cairo.h> #include "display/display-forward.h" #include "display/canvas-bpath.h" #include "forward.h" @@ -91,23 +92,6 @@ struct NRArenaShape : public NRArenaItem { } }; - enum FillRule { - EVEN_ODD, - NONZERO - }; - - enum CapType { - ROUND_CAP, - SQUARE_CAP, - BUTT_CAP - }; - - enum JoinType { - ROUND_JOIN, - BEVEL_JOIN, - MITRE_JOIN - }; - /* Shape data */ SPCurve *curve; SPStyle *style; @@ -117,37 +101,15 @@ struct NRArenaShape : public NRArenaItem { cairo_pattern_t *fill_pattern; cairo_pattern_t *stroke_pattern; + cairo_path_t *path; - SPPainter *fill_painter; - SPPainter *stroke_painter; - // the 2 cached polygons, for rasterizations uses - Shape *fill_shp; - Shape *stroke_shp; - // the stroke width of stroke_shp, to detect when it changes (on normal/outline switching) and rebuild - float cached_width; // delayed_shp=true means the *_shp polygons are not computed yet // they'll be computed on demand in *_render(), *_pick() or *_clip() // the goal is to not uncross polygons that are outside the viewing region bool delayed_shp; // approximate bounding box, for the case when the polygons have been delayed NRRectL approx_bbox; - // cache for transformations: cached_fill and cached_stroke are - // polygons computed for the cached_fctm and cache_sctm respectively - // when the transformation changes interactively (tracked by the - // SP_OBJECT_USER_MODIFIED_FLAG_B), we check if it's an isometry wrt - // the cached ctm. if it's an isometry, just apply it to the cached - // polygon to get the *_shp polygon. Otherwise, recompute so this - // works fine for translation and rotation, but not scaling and - // skewing - Geom::Matrix cached_fctm; - Geom::Matrix cached_sctm; - NRRectL cached_farea; - NRRectL cached_sarea; - bool cached_fpartialy; - bool cached_spartialy; - - Shape *cached_fill; - Shape *cached_stroke; + /* Markers */ NRArenaItem *markers; @@ -164,29 +126,21 @@ struct NRArenaShape : public NRArenaItem { void setFill(SPPaintServer *server); void setFill(SPColor const &color); void setFillOpacity(double opacity); - void setFillRule(FillRule rule); + void setFillRule(cairo_fill_rule_t rule); void setStroke(SPPaintServer *server); void setStroke(SPColor const &color); void setStrokeOpacity(double opacity); void setStrokeWidth(double width); - void setLineCap(CapType cap); - void setLineJoin(JoinType join); + void setLineCap(cairo_line_cap_t cap); + void setLineJoin(cairo_line_join_t join); void setMitreLimit(double limit); void setPaintBox(Geom::Rect const &pbox); void _invalidateCachedFill() { - if (cached_fill) { - delete cached_fill; - cached_fill = NULL; - } } void _invalidateCachedStroke() { - if (cached_stroke) { - delete cached_stroke; - cached_stroke = NULL; - } } struct Style { @@ -195,17 +149,17 @@ struct NRArenaShape : public NRArenaItem { double opacity; }; struct FillStyle : public Style { - FillStyle() : rule(EVEN_ODD) {} - FillRule rule; + FillStyle() : rule(CAIRO_FILL_RULE_EVEN_ODD) {} + cairo_fill_rule_t rule; } _fill; struct StrokeStyle : public Style { StrokeStyle() - : cap(ROUND_CAP), join(ROUND_JOIN), + : cap(CAIRO_LINE_CAP_ROUND), join(CAIRO_LINE_JOIN_ROUND), width(0.0), mitre_limit(0.0) {} - CapType cap; - JoinType join; + cairo_line_cap_t cap; + cairo_line_join_t join; double width; double mitre_limit; } _stroke; diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 29a5cd740..31e80d1f9 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -1650,19 +1650,20 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, buf.visible_rect.x1 = draw_x2; buf.visible_rect.y1 = draw_y2; buf.is_empty = true; - //buf.bg_color = &widget->style->bg[GTK_STATE_NORMAL]; - //buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf); - buf.ct = gdk_cairo_create(widget->window); + //buf.ct = gdk_cairo_create(widget->window); + + // create temporary surface + cairo_surface_t *imgs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, x1 - x0, y1 - y0); + buf.ct = cairo_create(imgs); + //cairo_translate(buf.ct, -x0, -y0); // fix coordinates, clip all drawing to the tile and clear the background - // TODO: the translation is done to remain compatible with legacy code. - // Fix the code so it doesn't refer to buf.rect and remove the translation. - cairo_translate(buf.ct, x0 - canvas->x0, y0 - canvas->y0); // ? - cairo_rectangle(buf.ct, 0, 0, x1 - x0, y1 - y0); + //cairo_translate(buf.ct, x0 - canvas->x0, y0 - canvas->y0); + //cairo_rectangle(buf.ct, 0, 0, x1 - x0, y1 - y0); //cairo_set_line_width(buf.ct, 3); //cairo_set_source_rgba(buf.ct, 1.0, 0.0, 0.0, 0.1); //cairo_stroke_preserve(buf.ct); - cairo_clip(buf.ct); + //cairo_clip(buf.ct); gdk_cairo_set_source_color(buf.ct, &widget->style->bg[GTK_STATE_NORMAL]); cairo_set_operator(buf.ct, CAIRO_OPERATOR_SOURCE); @@ -1762,8 +1763,21 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, } #endif + // output to X + cairo_destroy(buf.ct); + + cairo_t *xct = gdk_cairo_create(widget->window); + cairo_translate(xct, x0 - canvas->x0, y0 - canvas->y0); + cairo_rectangle(xct, 0, 0, x1-x0, y1-y0); + cairo_clip(xct); + cairo_set_source_surface(xct, imgs, 0, 0); + cairo_set_operator(xct, CAIRO_OPERATOR_SOURCE); + cairo_paint(xct); + cairo_destroy(xct); + cairo_surface_destroy(imgs); + //cairo_surface_t *cst = cairo_get_target(buf.ct); - cairo_destroy (buf.ct); + //cairo_destroy (buf.ct); //cairo_surface_finish (cst); //cairo_surface_destroy (cst); @@ -1831,21 +1845,21 @@ sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect) if (bw * bh < setup->max_pixels) { // We are small enough - GdkRectangle r; + /*GdkRectangle r; r.x = this_rect.x0 - setup->canvas->x0; r.y = this_rect.y0 - setup->canvas->y0; r.width = this_rect.x1 - this_rect.x0; r.height = this_rect.y1 - this_rect.y0; GdkWindow *window = GTK_WIDGET(setup->canvas)->window; - gdk_window_begin_paint_rect(window, &r); + gdk_window_begin_paint_rect(window, &r);*/ sp_canvas_paint_single_buffer (setup->canvas, this_rect.x0, this_rect.y0, this_rect.x1, this_rect.y1, setup->big_rect.x0, setup->big_rect.y0, setup->big_rect.x1, setup->big_rect.y1, bw); - gdk_window_end_paint(window); + //gdk_window_end_paint(window); return 1; } |
