void FilterFlood::render_cairo(FilterSlot &slot) { cairo_surface_t *input = slot.getcairo(_input); double r = SP_RGBA32_R_F(color); double g = SP_RGBA32_G_F(color); double b = SP_RGBA32_B_F(color); double a = opacity; #if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) if (icc) { guchar ru, gu, bu; icc_color_to_sRGB(icc, &ru, &gu, &bu); r = SP_COLOR_U_TO_F(ru); g = SP_COLOR_U_TO_F(gu); b = SP_COLOR_U_TO_F(bu); } #endif cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA); // Get filter primitive area in user units Geom::Rect fp = filter_primitive_area( slot.get_units() ); // Convert to Cairo units Geom::Rect fp_cairo = fp * slot.get_units().get_matrix_user2pb(); // Get area in slot (tile to fill) Geom::Rect sa = slot.get_slot_area(); // Get overlap Geom::OptRect optoverlap = intersect( fp_cairo, sa ); if( optoverlap ) { Geom::Rect overlap = *optoverlap; double dx = fp_cairo.min()[Geom::X] - sa.min()[Geom::X]; double dy = fp_cairo.min()[Geom::Y] - sa.min()[Geom::Y]; if( dx < 0.0 ) dx = 0.0; if( dy < 0.0 ) dy = 0.0; cairo_t *ct = cairo_create(out); cairo_set_source_rgba(ct, r, g, b, a); cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); cairo_rectangle(ct, dx, dy, overlap.width(), overlap.height() ); cairo_fill(ct); cairo_destroy(ct); } slot.set(_output, out); cairo_surface_destroy(out); }
Persp3D *persp3d_create_xml_element(SPDocument *document, Persp3DImpl *dup) {// if dup is given, copy the attributes over SPDefs *defs = document->getDefs(); Inkscape::XML::Document *xml_doc = document->getReprDoc(); Inkscape::XML::Node *repr; /* if no perspective is given, create a default one */ repr = xml_doc->createElement("inkscape:perspective"); repr->setAttribute("sodipodi:type", "inkscape:persp3d"); // Use 'user-units' double width = document->getWidth().value("px"); double height = document->getHeight().value("px"); if( document->getRoot()->viewBox_set ) { Geom::Rect vb = document->getRoot()->viewBox; width = vb.width(); height = vb.height(); } Proj::Pt2 proj_vp_x = Proj::Pt2 (0.0, height/2.0, 1.0); Proj::Pt2 proj_vp_y = Proj::Pt2 (0.0, 1000.0, 0.0); Proj::Pt2 proj_vp_z = Proj::Pt2 (width, height/2.0, 1.0); Proj::Pt2 proj_origin = Proj::Pt2 (width/2.0, height/3.0, 1.0 ); if (dup) { proj_vp_x = dup->tmat.column (Proj::X); proj_vp_y = dup->tmat.column (Proj::Y); proj_vp_z = dup->tmat.column (Proj::Z); proj_origin = dup->tmat.column (Proj::W); } gchar *str = NULL; str = proj_vp_x.coord_string(); repr->setAttribute("inkscape:vp_x", str); g_free (str); str = proj_vp_y.coord_string(); repr->setAttribute("inkscape:vp_y", str); g_free (str); str = proj_vp_z.coord_string(); repr->setAttribute("inkscape:vp_z", str); g_free (str); str = proj_origin.coord_string(); repr->setAttribute("inkscape:persp3d-origin", str); g_free (str); /* Append the new persp3d to defs */ defs->getRepr()->addChild(repr, NULL); Inkscape::GC::release(repr); return reinterpret_cast<Persp3D *>( defs->get_child_by_repr(repr) ); }
SPItem *Selection::_sizeistItem(bool sml, Selection::CompareSize compare) { std::vector<SPItem*> const items = const_cast<Selection *>(this)->itemList(); gdouble max = sml ? 1e18 : 0; SPItem *ist = NULL; for ( std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end(); ++i) { Geom::OptRect obox = SP_ITEM(*i)->desktopPreferredBounds(); if (!obox || obox.isEmpty()) continue; Geom::Rect bbox = *obox; gdouble size = compare == 2 ? bbox.area() : (compare == 1 ? bbox.width() : bbox.height()); size = sml ? size : size * -1; if (size < max) { max = size; ist = SP_ITEM(*i); } } return ist; }
/** * Given a Geom::Rect that may, for example, correspond to the bbox of an object, * this function fits the canvas to that rect by resizing the canvas * and translating the document root into position. */ void SPDocument::fitToRect(Geom::Rect const &rect) { double const w = rect.width(); double const h = rect.height(); double const old_height = sp_document_height(this); SPUnit const &px(sp_unit_get_by_id(SP_UNIT_PX)); sp_document_set_width(this, w, &px); sp_document_set_height(this, h, &px); Geom::Translate const tr(Geom::Point(0, (old_height - h)) - to_2geom(rect.min())); SP_GROUP(root)->translateChildItems(tr); SPNamedView *nv = sp_document_namedview(this, 0); if(nv) { Geom::Translate tr2(-rect.min()); nv->translateGuides(tr2); // update the viewport so the drawing appears to stay where it was nv->scrollAllDesktops(-tr2[0], tr2[1], false); } }
// Apply scaling from viewbox void SPViewBox::apply_viewbox(const Geom::Rect& in) { /* Determine actual viewbox in viewport coordinates */ double x = 0.0; double y = 0.0; double width = in.width(); double height = in.height(); // std::cout << " width: " << width << " height: " << height << std::endl; if (this->aspect_align != SP_ASPECT_NONE) { /* Things are getting interesting */ double scalex = in.width() / this->viewBox.width(); double scaley = in.height() / this->viewBox.height(); double scale = (this->aspect_clip == SP_ASPECT_MEET) ? MIN (scalex, scaley) : MAX (scalex, scaley); width = this->viewBox.width() * scale; height = this->viewBox.height() * scale; /* Now place viewbox to requested position */ switch (this->aspect_align) { case SP_ASPECT_XMIN_YMIN: break; case SP_ASPECT_XMID_YMIN: x = 0.5 * (in.width() - width); break; case SP_ASPECT_XMAX_YMIN: x = 1.0 * (in.width() - width); break; case SP_ASPECT_XMIN_YMID: y = 0.5 * (in.height() - height); break; case SP_ASPECT_XMID_YMID: x = 0.5 * (in.width() - width); y = 0.5 * (in.height() - height); break; case SP_ASPECT_XMAX_YMID: x = 1.0 * (in.width() - width); y = 0.5 * (in.height() - height); break; case SP_ASPECT_XMIN_YMAX: y = 1.0 * (in.height() - height); break; case SP_ASPECT_XMID_YMAX: x = 0.5 * (in.width() - width); y = 1.0 * (in.height() - height); break; case SP_ASPECT_XMAX_YMAX: x = 1.0 * (in.width() - width); y = 1.0 * (in.height() - height); break; default: break; } } /* Viewbox transform from scale and position */ Geom::Affine q; q[0] = width / this->viewBox.width(); q[1] = 0.0; q[2] = 0.0; q[3] = height / this->viewBox.height(); q[4] = x - q[0] * this->viewBox.left(); q[5] = y - q[3] * this->viewBox.top(); // std::cout << " q\n" << q << std::endl; /* Append viewbox transformation */ this->c2p = q * this->c2p; }
void FilterTile::render_cairo(FilterSlot &slot) { // FIX ME! static bool tile_warning = false; if (!tile_warning) { g_warning("Renderer for feTile has non-optimal implementation, expect slowness and bugs."); tile_warning = true; } // Fixing isn't so easy as the Inkscape renderer breaks the canvas into "rendering" tiles for // faster rendering. (The "rendering" tiles are not the same as the tiles in this primitive.) // Only if the the feTile tile source falls inside the current "rendering" tile will the tile // image be available. // This input source contains only the "rendering" tile. cairo_surface_t *in = slot.getcairo(_input); // For debugging // static int i = 0; // ++i; // std::stringstream filename; // filename << "dump." << i << ".png"; // cairo_surface_write_to_png( in, filename.str().c_str() ); // This is the feTile source area as determined by the input primitive area (see SVG spec). Geom::Rect tile_area = slot.get_primitive_area(_input); if( tile_area.width() == 0.0 || tile_area.height() == 0.0 ) { slot.set(_output, in); std::cerr << "FileTile::render_cairo: tile has zero width or height" << std::endl; } else { cairo_surface_t *out = ink_cairo_surface_create_identical(in); // color_interpolation_filters for out same as in. copy_cairo_surface_ci(in, out); cairo_t *ct = cairo_create(out); // The rectangle of the "rendering" tile. Geom::Rect sa = slot.get_slot_area(); Geom::Affine trans = slot.get_units().get_matrix_user2pb(); // Create feTile tile ---------------- // Get tile area in pixbuf units (tile transformed). Geom::Rect tt = tile_area * trans; // Shift between "rendering" tile and feTile tile Geom::Point shift = sa.min() - tt.min(); // Create feTile tile surface cairo_surface_t *tile = cairo_surface_create_similar(in, cairo_surface_get_content(in), tt.width(), tt.height()); cairo_t *ct_tile = cairo_create(tile); cairo_set_source_surface(ct_tile, in, shift[Geom::X], shift[Geom::Y]); cairo_paint(ct_tile); // Paint tiles ------------------ // For debugging // std::stringstream filename; // filename << "tile." << i << ".png"; // cairo_surface_write_to_png( tile, filename.str().c_str() ); // Determine number of feTile rows and columns Geom::Rect pr = filter_primitive_area( slot.get_units() ); int tile_cols = ceil( pr.width() / tile_area.width() ); int tile_rows = ceil( pr.height() / tile_area.height() ); // Do tiling (TO DO: restrict to slot area.) for( int col=0; col < tile_cols; ++col ) { for( int row=0; row < tile_rows; ++row ) { Geom::Point offset( col*tile_area.width(), row*tile_area.height() ); offset *= trans; offset[Geom::X] -= trans[4]; offset[Geom::Y] -= trans[5]; cairo_set_source_surface(ct, tile, offset[Geom::X], offset[Geom::Y]); cairo_paint(ct); } } slot.set(_output, out); // Clean up cairo_destroy(ct); cairo_surface_destroy(out); cairo_destroy(ct_tile); cairo_surface_destroy(tile); } }
ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename, Geom::Rect const &area, unsigned long width, unsigned long height, double xdpi, double ydpi, unsigned long bgcolor, unsigned (*status)(float, void *), void *data, bool force_overwrite, GSList *items_only) { g_return_val_if_fail(doc != NULL, EXPORT_ERROR); g_return_val_if_fail(filename != NULL, EXPORT_ERROR); g_return_val_if_fail(width >= 1, EXPORT_ERROR); g_return_val_if_fail(height >= 1, EXPORT_ERROR); g_return_val_if_fail(!area.hasZeroArea(), EXPORT_ERROR); if (!force_overwrite && !sp_ui_overwrite_file(filename)) { // aborted overwrite return EXPORT_ABORTED; } doc->ensureUpToDate(); /* Calculate translation by transforming to document coordinates (flipping Y)*/ Geom::Point translation = Geom::Point(-area[Geom::X][0], area[Geom::Y][1] - doc->getHeight().value("px")); /* This calculation is only valid when assumed that (x0,y0)= area.corner(0) and (x1,y1) = area.corner(2) * 1) a[0] * x0 + a[2] * y1 + a[4] = 0.0 * 2) a[1] * x0 + a[3] * y1 + a[5] = 0.0 * 3) a[0] * x1 + a[2] * y1 + a[4] = width * 4) a[1] * x0 + a[3] * y0 + a[5] = height * 5) a[1] = 0.0; * 6) a[2] = 0.0; * * (1,3) a[0] * x1 - a[0] * x0 = width * a[0] = width / (x1 - x0) * (2,4) a[3] * y0 - a[3] * y1 = height * a[3] = height / (y0 - y1) * (1) a[4] = -a[0] * x0 * (2) a[5] = -a[3] * y1 */ Geom::Affine const affine(Geom::Translate(translation) * Geom::Scale(width / area.width(), height / area.height())); //SP_PRINT_MATRIX("SVG2PNG", &affine); struct SPEBP ebp; ebp.width = width; ebp.height = height; ebp.background = bgcolor; /* Create new drawing */ Inkscape::Drawing drawing; drawing.setExact(true); // export with maximum blur rendering quality unsigned const dkey = SPItem::display_key_new(1); // Create ArenaItems and set transform drawing.setRoot(doc->getRoot()->invoke_show(drawing, dkey, SP_ITEM_SHOW_DISPLAY)); drawing.root()->setTransform(affine); ebp.drawing = &drawing; // We show all and then hide all items we don't want, instead of showing only requested items, // because that would not work if the shown item references something in defs if (items_only) { hide_other_items_recursively(doc->getRoot(), items_only, dkey); } ebp.status = status; ebp.data = data; bool write_status = false;; ebp.sheight = 64; ebp.px = g_try_new(guchar, 4 * ebp.sheight * width); if (ebp.px) { write_status = sp_png_write_rgba_striped(doc, filename, width, height, xdpi, ydpi, sp_export_get_rows, &ebp); g_free(ebp.px); } // Hide items, this releases arenaitem doc->getRoot()->invoke_hide(dkey); return write_status ? EXPORT_OK : EXPORT_ERROR; }
/** * Creates a surface with the given logical and physical extents. * When a drawing context is created for this surface, its pixels * will cover the area under the given rectangle. IT will contain * the number of pixels specified by the second argument. * @param logbox Logical extents of the surface * @param pixdims Pixel dimensions of the surface. */ DrawingSurface::DrawingSurface(Geom::Rect const &logbox, Geom::IntPoint const &pixdims) : _surface(NULL) , _origin(logbox.min()) , _scale(pixdims[X] / logbox.width(), pixdims[Y] / logbox.height()) , _pixels(pixdims) {}
void FilterImage::render_cairo(FilterSlot &slot) { if (!feImageHref) return; //cairo_surface_t *input = slot.getcairo(_input); // Viewport is filter primitive area (in user coordinates). // Note: viewport calculation in non-trivial. Do not rely // on get_matrix_primitiveunits2pb(). Geom::Rect vp = filter_primitive_area( slot.get_units() ); slot.set_primitive_area(_output, vp); // Needed for tiling double feImageX = vp.min()[Geom::X]; double feImageY = vp.min()[Geom::Y]; double feImageWidth = vp.width(); double feImageHeight = vp.height(); // feImage is suppose to use the same parameters as a normal SVG image. // If a width or height is set to zero, the image is not suppose to be displayed. // This does not seem to be what Firefox or Opera does, nor does the W3C displacement // filter test expect this behavior. If the width and/or height are zero, we use // the width and height of the object bounding box. Geom::Affine m = slot.get_units().get_matrix_user2filterunits().inverse(); Geom::Point bbox_00 = Geom::Point(0,0) * m; Geom::Point bbox_w0 = Geom::Point(1,0) * m; Geom::Point bbox_0h = Geom::Point(0,1) * m; double bbox_width = Geom::distance(bbox_00, bbox_w0); double bbox_height = Geom::distance(bbox_00, bbox_0h); if( feImageWidth == 0 ) feImageWidth = bbox_width; if( feImageHeight == 0 ) feImageHeight = bbox_height; // Internal image, like <use> if (from_element) { if (!SVGElem) return; // TODO: do not recreate the rendering tree every time // TODO: the entire thing is a hack, we should give filter primitives an "update" method // like the one for DrawingItems document->ensureUpToDate(); Drawing drawing; Geom::OptRect optarea = SVGElem->visualBounds(); if (!optarea) return; unsigned const key = SPItem::display_key_new(1); DrawingItem *ai = SVGElem->invoke_show(drawing, key, SP_ITEM_SHOW_DISPLAY); if (!ai) { g_warning("feImage renderer: error creating DrawingItem for SVG Element"); return; } drawing.setRoot(ai); Geom::Rect area = *optarea; Geom::Affine user2pb = slot.get_units().get_matrix_user2pb(); /* FIXME: These variables are currently unused. Why were they calculated? double scaleX = feImageWidth / area.width(); double scaleY = feImageHeight / area.height(); */ Geom::Rect sa = slot.get_slot_area(); cairo_surface_t *out = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, sa.width(), sa.height()); Inkscape::DrawingContext dc(out, sa.min()); dc.transform(user2pb); // we are now in primitive units dc.translate(feImageX, feImageY); // dc.scale(scaleX, scaleY); No scaling should be done Geom::IntRect render_rect = area.roundOutwards(); // dc.translate(render_rect.min()); This seems incorrect // Update to renderable state drawing.update(render_rect); drawing.render(dc, render_rect); SVGElem->invoke_hide(key); // For the moment, we'll assume that any image is in sRGB color space set_cairo_surface_ci(out, SP_CSS_COLOR_INTERPOLATION_SRGB); slot.set(_output, out); cairo_surface_destroy(out); return; } // External image, like <image> if (!image && !broken_ref) { broken_ref = true; /* TODO: If feImageHref is absolute, then use that (preferably handling the * case that it's not a file URI). Otherwise, go up the tree looking * for an xml:base attribute, and use that as the base URI for resolving * the relative feImageHref URI. Otherwise, if document->base is valid, * then use that as the base URI. Otherwise, use feImageHref directly * (i.e. interpreting it as relative to our current working directory). * (See http://www.w3.org/TR/xmlbase/#resolution .) */ gchar *fullname = feImageHref; if ( !g_file_test( fullname, G_FILE_TEST_EXISTS ) ) { // Try to load from relative postion combined with document base if( document ) { fullname = g_build_filename( document->getBase(), feImageHref, NULL ); } } if ( !g_file_test( fullname, G_FILE_TEST_EXISTS ) ) { // Should display Broken Image png. g_warning("FilterImage::render: Can not find: %s", feImageHref ); return; } image = Inkscape::Pixbuf::create_from_file(fullname); if( fullname != feImageHref ) g_free( fullname ); if ( !image ) { g_warning("FilterImage::render: failed to load image: %s", feImageHref); return; } broken_ref = false; } if (broken_ref) { return; } cairo_surface_t *image_surface = image->getSurfaceRaw(); Geom::Rect sa = slot.get_slot_area(); cairo_surface_t *out = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, sa.width(), sa.height()); // For the moment, we'll assume that any image is in sRGB color space // set_cairo_surface_ci(out, SP_CSS_COLOR_INTERPOLATION_SRGB); // This seemed like a sensible thing to do but it breaks filters-displace-01-f.svg cairo_t *ct = cairo_create(out); cairo_translate(ct, -sa.min()[Geom::X], -sa.min()[Geom::Y]); // now ct is in pb coordinates, note the feWidth etc. are in user units ink_cairo_transform(ct, slot.get_units().get_matrix_user2pb()); // now ct is in the coordinates of feImageX etc. // Now that we have the viewport, we must map image inside. // Partially copied from sp-image.cpp. // Do nothing if preserveAspectRatio is "none". if( aspect_align != SP_ASPECT_NONE ) { // Check aspect ratio of image vs. viewport double feAspect = feImageHeight/feImageWidth; double aspect = (double)image->height()/(double)image->width(); bool ratio = (feAspect < aspect); double ax, ay; // Align side switch( aspect_align ) { case SP_ASPECT_XMIN_YMIN: ax = 0.0; ay = 0.0; break; case SP_ASPECT_XMID_YMIN: ax = 0.5; ay = 0.0; break; case SP_ASPECT_XMAX_YMIN: ax = 1.0; ay = 0.0; break; case SP_ASPECT_XMIN_YMID: ax = 0.0; ay = 0.5; break; case SP_ASPECT_XMID_YMID: ax = 0.5; ay = 0.5; break; case SP_ASPECT_XMAX_YMID: ax = 1.0; ay = 0.5; break; case SP_ASPECT_XMIN_YMAX: ax = 0.0; ay = 1.0; break; case SP_ASPECT_XMID_YMAX: ax = 0.5; ay = 1.0; break; case SP_ASPECT_XMAX_YMAX: ax = 1.0; ay = 1.0; break; default: ax = 0.0; ay = 0.0; break; } if( aspect_clip == SP_ASPECT_SLICE ) { // image clipped by viewbox if( ratio ) { // clip top/bottom feImageY -= ay * (feImageWidth * aspect - feImageHeight); feImageHeight = feImageWidth * aspect; } else { // clip sides feImageX -= ax * (feImageHeight / aspect - feImageWidth); feImageWidth = feImageHeight / aspect; } } else { // image fits into viewbox if( ratio ) { // fit to height feImageX += ax * (feImageWidth - feImageHeight / aspect ); feImageWidth = feImageHeight / aspect; } else { // fit to width feImageY += ay * (feImageHeight - feImageWidth * aspect); feImageHeight = feImageWidth * aspect; } } } double scaleX = feImageWidth / image->width(); double scaleY = feImageHeight / image->height(); cairo_translate(ct, feImageX, feImageY); cairo_scale(ct, scaleX, scaleY); cairo_set_source_surface(ct, image_surface, 0, 0); cairo_paint(ct); cairo_destroy(ct); slot.set(_output, out); }
// Apply scaling from viewbox void SPViewBox::apply_viewbox(const Geom::Rect& in, double scale_none) { /* Determine actual viewbox in viewport coordinates */ // scale_none is the scale that would apply if the viewbox and page size are same size // it is passed here because it is a double-precision variable, while 'in' is originally float double x = 0.0; double y = 0.0; double scale_x = in.width() / this->viewBox.width(); double scale_y = in.height() / this->viewBox.height(); double scale_uniform = 1.0; // used only if scaling is uniform if (Geom::are_near(scale_x / scale_y, 1.0, Geom::EPSILON)) { // scaling is already uniform, reduce numerical error scale_uniform = (scale_x + scale_y)/2.0; if (Geom::are_near(scale_uniform / scale_none, 1.0, Geom::EPSILON)) scale_uniform = scale_none; // objects are same size, reduce numerical error scale_x = scale_uniform; scale_y = scale_uniform; } else if (this->aspect_align != SP_ASPECT_NONE) { // scaling is not uniform, but force it to be scale_uniform = (this->aspect_clip == SP_ASPECT_MEET) ? MIN (scale_x, scale_y) : MAX (scale_x, scale_y); scale_x = scale_uniform; scale_y = scale_uniform; double width = this->viewBox.width() * scale_uniform; double height = this->viewBox.height() * scale_uniform; /* Now place viewbox to requested position */ switch (this->aspect_align) { case SP_ASPECT_XMIN_YMIN: break; case SP_ASPECT_XMID_YMIN: x = 0.5 * (in.width() - width); break; case SP_ASPECT_XMAX_YMIN: x = 1.0 * (in.width() - width); break; case SP_ASPECT_XMIN_YMID: y = 0.5 * (in.height() - height); break; case SP_ASPECT_XMID_YMID: x = 0.5 * (in.width() - width); y = 0.5 * (in.height() - height); break; case SP_ASPECT_XMAX_YMID: x = 1.0 * (in.width() - width); y = 0.5 * (in.height() - height); break; case SP_ASPECT_XMIN_YMAX: y = 1.0 * (in.height() - height); break; case SP_ASPECT_XMID_YMAX: x = 0.5 * (in.width() - width); y = 1.0 * (in.height() - height); break; case SP_ASPECT_XMAX_YMAX: x = 1.0 * (in.width() - width); y = 1.0 * (in.height() - height); break; default: break; } } /* Viewbox transform from scale and position */ Geom::Affine q; q[0] = scale_x; q[1] = 0.0; q[2] = 0.0; q[3] = scale_y; q[4] = x - scale_x * this->viewBox.left(); q[5] = y - scale_y * this->viewBox.top(); // std::cout << " q\n" << q << std::endl; /* Append viewbox transformation */ this->c2p = q * this->c2p; }