Beispiel #1
0
Geom::Affine
sp_gradient_get_g2d_matrix(SPGradient const *gr, Geom::Affine const &ctm, Geom::Rect const &bbox)
{
    if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
        return ( Geom::Scale(bbox.dimensions())
                 * Geom::Translate(bbox.min())
                 * Geom::Affine(ctm) );
    } else {
        return ctm;
    }
}
Beispiel #2
0
Layout::iterator Layout::getLetterAt(double x, double y) const
{
    Geom::Point point(x, y);

    double rotation;
    for (iterator it = begin() ; it != end() ; it.nextCharacter()) {
        Geom::Rect box = characterBoundingBox(it, &rotation);
        // todo: rotation
        if (box.contains(point)) return it;
    }
    return end();
}
Beispiel #3
0
void
ZoomStatus::on_value_changed()
{
    if (_upd_f) return;
    _upd_f = true;
    g_assert(_dt);
    double zoom_factor = pow(2, get_value());
    Geom::Rect const d = _dt->get_display_area();
    _dt->zoom_absolute(d.midpoint()[Geom::X], d.midpoint()[Geom::Y], zoom_factor);
    gtk_widget_grab_focus(static_cast<GtkWidget*>((void*)_dt->canvas));   /// \todo this no love song
    _upd_f = false;
}
Beispiel #4
0
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);
}
Beispiel #5
0
void
sp_gradient_set_gs2d_matrix(SPGradient *gr, Geom::Affine const &ctm,
                            Geom::Rect const &bbox, Geom::Affine const &gs2d)
{
    gr->gradientTransform = gs2d * ctm.inverse();
    if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX ) {
        gr->gradientTransform = ( gr->gradientTransform
                                  * Geom::Translate(-bbox.min())
                                  * Geom::Scale(bbox.dimensions()).inverse() );
    }
    gr->gradientTransform_set = TRUE;

    gr->requestModified(SP_OBJECT_MODIFIED_FLAG);
}
Beispiel #6
0
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) );
}
Beispiel #7
0
void
LPEMirrorSymmetry::doOnApply (SPLPEItem *lpeitem)
{
    using namespace Geom;

    SPItem *item = SP_ITEM(lpeitem);
    Geom::Matrix t = sp_item_i2d_affine(item);
    Geom::Rect bbox = *item->getBounds(t); // fixme: what happens if getBounds does not return a valid rect?

    Point A(bbox.left(), bbox.bottom());
    Point B(bbox.left(), bbox.top());
    A *= t;
    B *= t;
    Piecewise<D2<SBasis> > rline = Piecewise<D2<SBasis> >(D2<SBasis>(Linear(A[X], B[X]), Linear(A[Y], B[Y])));
    reflection_line.set_new_value(rline, true);
}
void
LPEMirrorSymmetry::doOnApply (SPLPEItem const* lpeitem)
{
    using namespace Geom;

    // fixme: what happens if the bbox is empty?
    // fixme: this is probably wrong
    Geom::Affine t = lpeitem->i2dt_affine();
    Geom::Rect bbox = *lpeitem->desktopVisualBounds();

    Point A(bbox.left(), bbox.bottom());
    Point B(bbox.left(), bbox.top());
    A *= t;
    B *= t;
    Piecewise<D2<SBasis> > rline = Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(A[X], B[X]), SBasis(A[Y], B[Y])));
    reflection_line.set_new_value(rline, true);
}
Beispiel #9
0
/** Feeds path-creating calls to the cairo context translating them from the Path, with the given transform and shift */
static void
feed_path_to_cairo (cairo_t *ct, Geom::Path const &path, Geom::Affine trans, Geom::OptRect area, bool optimize_stroke, double stroke_width)
{
    if (!area)
        return;
    if (path.empty())
        return;

    // Transform all coordinates to coords within "area"
    Geom::Point shift = area->min();
    Geom::Rect view = *area;
    view.expandBy (stroke_width);
    view = view * (Geom::Affine)Geom::Translate(-shift);
    //  Pass transformation to feed_curve, so that we don't need to create a whole new path.
    Geom::Affine transshift(trans * Geom::Translate(-shift));

    Geom::Point initial = path.initialPoint() * transshift;
    cairo_move_to(ct, initial[0], initial[1] );

    for(Geom::Path::const_iterator cit = path.begin(); cit != path.end_open(); ++cit) {
        feed_curve_to_cairo(ct, *cit, transshift, view, optimize_stroke);
    }

    if (path.closed()) {
        if (!optimize_stroke) {
            cairo_close_path(ct);
        } else {
            cairo_line_to(ct, initial[0], initial[1]);
            /* We cannot use cairo_close_path(ct) here because some parts of the path may have been
               clipped and not drawn (maybe the before last segment was outside view area), which 
               would result in closing the "subpath" after the last interruption, not the entire path.

               However, according to cairo documentation:
               The behavior of cairo_close_path() is distinct from simply calling cairo_line_to() with the equivalent coordinate
               in the case of stroking. When a closed sub-path is stroked, there are no caps on the ends of the sub-path. Instead,
               there is a line join connecting the final and initial segments of the sub-path. 

               The correct fix will be possible when cairo introduces methods for moving without
               ending/starting subpaths, which we will use for skipping invisible segments; then we
               will be able to use cairo_close_path here. This issue also affects ps/eps/pdf export,
               see bug 168129
            */
        }
    }
}
Beispiel #10
0
/**
 * Get the item under the specified point.
 * Searches the tree for the first item in the Z-order which is closer than
 * @a delta to the given point. The pick should be visual - for example
 * an object with a thick stroke should pick on the entire area of the stroke.
 * @param p Search point
 * @param delta Maximum allowed distance from the point
 * @param sticky Whether the pick should ignore visibility and sensitivity.
 *               When false, only visible and sensitive objects are considered.
 *               When true, invisible and insensitive objects can also be picked.
 */
