summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2010-06-26 01:02:06 +0000
committerKrzysztof Kosiński <tweenk.pl@gmail.com>2010-06-26 01:02:06 +0000
commit31f84a59da72b31b932833ce1af7d78f0a67e185 (patch)
treef72a45ade3678183b262c7d92455e457373aed6e /src
parentMerge from trunk (diff)
downloadinkscape-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_insert2
-rw-r--r--src/display/cairo-utils.cpp113
-rw-r--r--src/display/cairo-utils.h88
-rw-r--r--src/display/nr-arena-glyphs.cpp11
-rw-r--r--src/display/nr-arena-group.cpp6
-rw-r--r--src/display/nr-arena-item.cpp148
-rw-r--r--src/display/nr-arena-item.h4
-rw-r--r--src/display/nr-arena-shape.cpp838
-rw-r--r--src/display/nr-arena-shape.h68
-rw-r--r--src/display/sp-canvas.cpp38
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;
}