X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fextension%2Finternal%2Fcairo-renderer.cpp;h=8d4b95f855b2dcb81422f23ec5a5d0a84f16435b;hb=b5ea3ee18029be0e1660324cb5df658ec6dc99d3;hp=f02189332624ff435a729f88c0f29edda16d207b;hpb=d444ed610362dab3c3e727d1b110312318b84cb8;p=inkscape.git diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index f02189332..8d4b95f85 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -8,7 +8,7 @@ * Miklos Erdelyi * * Copyright (C) 2006 Miklos Erdelyi - * + * * Licensed under GNU GPL */ @@ -16,8 +16,6 @@ # include "config.h" #endif -#ifdef HAVE_CAIRO_PDF - #ifndef PANGO_ENABLE_BACKEND #define PANGO_ENABLE_BACKEND #endif @@ -30,12 +28,22 @@ #include #include -#include #include #include #include #include +#include "libnr/nr-matrix-rotate-ops.h" +#include "libnr/nr-matrix-translate-ops.h" +#include "libnr/nr-rotate-fns.h" +#include "libnr/nr-scale-ops.h" +#include "libnr/nr-scale-translate-ops.h" +#include "libnr/nr-translate-matrix-ops.h" +#include "libnr/nr-translate-scale-ops.h" +#include "libnr/nr-convert2geom.h" +#include <2geom/transforms.h> +#include <2geom/pathvector.h> + #include #include @@ -46,9 +54,8 @@ #include "display/canvas-bpath.h" #include "sp-item.h" #include "sp-item-group.h" -#include "marker-status.h" #include "style.h" -#include "sp-marker.h" +#include "marker.h" #include "sp-linear-gradient.h" #include "sp-radial-gradient.h" #include "sp-root.h" @@ -63,6 +70,8 @@ #include "sp-clippath.h" #include +#include "helper/png-write.h" +#include "helper/pixbuf-ops.h" #include "cairo-renderer.h" #include "cairo-render-context.h" @@ -126,7 +135,7 @@ CairoRenderer::createContext(void) // create initial render state CairoRenderState *state = new_context->_createState(); - nr_matrix_set_identity(&state->transform); + state->transform = Geom::identity(); new_context->_state_stack = g_slist_prepend(new_context->_state_stack, state); new_context->_state = state; @@ -136,7 +145,7 @@ CairoRenderer::createContext(void) void CairoRenderer::destroyContext(CairoRenderContext *ctx) { - delete ctx; + delete ctx; } /* @@ -154,7 +163,9 @@ static void sp_text_render(SPItem *item, CairoRenderContext *ctx); static void sp_flowtext_render(SPItem *item, CairoRenderContext *ctx); static void sp_image_render(SPItem *item, CairoRenderContext *ctx); static void sp_symbol_render(SPItem *item, CairoRenderContext *ctx); +static void sp_asbitmap_render(SPItem *item, CairoRenderContext *ctx); +/* TODO FIXME: this does not render painting-marker-01-f.svg of SVG1.1 Test suite correctly. (orientation of one of the markers middle left ) */ static void sp_shape_render (SPItem *item, CairoRenderContext *ctx) { NRRect pbox; @@ -164,38 +175,90 @@ static void sp_shape_render (SPItem *item, CairoRenderContext *ctx) if (!shape->curve) return; /* fixme: Think (Lauris) */ - sp_item_invoke_bbox(item, &pbox, NR::identity(), TRUE); - NR::Matrix const i2d = sp_item_i2d_affine(item); + sp_item_invoke_bbox(item, &pbox, Geom::identity(), TRUE); SPStyle* style = SP_OBJECT_STYLE (item); CairoRenderer *renderer = ctx->getRenderer(); - NRBPath bp; - bp.path = SP_CURVE_BPATH(shape->curve); + Geom::PathVector const & pathv = shape->curve->get_pathvector(); - ctx->renderPath(&bp, style, &pbox); + ctx->renderPathVector(pathv, style, &pbox); - for (NArtBpath* bp = SP_CURVE_BPATH(shape->curve); bp->code != NR_END; bp++) { - for (int m = SP_MARKER_LOC_START; m < SP_MARKER_LOC_QTY; m++) { - if (sp_shape_marker_required (shape, m, bp)) { + /* TODO: make code prettier: lots of variables can be taken out of the loop! */ + for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) { + if ( shape->marker[SP_MARKER_LOC_START] ) { + SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_START]); + SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_START])); - SPMarker* marker = SP_MARKER (shape->marker[m]); - SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[m])); + Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front())); - NR::Matrix tr(sp_shape_marker_get_transform(shape, bp)); + if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) { + tr = Geom::Scale(style->stroke_width.computed) * tr; + } + + tr = (Geom::Matrix)marker_item->transform * (Geom::Matrix)marker->c2p * tr; + + Geom::Matrix old_tr = marker_item->transform; + marker_item->transform = tr; + renderer->renderItem (ctx, marker_item); + marker_item->transform = old_tr; + } + + if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) { + Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve + Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve + while (curve_it2 != path_it->end_default()) + { + /* Put marker between curve_it1 and curve_it2. + * Loop to end_default (so including closing segment), because when a path is closed, + * there should be a midpoint marker between last segment and closing straight line segment */ + + SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_MID]); + SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_MID])); + + Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2)); if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) { - tr = NR::scale(style->stroke_width.computed) * tr; + tr = Geom::Scale(style->stroke_width.computed) * tr; } - tr = marker_item->transform * marker->c2p * tr; + tr = (Geom::Matrix)marker_item->transform * (Geom::Matrix)marker->c2p * tr; - NR::Matrix old_tr = marker_item->transform; + Geom::Matrix old_tr = marker_item->transform; marker_item->transform = tr; renderer->renderItem (ctx, marker_item); marker_item->transform = old_tr; + + ++curve_it1; + ++curve_it2; } } + + if ( shape->marker[SP_MARKER_LOC_END] ) { + SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_END]); + SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_END])); + + /* Get reference to last curve in the path. + * For moveto-only path, this returns the "closing line segment". */ + unsigned int index = path_it->size_default(); + if (index > 0) { + index--; + } + Geom::Curve const &lastcurve = (*path_it)[index]; + + Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve); + + if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) { + tr = Geom::Scale(style->stroke_width.computed) * tr; + } + + tr = (Geom::Matrix)marker_item->transform * (Geom::Matrix)marker->c2p * tr; + + Geom::Matrix old_tr = marker_item->transform; + marker_item->transform = tr; + renderer->renderItem (ctx, marker_item); + marker_item->transform = old_tr; + } } } @@ -204,13 +267,13 @@ static void sp_group_render(SPItem *item, CairoRenderContext *ctx) SPGroup *group = SP_GROUP(item); CairoRenderer *renderer = ctx->getRenderer(); TRACE(("group op: %f\n", SP_SCALE24_TO_FLOAT(SP_OBJECT_STYLE(item)->opacity.value))); - + GSList *l = g_slist_reverse(group->childList(false)); while (l) { SPObject *o = SP_OBJECT (l->data); if (SP_IS_ITEM(o)) { renderer->renderItem (ctx, SP_ITEM (o)); - } + } l = g_slist_remove (l, o); } } @@ -218,12 +281,11 @@ static void sp_group_render(SPItem *item, CairoRenderContext *ctx) static void sp_use_render(SPItem *item, CairoRenderContext *ctx) { bool translated = false; - NRMatrix tp; SPUse *use = SP_USE(item); CairoRenderer *renderer = ctx->getRenderer(); if ((use->x._set && use->x.computed != 0) || (use->y._set && use->y.computed != 0)) { - nr_matrix_set_translate(&tp, use->x.computed, use->y.computed); + Geom::Matrix tp(Geom::Translate(use->x.computed, use->y.computed)); ctx->pushState(); ctx->transform(&tp); translated = true; @@ -253,7 +315,6 @@ static void sp_flowtext_render(SPItem *item, CairoRenderContext *ctx) static void sp_image_render(SPItem *item, CairoRenderContext *ctx) { SPImage *image; - NRMatrix tp, s, t; guchar *px; int w, h, rs; @@ -276,14 +337,14 @@ static void sp_image_render(SPItem *item, CairoRenderContext *ctx) calculatePreserveAspectRatio (image->aspect_align, image->aspect_clip, (double)w, (double)h, &x, &y, &width, &height); } - + if (image->aspect_clip == SP_ASPECT_SLICE && !ctx->getCurrentState()->has_overflow) { ctx->addClippingRect(image->x.computed, image->y.computed, image->width.computed, image->height.computed); } - nr_matrix_set_translate (&tp, x, y); - nr_matrix_set_scale (&s, width / (double)w, height / (double)h); - nr_matrix_multiply (&t, &s, &tp); + Geom::Translate tp(x, y); + Geom::Scale s(width / (double)w, height / (double)h); + Geom::Matrix t(s * tp); ctx->renderImage (px, w, h, rs, &t, SP_OBJECT_STYLE (item)); } @@ -293,29 +354,29 @@ static void sp_symbol_render(SPItem *item, CairoRenderContext *ctx) SPSymbol *symbol = SP_SYMBOL(item); if (!SP_OBJECT_IS_CLONED (symbol)) return; - + /* Cloned is actually renderable */ ctx->pushState(); ctx->transform(&symbol->c2p); - + // apply viewbox if set if (0 /*symbol->viewBox_set*/) { - NRMatrix vb2user; + Geom::Matrix vb2user; double x, y, width, height; double view_width, view_height; x = 0.0; y = 0.0; width = 1.0; height = 1.0; - + view_width = symbol->viewBox.x1 - symbol->viewBox.x0; view_height = symbol->viewBox.y1 - symbol->viewBox.y0; - + calculatePreserveAspectRatio(symbol->aspect_align, symbol->aspect_clip, view_width, view_height, &x, &y,&width, &height); // [itemTransform *] translate(x, y) * scale(w/vw, h/vh) * translate(-vx, -vy); - nr_matrix_set_identity(&vb2user); + vb2user = Geom::identity(); vb2user[0] = width / view_width; vb2user[3] = height / view_height; vb2user[4] = x - symbol->viewBox.x0 * vb2user[0]; @@ -323,7 +384,7 @@ static void sp_symbol_render(SPItem *item, CairoRenderContext *ctx) ctx->transform(&vb2user); } - + sp_group_render(item, ctx); ctx->popState(); } @@ -338,13 +399,156 @@ static void sp_root_render(SPItem *item, CairoRenderContext *ctx) ctx->pushState(); renderer->setStateForItem(ctx, item); - ctx->transform(root->c2p); + Geom::Matrix tempmat (root->c2p); + ctx->transform(&tempmat); sp_group_render(item, ctx); ctx->popState(); } +/** + this function convert the item to a raster image and include the raster into the cairo renderer +*/ +static void sp_asbitmap_render(SPItem *item, CairoRenderContext *ctx) +{ + g_warning("render as bitmap"); + + //the code now was copied from sp_selection_create_bitmap_copy + + SPDocument *document = SP_OBJECT(item)->document; + Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document); + + // Get the bounding box of the selection + //boost::optional _bbox = item->getBounds(sp_item_i2d_affine(item)); + // NRRect bbox = item->getBounds(sp_item_i2d_affine(item)); + NRRect bbox(item->getBounds(sp_item_i2d_affine(item))); + + + // List of the items to show; all others will be hidden + GSList *items = NULL; //g_slist_copy ((GSList *) selection->itemList()); + items = g_slist_append(items, item); + + // Remember parent and z-order of the topmost one + gint pos = SP_OBJECT_REPR(g_slist_last(items)->data)->position(); + SPObject *parent_object = SP_OBJECT_PARENT(g_slist_last(items)->data); + Inkscape::XML::Node *parent = SP_OBJECT_REPR(parent_object); + + // Calculate resolution + double res; + /** @TODO reimplement the resolution stuff + */ + res = ctx->getBitmapResolution(); + if(res == 0) { + res = PX_PER_IN; + } + + + // The width and height of the bitmap in pixels + unsigned width = (unsigned) floor ((bbox.x1 - bbox.x0) * res / PX_PER_IN); + unsigned height =(unsigned) floor ((bbox.y1 - bbox.y0) * res / PX_PER_IN); + + // Find out if we have to run a filter + gchar const *run = NULL; + gchar const *filter = NULL; + /** @TODO reimplement the filter stuff + //gchar const *filter = prefs_get_string_attribute ("options.createbitmap", "filter"); + if (filter) { + // filter command is given; + // see if we have a parameter to pass to it + gchar const *param1 = prefs_get_string_attribute ("options.createbitmap", "filter_param1"); + if (param1) { + if (param1[strlen(param1) - 1] == '%') { + // if the param string ends with %, interpret it as a percentage of the image's max dimension + gchar p1[256]; + g_ascii_dtostr (p1, 256, ceil (g_ascii_strtod (param1, NULL) * MAX(width, height) / 100)); + // the first param is always the image filename, the second is param1 + run = g_strdup_printf ("%s \"%s\" %s", filter, filepath, p1); + } else { + // otherwise pass the param1 unchanged + run = g_strdup_printf ("%s \"%s\" %s", filter, filepath, param1); + } + } else { + // run without extra parameter + run = g_strdup_printf ("%s \"%s\"", filter, filepath); + } + } + */ + // Calculate the matrix that will be applied to the image so that it exactly overlaps the source objects + Geom::Matrix eek = sp_item_i2d_affine (SP_ITEM(parent_object)); + Geom::Matrix t; + + double shift_x = bbox.x0; + double shift_y = bbox.y1; + if (res == PX_PER_IN) { // for default 90 dpi, snap it to pixel grid + shift_x = round (shift_x); + shift_y = -round (-shift_y); // this gets correct rounding despite coordinate inversion, remove the negations when the inversion is gone + } + t = (Geom::Matrix)(Geom::Scale(1, -1) * (Geom::Matrix)(Geom::Translate (shift_x, shift_y)* eek.inverse())); + + //t = t * ((Geom::Matrix)ctx->getCurrentState()->transform).inverse(); + + // Do the export + GdkPixbuf *pb = sp_generate_internal_bitmap(document, NULL, + bbox.x0, bbox.y0, bbox.x1, bbox.y1, width, height, res, res, (guint32) 0xffffff00, items ); + + // Run filter, if any + /* + if (run) { + g_print ("Running external filter: %s\n", run); + system (run); + } + */ + if (pb) { + unsigned char *px = gdk_pixbuf_get_pixels (pb); + unsigned int w = gdk_pixbuf_get_width(pb); + unsigned int h = gdk_pixbuf_get_height(pb); + unsigned int rs = gdk_pixbuf_get_rowstride(pb); + Geom::Matrix matrix; + matrix = t; + //matrix = ((Geom::Matrix)ctx->getCurrentState()->transform).inverse(); + //matrix.set_identity(); + + ctx->renderImage (px, w, h, rs, &matrix, SP_OBJECT_STYLE (item)); + /* + // Create the repr for the image + Inkscape::XML::Node * repr = xml_doc->createElement("svg:image"); + repr->setAttribute("xlink:href", filename); + repr->setAttribute("sodipodi:absref", filepath); + if (res == PX_PER_IN) { // for default 90 dpi, snap it to pixel grid + sp_repr_set_svg_double(repr, "width", width); + sp_repr_set_svg_double(repr, "height", height); + } else { + sp_repr_set_svg_double(repr, "width", (bbox.x1 - bbox.x0)); + sp_repr_set_svg_double(repr, "height", (bbox.y1 - bbox.y0)); + } + + // Write transform + gchar *c=sp_svg_transform_write(t); + repr->setAttribute("transform", c); + g_free(c); + + // add the new repr to the parent + parent->appendChild(repr); + + // move to the saved position + repr->setPosition(pos > 0 ? pos + 1 : 1); + */ + gdk_pixbuf_unref (pb); + } + g_slist_free (items); +} + static void sp_item_invoke_render(SPItem *item, CairoRenderContext *ctx) { + // Check item's visibility + if (item->isHidden()) { + return; + } + + SPStyle* style = SP_OBJECT_STYLE (item); + if((ctx->getFilterToBitmap() == TRUE) && (style->filter.set != 0)) { + return sp_asbitmap_render(item, ctx); + } + if (SP_IS_ROOT(item)) { TRACE(("root\n")); return sp_root_render(item, ctx); @@ -378,7 +582,7 @@ CairoRenderer::setStateForItem(CairoRenderContext *ctx, SPItem const *item) { SPStyle const *style = SP_OBJECT_STYLE(item); ctx->setStateForStyle(style); - + CairoRenderState *state = ctx->getCurrentState(); state->clip_path = item->clip_ref->getObject(); state->mask = item->mask_ref->getObject(); @@ -398,7 +602,7 @@ CairoRenderer::renderItem(CairoRenderContext *ctx, SPItem *item) { ctx->pushState(); setStateForItem(ctx, item); - + CairoRenderState *state = ctx->getCurrentState(); state->need_layer = ( state->mask || state->clip_path || state->opacity != 1.0 ); @@ -406,7 +610,8 @@ CairoRenderer::renderItem(CairoRenderContext *ctx, SPItem *item) state->merge_opacity = FALSE; ctx->pushLayer(); } - ctx->transform(item->transform); + Geom::Matrix tempmat (item->transform); + ctx->transform(&tempmat); sp_item_invoke_render(item, ctx); if (state->need_layer) @@ -416,7 +621,7 @@ CairoRenderer::renderItem(CairoRenderContext *ctx, SPItem *item) } bool -CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc) +CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool pageBoundingBox, SPItem *base) { g_assert( ctx != NULL ); @@ -430,14 +635,12 @@ CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc) } NRRect d; - bool pageBoundingBox = true; - if (pageBoundingBox) { + if (pageBoundingBox || !base) { d.x0 = d.y0 = 0; d.x1 = ceil(ctx->_width); d.y1 = ceil(ctx->_height); } else { - SPItem* doc_item = SP_ITEM(sp_document_root(doc)); - sp_item_invoke_bbox(doc_item, &d, sp_item_i2r_affine(doc_item), TRUE); + sp_item_invoke_bbox(base, &d, sp_item_i2r_affine(base), TRUE); if (ctx->_vector_based_target) { // convert from px to pt d.x0 *= PT_PER_PX; @@ -447,8 +650,20 @@ CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc) } } TRACE(("%f x %f\n", ctx->_width, ctx->_height)); + + bool ret = ctx->setupSurface(d.x1-d.x0, d.y1-d.y0); + + if (ret && !pageBoundingBox && base) + { + Geom::Matrix tp(Geom::Translate(-d.x0 * (ctx->_vector_based_target ? PX_PER_PT : 1.0), + (d.y1 - ctx->_height) * (ctx->_vector_based_target ? PX_PER_PT : 1.0))); + ctx->transform(&tp); + + ctx->_width = d.x1 - d.x0; + ctx->_height = d.y1 - d.y0; + } - return ctx->setupSurface(d.x1-d.x0, d.y1-d.y0); + return ret; } #include "macros.h" // SP_PRINT_* @@ -457,22 +672,21 @@ void CairoRenderer::applyClipPath(CairoRenderContext *ctx, SPClipPath const *cp) { g_assert( ctx != NULL && ctx->_is_valid ); - + if (cp == NULL) return; - + CairoRenderContext::CairoRenderMode saved_mode = ctx->getRenderMode(); ctx->setRenderMode(CairoRenderContext::RENDER_MODE_CLIP); - NRMatrix saved_ctm; + Geom::Matrix saved_ctm; if (cp->clipPathUnits == SP_CONTENT_UNITS_OBJECTBOUNDINGBOX) { - NRMatrix t; //SP_PRINT_DRECT("clipd", cp->display->bbox); NRRect clip_bbox(cp->display->bbox); - nr_matrix_set_scale(&t, clip_bbox.x1 - clip_bbox.x0, clip_bbox.y1 - clip_bbox.y0); - t.c[4] = clip_bbox.x0; - t.c[5] = clip_bbox.y0; - nr_matrix_multiply(&t, &t, &ctx->getCurrentState()->transform); + Geom::Matrix t(Geom::Scale(clip_bbox.x1 - clip_bbox.x0, clip_bbox.y1 - clip_bbox.y0)); + t[4] = clip_bbox.x0; + t[5] = clip_bbox.y0; + t *= ctx->getCurrentState()->transform; ctx->getTransform(&saved_ctm); ctx->setTransform(&t); } @@ -502,7 +716,7 @@ void CairoRenderer::applyMask(CairoRenderContext *ctx, SPMask const *mask) { g_assert( ctx != NULL && ctx->_is_valid ); - + if (mask == NULL) return; @@ -510,11 +724,10 @@ CairoRenderer::applyMask(CairoRenderContext *ctx, SPMask const *mask) NRRect mask_bbox(mask->display->bbox); // TODO: should the bbox be transformed if maskUnits != userSpaceOnUse ? if (mask->maskContentUnits == SP_CONTENT_UNITS_OBJECTBOUNDINGBOX) { - NRMatrix t; - nr_matrix_set_scale(&t, mask_bbox.x1 - mask_bbox.x0, mask_bbox.y1 - mask_bbox.y0); - t.c[4] = mask_bbox.x0; - t.c[5] = mask_bbox.y0; - nr_matrix_multiply(&t, &t, &ctx->getCurrentState()->transform); + Geom::Matrix t(Geom::Scale(mask_bbox.x1 - mask_bbox.x0, mask_bbox.y1 - mask_bbox.y0)); + t[4] = mask_bbox.x0; + t[5] = mask_bbox.y0; + t *= ctx->getCurrentState()->transform; ctx->setTransform(&t); } @@ -542,7 +755,7 @@ calculatePreserveAspectRatio(unsigned int aspect_align, unsigned int aspect_clip { if (aspect_align == SP_ASPECT_NONE) return; - + double scalex, scaley, scale; double new_width, new_height; scalex = *width / vp_width; @@ -599,9 +812,6 @@ calculatePreserveAspectRatio(unsigned int aspect_align, unsigned int aspect_clip /* End of GNU GPL code */ -#endif /* HAVE_CAIRO_PDF */ - - /* Local Variables: mode:c++