DrawingItem *
DrawingItem::pick(Geom::Point const &p, double delta, unsigned flags)
{
    // Sometimes there's no BBOX in state, reason unknown (bug 992817)
    // I made this not an assert to remove the warning
    if (!(_state & STATE_BBOX) || !(_state & STATE_PICK)) {
        g_warning("Invalid state when picking: STATE_BBOX = %d, STATE_PICK = %d",
                  _state & STATE_BBOX, _state & STATE_PICK);
        return NULL;
    }
    // ignore invisible and insensitive items unless sticky
    if (!(flags & PICK_STICKY) && !(_visible && _sensitive))
        return NULL;

    bool outline = _drawing.outline();

    if (!_drawing.outline()) {
        // pick inside clipping path; if NULL, it means the object is clipped away there
        if (_clip) {
            DrawingItem *cpick = _clip->pick(p, delta, flags | PICK_AS_CLIP);
            if (!cpick) return NULL;
        }
        // same for mask
        if (_mask) {
            DrawingItem *mpick = _mask->pick(p, delta, flags);
            if (!mpick) return NULL;
        }
    }

    Geom::OptIntRect box = (outline || (flags & PICK_AS_CLIP)) ? _bbox : _drawbox;
    if (!box) {
        return NULL;
    }

    Geom::Rect expanded = *box;
    expanded.expandBy(delta);

    if (expanded.contains(p)) {
        return _pickItem(p, delta, flags);
    }
    return NULL;
}
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;
}
Beispiel #12
0
static double
sp_canvas_bpath_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
{
    SPCanvasBPath *cbp = SP_CANVAS_BPATH (item);

    if ( !cbp->curve  || 
         ((cbp->stroke_rgba & 0xff) == 0 && (cbp->fill_rgba & 0xff) == 0 ) || 
         cbp->curve->get_segment_count() < 1)
        return Geom::infinity();

    double width = 0.5;
    Geom::Rect viewbox = item->canvas->getViewbox();
    viewbox.expandBy (width);
    double dist = Geom::infinity();
    pathv_matrix_point_bbox_wind_distance(cbp->curve->get_pathvector(), cbp->affine, p, NULL, NULL, &dist, 0.5, &viewbox);

    if (dist <= 1.0) {
        *actual_item = item;
    }

    return dist;
}
/**
 * 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);
    }
}
Beispiel #14
0
boost::optional<Geom::Point> Line::intersection_with_viewbox (SPDesktop *desktop)
{
    Geom::Rect vb = desktop->get_display_area();
    /* remaining viewbox corners */
    Geom::Point ul (vb.min()[Geom::X], vb.max()[Geom::Y]);
    Geom::Point lr (vb.max()[Geom::X], vb.min()[Geom::Y]);

    std::pair <Geom::Point, Geom::Point> e = side_of_intersection (vb.min(), lr, vb.max(), ul, this->pt, this->v_dir);
    if (e.first == e.second) {
        // perspective line lies outside the canvas
        return boost::optional<Geom::Point>();
    }

    Line line (e.first, e.second);
    return this->intersect (line);
}
static bool is_within(Geom::Rect const &area, Geom::Rect const &box)
{
    return area.contains(box);
}
Beispiel #16
0
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;
}
Beispiel #17
0
// 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;
}
/**
 * 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)
{}
Beispiel #19
0
std::vector<Geom::Point> Layout::createSelectionShape(iterator const &it_start, iterator const &it_end, Geom::Affine const &transform) const
{
    std::vector<Geom::Point> quads;
    unsigned char_index;
    unsigned end_char_index;

    if (it_start._char_index < it_end._char_index) {
        char_index = it_start._char_index;
        end_char_index = it_end._char_index;
    } else {
        char_index = it_end._char_index;
        end_char_index = it_start._char_index;
    }
    for ( ; char_index < end_char_index ; ) {
        if (_characters[char_index].in_glyph == -1) {
            char_index++;
            continue;
        }
        double char_rotation = _glyphs[_characters[char_index].in_glyph].rotation;
        unsigned span_index = _characters[char_index].in_span;

        Geom::Point top_left, bottom_right;
        if (_path_fitted || char_rotation != 0.0) {
            Geom::Rect box = characterBoundingBox(iterator(this, char_index), &char_rotation);
            top_left = box.min();
            bottom_right = box.max();
            char_index++;
        } else {   // for straight text we can be faster by combining all the character boxes in a span into one box
            double span_x = _spans[span_index].x_start + _spans[span_index].chunk(this).left_x;
            top_left[Geom::X] = span_x + _characters[char_index].x;
            while (char_index < end_char_index && _characters[char_index].in_span == span_index)
                char_index++;
            if (char_index == _characters.size() || _characters[char_index].in_span != span_index)
                bottom_right[Geom::X] = _spans[span_index].x_end + _spans[span_index].chunk(this).left_x;
            else
                bottom_right[Geom::X] = span_x + _characters[char_index].x;

            double baseline_y = _spans[span_index].line(this).baseline_y + _spans[span_index].baseline_shift;
            double vertical_scale = _glyphs.back().vertical_scale;

            if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
                double span_height = vertical_scale * (_spans[span_index].line_height.ascent + _spans[span_index].line_height.descent);
                top_left[Geom::Y] = top_left[Geom::X];
                top_left[Geom::X] = baseline_y - span_height * 0.5;
                bottom_right[Geom::Y] = bottom_right[Geom::X];
                bottom_right[Geom::X] = baseline_y + span_height * 0.5;
            } else {
                top_left[Geom::Y] =  baseline_y - vertical_scale * _spans[span_index].line_height.ascent;
                bottom_right[Geom::Y] = baseline_y + vertical_scale * _spans[span_index].line_height.descent;
            }
        }

        Geom::Rect char_box(top_left, bottom_right);
        if (char_box.dimensions()[Geom::X] == 0.0 || char_box.dimensions()[Geom::Y] == 0.0)
            continue;
        Geom::Point center_of_rotation((top_left[Geom::X] + bottom_right[Geom::X]) * 0.5,
                                       top_left[Geom::Y] + _spans[span_index].line_height.ascent);
        Geom::Affine total_transform = Geom::Translate(-center_of_rotation) * Geom::Rotate(char_rotation) * Geom::Translate(center_of_rotation) * transform;
        for(int i = 0; i < 4; i ++)
            quads.push_back(char_box.corner(i) * total_transform);
    }
    return quads;
}
Beispiel #20
0
int main(int argc, char **argv) {
    char const *const filename = (argc >= 2
                                  ? argv[1]
                                  : "toy.svgd");
    FILE* f = fopen(filename, "r");
    if (!f) {
        perror(filename);
        return 1;
    }
    display_path = read_svgd(f);

    Geom::Rect r = display_path.bbox();
    
    display_path = display_path*Geom::translate(-r.min());
    Geom::scale sc(r.max() - r.min());
    display_path = display_path*(sc.inverse()*Geom::scale(500,500));
    
    gtk_init (&argc, &argv);

    gdk_rgb_init();

    GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    gtk_window_set_title(GTK_WINDOW(window), "text toy");

    gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE);

    gtk_signal_connect(GTK_OBJECT(window),
                       "delete_event",
                       GTK_SIGNAL_FUNC(delete_event_cb),
                       NULL);

    gtk_widget_push_visual(gdk_rgb_get_visual());
    gtk_widget_push_colormap(gdk_rgb_get_cmap());
    canvas = gtk_drawing_area_new();

    gtk_signal_connect(GTK_OBJECT (canvas),
                       "expose_event",
                       GTK_SIGNAL_FUNC(expose_event),
                       0);
    gtk_widget_add_events(canvas, (GDK_BUTTON_PRESS_MASK |
                                   GDK_BUTTON_RELEASE_MASK |
                                   GDK_KEY_PRESS_MASK    |
                                   GDK_POINTER_MOTION_MASK));
    gtk_signal_connect(GTK_OBJECT (canvas),
                       "button_press_event",
                       GTK_SIGNAL_FUNC(mouse_event),
                       0);
    gtk_signal_connect(GTK_OBJECT (canvas),
                       "button_release_event",
                       GTK_SIGNAL_FUNC(mouse_release_event),
                       0);
    gtk_signal_connect(GTK_OBJECT (canvas),
                       "motion_notify_event",
                       GTK_SIGNAL_FUNC(mouse_motion_event),
                       0);
    gtk_signal_connect(GTK_OBJECT(canvas),
                       "key_press_event",
                       GTK_SIGNAL_FUNC(key_release_event),
                       0);

    gtk_widget_pop_colormap();
    gtk_widget_pop_visual();

    GtkWidget *vb = gtk_vbox_new(0, 0);


    gtk_container_add(GTK_CONTAINER(window), vb);

    gtk_box_pack_start(GTK_BOX(vb), canvas, TRUE, TRUE, 0);

    gtk_window_set_default_size(GTK_WINDOW(window), 600, 600);

    gtk_widget_show_all(window);

    dash_gc = gdk_gc_new(canvas->window);
    gint8 dash_list[] = {4, 4};
    gdk_gc_set_dashes(dash_gc, 0, dash_list, 2);
    GdkColor colour;
    colour.red = 0xffff;
    colour.green = 0xffff;
    colour.blue = 0xffff;

    plain_gc = gdk_gc_new(canvas->window);
    
    //gdk_gc_set_rgb_fg_color(dash_gc, &colour);
    gdk_rgb_find_color(gtk_widget_get_colormap(canvas), &colour);
    gdk_window_set_background(canvas->window, &colour);
    gdk_gc_set_line_attributes(dash_gc, 1, GDK_LINE_ON_OFF_DASH,
                               GDK_CAP_BUTT,GDK_JOIN_MITER);

    /* Make sure the canvas can receive key press events. */
    GTK_WIDGET_SET_FLAGS(canvas, GTK_CAN_FOCUS);
    assert(GTK_WIDGET_CAN_FOCUS(canvas));
    gtk_widget_grab_focus(canvas);
    assert(gtk_widget_is_focus(canvas));

    gtk_main();

    return 0;
}
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);
}
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);
    }
}
Beispiel #23
0
// 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;
}
static bool overlaps(Geom::Rect const &area, Geom::Rect const &box)
{
    return area.intersects(box);
}