diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2010-06-22 21:36:47 +0000 |
|---|---|---|
| committer | Krzysztof KosiĆski <tweenk.pl@gmail.com> | 2010-06-22 21:36:47 +0000 |
| commit | 917de2bab458d51b4149a1b794cbb1b6b9562171 (patch) | |
| tree | 9abd6fe6497b8b0965bb85caf4b4456ff2099989 /src | |
| parent | * [INTL:sk] Slovak translation update (diff) | |
| download | inkscape-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.cpp | 25 | ||||
| -rw-r--r-- | src/display/canvas-bpath.cpp | 22 | ||||
| -rw-r--r-- | src/display/inkscape-cairo.cpp | 35 | ||||
| -rw-r--r-- | src/display/inkscape-cairo.h | 5 | ||||
| -rw-r--r-- | src/display/nr-arena-item.cpp | 13 | ||||
| -rw-r--r-- | src/display/nr-arena-shape.cpp | 259 | ||||
| -rw-r--r-- | src/display/nr-arena-shape.h | 3 | ||||
| -rw-r--r-- | src/display/sodipodi-ctrl.cpp | 283 | ||||
| -rw-r--r-- | src/display/sodipodi-ctrl.h | 2 | ||||
| -rw-r--r-- | src/display/sodipodi-ctrlrect.cpp | 35 | ||||
| -rw-r--r-- | src/display/sp-canvas-util.cpp | 29 | ||||
| -rw-r--r-- | src/display/sp-canvas-util.h | 5 | ||||
| -rw-r--r-- | src/display/sp-canvas.cpp | 53 | ||||
| -rw-r--r-- | src/display/sp-canvas.h | 14 | ||||
| -rw-r--r-- | src/display/sp-ctrlline.cpp | 6 | ||||
| -rw-r--r-- | src/sp-gradient.cpp | 86 | ||||
| -rw-r--r-- | src/sp-paint-server.cpp | 32 | ||||
| -rw-r--r-- | src/sp-paint-server.h | 4 | ||||
| -rw-r--r-- | src/sp-pattern.cpp | 128 |
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 : |
