diff options
| author | Liam P. White <inkscapebrony@gmail.com> | 2014-10-18 21:44:39 +0000 |
|---|---|---|
| committer | Liam P. White <inkscapebrony@gmail.com> | 2014-10-18 21:44:39 +0000 |
| commit | 68136c8b24a6995976db963a790207858e9baba5 (patch) | |
| tree | 550e81af7c8d626ec80a2be5efec66dd10d84bc4 /src | |
| parent | Update to experimental r13598 (diff) | |
| parent | Update to trunk r13621 (diff) | |
| download | inkscape-68136c8b24a6995976db963a790207858e9baba5.tar.gz inkscape-68136c8b24a6995976db963a790207858e9baba5.zip | |
Update to experimental r13619
(bzr r13341.5.18)
Diffstat (limited to 'src')
80 files changed, 3384 insertions, 737 deletions
diff --git a/src/2geom/math-utils.h b/src/2geom/math-utils.h index 83c7b4f5e..0d5d3667b 100644 --- a/src/2geom/math-utils.h +++ b/src/2geom/math-utils.h @@ -111,7 +111,7 @@ inline void sincos(double angle, double &sin_, double &cos_) { # define IS_NAN(_a) (_isnan(_a)) /* Win32 definition */ #elif defined(isnan) || defined(__FreeBSD__) || defined(__osf__) # define IS_NAN(_a) (isnan(_a)) /* GNU definition */ -#elif defined (SOLARIS_2_8) && __GNUC__ == 3 && __GNUC_MINOR__ == 2 +#elif defined (SOLARIS) # define IS_NAN(_a) (isnan(_a)) /* GNU definition */ #else # define IS_NAN(_a) (boost::math::isnan(_a)) @@ -129,7 +129,7 @@ inline void sincos(double angle, double &sin_, double &cos_) { # define IS_FINITE(_a) (isfinite(_a)) #elif defined(__osf__) # define IS_FINITE(_a) (finite(_a) && !IS_NAN(_a)) -#elif defined (SOLARIS_2_8) && __GNUC__ == 3 && __GNUC_MINOR__ == 2 +#elif defined (SOLARIS) #include <ieeefp.h> #define IS_FINITE(_a) (finite(_a) && !IS_NAN(_a)) #else diff --git a/src/2geom/numeric/symmetric-matrix-fs-operation.h b/src/2geom/numeric/symmetric-matrix-fs-operation.h index 5222d2734..37ece56ae 100644 --- a/src/2geom/numeric/symmetric-matrix-fs-operation.h +++ b/src/2geom/numeric/symmetric-matrix-fs-operation.h @@ -44,7 +44,7 @@ namespace Geom { namespace NL { template <size_t N> inline -SymmetricMatrix<N> adj(const ConstBaseSymmetricMatrix<N> & S) +SymmetricMatrix<N> adj(const ConstBaseSymmetricMatrix<N> & /*S*/) { THROW_NOTIMPLEMENTED(); return SymmetricMatrix<N>(); diff --git a/src/2geom/numeric/symmetric-matrix-fs-trace.h b/src/2geom/numeric/symmetric-matrix-fs-trace.h index 099c834a8..dbabecf6e 100644 --- a/src/2geom/numeric/symmetric-matrix-fs-trace.h +++ b/src/2geom/numeric/symmetric-matrix-fs-trace.h @@ -74,7 +74,7 @@ template <size_t K, size_t N> struct trace { static - double evaluate (const ConstBaseSymmetricMatrix<N> & S) + double evaluate (const ConstBaseSymmetricMatrix<N> & /*S*/) { THROW_NOTIMPLEMENTED(); return K; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 008a888e0..f0bacff16 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,8 @@ set(sp_SRC sp-gradient-reference.cpp sp-gradient.cpp sp-guide.cpp + sp-hatch.cpp + sp-hatch-path.cpp sp-image.cpp sp-item-group.cpp sp-item-notify-moveto.cpp @@ -116,6 +118,8 @@ set(sp_SRC sp-guide-attachment.h sp-guide-constraint.h sp-guide.h + sp-hatch.h + sp-hatch-path.h sp-image.h sp-item-group.h sp-item-notify-moveto.h diff --git a/src/Makefile_insert b/src/Makefile_insert index 541bd20ca..acf317c47 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -145,6 +145,8 @@ ink_common_sources += \ sp-guide-attachment.h \ sp-guide-constraint.h \ sp-guide.cpp sp-guide.h \ + sp-hatch.cpp sp-hatch.h \ + sp-hatch-path.cpp sp-hatch-path.h \ sp-image.cpp sp-image.h \ sp-item.cpp sp-item.h \ sp-item-group.cpp sp-item-group.h \ diff --git a/src/attributes.cpp b/src/attributes.cpp index 87bfdbe88..d142d1a06 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -293,6 +293,11 @@ static SPStyleProp const props[] = { {SP_ATTR_PATTERNUNITS, "patternUnits"}, {SP_ATTR_PATTERNCONTENTUNITS, "patternContentUnits"}, {SP_ATTR_PATTERNTRANSFORM, "patternTransform"}, + /* SPHatch */ + {SP_ATTR_HATCHUNITS, "hatchUnits"}, + {SP_ATTR_HATCHCONTENTUNITS, "hatchContentUnits"}, + {SP_ATTR_HATCHTRANSFORM, "hatchTransform"}, + {SP_ATTR_PITCH, "pitch"}, /* SPClipPath */ {SP_ATTR_CLIPPATHUNITS, "clipPathUnits"}, /* SPMask */ diff --git a/src/attributes.h b/src/attributes.h index 598d68fa3..8e091df71 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -295,6 +295,11 @@ enum SPAttributeEnum { SP_ATTR_PATTERNUNITS, SP_ATTR_PATTERNCONTENTUNITS, SP_ATTR_PATTERNTRANSFORM, + /* SPHatch */ + SP_ATTR_HATCHUNITS, + SP_ATTR_HATCHCONTENTUNITS, + SP_ATTR_HATCHTRANSFORM, + SP_ATTR_PITCH, /* SPClipPath */ SP_ATTR_CLIPPATHUNITS, /* SPMask */ diff --git a/src/box3d-side.h b/src/box3d-side.h index 89b3b0399..4783a5f24 100644 --- a/src/box3d-side.h +++ b/src/box3d-side.h @@ -7,6 +7,7 @@ * Authors: * Maximilian Albert <Anhalter42@gmx.de> * Abhishek Sharma + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 2007 Authors * @@ -17,9 +18,6 @@ #include "axis-manip.h" -#define SP_BOX3D_SIDE(obj) (dynamic_cast<Box3DSide*>((SPObject*)obj)) -#define SP_IS_BOX3D_SIDE(obj) (dynamic_cast<const Box3DSide*>((SPObject*)obj) != NULL) - class SPBox3D; class Persp3D; diff --git a/src/box3d.cpp b/src/box3d.cpp index 5598bc83d..f872081b8 100644 --- a/src/box3d.cpp +++ b/src/box3d.cpp @@ -6,6 +6,7 @@ * Lauris Kaplinski <lauris@kaplinski.com> * bulia byak <buliabyak@users.sf.net> * Abhishek Sharma + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 2007 Authors * Copyright (C) 1999-2002 Lauris Kaplinski @@ -263,9 +264,10 @@ void box3d_position_set(SPBox3D *box) { /* This draws the curve and calls requestDisplayUpdate() for each side (the latter is done in box3d_side_position_set() to avoid update conflicts with the parent box) */ - for ( SPObject *child = box->firstChild(); child; child = child->getNext() ) { - if (SP_IS_BOX3D_SIDE(child)) { - box3d_side_position_set(SP_BOX3D_SIDE(child)); + for ( SPObject *obj = box->firstChild(); obj; obj = obj->getNext() ) { + Box3DSide *side = dynamic_cast<Box3DSide *>(obj); + if (side) { + box3d_side_position_set(side); } } } @@ -1079,10 +1081,10 @@ box3d_recompute_z_orders (SPBox3D *box) { static std::map<int, Box3DSide *> box3d_get_sides(SPBox3D *box) { std::map<int, Box3DSide *> sides; - for ( SPObject *side = box->firstChild(); side; side = side->getNext() ) { - if (SP_IS_BOX3D_SIDE(side)){ - Box3DSide *bside = SP_BOX3D_SIDE(side); - sides[Box3D::face_to_int(bside->getFaceId())] = bside; + for ( SPObject *obj = box->firstChild(); obj; obj = obj->getNext() ) { + Box3DSide *side = dynamic_cast<Box3DSide *>(obj); + if (side) { + sides[Box3D::face_to_int(side->getFaceId())] = side; } } sides.erase(-1); @@ -1280,9 +1282,10 @@ SPGroup *box3d_convert_to_group(SPBox3D *box) // create a new group and add the sides (converted to ordinary paths) as its children Inkscape::XML::Node *grepr = xml_doc->createElement("svg:g"); - for ( SPObject *child = box->firstChild(); child; child = child->getNext() ) { - if (SP_IS_BOX3D_SIDE(child)) { - Inkscape::XML::Node *repr = box3d_side_convert_to_path(SP_BOX3D_SIDE(child)); + for ( SPObject *obj = box->firstChild(); obj; obj = obj->getNext() ) { + Box3DSide *side = dynamic_cast<Box3DSide *>(obj); + if (side) { + Inkscape::XML::Node *repr = box3d_side_convert_to_path(side); grepr->appendChild(repr); } else { g_warning("Non-side item encountered as child of a 3D box."); diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp index ce8a06f29..15dc339cf 100644 --- a/src/desktop-style.cpp +++ b/src/desktop-style.cpp @@ -174,8 +174,10 @@ sp_desktop_set_style(SPDesktop *desktop, SPCSSAttr *css, bool change, bool write for (const GSList *i = desktop->selection->itemList(); i != NULL; i = i->next) { /* last used styles for 3D box faces are stored separately */ - if (SP_IS_BOX3D_SIDE (i->data)) { - const char * descr = box3d_side_axes_string(SP_BOX3D_SIDE(i->data)); + SPObject *obj = reinterpret_cast<SPObject *>(i->data); // TODO unsafe until Selection is refactored. + Box3DSide *side = dynamic_cast<Box3DSide *>(obj); + if (side) { + const char * descr = box3d_side_axes_string(side); if (descr != NULL) { prefs->mergeStyle(Glib::ustring("/desktop/") + descr + "/style", css_write); } diff --git a/src/display/CMakeLists.txt b/src/display/CMakeLists.txt index 20424c845..800c4d0d4 100644 --- a/src/display/CMakeLists.txt +++ b/src/display/CMakeLists.txt @@ -13,6 +13,7 @@ set(display_SRC drawing-group.cpp drawing-image.cpp drawing-item.cpp + drawing-pattern.cpp drawing-shape.cpp drawing-surface.cpp drawing-text.cpp @@ -75,6 +76,7 @@ set(display_SRC drawing-group.h drawing-image.h drawing-item.h + drawing-pattern.h drawing-shape.h drawing-surface.h drawing-text.h diff --git a/src/display/Makefile_insert b/src/display/Makefile_insert index abbd89a68..2355c3653 100644 --- a/src/display/Makefile_insert +++ b/src/display/Makefile_insert @@ -33,6 +33,8 @@ ink_common_sources += \ display/drawing-image.h \ display/drawing-item.cpp \ display/drawing-item.h \ + display/drawing-pattern.cpp \ + display/drawing-pattern.h \ display/drawing-shape.cpp \ display/drawing-shape.h \ display/drawing-surface.cpp \ diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 56507bacb..9bb81cea5 100644 --- a/src/display/drawing-item.cpp +++ b/src/display/drawing-item.cpp @@ -15,6 +15,7 @@ #include "display/drawing-context.h" #include "display/drawing-item.h" #include "display/drawing-group.h" +#include "display/drawing-pattern.h" #include "display/drawing-surface.h" #include "nr-filter.h" #include "preferences.h" @@ -112,6 +113,8 @@ DrawingItem::DrawingItem(Drawing &drawing) , _transform(NULL) , _clip(NULL) , _mask(NULL) + , _fill_pattern(NULL) + , _stroke_pattern(NULL) , _filter(NULL) , _user_data(NULL) , _cache(NULL) @@ -166,6 +169,12 @@ DrawingItem::~DrawingItem() case CHILD_ROOT: _drawing._root = NULL; break; + case CHILD_FILL_PATTERN: + _parent->_fill_pattern = NULL; + break; + case CHILD_STROKE_PATTERN: + _parent->_stroke_pattern = NULL; + break; default: ; } @@ -174,6 +183,8 @@ DrawingItem::~DrawingItem() } clearChildren(); delete _transform; + delete _stroke_pattern; + delete _fill_pattern; delete _clip; delete _mask; delete _filter; @@ -368,6 +379,34 @@ DrawingItem::setMask(DrawingItem *item) _markForUpdate(STATE_ALL, true); } +void +DrawingItem::setFillPattern(DrawingPattern *pattern) +{ + _markForRendering(); + delete _fill_pattern; + _fill_pattern = pattern; + if (pattern) { + pattern->_parent = this; + assert(pattern->_child_type == CHILD_ORPHAN); + pattern->_child_type = CHILD_FILL_PATTERN; + } + _markForUpdate(STATE_ALL, true); +} + +void +DrawingItem::setStrokePattern(DrawingPattern *pattern) +{ + _markForRendering(); + delete _stroke_pattern; + _stroke_pattern = pattern; + if (pattern) { + pattern->_parent = this; + assert(pattern->_child_type == CHILD_ORPHAN); + pattern->_child_type = CHILD_STROKE_PATTERN; + } + _markForUpdate(STATE_ALL, true); +} + /// Move this item to the given place in the Z order of siblings. /// Does nothing if the item has no parent. void @@ -532,6 +571,12 @@ DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigne if (to_update & STATE_RENDER) { // now that we know drawbox, dirty the corresponding rect on canvas // unless filtered, groups do not need to render by themselves, only their members + if (_fill_pattern) { + _fill_pattern->update(area, child_ctx, flags, reset); + } + if (_stroke_pattern) { + _stroke_pattern->update(area, child_ctx, flags, reset); + } if (!is_drawing_group(this) || (_filter && render_filters)) { _markForRendering(); } @@ -970,7 +1015,7 @@ DrawingItem::_setStyleCommon(SPStyle *&_style, SPStyle *style) if (_style) sp_style_unref(_style); _style = style; - if (style->filter.set && style->getFilter()) { + if (style && style->filter.set && style->getFilter()) { if (!_filter) { int primitives = sp_filter_primitive_count(SP_FILTER(style->getFilter())); _filter = new Inkscape::Filters::Filter(primitives); diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h index dda5cd6ac..9b399e6e3 100644 --- a/src/display/drawing-item.h +++ b/src/display/drawing-item.h @@ -28,6 +28,7 @@ class Drawing; class DrawingCache; class DrawingContext; class DrawingItem; +class DrawingPattern; namespace Filters { @@ -114,6 +115,8 @@ public: void setTransform(Geom::Affine const &trans); void setClip(DrawingItem *item); void setMask(DrawingItem *item); + void setFillPattern(DrawingPattern *pattern); + void setStrokePattern(DrawingPattern *pattern); void setZOrder(unsigned z); void setItemBounds(Geom::OptRect const &bounds); void setFilterBounds(Geom::OptRect const &bounds); @@ -135,8 +138,8 @@ protected: CHILD_CLIP = 2, // referenced by _clip member of parent CHILD_MASK = 3, // referenced by _mask member of parent CHILD_ROOT = 4, // root item of _drawing - CHILD_FILL_PATTERN = 5, // not yet implemented: referenced by fill pattern of parent - CHILD_STROKE_PATTERN = 6 // not yet implemented: referenced by stroke pattern of parent + CHILD_FILL_PATTERN = 5, // referenced by fill pattern of parent + CHILD_STROKE_PATTERN = 6 // referenced by stroke pattern of parent }; enum RenderResult { RENDER_OK = 0, @@ -185,6 +188,8 @@ protected: DrawingItem *_clip; DrawingItem *_mask; + DrawingPattern *_fill_pattern; + DrawingPattern *_stroke_pattern; Inkscape::Filters::Filter *_filter; void *_user_data; ///< Used to associate DrawingItems with SPItems that created them DrawingCache *_cache; diff --git a/src/display/drawing-pattern.cpp b/src/display/drawing-pattern.cpp new file mode 100644 index 000000000..cf6358278 --- /dev/null +++ b/src/display/drawing-pattern.cpp @@ -0,0 +1,194 @@ +/** + * @file + * Canvas belonging to SVG pattern. + *//* + * Authors: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "display/cairo-utils.h" +#include "display/drawing-context.h" +#include "display/drawing-pattern.h" +#include "display/drawing-surface.h" + +namespace Inkscape { + +DrawingPattern::DrawingPattern(Drawing &drawing, bool debug) + : DrawingGroup(drawing) + , _pattern_to_user(NULL) + , _overflow_steps(1) + , _debug(debug) +{ +} + +DrawingPattern::~DrawingPattern() +{ + delete _pattern_to_user; // delete NULL; is safe +} + +void +DrawingPattern::setPatternToUserTransform(Geom::Affine const &new_trans) { + Geom::Affine current; + if (_pattern_to_user) { + current = *_pattern_to_user; + } + + if (!Geom::are_near(current, new_trans, 1e-18)) { + // mark the area where the object was for redraw. + _markForRendering(); + if (new_trans.isIdentity()) { + delete _pattern_to_user; // delete NULL; is safe + _pattern_to_user = NULL; + } else { + _pattern_to_user = new Geom::Affine(new_trans); + } + _markForUpdate(STATE_ALL, true); + } +} + +void +DrawingPattern::setTileRect(Geom::Rect const &tile_rect) { + _tile_rect = tile_rect; +} + +void +DrawingPattern::setOverflow(Geom::Affine initial_transform, int steps, Geom::Affine step_transform) { + _overflow_initial_transform = initial_transform; + _overflow_steps = steps; + _overflow_step_transform = step_transform; +} + +cairo_pattern_t * +DrawingPattern::renderPattern(float opacity) { + bool needs_opacity = (1.0 - opacity) >= 1e-3; + bool visible = opacity >= 1e-3; + + if (!visible) { + return NULL; + } + + if (!_tile_rect || (_tile_rect->area() == 0)) { + return NULL; + } + Geom::Rect pattern_tile = *_tile_rect; + + //TODO: If pattern_to_user set it to identity transform + + // The DrawingSurface class handles the mapping from "logical space" + // (coordinates in the rendering) to "physical space" (surface pixels). + // An oversampling is done as the pattern may not pixel align with the final surface. + // The cairo surface is created when the DrawingContext is declared. + // Create drawing surface with size of pattern tile (in pattern space) but with number of pixels + // based on required resolution (c). + Inkscape::DrawingSurface pattern_surface(pattern_tile, _pattern_resolution); + Inkscape::DrawingContext dc(pattern_surface); + dc.transform( pattern_surface.drawingTransform().inverse() ); + + pattern_tile *= pattern_surface.drawingTransform(); + Geom::IntRect one_tile = pattern_tile.roundOutwards(); + + // Render pattern. + if (needs_opacity) { + dc.pushGroup(); // this group is for pattern + opacity + } + + if (_debug) { + dc.setSource(0.8, 0.0, 0.8); + dc.paint(); + } + + //FIXME: What flags to choose? + if (_overflow_steps == 1) { + render(dc, one_tile, RENDER_DEFAULT); + } else { + //Overflow transforms need to be transformed to the new coordinate system + //introduced by dc.transform( pattern_surface.drawingTransform().inverse() ); + Geom::Affine dt = pattern_surface.drawingTransform(); + Geom::Affine idt = pattern_surface.drawingTransform().inverse(); + Geom::Affine initial_transform = idt * _overflow_initial_transform * dt; + Geom::Affine step_transform = idt * _overflow_step_transform * dt; + + dc.transform(initial_transform); + for (int i = 0; i < _overflow_steps; i++) { + render(dc, one_tile, RENDER_DEFAULT); + dc.transform(step_transform); + } + } + + //Uncomment to debug + // cairo_surface_t* raw = pattern_surface.raw(); + // std::cout << " cairo_surface (sp-pattern): " + // << " width: " << cairo_image_surface_get_width( raw ) + // << " height: " << cairo_image_surface_get_height( raw ) + // << std::endl; + // std::string filename = "drawing-pattern.png"; + // cairo_surface_write_to_png( pattern_surface.raw(), filename.c_str() ); + + if (needs_opacity) { + dc.popGroupToSource(); // pop raw pattern + dc.paint(opacity); // apply opacity + } + + cairo_pattern_t *cp = cairo_pattern_create_for_surface(pattern_surface.raw()); + // Apply transformation to user space. Also compensate for oversampling. + if (_pattern_to_user) { + ink_cairo_pattern_set_matrix(cp, _pattern_to_user->inverse() * pattern_surface.drawingTransform()); + } else { + ink_cairo_pattern_set_matrix(cp, pattern_surface.drawingTransform()); + } + + if (_debug) { + cairo_pattern_set_extend(cp, CAIRO_EXTEND_NONE); + } else { + cairo_pattern_set_extend(cp, CAIRO_EXTEND_REPEAT); + } + + return cp; +} + +unsigned +DrawingPattern::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) +{ + UpdateContext pattern_ctx; + + if (!_tile_rect || (_tile_rect->area() == 0)) { + return STATE_NONE; + } + + Geom::Rect pattern_tile = *_tile_rect; + Geom::Coord det_ctm = ctx.ctm.descrim(); + Geom::Coord det_ps2user = _pattern_to_user ? _pattern_to_user->descrim() : 1.0; + Geom::Coord det_child_transform = _child_transform ? _child_transform->descrim() : 1.0; + const double oversampling = 2.0; + double scale = det_ctm*det_ps2user*det_child_transform * oversampling; + //FIXME: When scale is too big (zooming in a hatch), cairo doesn't render the pattern + //More precisely it fails when seting pattern matrix in DrawingPattern::renderPattern + //Fully correct solution should make use of visible area bbox and change hach tile rect + //accordingly + if (scale > 25) { + scale = 25; + } + Geom::Point c(pattern_tile.dimensions()*scale*oversampling); + _pattern_resolution = c.ceil(); + + Inkscape::DrawingSurface pattern_surface(pattern_tile, _pattern_resolution); + + pattern_ctx.ctm = pattern_surface.drawingTransform(); + return DrawingGroup::_updateItem(Geom::IntRect::infinite(), pattern_ctx, flags, reset); +} + +} // end namespace Inkscape + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-pattern.h b/src/display/drawing-pattern.h new file mode 100644 index 000000000..7483ba067 --- /dev/null +++ b/src/display/drawing-pattern.h @@ -0,0 +1,87 @@ +/** + * @file + * Canvas belonging to SVG pattern. + *//* + * Authors: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_PATTERN_H +#define SEEN_INKSCAPE_DISPLAY_DRAWING_PATTERN_H + +#include "display/drawing-group.h" + +typedef struct _cairo_pattern cairo_pattern_t; +class SPStyle; + +namespace Inkscape { + +/** + * @brief Drawing tree node used for rendering paints. + * + * DrawingPattern is used for rendering patterns and hatches. + * + * It renders it's children to a cairo_pattern_t structure that can be + * applied as source for fill or stroke operations. + */ +class DrawingPattern + : public DrawingGroup +{ +public: + DrawingPattern(Drawing &drawing, bool debug = false); + ~DrawingPattern(); + + /** + * Set the transformation from pattern to user coordinate systems. + * @see SPPattern description for explanation of coordinate systems. + */ + void setPatternToUserTransform(Geom::Affine const &new_trans); + /** + * Set the tile rect position and dimentions in content coordinate system + */ + void setTileRect(Geom::Rect const &tile_rect); + /** + * Turn on overflow rendering. + * + * Overflow is implemented as repeated rendering of pattern contents. In every step + * a translation transform is applied. + */ + void setOverflow(Geom::Affine initial_transform, int steps, Geom::Affine step_transform); + /** + * Render the pattern. + * + * Returns caito_pattern_t structure that can be set as source surface. + */ + cairo_pattern_t *renderPattern(float opacity); +protected: + virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, + unsigned flags, unsigned reset); + + Geom::Affine *_pattern_to_user; + Geom::Affine _overflow_initial_transform; + Geom::Affine _overflow_step_transform; + int _overflow_steps; + Geom::OptRect _tile_rect; + bool _debug; + Geom::IntPoint _pattern_resolution; +}; + +bool is_drawing_group(DrawingItem *item); + +} // end namespace Inkscape + +#endif // !SEEN_INKSCAPE_DISPLAY_DRAWING_PATTERN_H + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-shape.cpp b/src/display/drawing-shape.cpp index 88506f2b9..66160638f 100644 --- a/src/display/drawing-shape.cpp +++ b/src/display/drawing-shape.cpp @@ -155,7 +155,7 @@ DrawingShape::_renderFill(DrawingContext &dc) Inkscape::DrawingContext::Save save(dc); dc.transform(_ctm); - bool has_fill = _nrstyle.prepareFill(dc, _item_bbox); + bool has_fill = _nrstyle.prepareFill(dc, _item_bbox, _fill_pattern); if( has_fill ) { dc.path(_curve->get_pathvector()); @@ -171,7 +171,7 @@ DrawingShape::_renderStroke(DrawingContext &dc) Inkscape::DrawingContext::Save save(dc); dc.transform(_ctm); - bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox); + bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox, _stroke_pattern); has_stroke &= (_nrstyle.stroke_width != 0); if( has_stroke ) { @@ -231,8 +231,8 @@ DrawingShape::_renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigne // update fill and stroke paints. // this cannot be done during nr_arena_shape_update, because we need a Cairo context // to render svg:pattern - bool has_fill = _nrstyle.prepareFill(dc, _item_bbox); - bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox); + bool has_fill = _nrstyle.prepareFill(dc, _item_bbox, _fill_pattern); + bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox, _stroke_pattern); has_stroke &= (_nrstyle.stroke_width != 0); if (has_fill || has_stroke) { diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp index 97a8c23d3..afe661b2e 100644 --- a/src/display/drawing-text.cpp +++ b/src/display/drawing-text.cpp @@ -448,13 +448,13 @@ unsigned DrawingText::_renderItem(DrawingContext &dc, Geom::IntRect const &/*are Inkscape::DrawingContext::Save save(dc); dc.transform(_ctm); - has_fill = _nrstyle.prepareFill( dc, _item_bbox); - has_stroke = _nrstyle.prepareStroke( dc, _item_bbox); + has_fill = _nrstyle.prepareFill( dc, _item_bbox, _fill_pattern); + has_stroke = _nrstyle.prepareStroke( dc, _item_bbox, _stroke_pattern); // Avoid creating patterns if not needed if( decorate ) { - has_td_fill = _nrstyle.prepareTextDecorationFill( dc, _item_bbox); - has_td_stroke = _nrstyle.prepareTextDecorationStroke(dc, _item_bbox); + has_td_fill = _nrstyle.prepareTextDecorationFill( dc, _item_bbox, _fill_pattern); + has_td_stroke = _nrstyle.prepareTextDecorationStroke(dc, _item_bbox, _stroke_pattern); } } diff --git a/src/display/nr-style.cpp b/src/display/nr-style.cpp index a9f101f69..96d16bf06 100644 --- a/src/display/nr-style.cpp +++ b/src/display/nr-style.cpp @@ -14,6 +14,7 @@ #include "sp-paint-server.h" #include "display/canvas-bpath.h" // contains SPStrokeJoinType, SPStrokeCapType etc. (WTF!) #include "display/drawing-context.h" +#include "display/drawing-pattern.h" void NRStyle::Paint::clear() { @@ -299,13 +300,16 @@ void NRStyle::set(SPStyle *style) update(); } -bool NRStyle::prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox) +bool NRStyle::prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern) { // update fill pattern if (!fill_pattern) { switch (fill.type) { - case PAINT_SERVER: { - fill_pattern = fill.server->pattern_new(dc.raw(), paintbox, fill.opacity); + case PAINT_SERVER: + if (pattern) { + fill_pattern = pattern->renderPattern(fill.opacity); + } else { + fill_pattern = fill.server->pattern_new(dc.raw(), paintbox, fill.opacity); } break; case PAINT_COLOR: { SPColor const &c = fill.color; @@ -325,14 +329,20 @@ void NRStyle::applyFill(Inkscape::DrawingContext &dc) dc.setFillRule(fill_rule); } -bool NRStyle::prepareTextDecorationFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox) +bool NRStyle::prepareTextDecorationFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern) { // update text decoration pattern if (!text_decoration_fill_pattern) { switch (text_decoration_fill.type) { - case PAINT_SERVER: { - text_decoration_fill_pattern = text_decoration_fill.server->pattern_new(dc.raw(), paintbox, text_decoration_fill.opacity); - } break; + case PAINT_SERVER: + if (pattern) { + text_decoration_fill_pattern = pattern->renderPattern( + text_decoration_fill.opacity); + } else { + text_decoration_fill_pattern = text_decoration_fill.server->pattern_new(dc.raw(), + paintbox, text_decoration_fill.opacity); + } + break; case PAINT_COLOR: { SPColor const &c = text_decoration_fill.color; text_decoration_fill_pattern = cairo_pattern_create_rgba( @@ -351,19 +361,25 @@ void NRStyle::applyTextDecorationFill(Inkscape::DrawingContext &dc) // Fill rule does not matter, no intersections. } -bool NRStyle::prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox) +bool NRStyle::prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern) { if (!stroke_pattern) { switch (stroke.type) { - case PAINT_SERVER: { - stroke_pattern = stroke.server->pattern_new(dc.raw(), paintbox, stroke.opacity); - } break; + case PAINT_SERVER: + if (pattern) { + stroke_pattern = pattern->renderPattern(stroke.opacity); + } else { + stroke_pattern = stroke.server->pattern_new(dc.raw(), paintbox, stroke.opacity); + } + break; case PAINT_COLOR: { SPColor const &c = stroke.color; - stroke_pattern = cairo_pattern_create_rgba( - c.v.c[0], c.v.c[1], c.v.c[2], stroke.opacity); - } break; - default: break; + stroke_pattern = cairo_pattern_create_rgba(c.v.c[0], c.v.c[1], c.v.c[2], + stroke.opacity); + } + break; + default: + break; } } if (!stroke_pattern) return false; @@ -380,13 +396,19 @@ void NRStyle::applyStroke(Inkscape::DrawingContext &dc) cairo_set_dash(dc.raw(), dash, n_dash, dash_offset); // fixme } -bool NRStyle::prepareTextDecorationStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox) +bool NRStyle::prepareTextDecorationStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern) { if (!text_decoration_stroke_pattern) { switch (text_decoration_stroke.type) { - case PAINT_SERVER: { - text_decoration_stroke_pattern = text_decoration_stroke.server->pattern_new(dc.raw(), paintbox, text_decoration_stroke.opacity); - } break; + case PAINT_SERVER: + if (pattern) { + text_decoration_stroke_pattern = pattern->renderPattern( + text_decoration_stroke.opacity); + } else { + text_decoration_stroke_pattern = text_decoration_stroke.server->pattern_new( + dc.raw(), paintbox, text_decoration_stroke.opacity); + } + break; case PAINT_COLOR: { SPColor const &c = text_decoration_stroke.color; text_decoration_stroke_pattern = cairo_pattern_create_rgba( diff --git a/src/display/nr-style.h b/src/display/nr-style.h index 83bcb1ab7..f324fdb56 100644 --- a/src/display/nr-style.h +++ b/src/display/nr-style.h @@ -21,6 +21,7 @@ class SPStyle; namespace Inkscape { class DrawingContext; +class DrawingPattern; } struct NRStyle { @@ -28,10 +29,10 @@ struct NRStyle { ~NRStyle(); void set(SPStyle *); - bool prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox); - bool prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox); - bool prepareTextDecorationFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox); - bool prepareTextDecorationStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox); + bool prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern); + bool prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern); + bool prepareTextDecorationFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern); + bool prepareTextDecorationStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern); void applyFill(Inkscape::DrawingContext &dc); void applyStroke(Inkscape::DrawingContext &dc); void applyTextDecorationFill(Inkscape::DrawingContext &dc); diff --git a/src/document.cpp b/src/document.cpp index 5aad4ca5e..70fec83d7 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -709,7 +709,12 @@ void SPDocument::fitToRect(Geom::Rect const &rect, bool with_margins) double const h = rect.height(); double const old_height = getHeight().value("px"); - Inkscape::Util::Unit const *px = unit_table.getUnit("px"); + Inkscape::Util::Unit const *nv_units = unit_table.getUnit("px"); + SPNamedView *nv = sp_document_namedview(this, NULL); + if (nv != NULL) { + if (nv->getAttribute("units")) + nv_units = unit_table.getUnit(nv->getAttribute("units")); + } /* in px */ double margin_top = 0.0; @@ -717,22 +722,16 @@ void SPDocument::fitToRect(Geom::Rect const &rect, bool with_margins) double margin_right = 0.0; double margin_bottom = 0.0; - SPNamedView *nv = sp_document_namedview(this, NULL); - if (with_margins && nv) { if (nv != NULL) { - gchar const * const units_abbr = nv->getAttribute("units"); - Inkscape::Util::Unit const *margin_units = NULL; - if (units_abbr) { - margin_units = unit_table.getUnit(units_abbr); - } - if (!margin_units) { - margin_units = px; - } - margin_top = nv->getMarginLength("fit-margin-top",margin_units, px, w, h, false); - margin_left = nv->getMarginLength("fit-margin-left",margin_units, px, w, h, true); - margin_right = nv->getMarginLength("fit-margin-right",margin_units, px, w, h, true); - margin_bottom = nv->getMarginLength("fit-margin-bottom",margin_units, px, w, h, false); + margin_top = nv->getMarginLength("fit-margin-top", nv_units, unit_table.getUnit("px"), w, h, false); + margin_left = nv->getMarginLength("fit-margin-left", nv_units, unit_table.getUnit("px"), w, h, true); + margin_right = nv->getMarginLength("fit-margin-right", nv_units, unit_table.getUnit("px"), w, h, true); + margin_bottom = nv->getMarginLength("fit-margin-bottom", nv_units, unit_table.getUnit("px"), w, h, false); + margin_top = Inkscape::Util::Quantity::convert(margin_top, nv_units, "px"); + margin_left = Inkscape::Util::Quantity::convert(margin_left, nv_units, "px"); + margin_right = Inkscape::Util::Quantity::convert(margin_right, nv_units, "px"); + margin_bottom = Inkscape::Util::Quantity::convert(margin_bottom, nv_units, "px"); } } @@ -741,8 +740,8 @@ void SPDocument::fitToRect(Geom::Rect const &rect, bool with_margins) rect.max() + Geom::Point(margin_right, margin_top)); - setWidth(Inkscape::Util::Quantity(rect_with_margins.width(), px)); - setHeight(Inkscape::Util::Quantity(rect_with_margins.height(), px)); + setWidth(Inkscape::Util::Quantity(Inkscape::Util::Quantity::convert(rect_with_margins.width(), "px", nv_units), nv_units)); + setHeight(Inkscape::Util::Quantity(Inkscape::Util::Quantity::convert(rect_with_margins.height(), "px", nv_units), nv_units)); Geom::Translate const tr( Geom::Point(0, old_height - rect_with_margins.height()) diff --git a/src/extension/implementation/script.cpp b/src/extension/implementation/script.cpp index 5ee226ccf..e6ac13cdc 100644 --- a/src/extension/implementation/script.cpp +++ b/src/extension/implementation/script.cpp @@ -726,12 +726,12 @@ void Script::effect(Inkscape::Extension::Effect *module, vd->emitReconstructionStart(); copy_doc(vd->rroot, mydoc->rroot); vd->emitReconstructionFinish(); - SPObject *layer = NULL; - + // Getting the named view from the document generated by the extension SPNamedView *nv = sp_document_namedview(mydoc, NULL); //Check if it has a default layer set up + SPObject *layer = NULL; if ( nv != NULL) { if( nv->default_layer_id != 0 ) { @@ -760,19 +760,21 @@ void Script::effect(Inkscape::Extension::Effect *module, /** - \brief A function to take all the svg elements from one document - and put them in another. - \param oldroot The root node of the document to be replaced - \param newroot The root node of the document to replace it with - - This function first deletes all of the data in the old document. It - does this by creating a list of what needs to be deleted, and then - goes through the list. This two pass approach removes issues with - the list being change while parsing through it. Lots of nasty bugs. - - Then, it goes through the new document, duplicating all of the - elements and putting them into the old document. The copy - is then complete. + \brief A function to replace all the elements in an old document + by those from a new document. + document and repinserts them into an emptied old document. + \param oldroot The root node of the old (destination) document. + \param newroot The root node of the new (source) document. + + This function first deletes all the elements in the old document by + making two pass, the first to create a list of the old elements and + the second to actually delete them. This two pass approach removes issues + with the list being change while parsing through it... lots of nasty bugs. + + Then, it copies all the element in the new document into the old document. + + Finally, it replaces the attributes in the root element of the old document + by the attributes in root of the new document. */ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot) { @@ -781,9 +783,21 @@ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newr g_warning("Error on copy_doc: NULL pointer input."); return; } + + // For copying attributes in root and in namedview + using Inkscape::Util::List; + using Inkscape::XML::AttributeRecord; + + // Question: Why is the "sodipodi:namedview" special? Treating it as a normal + // elmement results in crashes. + // Seems to be a bug: + // http://inkscape.13.x6.nabble.com/Effect-that-modifies-the-document-properties-tt2822126.html + std::vector<Inkscape::XML::Node *> delete_list; Inkscape::XML::Node * oldroot_namedview = NULL; + Inkscape::XML::Node * newroot_namedview = NULL; + // Make list for (Inkscape::XML::Node * child = oldroot->firstChild(); child != NULL; child = child->next()) { @@ -798,14 +812,18 @@ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newr delete_list.push_back(child); } } + + // Unparent (delete) for (unsigned int i = 0; i < delete_list.size(); i++) { sp_repr_unparent(delete_list[i]); } + // Copy for (Inkscape::XML::Node * child = newroot->firstChild(); child != NULL; child = child->next()) { if (!strcmp("sodipodi:namedview", child->name())) { + newroot_namedview = child; if (oldroot_namedview != NULL) { for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild(); newroot_namedview_child != NULL; @@ -818,26 +836,44 @@ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newr } } - { - using Inkscape::Util::List; - using Inkscape::XML::AttributeRecord; - std::vector<gchar const *> attribs; + std::vector<gchar const *> attribs; - // Make a list of all attributes of the old root node. - for (List<AttributeRecord const> iter = oldroot->attributeList(); iter; ++iter) { - attribs.push_back(g_quark_to_string(iter->key)); - } + // Must explicitly copy root attributes. - // Delete the attributes of the old root nodes. - for (std::vector<gchar const *>::const_iterator it = attribs.begin(); it != attribs.end(); ++it) { - oldroot->setAttribute(*it, NULL); - } + // Make a list of all attributes of the old root node. + for (List<AttributeRecord const> iter = oldroot->attributeList(); iter; ++iter) { + attribs.push_back(g_quark_to_string(iter->key)); + } - // Set the new attributes. - for (List<AttributeRecord const> iter = newroot->attributeList(); iter; ++iter) { - gchar const *name = g_quark_to_string(iter->key); - oldroot->setAttribute(name, newroot->attribute(name)); - } + // Delete the attributes of the old root node. + for (std::vector<gchar const *>::const_iterator it = attribs.begin(); it != attribs.end(); ++it) { + oldroot->setAttribute(*it, NULL); + } + + // Set the new attributes. + for (List<AttributeRecord const> iter = newroot->attributeList(); iter; ++iter) { + gchar const *name = g_quark_to_string(iter->key); + oldroot->setAttribute(name, newroot->attribute(name)); + } + + attribs.clear(); + + // Must explicitly copy namedview attributes. + + // Make a list of all attributes of the old namedview node. + for (List<AttributeRecord const> iter = oldroot_namedview->attributeList(); iter; ++iter) { + attribs.push_back(g_quark_to_string(iter->key)); + } + + // Delete the attributes of the old namedview node. + for (std::vector<gchar const *>::const_iterator it = attribs.begin(); it != attribs.end(); ++it) { + oldroot_namedview->setAttribute(*it, NULL); + } + + // Set the new attributes. + for (List<AttributeRecord const> iter = newroot_namedview->attributeList(); iter; ++iter) { + gchar const *name = g_quark_to_string(iter->key); + oldroot_namedview->setAttribute(name, newroot_namedview->attribute(name)); } /** \todo Restore correct layer */ diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp index c09b8e9c8..7e61cdbbb 100644 --- a/src/extension/internal/cairo-render-context.cpp +++ b/src/extension/internal/cairo-render-context.cpp @@ -39,6 +39,7 @@ #include "sp-item.h" #include "sp-item-group.h" #include "style.h" +#include "sp-hatch.h" #include "sp-linear-gradient.h" #include "sp-radial-gradient.h" #include "sp-pattern.h" @@ -1130,6 +1131,82 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver } cairo_pattern_t* +CairoRenderContext::_createHatchPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox) { + g_assert( SP_IS_HATCH(paintserver) ); + SPHatch *hatch = SP_HATCH(paintserver); + + g_assert(hatch->pitch() > 0); + + // create drawing and group + Inkscape::Drawing drawing; + unsigned dkey = SPItem::display_key_new(1); + + hatch->show(drawing, dkey, pbox); + + SPHatch::RenderInfo render_info = hatch->calculateRenderInfo(dkey); + Geom::Rect tile_rect = render_info.tile_rect; + + // Cairo requires an integer pattern surface width/height. + // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel. + // Multiply by SUBPIX_SCALE to allow for less than a pixel precision + const int subpix_scale = 10; + double surface_width = MAX(ceil(subpix_scale * tile_rect.width() - 0.5), 1); + double surface_height = MAX(ceil(subpix_scale * tile_rect.height() - 0.5), 1); + Geom::Affine drawing_scale = Geom::Scale(surface_width / tile_rect.width(), surface_height / tile_rect.height()); + Geom::Affine drawing_transform = Geom::Translate(-tile_rect.min()) * drawing_scale; + + Geom::Affine child_transform = render_info.child_transform; + child_transform *= drawing_transform; + + //The rendering of hatch overflow is implemented by repeated drawing + //of hatch paths over one strip. Within each iteration paths are moved by pitch value. + //The movement progresses from right to left. This gives the same result + //as drawing whole strips in left-to-right order. + gdouble overflow_right_strip = 0.0; + int overflow_steps = 1; + Geom::Affine overflow_transform; + if (hatch->style->overflow.computed == SP_CSS_OVERFLOW_VISIBLE) { + Geom::Interval bounds = hatch->bounds(); + overflow_right_strip = floor(bounds.max() / hatch->pitch()) * hatch->pitch(); + overflow_steps = ceil((overflow_right_strip - bounds.min()) / hatch->pitch()) + 1; + overflow_transform = Geom::Translate(hatch->pitch(), 0.0); + } + + CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height); + pattern_ctx->setTransform(child_transform); + pattern_ctx->transform(Geom::Translate(-overflow_right_strip, 0.0)); + pattern_ctx->pushState(); + + std::vector<SPHatchPath *> children; + hatch->hatchPaths(children); + + for (int i = 0; i < overflow_steps; i++) { + for (std::vector<SPHatchPath *>::iterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath *path = *iter; + _renderer->renderHatchPath(pattern_ctx, *path, dkey); + } + pattern_ctx->transform(overflow_transform); + } + + pattern_ctx->popState(); + + // setup a cairo_pattern_t + cairo_surface_t *pattern_surface = pattern_ctx->getSurface(); + TEST(pattern_ctx->saveAsPng("hatch.png")); + cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface); + cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT); + + Geom::Affine pattern_transform; + pattern_transform = render_info.pattern_to_user_transform.inverse() * drawing_transform; + ink_cairo_pattern_set_matrix(result, pattern_transform); + + hatch->hide(dkey); + + delete pattern_ctx; + return result; +} + +cairo_pattern_t* CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver, Geom::OptRect const &pbox, float alpha) { @@ -1182,8 +1259,9 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha); } } else if (SP_IS_PATTERN (paintserver)) { - pattern = _createPatternPainter(paintserver, pbox); + } else if (SP_IS_HATCH (paintserver)) { + pattern = _createHatchPainter(paintserver, pbox); } else { return NULL; } @@ -1249,26 +1327,29 @@ CairoRenderContext::_setFillStyle(SPStyle const *const style, Geom::OptRect cons TRACE(("merged op=%f\n", alpha)); } - if (style->fill.isColor()) { - float rgb[3]; - sp_color_get_rgb_floatv(&style->fill.value.color, rgb); + SPPaintServer const *paint_server = style->getFillPaintServer(); + if (paint_server && paint_server->isValid()) { - cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha); - - } else if (!style->fill.set) { // unset fill is black - cairo_set_source_rgba(_cr, 0, 0, 0, alpha); - - } else { - g_assert( style->fill.isPaintserver() - || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) - || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) ); - - cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha); + g_assert(SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) + || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) + || SP_IS_HATCH(SP_STYLE_FILL_SERVER(style))); + cairo_pattern_t *pattern = _createPatternForPaintServer(paint_server, pbox, alpha); if (pattern) { cairo_set_source(_cr, pattern); cairo_pattern_destroy(pattern); } + } else if (style->fill.colorSet) { + float rgb[3]; + sp_color_get_rgb_floatv(&style->fill.value.color, rgb); + + cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha); + + } else { // unset fill is black + g_assert(!style->fill.set + || (paint_server && !paint_server->isValid())); + + cairo_set_source_rgba(_cr, 0, 0, 0, alpha); } } @@ -1279,7 +1360,7 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, Geom::OptRect const &p if (_state->merge_opacity) alpha *= _state->opacity; - if (style->stroke.isColor()) { + if (style->stroke.isColor() || (style->stroke.isPaintserver() && !style->getStrokePaintServer()->isValid())) { float rgb[3]; sp_color_get_rgb_floatv(&style->stroke.value.color, rgb); @@ -1287,7 +1368,8 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, Geom::OptRect const &p } else { g_assert( style->stroke.isPaintserver() || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style)) - || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) ); + || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) + || SP_IS_HATCH(SP_STYLE_STROKE_SERVER(style))); cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha); diff --git a/src/extension/internal/cairo-render-context.h b/src/extension/internal/cairo-render-context.h index 8071cae36..57d155b60 100644 --- a/src/extension/internal/cairo-render-context.h +++ b/src/extension/internal/cairo-render-context.h @@ -204,6 +204,7 @@ protected: cairo_pattern_t *_createPatternForPaintServer(SPPaintServer const *const paintserver, Geom::OptRect const &pbox, float alpha); cairo_pattern_t *_createPatternPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox); + cairo_pattern_t *_createHatchPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox); unsigned int _showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke); diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index 8196e34fb..a725685d4 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -50,6 +50,7 @@ #include "sp-use.h" #include "sp-text.h" #include "sp-flowtext.h" +#include "sp-hatch-path.h" #include "sp-image.h" #include "sp-symbol.h" #include "sp-pattern.h" @@ -598,6 +599,21 @@ void CairoRenderer::renderItem(CairoRenderContext *ctx, SPItem *item) ctx->popState(); } +void CairoRenderer::renderHatchPath(CairoRenderContext *ctx, SPHatchPath const &hatchPath, unsigned key) { + ctx->pushState(); + ctx->setStateForStyle(hatchPath.style); + ctx->transform(Geom::Translate(hatchPath.offset.computed, 0)); + + SPCurve *curve = hatchPath.calculateRenderCurve(key); + Geom::PathVector const & pathv =curve->get_pathvector(); + if (!pathv.empty()) { + ctx->renderPathVector(pathv, hatchPath.style, Geom::OptRect()); + } + + curve->unref(); + ctx->popState(); +} + bool CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool pageBoundingBox, float bleedmargin_px, SPItem *base) { diff --git a/src/extension/internal/cairo-renderer.h b/src/extension/internal/cairo-renderer.h index cfef6bdea..abc0447d8 100644 --- a/src/extension/internal/cairo-renderer.h +++ b/src/extension/internal/cairo-renderer.h @@ -29,6 +29,7 @@ class SPClipPath; class SPMask; +class SPHatchPath; namespace Inkscape { namespace Extension { @@ -57,6 +58,7 @@ public: /** Traverses the object tree and invokes the render methods. */ void renderItem(CairoRenderContext *ctx, SPItem *item); + void renderHatchPath(CairoRenderContext *ctx, SPHatchPath const &hatchPath, unsigned key); }; // FIXME: this should be a static method of CairoRenderer diff --git a/src/extension/internal/cdr-input.cpp b/src/extension/internal/cdr-input.cpp index 1de3f304b..fb58887c9 100644 --- a/src/extension/internal/cdr-input.cpp +++ b/src/extension/internal/cdr-input.cpp @@ -24,7 +24,21 @@ #include <cstring> #include <libcdr/libcdr.h> -#include <libwpd-stream/libwpd-stream.h> + +// TODO: Drop this check when librevenge is widespread. +#if WITH_LIBCDR01 + #include <librevenge-stream/librevenge-stream.h> + + using librevenge::RVNGString; + using librevenge::RVNGFileStream; + using librevenge::RVNGStringVector; +#else + #include <libwpd-stream/libwpd-stream.h> + + typedef WPXString RVNGString; + typedef WPXFileStream RVNGFileStream; + typedef libcdr::CDRStringVector RVNGStringVector; +#endif #include <gtkmm/alignment.h> #include <gtkmm/comboboxtext.h> @@ -60,7 +74,7 @@ namespace Internal { class CdrImportDialog : public Gtk::Dialog { public: - CdrImportDialog(const std::vector<WPXString> &vec); + CdrImportDialog(const std::vector<RVNGString> &vec); virtual ~CdrImportDialog(); bool showDialog(); @@ -86,12 +100,12 @@ private: class Gtk::VBox * vbox2; class Gtk::Widget * _previewArea; - const std::vector<WPXString> &_vec; // Document to be imported + const std::vector<RVNGString> &_vec; // Document to be imported unsigned _current_page; // Current selected page int _preview_width, _preview_height; // Size of the preview area }; -CdrImportDialog::CdrImportDialog(const std::vector<WPXString> &vec) +CdrImportDialog::CdrImportDialog(const std::vector<RVNGString> &vec) : _vec(vec), _current_page(1) { int num_pages = _vec.size(); @@ -216,14 +230,20 @@ void CdrImportDialog::_setPreviewPage(unsigned page) SPDocument *CdrInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { - WPXFileStream input(uri); + RVNGFileStream input(uri); if (!libcdr::CDRDocument::isSupported(&input)) { return NULL; } - libcdr::CDRStringVector output; + RVNGStringVector output; +#if WITH_LIBCDR01 + librevenge::RVNGSVGDrawingGenerator generator(output, "svg"); + + if (!libcdr::CDRDocument::parse(&input, &generator)) { +#else if (!libcdr::CDRDocument::generateSVG(&input, output)) { +#endif return NULL; } @@ -231,9 +251,9 @@ SPDocument *CdrInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u return NULL; } - std::vector<WPXString> tmpSVGOutput; + std::vector<RVNGString> tmpSVGOutput; for (unsigned i=0; i<output.size(); ++i) { - WPXString tmpString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); + RVNGString tmpString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); tmpString.append(output[i]); tmpSVGOutput.push_back(tmpString); } diff --git a/src/extension/internal/pdfinput/pdf-input.cpp b/src/extension/internal/pdfinput/pdf-input.cpp index e18d898f5..defb15754 100644 --- a/src/extension/internal/pdfinput/pdf-input.cpp +++ b/src/extension/internal/pdfinput/pdf-input.cpp @@ -647,7 +647,31 @@ PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) { // Initialize the globalParams variable for poppler if (!globalParams) { +#ifdef ENABLE_OSX_APP_LOCATIONS + // + // data files for poppler are not relocatable (loaded from + // path defined at build time). This fails to work with relocatable + // application bundles for OS X. + // + // Workaround: + // 1. define $POPPLER_DATADIR env variable in app launcher script + // 2. pass custom $POPPLER_DATADIR via poppler's GlobalParams() + // + // relevant poppler commit: + // <http://cgit.freedesktop.org/poppler/poppler/commit/?id=869584a84eed507775ff1c3183fe484c14b6f77b> + // + // FIXES: Inkscape bug #956282, #1264793 + // TODO: report RFE upstream (full relocation support for OS X packaging) + // + gchar const *poppler_datadir = g_getenv("POPPLER_DATADIR"); + if (poppler_datadir != NULL) { + globalParams = new GlobalParams(poppler_datadir); + } else { + globalParams = new GlobalParams(); + } +#else globalParams = new GlobalParams(); +#endif // ENABLE_OSX_APP_LOCATIONS } // poppler does not use glib g_open. So on win32 we must use unicode call. code was copied from glib gstdio.c #ifndef WIN32 diff --git a/src/extension/internal/pdfinput/pdf-parser.cpp b/src/extension/internal/pdfinput/pdf-parser.cpp index c5f03e5aa..0b7dc7905 100644 --- a/src/extension/internal/pdfinput/pdf-parser.cpp +++ b/src/extension/internal/pdfinput/pdf-parser.cpp @@ -247,30 +247,74 @@ PdfOperator PdfParser::opTab[] = { #define numOps (sizeof(opTab) / sizeof(PdfOperator)) +namespace { + +GfxPatch blankPatch() +{ + GfxPatch patch; + memset(&patch, 0, sizeof(patch)); // quick-n-dirty + return patch; +} + +} // namespace + //------------------------------------------------------------------------ -// PdfParser +// ClipHistoryEntry //------------------------------------------------------------------------ -PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA, - int /*pageNum*/, int rotate, Dict *resDict, - PDFRectangle *box, PDFRectangle *cropBox) -{ - xref = xrefA; - subPage = gFalse; - printCommands = false; +class ClipHistoryEntry { +public: + + ClipHistoryEntry(GfxPath *clipPath = NULL, GfxClipType clipType = clipNormal); + virtual ~ClipHistoryEntry(); + + // Manipulate clip path stack + ClipHistoryEntry *save(); + ClipHistoryEntry *restore(); + GBool hasSaves() { return saved != NULL; } + void setClip(GfxPath *newClipPath, GfxClipType newClipType = clipNormal); + GfxPath *getClipPath() { return clipPath; } + GfxClipType getClipType() { return clipType; } - // start the resource stack - res = new GfxResources(xref, resDict, NULL); +private: - // initialize - state = new GfxState(72.0, 72.0, box, rotate, gTrue); - clipHistory = new ClipHistoryEntry(); + ClipHistoryEntry *saved; // next clip path on stack + + GfxPath *clipPath; // used as the path to be filled for an 'sh' operator + GfxClipType clipType; + + ClipHistoryEntry(ClipHistoryEntry *other); +}; + +//------------------------------------------------------------------------ +// PdfParser +//------------------------------------------------------------------------ + +PdfParser::PdfParser(XRef *xrefA, + Inkscape::Extension::Internal::SvgBuilder *builderA, + int /*pageNum*/, + int rotate, + Dict *resDict, + PDFRectangle *box, + PDFRectangle *cropBox) : + xref(xrefA), + builder(builderA), + subPage(gFalse), + printCommands(false), + res(new GfxResources(xref, resDict, NULL)), // start the resource stack + state(new GfxState(72.0, 72.0, box, rotate, gTrue)), + fontChanged(gFalse), + clip(clipNone), + ignoreUndef(0), + baseMatrix(), + formDepth(0), + parser(NULL), + colorDeltas(), + maxDepths(), + clipHistory(new ClipHistoryEntry()), + operatorHistory(NULL) +{ setDefaultApproximationPrecision(); - fontChanged = gFalse; - clip = clipNone; - ignoreUndef = 0; - operatorHistory = NULL; - builder = builderA; builder->setDocumentSize(Inkscape::Util::Quantity::convert(state->getPageWidth(), "pt", "px"), Inkscape::Util::Quantity::convert(state->getPageHeight(), "pt", "px")); @@ -306,50 +350,62 @@ PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *bui pushOperator("startPage"); } -PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA, - Dict *resDict, PDFRectangle *box) { - - int i; - parser = NULL; - - xref = xrefA; - subPage = gTrue; - printCommands = false; - - // start the resource stack - res = new GfxResources(xref, resDict, NULL); - - // initialize - operatorHistory = NULL; - builder = builderA; - state = new GfxState(72, 72, box, 0, gFalse); - clipHistory = new ClipHistoryEntry(); +PdfParser::PdfParser(XRef *xrefA, + Inkscape::Extension::Internal::SvgBuilder *builderA, + Dict *resDict, + PDFRectangle *box) : + xref(xrefA), + builder(builderA), + subPage(gTrue), + printCommands(false), + res(new GfxResources(xref, resDict, NULL)), // start the resource stack + state(new GfxState(72, 72, box, 0, gFalse)), + fontChanged(gFalse), + clip(clipNone), + ignoreUndef(0), + baseMatrix(), + formDepth(0), + parser(NULL), + colorDeltas(), + maxDepths(), + clipHistory(new ClipHistoryEntry()), + operatorHistory(NULL) +{ setDefaultApproximationPrecision(); - fontChanged = gFalse; - clip = clipNone; - ignoreUndef = 0; - for (i = 0; i < 6; ++i) { + for (int i = 0; i < 6; ++i) { baseMatrix[i] = state->getCTM()[i]; } formDepth = 0; } PdfParser::~PdfParser() { - while (state->hasSaves()) { + while(operatorHistory) { + OpHistoryEntry *tmp = operatorHistory->next; + delete operatorHistory; + operatorHistory = tmp; + } + + while (state && state->hasSaves()) { restoreState(); } + if (!subPage) { //out->endPage(); } + while (res) { popResources(); } + if (state) { delete state; + state = NULL; } + if (clipHistory) { delete clipHistory; + clipHistory = NULL; } } @@ -460,7 +516,8 @@ void PdfParser::go(GBool /*topLevel*/) } } -void PdfParser::pushOperator(const char *name) { +void PdfParser::pushOperator(const char *name) +{ OpHistoryEntry *newEntry = new OpHistoryEntry; newEntry->name = name; newEntry->state = NULL; @@ -2086,13 +2143,19 @@ void PdfParser::doPatchMeshShFill(GfxPatchMeshShading *shading) { } void PdfParser::fillPatch(GfxPatch *patch, int nComps, int depth) { - GfxPatch patch00, patch01, patch10, patch11; + GfxPatch patch00 = blankPatch(); + GfxPatch patch01 = blankPatch(); + GfxPatch patch10 = blankPatch(); + GfxPatch patch11 = blankPatch(); #ifdef POPPLER_NEW_GFXPATCH - GfxColor color; + GfxColor color = {{0}}; #endif - double xx[4][8], yy[4][8]; - double xxm, yym; - double patchColorDelta = colorDeltas[pdfPatchMeshShading-1]; + double xx[4][8]; + double yy[4][8]; + double xxm; + double yym; + double patchColorDelta = colorDeltas[pdfPatchMeshShading - 1]; + int i; for (i = 0; i < nComps; ++i) { @@ -3463,9 +3526,7 @@ void PdfParser::popResources() { } void PdfParser::setDefaultApproximationPrecision() { - int i; - - for (i = 1; i <= pdfNumShadingTypes; ++i) { + for (int i = 1; i <= pdfNumShadingTypes; ++i) { setApproximationPrecision(i, defaultShadingColorDelta, defaultShadingMaxDepth); } } @@ -3484,19 +3545,18 @@ void PdfParser::setApproximationPrecision(int shadingType, double colorDelta, // ClipHistoryEntry //------------------------------------------------------------------------ -ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) { - if (clipPathA) { - clipPath = clipPathA->copy(); - } else { - clipPath = NULL; - } - clipType = clipTypeA; - saved = NULL; +ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) : + saved(NULL), + clipPath((clipPathA) ? clipPathA->copy() : NULL), + clipType(clipTypeA) +{ } -ClipHistoryEntry::~ClipHistoryEntry() { +ClipHistoryEntry::~ClipHistoryEntry() +{ if (clipPath) { delete clipPath; + clipPath = NULL; } } @@ -3510,6 +3570,7 @@ void ClipHistoryEntry::setClip(GfxPath *clipPathA, GfxClipType clipTypeA) { clipType = clipTypeA; } else { clipPath = NULL; + clipType = clipNormal; } } @@ -3526,7 +3587,7 @@ ClipHistoryEntry *ClipHistoryEntry::restore() { if (saved) { oldEntry = saved; saved = NULL; - delete this; + delete this; // TODO really should avoid deleting from inside. } else { oldEntry = this; } @@ -3540,6 +3601,7 @@ ClipHistoryEntry::ClipHistoryEntry(ClipHistoryEntry *other) { this->clipType = other->clipType; } else { this->clipPath = NULL; + this->clipType = clipNormal; } saved = NULL; } diff --git a/src/extension/internal/pdfinput/pdf-parser.h b/src/extension/internal/pdfinput/pdf-parser.h index a63d669c7..e28fecc2e 100644 --- a/src/extension/internal/pdfinput/pdf-parser.h +++ b/src/extension/internal/pdfinput/pdf-parser.h @@ -58,6 +58,8 @@ class AnnotBorderStyle; class PdfParser; +class ClipHistoryEntry; + //------------------------------------------------------------------------ #ifndef GFX_H @@ -101,34 +103,6 @@ struct OpHistoryEntry { }; //------------------------------------------------------------------------ -// ClipHistoryEntry -//------------------------------------------------------------------------ - -class ClipHistoryEntry { -public: - - ClipHistoryEntry(GfxPath *clipPath=NULL, GfxClipType clipType=clipNormal); - virtual ~ClipHistoryEntry(); - - // Manipulate clip path stack - ClipHistoryEntry *save(); - ClipHistoryEntry *restore(); - GBool hasSaves() { return saved != NULL; } - void setClip(GfxPath *newClipPath, GfxClipType newClipType=clipNormal); - GfxPath *getClipPath() { return clipPath; } - GfxClipType getClipType() { return clipType; } - -private: - - ClipHistoryEntry *saved; // next clip path on stack - - GfxPath *clipPath; // used as the path to be filled for an 'sh' operator - GfxClipType clipType; - - ClipHistoryEntry(ClipHistoryEntry *other); -}; - -//------------------------------------------------------------------------ // PdfParser //------------------------------------------------------------------------ diff --git a/src/extension/internal/vsd-input.cpp b/src/extension/internal/vsd-input.cpp index 2623d1ca7..25815e64f 100644 --- a/src/extension/internal/vsd-input.cpp +++ b/src/extension/internal/vsd-input.cpp @@ -24,7 +24,22 @@ #include <cstring> #include <libvisio/libvisio.h> -#include <libwpd-stream/libwpd-stream.h> + +// TODO: Drop this check when librevenge is widespread. +#if WITH_LIBVISIO01 + #include <librevenge-stream/librevenge-stream.h> + + using librevenge::RVNGString; + using librevenge::RVNGFileStream; + using librevenge::RVNGStringVector; +#else + #include <libwpd-stream/libwpd-stream.h> + + typedef WPXString RVNGString; + typedef WPXFileStream RVNGFileStream; + typedef libvisio::VSDStringVector RVNGStringVector; +#endif + #include <gtkmm/alignment.h> #include <gtkmm/comboboxtext.h> @@ -59,7 +74,7 @@ namespace Internal { class VsdImportDialog : public Gtk::Dialog { public: - VsdImportDialog(const std::vector<WPXString> &vec); + VsdImportDialog(const std::vector<RVNGString> &vec); virtual ~VsdImportDialog(); bool showDialog(); @@ -85,12 +100,12 @@ private: class Gtk::VBox * vbox2; class Gtk::Widget * _previewArea; - const std::vector<WPXString> &_vec; // Document to be imported + const std::vector<RVNGString> &_vec; // Document to be imported unsigned _current_page; // Current selected page int _preview_width, _preview_height; // Size of the preview area }; -VsdImportDialog::VsdImportDialog(const std::vector<WPXString> &vec) +VsdImportDialog::VsdImportDialog(const std::vector<RVNGString> &vec) : _vec(vec), _current_page(1) { int num_pages = _vec.size(); @@ -215,14 +230,20 @@ void VsdImportDialog::_setPreviewPage(unsigned page) SPDocument *VsdInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { - WPXFileStream input(uri); + RVNGFileStream input(uri); if (!libvisio::VisioDocument::isSupported(&input)) { return NULL; } - libvisio::VSDStringVector output; + RVNGStringVector output; +#if WITH_LIBVISIO01 + librevenge::RVNGSVGDrawingGenerator generator(output, "svg"); + + if (!libvisio::VisioDocument::parse(&input, &generator)) { +#else if (!libvisio::VisioDocument::generateSVG(&input, output)) { +#endif return NULL; } @@ -230,9 +251,9 @@ SPDocument *VsdInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u return NULL; } - std::vector<WPXString> tmpSVGOutput; + std::vector<RVNGString> tmpSVGOutput; for (unsigned i=0; i<output.size(); ++i) { - WPXString tmpString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); + RVNGString tmpString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); tmpString.append(output[i]); tmpSVGOutput.push_back(tmpString); } diff --git a/src/extension/internal/wpg-input.cpp b/src/extension/internal/wpg-input.cpp index 14ff3ec77..12d86a99a 100644 --- a/src/extension/internal/wpg-input.cpp +++ b/src/extension/internal/wpg-input.cpp @@ -52,16 +52,24 @@ #include "util/units.h" #include <cstring> -// Take a guess and fallback to 0.1.x if no configure has run -#if !defined(WITH_LIBWPG01) && !defined(WITH_LIBWPG02) -#define WITH_LIBWPG01 1 +// Take a guess and fallback to 0.2.x if no configure has run +#if !defined(WITH_LIBWPG03) && !defined(WITH_LIBWPG02) +#define WITH_LIBWPG02 1 #endif #include "libwpg/libwpg.h" -#if WITH_LIBWPG01 -#include "libwpg/WPGStreamImplementation.h" -#elif WITH_LIBWPG02 -#include "libwpd-stream/libwpd-stream.h" +#if WITH_LIBWPG03 + #include <librevenge-stream/librevenge-stream.h> + + using librevenge::RVNGString; + using librevenge::RVNGFileStream; + using librevenge::RVNGInputStream; +#else + #include "libwpd-stream/libwpd-stream.h" + + typedef WPXString RVNGString; + typedef WPXFileStream RVNGFileStream; + typedef WPXInputStream RVNGInputStream; #endif using namespace libwpg; @@ -73,17 +81,15 @@ namespace Internal { SPDocument *WpgInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { -#if WITH_LIBWPG01 - WPXInputStream* input = new libwpg::WPGFileStream(uri); -#elif WITH_LIBWPG02 - WPXInputStream* input = new WPXFileStream(uri); -#endif + RVNGInputStream* input = new RVNGFileStream(uri); +#if WITH_LIBWPG03 + if (input->isStructured()) { + RVNGInputStream* olestream = input->getSubStreamByName("PerfectOffice_MAIN"); +#else if (input->isOLEStream()) { -#if WITH_LIBWPG01 - WPXInputStream* olestream = input->getDocumentOLEStream(); -#elif WITH_LIBWPG02 - WPXInputStream* olestream = input->getDocumentOLEStream("PerfectOffice_MAIN"); + RVNGInputStream* olestream = input->getDocumentOLEStream("PerfectOffice_MAIN"); #endif + if (olestream) { delete input; input = olestream; @@ -98,15 +104,24 @@ SPDocument *WpgInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u return NULL; } -#if WITH_LIBWPG01 - libwpg::WPGString output; -#elif WITH_LIBWPG02 - WPXString output; -#endif +#if WITH_LIBWPG03 + librevenge::RVNGStringVector vec; + librevenge::RVNGSVGDrawingGenerator generator(vec, ""); + + if (!libwpg::WPGraphics::parse(input, &generator) || vec.empty() || vec[0].empty()) { + delete input; + return NULL; + } + + RVNGString output("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); + output.append(vec[0]); +#else + RVNGString output; if (!libwpg::WPGraphics::generateSVG(input, output)) { delete input; return NULL; } +#endif //printf("I've got a doc: \n%s", painter.document.c_str()); diff --git a/src/extension/param/color.cpp b/src/extension/param/color.cpp index 0a2598c56..5bd70359f 100644 --- a/src/extension/param/color.cpp +++ b/src/extension/param/color.cpp @@ -59,9 +59,10 @@ guint32 ParamColor::set( guint32 in, SPDocument * /*doc*/, Inkscape::XML::Node * return _value; } -ParamColor::ParamColor (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), - _changeSignal(0) +ParamColor::ParamColor(const gchar *name, const gchar *guitext, const gchar *desc, const Parameter::_scope_t scope, + bool gui_hidden, const gchar *gui_tip, Inkscape::Extension::Extension *ext, + Inkscape::XML::Node *xml) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), _value(0), _changeSignal(0) { const char * defaulthex = NULL; if (xml->firstChild() != NULL) @@ -75,7 +76,8 @@ ParamColor::ParamColor (const gchar * name, const gchar * guitext, const gchar * if (!paramval.empty()) defaulthex = paramval.data(); - _value = atoi(defaulthex); + if (defaulthex) + _value = atoi(defaulthex); } void ParamColor::string(std::string &string) const @@ -87,7 +89,7 @@ void ParamColor::string(std::string &string) const Gtk::Widget *ParamColor::get_widget( SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/, sigc::signal<void> * changeSignal ) { - if (_gui_hidden) return NULL; + if (_gui_hidden) return NULL; _changeSignal = new sigc::signal<void>(*changeSignal); Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4)); diff --git a/src/extension/param/enum.cpp b/src/extension/param/enum.cpp index bb50c06e1..74b2a75ad 100644 --- a/src/extension/param/enum.cpp +++ b/src/extension/param/enum.cpp @@ -41,8 +41,8 @@ namespace Extension { class enumentry { public: enumentry (Glib::ustring &val, Glib::ustring &text) : - value(val), - guitext(text) + value(val), + guitext(text) {} Glib::ustring value; @@ -50,16 +50,19 @@ public: }; -ParamComboBox::ParamComboBox (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), _indent(0) +ParamComboBox::ParamComboBox(const gchar *name, const gchar *guitext, const gchar *desc, + const Parameter::_scope_t scope, bool gui_hidden, const gchar *gui_tip, + Inkscape::Extension::Extension *ext, Inkscape::XML::Node *xml) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext) + , _value(NULL) + , _indent(0) + , choices(NULL) { - choices = NULL; - _value = NULL; + const char *xmlval = NULL; // the value stored in XML - // Read XML tree to add enumeration items: - // printf("Extension Constructor: "); if (xml != NULL) { - for (Inkscape::XML::Node *node = xml->firstChild(); node; node = node->next()) { + // Read XML tree to add enumeration items: + for (Inkscape::XML::Node *node = xml->firstChild(); node; node = node->next()) { char const * chname = node->name(); if (!strcmp(chname, INKSCAPE_EXTENSION_NS "item") || !strcmp(chname, INKSCAPE_EXTENSION_NS "_item")) { Glib::ustring newguitext, newvalue; @@ -69,8 +72,8 @@ ParamComboBox::ParamComboBox (const gchar * name, const gchar * guitext, const g } if (contents != NULL) { // don't translate when 'item' but do translate when '_item' - // NOTE: internal extensions use build_from_mem and don't need _item but - // still need to include if are to be localized + // NOTE: internal extensions use build_from_mem and don't need _item but + // still need to include if are to be localized if (!strcmp(chname, INKSCAPE_EXTENSION_NS "_item")) { if (node->attribute("msgctxt") != NULL) { newguitext = g_dpgettext2(NULL, node->attribute("msgctxt"), contents); @@ -95,30 +98,28 @@ ParamComboBox::ParamComboBox (const gchar * name, const gchar * guitext, const g } } } - } - - // Initialize _value with the default value from xml - // for simplicity : default to the contents of the first xml-child - const char * defaultval = NULL; - if (xml->firstChild() && xml->firstChild()->firstChild()) { - defaultval = xml->firstChild()->attribute("value"); - } + + // Initialize _value with the default value from xml + // for simplicity : default to the contents of the first xml-child + if (xml->firstChild() && xml->firstChild()->firstChild()) { + xmlval = xml->firstChild()->attribute("value"); + } - const char * indent = xml->attribute("indent"); - if (indent != NULL) { - _indent = atoi(indent) * 12; + const char *indent = xml->attribute("indent"); + if (indent != NULL) { + _indent = atoi(indent) * 12; + } } gchar * pref_name = this->pref_name(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - Glib::ustring paramval = prefs->getString(extension_pref_root + pref_name); + Glib::ustring paramval = prefs ? prefs->getString(extension_pref_root + pref_name) : ""; g_free(pref_name); if (!paramval.empty()) { - defaultval = paramval.data(); - } - if (defaultval != NULL) { - _value = g_strdup(defaultval); + _value = g_strdup(paramval.data()); + } else if (xmlval) { + _value = g_strdup(xmlval); } } diff --git a/src/filters/blend.cpp b/src/filters/blend.cpp index fd5a9871e..ca1d5bf96 100644 --- a/src/filters/blend.cpp +++ b/src/filters/blend.cpp @@ -183,6 +183,8 @@ void SPFeBlend::update(SPCtx *ctx, guint flags) { /* Unlike normal in, in2 is required attribute. Make sure, we can call * it by some name. */ + /* This may not be true.... see issue at + * http://www.w3.org/TR/filter-effects/#feBlendElement (but it doesn't hurt). */ if (this->in2 == Inkscape::Filters::NR_FILTER_SLOT_NOT_SET || this->in2 == Inkscape::Filters::NR_FILTER_UNNAMED_SLOT) { @@ -206,26 +208,30 @@ Inkscape::XML::Node* SPFeBlend::write(Inkscape::XML::Document *doc, Inkscape::XM repr = doc->createElement("svg:feBlend"); } - gchar const *out_name = sp_filter_name_for_image(parent, this->in2); + gchar const *in2_name = sp_filter_name_for_image(parent, this->in2); - if (out_name) { - repr->setAttribute("in2", out_name); - } else { + if( !in2_name ) { + + // This code is very similar to sp_filter_primtive_name_previous_out() SPObject *i = parent->children; + // Find previous filter primitive while (i && i->next != this) { i = i->next; } - SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); - out_name = sp_filter_name_for_image(parent, i_prim->image_out); - repr->setAttribute("in2", out_name); - - if (!out_name) { - g_warning("Unable to set in2 for feBlend"); + if( i ) { + SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); + in2_name = sp_filter_name_for_image(parent, i_prim->image_out); } } + if (in2_name) { + repr->setAttribute("in2", in2_name); + } else { + g_warning("Unable to set in2 for feBlend"); + } + char const *mode; switch(this->blend_mode) { case Inkscape::Filters::BLEND_NORMAL: diff --git a/src/filters/composite.cpp b/src/filters/composite.cpp index 257292f12..e600b6d20 100644 --- a/src/filters/composite.cpp +++ b/src/filters/composite.cpp @@ -205,6 +205,8 @@ void SPFeComposite::update(SPCtx *ctx, guint flags) { /* Unlike normal in, in2 is required attribute. Make sure, we can call * it by some name. */ + /* This may not be true.... see issue at + * http://www.w3.org/TR/filter-effects/#feBlendElement (but it doesn't hurt). */ if (this->in2 == Inkscape::Filters::NR_FILTER_SLOT_NOT_SET || this->in2 == Inkscape::Filters::NR_FILTER_UNNAMED_SLOT) { @@ -228,26 +230,30 @@ Inkscape::XML::Node* SPFeComposite::write(Inkscape::XML::Document *doc, Inkscape repr = doc->createElement("svg:feComposite"); } - gchar const *out_name = sp_filter_name_for_image(parent, this->in2); + gchar const *in2_name = sp_filter_name_for_image(parent, this->in2); - if (out_name) { - repr->setAttribute("in2", out_name); - } else { + if( !in2_name ) { + + // This code is very similar to sp_filter_primitive_name_previous_out() SPObject *i = parent->children; + // Find previous filter primitive while (i && i->next != this) { i = i->next; } - SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); - out_name = sp_filter_name_for_image(parent, i_prim->image_out); - repr->setAttribute("in2", out_name); - - if (!out_name) { - g_warning("Unable to set in2 for feComposite"); + if( i ) { + SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); + in2_name = sp_filter_name_for_image(parent, i_prim->image_out); } } + if (in2_name) { + repr->setAttribute("in2", in2_name); + } else { + g_warning("Unable to set in2 for feComposite"); + } + char const *comp_op; switch (this->composite_operator) { diff --git a/src/filters/displacementmap.cpp b/src/filters/displacementmap.cpp index 473e4913f..7dedfc031 100644 --- a/src/filters/displacementmap.cpp +++ b/src/filters/displacementmap.cpp @@ -202,25 +202,30 @@ Inkscape::XML::Node* SPFeDisplacementMap::write(Inkscape::XML::Document *doc, In repr = doc->createElement("svg:feDisplacementMap"); } - gchar const *out_name = sp_filter_name_for_image(parent, this->in2); - if (out_name) { - repr->setAttribute("in2", out_name); - } else { + gchar const *in2_name = sp_filter_name_for_image(parent, this->in2); + + if( !in2_name ) { + + // This code is very similar to sp_filter_primtive_name_previous_out() SPObject *i = parent->children; + // Find previous filter primitive while (i && i->next != this) { i = i->next; } - SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); - out_name = sp_filter_name_for_image(parent, i_prim->image_out); - repr->setAttribute("in2", out_name); - - if (!out_name) { - g_warning("Unable to set in2 for feDisplacementMap"); + if( i ) { + SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); + in2_name = sp_filter_name_for_image(parent, i_prim->image_out); } } + if (in2_name) { + repr->setAttribute("in2", in2_name); + } else { + g_warning("Unable to set in2 for feDisplacementMap"); + } + sp_repr_set_svg_double(repr, "scale", this->scale); repr->setAttribute("xChannelSelector", get_channelselector_name(this->xChannelSelector)); diff --git a/src/libnrtype/FontFactory.h b/src/libnrtype/FontFactory.h index 46df0181a..bd5a4460c 100644 --- a/src/libnrtype/FontFactory.h +++ b/src/libnrtype/FontFactory.h @@ -21,7 +21,7 @@ #include <pango/pango.h> #include "nr-type-primitives.h" -#include "../style.h" +#include "style.h" /* Freetype */ #ifdef USE_PANGO_WIN32 diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt index 30c2b2f41..c8a02c810 100644 --- a/src/live_effects/CMakeLists.txt +++ b/src/live_effects/CMakeLists.txt @@ -15,7 +15,6 @@ set(live_effects_SRC lpe-dynastroke.cpp lpe-ellipse-5pts.cpp lpe-envelope.cpp - lpe-envelope-perspective.cpp lpe-extrude.cpp lpe-fill-between-many.cpp lpe-fill-between-strokes.cpp @@ -32,6 +31,7 @@ set(live_effects_SRC lpe-patternalongpath.cpp lpe-perp_bisector.cpp lpe-perspective_path.cpp + lpe-perspective-envelope.cpp lpe-powerstroke.cpp lpe-recursiveskeleton.cpp lpe-rough-hatches.cpp @@ -47,7 +47,6 @@ set(live_effects_SRC lpe-bspline.cpp lpe-text_label.cpp lpe-vonkoch.cpp - lpe-envelope-perspective.cpp lpegroupbbox.cpp lpeobject-reference.cpp lpeobject.cpp @@ -106,6 +105,7 @@ set(live_effects_SRC lpe-patternalongpath.h lpe-perp_bisector.h lpe-perspective_path.h + lpe-perspective-envelope.h lpe-powerstroke.h lpe-powerstroke-interpolators.h lpe-recursiveskeleton.h @@ -122,7 +122,6 @@ set(live_effects_SRC lpe-bspline.h lpe-text_label.h lpe-vonkoch.h - lpe-envelope-perspective.h lpegroupbbox.h lpeobject-reference.h lpeobject.h diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert index f18dcdef0..8f0a3ac57 100644 --- a/src/live_effects/Makefile_insert +++ b/src/live_effects/Makefile_insert @@ -70,6 +70,8 @@ ink_common_sources += \ live_effects/lpe-circle_with_radius.h \ live_effects/lpe-perspective_path.cpp \ live_effects/lpe-perspective_path.h \ + live_effects/lpe-perspective-envelope.cpp \ + live_effects/lpe-perspective-envelope.h \ live_effects/lpe-mirror_symmetry.cpp \ live_effects/lpe-mirror_symmetry.h \ live_effects/lpe-circle_3pts.cpp \ @@ -112,6 +114,4 @@ ink_common_sources += \ live_effects/lpe-jointype.cpp \ live_effects/lpe-jointype.h \ live_effects/lpe-taperstroke.cpp \ - live_effects/lpe-taperstroke.h \ - live_effects/lpe-envelope-perspective.cpp \ - live_effects/lpe-envelope-perspective.h + live_effects/lpe-taperstroke.h diff --git a/src/live_effects/effect-enum.h b/src/live_effects/effect-enum.h index c53f1a5b9..383eec19e 100644 --- a/src/live_effects/effect-enum.h +++ b/src/live_effects/effect-enum.h @@ -63,7 +63,7 @@ enum EffectType { BOUNDING_BOX, JOIN_TYPE, TAPER_STROKE, - ENVELOPE_PERSPECTIVE, + PERSPECTIVE_ENVELOPE, FILLET_CHAMFER, INVALID_LPE // This must be last (I made it such that it is not needed anymore I think..., Don't trust on it being last. - johan) }; diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 22f688873..b002eaf7f 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -25,6 +25,7 @@ #include "live_effects/lpe-curvestitch.h" #include "live_effects/lpe-circle_with_radius.h" #include "live_effects/lpe-perspective_path.h" +#include "live_effects/lpe-perspective-envelope.h" #include "live_effects/lpe-spiro.h" #include "live_effects/lpe-lattice.h" #include "live_effects/lpe-lattice2.h" @@ -60,7 +61,6 @@ #include "live_effects/lpe-bounding-box.h" #include "live_effects/lpe-jointype.h" #include "live_effects/lpe-taperstroke.h" -#include "live_effects/lpe-envelope-perspective.h" #include "live_effects/lpe-fillet-chamfer.h" #include "xml/node-event-vector.h" @@ -152,7 +152,7 @@ const Util::EnumData<EffectType> LPETypeData[] = { /* 0.91 */ {SIMPLIFY, N_("Simplify"), "simplify"}, {LATTICE2, N_("Lattice Deformation 2"), "lattice2"}, - {ENVELOPE_PERSPECTIVE, N_("Envelope-Perspective"), "envelope-perspective"}, + {PERSPECTIVE_ENVELOPE, N_("Perspective/Envelope"), "perspective-envelope"}, {FILLET_CHAMFER, N_("Fillet/Chamfer"), "fillet-chamfer"}, {INTERPOLATE_POINTS, N_("Interpolate points"), "interpolate_points"}, }; @@ -311,8 +311,8 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj) case LATTICE2: neweffect = static_cast<Effect*> ( new LPELattice2(lpeobj) ); break; - case ENVELOPE_PERSPECTIVE: - neweffect = static_cast<Effect*> ( new LPEEnvelopePerspective(lpeobj) ); + case PERSPECTIVE_ENVELOPE: + neweffect = static_cast<Effect*> ( new LPEPerspectiveEnvelope(lpeobj) ); break; case FILLET_CHAMFER: neweffect = static_cast<Effect*> ( new LPEFilletChamfer(lpeobj) ); diff --git a/src/live_effects/lpe-bspline.cpp b/src/live_effects/lpe-bspline.cpp index 5ba0d697b..7703b8221 100644 --- a/src/live_effects/lpe-bspline.cpp +++ b/src/live_effects/lpe-bspline.cpp @@ -59,17 +59,22 @@ const double noPower = 0.0; const double defaultStartPower = 0.3334; const double defaultEndPower = 0.6667; -LPEBSpline::LPEBSpline(LivePathEffectObject *lpeobject) : - Effect(lpeobject), - steps(_("Steps whith CTRL:"), _("Change number of steps whith CTRL pressed"), "steps", &wr, this, 2), - ignoreCusp(_("Ignore cusp nodes"), _("Change ignoring cusp nodes"), "ignoreCusp", &wr, this, true), - onlySelected(_("Change only selected nodes"), _("Change only selected nodes"), "onlySelected", &wr, this, false), - weight(_("Change weight:"), _("Change weight of the effect"), "weight", &wr, this, defaultStartPower) +LPEBSpline::LPEBSpline(LivePathEffectObject *lpeobject) + : Effect(lpeobject), + // initialise your parameters here: + //testpointA(_("Test Point A"), _("Test A"), "ptA", &wr, this, + //Geom::Point(100,100)), + steps(_("Steps whith CTRL:"), _("Change number of steps whith CTRL pressed"), "steps", &wr, this, 2), + ignoreCusp(_("Ignore cusp nodes"), _("Change ignoring cusp nodes"), "ignoreCusp", &wr, this, true), + onlySelected(_("Change only selected nodes"), _("Change only selected nodes"), "onlySelected", &wr, this, false), + showHelper(_("Show helper paths"), _("Show helper paths"), "showHelper", &wr, this, false), + weight(_("Change weight:"), _("Change weight of the effect"), "weight", &wr, this, defaultStartPower) { - registerParameter(&weight); - registerParameter(&steps); - registerParameter(&ignoreCusp); - registerParameter(&onlySelected); + registerParameter(dynamic_cast<Parameter *>(&weight)); + registerParameter(dynamic_cast<Parameter *>(&steps)); + registerParameter(dynamic_cast<Parameter *>(&ignoreCusp)); + registerParameter(dynamic_cast<Parameter *>(&onlySelected)); + registerParameter(dynamic_cast<Parameter *>(&showHelper)); weight.param_set_range(noPower, 1); weight.param_set_increments(0.1, 0.1); @@ -89,7 +94,9 @@ void LPEBSpline::doBeforeEffect (SPLPEItem const* lpeitem) } } -void LPEBSpline::createAndApply(const char *name, SPDocument *doc, SPItem *item) + +void LPEBSpline::createAndApply(const char *name, SPDocument *doc, + SPItem *item) { if (!SP_IS_SHAPE(item)) { g_warning("LPE BSpline can only be applied to shapes (not groups)."); @@ -99,7 +106,8 @@ void LPEBSpline::createAndApply(const char *name, SPDocument *doc, SPItem *item) Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect"); repr->setAttribute("effect", name); - doc->getDefs()->getRepr()->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute + doc->getDefs()->getRepr() + ->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute const gchar *repr_id = repr->attribute("id"); Inkscape::GC::release(repr); @@ -111,13 +119,13 @@ void LPEBSpline::createAndApply(const char *name, SPDocument *doc, SPItem *item) void LPEBSpline::doEffect(SPCurve *curve) { + if (curve->get_segment_count() < 1){ return; } - + // Make copy of old path as it is changed during processing Geom::PathVector const original_pathv = curve->get_pathvector(); curve->reset(); - double radiusHelperNodes = 6.0; SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (desktop){ @@ -125,7 +133,6 @@ void LPEBSpline::doEffect(SPCurve *curve) SPNamedView *nv = sp_desktop_namedview(desktop); radiusHelperNodes = Inkscape::Util::Quantity::convert(radiusHelperNodes, "px", nv->doc_units->abbr); } - for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { if (path_it->empty()) @@ -133,7 +140,7 @@ void LPEBSpline::doEffect(SPCurve *curve) Geom::Path::const_iterator curve_it1 = path_it->begin(); Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); - Geom::Path::const_iterator curve_endit = path_it->end_default(); + Geom::Path::const_iterator curve_endit = path_it->end_default(); SPCurve *nCurve = new SPCurve(); Geom::Point previousNode(0, 0); Geom::Point node(0, 0); @@ -145,13 +152,29 @@ void LPEBSpline::doEffect(SPCurve *curve) Geom::D2<Geom::SBasis> SBasisHelper; Geom::CubicBezier const *cubic = NULL; if (path_it->closed()) { - const Geom::Curve &closingline = path_it->back_closed(); + // if the path is closed, maybe we have to stop a bit earlier because the + // closing line segment has zerolength. + const Geom::Curve &closingline = + path_it->back_closed(); // the closing line segment is always of type + // Geom::LineSegment. if (are_near(closingline.initialPoint(), closingline.finalPoint())) { + // closingline.isDegenerate() did not work, because it only checks for + // *exact* zero length, which goes wrong for relative coordinates and + // rounding errors... + // the closing line segment has zero-length. So stop before that one! curve_endit = path_it->end_open(); } } + //Si la curva está cerrada calculamos el punto donde + //deveria estar el nodo BSpline de cierre/inicio de la curva + //en posible caso de que se cierre con una linea recta creando un nodo + //BSPline nCurve->moveto(curve_it1->initialPoint()); + //Recorremos todos los segmentos menos el último while (curve_it1 != curve_endit) { + //previousPointAt3 = pointAt3; + //Calculamos los puntos que dividirían en tres segmentos iguales el path + //recto de entrada y de salida SPCurve *in = new SPCurve(); in->moveto(curve_it1->initialPoint()); in->lineto(curve_it1->finalPoint()); @@ -192,7 +215,6 @@ void LPEBSpline::doEffect(SPCurve *curve) out->reset(); delete out; } - Geom::Point startNode = path_it->begin()->initialPoint(); if (path_it->closed() && curve_it2 == curve_endit) { SPCurve *start = new SPCurve(); start->moveto(path_it->begin()->initialPoint()); @@ -225,9 +247,9 @@ void LPEBSpline::doEffect(SPCurve *curve) SBasisHelper = lineHelper->first_segment()->toSBasis(); lineHelper->reset(); delete lineHelper; - startNode = SBasisHelper.valueAt(0.5); - nCurve->curveto(pointAt1, pointAt2, startNode); - nCurve->move_endpoints(startNode, startNode); + node = SBasisHelper.valueAt(0.5); + nCurve->curveto(pointAt1, pointAt2, node); + nCurve->move_endpoints(node, node); } else if ( curve_it2 == curve_endit) { nCurve->curveto(pointAt1, pointAt2, curve_it1->finalPoint()); nCurve->move_endpoints(path_it->begin()->initialPoint(), curve_it1->finalPoint()); @@ -238,17 +260,26 @@ void LPEBSpline::doEffect(SPCurve *curve) SBasisHelper = lineHelper->first_segment()->toSBasis(); lineHelper->reset(); delete lineHelper; + //almacenamos el punto del anterior bucle -o el de cierre- que nos hara de + //principio de curva previousNode = node; + //Y este hará de final de curva node = SBasisHelper.valueAt(0.5); Geom::CubicBezier const *cubic2 = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1); if((cubic && are_near((*cubic)[0],(*cubic)[1])) || (cubic2 && are_near((*cubic2)[2],(*cubic2)[3]))) { node = curve_it1->finalPoint(); } nCurve->curveto(pointAt1, pointAt2, node); - if(!are_near(node,curve_it1->finalPoint())){ - drawHandle(node, radiusHelperNodes); - } } + if(!are_near(node,curve_it1->finalPoint()) && showHelper){ + drawHandle(node, radiusHelperNodes); + } + //La curva BSpline se forma calculando el centro del segmanto de unión + //de el punto situado en las 2/3 partes de el segmento de entrada + //con el punto situado en la posición 1/3 del segmento de salida + //Estos dos puntos ademas estan posicionados en el lugas correspondiente + //de los manejadores de la curva + //aumentamos los valores para el siguiente paso en el bucle ++curve_it1; ++curve_it2; } @@ -259,6 +290,8 @@ void LPEBSpline::doEffect(SPCurve *curve) curve->append(nCurve, false); nCurve->reset(); delete nCurve; + } + if(showHelper){ Geom::PathVector const pathv = curve->get_pathvector(); hp.push_back(pathv[0]); } @@ -293,32 +326,44 @@ Gtk::Widget *LPEBSpline::newWidget() while (it != param_vector.end()) { if ((*it)->widget_is_visible) { Parameter *param = *it; - Gtk::Widget *widg = Gtk::manage(param->param_newWidget()); + Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget()); if (param->param_key == "weight") { - Gtk::HBox * buttons = Gtk::manage(new Gtk::HBox(true, 0)); - - Gtk::Button *defaultWeight = Gtk::manage(new Gtk::Button(Glib::ustring(_("Default weight")))); - defaultWeight->signal_clicked().connect(sigc::bind<Gtk::Widget *>(sigc::mem_fun(*this, &LPEBSpline::toDefaultWeight), widg)); + Gtk::HBox * buttons = Gtk::manage(new Gtk::HBox(true,0)); + Gtk::Button *defaultWeight = + Gtk::manage(new Gtk::Button(Glib::ustring(_("Default weight")))); + defaultWeight->signal_clicked() + .connect(sigc::bind<Gtk::Widget *>(sigc::mem_fun(*this, &LPEBSpline::toDefaultWeight), widg)); buttons->pack_start(*defaultWeight, true, true, 2); - - Gtk::Button *makeCusp = Gtk::manage(new Gtk::Button(Glib::ustring(_("Make cusp")))); - makeCusp->signal_clicked().connect(sigc::bind<Gtk::Widget *>(sigc::mem_fun(*this, &LPEBSpline::toMakeCusp), widg)); + Gtk::Button *makeCusp = + Gtk::manage(new Gtk::Button(Glib::ustring(_("Make cusp")))); + makeCusp->signal_clicked() + .connect(sigc::bind<Gtk::Widget *>(sigc::mem_fun(*this, &LPEBSpline::toMakeCusp), widg)); buttons->pack_start(*makeCusp, true, true, 2); - vbox->pack_start(*buttons, true, true, 2); } if (param->param_key == "weight" || param->param_key == "steps") { - Inkscape::UI::Widget::Scalar *widgRegistered = Gtk::manage(dynamic_cast<Inkscape::UI::Widget::Scalar *>(widg)); - widgRegistered->signal_value_changed().connect(sigc::mem_fun(*this, &LPEBSpline::toWeight)); - + Inkscape::UI::Widget::Scalar *widgRegistered = + Gtk::manage(dynamic_cast<Inkscape::UI::Widget::Scalar *>(widg)); + widgRegistered->signal_value_changed() + .connect(sigc::mem_fun(*this, &LPEBSpline::toWeight)); + widg = dynamic_cast<Gtk::Widget *>(widgRegistered); if (widg) { Gtk::HBox * scalarParameter = dynamic_cast<Gtk::HBox *>(widg); - std::vector<Gtk::Widget *> childList = scalarParameter->get_children(); + std::vector< Gtk::Widget* > childList = scalarParameter->get_children(); Gtk::Entry* entryWidg = dynamic_cast<Gtk::Entry *>(childList[1]); entryWidg->set_width_chars(6); } } - + if (param->param_key == "onlySelected") { + Gtk::CheckButton *widgRegistered = + Gtk::manage(dynamic_cast<Gtk::CheckButton *>(widg)); + widg = dynamic_cast<Gtk::Widget *>(widgRegistered); + } + if (param->param_key == "ignoreCusp") { + Gtk::CheckButton *widgRegistered = + Gtk::manage(dynamic_cast<Gtk::CheckButton *>(widg)); + widg = dynamic_cast<Gtk::Widget *>(widgRegistered); + } Glib::ustring *tip = param->param_getTooltip(); if (widg) { vbox->pack_start(*widg, true, true, 2); @@ -333,7 +378,7 @@ Gtk::Widget *LPEBSpline::newWidget() ++it; } - return vbox; + return dynamic_cast<Gtk::Widget *>(vbox); } void LPEBSpline::toDefaultWeight(Gtk::Widget *widgWeight) @@ -379,7 +424,8 @@ void LPEBSpline::changeWeight(double weightValue) g_free(str); curve->unref(); desktop->clearWaitingCursor(); - DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_LPE, _("Modified the weight of a BSpline")); + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_LPE, + _("Modified the weight of the BSpline")); } bool LPEBSpline::nodeIsSelected(Geom::Point nodePoint) diff --git a/src/live_effects/lpe-bspline.h b/src/live_effects/lpe-bspline.h index e066015ad..169658b94 100644 --- a/src/live_effects/lpe-bspline.h +++ b/src/live_effects/lpe-bspline.h @@ -24,19 +24,16 @@ public: return SUPPRESS_FLASH; } virtual void doEffect(SPCurve *curve); - virtual void doBeforeEffect(SPLPEItem const* lpeitem); + virtual void doBeforeEffect (SPLPEItem const* lpeitem); void drawHandle(Geom::Point p, double radiusHelperNodes); void addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec); - - void doBSplineFromWidget(SPCurve *curve, double value); - bool nodeIsSelected(Geom::Point nodePoint); - + virtual void doBSplineFromWidget(SPCurve *curve, double value); + virtual bool nodeIsSelected(Geom::Point nodePoint); virtual Gtk::Widget *newWidget(); - - void changeWeight(double weightValue); - void toDefaultWeight(Gtk::Widget *widgWeight); - void toMakeCusp(Gtk::Widget *widgWeight); - void toWeight(); + virtual void changeWeight(double weightValue); + virtual void toDefaultWeight(Gtk::Widget *widgWeight); + virtual void toMakeCusp(Gtk::Widget *widgWeight); + virtual void toWeight(); // TODO make this private ScalarParam steps; @@ -45,6 +42,7 @@ private: std::vector<Geom::Point> points; BoolParam ignoreCusp; BoolParam onlySelected; + BoolParam showHelper; ScalarParam weight; Geom::PathVector hp; diff --git a/src/live_effects/lpe-envelope-perspective.cpp b/src/live_effects/lpe-perspective-envelope.cpp index 378ef5a92..6f6838c81 100644 --- a/src/live_effects/lpe-envelope-perspective.cpp +++ b/src/live_effects/lpe-perspective-envelope.cpp @@ -1,5 +1,5 @@ /** \file - * LPE <envelope-perspective> implementation + * LPE <perspective-envelope> implementation */ /* @@ -15,7 +15,7 @@ */ #include <gtkmm.h> -#include "live_effects/lpe-envelope-perspective.h" +#include "live_effects/lpe-perspective-envelope.h" #include "helper/geom.h" #include "display/curve.h" #include "svg/svg.h" @@ -29,21 +29,21 @@ namespace Inkscape { namespace LivePathEffect { enum DeformationType { - DEFORMATION_ENVELOPE, - DEFORMATION_PERSPECTIVE + DEFORMATION_PERSPECTIVE, + DEFORMATION_ENVELOPE }; static const Util::EnumData<unsigned> DeformationTypeData[] = { - {DEFORMATION_ENVELOPE , N_("Envelope deformation"), "Envelope deformation"}, - {DEFORMATION_PERSPECTIVE , N_("Perspective"), "Perspective"} + {DEFORMATION_PERSPECTIVE , N_("Perspective"), "Perspective"}, + {DEFORMATION_ENVELOPE , N_("Envelope deformation"), "Envelope deformation"} }; static const Util::EnumDataConverter<unsigned> DeformationTypeConverter(DeformationTypeData, sizeof(DeformationTypeData)/sizeof(*DeformationTypeData)); -LPEEnvelopePerspective::LPEEnvelopePerspective(LivePathEffectObject *lpeobject) : +LPEPerspectiveEnvelope::LPEPerspectiveEnvelope(LivePathEffectObject *lpeobject) : Effect(lpeobject), // initialise your parameters here: - deform_type(_("Type"), _("Select the type of deformation"), "deform_type", DeformationTypeConverter, &wr, this, DEFORMATION_ENVELOPE), + deform_type(_("Type"), _("Select the type of deformation"), "deform_type", DeformationTypeConverter, &wr, this, DEFORMATION_PERSPECTIVE), Up_Left_Point(_("Top Left"), _("Top Left - Ctrl+Alt+Click to reset"), "Up_Left_Point", &wr, this), Up_Right_Point(_("Top Right"), _("Top Right - Ctrl+Alt+Click to reset"), "Up_Right_Point", &wr, this), Down_Left_Point(_("Down Left"), _("Down Left - Ctrl+Alt+Click to reset"), "Down_Left_Point", &wr, this), @@ -57,11 +57,11 @@ LPEEnvelopePerspective::LPEEnvelopePerspective(LivePathEffectObject *lpeobject) registerParameter( dynamic_cast<Parameter *>(&Down_Right_Point) ); } -LPEEnvelopePerspective::~LPEEnvelopePerspective() +LPEPerspectiveEnvelope::~LPEPerspectiveEnvelope() { } -void LPEEnvelopePerspective::doEffect(SPCurve *curve) { +void LPEPerspectiveEnvelope::doEffect(SPCurve *curve) { using Geom::X; using Geom::Y; double projmatrix[3][3]; @@ -135,32 +135,23 @@ void LPEEnvelopePerspective::doEffect(SPCurve *curve) { continue; //Itreadores SPCurve *nCurve = new SPCurve(); - Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve - Geom::Path::const_iterator curve_it2 = - ++(path_it->begin()); // outgoing curve - Geom::Path::const_iterator curve_endit = - path_it->end_default(); // this determines when the loop has to stop + Geom::Path::const_iterator curve_it1 = path_it->begin(); + Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); + Geom::Path::const_iterator curve_endit = path_it->end_default(); if (path_it->closed()) { - // if the path is closed, maybe we have to stop a bit earlier because the - // closing line segment has zerolength. - const Geom::Curve &closingline = - path_it->back_closed(); // the closing line segment is always of type - // Geom::LineSegment. - if (are_near(closingline.initialPoint(), closingline.finalPoint())) { - // closingline.isDegenerate() did not work, because it only checks for - // *exact* zero length, which goes wrong for relative coordinates and - // rounding errors... - // the closing line segment has zero-length. So stop before that one! - curve_endit = path_it->end_open(); - } + const Geom::Curve &closingline = + path_it->back_closed(); + if (are_near(closingline.initialPoint(), closingline.finalPoint())) { + curve_endit = path_it->end_open(); + } } if(deform_type == DEFORMATION_PERSPECTIVE){ nCurve->moveto(project_point(curve_it1->initialPoint(),projmatrix)); }else{ nCurve->moveto(project_point(curve_it1->initialPoint())); } - while (curve_it2 != curve_endit) { + while (curve_it1 != curve_endit) { cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1); if (cubic) { pointAt1 = (*cubic)[1]; @@ -181,34 +172,13 @@ void LPEEnvelopePerspective::doEffect(SPCurve *curve) { } nCurve->curveto(pointAt1, pointAt2, pointAt3); ++curve_it1; - ++curve_it2; - } - cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1); - if (cubic) { - pointAt1 = (*cubic)[1]; - pointAt2 = (*cubic)[2]; - } else { - pointAt1 = curve_it1->initialPoint(); - pointAt2 = curve_it1->finalPoint(); - } - pointAt3 = curve_it1->finalPoint(); - if(deform_type == DEFORMATION_PERSPECTIVE){ - pointAt1 = project_point(pointAt1,projmatrix); - pointAt2 = project_point(pointAt2,projmatrix); - pointAt3 = project_point(pointAt3,projmatrix); - }else{ - pointAt1 = project_point(pointAt1); - pointAt2 = project_point(pointAt2); - pointAt3 = project_point(pointAt3); - } - nCurve->curveto(pointAt1, pointAt2, pointAt3); - if(deform_type == DEFORMATION_PERSPECTIVE){ - nCurve->move_endpoints(project_point(path_it->begin()->initialPoint(),projmatrix), pointAt3); - }else{ - nCurve->move_endpoints(project_point(path_it->begin()->initialPoint()), pointAt3); + if(curve_it2 != curve_endit) { + ++curve_it2; + } } //y cerramos la curva if (path_it->closed()) { + nCurve->move_endpoints(pointAt3, pointAt3); nCurve->closepath_current(); } curve->append(nCurve, false); @@ -218,11 +188,13 @@ void LPEEnvelopePerspective::doEffect(SPCurve *curve) { } Geom::Point -LPEEnvelopePerspective::project_point(Geom::Point p){ +LPEPerspectiveEnvelope::project_point(Geom::Point p){ double width = boundingbox_X.extent(); double height = boundingbox_Y.extent(); - Geom::Coord xratio = abs(Geom::Point(boundingbox_X.min(), boundingbox_Y.max())[X]-p[X])/width; - Geom::Coord yratio = abs(Geom::Point(boundingbox_X.min(), boundingbox_Y.max())[Y]-p[Y])/height; + double delta_x = boundingbox_X.min() - p[X]; + double delta_y = boundingbox_Y.max() - p[Y]; + Geom::Coord xratio = (delta_x * sgn(delta_x)) / width; + Geom::Coord yratio = (delta_y * sgn(delta_y)) / height; Geom::Line* horiz = new Geom::Line(); Geom::Line* vert = new Geom::Line(); vert->setPoints (pointAtRatio(yratio,Down_Left_Point,Up_Left_Point),pointAtRatio(yratio,Down_Right_Point,Up_Right_Point)); @@ -237,7 +209,7 @@ LPEEnvelopePerspective::project_point(Geom::Point p){ } Geom::Point -LPEEnvelopePerspective::project_point(Geom::Point p, double m[][3]){ +LPEPerspectiveEnvelope::project_point(Geom::Point p, double m[][3]){ Geom::Coord x = p[0]; Geom::Coord y = p[1]; return Geom::Point( @@ -246,7 +218,7 @@ LPEEnvelopePerspective::project_point(Geom::Point p, double m[][3]){ } Geom::Point -LPEEnvelopePerspective::pointAtRatio(Geom::Coord ratio,Geom::Point A, Geom::Point B){ +LPEPerspectiveEnvelope::pointAtRatio(Geom::Coord ratio,Geom::Point A, Geom::Point B){ Geom::Coord x = A[X] + (ratio * (B[X]-A[X])); Geom::Coord y = A[Y]+ (ratio * (B[Y]-A[Y])); return Point(x, y); @@ -254,7 +226,7 @@ LPEEnvelopePerspective::pointAtRatio(Geom::Coord ratio,Geom::Point A, Geom::Poin Gtk::Widget * -LPEEnvelopePerspective::newWidget() +LPEPerspectiveEnvelope::newWidget() { // use manage here, because after deletion of Effect object, others might still be pointing to this widget. Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox(Effect::newWidget()) ); @@ -285,20 +257,12 @@ LPEEnvelopePerspective::newWidget() Gtk::Label* handles = Gtk::manage(new Gtk::Label(Glib::ustring(_("Handles:")),Gtk::ALIGN_START)); vbox->pack_start(*handles, false, false, 2); hboxUpHandles->pack_start(*widg, true, true, 2); -#if WITH_GTKMM_3_0 - hboxUpHandles->pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_VERTICAL)), Gtk::PACK_EXPAND_WIDGET); -#else hboxUpHandles->pack_start(*Gtk::manage(new Gtk::VSeparator()), Gtk::PACK_EXPAND_WIDGET); -#endif }else if(param->param_key == "Up_Right_Point"){ hboxUpHandles->pack_start(*widg, true, true, 2); }else if(param->param_key == "Down_Left_Point"){ hboxDownHandles->pack_start(*widg, true, true, 2); -#if WITH_GTKMM_3_0 - hboxDownHandles->pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_VERTICAL)), Gtk::PACK_EXPAND_WIDGET); -#else hboxDownHandles->pack_start(*Gtk::manage(new Gtk::VSeparator()), Gtk::PACK_EXPAND_WIDGET); -#endif }else{ hboxDownHandles->pack_start(*widg, true, true, 2); } @@ -327,18 +291,13 @@ LPEEnvelopePerspective::newWidget() } vbox->pack_start(*hboxUpHandles,true, true, 2); Gtk::HBox * hboxMiddle = Gtk::manage(new Gtk::HBox(true,2)); -#if WITH_GTKMM_3_0 - hboxMiddle->pack_start(*Gtk::manage(new Gtk::Separator()), Gtk::PACK_EXPAND_WIDGET); - hboxMiddle->pack_start(*Gtk::manage(new Gtk::Separator()), Gtk::PACK_EXPAND_WIDGET); -#else hboxMiddle->pack_start(*Gtk::manage(new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET); hboxMiddle->pack_start(*Gtk::manage(new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET); -#endif vbox->pack_start(*hboxMiddle, false, true, 2); vbox->pack_start(*hboxDownHandles, true, true, 2); Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false,0)); Gtk::Button* resetButton = Gtk::manage(new Gtk::Button(Gtk::Stock::CLEAR)); - resetButton->signal_clicked().connect(sigc::mem_fun (*this,&LPEEnvelopePerspective::resetGrid)); + resetButton->signal_clicked().connect(sigc::mem_fun (*this,&LPEPerspectiveEnvelope::resetGrid)); resetButton->set_size_request(140,45); vbox->pack_start(*hbox, true,true,2); hbox->pack_start(*resetButton, false, false,2); @@ -346,17 +305,14 @@ LPEEnvelopePerspective::newWidget() } void -LPEEnvelopePerspective::doBeforeEffect (SPLPEItem const* lpeitem) +LPEPerspectiveEnvelope::doBeforeEffect (SPLPEItem const* lpeitem) { original_bbox(lpeitem); setDefaults(); - SPLPEItem * item = const_cast<SPLPEItem*>(lpeitem); - item->apply_to_clippath(item); - item->apply_to_mask(item); } void -LPEEnvelopePerspective::setDefaults() +LPEPerspectiveEnvelope::setDefaults() { Geom::Point Up_Left(boundingbox_X.min(), boundingbox_Y.min()); Geom::Point Up_Right(boundingbox_X.max(), boundingbox_Y.min()); @@ -370,7 +326,7 @@ LPEEnvelopePerspective::setDefaults() } void -LPEEnvelopePerspective::resetGrid() +LPEPerspectiveEnvelope::resetGrid() { Up_Left_Point.param_set_and_write_default(); Up_Right_Point.param_set_and_write_default(); @@ -384,7 +340,7 @@ LPEEnvelopePerspective::resetGrid() } void -LPEEnvelopePerspective::resetDefaults(SPItem const* item) +LPEPerspectiveEnvelope::resetDefaults(SPItem const* item) { Effect::resetDefaults(item); original_bbox(SP_LPE_ITEM(item)); @@ -393,19 +349,7 @@ LPEEnvelopePerspective::resetDefaults(SPItem const* item) } void -LPEEnvelopePerspective::calculateCurve(Geom::Point a,Geom::Point b, SPCurve* c, bool horizontal, bool move) -{ - using Geom::X; - using Geom::Y; - if(move) c->moveto(a); - Geom::Point cubic1 = a + (1./3)* (b - a); - Geom::Point cubic2 = b + (1./3)* (a - b); - if(horizontal) c->curveto(Geom::Point(cubic1[X],a[Y]),Geom::Point(cubic2[X],b[Y]),b); - else c->curveto(Geom::Point(a[X],cubic1[Y]),Geom::Point(b[X],cubic2[Y]),b); -} - -void -LPEEnvelopePerspective::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec) +LPEPerspectiveEnvelope::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec) { hp_vec.clear(); @@ -437,4 +381,4 @@ LPEEnvelopePerspective::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::v fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: file_type=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/lpe-envelope-perspective.h b/src/live_effects/lpe-perspective-envelope.h index 0de9a0e35..2f253882e 100644 --- a/src/live_effects/lpe-envelope-perspective.h +++ b/src/live_effects/lpe-perspective-envelope.h @@ -1,8 +1,8 @@ -#ifndef INKSCAPE_LPE_ENVELOPE_PERSPECTIVE_H -#define INKSCAPE_LPE_ENVELOPE_PERSPECTIVE_H +#ifndef INKSCAPE_LPE_PERSPECTIVE_ENVELOPE_H +#define INKSCAPE_LPE_PERSPECTIVE_ENVELOPE_H /** \file - * LPE <envelope-perspective> implementation , see lpe-envelope-perspective.cpp. + * LPE <perspective-envelope> implementation , see lpe-perspective-envelope.cpp. */ /* @@ -25,11 +25,12 @@ namespace Inkscape { namespace LivePathEffect { -class LPEEnvelopePerspective : public Effect, GroupBBoxEffect { +class LPEPerspectiveEnvelope : public Effect, GroupBBoxEffect { public: - LPEEnvelopePerspective(LivePathEffectObject *lpeobject); - virtual ~LPEEnvelopePerspective(); + LPEPerspectiveEnvelope(LivePathEffectObject *lpeobject); + + virtual ~LPEPerspectiveEnvelope(); virtual void doEffect(SPCurve *curve); @@ -37,7 +38,7 @@ public: virtual Geom::Point project_point(Geom::Point p, double m[][3]); - Geom::Point pointAtRatio(Geom::Coord ratio,Geom::Point A, Geom::Point B); + virtual Geom::Point pointAtRatio(Geom::Coord ratio,Geom::Point A, Geom::Point B); virtual void resetDefaults(SPItem const* item); @@ -45,17 +46,10 @@ public: virtual Gtk::Widget * newWidget(); - virtual void calculateCurve(Geom::Point a,Geom::Point b, SPCurve *c, bool horizontal, bool move); - virtual void setDefaults(); virtual void resetGrid(); - //virtual void original_bbox(SPLPEItem const* lpeitem, bool absolute = false); - - //virtual void addCanvasIndicators(SPLPEItem const*/*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/); - - //virtual std::vector<Geom::PathVector> getHelperPaths(SPLPEItem const* lpeitem); protected: void addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec); private: @@ -66,8 +60,8 @@ private: PointReseteableParam Down_Left_Point; PointReseteableParam Down_Right_Point; - LPEEnvelopePerspective(const LPEEnvelopePerspective&); - LPEEnvelopePerspective& operator=(const LPEEnvelopePerspective&); + LPEPerspectiveEnvelope(const LPEPerspectiveEnvelope&); + LPEPerspectiveEnvelope& operator=(const LPEPerspectiveEnvelope&); }; } //namespace LivePathEffect diff --git a/src/live_effects/lpe-vonkoch.cpp b/src/live_effects/lpe-vonkoch.cpp index 7c1913076..b52816c87 100644 --- a/src/live_effects/lpe-vonkoch.cpp +++ b/src/live_effects/lpe-vonkoch.cpp @@ -132,8 +132,8 @@ LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in) int path_in_complexity = 0; for (unsigned k = 0; k < path_in.size(); k++){ path_in_complexity+=path_in[k].size(); - } - double complexity = std::pow(transforms.size(),nbgenerations)*path_in_complexity; + } + double complexity = std::pow(transforms.size(), nbgenerations) * path_in_complexity; if (drawall.get_value()){ int k = transforms.size(); if(k>1){ @@ -141,8 +141,6 @@ LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in) }else{ complexity = nbgenerations*k*path_in_complexity; } - }else{ - complexity = std::pow(transforms.size(),nbgenerations)*path_in_complexity; } if (complexity > double(maxComplexity)){ g_warning("VonKoch lpe's output too complex. Effect bypassed."); diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 9ff6df3bf..2b29814b0 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -289,7 +289,7 @@ void Inkscape::ObjectSnapper::_snapNodes(IntermSnapResults &isr, for (std::vector<SnapCandidatePoint>::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); ++k) { if (_allowSourceToSnapToTarget(p.getSourceType(), (*k).getTargetType(), strict_snapping)) { Geom::Point target_pt = (*k).getPoint(); - Geom::Coord dist = Geom::infinity(); + Geom::Coord dist = Geom::L2(target_pt - p.getPoint()); // Default: free (unconstrained) snapping if (!c.isUndefined()) { // We're snapping to nodes along a constraint only, so find out if this node // is at the constraint, while allowing for a small margin @@ -299,9 +299,6 @@ void Inkscape::ObjectSnapper::_snapNodes(IntermSnapResults &isr, continue; } dist = Geom::L2(target_pt - p_proj_on_constraint); - } else { - // Free (unconstrained) snapping - dist = Geom::L2(target_pt - p.getPoint()); } if (dist < getSnapperTolerance() && dist < s.getSnapDistance()) { diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index a72601276..fe241381f 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -79,6 +79,7 @@ sp_selected_path_combine(SPDesktop *desktop) items = g_slist_sort(items, (GCompareFunc) sp_item_repr_compare_position); items = g_slist_reverse(items); + assert(items); // cannot be NULL because of list length check at top of function // remember the position, id, transform and style of the topmost path, they will be assigned to the combined one gint position = 0; diff --git a/src/path-prefix.h b/src/path-prefix.h index 6ef0ccbe9..7042d5124 100644 --- a/src/path-prefix.h +++ b/src/path-prefix.h @@ -66,23 +66,23 @@ # define CREATE_PALETTESDIR WIN32_DATADIR("create\\swatches") # define CREATE_PATTERNSDIR WIN32_DATADIR("create\\patterns\\vector") # elif defined ENABLE_OSX_APP_LOCATIONS -# define INKSCAPE_APPICONDIR "Contents/Resources/pixmaps" -# define INKSCAPE_ATTRRELDIR "Contents/Resources/attributes" -# define INKSCAPE_BINDDIR "Contents/Resources/bind" -# define INKSCAPE_EXAMPLESDIR "Contents/Resources/examples" -# define INKSCAPE_EXTENSIONDIR "Contents/Resources/extensions" -# define INKSCAPE_FILTERDIR "Contents/Resources/filters" -# define INKSCAPE_GRADIENTSDIR "Contents/Resources/gradients" -# define INKSCAPE_KEYSDIR "Contents/Resources/keys" -# define INKSCAPE_PIXMAPDIR "Contents/Resources/icons" -# define INKSCAPE_MARKERSDIR "Contents/Resources/markers" -# define INKSCAPE_PALETTESDIR "Contents/Resources/palettes" -# define INKSCAPE_PATTERNSDIR "Contents/Resources/patterns" -# define INKSCAPE_SCREENSDIR "Contents/Resources/screens" -# define INKSCAPE_SYMBOLSDIR "Contents/Resources/symbols" -# define INKSCAPE_TUTORIALSDIR "Contents/Resources/tutorials" -# define INKSCAPE_TEMPLATESDIR "Contents/Resources/templates" -# define INKSCAPE_UIDIR "Contents/Resources/ui" +# define INKSCAPE_APPICONDIR "Contents/Resources/share/pixmaps" +# define INKSCAPE_ATTRRELDIR "Contents/Resources/share/inkscape/attributes" +# define INKSCAPE_BINDDIR "Contents/Resources/share/inkscape/bind" +# define INKSCAPE_EXAMPLESDIR "Contents/Resources/share/inkscape/examples" +# define INKSCAPE_EXTENSIONDIR "Contents/Resources/share/inkscape/extensions" +# define INKSCAPE_FILTERDIR "Contents/Resources/share/inkscape/filters" +# define INKSCAPE_GRADIENTSDIR "Contents/Resources/share/inkscape/gradients" +# define INKSCAPE_KEYSDIR "Contents/Resources/share/inkscape/keys" +# define INKSCAPE_PIXMAPDIR "Contents/Resources/share/inkscape/icons" +# define INKSCAPE_MARKERSDIR "Contents/Resources/share/inkscape/markers" +# define INKSCAPE_PALETTESDIR "Contents/Resources/share/inkscape/palettes" +# define INKSCAPE_PATTERNSDIR "Contents/Resources/share/inkscape/patterns" +# define INKSCAPE_SCREENSDIR "Contents/Resources/share/inkscape/screens" +# define INKSCAPE_SYMBOLSDIR "Contents/Resources/share/inkscape/symbols" +# define INKSCAPE_TUTORIALSDIR "Contents/Resources/share/inkscape/tutorials" +# define INKSCAPE_TEMPLATESDIR "Contents/Resources/share/inkscape/templates" +# define INKSCAPE_UIDIR "Contents/Resources/share/inkscape/ui" //CREATE V0.1 support # define CREATE_GRADIENTSDIR "/Library/Application Support/create/gradients/gimp" # define CREATE_PALETTESDIR "/Library/Application Support/create/swatches" diff --git a/src/sp-hatch-path.cpp b/src/sp-hatch-path.cpp new file mode 100644 index 000000000..f7138fac2 --- /dev/null +++ b/src/sp-hatch-path.cpp @@ -0,0 +1,331 @@ +/** @file + * SVG <hatchPath> implementation + *//* + * Author: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cstring> +#include <string> +#include <2geom/path.h> +#include <2geom/transforms.h> + +#include "svg/svg.h" +#include "display/cairo-utils.h" +#include "display/curve.h" +#include "display/drawing-context.h" +#include "display/drawing-surface.h" +#include "display/drawing.h" +#include "display/drawing-shape.h" +#include "helper/geom.h" +#include "attributes.h" +#include "document-private.h" +#include "uri.h" +#include "style.h" +#include "sp-hatch-path.h" +#include "svg/css-ostringstream.h" +#include "xml/repr.h" + +#include "sp-factory.h" + +namespace { +SPObject* createHatchPath() { + return new SPHatchPath(); +} + +bool hatchRegistered = SPFactory::instance().registerObject("svg:hatchPath", createHatchPath); +} + +SPHatchPath::SPHatchPath() + : _curve(NULL) + , _continuous(false) +{ + offset.unset(); +} + +SPHatchPath::~SPHatchPath() { +} + +void SPHatchPath::setCurve(SPCurve *new_curve, bool owner) { + if (_curve) { + _curve = _curve->unref(); + } + + if (new_curve) { + if (owner) { + _curve = new_curve->ref(); + } else { + _curve = new_curve->copy(); + } + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void SPHatchPath::build(SPDocument* doc, Inkscape::XML::Node* repr) { + SPObject::build(doc, repr); + + this->readAttr("d"); + this->readAttr("offset"); + this->readAttr( "style" ); + + style->fill.setNone(); +} + +void SPHatchPath::release() { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + delete iter->arenaitem; + iter->arenaitem = NULL; + } + + SPObject::release(); +} + +void SPHatchPath::set(unsigned int key, const gchar* value) { + switch (key) { + case SP_ATTR_D: + if (value) { + Geom::PathVector pv; + _readHatchPathVector(value, pv, _continuous); + SPCurve *curve = new SPCurve(pv); + + if (curve) { + this->setCurve(curve, true); + curve->unref(); + } + } else { + this->setCurve(NULL, true); + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_OFFSET: + offset.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + default: + if (SP_ATTRIBUTE_IS_CSS(key)) { + sp_style_read_from_object(this->style, this); + requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } else { + SPObject::set(key, value); + } + break; + } +} + + +void SPHatchPath::update(SPCtx* ctx, unsigned int flags) { + + if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { + flags &= ~SP_OBJECT_USER_MODIFIED_FLAG_B; + } + + if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { + if (this->style->stroke_width.unit == SP_CSS_UNIT_PERCENT) { + //TODO: Check specification + + SPItemCtx *ictx = (SPItemCtx *) ctx; + double const aw = 1.0 / ictx->i2vp.descrim(); + this->style->stroke_width.computed = this->style->stroke_width.value * aw; + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + iter->arenaitem->setStyle(this->style); + } + } + } + + if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + _updateView(*iter); + } + } +} + +bool SPHatchPath::isValid() const { + if (_curve && (_repeatLength() <= 0)) { + return false; + } + return true; +} + +Inkscape::DrawingItem *SPHatchPath::show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptInterval extents) { + Inkscape::DrawingShape *s = new Inkscape::DrawingShape(drawing); + _display.push_front(View(s, key)); + _display.front().extents = extents; + + _updateView(_display.front()); + + return s; +} + +void SPHatchPath::hide(unsigned int key) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + delete iter->arenaitem; + _display.erase(iter); + return; + } + } + + g_assert_not_reached(); +} + +void SPHatchPath::setStripExtents(unsigned int key, Geom::OptInterval const &extents) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + iter->extents = extents; + break; + } + } +} + +Geom::Interval SPHatchPath::bounds() const { + Geom::OptRect bbox; + Geom::Interval result; + + Geom::Affine transform = Geom::Translate(offset.computed, 0); + if (!this->_curve) { + SPCurve test_curve; + test_curve.moveto(Geom::Point(0, 0)); + test_curve.moveto(Geom::Point(0, 1)); + bbox = bounds_exact_transformed(test_curve.get_pathvector(), transform); + } else { + bbox = bounds_exact_transformed(this->_curve->get_pathvector(), transform); + } + + gdouble stroke_width = style->stroke_width.computed; + result.setMin(bbox->left() - stroke_width / 2); + result.setMax(bbox->right() + stroke_width / 2); + return result; +} + +SPCurve *SPHatchPath::calculateRenderCurve(unsigned key) const { + for (ConstViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + return _calculateRenderCurve(*iter); + } + } + g_assert_not_reached(); + return NULL; +} + +gdouble SPHatchPath::_repeatLength() const { + if (!_curve) { + return 0; + } + + if (!_curve->last_point()) { + return 0; + } + + return _curve->last_point()->y(); +} + +void SPHatchPath::_updateView(View &view) { + SPCurve *calculated_curve = _calculateRenderCurve(view); + + Geom::Affine offset_transform = Geom::Translate(offset.computed, 0); + view.arenaitem->setTransform(offset_transform); + style->fill.setNone(); + view.arenaitem->setStyle(this->style); + view.arenaitem->setPath(calculated_curve); + + calculated_curve->unref(); +} + +SPCurve *SPHatchPath::_calculateRenderCurve(View const &view) const { + SPCurve *calculated_curve = new SPCurve; + + if (!view.extents) { + return calculated_curve; + } + + if (!_curve) { + calculated_curve->moveto(0, view.extents->min()); + calculated_curve->lineto(0, view.extents->max()); + //TODO: if hatch has a dasharray defined, adjust line ends + } else { + gdouble repeatLength = _repeatLength(); + if (repeatLength > 0) { + gdouble initial_y = floor(view.extents->min() / repeatLength) * repeatLength; + int segment_cnt = ceil((view.extents->extent()) / repeatLength) + 1; + + SPCurve *segment =_curve->copy(); + segment->transform(Geom::Translate(0, initial_y)); + + Geom::Affine step_transform = Geom::Translate(0, repeatLength); + for (int i = 0; i < segment_cnt; i++) { + if (_continuous) { + calculated_curve->append_continuous(segment, 0.0625); + } else { + calculated_curve->append(segment, false); + } + segment->transform(step_transform); + } + + segment->unref(); + } + } + return calculated_curve; +} + + +void SPHatchPath::_readHatchPathVector(char const *str, Geom::PathVector &pathv, bool &continous_join) { + if (!str) { + return; + } + + pathv = sp_svg_read_pathv(str); + + if (!pathv.empty()) { + continous_join = false; + } else { + Glib::ustring str2 = Glib::ustring::compose("M0,0 %1", str); + pathv = sp_svg_read_pathv(str2.c_str()); + if (pathv.empty()) { + return; + } + + gdouble last_point_x = pathv.back().finalPoint().x(); + Inkscape::CSSOStringStream stream; + stream << last_point_x; + Glib::ustring str3 = Glib::ustring::compose("M%1,0 %2", stream.str(), str); + Geom::PathVector pathv3 = sp_svg_read_pathv(str3.c_str()); + + //Path can be composed of relative commands only. In this case final point + //coordinates would depend on first point position. If this happens, fall + //back to using 0,0 as first path point + if (pathv3.back().finalPoint().y() == pathv.back().finalPoint().y()) { + pathv = pathv3; + } + continous_join = true; + } +} + +SPHatchPath::View::View(Inkscape::DrawingShape *arenaitem, int key) + : arenaitem(arenaitem), key(key) +{ +} + + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/sp-hatch-path.h b/src/sp-hatch-path.h new file mode 100644 index 000000000..57b3a8237 --- /dev/null +++ b/src/sp-hatch-path.h @@ -0,0 +1,91 @@ +/** @file + * SVG <hatchPath> implementation + *//* + * Author: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_SP_HATCH_PATH_H +#define SEEN_SP_HATCH_PATH_H + +#include <list> +#include <stddef.h> +#include <glibmm/ustring.h> +#include <sigc++/connection.h> + +#include "svg/svg-length.h" + +namespace Inkscape { + +class Drawing; +class DrawingShape; + +} + +#define SP_HATCH_PATH(obj) (dynamic_cast<SPHatchPath*>((SPObject*)obj)) +#define SP_IS_HATCH_PATH(obj) (dynamic_cast<const SPHatchPath*>((SPObject*)obj) != NULL) + +class SPHatchPath : public SPObject { +public: + SPHatchPath(); + virtual ~SPHatchPath(); + + SVGLength offset; + + void setCurve(SPCurve *curve, bool owner); + + bool isValid() const; + + Inkscape::DrawingItem *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptInterval extents); + void hide(unsigned int key); + + void setStripExtents(unsigned int key, Geom::OptInterval const &extents); + Geom::Interval bounds() const; + + SPCurve *calculateRenderCurve(unsigned key) const; + +protected: + virtual void build(SPDocument* doc, Inkscape::XML::Node* repr); + virtual void release(); + virtual void set(unsigned int key, const gchar* value); + virtual void update(SPCtx* ctx, unsigned int flags); + +private: + struct View { + View(Inkscape::DrawingShape *arenaitem, int key); + //Do not delete arenaitem in destructor. + + Inkscape::DrawingShape *arenaitem; + Geom::OptInterval extents; + unsigned int key; + }; + typedef std::list<SPHatchPath::View>::iterator ViewIterator; + typedef std::list<SPHatchPath::View>::const_iterator ConstViewIterator; + std::list<View> _display; + + gdouble _repeatLength() const; + void _updateView(View &view); + SPCurve *_calculateRenderCurve(View const &view) const; + + void _readHatchPathVector(char const *str, Geom::PathVector &pathv, bool &continous_join); + + SPCurve *_curve; + bool _continuous; +}; + +#endif // SEEN_SP_HATCH_PATH_H + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/sp-hatch.cpp b/src/sp-hatch.cpp new file mode 100644 index 000000000..b007fc846 --- /dev/null +++ b/src/sp-hatch.cpp @@ -0,0 +1,679 @@ +/** @file + * SVG <hatch> implementation + *//* + * Author: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cstring> +#include <string> +#include <2geom/transforms.h> +#include <sigc++/functors/mem_fun.h> + +#include "svg/svg.h" +#include "display/cairo-utils.h" +#include "display/drawing-context.h" +#include "display/drawing-surface.h" +#include "display/drawing.h" +#include "display/drawing-pattern.h" +#include "attributes.h" +#include "document-private.h" +#include "uri.h" +#include "style.h" +#include "sp-hatch.h" +#include "sp-hatch-path.h" +#include "xml/repr.h" + +#include "sp-factory.h" + +namespace { +SPObject* createHatch() { + return new SPHatch(); +} + +bool hatchRegistered = SPFactory::instance().registerObject("svg:hatch", createHatch); +} + +SPHatch::SPHatch() + : SPPaintServer() +{ + this->ref = new SPHatchReference(this); + this->ref->changedSignal().connect(sigc::mem_fun(this, &SPHatch::_onRefChanged)); + + this->_hatchUnits = UNITS_OBJECTBOUNDINGBOX; + this->_hatchUnits_set = false; + + this->_hatchContentUnits = UNITS_USERSPACEONUSE; + this->_hatchContentUnits_set = false; + + this->_hatchTransform = Geom::identity(); + this->_hatchTransform_set = false; + + this->_x.unset(); + this->_y.unset(); + this->_pitch.unset(); + this->_rotate.unset(); +} + +SPHatch::~SPHatch() { +} + +void SPHatch::build(SPDocument* doc, Inkscape::XML::Node* repr) { + SPPaintServer::build(doc, repr); + + this->readAttr("hatchUnits"); + this->readAttr("hatchContentUnits"); + this->readAttr("hatchTransform"); + this->readAttr("x"); + this->readAttr("y"); + this->readAttr("pitch"); + this->readAttr("rotate"); + this->readAttr("xlink:href"); + this->readAttr( "style" ); + + /* Register ourselves */ + doc->addResource("hatch", this); +} + +void SPHatch::release() { + if (this->document) { + // Unregister ourselves + this->document->removeResource("hatch", this); + } + + std::vector<SPHatchPath *> children; + hatchPaths(children); + for (ViewIterator view_iter = _display.begin(); view_iter != _display.end(); view_iter++) { + for (ChildIterator child_iter = children.begin(); child_iter != children.end(); + child_iter++) { + SPHatchPath *child = *child_iter; + child->hide(view_iter->key); + } + delete view_iter->arenaitem; + view_iter->arenaitem = NULL; + } + + if (this->ref) { + this->_modified_connection.disconnect(); + this->ref->detach(); + delete this->ref; + this->ref = NULL; + } + + SPPaintServer::release(); +} + +void SPHatch::child_added(Inkscape::XML::Node* child, Inkscape::XML::Node* ref) { + SPObject::child_added(child, ref); + + SPHatchPath *path_child = SP_HATCH_PATH(this->document->getObjectByRepr(child)); + + if (path_child) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + Geom::OptInterval extents = _calculateStripExtents(iter->bbox); + Inkscape::DrawingItem *ac = path_child->show(iter->arenaitem->drawing(), iter->key, extents); + + path_child->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + if (ac) { + iter->arenaitem->prependChild(ac); + } + } + } + //FIXME: notify all hatches that refer to this child set +} + +void SPHatch::set(unsigned int key, const gchar* value) { + switch (key) { + case SP_ATTR_HATCHUNITS: + if (value) { + if (!strcmp(value, "userSpaceOnUse")) { + this->_hatchUnits = UNITS_USERSPACEONUSE; + } else { + this->_hatchUnits = UNITS_OBJECTBOUNDINGBOX; + } + + this->_hatchUnits_set = true; + } else { + this->_hatchUnits_set = false; + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_HATCHCONTENTUNITS: + if (value) { + if (!strcmp(value, "userSpaceOnUse")) { + this->_hatchContentUnits = UNITS_USERSPACEONUSE; + } else { + this->_hatchContentUnits = UNITS_OBJECTBOUNDINGBOX; + } + + this->_hatchContentUnits_set = true; + } else { + this->_hatchContentUnits_set = false; + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_HATCHTRANSFORM: { + Geom::Affine t; + + if (value && sp_svg_transform_read(value, &t)) { + this->_hatchTransform = t; + this->_hatchTransform_set = true; + } else { + this->_hatchTransform = Geom::identity(); + this->_hatchTransform_set = false; + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + } + case SP_ATTR_X: + this->_x.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_Y: + this->_y.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_PITCH: + this->_pitch.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_ROTATE: + this->_rotate.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_XLINK_HREF: + if (value && this->href == value) { + /* Href unchanged, do nothing. */ + } else { + this->href.clear(); + + if (value) { + // First, set the href field; it's only used in the "unchanged" check above. + this->href = value; + // Now do the attaching, which emits the changed signal. + if (value) { + try { + this->ref->attach(Inkscape::URI(value)); + } catch (Inkscape::BadURIException &e) { + g_warning("%s", e.what()); + this->ref->detach(); + } + } else { + this->ref->detach(); + } + } + } + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + default: + if (SP_ATTRIBUTE_IS_CSS(key)) { + sp_style_read_from_object(this->style, this); + requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } else { + SPPaintServer::set(key, value); + } + break; + } +} + +bool SPHatch::_hasHatchPatchChildren(SPHatch const *hatch) { + for (SPObject const *child = hatch->firstChild(); child; child = child->getNext() ) { + if (SP_IS_HATCH_PATH(child)) { + return true; + } + } + return false; +} + +void SPHatch::hatchPaths(std::vector<SPHatchPath*>& l) { + SPHatch *src = chase_hrefs<SPHatch>(this, sigc::ptr_fun(&_hasHatchPatchChildren)); + + if (src) { + for (SPObject *child = src->firstChild(); child; child = child->getNext()) { + if (SP_IS_HATCH_PATH(child)) { + l.push_back(SP_HATCH_PATH(child)); + } + } + } +} + +void SPHatch::hatchPaths(std::vector<SPHatchPath const*>& l) const { + SPHatch const *src = chase_hrefs<SPHatch const>(this, sigc::ptr_fun(&_hasHatchPatchChildren)); + + if (src) { + for (SPObject const *child = src->firstChild(); child; child = child->getNext()) { + if (SP_IS_HATCH_PATH(child)) { + l.push_back(SP_HATCH_PATH(child)); + } + } + } +} + +/* TODO: ::remove_child and ::order_changed handles - see SPPattern */ + + +void SPHatch::update(SPCtx* ctx, unsigned int flags) { + typedef std::list<SPHatch::View>::iterator ViewIterator; + + if (flags & SP_OBJECT_MODIFIED_FLAG) { + flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + } + + flags &= SP_OBJECT_MODIFIED_CASCADE; + + std::vector<SPHatchPath *> children; + hatchPaths(children); + + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath* child = *iter; + + sp_object_ref(child, NULL); + + for (ViewIterator view_iter = _display.begin(); view_iter != _display.end(); view_iter++) { + Geom::OptInterval strip_extents = _calculateStripExtents(view_iter->bbox); + child->setStripExtents(view_iter->key, strip_extents); + } + + if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { + + child->updateDisplay(ctx, flags); + } + + sp_object_unref(child, NULL); + } + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + _updateView(*iter); + } +} + +void SPHatch::modified(unsigned int flags) { + if (flags & SP_OBJECT_MODIFIED_FLAG) { + flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + } + + flags &= SP_OBJECT_MODIFIED_CASCADE; + + std::vector<SPHatchPath *> children; + hatchPaths(children); + + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPObject *child = *iter; + + sp_object_ref(child, NULL); + + if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { + child->emitModified(flags); + } + + sp_object_unref(child, NULL); + } +} + +void SPHatch::_onRefChanged(SPObject *old_ref, SPObject *ref) { + typedef std::list<SPHatch::View>::iterator ViewIterator; + + if (old_ref) { + _modified_connection.disconnect(); + } + + if (SP_IS_HATCH(ref)) { + _modified_connection = ref->connectModified(sigc::mem_fun(this, &SPHatch::_onRefModified)); + } + + if (!_hasHatchPatchChildren(this)) { + SPHatch *old_shown = NULL; + SPHatch *new_shown = NULL; + std::vector<SPHatchPath *> oldhatchPaths; + std::vector<SPHatchPath *> newhatchPaths; + if (SP_IS_HATCH(old_ref)) { + old_shown = SP_HATCH(old_ref)->rootHatch(); + old_shown->hatchPaths(oldhatchPaths); + } + if (SP_IS_HATCH(ref)) { + new_shown = SP_HATCH(ref)->rootHatch(); + new_shown->hatchPaths(newhatchPaths); + } + if (old_shown != new_shown) { + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + Geom::OptInterval extents = _calculateStripExtents(iter->bbox); + + for (ChildIterator child_iter = oldhatchPaths.begin(); child_iter != oldhatchPaths.end(); child_iter++) { + SPHatchPath *child = *child_iter; + child->hide(iter->key); + } + for (ChildIterator child_iter = newhatchPaths.begin(); child_iter != newhatchPaths.end(); child_iter++) { + SPHatchPath *child = *child_iter; + Inkscape::DrawingItem *cai = child->show(iter->arenaitem->drawing(), iter->key, extents); + child->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + if (cai) { + iter->arenaitem->appendChild(cai); + } + + } + } + } + } + + _onRefModified(ref, 0); +} + +void SPHatch::_onRefModified(SPObject */*ref*/, guint /*flags*/) { + requestModified(SP_OBJECT_MODIFIED_FLAG); + // Conditional to avoid causing infinite loop if there's a cycle in the href chain. +} + + +SPHatch *SPHatch::rootHatch() { + SPHatch *src = chase_hrefs<SPHatch>(this, sigc::ptr_fun(&_hasHatchPatchChildren)); + return src ? src : this; // document is broken, we can't get to root; but at least we can return pat which is supposedly a valid hatch +} + +// Access functions that look up fields up the chain of referenced hatchs and return the first one which is set +// FIXME: all of them must use chase_hrefs as children() and rootHatch() + +SPHatch::HatchUnits SPHatch::hatchUnits() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_hatchUnits_set) + return pat_i->_hatchUnits; + } + return _hatchUnits; +} + +SPHatch::HatchUnits SPHatch::hatchContentUnits() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_hatchContentUnits_set) + return pat_i->_hatchContentUnits; + } + return _hatchContentUnits; +} + +Geom::Affine const &SPHatch::hatchTransform() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_hatchTransform_set) + return pat_i->_hatchTransform; + } + return _hatchTransform; +} + +gdouble SPHatch::x() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_x._set) + return pat_i->_x.computed; + } + return 0; +} + +gdouble SPHatch::y() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_y._set) + return pat_i->_y.computed; + } + return 0; +} + +gdouble SPHatch::pitch() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_pitch._set) + return pat_i->_pitch.computed; + } + return 0; +} + +gdouble SPHatch::rotate() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_rotate._set) + return pat_i->_rotate.computed; + } + return 0; +} + +bool SPHatch::isValid() const { + double strip_pitch = pitch(); + if (strip_pitch <= 0) { + return false; + } + + std::vector<SPHatchPath const *> children; + hatchPaths(children); + if (children.empty()) { + return false; + } + for (ConstChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath const *child = *iter; + if (!child->isValid()) { + return false; + } + } + + return true; +} + +Inkscape::DrawingPattern *SPHatch::show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox) { + Inkscape::DrawingPattern *ai = new Inkscape::DrawingPattern(drawing); + //TODO: set some debug flag to see DrawingPattern + _display.push_front(View(ai, key)); + _display.front().bbox = bbox; + + std::vector<SPHatchPath *> children; + hatchPaths(children); + + Geom::OptInterval extents = _calculateStripExtents(bbox); + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath *child = *iter; + Inkscape::DrawingItem *cai = child->show(drawing, key, extents); + if (cai) { + ai->appendChild(cai); + } + } + + View& view = _display.front(); + _updateView(view); + + return ai; +} + +void SPHatch::hide(unsigned int key) { + std::vector<SPHatchPath *> children; + hatchPaths(children); + + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath *child = *iter; + child->hide(key); + } + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + delete iter->arenaitem; + _display.erase(iter); + return; + } + } + + g_assert_not_reached(); +} + + +Geom::Interval SPHatch::bounds() const { + Geom::Interval result; + std::vector<SPHatchPath const *> children; + hatchPaths(children); + + for (ConstChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath const *child = *iter; + if (result.extent() == 0) { + result = child->bounds(); + } else { + result |= child->bounds(); + } + } + return result; +} + +SPHatch::RenderInfo SPHatch::calculateRenderInfo(unsigned key) const { + RenderInfo info; + for (ConstViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + return _calculateRenderInfo(*iter); + } + } + g_assert_not_reached(); + return info; +} + +void SPHatch::_updateView(View &view) { + RenderInfo info = _calculateRenderInfo(view); + //The rendering of hatch overflow is implemented by repeated drawing + //of hatch paths over one strip. Within each iteration paths are moved by pitch value. + //The movement progresses from right to left. This gives the same result + //as drawing whole strips in left-to-right order. + + + view.arenaitem->setChildTransform(info.child_transform); + view.arenaitem->setPatternToUserTransform(info.pattern_to_user_transform); + view.arenaitem->setTileRect(info.tile_rect); + view.arenaitem->setStyle(this->style); + view.arenaitem->setOverflow(info.overflow_initial_transform, info.overflow_steps, + info.overflow_step_transform); +} + +SPHatch::RenderInfo SPHatch::_calculateRenderInfo(View const &view) const { + RenderInfo info; + + Geom::OptInterval extents = _calculateStripExtents(view.bbox); + if (!extents) { + return info; + } + + double tile_x = x(); + double tile_y = y(); + double tile_width = pitch(); + double tile_height = extents->max() - extents->min(); + double tile_rotate = rotate(); + double tile_render_y = extents->min(); + + if (view.bbox && (hatchUnits() == UNITS_OBJECTBOUNDINGBOX)) { + tile_x *= view.bbox->width(); + tile_y *= view.bbox->height(); + tile_width *= view.bbox->width(); + tile_height *= view.bbox->height(); + tile_render_y *= view.bbox->height(); + } + + // Pattern size in hatch space + Geom::Rect hatch_tile = Geom::Rect::from_xywh(0, tile_render_y, tile_width, tile_height); + // Content to bbox + Geom::Affine content2ps; + if (view.bbox && (hatchContentUnits() == UNITS_OBJECTBOUNDINGBOX)) { + content2ps = Geom::Affine(view.bbox->width(), 0.0, 0.0, view.bbox->height(), 0, 0); + } + + // Tile (hatch space) to user. + Geom::Affine ps2user = Geom::Translate(tile_x, tile_y) * Geom::Rotate::from_degrees(tile_rotate) * hatchTransform(); + + info.child_transform = content2ps; + info.pattern_to_user_transform = ps2user; + info.tile_rect = hatch_tile; + + if (style->overflow.computed == SP_CSS_OVERFLOW_VISIBLE) { + Geom::Interval bounds = this->bounds(); + gdouble pitch = this->pitch(); + gdouble overflow_right_strip = floor(bounds.max() / pitch) * pitch; + info.overflow_steps = ceil((overflow_right_strip - bounds.min()) / pitch) + 1; + info.overflow_step_transform = Geom::Translate(pitch, 0.0); + info.overflow_initial_transform = Geom::Translate(-overflow_right_strip, 0.0); + } else { + info.overflow_steps = 1; + } + + return info; +} + +//calculates strip extents in content space +Geom::OptInterval SPHatch::_calculateStripExtents(Geom::OptRect bbox) const { + if (!bbox || (bbox->area() == 0)) { + return Geom::OptInterval(); + } + + double tile_x = x(); + double tile_y = y(); + double tile_rotate = rotate(); + + Geom::Affine ps2user = Geom::Translate(tile_x, tile_y) * Geom::Rotate::from_degrees(tile_rotate) * hatchTransform(); + Geom::Affine user2ps = ps2user.inverse(); + + Geom::Interval extents; + for (int i = 0; i < 4; i++) { + Geom::Point corner = bbox->corner(i); + Geom::Point corner_ps = corner * user2ps; + if (i == 0 || corner_ps.y() < extents.min()) { + extents.setMin(corner_ps.y()); + } + if (i == 0 || corner_ps.y() > extents.max()) { + extents.setMax(corner_ps.y()); + } + } + + if (hatchUnits() == UNITS_OBJECTBOUNDINGBOX) { + extents /= bbox->height(); + } + + return extents; +} + +cairo_pattern_t* SPHatch::pattern_new(cairo_t * /*base_ct*/, Geom::OptRect const &/*bbox*/, double /*opacity*/) +{ + //this code should not be used + //it is however required by the fact that SPPaintServer::hatch_new is pure virtual + return cairo_pattern_create_rgb(0.5, 0.5, 1.0); +} + +void SPHatch::setBBox(unsigned int key, Geom::OptRect const &bbox) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + iter->bbox = bbox; + break; + } + } +} + +SPHatch::View::View(Inkscape::DrawingPattern *arenaitem, int key) + : arenaitem(arenaitem), key(key) +{ +} +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/sp-hatch.h b/src/sp-hatch.h new file mode 100644 index 000000000..dc6ee0add --- /dev/null +++ b/src/sp-hatch.h @@ -0,0 +1,174 @@ +/** @file + * SVG <hatch> implementation + *//* + * Author: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_SP_HATCH_H +#define SEEN_SP_HATCH_H + +#include <list> +#include <stddef.h> +#include <glibmm/ustring.h> +#include <sigc++/connection.h> + +#include "svg/svg-length.h" +#include "svg/svg-angle.h" +#include "sp-paint-server.h" +#include "uri-references.h" + +class SPHatchReference; +class SPHatchPath; +class SPItem; + +namespace Inkscape { + +class Drawing; +class DrawingPattern; + +namespace XML { + +class Node; + +} +} + +#define SP_HATCH(obj) (dynamic_cast<SPHatch*>((SPObject*)obj)) +#define SP_IS_HATCH(obj) (dynamic_cast<const SPHatch*>((SPObject*)obj) != NULL) + +class SPHatch : public SPPaintServer { +public: + enum HatchUnits { + UNITS_USERSPACEONUSE, + UNITS_OBJECTBOUNDINGBOX + }; + + struct RenderInfo { + Geom::Affine child_transform; + Geom::Affine pattern_to_user_transform; + Geom::Rect tile_rect; + + int overflow_steps; + Geom::Affine overflow_step_transform; + Geom::Affine overflow_initial_transform; + }; + + SPHatch(); + virtual ~SPHatch(); + + /* Reference (href) */ + Glib::ustring href; + SPHatchReference *ref; + + gdouble x() const; + gdouble y() const; + gdouble pitch() const; + gdouble rotate() const; + HatchUnits hatchUnits() const; + HatchUnits hatchContentUnits() const; + Geom::Affine const &hatchTransform() const; + SPHatch *rootHatch(); //TODO: const + + void hatchPaths(std::vector<SPHatchPath*>& l); + void hatchPaths(std::vector<SPHatchPath const *>& l) const; + + bool isValid() const; + + Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox); + void hide(unsigned int key); + virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity); + + RenderInfo calculateRenderInfo(unsigned key) const; + Geom::Interval bounds() const; + void setBBox(unsigned int key, Geom::OptRect const &bbox); + +protected: + virtual void build(SPDocument* doc, Inkscape::XML::Node* repr); + virtual void release(); + virtual void child_added(Inkscape::XML::Node* child, Inkscape::XML::Node* ref); + virtual void set(unsigned int key, const gchar* value); + virtual void update(SPCtx* ctx, unsigned int flags); + virtual void modified(unsigned int flags); + +private: + struct View { + View(Inkscape::DrawingPattern *arenaitem, int key); + //Do not delete arenaitem in destructor. + + Inkscape::DrawingPattern *arenaitem; + Geom::OptRect bbox; + unsigned int key; + }; + + typedef std::vector<SPHatchPath *>::iterator ChildIterator; + typedef std::vector<SPHatchPath const *>::const_iterator ConstChildIterator; + typedef std::list<View>::iterator ViewIterator; + typedef std::list<View>::const_iterator ConstViewIterator; + + static bool _hasHatchPatchChildren(SPHatch const* hatch); + + void _updateView(View &view); + RenderInfo _calculateRenderInfo(View const &view) const; + Geom::OptInterval _calculateStripExtents(Geom::OptRect bbox) const; + + + /** + Gets called when the hatch is reattached to another <hatch> + */ + void _onRefChanged(SPObject *old_ref, SPObject *ref); + + /** + Gets called when the referenced <hatch> is changed + */ + void _onRefModified(SPObject *ref, guint flags); + + /* patternUnits and patternContentUnits attribute */ + HatchUnits _hatchUnits : 1; + bool _hatchUnits_set : 1; + HatchUnits _hatchContentUnits : 1; + bool _hatchContentUnits_set : 1; + /* hatchTransform attribute */ + Geom::Affine _hatchTransform; + bool _hatchTransform_set : 1; + /* Strip */ + SVGLength _x; + SVGLength _y; + SVGLength _pitch; + SVGAngle _rotate; + + sigc::connection _modified_connection; + + std::list<View> _display; +}; + + +class SPHatchReference : public Inkscape::URIReference { +public: + SPHatchReference (SPObject *obj) : URIReference(obj) {} + SPHatch *getObject() const { + return reinterpret_cast<SPHatch *>(URIReference::getObject()); + } + +protected: + virtual bool _acceptObject(SPObject *obj) const { + return SP_IS_HATCH (obj); + } +}; + +#endif // SEEN_SP_HATCH_H + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/sp-item.cpp b/src/sp-item.cpp index b1a22fd8c..94ab1b181 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -120,6 +120,9 @@ SPItem::SPItem() : SPObject() { mask_ref = new SPMaskReference(this); mask_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(mask_ref_changed), this)); + style->signal_fill_ps_changed.connect(sigc::bind(sigc::ptr_fun(fill_ps_ref_changed), this)); + style->signal_stroke_ps_changed.connect(sigc::bind(sigc::ptr_fun(stroke_ps_ref_changed), this)); + avoidRef = new SPAvoidRef(this); } @@ -465,7 +468,15 @@ void SPItem::release() { SPObject::release(); + SPPaintServer *fill_ps = style->getFillPaintServer(); + SPPaintServer *stroke_ps = style->getStrokePaintServer(); while (item->display) { + if (fill_ps) { + fill_ps->hide(item->display->arenaitem->key()); + } + if (stroke_ps) { + stroke_ps->hide(item->display->arenaitem->key()); + } item->display = sp_item_view_list_remove(item->display, item->display); } @@ -623,6 +634,56 @@ void SPItem::mask_ref_changed(SPObject *old_mask, SPObject *mask, SPItem *item) } } +void SPItem::fill_ps_ref_changed(SPObject *old_ps, SPObject *ps, SPItem *item) { + SPPaintServer *old_fill_ps = SP_PAINT_SERVER(old_ps); + if (old_fill_ps) { + for (SPItemView *v =item->display; v != NULL; v = v->next) { + old_fill_ps->hide(v->arenaitem->key()); + } + } + + SPPaintServer *new_fill_ps = SP_PAINT_SERVER(ps); + if (new_fill_ps) { + Geom::OptRect bbox = item->geometricBounds(); + for (SPItemView *v = item->display; v != NULL; v = v->next) { + if (!v->arenaitem->key()) { + v->arenaitem->setKey(SPItem::display_key_new(3)); + } + Inkscape::DrawingPattern *pi = new_fill_ps->show( + v->arenaitem->drawing(), v->arenaitem->key(), bbox); + v->arenaitem->setFillPattern(pi); + if (pi) { + new_fill_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + } + } +} + +void SPItem::stroke_ps_ref_changed(SPObject *old_ps, SPObject *ps, SPItem *item) { + SPPaintServer *old_stroke_ps = SP_PAINT_SERVER(old_ps); + if (old_stroke_ps) { + for (SPItemView *v =item->display; v != NULL; v = v->next) { + old_stroke_ps->hide(v->arenaitem->key()); + } + } + + SPPaintServer *new_stroke_ps = SP_PAINT_SERVER(ps); + if (new_stroke_ps) { + Geom::OptRect bbox = item->geometricBounds(); + for (SPItemView *v = item->display; v != NULL; v = v->next) { + if (!v->arenaitem->key()) { + v->arenaitem->setKey(SPItem::display_key_new(3)); + } + Inkscape::DrawingPattern *pi = new_stroke_ps->show( + v->arenaitem->drawing(), v->arenaitem->key(), bbox); + v->arenaitem->setStrokePattern(pi); + if (pi) { + new_stroke_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + } + } +} + void SPItem::update(SPCtx* /*ctx*/, guint flags) { SPItem *item = this; SPItem* object = item; @@ -682,6 +743,10 @@ void SPItem::update(SPCtx* /*ctx*/, guint flags) { item->avoidRef->handleSettingChange(); } +void SPItem::modified(unsigned int /*flags*/) +{ +} + Inkscape::XML::Node* SPItem::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { SPItem *item = this; SPItem* object = item; @@ -1130,6 +1195,33 @@ Inkscape::DrawingItem *SPItem::invoke_show(Inkscape::Drawing &drawing, unsigned SP_MASK(mask)->sp_mask_set_bbox(mask_key, item_bbox); mask->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } + + SPPaintServer *fill_ps = style->getFillPaintServer(); + if (fill_ps) { + if (!display->arenaitem->key()) { + display->arenaitem->setKey(display_key_new(3)); + } + int fill_key = display->arenaitem->key(); + + Inkscape::DrawingPattern *ap = fill_ps->show(drawing, fill_key, item_bbox); + ai->setFillPattern(ap); + if (ap) { + fill_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + } + SPPaintServer *stroke_ps = style->getStrokePaintServer(); + if (stroke_ps) { + if (!display->arenaitem->key()) { + display->arenaitem->setKey(display_key_new(3)); + } + int stroke_key = display->arenaitem->key(); + + Inkscape::DrawingPattern *ap = stroke_ps->show(drawing, stroke_key, item_bbox); + ai->setStrokePattern(ap); + if (ap) { + stroke_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + } ai->setData(this); ai->setItemBounds(geometricBounds()); } @@ -1159,6 +1251,14 @@ void SPItem::invoke_hide(unsigned key) mask_ref->getObject()->sp_mask_hide(v->arenaitem->key()); v->arenaitem->setMask(NULL); } + SPPaintServer *fill_ps = style->getFillPaintServer(); + if (fill_ps) { + fill_ps->hide(v->arenaitem->key()); + } + SPPaintServer *stroke_ps = style->getStrokePaintServer(); + if (stroke_ps) { + stroke_ps->hide(v->arenaitem->key()); + } if (!ref) { display = v->next; } else { diff --git a/src/sp-item.h b/src/sp-item.h index 1e4f5f4c6..1656a0c2b 100644 --- a/src/sp-item.h +++ b/src/sp-item.h @@ -34,6 +34,7 @@ class SPClipPathReference; class SPMaskReference; class SPAvoidRef; +class SPPattern; struct SPPrintContext; typedef unsigned int guint32; @@ -252,12 +253,15 @@ private: static SPItemView *sp_item_view_new_prepend(SPItemView *list, SPItem *item, unsigned flags, unsigned key, Inkscape::DrawingItem *arenaitem); static void clip_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); static void mask_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); + static void fill_ps_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); + static void stroke_ps_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); public: virtual void build(SPDocument *document, Inkscape::XML::Node *repr); virtual void release(); virtual void set(unsigned int key, char const* value); virtual void update(SPCtx *ctx, unsigned int flags); + virtual void modified(unsigned int flags); virtual Inkscape::XML::Node* write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags); virtual Geom::OptRect bbox(Geom::Affine const &transform, SPItem::BBoxType type) const; diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index a2a935951..019d15162 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -702,9 +702,8 @@ SPLPEItem::apply_to_mask(SPItem *item) { SPMask *mask = item->mask_ref->getObject(); if(mask) { - SPObject * mask_data = mask->firstChild(); + SPObject *mask_data = mask->firstChild(); SPCurve * mask_curve = NULL; - mask_data = mask->firstChild(); if (SP_IS_PATH(mask_data)) { mask_curve = SP_PATH(mask_data)->get_original_curve(); } else if(SP_IS_SHAPE(mask_data)) { diff --git a/src/sp-paint-server.cpp b/src/sp-paint-server.cpp index 77ad7a35f..ae40ad1aa 100644 --- a/src/sp-paint-server.cpp +++ b/src/sp-paint-server.cpp @@ -60,7 +60,25 @@ bool SPPaintServer::isSolid() const bool SPPaintServer::isValid() const { - return true; + return true; +} + +Inkscape::DrawingPattern *SPPaintServer::show(Inkscape::Drawing &/*drawing*/, unsigned int /*key*/, Geom::OptRect /*bbox*/) +{ + return NULL; +} + +void SPPaintServer::hide(unsigned int /*key*/) +{ +} + +void SPPaintServer::setBBox(unsigned int /*key*/, Geom::OptRect const &/*bbox*/) +{ +} + +cairo_pattern_t* SPPaintServer::pattern_new(cairo_t * /*ct*/, Geom::OptRect const &/*bbox*/, double /*opacity*/) +{ + return NULL; } /* diff --git a/src/sp-paint-server.h b/src/sp-paint-server.h index beac72af5..7f3bfcba0 100644 --- a/src/sp-paint-server.h +++ b/src/sp-paint-server.h @@ -15,11 +15,17 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <cairo.h> #include <2geom/rect.h> +#include <sigc++/slot.h> #include "sp-object.h" -typedef struct _cairo cairo_t; -typedef struct _cairo_pattern cairo_pattern_t; +namespace Inkscape { + +class Drawing; +class DrawingPattern; + +} #define SP_PAINT_SERVER(obj) (dynamic_cast<SPPaintServer*>((SPObject*)obj)) #define SP_IS_PAINT_SERVER(obj) (dynamic_cast<const SPPaintServer*>((SPObject*)obj) != NULL) @@ -33,12 +39,59 @@ public: bool isSolid() const; virtual bool isValid() const; - virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity) = 0; + //There are two ways to render a paint. The simple one is to create cairo_pattern_t structure + //on demand by pattern_new method. It is used for gradients. The other one is to add elements + //representing PaintServer in NR tree. It is used by hatches and patterns. + //Either pattern new or all three methods show, hide, setBBox need to be implemented + virtual Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox); // TODO check passing bbox by value. Looks suspicious. + virtual void hide(unsigned int key); + virtual void setBBox(unsigned int key, Geom::OptRect const &bbox); + + virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity); protected: bool swatch; }; +/** + * Returns the first of {src, src-\>ref-\>getObject(), + * src-\>ref-\>getObject()-\>ref-\>getObject(),...} + * for which \a match is true, or NULL if none found. + * + * The raison d'être of this routine is that it correctly handles cycles in the href chain (e.g., if + * a gradient gives itself as its href, or if each of two gradients gives the other as its href). + * + * \pre SP_IS_GRADIENT(src). + */ +template <class PaintServer> +PaintServer *chase_hrefs(PaintServer *src, sigc::slot<bool, PaintServer const *> match) { + /* Use a pair of pointers for detecting loops: p1 advances half as fast as p2. If there is a + loop, then once p1 has entered the loop, we'll detect it the next time the distance between + p1 and p2 is a multiple of the loop size. */ + PaintServer *p1 = src, *p2 = src; + bool do1 = false; + for (;;) { + if (match(p2)) { + return p2; + } + + p2 = p2->ref->getObject(); + if (!p2) { + return p2; + } + if (do1) { + p1 = p1->ref->getObject(); + } + do1 = !do1; + + if ( p2 == p1 ) { + /* We've been here before, so return NULL to indicate that no matching gradient found + * in the chain. */ + return NULL; + } + } +} + #endif // SEEN_SP_PAINT_SERVER_H /* Local Variables: diff --git a/src/style-internal.cpp b/src/style-internal.cpp index a55a11403..b858e5cb6 100644 --- a/src/style-internal.cpp +++ b/src/style-internal.cpp @@ -964,7 +964,11 @@ SPIPaint::read( gchar const *str ) { if (!value.href && document) { // std::cout << " Creating value.href" << std::endl; value.href = new SPPaintServerReference(document); - value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun((this == &style->fill)? sp_style_fill_paint_server_ref_changed : sp_style_stroke_paint_server_ref_changed), style)); + if (this == &style->fill) { + style->fill_ps_changed_connection = value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), style)); + } else { + style->stroke_ps_changed_connection = value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), style)); + } } // std::cout << "uri: " << (uri?uri:"null") << std::endl; @@ -981,7 +985,16 @@ SPIPaint::read( gchar const *str ) { if (streq(str, "currentColor")) { set = true; currentcolor = true; - setColor( style->color.value.color ); + if (style) { + setColor( style->color.value.color ); + } else { + // Normally an SPIPaint is part of an SPStyle and the value of 'color' is + // available. SPIPaint can be used 'stand-alone' (e.g. to parse color values) in + // which case a value of 'currentColor' is meaningless, thus we shouldn't reach + // here. + std::cerr << "SPIPaint::read(): value is 'currentColor' but 'color' not available." << std::endl; + setColor( 0 ); + } } else if (streq(str, "none")) { set = true; noneSet = true; @@ -1396,11 +1409,20 @@ SPIFilter::read( gchar const *str ) { set = true; // Create href if not already done. - if (!href && style->object) { - href = new SPFilterReference(style->object); - href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), style)); + if (!href) { + if (style->object) { + href = new SPFilterReference(style->object); + } + // Do we have href now? + if ( href ) { + href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), style)); + } else { + std::cerr << "SPIFilter::read(): Could not allocate 'href'" << std::endl; + return; + } } + // We have href try { href->attach(Inkscape::URI(uri)); } catch (Inkscape::BadURIException &e) { diff --git a/src/style-internal.h b/src/style-internal.h index 9158ed60e..32792827a 100644 --- a/src/style-internal.h +++ b/src/style-internal.h @@ -104,22 +104,45 @@ static const unsigned SP_STYLE_FLAG_IFDIFF (1 << 1); */ /// Virtual base class for all SPStyle interal classes -class SPIBase { +class SPIBase +{ - public: +public: SPIBase( Glib::ustring const &name, bool inherits = true ) - : name(name), inherits(inherits), set(false), inherit(false), style_att(false), style(NULL) {}; - virtual ~SPIBase() {}; + : name(name), + inherits(inherits), + set(false), + inherit(false), + style_att(false), + style(NULL) + {} + + virtual ~SPIBase() + {} + virtual void read( gchar const *str ) = 0; - virtual void readIfUnset( gchar const *str ) { if( !set ) read( str ); } - virtual void readAttribute( Inkscape::XML::Node *repr ) { readIfUnset( repr->attribute( name.c_str() ) ); } + virtual void readIfUnset( gchar const *str ) { + if ( !set ) { + read( str ); + } + } + + virtual void readAttribute( Inkscape::XML::Node *repr ) { + readIfUnset( repr->attribute( name.c_str() ) ); + } + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const = 0; - virtual void clear() { set = false, inherit = false; }; + virtual void clear() { + set = false, inherit = false; + } + virtual void cascade( const SPIBase* const parent ) = 0; virtual void merge( const SPIBase* const parent ) = 0; - virtual void setStylePointer( SPStyle *style_in ) { style = style_in; }; + virtual void setStylePointer( SPStyle *style_in ) { + style = style_in; + } // Explicit assignment operator required due to templates. SPIBase& operator=(const SPIBase& rhs) { @@ -133,11 +156,16 @@ class SPIBase { } // Check apples being compared to apples - virtual bool operator==(const SPIBase& rhs) { return (name == rhs.name); }; - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator==(const SPIBase& rhs) { + return (name == rhs.name); + } + + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: Glib::ustring name; // Make const unsigned inherits : 1; // Property inherits by default from parent. unsigned set : 1; // Property has been explicitly set (vs. inherited). @@ -145,22 +173,35 @@ class SPIBase { unsigned style_att : 2; // Source (attribute, style attribute, style-sheet). NOT USED YET FIX ME // To do: make private after g_asserts removed - public: +public: SPStyle* style; // Used by SPIPaint, SPIFilter... to find values of other properties }; /// Float type internal to SPStyle. (Only 'stroke-miterlimit') -class SPIFloat : public SPIBase { - - public: - SPIFloat() : SPIBase( "anonymous_float" ), value(0.0) {}; - SPIFloat( Glib::ustring name, float value_default = 0.0 ) - : SPIBase( name ), value(value_default), value_default(value_default) {}; - virtual ~SPIFloat() {}; +class SPIFloat : public SPIBase +{ + +public: + SPIFloat() + : SPIBase( "anonymous_float" ), + value(0.0) + {} + + SPIFloat( Glib::ustring const &name, float value_default = 0.0 ) + : SPIBase( name ), + value(value_default), + value_default(value_default) + {} + + virtual ~SPIFloat() {} virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); value = value_default; }; + virtual void clear() { + SPIBase::clear(); + value = value_default; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -172,13 +213,15 @@ class SPIFloat : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: float value; - private: +private: float value_default; }; @@ -214,17 +257,32 @@ static const unsigned SP_SCALE24_MAX = 0xff0000; /// 24 bit data type internal to SPStyle. // Used only for opacity, fill-opacity, stroke-opacity. // Opacity does not inherit but stroke-opacity and fill-opacity do. -class SPIScale24 : public SPIBase { +class SPIScale24 : public SPIBase +{ + +public: + SPIScale24() + : SPIBase( "anonymous_scale24" ), + value(0) + {} + + SPIScale24( Glib::ustring const &name, unsigned value = 0, bool inherits = true ) + : SPIBase( name, inherits ), + value(value), + value_default(value) + {} + + virtual ~SPIScale24() + {} - public: - SPIScale24() : SPIBase( "anonymous_scale24" ), value(0) {}; - SPIScale24( Glib::ustring name, unsigned value = 0, bool inherits = true ) - : SPIBase( name, inherits ), value(value), value_default(value) {}; - virtual ~SPIScale24() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); value = value_default; }; + virtual void clear() { + SPIBase::clear(); + value = value_default; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -236,14 +294,16 @@ class SPIScale24 : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: unsigned value : 24; - private: +private: unsigned value_default : 24; }; @@ -265,17 +325,37 @@ enum SPCSSUnit { /// Length type internal to SPStyle. // Needs access to 'font-size' and 'font-family' for computed values. // Used for 'stroke-width' 'stroke-dash-offset' ('none' not handled), text-indent -class SPILength : public SPIBase { +class SPILength : public SPIBase +{ + +public: + SPILength() + : SPIBase( "anonymous_length" ), + unit(SP_CSS_UNIT_NONE), + value(0), + computed(0) + {} + + SPILength( Glib::ustring const &name, unsigned value = 0 ) + : SPIBase( name ), + unit(SP_CSS_UNIT_NONE), + value(value), + computed(value), + value_default(value) + {} + + virtual ~SPILength() + {} - public: - SPILength() : SPIBase( "anonymous_length" ), unit(SP_CSS_UNIT_NONE), value(0), computed(0) {}; - SPILength( Glib::ustring name, unsigned value = 0 ) - : SPIBase( name ), unit(SP_CSS_UNIT_NONE), value(value), computed(value), value_default(value) {}; - virtual ~SPILength() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); unit = SP_CSS_UNIT_NONE, value = value_default; computed = value_default; }; + virtual void clear() { + SPIBase::clear(); + unit = SP_CSS_UNIT_NONE, value = value_default; + computed = value_default; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -286,35 +366,51 @@ class SPILength : public SPIBase { computed = rhs.computed; value_default = rhs.value_default; return *this; - }; + } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: unsigned unit : 4; float value; float computed; - private: +private: float value_default; }; /// Extended length type internal to SPStyle. // Used for: line-height, letter-spacing, word-spacing -class SPILengthOrNormal : public SPILength { +class SPILengthOrNormal : public SPILength +{ + +public: + SPILengthOrNormal() + : SPILength( "anonymous_length" ), + normal(true) + {} + + SPILengthOrNormal( Glib::ustring const &name, unsigned value = 0 ) + : SPILength( name, value ), + normal(true) + {} + + virtual ~SPILengthOrNormal() + {} - public: - SPILengthOrNormal() : SPILength( "anonymous_length" ), normal(true) {}; - SPILengthOrNormal( Glib::ustring name, unsigned value = 0 ) - : SPILength( name, value ), normal(true) {}; - virtual ~SPILengthOrNormal() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPILength::clear(); normal = true; }; + virtual void clear() { + SPILength::clear(); + normal = true; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -325,33 +421,59 @@ class SPILengthOrNormal : public SPILength { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: bool normal : 1; }; /// Enum type internal to SPStyle. // Used for many properties. 'font-stretch' and 'font-weight' must be special cased. -class SPIEnum : public SPIBase { +class SPIEnum : public SPIBase +{ - public: +public: SPIEnum() : - SPIBase( "anonymous_enum" ), enums( NULL ), value(0), computed(0) {}; - SPIEnum( Glib::ustring const name, SPStyleEnum const *enums, unsigned value = 0, bool inherits = true ) : - SPIBase( name, inherits ), enums( enums ), value(value), computed(value), - value_default(value), computed_default(value) {}; + SPIBase( "anonymous_enum" ), + enums( NULL ), + value(0), + computed(0) + {} + + SPIEnum( Glib::ustring const &name, SPStyleEnum const *enums, unsigned value = 0, bool inherits = true ) : + SPIBase( name, inherits ), + enums( enums ), + value(value), + computed(value), + value_default(value), + computed_default(value) + {} + // Following is needed for font-weight - SPIEnum( Glib::ustring name, SPStyleEnum const *enums, SPCSSFontWeight value, SPCSSFontWeight computed ) : - SPIBase( name ), enums( enums ), value(value), computed(computed), - value_default(value), computed_default(computed) {}; - virtual ~SPIEnum() {}; + SPIEnum( Glib::ustring const &name, SPStyleEnum const *enums, SPCSSFontWeight value, SPCSSFontWeight computed ) : + SPIBase( name ), + enums( enums ), + value(value), + computed(computed), + value_default(value), + computed_default(computed) + {} + + virtual ~SPIEnum() + {} + virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); value = value_default, computed = computed_default; }; + virtual void clear() { + SPIBase::clear(); + value = value_default, computed = computed_default; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -365,16 +487,18 @@ class SPIEnum : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: SPStyleEnum const *enums; unsigned value : 8; unsigned computed: 8; - private: +private: unsigned value_default : 8; unsigned computed_default: 8; // for font-weight }; @@ -382,20 +506,31 @@ class SPIEnum : public SPIBase { /// String type internal to SPStyle. // Used for 'marker', ..., 'font', 'font-family', 'inkscape-font-specification' -class SPIString : public SPIBase { - - public: - SPIString() : - SPIBase( "anonymous_string" ), value(NULL) {}; - SPIString( Glib::ustring name, gchar* value_default_in = NULL ) : - SPIBase( name ) , value(NULL) , value_default(NULL) { - value_default = value_default_in?g_strdup(value_default_in):NULL; - }; - virtual ~SPIString() { g_free(value); g_free(value_default); }; +class SPIString : public SPIBase +{ + +public: + SPIString() + : SPIBase( "anonymous_string" ), + value(NULL) + {} + + // TODO probably want to avoid gchar* and c-style strings. + SPIString( Glib::ustring const &name, gchar const* value_default_in = NULL ) + : SPIBase( name ), + value(NULL), + value_default(value_default_in ? g_strdup(value_default_in) : NULL) + {} + + virtual ~SPIString() { + g_free(value); + g_free(value_default); + } + virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear(); + virtual void clear(); // TODO check about value and value_default virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -403,31 +538,50 @@ class SPIString : public SPIBase { SPIBase::operator=(rhs); g_free(value); g_free(value_default); - value = rhs.value?g_strdup(rhs.value):NULL; - value_default = rhs.value_default?g_strdup(rhs.value_default):NULL; + value = rhs.value ? g_strdup(rhs.value) : NULL; + value_default = rhs.value_default ? g_strdup(rhs.value_default) : NULL; return *this; } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private, convert value to Glib::ustring - public: +public: gchar *value; gchar *value_default; }; /// Color type interal to SPStyle, FIXME Add string value to store SVG named color. -class SPIColor : public SPIBase { +class SPIColor : public SPIBase +{ + +public: + SPIColor() + : SPIBase( "anonymous_color" ), + currentcolor(false) { + value.color.set(0); + } + + SPIColor( Glib::ustring const &name ) + : SPIBase( name ), + currentcolor(false) { + value.color.set(0); + } + + virtual ~SPIColor() + {} - public: - SPIColor() : SPIBase( "anonymous_color" ), currentcolor(false) { value.color.set(0); } - SPIColor( Glib::ustring name ) : SPIBase( name ), currentcolor(false) { value.color.set(0); } - virtual ~SPIColor() {} virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); value.color.set(0); } + virtual void clear() { + SPIBase::clear(); + value.color.set(0); + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -439,13 +593,23 @@ class SPIColor : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); } + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } - void setColor( float r, float g, float b ) { value.color.set( r, g, b ); } - void setColor( guint32 val ) { value.color.set( val ); } - void setColor( SPColor const& color ) { value.color = color; } + void setColor( float r, float g, float b ) { + value.color.set( r, g, b ); + } + + void setColor( guint32 val ) { + value.color.set( val ); + } + + void setColor( SPColor const& color ) { + value.color = color; + } - public: +public: bool currentcolor : 1; // FIXME: remove structure and derive SPIPaint from this class. struct { @@ -459,18 +623,28 @@ class SPIColor : public SPIBase { #define SP_STYLE_STROKE_SERVER(s) ((const_cast<SPStyle *> (s))->getStrokePaintServer()) /// Paint type internal to SPStyle. -class SPIPaint : public SPIBase { - - public: - SPIPaint() : SPIBase( "anonymous_paint" ), currentcolor(false), colorSet(false), noneSet(false) { +class SPIPaint : public SPIBase +{ + +public: + SPIPaint() + : SPIBase( "anonymous_paint" ), + currentcolor(false), + colorSet(false), + noneSet(false) { value.href = NULL; clear(); - }; - SPIPaint( Glib::ustring name ) - : SPIBase( name ), currentcolor(false), colorSet(false), noneSet(false) { + } + + SPIPaint( Glib::ustring const &name ) + : SPIBase( name ), + currentcolor(false), + colorSet(false), + noneSet(false) { value.href = NULL; clear(); // Sets defaults - }; + } + virtual ~SPIPaint(); // Clear and delete href. virtual void read( gchar const *str ); virtual void read( gchar const *str, SPStyle &style, SPDocument *document = 0); @@ -492,24 +666,46 @@ class SPIPaint : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } + + bool isSameType( SPIPaint const & other ) const { + return (isPaintserver() == other.isPaintserver()) && (colorSet == other.colorSet) && (currentcolor == other.currentcolor); + } - bool isSameType( SPIPaint const & other ) const {return (isPaintserver() == other.isPaintserver()) && (colorSet == other.colorSet) && (currentcolor == other.currentcolor);} + bool isNoneSet() const { + return noneSet; + } + + bool isNone() const { + return !currentcolor && !colorSet && !isPaintserver(); + } // TODO refine + + bool isColor() const { + return colorSet && !isPaintserver(); + } - bool isNoneSet() const {return noneSet;} + bool isPaintserver() const { + return (value.href) ? value.href->getObject() : 0; + } - bool isNone() const {return !currentcolor && !colorSet && !isPaintserver();} // TODO refine - bool isColor() const {return colorSet && !isPaintserver();} - bool isPaintserver() const {return (value.href) ? value.href->getObject():0;} + void setColor( float r, float g, float b ) { + value.color.set( r, g, b ); colorSet = true; + } - void setColor( float r, float g, float b ) {value.color.set( r, g, b ); colorSet = true;} - void setColor( guint32 val ) {value.color.set( val ); colorSet = true;} - void setColor( SPColor const& color ) {value.color = color; colorSet = true;} + void setColor( guint32 val ) { + value.color.set( val ); colorSet = true; + } + void setColor( SPColor const& color ) { + value.color = color; colorSet = true; + } + void setNone() {noneSet = true; colorSet=false;} // To do: make private - public: +public: bool currentcolor : 1; bool colorSet : 1; bool noneSet : 1; @@ -536,11 +732,20 @@ enum SPPaintOrderLayer { const size_t PAINT_ORDER_LAYERS = 3; /// Paint order type internal to SPStyle -class SPIPaintOrder : public SPIBase { +class SPIPaintOrder : public SPIBase +{ + +public: + SPIPaintOrder() + : SPIBase( "paint-order" ), + value(NULL) { + this->clear(); + } + + virtual ~SPIPaintOrder() { + g_free( value ); + } - public: - SPIPaintOrder() : SPIBase( "paint-order" ), value(NULL) { this->clear(); }; - virtual ~SPIPaintOrder() { g_free( value ); }; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; @@ -568,11 +773,13 @@ class SPIPaintOrder : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: SPPaintOrderLayer layer[PAINT_ORDER_LAYERS]; bool layer_set[PAINT_ORDER_LAYERS]; gchar *value; // Raw string @@ -580,15 +787,25 @@ class SPIPaintOrder : public SPIBase { /// Filter type internal to SPStyle -class SPIDashArray : public SPIBase { +class SPIDashArray : public SPIBase +{ + +public: + SPIDashArray() + : SPIBase( "stroke-dasharray" ) + {} // Only one instance of SPIDashArray + + virtual ~SPIDashArray() + {} - public: - SPIDashArray() : SPIBase( "stroke-dasharray" ) {}; // Only one instance of SPIDashArray - virtual ~SPIDashArray() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); values.clear(); }; + virtual void clear() { + SPIBase::clear(); + values.clear(); + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -599,19 +816,26 @@ class SPIDashArray : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private, change double to SVGLength - public: +public: std::vector<double> values; }; /// Filter type internal to SPStyle -class SPIFilter : public SPIBase { +class SPIFilter : public SPIBase +{ + +public: + SPIFilter() + : SPIBase( "filter", false ), + href(NULL) + {} - public: - SPIFilter() : SPIBase( "filter", false ), href(NULL) {}; virtual ~SPIFilter(); virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, @@ -627,10 +851,12 @@ class SPIFilter : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: SPFilterReference *href; }; @@ -643,16 +869,27 @@ enum { }; /// Fontsize type internal to SPStyle (also used by libnrtype/Layout-TNG-Input.cpp). -class SPIFontSize : public SPIBase { +class SPIFontSize : public SPIBase +{ + +public: + SPIFontSize() + : SPIBase( "font-size" ) { + this->clear(); + } + + virtual ~SPIFontSize() + {} - public: - SPIFontSize() : SPIBase( "font-size" ) { this->clear(); }; - virtual ~SPIFontSize() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); type = SP_FONT_SIZE_LITERAL, unit = SP_CSS_UNIT_NONE, - literal = SP_CSS_FONT_SIZE_MEDIUM, value = 12.0, computed = 12.0; } + virtual void clear() { + SPIBase::clear(); + type = SP_FONT_SIZE_LITERAL, unit = SP_CSS_UNIT_NONE, + literal = SP_CSS_FONT_SIZE_MEDIUM, value = 12.0, computed = 12.0; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -667,39 +904,51 @@ class SPIFontSize : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } - public: +public: static float const font_size_default; // To do: make private - public: +public: unsigned type : 2; unsigned unit : 4; unsigned literal : 4; float value; float computed; - private: +private: double relative_fraction() const; static float const font_size_table[]; }; /// Font type internal to SPStyle ('font' shorthand) -class SPIFont : public SPIBase { +class SPIFont : public SPIBase +{ + +public: + SPIFont() + : SPIBase( "font" ) + {} + + virtual ~SPIFont() + {} - public: - SPIFont() : SPIBase( "font" ) {}; - virtual ~SPIFont() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); - }; - virtual void cascade( const SPIBase* const parent ) { (void)parent; }; // Done in dependent properties - virtual void merge( const SPIBase* const parent ) { (void)parent; }; + } + + virtual void cascade( const SPIBase* const /*parent*/ ) + {} // Done in dependent properties + + virtual void merge( const SPIBase* const /*parent*/ ) + {} SPIFont& operator=(const SPIFont& rhs) { SPIBase::operator=(rhs); @@ -707,7 +956,9 @@ class SPIFont : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } }; @@ -718,16 +969,27 @@ enum { }; /// Baseline shift type internal to SPStyle. (This is actually just like SPIFontSize) -class SPIBaselineShift : public SPIBase { +class SPIBaselineShift : public SPIBase +{ + +public: + SPIBaselineShift() + : SPIBase( "baseline-shift", false ) { + this->clear(); + } + + virtual ~SPIBaselineShift() + {} - public: - SPIBaselineShift() : SPIBase( "baseline-shift", false ) { this->clear(); }; - virtual ~SPIBaselineShift() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); type=SP_BASELINE_SHIFT_LITERAL, unit=SP_CSS_UNIT_NONE, - literal = SP_CSS_BASELINE_SHIFT_BASELINE, value = 0.0, computed = 0.0; } + virtual void clear() { + SPIBase::clear(); + type=SP_BASELINE_SHIFT_LITERAL, unit=SP_CSS_UNIT_NONE, + literal = SP_CSS_BASELINE_SHIFT_BASELINE, value = 0.0, computed = 0.0; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -743,11 +1005,14 @@ class SPIBaselineShift : public SPIBase { // This is not used but we have it for completeness, it has not been tested. virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } + bool isZero() const; // To do: make private - public: +public: unsigned type : 2; unsigned unit : 4; unsigned literal: 2; @@ -760,15 +1025,26 @@ class SPIBaselineShift : public SPIBase { // CSS3 2.2 /// Text decoration line type internal to SPStyle. THIS SHOULD BE A GENERIC CLASS -class SPITextDecorationLine : public SPIBase { +class SPITextDecorationLine : public SPIBase +{ + +public: + SPITextDecorationLine() + : SPIBase( "text-decoration-line" ) { + this->clear(); + } + + virtual ~SPITextDecorationLine() + {} - public: - SPITextDecorationLine() : SPIBase( "text-decoration-line" ) { this->clear(); }; - virtual ~SPITextDecorationLine() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); underline = false, overline = false, line_through = false, blink = false; } + virtual void clear() { + SPIBase::clear(); + underline = false, overline = false, line_through = false, blink = false; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -782,10 +1058,12 @@ class SPITextDecorationLine : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: bool underline : 1; bool overline : 1; bool line_through : 1; @@ -794,15 +1072,26 @@ class SPITextDecorationLine : public SPIBase { // CSS3 2.2 /// Text decoration style type internal to SPStyle. THIS SHOULD JUST BE SPIEnum! -class SPITextDecorationStyle : public SPIBase { +class SPITextDecorationStyle : public SPIBase +{ + +public: + SPITextDecorationStyle() + : SPIBase( "text-decoration-style" ) { + this->clear(); + } + + virtual ~SPITextDecorationStyle() + {} - public: - SPITextDecorationStyle() : SPIBase( "text-decoration-style" ) { this->clear(); }; - virtual ~SPITextDecorationStyle() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); solid = true, isdouble = false, dotted = false, dashed = false, wavy = false; } + virtual void clear() { + SPIBase::clear(); + solid = true, isdouble = false, dotted = false, dashed = false, wavy = false; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -817,10 +1106,12 @@ class SPITextDecorationStyle : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: bool solid : 1; bool isdouble : 1; // cannot use "double" as it is a reserved keyword bool dotted : 1; @@ -837,18 +1128,26 @@ class SPITextDecorationStyle : public SPIBase { // the right style. (See http://www.w3.org/TR/css-text-decor-3/#text-decoration-property ) /// Text decoration type internal to SPStyle. -class SPITextDecoration : public SPIBase { +class SPITextDecoration : public SPIBase +{ + +public: + SPITextDecoration() + : SPIBase( "text-decoration" ), + style_td( NULL ) + {} + + virtual ~SPITextDecoration() + {} - public: - SPITextDecoration() : SPIBase( "text-decoration" ), style_td( NULL ) {}; - virtual ~SPITextDecoration() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); style_td = NULL; - }; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -859,9 +1158,11 @@ class SPITextDecoration : public SPIBase { // Use CSS2 value virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } - public: +public: SPStyle* style_td; // Style to be used for drawing CSS2 text decorations }; diff --git a/src/style.cpp b/src/style.cpp index 66672e949..e0c8536fc 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -440,6 +440,8 @@ SPStyle::~SPStyle() { // Remove connections release_connection.disconnect(); + fill_ps_changed_connection.disconnect(); + stroke_ps_changed_connection.disconnect(); // The following shoud be moved into SPIPaint and SPIFilter if (fill.value.href) { @@ -499,10 +501,10 @@ SPStyle::clear() { filter.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), this)); fill.value.href = new SPPaintServerReference(document); - fill.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), this)); + fill_ps_changed_connection = fill.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), this)); stroke.value.href = new SPPaintServerReference(document); - stroke.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), this)); + stroke_ps_changed_connection = stroke.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), this)); } cloned = false; @@ -1118,6 +1120,7 @@ sp_style_fill_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_paint_server_ref_modified), style)); } + style->signal_fill_ps_changed.emit(old_ref, ref); sp_style_paint_server_ref_modified(ref, 0, style); } @@ -1135,6 +1138,7 @@ sp_style_stroke_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPSty ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_paint_server_ref_modified), style)); } + style->signal_stroke_ps_changed.emit(old_ref, ref); sp_style_paint_server_ref_modified(ref, 0, style); } @@ -1363,7 +1367,11 @@ sp_style_set_ipaint_to_uri(SPStyle *style, SPIPaint *paint, const Inkscape::URI // now that we have a document, we can create it here if (!paint->value.href && document) { paint->value.href = new SPPaintServerReference(document); - paint->value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun((paint == &style->fill)? sp_style_fill_paint_server_ref_changed : sp_style_stroke_paint_server_ref_changed), style)); + if (paint == &style->fill) { + style->fill_ps_changed_connection = paint->value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), style)); + } else { + style->stroke_ps_changed_connection = paint->value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), style)); + } } if (paint->value.href){ diff --git a/src/style.h b/src/style.h index 48eb55430..a4c2b3043 100644 --- a/src/style.h +++ b/src/style.h @@ -252,6 +252,22 @@ public: sigc::connection filter_modified_connection; sigc::connection fill_ps_modified_connection; sigc::connection stroke_ps_modified_connection; + sigc::connection fill_ps_changed_connection; + sigc::connection stroke_ps_changed_connection; + + /** + * Emitted when paint server object, fill paint refers to, is changed. That is + * when the reference starts pointing to a different address in memory. + * + * NB It is different from fill_ps_modified signal. When paint server is modified + * it means some of it's attributes or chilren change. + */ + sigc::signal<void, SPObject *, SPObject *> signal_fill_ps_changed; + /** + * Emitted when paint server object, fill paint refers to, is changed. That is + * when the reference starts pointing to a different address in memory. + */ + sigc::signal<void, SPObject *, SPObject *> signal_stroke_ps_changed; SPObject *getFilter() { return (filter.href) ? filter.href->getObject() : NULL; } SPObject const *getFilter() const { return (filter.href) ? filter.href->getObject() : NULL; } diff --git a/src/svg/CMakeLists.txt b/src/svg/CMakeLists.txt index 968287895..f9d0bc52d 100644 --- a/src/svg/CMakeLists.txt +++ b/src/svg/CMakeLists.txt @@ -7,6 +7,7 @@ set(svg_SRC strip-trailing-zeros.cpp svg-affine.cpp svg-color.cpp + svg-angle.cpp svg-length.cpp svg-path.cpp # test-stubs.cpp @@ -24,6 +25,7 @@ set(svg_SRC svg-color-test.h svg-color.h svg-icc-color.h + svg-angle.h svg-length-test.h svg-length.h svg-path-geom-test.h diff --git a/src/svg/Makefile_insert b/src/svg/Makefile_insert index cf9bf3fbb..4f82bdd76 100644 --- a/src/svg/Makefile_insert +++ b/src/svg/Makefile_insert @@ -13,6 +13,8 @@ ink_common_sources += \ svg/svg-color.cpp \ svg/svg-color.h \ svg/svg-icc-color.h \ + svg/svg-angle.cpp \ + svg/svg-angle.h \ svg/svg-length.cpp \ svg/svg-length.h \ svg/svg-path.cpp \ diff --git a/src/svg/svg-angle.cpp b/src/svg/svg-angle.cpp new file mode 100644 index 000000000..63152368e --- /dev/null +++ b/src/svg/svg-angle.cpp @@ -0,0 +1,136 @@ +/** + * \file src/svg/svg-angle.cpp + * \brief SVG angle type + */ +/* + * Authors: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cstring> +#include <string> +#include <math.h> +#include <glib.h> + +#include "svg.h" +#include "stringstream.h" +#include "svg/svg-angle.h" +#include "util/units.h" + + +static bool sp_svg_angle_read_lff(gchar const *str, SVGAngle::Unit &unit, float &val, float &computed); + +SVGAngle::SVGAngle() + : _set(false) + , unit(NONE) + , value(0) + , computed(0) +{ +} + +/* Angle */ + +bool SVGAngle::read(gchar const *str) +{ + if (!str) { + return false; + } + + SVGAngle::Unit u; + float v; + float c; + if (!sp_svg_angle_read_lff(str, u, v, c)) { + return false; + } + + _set = true; + unit = u; + value = v; + computed = c; + + return true; +} + +void SVGAngle::unset(SVGAngle::Unit u, float v, float c) { + _set = false; + unit = u; + value = v; + computed = c; +} + +void SVGAngle::readOrUnset(gchar const *str, Unit u, float v, float c) { + if (!read(str)) { + unset(u, v, c); + } +} + +static bool sp_svg_angle_read_lff(gchar const *str, SVGAngle::Unit &unit, float &val, float &computed) +{ + if (!str) { + return false; + } + + gchar const *e; + float const v = g_ascii_strtod(str, (char **) &e); + if (e == str) { + return false; + } + + if (!e[0]) { + /* Unitless (defaults to degrees)*/ + unit = SVGAngle::NONE; + val = v; + computed = v; + return true; + } else if (!g_ascii_isalnum(e[0])) { + if (g_ascii_isspace(e[0]) && e[1] && g_ascii_isalpha(e[1])) { + return false; // spaces between value and unit are not allowed + } else { + /* Unitless (defaults to degrees)*/ + unit = SVGAngle::NONE; + val = v; + computed = v; + return true; + } + } else { + if (strncmp(e, "deg", 3) == 0) { + unit = SVGAngle::DEG; + computed = v; + } else if (strncmp(e, "grad", 4) == 0) { + unit = SVGAngle::GRAD; + computed = Inkscape::Util::Quantity::convert(v, "grad", "°"); + } else if (strncmp(e, "rad", 3) == 0) { + unit = SVGAngle::RAD; + computed = Inkscape::Util::Quantity::convert(v, "rad", "°"); + } else if (strncmp(e, "turn", 4) == 0) { + unit = SVGAngle::TURN; + computed = Inkscape::Util::Quantity::convert(v, "turn", "°"); + } else { + return false; + } + return true; + } + + /* Invalid */ + return false; +} + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/svg/svg-angle.h b/src/svg/svg-angle.h new file mode 100644 index 000000000..966491174 --- /dev/null +++ b/src/svg/svg-angle.h @@ -0,0 +1,69 @@ +#ifndef SEEN_SP_SVG_ANGLE_H +#define SEEN_SP_SVG_ANGLE_H + +/** + * \file src/svg/svg-angle.h + * \brief SVG angle type + */ +/* + * Authors: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <glib.h> + +class SVGAngle +{ +public: + SVGAngle(); + + enum Unit { + NONE, + DEG, + GRAD, + RAD, + TURN, + LAST_UNIT = TURN + }; + + // The object's value is valid / exists in SVG. + bool _set; + + // The unit of value. + Unit unit; + + // The value of this SVGAngle as found in the SVG. + float value; + + // The value in degrees. + float computed; + + float operator=(float v) { + _set = true; + unit = NONE; + value = computed = v; + return v; + } + + bool read(gchar const *str); + void unset(Unit u = NONE, float v = 0, float c = 0); + void readOrUnset(gchar const *str, Unit u = NONE, float v = 0, float c = 0); +}; + +#endif // SEEN_SP_SVG_ANGLE_H + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/svg/svg-color.cpp b/src/svg/svg-color.cpp index 5108b7702..c9f22f8a4 100644 --- a/src/svg/svg-color.cpp +++ b/src/svg/svg-color.cpp @@ -511,18 +511,18 @@ sp_svg_create_color_hash() void icc_color_to_sRGB(SVGICCColor* icc, guchar* r, guchar* g, guchar* b) { - guchar color_out[4]; - guchar color_in[4]; if (icc) { g_message("profile name: %s", icc->colorProfile.c_str()); Inkscape::ColorProfile* prof = SP_ACTIVE_DOCUMENT->profileManager->find(icc->colorProfile.c_str()); if ( prof ) { + guchar color_out[4] = {0,0,0,0}; cmsHTRANSFORM trans = prof->getTransfToSRGB8(); if ( trans ) { std::vector<colorspace::Component> comps = colorspace::getColorSpaceInfo( prof ); size_t count = CMSSystem::getChannelCount( prof ); size_t cap = std::min(count, comps.size()); + guchar color_in[4]; for (size_t i = 0; i < cap; i++) { color_in[i] = static_cast<guchar>((((gdouble)icc->colors[i]) * 256.0) * (gdouble)comps[i].scale); g_message("input[%d]: %d", (int)i, (int)color_in[i]); diff --git a/src/ui/dialog/clonetiler.cpp b/src/ui/dialog/clonetiler.cpp index 8d3d2b57c..5ac2ef29d 100644 --- a/src/ui/dialog/clonetiler.cpp +++ b/src/ui/dialog/clonetiler.cpp @@ -2235,7 +2235,11 @@ void CloneTiler::clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg) gdk_window_process_all_updates(); SPObject *obj = selection->singleItem(); - SPItem *item = SP_IS_ITEM(obj) ? SP_ITEM(obj) : 0; + if (!obj) { + // Should never happen (empty selection checked above). + std::cerr << "CloneTiler::clonetile_apply(): No object in single item selection!!!" << std::endl; + return; + } Inkscape::XML::Node *obj_repr = obj->getRepr(); const char *id_href = g_strdup_printf("#%s", obj_repr->attribute("id")); SPObject *parent = obj->parent; @@ -2326,6 +2330,7 @@ void CloneTiler::clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg) bool invert_picked = prefs->getBool(prefs_path + "invert_picked"); double gamma_picked = prefs->getDoubleLimited(prefs_path + "gamma_picked", 0, -10, 10); + SPItem *item = SP_IS_ITEM(obj) ? SP_ITEM(obj) : 0; if (dotrace) { clonetiler_trace_setup (sp_desktop_document(desktop), 1.0, item); } diff --git a/src/ui/dialog/symbols.cpp b/src/ui/dialog/symbols.cpp index 76532eb7e..647753c4f 100644 --- a/src/ui/dialog/symbols.cpp +++ b/src/ui/dialog/symbols.cpp @@ -62,8 +62,20 @@ #include "widgets/icon.h" #ifdef WITH_LIBVISIO -#include <libvisio/libvisio.h> -#include <libwpd-stream/libwpd-stream.h> + #include <libvisio/libvisio.h> + + // TODO: Drop this check when librevenge is widespread. + #if WITH_LIBVISIO01 + #include <librevenge-stream/librevenge-stream.h> + + using librevenge::RVNGFileStream; + using librevenge::RVNGStringVector; + #else + #include <libwpd-stream/libwpd-stream.h> + + typedef WPXFileStream RVNGFileStream; + typedef libvisio::VSDStringVector RVNGStringVector; + #endif #endif #include "verbs.h" @@ -495,14 +507,20 @@ void SymbolsDialog::iconChanged() { // Read Visio stencil files SPDocument* read_vss( gchar* fullname, gchar* filename ) { - WPXFileStream input(fullname); + RVNGFileStream input(fullname); if (!libvisio::VisioDocument::isSupported(&input)) { return NULL; } - libvisio::VSDStringVector output; + RVNGStringVector output; +#if WITH_LIBVISIO01 + librevenge::RVNGSVGDrawingGenerator generator(output, "svg"); + + if (!libvisio::VisioDocument::parseStencils(&input, &generator)) { +#else if (!libvisio::VisioDocument::generateSVGStencils(&input, output)) { +#endif return NULL; } diff --git a/src/ui/dialog/template-widget.cpp b/src/ui/dialog/template-widget.cpp index 9758b35ac..f79d166f2 100644 --- a/src/ui/dialog/template-widget.cpp +++ b/src/ui/dialog/template-widget.cpp @@ -24,6 +24,7 @@ #include "document.h" #include "document-undo.h" #include "file.h" +#include "sp-namedview.h" #include "extension/implementation/implementation.h" #include "inkscape.h" @@ -69,7 +70,10 @@ void TemplateWidget::create() _current_template.tpl_effect->effect(desc); DocumentUndo::clearUndo(sp_desktop_document(desc)); sp_desktop_document(desc)->setModifiedSinceSave(false); - + + // Apply cx,cy etc. from document + sp_namedview_window_from_document( desc ); + if (desktop) desktop->clearWaitingCursor(); } diff --git a/src/ui/widget/highlight-picker.cpp b/src/ui/widget/highlight-picker.cpp index bc6bdcf22..8593f0bdf 100644 --- a/src/ui/widget/highlight-picker.cpp +++ b/src/ui/widget/highlight-picker.cpp @@ -7,15 +7,8 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - +#include <glibmm.h> #include <gtkmm/icontheme.h> - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif #include "display/cairo-utils.h" diff --git a/src/ui/widget/insertordericon.cpp b/src/ui/widget/insertordericon.cpp index 97aa30aa6..a28b0f834 100644 --- a/src/ui/widget/insertordericon.cpp +++ b/src/ui/widget/insertordericon.cpp @@ -9,14 +9,6 @@ #include "ui/widget/insertordericon.h" -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/icontheme.h> #include "widgets/icon.h" diff --git a/src/ui/widget/insertordericon.h b/src/ui/widget/insertordericon.h index e6c2e1c5b..fb3412d3f 100644 --- a/src/ui/widget/insertordericon.h +++ b/src/ui/widget/insertordericon.h @@ -10,12 +10,12 @@ */ #if HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif +#include <glibmm.h> #include <gtkmm/cellrendererpixbuf.h> #include <gtkmm/widget.h> -#include <glibmm/property.h> namespace Inkscape { namespace UI { diff --git a/src/ui/widget/selected-style.cpp b/src/ui/widget/selected-style.cpp index d3adb6307..d22c59aa4 100644 --- a/src/ui/widget/selected-style.cpp +++ b/src/ui/widget/selected-style.cpp @@ -1220,7 +1220,9 @@ void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) { menu->show_all(); } -void SelectedStyle::on_opacity_changed () { +void SelectedStyle::on_opacity_changed () +{ + g_return_if_fail(_desktop); // TODO this shouldn't happen! if (_opacity_blocked) return; _opacity_blocked = true; diff --git a/src/viewbox.cpp b/src/viewbox.cpp index f59909abc..662b05686 100644 --- a/src/viewbox.cpp +++ b/src/viewbox.cpp @@ -164,16 +164,17 @@ void SPViewBox::set_preserveAspectRatio(const gchar* value) { 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(); + /* These are floats since SVGLength is a float: See bug 1374614 */ + float x = 0.0; + float y = 0.0; + float width = in.width(); + float 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 scalex = in.width() / ((float) this->viewBox.width()); + double scaley = in.height() / ((float) 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; @@ -217,12 +218,12 @@ void SPViewBox::apply_viewbox(const Geom::Rect& in) { /* Viewbox transform from scale and position */ Geom::Affine q; - q[0] = width / this->viewBox.width(); + q[0] = width / ((float) 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(); + q[3] = height / ((float) this->viewBox.height()); + q[4] = x - q[0] * ((float) this->viewBox.left()); + q[5] = y - q[3] * ((float) this->viewBox.top()); // std::cout << " q\n" << q << std::endl; diff --git a/src/widgets/ink-comboboxentry-action.cpp b/src/widgets/ink-comboboxentry-action.cpp index 5c59f6961..1114d2cdb 100644 --- a/src/widgets/ink-comboboxentry-action.cpp +++ b/src/widgets/ink-comboboxentry-action.cpp @@ -389,7 +389,7 @@ GtkWidget* create_tool_item( GtkAction* action ) GtkTreeViewRowSeparatorFunc (ink_comboboxentry_action->separator_func), NULL, NULL ); } - + // FIXME: once gtk3 migration is done this can be removed // https://bugzilla.gnome.org/show_bug.cgi?id=734915 gtk_widget_show_all (comboBoxEntry); diff --git a/src/widgets/stroke-style.cpp b/src/widgets/stroke-style.cpp index 3fa39da6f..cb18207f4 100644 --- a/src/widgets/stroke-style.cpp +++ b/src/widgets/stroke-style.cpp @@ -763,6 +763,9 @@ StrokeStyle::setJoinType (unsigned const jointype) tb = joinBevel; break; default: + // Should not happen + std::cerr << "StrokeStyle::setJoinType(): Invalid value" << std::endl; + tb = joinMiter; break; } setJoinButtons(tb); @@ -786,6 +789,9 @@ StrokeStyle::setCapType (unsigned const captype) tb = capSquare; break; default: + // Should not happen + std::cerr << "StrokeStyle::setCapType(): Invalid value" << std::endl; + tb = capButt; break; } setCapButtons(tb); diff --git a/src/xml/node-event-vector.h b/src/xml/node-event-vector.h index 416640b86..16add2960 100644 --- a/src/xml/node-event-vector.h +++ b/src/xml/node-event-vector.h @@ -58,11 +58,7 @@ struct NodeEventVector { void (* attr_changed) (Node *repr, char const *key, char const *oldval, char const *newval, bool is_interactive, void* data); void (* content_changed) (Node *repr, char const *oldcontent, char const *newcontent, void * data); void (* order_changed) (Node *repr, Node *child, Node *oldref, Node *newref, void* data); -} -#ifdef __GNUC__ -__attribute__((deprecated)) -#endif -; +}; } } diff --git a/src/xml/repr-io.cpp b/src/xml/repr-io.cpp index 0319bb5e3..a4146f215 100644 --- a/src/xml/repr-io.cpp +++ b/src/xml/repr-io.cpp @@ -623,7 +623,6 @@ static Node *sp_repr_svg_read_node (Document *xml_doc, xmlNodePtr node, const gc repr->setContent(reinterpret_cast<gchar*>(node->content)); } - child = node->xmlChildrenNode; for (child = node->xmlChildrenNode; child != NULL; child = child->next) { Node *crepr = sp_repr_svg_read_node (xml_doc, child, default_ns, prefix_map); if (crepr) { |
