summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2010-06-22 21:36:47 +0000
committerKrzysztof KosiƄski <tweenk.pl@gmail.com>2010-06-22 21:36:47 +0000
commit917de2bab458d51b4149a1b794cbb1b6b9562171 (patch)
tree9abd6fe6497b8b0965bb85caf4b4456ff2099989 /src
parent* [INTL:sk] Slovak translation update (diff)
downloadinkscape-917de2bab458d51b4149a1b794cbb1b6b9562171.tar.gz
inkscape-917de2bab458d51b4149a1b794cbb1b6b9562171.zip
Initial Cairo rendering commit: solid shapes, gradients, opacity and patterns
(bzr r9508.1.1)
Diffstat (limited to 'src')
-rw-r--r--src/display/canvas-arena.cpp25
-rw-r--r--src/display/canvas-bpath.cpp22
-rw-r--r--src/display/inkscape-cairo.cpp35
-rw-r--r--src/display/inkscape-cairo.h5
-rw-r--r--src/display/nr-arena-item.cpp13
-rw-r--r--src/display/nr-arena-shape.cpp259
-rw-r--r--src/display/nr-arena-shape.h3
-rw-r--r--src/display/sodipodi-ctrl.cpp283
-rw-r--r--src/display/sodipodi-ctrl.h2
-rw-r--r--src/display/sodipodi-ctrlrect.cpp35
-rw-r--r--src/display/sp-canvas-util.cpp29
-rw-r--r--src/display/sp-canvas-util.h5
-rw-r--r--src/display/sp-canvas.cpp53
-rw-r--r--src/display/sp-canvas.h14
-rw-r--r--src/display/sp-ctrlline.cpp6
-rw-r--r--src/sp-gradient.cpp86
-rw-r--r--src/sp-paint-server.cpp32
-rw-r--r--src/sp-paint-server.h4
-rw-r--r--src/sp-pattern.cpp128
19 files changed, 682 insertions, 357 deletions
diff --git a/src/display/canvas-arena.cpp b/src/display/canvas-arena.cpp
index 733f9a513..86d902be2 100644
--- a/src/display/canvas-arena.cpp
+++ b/src/display/canvas-arena.cpp
@@ -190,6 +190,7 @@ sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf)
gint bw, bh;
SPCanvasArena *arena = SP_CANVAS_ARENA (item);
+ SPCanvas *canvas = item->canvas;
nr_arena_item_invoke_update (arena->root, NULL, &arena->gc,
NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_RENDER,
@@ -209,19 +210,31 @@ sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf)
area.x1 = buf->rect.x1;
area.y1 = buf->rect.y1;
+ sp_canvas_prepare_buffer(buf);
+
nr_pixblock_setup_extern (&cb, NR_PIXBLOCK_MODE_R8G8B8A8P, area.x0, area.y0, area.x1, area.y1,
buf->buf,
buf->buf_rowstride,
FALSE, FALSE);
cb.visible_area = buf->visible_rect;
- cairo_t *ct = nr_create_cairo_context (&area, &cb);
- nr_arena_item_invoke_render (ct, arena->root, &area, &cb, 0);
+ //cairo_t *ct = nr_create_cairo_context (&area, &cb);
+
+ cairo_save(buf->ct);
+ //cairo_translate(buf->ct, area.x0 - canvas->x0, area.y0 - canvas->y0);
+ nr_arena_item_invoke_render (buf->ct, arena->root, &area, &cb, 0);
+ cairo_restore(buf->ct);
+
+ //cairo_surface_t *cst = cairo_get_target(ct);
+
+ //cairo_save(buf->ct);
+ //cairo_set_source_surface(buf->ct, cst, 0, 0);
+ //cairo_paint(buf->ct);
+ //cairo_restore(buf->ct);
- cairo_surface_t *cst = cairo_get_target(ct);
- cairo_destroy (ct);
- cairo_surface_finish (cst);
- cairo_surface_destroy (cst);
+ //cairo_destroy (ct);
+ //cairo_surface_finish (cst);
+ //cairo_surface_destroy (cst);
nr_pixblock_release (&cb);
}
diff --git a/src/display/canvas-bpath.cpp b/src/display/canvas-bpath.cpp
index c47806615..5726fef02 100644
--- a/src/display/canvas-bpath.cpp
+++ b/src/display/canvas-bpath.cpp
@@ -149,8 +149,6 @@ sp_canvas_bpath_render (SPCanvasItem *item, SPCanvasBuf *buf)
{
SPCanvasBPath *cbp = SP_CANVAS_BPATH (item);
- sp_canvas_prepare_buffer(buf);
-
Geom::Rect area (Geom::Point(buf->rect.x0, buf->rect.y0), Geom::Point(buf->rect.x1, buf->rect.y1));
if ( !cbp->curve ||
@@ -164,33 +162,29 @@ sp_canvas_bpath_render (SPCanvasItem *item, SPCanvasBuf *buf)
bool dofill = ((cbp->fill_rgba & 0xff) != 0);
bool dostroke = ((cbp->stroke_rgba & 0xff) != 0);
- cairo_set_tolerance(buf->ct, 1.25); // low quality, but good enough for canvas items
+ cairo_set_tolerance(buf->ct, 0.5);
cairo_new_path(buf->ct);
- if (!dofill)
- feed_pathvector_to_cairo (buf->ct, cbp->curve->get_pathvector(), cbp->affine, area, true, 1);
- else
- feed_pathvector_to_cairo (buf->ct, cbp->curve->get_pathvector(), cbp->affine, area, false, 1);
+ feed_pathvector_to_cairo (buf->ct, cbp->curve->get_pathvector(), cbp->affine, area,
+ /* optimized_stroke = */ !dofill, 1);
if (dofill) {
// RGB / BGR
- cairo_set_source_rgba(buf->ct, SP_RGBA32_B_F(cbp->fill_rgba), SP_RGBA32_G_F(cbp->fill_rgba), SP_RGBA32_R_F(cbp->fill_rgba), SP_RGBA32_A_F(cbp->fill_rgba));
+ ink_cairo_set_source_rgba32(buf->ct, cbp->fill_rgba);
cairo_set_fill_rule(buf->ct, cbp->fill_rule == SP_WIND_RULE_EVENODD? CAIRO_FILL_RULE_EVEN_ODD
: CAIRO_FILL_RULE_WINDING);
- if (dostroke)
- cairo_fill_preserve(buf->ct);
- else
- cairo_fill(buf->ct);
+ cairo_fill_preserve(buf->ct);
}
if (dostroke) {
- // RGB / BGR
- cairo_set_source_rgba(buf->ct, SP_RGBA32_B_F(cbp->stroke_rgba), SP_RGBA32_G_F(cbp->stroke_rgba), SP_RGBA32_R_F(cbp->stroke_rgba), SP_RGBA32_A_F(cbp->stroke_rgba));
+ ink_cairo_set_source_rgba32(buf->ct, cbp->stroke_rgba);
cairo_set_line_width(buf->ct, 1);
if (cbp->dashes[0] != 0 && cbp->dashes[1] != 0) {
cairo_set_dash (buf->ct, cbp->dashes, 2, 0);
}
cairo_stroke(buf->ct);
+ } else {
+ cairo_new_path(buf->ct);
}
}
diff --git a/src/display/inkscape-cairo.cpp b/src/display/inkscape-cairo.cpp
index a3e550fc5..fa5a7cfe2 100644
--- a/src/display/inkscape-cairo.cpp
+++ b/src/display/inkscape-cairo.cpp
@@ -53,12 +53,14 @@ nr_create_cairo_context_for_data (NRRectL *area, NRRectL *buf_area, unsigned cha
return ct;
}
+#if 0
/** Creates a cairo context to render to the given SPCanvasBuf on the given area */
cairo_t *
nr_create_cairo_context_canvasbuf (NRRectL */*area*/, SPCanvasBuf *b)
{
return nr_create_cairo_context_for_data (&(b->rect), &(b->rect), b->buf, b->buf_rowstride);
}
+#endif
/** Creates a cairo context to render to the given NRPixBlock on the given area */
@@ -236,6 +238,39 @@ feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv)
}
}
+void
+ink_cairo_set_source_rgba32(cairo_t *ct, guint32 rgba)
+{
+ cairo_set_source_rgba(ct, SP_RGBA32_R_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_B_F(rgba), SP_RGBA32_A_F(rgba));
+}
+
+static void
+ink_cairo_convert_matrix(cairo_matrix_t &cm, Geom::Matrix const &m)
+{
+ cm.xx = m[0];
+ cm.xy = m[2];
+ cm.x0 = m[4];
+ cm.yx = m[1];
+ cm.yy = m[3];
+ cm.y0 = m[5];
+}
+
+void
+ink_cairo_transform(cairo_t *ct, Geom::Matrix const &m)
+{
+ cairo_matrix_t cm;
+ ink_cairo_convert_matrix(cm, m);
+ cairo_transform(ct, &cm);
+}
+
+void
+ink_cairo_pattern_set_matrix(cairo_pattern_t *cp, Geom::Matrix const &m)
+{
+ cairo_matrix_t cm;
+ ink_cairo_convert_matrix(cm, m);
+ cairo_pattern_set_matrix(cp, &cm);
+}
+
/*
Local Variables:
mode:c++
diff --git a/src/display/inkscape-cairo.h b/src/display/inkscape-cairo.h
index cb4d474a6..74dc10995 100644
--- a/src/display/inkscape-cairo.h
+++ b/src/display/inkscape-cairo.h
@@ -19,11 +19,14 @@
struct NRPixBlock;
class SPCanvasBuf;
-cairo_t *nr_create_cairo_context_canvasbuf (NRRectL *area, SPCanvasBuf *b);
cairo_t *nr_create_cairo_context (NRRectL *area, NRPixBlock *pb);
void feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv, Geom::Matrix trans, Geom::OptRect area, bool optimize_stroke, double stroke_width);
void feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv);
+void ink_cairo_set_source_rgba32(cairo_t *ct, guint32 rgba);
+void ink_cairo_transform(cairo_t *ct, Geom::Matrix const &m);
+void ink_cairo_pattern_set_matrix(cairo_pattern_t *cp, Geom::Matrix const &m);
+
#endif
/*
Local Variables:
diff --git a/src/display/nr-arena-item.cpp b/src/display/nr-arena-item.cpp
index b80df7273..d101a9e54 100644
--- a/src/display/nr-arena-item.cpp
+++ b/src/display/nr-arena-item.cpp
@@ -369,6 +369,7 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area
return item->state | NR_ARENA_ITEM_STATE_RENDER;
}
+#if 0
NRPixBlock cpb;
if (item->px) {
/* Has cache pixblock, render this and return */
@@ -386,9 +387,9 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area
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) &&
@@ -411,12 +412,14 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area
// 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
+/* if (item->clip || item->mask
|| ((item->opacity != 255) && !item->render_opacity)
|| (item->filter && filter) || item->background_new
- || (item->parent && item->parent->background_pb)) {
+ || (item->parent && item->parent->background_pb))*/
+ if (0) {
/* Setup and render item buffer */
NRPixBlock ipb;
@@ -575,7 +578,7 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area
item->background_pb = NULL;
} else {
/* Just render */
- unsigned int state = NR_ARENA_ITEM_VIRTUAL (item, render) (ct, item, &carea, dpb, flags);
+ unsigned int state = NR_ARENA_ITEM_VIRTUAL (item, render) (ct, item, const_cast<NRRectL*>(area), dpb, flags);
if (state & NR_ARENA_ITEM_STATE_INVALID) {
/* Clean up and return error */
if (dpb != pb)
diff --git a/src/display/nr-arena-shape.cpp b/src/display/nr-arena-shape.cpp
index a3b295a4e..9ec8f1100 100644
--- a/src/display/nr-arena-shape.cpp
+++ b/src/display/nr-arena-shape.cpp
@@ -145,6 +145,8 @@ nr_arena_shape_finalize(NRObject *object)
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->style) sp_style_unref(shape->style);
if (shape->curve) shape->curve->unref();
@@ -317,13 +319,15 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g
delete shape->stroke_shp;
shape->stroke_shp = NULL;
}
- if (shape->fill_painter) {
- sp_painter_free(shape->fill_painter);
- shape->fill_painter = NULL;
+
+ // clear Cairo patterns to force update
+ if (shape->fill_pattern) {
+ cairo_pattern_destroy(shape->fill_pattern);
+ shape->fill_pattern = NULL;
}
- if (shape->stroke_painter) {
- sp_painter_free(shape->stroke_painter);
- shape->stroke_painter = NULL;
+ if (shape->stroke_pattern) {
+ cairo_pattern_destroy(shape->stroke_pattern);
+ shape->stroke_pattern = NULL;
}
if (!shape->curve ||
@@ -366,31 +370,10 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g
item->bbox.x1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[0][1]));
item->bbox.y1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[1][1]));
- item->render_opacity = TRUE;
- if ( shape->_fill.paint.type() == NRArenaShape::Paint::SERVER ) {
- if (gc && gc->parent) {
- shape->fill_painter = sp_paint_server_painter_new(shape->_fill.paint.server(),
- gc->transform, gc->parent->transform,
- &shape->paintbox);
- }
- item->render_opacity = FALSE;
- }
- if ( shape->_stroke.paint.type() == NRArenaShape::Paint::SERVER ) {
- if (gc && gc->parent) {
- shape->stroke_painter = sp_paint_server_painter_new(shape->_stroke.paint.server(),
- gc->transform, gc->parent->transform,
- &shape->paintbox);
- }
- item->render_opacity = FALSE;
- }
- if ( (shape->_fill.paint.type() != NRArenaShape::Paint::NONE &&
- shape->_stroke.paint.type() != NRArenaShape::Paint::NONE)
- || (shape->markers)
- )
- {
- // don't merge item opacity with paint opacity if there is a stroke on the fill, or markers on stroke
- item->render_opacity = FALSE;
- }
+ // 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) {
@@ -745,92 +728,6 @@ cairo_arena_shape_render_outline(cairo_t *ct, NRArenaItem *item, Geom::OptRect a
return item->state;
}
-// cairo stroke rendering (flat color only so far!):
-// works on canvas, but wrongs the colors in nonpremul buffers: icons and png export
-// (need to switch them to premul before this can be enabled)
-void
-cairo_arena_shape_render_stroke(NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
-{
- NRArenaShape *shape = NR_ARENA_SHAPE(item);
- SPStyle const *style = shape->style;
-
- float const scale = shape->ctm.descrim();
-
- if (fabs(shape->_stroke.width * scale) < 0.01)
- return;
-
- cairo_t *ct = nr_create_cairo_context (area, pb);
-
- if (!ct)
- return;
-
- guint32 rgba;
- if ( item->render_opacity ) {
- rgba = shape->_stroke.paint.color().toRGBA32( shape->_stroke.opacity *
- SP_SCALE24_TO_FLOAT(style->opacity.value) );
- } else {
- rgba = shape->_stroke.paint.color().toRGBA32( shape->_stroke.opacity );
- }
-
- // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat
- // by setting color channels in the "wrong" order
- cairo_set_source_rgba(ct, SP_RGBA32_B_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_R_F(rgba), SP_RGBA32_A_F(rgba));
-
- float style_width = MAX(0.125, shape->_stroke.width * scale);
- cairo_set_line_width(ct, style_width);
-
- switch (shape->_stroke.cap) {
- case NRArenaShape::BUTT_CAP:
- cairo_set_line_cap(ct, CAIRO_LINE_CAP_BUTT);
- break;
- case NRArenaShape::ROUND_CAP:
- cairo_set_line_cap(ct, CAIRO_LINE_CAP_ROUND);
- break;
- case NRArenaShape::SQUARE_CAP:
- cairo_set_line_cap(ct, CAIRO_LINE_CAP_SQUARE);
- break;
- }
- switch (shape->_stroke.join) {
- case NRArenaShape::MITRE_JOIN:
- cairo_set_line_join(ct, CAIRO_LINE_JOIN_MITER);
- break;
- case NRArenaShape::ROUND_JOIN:
- cairo_set_line_join(ct, CAIRO_LINE_JOIN_ROUND);
- break;
- case NRArenaShape::BEVEL_JOIN:
- cairo_set_line_join(ct, CAIRO_LINE_JOIN_BEVEL);
- break;
- }
-
- cairo_set_miter_limit (ct, style->stroke_miterlimit.value);
-
- if (style->stroke_dash.n_dash) {
- NRVpathDash dash;
- dash.offset = style->stroke_dash.offset * scale;
- dash.n_dash = style->stroke_dash.n_dash;
- dash.dash = g_new(double, dash.n_dash);
- for (int i = 0; i < dash.n_dash; i++) {
- dash.dash[i] = style->stroke_dash.dash[i] * scale;
- }
- cairo_set_dash (ct, dash.dash, dash.n_dash, dash.offset);
- g_free(dash.dash);
- }
-
- cairo_set_tolerance(ct, 0.1);
- cairo_new_path(ct);
-
- feed_pathvector_to_cairo (ct, shape->curve->get_pathvector(), shape->ctm, to_2geom(area->upgrade()), true, style_width);
-
- cairo_stroke(ct);
-
- cairo_surface_t *cst = cairo_get_target(ct);
- cairo_destroy (ct);
- cairo_surface_finish (cst);
- cairo_surface_destroy (cst);
-
- pb->empty = FALSE;
-}
-
/**
* Renders the item. Markers are just composed into the parent buffer.
*/
@@ -841,9 +738,10 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock
if (!shape->curve) return item->state;
if (!shape->style) return item->state;
+ if (!ct) 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);
+ //bool print_colors_preview = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW);
if (outline) { // cairo outline rendering
@@ -875,6 +773,121 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock
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;
+ }
+ 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);
+ }
+ 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_shp) {
NRPixBlock m;
guint32 rgba;
@@ -917,10 +930,11 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock
nr_pixblock_release(&m);
}
- if (shape->stroke_shp && shape->_stroke.paint.type() == NRArenaShape::Paint::COLOR) {
+ if (shape->_stroke.paint.type() == NRArenaShape::Paint::COLOR) {
- // cairo_arena_shape_render_stroke(item, area, pb);
+
+
guint32 rgba;
NRPixBlock m;
@@ -951,6 +965,7 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock
pb->empty = FALSE;
nr_pixblock_release(&m);
+
} else if (shape->stroke_shp && shape->_stroke.paint.type() == NRArenaShape::Paint::SERVER) {
@@ -973,8 +988,8 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock
}
nr_pixblock_release(&m);
- }
-
+ }
+*/
} // non-cairo non-outline branch
/* Render markers into parent buffer */
diff --git a/src/display/nr-arena-shape.h b/src/display/nr-arena-shape.h
index 455757806..a88129286 100644
--- a/src/display/nr-arena-shape.h
+++ b/src/display/nr-arena-shape.h
@@ -115,6 +115,9 @@ struct NRArenaShape : public NRArenaItem {
/* State data */
Geom::Matrix ctm;
+ cairo_pattern_t *fill_pattern;
+ cairo_pattern_t *stroke_pattern;
+
SPPainter *fill_painter;
SPPainter *stroke_painter;
// the 2 cached polygons, for rasterizations uses
diff --git a/src/display/sodipodi-ctrl.cpp b/src/display/sodipodi-ctrl.cpp
index caa5fa697..c85fb586b 100644
--- a/src/display/sodipodi-ctrl.cpp
+++ b/src/display/sodipodi-ctrl.cpp
@@ -13,6 +13,7 @@
#include "display-forward.h"
#include "sodipodi-ctrl.h"
#include "libnr/nr-pixops.h"
+#include "display/inkscape-cairo.h"
enum {
ARG_0,
@@ -176,11 +177,14 @@ sp_ctrl_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
sp_canvas_item_request_update (item);
break;
- case ARG_FILL_COLOR:
- ctrl->fill_color = GTK_VALUE_INT (*arg);
+ case ARG_FILL_COLOR: {
+ // treat colors with zero alpha as opaque
+ guint32 fill = GTK_VALUE_INT (*arg);
+ fill = ((fill & 0xff) == 0 && fill) ? fill | 0xff : fill;
+ ctrl->fill_color = fill;
ctrl->build = FALSE;
sp_canvas_item_request_update (item);
- break;
+ } break;
case ARG_STROKED:
ctrl->stroked = GTK_VALUE_BOOL (*arg);
@@ -188,11 +192,14 @@ sp_ctrl_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
sp_canvas_item_request_update (item);
break;
- case ARG_STROKE_COLOR:
- ctrl->stroke_color = GTK_VALUE_INT (*arg);
+ case ARG_STROKE_COLOR: {
+ // treat colors with zero alpha as opaque
+ guint32 stroke = GTK_VALUE_INT (*arg);
+ stroke = ((stroke & 0xff) == 0 && stroke) ? stroke | 0xff : stroke;
+ ctrl->stroke_color = stroke;
ctrl->build = FALSE;
sp_canvas_item_request_update (item);
- break;
+ } break;
case ARG_PIXBUF:
pixbuf = (GdkPixbuf*)(GTK_VALUE_POINTER (*arg));
@@ -298,11 +305,11 @@ sp_ctrl_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
static void
sp_ctrl_build_cache (SPCtrl *ctrl)
{
- guchar * p, *q;
- gint size, x, y, z, s, a, side, c;
- guint8 fr, fg, fb, fa, sr, sg, sb, sa;
+ //guchar * p, *q;
+ //int size, x, y, z, s, a, side, c;
+ //guint8 fr, fg, fb, fa, sr, sg, sb, sa;
- if (ctrl->filled) {
+ /*if (ctrl->filled) {
fr = (ctrl->fill_color >> 24) & 0xff;
fg = (ctrl->fill_color >> 16) & 0xff;
fb = (ctrl->fill_color >> 8) & 0xff;
@@ -317,147 +324,85 @@ sp_ctrl_build_cache (SPCtrl *ctrl)
sa = (ctrl->stroke_color) & 0xff;
} else {
sr = fr; sg = fg; sb = fb; sa = fa;
- }
+ }*/
+ int w, h; // for clarity; w and h are always odd
+ w = h = (ctrl->span * 2 +1);
+ int c = ctrl->span ;
- side = (ctrl->span * 2 +1);
- c = ctrl->span ;
- size = (side) * (side) * 4;
- if (side < 2) return;
+ if (ctrl->cache) {
+ cairo_surface_finish(ctrl->cache);
+ cairo_surface_destroy(ctrl->cache);
+ }
+ ctrl->cache = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
+ cairo_t *cr = cairo_create(ctrl->cache);
- if (ctrl->cache)
- g_free (ctrl->cache);
- ctrl->cache = (guchar*)g_malloc (size);
+ bool supress_fill = false;
+ bool supress_paint = false;
switch (ctrl->shape) {
case SP_CTRL_SHAPE_SQUARE:
- p = ctrl->cache;
- for (x=0; x < side; x++) {
- *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
- }
- for (y = 2; y < side; y++) {
- *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
- for (x=2; x < side; x++) {
- *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa;
- }
- *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
- }
- for (x=0; x < side; x++) {
- *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
- }
+ cairo_rectangle(cr, 0, 0, w, h);
ctrl->build = TRUE;
break;
case SP_CTRL_SHAPE_DIAMOND:
- p = ctrl->cache;
- for (y = 0; y < side; y++) {
- z = abs (c - y);
- for (x = 0; x < z; x++) {
- *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
- }
- *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++;
- for (; x < side - z -1; x++) {
- *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa;
- }
- if (z != c) {
- *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++;
- }
- for (; x < side; x++) {
- *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
- }
- }
+ cairo_move_to(cr, c, 0); // c stands for "center" - it is half of the width / height
+ cairo_line_to(cr, w, c);
+ cairo_line_to(cr, c, h);
+ cairo_line_to(cr, 0, c);
+ cairo_close_path(cr);
+ ctrl->build = TRUE;
break;
case SP_CTRL_SHAPE_CIRCLE:
- p = ctrl->cache;
- q = p + size -1;
- s = -1;
- for (y = 0; y <= c ; y++) {
- a = abs (c - y);
- z = (gint)(0.0 + sqrt ((c+.4)*(c+.4) - a*a));
- x = 0;
- while (x < c-z) {
- *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
- *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; *q-- = 0x00;
- x++;
- }
- do {
- *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
- *q-- = sa; *q-- = sb; *q-- = sg; *q-- = sr;
- x++;
- } while (x < c-s);
- while (x < MIN(c+s+1, c+z)) {
- *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa;
- *q-- = fa; *q-- = fb; *q-- = fg; *q-- = fr;
- x++;
- }
- do {
- *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
- *q-- = sa; *q-- = sb; *q-- = sg; *q-- = sr;
- x++;
- } while (x <= c+z);
- while (x < side) {
- *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
- *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; *q-- = 0x00;
- x++;
- }
- s = z;
- }
+ cairo_arc(cr, 0.5+c, 0.5+c, c, 0, 2*M_PI);
+ cairo_close_path(cr);
ctrl->build = TRUE;
break;
case SP_CTRL_SHAPE_CROSS:
- p = ctrl->cache;
- for (y = 0; y < side; y++) {
- z = abs (c - y);
- for (x = 0; x < c-z; x++) {
- *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
- }
- *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++;
- for (; x < c + z; x++) {
- *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
- }
- if (z != 0) {
- *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++;
- }
- for (; x < side; x++) {
- *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
- }
- }
+ cairo_move_to(cr, 0.5+c, 0); // top stroke
+ cairo_line_to(cr, 0.5+c, c);
+ cairo_move_to(cr, w, 0.5+c); // right stroke
+ cairo_line_to(cr, w, c+1);
+ cairo_move_to(cr, 0.5+c, h); // bottom stroke
+ cairo_line_to(cr, 0.5+c, c+1);
+ cairo_move_to(cr, 0, 0.5+c); // left stroke
+ cairo_line_to(cr, c, 0.5+c);
+ supress_fill = true;
ctrl->build = TRUE;
break;
case SP_CTRL_SHAPE_BITMAP:
if (ctrl->pixbuf) {
- unsigned char *px;
- unsigned int rs;
- px = gdk_pixbuf_get_pixels (ctrl->pixbuf);
- rs = gdk_pixbuf_get_rowstride (ctrl->pixbuf);
- for (y = 0; y < side; y++){
- unsigned char *s, *d;
- s = px + y * rs;
- d = ctrl->cache + 4 * side * y;
- for (x = 0; x < side; x++) {
- if (s[3] < 0x80) {
- d[0] = 0x00;
- d[1] = 0x00;
- d[2] = 0x00;
- d[3] = 0x00;
- } else if (s[0] < 0x80) {
- d[0] = sr;
- d[1] = sg;
- d[2] = sb;
- d[3] = sa;
+ gdk_cairo_set_source_pixbuf(cr, ctrl->pixbuf, 0, 0);
+ cairo_paint(cr);
+ cairo_surface_flush(ctrl->cache);
+
+ // TODO lame!!! find a way to do this without direct pixel manipulation.
+ int stride = cairo_image_surface_get_stride(ctrl->cache);
+ guint32 *px = reinterpret_cast<guint32*>(cairo_image_surface_get_data(ctrl->cache));
+
+ // fix byte order. fill_color is 0xrrggbbaa, cairo needs 0xaarrggbb.
+ // both quantities are native-endian, so it should be portable.
+ guint32 fill = ctrl->fill_color;
+ guint32 stroke = ctrl->stroke_color;
+ fill = ((fill & 0xff) << 24) | ((fill & 0xffffff00) >> 8);
+ stroke = ((stroke & 0xff) << 24) | ((stroke & 0xffffff00) >> 8);
+
+ for (int i = 0; i < h; ++i) {
+ for (int j = 0; j < w; ++j) {
+ int index = i * stride / 4 + j;
+ if (px[index] & 0xff000000) {
+ px[index] = px[index] ? stroke : fill;
} else {
- d[0] = fr;
- d[1] = fg;
- d[2] = fb;
- d[3] = fa;
+ px[index] = 0;
}
- s += 4;
- d += 4;
}
}
+ cairo_surface_mark_dirty(ctrl->cache);
+ supress_paint = true;
} else {
g_print ("control has no pixmap\n");
}
@@ -466,19 +411,9 @@ sp_ctrl_build_cache (SPCtrl *ctrl)
case SP_CTRL_SHAPE_IMAGE:
if (ctrl->pixbuf) {
- guint r = gdk_pixbuf_get_rowstride (ctrl->pixbuf);
- guchar * pix;
- q = gdk_pixbuf_get_pixels (ctrl->pixbuf);
- p = ctrl->cache;
- for (y = 0; y < side; y++){
- pix = q + (y * r);
- for (x = 0; x < side; x++) {
- *p++ = *pix++;
- *p++ = *pix++;
- *p++ = *pix++;
- *p++ = *pix++;
- }
- }
+ gdk_cairo_set_source_pixbuf(cr, ctrl->pixbuf, 0, 0);
+ cairo_paint(cr);
+ supress_paint = true;
} else {
g_print ("control has no pixmap\n");
}
@@ -489,6 +424,20 @@ sp_ctrl_build_cache (SPCtrl *ctrl)
break;
}
+ if (ctrl->build && !supress_paint) {
+ if (ctrl->filled && !supress_fill) {
+ ink_cairo_set_source_rgba32(cr, ctrl->fill_color);
+ cairo_fill_preserve(cr);
+ }
+ if (ctrl->stroked) {
+ ink_cairo_set_source_rgba32(cr, ctrl->stroke_color);
+ cairo_set_line_width(cr, 2);
+ cairo_clip_preserve(cr);
+ cairo_stroke(cr);
+ }
+ }
+
+ cairo_destroy(cr);
}
// composite background, foreground, alpha for xor mode
@@ -499,29 +448,74 @@ sp_ctrl_build_cache (SPCtrl *ctrl)
static void
sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf)
{
- gint y0, y1, y, x0,x1,x;
- guchar *p, *q, a;
+ //gint y0, y1, y, x0,x1,x;
+ //guchar *p, *q, a;
SPCtrl *ctrl = SP_CTRL (item);
if (!ctrl->defined) return;
if ((!ctrl->filled) && (!ctrl->stroked)) return;
- sp_canvas_prepare_buffer (buf);
-
// the control-image is rendered into ctrl->cache
if (!ctrl->build) {
sp_ctrl_build_cache (ctrl);
}
+ cairo_set_source_surface(buf->ct, ctrl->cache,
+ ctrl->box.x0 - buf->rect.x0, ctrl->box.y0 - buf->rect.y0);
+ cairo_paint(buf->ct);
+
+ /*
+ double x0 = ctrl->box.x0;
+ double y0 = ctrl->box.y0;
+ double w = ctrl->box.x1 - ctrl->box.x0 + 1;
+ double h = ctrl->box.y1 - ctrl->box.y0 + 1;
+ //guint32 fill = ctrl->fill_color;
+ //fill = (fill & 0xff == 0 && fill) ? fill | 0xff : fill;
+
+
+ switch (ctrl->shape) {
+ case SP_CTRL_SHAPE_SQUARE:
+ cairo_rectangle(buf->ct, x0, y0, w, h);
+ break;
+ case SP_CTRL_SHAPE_DIAMOND:
+ cairo_move_to(buf->ct, x0 + w/2, y0);
+ cairo_line_to(buf->ct, x0 + w, y0 + h/2);
+ cairo_line_to(buf->ct, x0 + w/2, y0 + h);
+ cairo_line_to(buf->ct, x0, y0 + h/2);
+ cairo_close_path(buf->ct);
+ break;
+ //case SP_CTRL_SHAPE_CIRCLE:
+ default:
+ cairo_arc(buf->ct, x0 + w/2, y0 + h/2, w/2, 0, 2*M_PI);
+ cairo_close_path(buf->ct);
+ break;
+ }
+
+ //if (ctrl->mode == SP_CTRL_MODE_XOR) {
+ // cairo_set_operator(buf->ct, CAIRO_OPERATOR_XOR);
+ //}
+ if (ctrl->filled) {
+ ink_cairo_set_source_rgba32(buf->ct, ctrl->fill_color);
+ cairo_fill_preserve(buf->ct);
+ }
+ if (ctrl->stroked) {
+ ink_cairo_set_source_rgba32(buf->ct, ctrl->stroke_color);
+ cairo_set_line_width(buf->ct, 2);
+ cairo_clip_preserve(buf->ct);
+ cairo_stroke_preserve(buf->ct);
+ }
+
+ cairo_new_path(buf->ct);
+ cairo_restore(buf->ct);*/
+
+ #if 0
// then we render from ctrl->cache
y0 = MAX (ctrl->box.y0, buf->rect.y0);
y1 = MIN (ctrl->box.y1, buf->rect.y1 - 1);
x0 = MAX (ctrl->box.x0, buf->rect.x0);
x1 = MIN (ctrl->box.x1, buf->rect.x1 - 1);
- bool colormode;
-
for (y = y0; y <= y1; y++) {
p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 4;
q = ctrl->cache + ((y - ctrl->box.y0) * (ctrl->span*2+1) + (x0 - ctrl->box.x0)) * 4;
@@ -548,6 +542,7 @@ sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf)
}
}
}
+ #endif
ctrl->shown = TRUE;
}
diff --git a/src/display/sodipodi-ctrl.h b/src/display/sodipodi-ctrl.h
index 859735e4f..ed4db27fe 100644
--- a/src/display/sodipodi-ctrl.h
+++ b/src/display/sodipodi-ctrl.h
@@ -50,7 +50,7 @@ struct SPCtrl : public SPCanvasItem{
bool _moved;
NRRectL box; /* NB! x1 & y1 are included */
- guchar *cache;
+ cairo_surface_t *cache;
GdkPixbuf * pixbuf;
void moveto(Geom::Point const p);
diff --git a/src/display/sodipodi-ctrlrect.cpp b/src/display/sodipodi-ctrlrect.cpp
index dcd6dc0a6..09bfde6fb 100644
--- a/src/display/sodipodi-ctrlrect.cpp
+++ b/src/display/sodipodi-ctrlrect.cpp
@@ -18,7 +18,7 @@
#include "display-forward.h"
#include "sp-canvas-util.h"
#include "sodipodi-ctrlrect.h"
-#include "libnr/nr-pixops.h"
+#include "display/inkscape-cairo.h"
/*
* Currently we do not have point method, as it should always be painted
@@ -84,7 +84,7 @@ static void sp_ctrlrect_destroy(GtkObject *object)
(* GTK_OBJECT_CLASS(parent_class)->destroy)(object);
}
}
-
+#if 0
/* FIXME: use definitions from somewhere else */
#define RGBA_R(v) ((v) >> 24)
#define RGBA_G(v) (((v) >> 16) & 0xff)
@@ -154,6 +154,7 @@ static void sp_ctrlrect_area(SPCanvasBuf *buf, gint xs, gint ys, gint xe, gint y
}
}
}
+#endif
static void sp_ctrlrect_render(SPCanvasItem *item, SPCanvasBuf *buf)
{
@@ -189,13 +190,38 @@ void CtrlRect::init()
void CtrlRect::render(SPCanvasBuf *buf)
{
+ static double const dashes[2] = {4.0, 4.0};
+
if ((_area.x0 != 0 || _area.x1 != 0 || _area.y0 != 0 || _area.y1 != 0) &&
(_area.x0 < buf->rect.x1) &&
(_area.y0 < buf->rect.y1) &&
((_area.x1 + _shadow_size) >= buf->rect.x0) &&
- ((_area.y1 + _shadow_size) >= buf->rect.y0)) {
- sp_canvas_prepare_buffer(buf);
+ ((_area.y1 + _shadow_size) >= buf->rect.y0))
+ {
+ cairo_save(buf->ct);
+ cairo_translate(buf->ct, -buf->rect.x0, -buf->rect.y0);
+ cairo_set_line_width(buf->ct, 1);
+ if (_dashed) cairo_set_dash(buf->ct, dashes, 2, 0);
+ cairo_rectangle(buf->ct, 0.5 + _area.x0, 0.5 + _area.y0,
+ _area.x1 - _area.x0, _area.y1 - _area.y0);
+ if (_has_fill) {
+ ink_cairo_set_source_rgba32(buf->ct, _fill_color);
+ cairo_fill_preserve(buf->ct);
+ }
+ ink_cairo_set_source_rgba32(buf->ct, _border_color);
+ cairo_stroke(buf->ct);
+
+ if (_shadow_size > 0) {
+ ink_cairo_set_source_rgba32(buf->ct, _shadow_color);
+ cairo_rectangle(buf->ct, 1 + _area.x1, _area.y0 + _shadow_size,
+ _shadow_size, _area.y1 - _area.y0 + 1); // right shadow
+ cairo_rectangle(buf->ct, _area.x0 + _shadow_size, 1 + _area.y1,
+ _area.x1 - _area.x0 - _shadow_size + 1, _shadow_size);
+ cairo_fill(buf->ct);
+ }
+ cairo_restore(buf->ct);
+#if 0
/* Top */
sp_ctrlrect_hline(buf, _area.y0, _area.x0, _area.x1, _border_color, _dashed);
/* Bottom */
@@ -217,6 +243,7 @@ void CtrlRect::render(SPCanvasBuf *buf)
sp_ctrlrect_area(buf, _area.x0 + 1, _area.y0 + 1,
_area.x1 - 1, _area.y1 - 1, _fill_color);
}
+#endif
}
}
diff --git a/src/display/sp-canvas-util.cpp b/src/display/sp-canvas-util.cpp
index a23b157df..970fea0e5 100644
--- a/src/display/sp-canvas-util.cpp
+++ b/src/display/sp-canvas-util.cpp
@@ -43,36 +43,11 @@ void
sp_canvas_prepare_buffer (SPCanvasBuf *buf)
{
if (buf->is_empty) {
- sp_canvas_clear_buffer(buf);
- buf->is_empty = false;
- }
-}
-
-void
-sp_canvas_clear_buffer (SPCanvasBuf *buf)
-{
- unsigned char r, g, b;
-
- r = (buf->bg_color >> 16) & 0xff;
- g = (buf->bg_color >> 8) & 0xff;
- b = buf->bg_color & 0xff;
-
- if ((r != g) || (r != b)) {
- int x, y;
- for (y = buf->rect.y0; y < buf->rect.y1; y++) {
- unsigned char *p;
- p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride;
- for (x = buf->rect.x0; x < buf->rect.x1; x++) {
- *p++ = r;
- *p++ = g;
- *p++ = b;
- }
- }
- } else {
int y;
for (y = buf->rect.y0; y < buf->rect.y1; y++) {
- memset (buf->buf + (y - buf->rect.y0) * buf->buf_rowstride, r, 4 * (buf->rect.x1 - buf->rect.x0));
+ memset (buf->buf + (y - buf->rect.y0) * buf->buf_rowstride, 0, 4 * (buf->rect.x1 - buf->rect.x0));
}
+ buf->is_empty = false;
}
}
diff --git a/src/display/sp-canvas-util.h b/src/display/sp-canvas-util.h
index 4708126e5..e86eeba20 100644
--- a/src/display/sp-canvas-util.h
+++ b/src/display/sp-canvas-util.h
@@ -21,11 +21,6 @@ void sp_canvas_update_bbox (SPCanvasItem *item, int x1, int y1, int x2, int y2);
void sp_canvas_item_reset_bounds (SPCanvasItem *item);
void sp_canvas_prepare_buffer (SPCanvasBuf *buf);
-/* fill buffer with background color */
-
-void
-sp_canvas_clear_buffer (SPCanvasBuf * buf);
-
/* get i2p (item to parent) affine transformation as general 6-element array */
Geom::Matrix sp_canvas_item_i2p_affine (SPCanvasItem * item);
diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp
index c6778c8c5..29a5cd740 100644
--- a/src/display/sp-canvas.cpp
+++ b/src/display/sp-canvas.cpp
@@ -1649,18 +1649,32 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1,
buf.visible_rect.y0 = draw_y1;
buf.visible_rect.x1 = draw_x2;
buf.visible_rect.y1 = draw_y2;
- GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL];
- buf.bg_color = (((color->red & 0xff00) << 8)
- | (color->green & 0xff00)
- | (color->blue >> 8));
buf.is_empty = true;
-
- buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf);
+ //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);
+
+ // 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_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);
+
+ gdk_cairo_set_source_color(buf.ct, &widget->style->bg[GTK_STATE_NORMAL]);
+ cairo_set_operator(buf.ct, CAIRO_OPERATOR_SOURCE);
+ //cairo_rectangle(buf.ct, 0, 0, x1 - x0, y1 - y0);
+ cairo_paint(buf.ct);
+ cairo_set_operator(buf.ct, CAIRO_OPERATOR_OVER);
if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) {
SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf);
}
+#if 0
#if ENABLE_LCMS
cmsHTRANSFORM transf = 0;
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
@@ -1702,22 +1716,22 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1,
// use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo
// cannot handle at all. Still, this way is currently faster even despite the blit with squeeze.
-///#define CANVAS_OUTPUT_VIA_CAIRO
+//#define CANVAS_OUTPUT_VIA_CAIRO
#ifdef CANVAS_OUTPUT_VIA_CAIRO
- buf.cst = cairo_image_surface_create_for_data (
+ cairo_surface_t *cst = cairo_image_surface_create_for_data (
buf.buf,
CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused
x1 - x0, y1 - y0,
buf.buf_rowstride
);
cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas));
- cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0);
+ cairo_set_source_surface (window_ct, cst, x0 - canvas->x0, y0 - canvas->y0);
cairo_paint (window_ct);
cairo_destroy (window_ct);
- cairo_surface_finish (buf.cst);
- cairo_surface_destroy (buf.cst);
+ cairo_surface_finish (cst);
+ cairo_surface_destroy (cst);
#else
@@ -1746,11 +1760,12 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1,
nr_pixblock_release (&b4);
#endif
}
+#endif
- cairo_surface_t *cst = cairo_get_target(buf.ct);
+ //cairo_surface_t *cst = cairo_get_target(buf.ct);
cairo_destroy (buf.ct);
- cairo_surface_finish (cst);
- cairo_surface_destroy (cst);
+ //cairo_surface_finish (cst);
+ //cairo_surface_destroy (cst);
if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
nr_pixelstore_256K_free (buf.buf);
@@ -1816,11 +1831,21 @@ sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect)
if (bw * bh < setup->max_pixels) {
// We are small enough
+ 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);
+
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);
return 1;
}
diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h
index a2af080ef..6a2ee074e 100644
--- a/src/display/sp-canvas.h
+++ b/src/display/sp-canvas.h
@@ -52,18 +52,16 @@ enum {
};
/**
- * The canvas buf contains the actual pixels.
+ * Structure used when rendering canvas items.
*/
-struct SPCanvasBuf{
- guchar *buf;
- int buf_rowstride;
+struct SPCanvasBuf {
+ cairo_t *ct;
NRRectL rect;
NRRectL visible_rect;
- /// Background color, given as 0xrrggbb
- guint32 bg_color;
- // If empty, ignore contents of buffer and use a solid area of bg_color
+
+ unsigned char *buf;
+ int buf_rowstride;
bool is_empty;
- cairo_t *ct;
};
/**
diff --git a/src/display/sp-ctrlline.cpp b/src/display/sp-ctrlline.cpp
index 033c8d1f8..043736d94 100644
--- a/src/display/sp-ctrlline.cpp
+++ b/src/display/sp-ctrlline.cpp
@@ -106,11 +106,7 @@ sp_ctrlline_render (SPCanvasItem *item, SPCanvasBuf *buf)
if (cl->s == cl->e)
return;
- sp_canvas_prepare_buffer (buf);
-
- guint32 rgba = cl->rgba;
- cairo_set_source_rgba(buf->ct, SP_RGBA32_B_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_R_F(rgba), SP_RGBA32_A_F(rgba));
-
+ ink_cairo_set_source_rgba32(buf->ct, cl->rgba);
cairo_set_line_width(buf->ct, 1);
cairo_new_path(buf->ct);
diff --git a/src/sp-gradient.cpp b/src/sp-gradient.cpp
index 3d4d69672..f63436e71 100644
--- a/src/sp-gradient.cpp
+++ b/src/sp-gradient.cpp
@@ -31,6 +31,7 @@
#include <sigc++/functors/ptr_fun.h>
#include <sigc++/adaptors/bind.h>
+#include "display/inkscape-cairo.h"
#include "libnr/nr-gradient.h"
#include "libnr/nr-pixops.h"
#include "svg/svg.h"
@@ -1425,7 +1426,7 @@ static SPPainter *sp_lineargradient_painter_new(SPPaintServer *ps,
Geom::Matrix const &parent_transform,
NRRect const *bbox);
static void sp_lineargradient_painter_free(SPPaintServer *ps, SPPainter *painter);
-
+static cairo_pattern_t *sp_lineargradient_create_pattern(SPPaintServer *ps, cairo_t *ct, NRRect const *bbox, double opacity);
static void sp_lg_fill(SPPainter *painter, NRPixBlock *pb);
static SPGradientClass *lg_parent_class;
@@ -1469,6 +1470,7 @@ static void sp_lineargradient_class_init(SPLinearGradientClass *klass)
ps_class->painter_new = sp_lineargradient_painter_new;
ps_class->painter_free = sp_lineargradient_painter_free;
+ ps_class->pattern_new = sp_lineargradient_create_pattern;
}
/**
@@ -1700,6 +1702,7 @@ static SPPainter *sp_radialgradient_painter_new(SPPaintServer *ps,
Geom::Matrix const &parent_transform,
NRRect const *bbox);
static void sp_radialgradient_painter_free(SPPaintServer *ps, SPPainter *painter);
+static cairo_pattern_t *sp_radialgradient_create_pattern(SPPaintServer *ps, cairo_t *ct, NRRect const *bbox, double opacity);
static void sp_rg_fill(SPPainter *painter, NRPixBlock *pb);
@@ -1744,6 +1747,7 @@ static void sp_radialgradient_class_init(SPRadialGradientClass *klass)
ps_class->painter_new = sp_radialgradient_painter_new;
ps_class->painter_free = sp_radialgradient_painter_free;
+ ps_class->pattern_new = sp_radialgradient_create_pattern;
}
/**
@@ -1950,6 +1954,86 @@ sp_rg_fill(SPPainter *painter, NRPixBlock *pb)
nr_render((NRRenderer *) &rgp->rgr, pb, NULL);
}
+/* CAIRO RENDERING STUFF */
+
+static void
+sp_gradient_pattern_common_setup(cairo_pattern_t *cp,
+ SPGradient *gr,
+ NRRect const *bbox,
+ double opacity)
+{
+ // set spread type
+ switch (sp_gradient_get_spread(gr)) {
+ case SP_GRADIENT_SPREAD_REFLECT:
+ cairo_pattern_set_extend(cp, CAIRO_EXTEND_REFLECT);
+ break;
+ case SP_GRADIENT_SPREAD_REPEAT:
+ cairo_pattern_set_extend(cp, CAIRO_EXTEND_REPEAT);
+ break;
+ case SP_GRADIENT_SPREAD_PAD:
+ default:
+ cairo_pattern_set_extend(cp, CAIRO_EXTEND_PAD);
+ break;
+ }
+
+ // add stops
+ for (std::vector<SPGradientStop>::iterator i = gr->vector.stops.begin();
+ i != gr->vector.stops.end(); ++i)
+ {
+ // multiply stop opacity by paint opacity
+ cairo_pattern_add_color_stop_rgba(cp, i->offset,
+ i->color.v.c[0], i->color.v.c[1], i->color.v.c[2], i->opacity * opacity);
+ }
+
+ // set pattern matrix
+ Geom::Matrix gs2user = gr->gradientTransform;
+ if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
+ Geom::Matrix bbox2user(bbox->x1 - bbox->x0, 0, 0, bbox->y1 - bbox->y0, bbox->x0, bbox->y0);
+ gs2user *= bbox2user;
+ }
+ ink_cairo_pattern_set_matrix(cp, gs2user.inverse());
+}
+
+static cairo_pattern_t *
+sp_radialgradient_create_pattern(SPPaintServer *ps,
+ cairo_t */* ct */,
+ NRRect const *bbox,
+ double opacity)
+{
+ SPRadialGradient *rg = SP_RADIALGRADIENT(ps);
+ SPGradient *gr = SP_GRADIENT(ps);
+
+ if (!gr->color) sp_gradient_ensure_colors(gr);
+
+ cairo_pattern_t *cp = cairo_pattern_create_radial(
+ rg->fx.computed, rg->fy.computed, 0,
+ rg->cx.computed, rg->cy.computed, rg->r.computed);
+
+ sp_gradient_pattern_common_setup(cp, gr, bbox, opacity);
+
+ return cp;
+}
+
+static cairo_pattern_t *
+sp_lineargradient_create_pattern(SPPaintServer *ps,
+ cairo_t */* ct */,
+ NRRect const *bbox,
+ double opacity)
+{
+ SPLinearGradient *lg = SP_LINEARGRADIENT(ps);
+ SPGradient *gr = SP_GRADIENT(ps);
+
+ if (!gr->color) sp_gradient_ensure_colors(gr);
+
+ cairo_pattern_t *cp = cairo_pattern_create_linear(
+ lg->x1.computed, lg->y1.computed,
+ lg->x2.computed, lg->y2.computed);
+
+ sp_gradient_pattern_common_setup(cp, gr, bbox, opacity);
+
+ return cp;
+}
+
/*
Local Variables:
mode:c++
diff --git a/src/sp-paint-server.cpp b/src/sp-paint-server.cpp
index 258323a93..e49e6a378 100644
--- a/src/sp-paint-server.cpp
+++ b/src/sp-paint-server.cpp
@@ -26,6 +26,7 @@ static void sp_paint_server_init(SPPaintServer *ps);
static void sp_paint_server_release(SPObject *object);
static void sp_painter_stale_fill(SPPainter *painter, NRPixBlock *pb);
+static cairo_pattern_t *sp_paint_server_create_dummy_pattern(SPPaintServer *ps, cairo_t *ct, NRRect const *bbox, double opacity);
static SPObjectClass *parent_class;
static GSList *stale_painters = NULL;
@@ -55,6 +56,7 @@ static void sp_paint_server_class_init(SPPaintServerClass *psc)
{
SPObjectClass *sp_object_class = (SPObjectClass *) psc;
sp_object_class->release = sp_paint_server_release;
+ psc->pattern_new = sp_paint_server_create_dummy_pattern;
parent_class = (SPObjectClass *) g_type_class_ref(SP_TYPE_OBJECT);
}
@@ -106,6 +108,36 @@ SPPainter *sp_paint_server_painter_new(SPPaintServer *ps,
return painter;
}
+cairo_pattern_t *sp_paint_server_create_pattern(SPPaintServer *ps,
+ cairo_t *ct,
+ NRRect const *bbox,
+ double opacity)
+{
+ // NOTE: the ct argument is used for when rendering patterns
+ // to create a group, instead of explicitly creating a temporary surface
+ g_return_val_if_fail(ps != NULL, NULL);
+ g_return_val_if_fail(SP_IS_PAINT_SERVER(ps), NULL);
+ g_return_val_if_fail(bbox != NULL, NULL);
+
+ cairo_pattern_t *cp = NULL;
+ SPPaintServerClass *psc = (SPPaintServerClass *) G_OBJECT_GET_CLASS(ps);
+ if ( psc->pattern_new ) {
+ cp = (*psc->pattern_new)(ps, ct, bbox, opacity);
+ }
+
+ return cp;
+}
+
+static cairo_pattern_t *
+sp_paint_server_create_dummy_pattern(SPPaintServer */*ps*/,
+ cairo_t */* ct */,
+ NRRect const */*bbox*/,
+ double /* opacity */)
+{
+ cairo_pattern_t *cp = cairo_pattern_create_rgb(1.0, 0.0, 1.0);
+ return cp;
+}
+
static void sp_paint_server_painter_free(SPPaintServer *ps, SPPainter *painter)
{
g_return_if_fail(ps != NULL);
diff --git a/src/sp-paint-server.h b/src/sp-paint-server.h
index a76daf4d1..d1fc9b7ac 100644
--- a/src/sp-paint-server.h
+++ b/src/sp-paint-server.h
@@ -15,6 +15,7 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#include <cairo.h>
#include <libnr/nr-pixblock.h>
#include "sp-object.h"
#include "uri-references.h"
@@ -57,11 +58,14 @@ struct SPPaintServerClass {
SPPainter * (* painter_new) (SPPaintServer *ps, Geom::Matrix const &full_transform, Geom::Matrix const &parent_transform, const NRRect *bbox);
/** Free SPPaint instance. */
void (* painter_free) (SPPaintServer *ps, SPPainter *painter);
+
+ cairo_pattern_t *(*pattern_new)(SPPaintServer *ps, cairo_t *ct, const NRRect *bbox, double opacity);
};
GType sp_paint_server_get_type (void);
SPPainter *sp_paint_server_painter_new (SPPaintServer *ps, Geom::Matrix const &full_transform, Geom::Matrix const &parent_transform, const NRRect *bbox);
+cairo_pattern_t *sp_paint_server_create_pattern(SPPaintServer *ps, cairo_t *ct, NRRect const *bbox, double opacity);
SPPainter *sp_painter_free (SPPainter *painter);
diff --git a/src/sp-pattern.cpp b/src/sp-pattern.cpp
index ec0d0d576..8d1e8dab5 100644
--- a/src/sp-pattern.cpp
+++ b/src/sp-pattern.cpp
@@ -23,6 +23,7 @@
#include <2geom/transforms.h>
#include "macros.h"
#include "svg/svg.h"
+#include "display/inkscape-cairo.h"
#include "display/nr-arena.h"
#include "display/nr-arena-group.h"
#include "attributes.h"
@@ -76,6 +77,7 @@ static void pattern_ref_modified (SPObject *ref, guint flags, SPPattern *pattern
static SPPainter *sp_pattern_painter_new (SPPaintServer *ps, Geom::Matrix const &full_transform, Geom::Matrix const &parent_transform, const NRRect *bbox);
static void sp_pattern_painter_free (SPPaintServer *ps, SPPainter *painter);
+static cairo_pattern_t *sp_pattern_create_pattern(SPPaintServer *ps, cairo_t *ct, NRRect const *bbox, double opacity);
static SPPaintServerClass * pattern_parent_class;
@@ -123,6 +125,7 @@ sp_pattern_class_init (SPPatternClass *klass)
ps_class->painter_new = sp_pattern_painter_new;
ps_class->painter_free = sp_pattern_painter_free;
+ ps_class->pattern_new = sp_pattern_create_pattern;
}
static void
@@ -1016,3 +1019,128 @@ sp_pat_fill (SPPainter *painter, NRPixBlock *pb)
}
}
}
+
+static cairo_pattern_t *
+sp_pattern_create_pattern(SPPaintServer *ps,
+ cairo_t *base_ct,
+ NRRect const *bbox,
+ double opacity)
+{
+ SPPattern *pat = SP_PATTERN (ps);
+ Geom::Matrix ps2user;
+ bool needs_opacity = (1.0 - opacity) >= 1e-3;
+ bool visible = opacity >= 1e-3;
+
+ if (!visible)
+ return NULL;
+
+ if (pat->viewBox_set) {
+ gdouble tmp_x = pattern_width (pat) / (pattern_viewBox(pat)->x1 - pattern_viewBox(pat)->x0);
+ gdouble tmp_y = pattern_height (pat) / (pattern_viewBox(pat)->y1 - pattern_viewBox(pat)->y0);
+
+ // FIXME: preserveAspectRatio must be taken into account here too!
+ Geom::Matrix vb2ps (tmp_x, 0.0, 0.0, tmp_y, pattern_x(pat) - pattern_viewBox(pat)->x0 * tmp_x, pattern_y(pat) - pattern_viewBox(pat)->y0 * tmp_y);
+
+ ps2user = vb2ps * pattern_patternTransform(pat);
+ } else {
+ /* No viewbox, have to parse units */
+ ps2user = pattern_patternTransform(pat);
+ if (pattern_patternContentUnits (pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
+ /* BBox to user coordinate system */
+ Geom::Matrix bbox2user (bbox->x1 - bbox->x0, 0.0, 0.0, bbox->y1 - bbox->y0, bbox->x0, bbox->y0);
+ ps2user *= bbox2user;
+ }
+ ps2user = Geom::Translate (pattern_x (pat), pattern_y (pat)) * ps2user;
+ }
+
+ /* Create arena */
+ NRArena *arena = NRArena::create();
+ unsigned int dkey = sp_item_display_key_new (1);
+ NRArenaGroup *root = NRArenaGroup::create(arena);
+
+ /* Show items */
+ for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
+ // find the first one with item children
+ if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) {
+ for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
+ if (SP_IS_ITEM (child)) {
+ // for each item in pattern, show it on our arena, add to the group,
+ // and connect to the release signal in case the item gets deleted
+ NRArenaItem *cai;
+ cai = sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
+ nr_arena_item_append_child (root, cai);
+ }
+ }
+ break; // do not go further up the chain if children are found
+ }
+ }
+
+ double x = pattern_x(pat);
+ double y = pattern_y(pat);
+ double w = pattern_width(pat);
+ double h = pattern_height(pat);
+
+ cairo_matrix_t cm;
+ cairo_get_matrix(base_ct, &cm);
+ Geom::Matrix full(cm.xx, cm.yx, cm.xy, cm.yy, 0, 0);
+
+ // oversample the pattern slightly
+ // TODO: find optimum value. Maybe sqrt(2)?
+ Geom::Point c(Geom::Point(w, h)*ps2user.descrim()*full.descrim()*1.2);
+ c[Geom::X] = ceil(c[Geom::X]);
+ c[Geom::Y] = ceil(c[Geom::Y]);
+ Geom::Matrix t = Geom::Scale(c[Geom::X]/w, c[Geom::Y]/h);
+
+ NRRectL one_tile;
+ one_tile.x0 = (int) floor(x);
+ one_tile.y0 = (int) floor(y);
+ one_tile.x1 = (int) ceil(x+w);
+ one_tile.y1 = (int) ceil(y+h);
+
+ cairo_surface_t *target = cairo_get_target(base_ct);
+ cairo_surface_t *temp = cairo_surface_create_similar(target, CAIRO_CONTENT_COLOR_ALPHA,
+ c[Geom::X], c[Geom::Y]);
+ cairo_t *ct = cairo_create(temp);
+ ink_cairo_transform(ct, t);
+
+ // render pattern.
+ if (needs_opacity) {
+ cairo_push_group(ct); // this group is for pattern + opacity
+ }
+
+ // TODO: make sure there are no leaks.
+ NRPixBlock pb;
+ nr_pixblock_setup (&pb, NR_PIXBLOCK_MODE_R8G8B8A8N, one_tile.x0, one_tile.y0,
+ one_tile.x1, one_tile.y1, TRUE);
+ NRGC gc(NULL);
+ gc.transform = Geom::identity();
+ nr_arena_item_invoke_update (root, NULL, &gc, NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_ALL);
+ nr_arena_item_invoke_render (ct, root, &one_tile, &pb, 0);
+ nr_object_unref(arena);
+ nr_pixblock_release(&pb);
+
+ if (needs_opacity) {
+ cairo_pop_group_to_source(ct); // pop raw pattern
+ cairo_paint_with_alpha(ct, opacity); // apply opacity
+ }
+
+ cairo_pattern_t *cp = cairo_pattern_create_for_surface(temp);
+ cairo_surface_destroy(temp);
+
+ // Apply transformation to user space. Also compensate for oversampling.
+ ink_cairo_pattern_set_matrix(cp, ps2user.inverse() * t);
+ cairo_pattern_set_extend(cp, CAIRO_EXTEND_REPEAT);
+
+ return cp;
+}
+
+/*
+ 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 :